From e02e85829b813fbae20f2092d421d13a44c1f31f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 27 Sep 2023 20:25:13 +0000 Subject: [PATCH] deploy: 45cf24ef198bdb48c8a43318cb11979db9d082f3 --- 30daysofIA/archive/index.html | 10 ++-- 30daysofIA/atom.xml | 52 ++++++++++++++++++ .../index.html | 26 +++++++++ .../index.html | 26 +++++++++ .../index.html | 10 ++-- .../index.html | 10 ++-- 30daysofIA/hacktogether-recap/index.html | 10 ++-- .../index.html | 10 ++-- .../index.html | 10 ++-- 30daysofIA/index.html | 10 ++-- 30daysofIA/kick-off/index.html | 10 ++-- 30daysofIA/page/10/index.html | 27 +++++++++ 30daysofIA/page/11/index.html | 26 +++++++++ 30daysofIA/page/2/index.html | 10 ++-- 30daysofIA/page/3/index.html | 12 ++-- 30daysofIA/page/4/index.html | 12 ++-- 30daysofIA/page/5/index.html | 12 ++-- 30daysofIA/page/6/index.html | 12 ++-- 30daysofIA/page/7/index.html | 13 ++--- 30daysofIA/page/8/index.html | 13 ++--- 30daysofIA/page/9/index.html | 13 +++-- .../index.html | 10 ++-- .../index.html | 10 ++-- 30daysofIA/road-to-fallforIA/index.html | 10 ++-- 30daysofIA/rss.xml | 44 +++++++++++++++ 30daysofIA/tags/30-days-of-ia/index.html | 12 ++-- .../tags/30-days-of-ia/page/10/index.html | 27 +++++++++ .../tags/30-days-of-ia/page/11/index.html | 26 +++++++++ .../tags/30-days-of-ia/page/2/index.html | 12 ++-- .../tags/30-days-of-ia/page/3/index.html | 12 ++-- .../tags/30-days-of-ia/page/4/index.html | 12 ++-- .../tags/30-days-of-ia/page/5/index.html | 12 ++-- .../tags/30-days-of-ia/page/6/index.html | 12 ++-- .../tags/30-days-of-ia/page/7/index.html | 13 ++--- .../tags/30-days-of-ia/page/8/index.html | 13 ++--- .../tags/30-days-of-ia/page/9/index.html | 13 +++-- 30daysofIA/tags/ask-the-expert/index.html | 12 ++-- .../tags/ask-the-expert/page/10/index.html | 27 +++++++++ .../tags/ask-the-expert/page/11/index.html | 26 +++++++++ .../tags/ask-the-expert/page/2/index.html | 12 ++-- .../tags/ask-the-expert/page/3/index.html | 12 ++-- .../tags/ask-the-expert/page/4/index.html | 12 ++-- .../tags/ask-the-expert/page/5/index.html | 12 ++-- .../tags/ask-the-expert/page/6/index.html | 12 ++-- .../tags/ask-the-expert/page/7/index.html | 13 ++--- .../tags/ask-the-expert/page/8/index.html | 13 ++--- .../tags/ask-the-expert/page/9/index.html | 13 +++-- .../tags/azure-container-apps/index.html | 12 ++-- .../azure-container-apps/page/10/index.html | 27 +++++++++ .../azure-container-apps/page/11/index.html | 26 +++++++++ .../azure-container-apps/page/2/index.html | 12 ++-- .../azure-container-apps/page/3/index.html | 12 ++-- .../azure-container-apps/page/4/index.html | 12 ++-- .../azure-container-apps/page/5/index.html | 12 ++-- .../azure-container-apps/page/6/index.html | 12 ++-- .../azure-container-apps/page/7/index.html | 13 ++--- .../azure-container-apps/page/8/index.html | 13 ++--- .../azure-container-apps/page/9/index.html | 13 +++-- 30daysofIA/tags/azure-cosmos-db/index.html | 12 ++-- .../tags/azure-cosmos-db/page/10/index.html | 27 +++++++++ .../tags/azure-cosmos-db/page/11/index.html | 26 +++++++++ .../tags/azure-cosmos-db/page/2/index.html | 12 ++-- .../tags/azure-cosmos-db/page/3/index.html | 12 ++-- .../tags/azure-cosmos-db/page/4/index.html | 12 ++-- .../tags/azure-cosmos-db/page/5/index.html | 12 ++-- .../tags/azure-cosmos-db/page/6/index.html | 12 ++-- .../tags/azure-cosmos-db/page/7/index.html | 13 ++--- .../tags/azure-cosmos-db/page/8/index.html | 13 ++--- .../tags/azure-cosmos-db/page/9/index.html | 13 +++-- 30daysofIA/tags/azure-functions/index.html | 12 ++-- .../tags/azure-functions/page/10/index.html | 27 +++++++++ .../tags/azure-functions/page/11/index.html | 26 +++++++++ .../tags/azure-functions/page/2/index.html | 12 ++-- .../tags/azure-functions/page/3/index.html | 12 ++-- .../tags/azure-functions/page/4/index.html | 12 ++-- .../tags/azure-functions/page/5/index.html | 12 ++-- .../tags/azure-functions/page/6/index.html | 12 ++-- .../tags/azure-functions/page/7/index.html | 13 ++--- .../tags/azure-functions/page/8/index.html | 13 ++--- .../tags/azure-functions/page/9/index.html | 13 +++-- .../tags/azure-kubernetes-service/index.html | 12 ++-- .../page/10/index.html | 27 +++++++++ .../page/11/index.html | 26 +++++++++ .../page/2/index.html | 12 ++-- .../page/3/index.html | 12 ++-- .../page/4/index.html | 12 ++-- .../page/5/index.html | 12 ++-- .../page/6/index.html | 12 ++-- .../page/7/index.html | 13 ++--- .../page/8/index.html | 13 ++--- .../page/9/index.html | 13 +++-- 30daysofIA/tags/azure-openai/index.html | 12 ++-- .../tags/azure-openai/page/10/index.html | 27 +++++++++ .../tags/azure-openai/page/11/index.html | 26 +++++++++ .../tags/azure-openai/page/2/index.html | 12 ++-- .../tags/azure-openai/page/3/index.html | 12 ++-- .../tags/azure-openai/page/4/index.html | 12 ++-- .../tags/azure-openai/page/5/index.html | 12 ++-- .../tags/azure-openai/page/6/index.html | 12 ++-- .../tags/azure-openai/page/7/index.html | 13 ++--- .../tags/azure-openai/page/8/index.html | 13 ++--- .../tags/azure-openai/page/9/index.html | 13 +++-- 30daysofIA/tags/community-buzz/index.html | 12 ++-- .../tags/community-buzz/page/10/index.html | 27 +++++++++ .../tags/community-buzz/page/11/index.html | 26 +++++++++ .../tags/community-buzz/page/2/index.html | 12 ++-- .../tags/community-buzz/page/3/index.html | 12 ++-- .../tags/community-buzz/page/4/index.html | 12 ++-- .../tags/community-buzz/page/5/index.html | 12 ++-- .../tags/community-buzz/page/6/index.html | 12 ++-- .../tags/community-buzz/page/7/index.html | 13 ++--- .../tags/community-buzz/page/8/index.html | 13 ++--- .../tags/community-buzz/page/9/index.html | 13 +++-- 30daysofIA/tags/fall-for-ia/index.html | 12 ++-- .../tags/fall-for-ia/page/10/index.html | 27 +++++++++ .../tags/fall-for-ia/page/11/index.html | 26 +++++++++ 30daysofIA/tags/fall-for-ia/page/2/index.html | 12 ++-- 30daysofIA/tags/fall-for-ia/page/3/index.html | 12 ++-- 30daysofIA/tags/fall-for-ia/page/4/index.html | 12 ++-- 30daysofIA/tags/fall-for-ia/page/5/index.html | 12 ++-- 30daysofIA/tags/fall-for-ia/page/6/index.html | 12 ++-- 30daysofIA/tags/fall-for-ia/page/7/index.html | 13 ++--- 30daysofIA/tags/fall-for-ia/page/8/index.html | 13 ++--- 30daysofIA/tags/fall-for-ia/page/9/index.html | 13 +++-- 30daysofIA/tags/github-actions/index.html | 12 ++-- .../tags/github-actions/page/10/index.html | 27 +++++++++ .../tags/github-actions/page/11/index.html | 26 +++++++++ .../tags/github-actions/page/2/index.html | 12 ++-- .../tags/github-actions/page/3/index.html | 12 ++-- .../tags/github-actions/page/4/index.html | 12 ++-- .../tags/github-actions/page/5/index.html | 12 ++-- .../tags/github-actions/page/6/index.html | 12 ++-- .../tags/github-actions/page/7/index.html | 13 ++--- .../tags/github-actions/page/8/index.html | 13 ++--- .../tags/github-actions/page/9/index.html | 13 +++-- 30daysofIA/tags/github-codespaces/index.html | 12 ++-- .../tags/github-codespaces/page/10/index.html | 27 +++++++++ .../tags/github-codespaces/page/11/index.html | 26 +++++++++ .../tags/github-codespaces/page/2/index.html | 12 ++-- .../tags/github-codespaces/page/3/index.html | 12 ++-- .../tags/github-codespaces/page/4/index.html | 12 ++-- .../tags/github-codespaces/page/5/index.html | 12 ++-- .../tags/github-codespaces/page/6/index.html | 12 ++-- .../tags/github-codespaces/page/7/index.html | 13 ++--- .../tags/github-codespaces/page/8/index.html | 13 ++--- .../tags/github-codespaces/page/9/index.html | 13 +++-- 30daysofIA/tags/github-copilot/index.html | 12 ++-- .../tags/github-copilot/page/10/index.html | 27 +++++++++ .../tags/github-copilot/page/11/index.html | 26 +++++++++ .../tags/github-copilot/page/2/index.html | 12 ++-- .../tags/github-copilot/page/3/index.html | 12 ++-- .../tags/github-copilot/page/4/index.html | 12 ++-- .../tags/github-copilot/page/5/index.html | 12 ++-- .../tags/github-copilot/page/6/index.html | 12 ++-- .../tags/github-copilot/page/7/index.html | 13 ++--- .../tags/github-copilot/page/8/index.html | 13 ++--- .../tags/github-copilot/page/9/index.html | 13 +++-- 30daysofIA/tags/hack-together/index.html | 12 ++-- .../tags/hack-together/page/10/index.html | 27 +++++++++ .../tags/hack-together/page/11/index.html | 26 +++++++++ .../tags/hack-together/page/2/index.html | 12 ++-- .../tags/hack-together/page/3/index.html | 12 ++-- .../tags/hack-together/page/4/index.html | 12 ++-- .../tags/hack-together/page/5/index.html | 12 ++-- .../tags/hack-together/page/6/index.html | 12 ++-- .../tags/hack-together/page/7/index.html | 13 ++--- .../tags/hack-together/page/8/index.html | 13 ++--- .../tags/hack-together/page/9/index.html | 13 +++-- 30daysofIA/tags/index.html | 10 ++-- 30daysofIA/tags/learn-live/index.html | 12 ++-- 30daysofIA/tags/learn-live/page/10/index.html | 27 +++++++++ 30daysofIA/tags/learn-live/page/11/index.html | 26 +++++++++ 30daysofIA/tags/learn-live/page/2/index.html | 12 ++-- 30daysofIA/tags/learn-live/page/3/index.html | 12 ++-- 30daysofIA/tags/learn-live/page/4/index.html | 12 ++-- 30daysofIA/tags/learn-live/page/5/index.html | 12 ++-- 30daysofIA/tags/learn-live/page/6/index.html | 12 ++-- 30daysofIA/tags/learn-live/page/7/index.html | 13 ++--- 30daysofIA/tags/learn-live/page/8/index.html | 13 ++--- 30daysofIA/tags/learn-live/page/9/index.html | 13 +++-- 404.html | 8 +-- Fall-For-IA/AskTheExpert/index.html | 8 +-- Fall-For-IA/CloudSkills/index.html | 8 +-- Fall-For-IA/CommunityGallery/index.html | 8 +-- Fall-For-IA/HackTogether/index.html | 8 +-- Fall-For-IA/LearnLive/index.html | 8 +-- Fall-For-IA/calendar/index.html | 8 +-- Fall-For-IA/index.html | 8 +-- New-Year/ate/index.html | 8 +-- New-Year/calendar/index.html | 8 +-- New-Year/index.html | 8 +-- ...e-1-1-e8cf7713343a8855e8f9b05434148f9d.png | Bin 0 -> 268714 bytes ...-1-11-96c8a57ad6beab15a2d06d1a0de0ad97.png | Bin 0 -> 65694 bytes ...e-1-2-49fb07b13652bc56001521b3312fae9d.png | Bin 0 -> 158984 bytes ...e-1-3-5b57e295568b12f4b213cb9fc3d39e81.png | Bin 0 -> 53757 bytes ...e-1-4-77065a4476c7a64b6b66d818a4c94a6c.png | Bin 0 -> 68277 bytes ...e-1-5-efe95dbf3756c59403a30a3a9a2b0c7a.png | Bin 0 -> 211808 bytes ...e-1-6-4e63cfb3a15c78ba383a4cdbdc221570.png | Bin 0 -> 48738 bytes ...e-1-7-1d88b437ef046ee6829ccc174cc61f3a.png | Bin 0 -> 81291 bytes ...e-1-8-f2e1dae9a99355268c4fc214e858ca32.png | Bin 0 -> 159949 bytes ...-2-1-974d3a64c66cbcd6e014779113bc1a4b.jpeg | Bin 0 -> 31313 bytes ...e-2-2-fb13fe692dc47f7d90de361ec6d5b771.png | Bin 0 -> 69516 bytes ...e-2-3-d8cd3a3026fb47d4589f3ee5baa3de5d.png | Bin 0 -> 71306 bytes assets/js/00984d5d.64d90ffb.js | 1 - assets/js/00984d5d.9e3ddb4e.js | 1 + ...9b9da.af23b11b.js => 0099b9da.c06b6456.js} | 2 +- assets/js/00ac6f8e.e3c9792d.js | 1 - assets/js/00ac6f8e.eb6db636.js | 1 + ...f3ab8.b7b3802c.js => 00ff3ab8.caab9076.js} | 2 +- assets/js/010f538e.14e91648.js | 1 + assets/js/010f538e.db12abf4.js | 1 - assets/js/016aea77.b7f3c03d.js | 1 + assets/js/016aea77.de8e6ee9.js | 1 - assets/js/017fab63.0aa7f6f0.js | 1 + assets/js/017fab63.72123fac.js | 1 - ...acc81.14f76dbc.js => 02eacc81.2a1132c0.js} | 2 +- assets/js/03633680.0840e695.js | 1 + assets/js/03633680.715dbb55.js | 1 - assets/js/03d787c9.6ad441d7.js | 1 + assets/js/046e34f6.40fe7f17.js | 1 + ...79bd3.d727ac5e.js => 04b79bd3.f545816b.js} | 2 +- ...e579b.c955f156.js => 059e579b.2f8f1cb2.js} | 2 +- assets/js/05b7df8f.2d4540ea.js | 1 - assets/js/05b7df8f.52a14524.js | 1 + assets/js/06db7cdd.2c092a64.js | 1 - assets/js/06db7cdd.6d416718.js | 1 + assets/js/07104faf.8a9124c8.js | 1 + ...65a4c.b82f3be9.js => 07865a4c.66788ab0.js} | 2 +- assets/js/07a9616f.1d585d6d.js | 1 - assets/js/07a9616f.42467a5f.js | 1 + assets/js/09bb4c1b.60f2b797.js | 1 + assets/js/09bb4c1b.b0d72bcb.js | 1 - ...ee2df.e815f469.js => 0a1ee2df.1f90514b.js} | 2 +- assets/js/0a9d7c60.6d9a0e9d.js | 1 + assets/js/0a9d7c60.74f6035d.js | 1 - assets/js/0b3e58d2.0b860b7c.js | 1 + assets/js/0ba01ce9.5ea69834.js | 1 + assets/js/0ba01ce9.96711b0f.js | 1 - assets/js/0c929776.14e0a845.js | 1 + assets/js/0c929776.f3c0971a.js | 1 - ...5e40a.a73c5335.js => 0cc5e40a.634e4e94.js} | 2 +- ...3c1f5.6f88c760.js => 0db3c1f5.881974f2.js} | 2 +- assets/js/0dcb0a11.0135dcb7.js | 1 + assets/js/0dcb0a11.d27a53ae.js | 1 - assets/js/0dfdf1d3.0805e944.js | 1 - assets/js/0dfdf1d3.91931fff.js | 1 + ...55584.9955d0b5.js => 0e655584.62e917d4.js} | 2 +- assets/js/0f41348e.36120cad.js | 1 + assets/js/11f27dd8.44e6cbc1.js | 1 - assets/js/11f27dd8.9bc2b0f1.js | 1 + assets/js/1280d58f.23a1732b.js | 1 - assets/js/1280d58f.6de38eb5.js | 1 + ...4fad1.8ed9ee95.js => 1314fad1.46b934d3.js} | 2 +- assets/js/138b7335.082758d9.js | 1 - assets/js/138b7335.eb7fa37d.js | 1 + assets/js/1414d4c0.42afa8a5.js | 1 + ...d8733.7801785a.js => 155d8733.684da0fe.js} | 2 +- assets/js/157ee2cb.798aeefb.js | 1 + assets/js/157ee2cb.ffe6e9fa.js | 1 - ...f29d7.34a5755a.js => 167f29d7.1f535b6c.js} | 2 +- assets/js/16a2dce4.f4990f35.js | 1 + assets/js/184bf20b.fdbb968a.js | 1 + assets/js/1864d48a.46bb768d.js | 1 - assets/js/1864d48a.72ef5fc2.js | 1 + assets/js/1a4e3b56.94112cbb.js | 1 + assets/js/1a4e3b56.ba710a9e.js | 1 - assets/js/1b60c5ab.01f23419.js | 1 + assets/js/1b60c5ab.aa889c53.js | 1 - assets/js/1b66ef97.007c766a.js | 1 + assets/js/1b66ef97.ceaa36a6.js | 1 - assets/js/1b81fd5d.02474c25.js | 1 - assets/js/1b81fd5d.8e6bd07c.js | 1 + assets/js/1ba2ee3a.57da460a.js | 1 - assets/js/1ba2ee3a.b9a76ace.js | 1 + assets/js/1c8f664c.3ffc5caa.js | 1 - assets/js/1c8f664c.5e59f258.js | 1 + ...4b949.b446e3f0.js => 1c94b949.504650e2.js} | 2 +- ...d1ee9.63f3fb3f.js => 1e1d1ee9.0ca4cd05.js} | 2 +- ...313b4.1b2c0ac8.js => 1e8313b4.0af591e7.js} | 2 +- assets/js/1fbbba6a.67758059.js | 1 + ...5a212.b583e52d.js => 1fc5a212.dd7baf7c.js} | 2 +- ...bfeb4.1eaf4f44.js => 209bfeb4.b791c5ad.js} | 2 +- assets/js/22598362.42a670fd.js | 1 + assets/js/22598362.827d1ce6.js | 1 - assets/js/239f6fb3.d9b7909e.js | 1 + ...34ed0.1840b116.js => 24634ed0.c21c945a.js} | 2 +- ...13bf4.483b2910.js => 24c13bf4.ab69752a.js} | 2 +- ...8adfd.9d6e76bb.js => 2508adfd.16666c3c.js} | 2 +- ...b3259.f50a6bd3.js => 259b3259.f38a6caf.js} | 2 +- assets/js/259ea4c2.d38b1087.js | 1 + ...2dd7b.c29a6888.js => 260068d7.43396f0e.js} | 2 +- assets/js/260068d7.535a13e7.js | 1 - assets/js/27ad03b1.04914118.js | 1 + ...dd6ab.2ed66b1f.js => 292dd6ab.96dd7aff.js} | 2 +- assets/js/2ab0497b.158cc56e.js | 1 - assets/js/2ab0497b.aedede75.js | 1 + assets/js/2bc88290.6bb9e2c5.js | 1 + assets/js/2bc88290.dcadace3.js | 1 - ...fefbc.79af042e.js => 2bcfefbc.888cb0ce.js} | 2 +- ...2658d.ba4d97e4.js => 2c72658d.366b7f1b.js} | 2 +- assets/js/2d813f9f.0d024571.js | 1 - assets/js/2d813f9f.11f4ebb8.js | 1 + assets/js/2efdc0bf.9e7e15e4.js | 1 + assets/js/2efdc0bf.a2651cf3.js | 1 - assets/js/30b4a191.1a460bb4.js | 1 + assets/js/30b4a191.e753ac3a.js | 1 - assets/js/30f26a7a.56f65583.js | 1 + assets/js/30f26a7a.b86dd9c1.js | 1 - assets/js/31098de5.8fc37c83.js | 1 + assets/js/31098de5.d19d9574.js | 1 - ...b45c7.6b2a492c.js => 328b45c7.6a90e9b2.js} | 2 +- assets/js/358af5d5.291d56e2.js | 1 + assets/js/358af5d5.f315e1eb.js | 1 - assets/js/367b1faa.1f016f0b.js | 1 + assets/js/367b1faa.5032b946.js | 1 - ...282e0.2f1c327e.js => 36e282e0.9737ed73.js} | 2 +- assets/js/36e9edc7.2c82a055.js | 1 + ...54ecb.f08d55bd.js => 38554ecb.c19b45fa.js} | 2 +- assets/js/388c8f2b.7cfa4692.js | 1 + assets/js/388c8f2b.a450e800.js | 1 - ...56339.e3ef9893.js => 39156339.7de5e275.js} | 2 +- assets/js/39200a92.792e3dd0.js | 1 - assets/js/39200a92.f4fd3199.js | 1 + ...92805.cbc408ac.js => 39792805.4f73ade9.js} | 2 +- assets/js/3a1a5f0f.bdfcb3d3.js | 1 + assets/js/3a1a5f0f.da9bb3b3.js | 1 - assets/js/3a894f2b.54db83b7.js | 1 + assets/js/3a894f2b.8ae98511.js | 1 - ...3f3bb.1ef37446.js => 3ae3f3bb.137796ec.js} | 2 +- assets/js/3bbe6001.60143556.js | 1 + assets/js/3bbe6001.935c8b71.js | 1 - assets/js/3bfb947c.79788038.js | 1 + assets/js/3bfb947c.b5293a1c.js | 1 - assets/js/3ccca367.66782563.js | 1 + assets/js/3ccca367.6ff99c10.js | 1 - assets/js/3e04afb2.1426ee55.js | 1 - assets/js/3e04afb2.7216156e.js | 1 + assets/js/3e675a42.127a98c0.js | 1 + ...9b1d2.945667d7.js => 3f09b1d2.5981be58.js} | 2 +- ...f9a5a.054d6ab3.js => 3f5f9a5a.59c1cdeb.js} | 2 +- assets/js/411bca4b.58d1962b.js | 1 + assets/js/42421040.47ac63e8.js | 1 + assets/js/42421040.72f2a6f9.js | 1 - assets/js/43949d84.c34a8a35.js | 1 + assets/js/460574f3.e2fea3ea.js | 1 + assets/js/48a6e2e6.09239c67.js | 1 + assets/js/48a6e2e6.0a891306.js | 1 - assets/js/4982e737.9b94071c.js | 1 + assets/js/4988404d.4fdc47ca.js | 1 - assets/js/4988404d.d1c1bdfa.js | 1 + assets/js/4b3f15eb.bb36c1c5.js | 1 + assets/js/4b3f15eb.f1bc5b6e.js | 1 - assets/js/4c23d5c0.228a8dca.js | 1 + assets/js/4c3a8a2f.c17067ba.js | 1 + ...b9235.80f5d621.js => 4c3b9235.46d1b0b8.js} | 2 +- assets/js/4c3daab0.5a2aacb5.js | 1 - assets/js/4c3daab0.75136e9f.js | 1 + assets/js/4d20e64b.a465a014.js | 1 - assets/js/4d20e64b.e0d96e14.js | 1 + ...6cbe9.5f7cc02a.js => 4d46cbe9.d054f671.js} | 2 +- assets/js/4f61748e.873488fb.js | 1 + ...89287.9959b695.js => 4f789287.8ea82e3b.js} | 2 +- assets/js/50b61013.901e79cd.js | 1 - assets/js/50b61013.91303c96.js | 1 + assets/js/51f200c1.29807d7a.js | 1 + assets/js/51f200c1.df545e82.js | 1 - assets/js/51fb1599.7c1dedad.js | 1 + assets/js/51fb1599.7d31782f.js | 1 - assets/js/52094cec.588fb7e8.js | 1 + assets/js/52094cec.85fb4be8.js | 1 - assets/js/52d53352.99ac5e15.js | 1 + ...cf760.097fca42.js => 535cf760.4e49de03.js} | 2 +- ...1a0e4.2555ab44.js => 5361a0e4.67242618.js} | 2 +- assets/js/53e9efec.60123bce.js | 1 + ...25fb2.b6386356.js => 54625fb2.408714cf.js} | 2 +- assets/js/55183266.d6c4c584.js | 1 + ...04de1.f85c3d88.js => 55404de1.4e969025.js} | 2 +- ...9118e.5b5c147b.js => 55c9118e.a4a0b9ed.js} | 2 +- assets/js/565dbc36.8e0e3747.js | 1 + assets/js/57904ffd.08e3b8d5.js | 1 - assets/js/57904ffd.9957ff58.js | 1 + ...f2372.97f2dd26.js => 587f2372.f4c6dce4.js} | 2 +- ...dc25b.eb24a7e7.js => 599dc25b.21b5c5c4.js} | 2 +- assets/js/59a2dd7b.281bc225.js | 1 + ...2fd00.9d39a649.js => 5ae2fd00.c1ff0dc1.js} | 2 +- assets/js/5b107cfc.b7ee69c7.js | 1 - assets/js/5b107cfc.e11439d4.js | 1 + ...ab9e3.fefeff21.js => 5c3ab9e3.52677e48.js} | 2 +- ...b2412.1a05c5b6.js => 5cab2412.16e6edc0.js} | 2 +- assets/js/5cd45a8d.5a5793d7.js | 1 - assets/js/5cd45a8d.d9bc1b69.js | 1 + assets/js/5e601653.5b8c60f6.js | 1 - assets/js/5e601653.c6820bf7.js | 1 + assets/js/5f63ac35.4fce26d7.js | 1 + assets/js/5f63ac35.c9dce46d.js | 1 - ...b448d.a2da63bd.js => 604b448d.0e585a19.js} | 2 +- ...b2ff8.eb682cc4.js => 605b2ff8.82b69a31.js} | 2 +- assets/js/647961e4.74c55fdf.js | 1 - assets/js/647961e4.bd66eaa3.js | 1 + assets/js/654f4d54.802219e0.js | 1 + assets/js/675273ae.6da76d91.js | 1 + assets/js/677498e0.35cb10c2.js | 1 + assets/js/677498e0.5d24dafb.js | 1 - ...85bba.ced7a540.js => 67e85bba.9649a011.js} | 2 +- ...0c0a4.d1c41a56.js => 6a20c0a4.722023e3.js} | 2 +- ...3c29a.ad250b7a.js => 6ac3c29a.3193dde7.js} | 2 +- ...5c7b1.85494c4c.js => 6b35c7b1.c8498244.js} | 2 +- assets/js/6b9868e6.16b7a1fe.js | 1 + assets/js/6b9868e6.213a574f.js | 1 - assets/js/6c2bf19a.02d06e0c.js | 1 - assets/js/6c2bf19a.d8a998d3.js | 1 + ...2d8c3.f1b34cea.js => 6c32d8c3.21a56d1b.js} | 2 +- ...a3851.858c1add.js => 6daa3851.10a139a9.js} | 2 +- assets/js/6e52823d.2341626e.js | 1 - assets/js/6e52823d.e1847255.js | 1 + assets/js/6ed72a5d.5da27e48.js | 1 + assets/js/6ed72a5d.875d7450.js | 1 - assets/js/6f46b4a6.62e0f8b1.js | 1 + assets/js/6f46b4a6.c8bfd2ea.js | 1 - assets/js/6f976579.e157b073.js | 1 - assets/js/6f976579.fb99fe0b.js | 1 + ...36db2.c30e6149.js => 6fa36db2.f9495fe1.js} | 2 +- assets/js/6fc59d81.5dc3c211.js | 1 + ...6f236.0de6d3c9.js => 7166f236.99c6f063.js} | 2 +- assets/js/737c62d3.27e16507.js | 1 - assets/js/737c62d3.b70b1402.js | 1 + assets/js/74c62dc2.662efd6c.js | 1 + assets/js/75acfd48.4c718337.js | 1 - assets/js/75acfd48.d5b75f35.js | 1 + ...e7d27.9d79171a.js => 782e7d27.8e79bd2a.js} | 2 +- assets/js/79170c02.e6a1b62c.js | 1 - assets/js/79170c02.edfc67cb.js | 1 + ...d445f.3c029ac8.js => 7b7d445f.7de464c0.js} | 2 +- ...0b9bf.c472bb0f.js => 7bd0b9bf.d41919bb.js} | 2 +- assets/js/7c838aa5.1d0ac9fa.js | 1 + assets/js/7c838aa5.a104c113.js | 1 - assets/js/7d20b517.bda2cf93.js | 1 + ...a7cb7.d43df8ec.js => 7d4a7cb7.7672322c.js} | 2 +- assets/js/7dba6303.c2fef4a8.js | 1 + assets/js/7dba6303.dd59dd94.js | 1 - ...c7a3f.95768422.js => 7e3c7a3f.5f562377.js} | 2 +- ...349ce.1a6a0103.js => 807349ce.2f17bbe8.js} | 2 +- assets/js/80aaee8e.3988266a.js | 1 - assets/js/80aaee8e.58d3b646.js | 1 + assets/js/824b618b.adaffeb7.js | 1 + assets/js/8362f252.4d6de7d4.js | 1 - assets/js/8362f252.ca0cce64.js | 1 + ...2e158.5679da5f.js => 8412e158.2fe1ac2d.js} | 2 +- ...5e33e.fae4094b.js => 8445e33e.c3f178d1.js} | 2 +- assets/js/84806527.0bdc75b2.js | 1 + assets/js/84806527.25945221.js | 1 - ...9913c.d075cf3f.js => 84d9913c.0edfd53c.js} | 2 +- assets/js/84e7f16f.95745ba9.js | 1 - assets/js/84e7f16f.cf9c5f42.js | 1 + assets/js/854ddf44.35dc9a6f.js | 1 + assets/js/854ddf44.d30be8b3.js | 1 - ...ba9e9.fc1d5689.js => 85cba9e9.47d358fd.js} | 2 +- assets/js/86e280de.6daaa018.js | 1 + ...bf9f8.32218a34.js => 87bbf9f8.7a2199ef.js} | 2 +- ...57ceb.f272dfc8.js => 88357ceb.3eb258f5.js} | 2 +- assets/js/88665212.74095adf.js | 1 - assets/js/88665212.76f8067e.js | 1 + ...15eee.453a46b1.js => 8b115eee.1be3c929.js} | 2 +- assets/js/8b5ab4ba.0875bda3.js | 1 + assets/js/8b5ab4ba.44ab3abd.js | 1 - assets/js/8baf15a9.17bcf8b4.js | 1 - assets/js/8baf15a9.28a9b535.js | 1 + ...d2b1d.340adc88.js => 8c3d2b1d.6b291ca2.js} | 2 +- assets/js/8ca49f56.4a007c7d.js | 1 + assets/js/8ca49f56.69bc234a.js | 1 - ...cefc1.1b7a94a3.js => 8cacefc1.23c0a970.js} | 2 +- ...0c8e1.abec1ca4.js => 8e80c8e1.5479c0dd.js} | 2 +- ...add25.26ce8afd.js => 8f4add25.52cd3e38.js} | 2 +- assets/js/8f65450d.b87b892f.js | 1 + assets/js/9089e36c.638f873c.js | 1 + assets/js/91e58248.d8e381ee.js | 1 + assets/js/92851fbb.33fa462d.js | 1 + assets/js/92851fbb.bb33843c.js | 1 - ...65d98.11300f8e.js => 93465d98.73c1ce2f.js} | 2 +- assets/js/937ccad8.5a0babb2.js | 1 - assets/js/937ccad8.e7f7c42a.js | 1 + assets/js/9448e033.517999af.js | 1 + assets/js/9575c44b.2c0af6f4.js | 1 + assets/js/9648b421.11e4bdea.js | 1 + ...6a951.d9386981.js => 96d6a951.77a40a44.js} | 2 +- assets/js/9762ef78.4d49e9aa.js | 1 - assets/js/9762ef78.7739d6af.js | 1 + ...96520.5c957134.js => 97a96520.1dc3826e.js} | 2 +- ...179be.208177bf.js => 97c179be.5049607e.js} | 2 +- assets/js/97e02956.3fa27551.js | 1 - assets/js/97e02956.58298c71.js | 1 + assets/js/9a9cfa79.62f8a80d.js | 1 + assets/js/9aee8dd5.0ea02461.js | 1 + assets/js/9aee8dd5.b79fba90.js | 1 - assets/js/9b3a3088.ae8c8777.js | 1 + assets/js/9b60d2ea.a5ae680f.js | 1 - assets/js/9b60d2ea.e73b3058.js | 1 + ...306e8.b47a21be.js => 9bc306e8.917cb835.js} | 2 +- ...7a54a.a27405f9.js => 9c17a54a.c7677255.js} | 2 +- assets/js/9cdc6aec.0e31f359.js | 1 - assets/js/9cdc6aec.3387524a.js | 1 + assets/js/9d6ed2de.b2a88d52.js | 1 - assets/js/9d6ed2de.b39d2901.js | 1 + ...f083a.e3f28d8b.js => 9e2f083a.eaf3c8af.js} | 2 +- ...4d4e5.1b829b26.js => 9f14d4e5.215dbdae.js} | 2 +- assets/js/9f3b21c4.335fca1f.js | 1 + assets/js/9fec92ba.311f5814.js | 1 + assets/js/9fec92ba.b4a0c45f.js | 1 - ...724b8.735f7453.js => a0a724b8.0640c6c7.js} | 2 +- ...db7eb.6715de61.js => a11db7eb.1fc7a15a.js} | 2 +- ...4ef93.95f38638.js => a344ef93.057c2fb3.js} | 2 +- assets/js/a356d3a5.9b9b7f7e.js | 1 + assets/js/a40c8e08.c73a9ffd.js | 1 + assets/js/a40c8e08.e7b66816.js | 1 - assets/js/a4bab584.56403a36.js | 1 - assets/js/a4bab584.fa105b35.js | 1 + ...6a500.9e951485.js => a506a500.519b9970.js} | 2 +- assets/js/a5551a6a.2ccaa12a.js | 1 + assets/js/a60bbbfe.b43c9ef3.js | 1 + assets/js/a60bbbfe.fb7caf18.js | 1 - assets/js/a682bc17.920f04db.js | 1 + ...d4213.5ec66a35.js => a6ad4213.f48cbaaf.js} | 2 +- assets/js/a787e11b.3cc48baa.js | 1 - assets/js/a787e11b.db1f02b3.js | 1 + assets/js/a843e963.5e189999.js | 1 - assets/js/a843e963.e9dafc2d.js | 1 + ...c2360.fcbd9632.js => a89c2360.e2b43333.js} | 2 +- assets/js/a8dbe756.cc6b938c.js | 1 - assets/js/a8dbe756.d6c565de.js | 1 + ...ea87d.0dae27aa.js => aa8ea87d.9b19d907.js} | 2 +- ...26052.e78788a5.js => abf26052.16b0ff59.js} | 2 +- ...e80dd.bec66106.js => ac0e80dd.f0e06a6e.js} | 2 +- ...c78c4.3778ff8f.js => acfc78c4.f8739257.js} | 2 +- assets/js/adc56754.88e46af7.js | 1 + assets/js/adc56754.c85fd86a.js | 1 - ...62146.43d79204.js => aeb62146.59789cea.js} | 2 +- ...3e988.b4f9b01c.js => afc3e988.e856ccd5.js} | 2 +- ...c50e5.ef8d6e62.js => b34c50e5.3b84da93.js} | 2 +- ...f0f06.022baf69.js => b34f0f06.55109f43.js} | 2 +- assets/js/b62d91fe.140a584e.js | 1 + ...b65e1.b5e71854.js => b6fb65e1.5e333588.js} | 2 +- assets/js/b70dd8a8.35052561.js | 1 - assets/js/b70dd8a8.c323d911.js | 1 + ...0ee9a.9cdd7bcd.js => b760ee9a.5414a064.js} | 2 +- assets/js/b826954e.813adb62.js | 1 + assets/js/b826954e.e7d16a2c.js | 1 - ...0d9a8.94caac31.js => b860d9a8.c31c5887.js} | 2 +- assets/js/b95e1cc2.e4128be0.js | 1 + assets/js/bbae7a42.9919242e.js | 1 + assets/js/bd2cd00b.5a65b5d3.js | 1 - assets/js/bd2cd00b.9171a2a1.js | 1 + ...46f50.e1a8eedc.js => bd546f50.1939fe14.js} | 2 +- ...56ffe.bf51f60d.js => c0e56ffe.2b3d51b5.js} | 2 +- ...acb88.ca2fd49c.js => c0facb88.0c3864d1.js} | 2 +- assets/js/c141a364.3cb72f76.js | 1 + assets/js/c1fb8ad8.42c7a1ab.js | 1 + assets/js/c1fb8ad8.71d94677.js | 1 - ...19041.d383eb5c.js => c2319041.56343153.js} | 2 +- assets/js/c2e0ced8.43ee5c30.js | 1 - assets/js/c2e0ced8.5c7389e6.js | 1 + assets/js/c35448b1.2081a12b.js | 1 + assets/js/c35448b1.b9fc8a3d.js | 1 - assets/js/c3591ce3.f7b7a154.js | 1 + assets/js/c5298e55.748e52f9.js | 1 + assets/js/c5298e55.d8b0f9ee.js | 1 - assets/js/c580cfa2.9427a4b5.js | 1 + assets/js/c580cfa2.d12e48c2.js | 1 - assets/js/c58e39ac.86584c6e.js | 1 + ...1643e.06c56971.js => c751643e.e3ae0270.js} | 2 +- ...0b8e3.8bcb8248.js => c7f0b8e3.92f020e7.js} | 2 +- ...60460.636e88ff.js => c8c60460.07a85934.js} | 2 +- assets/js/caa33e0e.73a02ad0.js | 1 + ...c3c94.07cfb138.js => cafc3c94.1e4690ca.js} | 2 +- assets/js/cb997589.614d1f64.js | 1 + assets/js/cb997589.92b1d6e0.js | 1 - assets/js/cd923978.66869174.js | 1 + assets/js/cd923978.81ea0285.js | 1 - assets/js/ce5487d5.b32f6406.js | 1 + assets/js/cefbb4e5.5d3f7af4.js | 1 + assets/js/cefbb4e5.dd998262.js | 1 - assets/js/cf50db93.7a6a170b.js | 1 - assets/js/cf50db93.ece85fa1.js | 1 + assets/js/cf9f52c6.2fe7da63.js | 1 - assets/js/cf9f52c6.ca1e4d5d.js | 1 + assets/js/d0273d46.45fddbb3.js | 1 - assets/js/d0273d46.8ebabf4d.js | 1 + assets/js/d17a530e.735c308e.js | 1 + assets/js/d17a530e.85d36f48.js | 1 - ...a7198.cb837c7d.js => d22a7198.6455ea1b.js} | 2 +- assets/js/d32fff51.fdaaaaa6.js | 1 + assets/js/d36d48ac.15749bf4.js | 1 + assets/js/d37b1df8.3c0c8bee.js | 1 - assets/js/d37b1df8.fffa0321.js | 1 + assets/js/d436e3ab.1513077e.js | 1 - assets/js/d436e3ab.16bd3b08.js | 1 + ...c28fa.f5526b88.js => d60c28fa.9b2aab5c.js} | 2 +- ...399c5.be2c326f.js => d6e399c5.fd1984b6.js} | 2 +- ...fd83e.e5c7a76e.js => d70fd83e.5d0a7f03.js} | 2 +- ...1da33.133cf8a6.js => d721da33.7cdd1c40.js} | 2 +- ...f598b.1c9d82dc.js => d72f598b.b20b3e35.js} | 2 +- ...3cd59.e3dfac6d.js => d763cd59.5be11f16.js} | 2 +- ...130e9.4e9c66a5.js => db6130e9.d3ee41a7.js} | 2 +- assets/js/dd8667a0.6dae5430.js | 1 + assets/js/dd8667a0.f1b53af5.js | 1 - assets/js/dfd81e36.1479e607.js | 1 + assets/js/dfd81e36.ac9c856b.js | 1 - ...7deb5.9e5dc6c8.js => e127deb5.1a39730c.js} | 2 +- assets/js/e137ac4d.61f2c30e.js | 1 + assets/js/e137ac4d.cfeddd2a.js | 1 - assets/js/e1405df5.5058cd75.js | 1 - assets/js/e1405df5.ce5146c7.js | 1 + assets/js/e1daa54d.cae255b5.js | 1 - assets/js/e1daa54d.ee16c7ce.js | 1 + assets/js/e37c4032.16459f6f.js | 1 + assets/js/e37c4032.1bb77cf7.js | 1 - ...fdeb5.81940f11.js => e49fdeb5.2ef6a200.js} | 2 +- ...30f58.d3d059d5.js => e5530f58.5759edd6.js} | 2 +- assets/js/e57b34d0.e511a36d.js | 1 - assets/js/e57b34d0.fdf7b02e.js | 1 + assets/js/e645b9de.b8fdff07.js | 1 + assets/js/e705147d.cec3bc96.js | 1 + assets/js/e705147d.e3965d85.js | 1 - assets/js/e73a1950.3fd7c02b.js | 1 + assets/js/e82f66e0.b417c3c4.js | 1 + assets/js/e82f66e0.ecfbb01b.js | 1 - assets/js/e85f9578.ac63cec0.js | 1 - assets/js/e85f9578.eec3600f.js | 1 + assets/js/e90a85bc.31f0cf99.js | 1 + assets/js/e90a85bc.a37f7587.js | 1 - assets/js/e9f92e0d.606dbcda.js | 1 - assets/js/e9f92e0d.811b9008.js | 1 + assets/js/ea15e570.24c52116.js | 1 + assets/js/ea15e570.b26e2842.js | 1 - assets/js/ea89a27b.76738e76.js | 1 + ...89fda.e9a3cd63.js => eb689fda.ee9aef96.js} | 2 +- ...76b0d.6bc7151b.js => ebb76b0d.c0390620.js} | 2 +- assets/js/ebba27e3.19f47424.js | 1 + ...c2797.1a6aaf54.js => ef7c2797.1b163603.js} | 2 +- ...016d3.68e5b7b0.js => efe016d3.13a13de6.js} | 2 +- assets/js/f0d25600.1df33c41.js | 1 - assets/js/f0d25600.5e8ec391.js | 1 + ...90793.bc58d687.js => f0e90793.0eac9db8.js} | 2 +- ...c2396.ebc79d26.js => f12c2396.20aed50e.js} | 2 +- assets/js/f273b679.29701661.js | 1 + assets/js/f38d0525.5e24e53a.js | 1 + assets/js/f5611d39.2c8c96bd.js | 1 + assets/js/f5611d39.55a4e068.js | 1 - assets/js/f5b7c6a9.716334e9.js | 1 + assets/js/f5b7c6a9.eb909da9.js | 1 - assets/js/f6529765.07f885ca.js | 1 + assets/js/f6de2cbe.506318cb.js | 1 - assets/js/f6de2cbe.84638608.js | 1 + assets/js/f7310d14.17d5fea5.js | 1 + ...cbec7.647f04b0.js => f74cbec7.528f0994.js} | 2 +- assets/js/f7b7ef7e.988c1d34.js | 1 + assets/js/f7b7ef7e.e168f09f.js | 1 - assets/js/f7fb9a31.3cf88f51.js | 1 - assets/js/f7fb9a31.ae335a1f.js | 1 + assets/js/f872f327.23de5059.js | 1 + assets/js/f872f327.9d7eb238.js | 1 - assets/js/fa74e77e.245af8f3.js | 1 + assets/js/fa74e77e.723f4085.js | 1 - assets/js/faa3ccc1.2c725132.js | 1 - assets/js/faa3ccc1.8f3a55ed.js | 1 + assets/js/fac73890.b25b35b1.js | 1 - assets/js/fac73890.c2d4c256.js | 1 + ...b9b20.a3b5736b.js => fb5b9b20.a2dd7017.js} | 2 +- ...1245d.907b7674.js => fb71245d.5d25eb96.js} | 2 +- ...bf422.b27db388.js => fbdbf422.4cd21b16.js} | 2 +- assets/js/fd422a34.52ab7325.js | 1 - assets/js/fd422a34.a43285ba.js | 1 + assets/js/fd4ba951.23d19ee6.js | 1 + assets/js/fd4ba951.a923f798.js | 1 - assets/js/main.0f5b4036.js | 2 - assets/js/main.e96c061e.js | 2 + ...CENSE.txt => main.e96c061e.js.LICENSE.txt} | 0 assets/js/runtime~main.877c649b.js | 1 - assets/js/runtime~main.ee8f3c68.js | 1 + blog/01-kickoff/index.html | 8 +-- blog/02-functions-intro/index.html | 8 +-- blog/03-functions-quickstart/index.html | 8 +-- blog/04-functions-java/index.html | 8 +-- blog/05-functions-js/index.html | 8 +-- blog/06-functions-dotnet/index.html | 8 +-- blog/07-functions-python/index.html | 8 +-- blog/08-functions-azure/index.html | 8 +-- blog/09-aca-fundamentals/index.html | 8 +-- blog/11-scaling-container-apps/index.html | 8 +-- blog/12-build-with-dapr/index.html | 8 +-- blog/13-aca-managed-id/index.html | 8 +-- blog/14-dapr-aca-quickstart/index.html | 8 +-- blog/15-microservices-azure/index.html | 8 +-- blog/17-integrate-cosmosdb/index.html | 8 +-- blog/18-cloudmail/index.html | 8 +-- blog/20-events-graph/index.html | 8 +-- blog/21-cloudevents-via-event-grid/index.html | 8 +-- blog/24-aca-dotnet/index.html | 8 +-- blog/25-aca-java/index.html | 8 +-- blog/28-where-am-i/index.html | 8 +-- blog/29-awesome-azd/index.html | 8 +-- blog/29-azure-developer-cli/index.html | 8 +-- blog/archive/index.html | 8 +-- blog/index.html | 8 +-- blog/microservices-10/index.html | 8 +-- blog/page/10/index.html | 8 +-- blog/page/11/index.html | 8 +-- blog/page/12/index.html | 8 +-- blog/page/13/index.html | 8 +-- blog/page/14/index.html | 8 +-- blog/page/15/index.html | 8 +-- blog/page/16/index.html | 8 +-- blog/page/17/index.html | 8 +-- blog/page/18/index.html | 8 +-- blog/page/19/index.html | 8 +-- blog/page/2/index.html | 8 +-- blog/page/20/index.html | 8 +-- blog/page/21/index.html | 8 +-- blog/page/22/index.html | 8 +-- blog/page/23/index.html | 8 +-- blog/page/24/index.html | 8 +-- blog/page/25/index.html | 8 +-- blog/page/26/index.html | 8 +-- blog/page/27/index.html | 8 +-- blog/page/28/index.html | 8 +-- blog/page/29/index.html | 8 +-- blog/page/3/index.html | 8 +-- blog/page/30/index.html | 8 +-- blog/page/31/index.html | 8 +-- blog/page/32/index.html | 8 +-- blog/page/33/index.html | 8 +-- blog/page/34/index.html | 8 +-- blog/page/4/index.html | 8 +-- blog/page/5/index.html | 8 +-- blog/page/6/index.html | 8 +-- blog/page/7/index.html | 8 +-- blog/page/8/index.html | 8 +-- blog/page/9/index.html | 8 +-- blog/serverless-status-post/index.html | 8 +-- blog/students/index.html | 8 +-- blog/tags/30-days-of-serverless/index.html | 8 +-- .../30-days-of-serverless/page/10/index.html | 8 +-- .../30-days-of-serverless/page/11/index.html | 8 +-- .../30-days-of-serverless/page/12/index.html | 8 +-- .../30-days-of-serverless/page/13/index.html | 8 +-- .../30-days-of-serverless/page/14/index.html | 8 +-- .../30-days-of-serverless/page/15/index.html | 8 +-- .../30-days-of-serverless/page/16/index.html | 8 +-- .../30-days-of-serverless/page/17/index.html | 8 +-- .../30-days-of-serverless/page/18/index.html | 8 +-- .../30-days-of-serverless/page/19/index.html | 8 +-- .../30-days-of-serverless/page/2/index.html | 8 +-- .../30-days-of-serverless/page/20/index.html | 8 +-- .../30-days-of-serverless/page/3/index.html | 8 +-- .../30-days-of-serverless/page/4/index.html | 8 +-- .../30-days-of-serverless/page/5/index.html | 8 +-- .../30-days-of-serverless/page/6/index.html | 8 +-- .../30-days-of-serverless/page/7/index.html | 8 +-- .../30-days-of-serverless/page/8/index.html | 8 +-- .../30-days-of-serverless/page/9/index.html | 8 +-- blog/tags/ask-the-expert/index.html | 8 +-- blog/tags/asp-net/index.html | 8 +-- blog/tags/autoscaling/index.html | 8 +-- blog/tags/azd/index.html | 8 +-- blog/tags/azure-container-apps/index.html | 8 +-- .../azure-container-apps/page/10/index.html | 8 +-- .../azure-container-apps/page/11/index.html | 8 +-- .../azure-container-apps/page/12/index.html | 8 +-- .../azure-container-apps/page/13/index.html | 8 +-- .../azure-container-apps/page/14/index.html | 8 +-- .../azure-container-apps/page/15/index.html | 8 +-- .../azure-container-apps/page/16/index.html | 8 +-- .../azure-container-apps/page/17/index.html | 8 +-- .../azure-container-apps/page/18/index.html | 8 +-- .../azure-container-apps/page/19/index.html | 8 +-- .../azure-container-apps/page/2/index.html | 8 +-- .../azure-container-apps/page/20/index.html | 8 +-- .../azure-container-apps/page/3/index.html | 8 +-- .../azure-container-apps/page/4/index.html | 8 +-- .../azure-container-apps/page/5/index.html | 8 +-- .../azure-container-apps/page/6/index.html | 8 +-- .../azure-container-apps/page/7/index.html | 8 +-- .../azure-container-apps/page/8/index.html | 8 +-- .../azure-container-apps/page/9/index.html | 8 +-- blog/tags/azure-developer-cli/index.html | 8 +-- .../azure-developer-cli/page/2/index.html | 8 +-- blog/tags/azure-event-grid/index.html | 8 +-- blog/tags/azure-event-grid/page/2/index.html | 8 +-- blog/tags/azure-event-grid/page/3/index.html | 8 +-- blog/tags/azure-functions/index.html | 8 +-- blog/tags/azure-functions/page/10/index.html | 8 +-- blog/tags/azure-functions/page/11/index.html | 8 +-- blog/tags/azure-functions/page/12/index.html | 8 +-- blog/tags/azure-functions/page/13/index.html | 8 +-- blog/tags/azure-functions/page/14/index.html | 8 +-- blog/tags/azure-functions/page/15/index.html | 8 +-- blog/tags/azure-functions/page/16/index.html | 8 +-- blog/tags/azure-functions/page/2/index.html | 8 +-- blog/tags/azure-functions/page/3/index.html | 8 +-- blog/tags/azure-functions/page/4/index.html | 8 +-- blog/tags/azure-functions/page/5/index.html | 8 +-- blog/tags/azure-functions/page/6/index.html | 8 +-- blog/tags/azure-functions/page/7/index.html | 8 +-- blog/tags/azure-functions/page/8/index.html | 8 +-- blog/tags/azure-functions/page/9/index.html | 8 +-- blog/tags/azure-logic-apps/index.html | 8 +-- blog/tags/azure-logic-apps/page/2/index.html | 8 +-- blog/tags/azure-logic-apps/page/3/index.html | 8 +-- blog/tags/cloud-native/index.html | 8 +-- blog/tags/cloudevents/index.html | 8 +-- blog/tags/custom-connector/index.html | 8 +-- blog/tags/dapr/index.html | 8 +-- blog/tags/dapr/page/10/index.html | 8 +-- blog/tags/dapr/page/11/index.html | 8 +-- blog/tags/dapr/page/12/index.html | 8 +-- blog/tags/dapr/page/13/index.html | 8 +-- blog/tags/dapr/page/14/index.html | 8 +-- blog/tags/dapr/page/15/index.html | 8 +-- blog/tags/dapr/page/16/index.html | 8 +-- blog/tags/dapr/page/2/index.html | 8 +-- blog/tags/dapr/page/3/index.html | 8 +-- blog/tags/dapr/page/4/index.html | 8 +-- blog/tags/dapr/page/5/index.html | 8 +-- blog/tags/dapr/page/6/index.html | 8 +-- blog/tags/dapr/page/7/index.html | 8 +-- blog/tags/dapr/page/8/index.html | 8 +-- blog/tags/dapr/page/9/index.html | 8 +-- blog/tags/devtools/index.html | 8 +-- blog/tags/devtools/page/2/index.html | 8 +-- blog/tags/docker-compose/index.html | 8 +-- blog/tags/dotnet/index.html | 8 +-- blog/tags/dotnet/page/2/index.html | 8 +-- blog/tags/event-hubs/index.html | 8 +-- blog/tags/hacktoberfest/index.html | 8 +-- blog/tags/hello/index.html | 8 +-- blog/tags/hello/page/2/index.html | 8 +-- blog/tags/index.html | 8 +-- blog/tags/java/index.html | 8 +-- blog/tags/javascript/index.html | 8 +-- blog/tags/keda/index.html | 8 +-- blog/tags/logic-apps/index.html | 8 +-- blog/tags/microservices/index.html | 8 +-- blog/tags/microservices/page/10/index.html | 8 +-- blog/tags/microservices/page/11/index.html | 8 +-- blog/tags/microservices/page/2/index.html | 8 +-- blog/tags/microservices/page/3/index.html | 8 +-- blog/tags/microservices/page/4/index.html | 8 +-- blog/tags/microservices/page/5/index.html | 8 +-- blog/tags/microservices/page/6/index.html | 8 +-- blog/tags/microservices/page/7/index.html | 8 +-- blog/tags/microservices/page/8/index.html | 8 +-- blog/tags/microservices/page/9/index.html | 8 +-- blog/tags/microsoft-365/index.html | 8 +-- blog/tags/microsoft-graph/index.html | 8 +-- blog/tags/openapi/index.html | 8 +-- blog/tags/power-platform/index.html | 8 +-- blog/tags/python/index.html | 8 +-- blog/tags/serverless-e-2-e/index.html | 8 +-- blog/tags/serverless-hacks/index.html | 8 +-- blog/tags/serverless-september/index.html | 8 +-- .../serverless-september/page/10/index.html | 8 +-- .../serverless-september/page/11/index.html | 8 +-- .../serverless-september/page/12/index.html | 8 +-- .../serverless-september/page/13/index.html | 8 +-- .../serverless-september/page/14/index.html | 8 +-- .../serverless-september/page/15/index.html | 8 +-- .../serverless-september/page/16/index.html | 8 +-- .../serverless-september/page/17/index.html | 8 +-- .../serverless-september/page/18/index.html | 8 +-- .../serverless-september/page/19/index.html | 8 +-- .../serverless-september/page/2/index.html | 8 +-- .../serverless-september/page/20/index.html | 8 +-- .../serverless-september/page/21/index.html | 8 +-- .../serverless-september/page/22/index.html | 8 +-- .../serverless-september/page/23/index.html | 8 +-- .../serverless-september/page/24/index.html | 8 +-- .../serverless-september/page/25/index.html | 8 +-- .../serverless-september/page/26/index.html | 8 +-- .../serverless-september/page/27/index.html | 8 +-- .../serverless-september/page/28/index.html | 8 +-- .../serverless-september/page/29/index.html | 8 +-- .../serverless-september/page/3/index.html | 8 +-- .../serverless-september/page/30/index.html | 8 +-- .../serverless-september/page/31/index.html | 8 +-- .../serverless-september/page/32/index.html | 8 +-- .../serverless-september/page/33/index.html | 8 +-- .../serverless-september/page/4/index.html | 8 +-- .../serverless-september/page/5/index.html | 8 +-- .../serverless-september/page/6/index.html | 8 +-- .../serverless-september/page/7/index.html | 8 +-- .../serverless-september/page/8/index.html | 8 +-- .../serverless-september/page/9/index.html | 8 +-- blog/tags/serverless/index.html | 8 +-- blog/tags/students/index.html | 8 +-- blog/tags/vscode/index.html | 8 +-- blog/tags/zero-to-hero/index.html | 8 +-- blog/tags/zero-to-hero/page/2/index.html | 8 +-- blog/tags/zero-to-hero/page/3/index.html | 8 +-- blog/tags/zero-to-hero/page/4/index.html | 8 +-- blog/tags/zero-to-hero/page/5/index.html | 8 +-- blog/tags/zero-to-hero/page/6/index.html | 8 +-- blog/tags/zero-to-hero/page/7/index.html | 8 +-- blog/tags/zero-to-hero/page/8/index.html | 8 +-- blog/welcome/index.html | 8 +-- blog/zero2hero-aca-01/index.html | 8 +-- blog/zero2hero-aca-04/index.html | 8 +-- blog/zero2hero-aca-06/index.html | 8 +-- blog/zero2hero-func-02/index.html | 8 +-- blog/zero2hero-func-03/index.html | 8 +-- blog/zero2hero-func-05/index.html | 8 +-- blog/zero2hero-func-07/index.html | 8 +-- calendar/index.html | 8 +-- cnny-2023/Kubernetes-101/index.html | 8 +-- cnny-2023/aks-extensions-addons/index.html | 8 +-- cnny-2023/archive/index.html | 8 +-- cnny-2023/bring-your-app-day-1/index.html | 8 +-- cnny-2023/bring-your-app-day-2/index.html | 8 +-- cnny-2023/bring-your-app-day-3/index.html | 8 +-- cnny-2023/bring-your-app-day-4/index.html | 8 +-- cnny-2023/bring-your-app-day-5/index.html | 8 +-- cnny-2023/building-with-draft/index.html | 8 +-- .../cloud-native-fundamentals/index.html | 8 +-- cnny-2023/cnny-kickoff/index.html | 8 +-- cnny-2023/cnny-wrap-up/index.html | 8 +-- cnny-2023/containers-101/index.html | 8 +-- cnny-2023/explore-options/index.html | 8 +-- cnny-2023/fundamentals-day-1/index.html | 8 +-- cnny-2023/fundamentals-day-2/index.html | 8 +-- cnny-2023/fundamentals-day-3/index.html | 8 +-- cnny-2023/fundamentals-day-4/index.html | 8 +-- cnny-2023/fundamentals-day-5/index.html | 8 +-- cnny-2023/index.html | 8 +-- cnny-2023/microservices-101/index.html | 8 +-- cnny-2023/page/10/index.html | 8 +-- cnny-2023/page/11/index.html | 8 +-- cnny-2023/page/12/index.html | 8 +-- cnny-2023/page/13/index.html | 8 +-- cnny-2023/page/14/index.html | 8 +-- cnny-2023/page/15/index.html | 8 +-- cnny-2023/page/16/index.html | 8 +-- cnny-2023/page/17/index.html | 8 +-- cnny-2023/page/18/index.html | 8 +-- cnny-2023/page/19/index.html | 8 +-- cnny-2023/page/2/index.html | 8 +-- cnny-2023/page/20/index.html | 8 +-- cnny-2023/page/21/index.html | 8 +-- cnny-2023/page/3/index.html | 8 +-- cnny-2023/page/4/index.html | 8 +-- cnny-2023/page/5/index.html | 8 +-- cnny-2023/page/6/index.html | 8 +-- cnny-2023/page/7/index.html | 8 +-- cnny-2023/page/8/index.html | 8 +-- cnny-2023/page/9/index.html | 8 +-- cnny-2023/serverless-containers/index.html | 8 +-- .../tags/30-daysofcloudnative/index.html | 8 +-- .../30-daysofcloudnative/page/10/index.html | 8 +-- .../30-daysofcloudnative/page/11/index.html | 8 +-- .../30-daysofcloudnative/page/12/index.html | 8 +-- .../30-daysofcloudnative/page/13/index.html | 8 +-- .../30-daysofcloudnative/page/14/index.html | 8 +-- .../30-daysofcloudnative/page/15/index.html | 8 +-- .../30-daysofcloudnative/page/16/index.html | 8 +-- .../30-daysofcloudnative/page/2/index.html | 8 +-- .../30-daysofcloudnative/page/3/index.html | 8 +-- .../30-daysofcloudnative/page/4/index.html | 8 +-- .../30-daysofcloudnative/page/5/index.html | 8 +-- .../30-daysofcloudnative/page/6/index.html | 8 +-- .../30-daysofcloudnative/page/7/index.html | 8 +-- .../30-daysofcloudnative/page/8/index.html | 8 +-- .../30-daysofcloudnative/page/9/index.html | 8 +-- cnny-2023/tags/addons/index.html | 8 +-- cnny-2023/tags/aks/index.html | 8 +-- cnny-2023/tags/aks/page/2/index.html | 8 +-- cnny-2023/tags/aks/page/3/index.html | 8 +-- cnny-2023/tags/aks/page/4/index.html | 8 +-- cnny-2023/tags/aks/page/5/index.html | 8 +-- cnny-2023/tags/ask-the-expert/index.html | 8 +-- .../tags/ask-the-expert/page/10/index.html | 8 +-- .../tags/ask-the-expert/page/11/index.html | 8 +-- .../tags/ask-the-expert/page/12/index.html | 8 +-- .../tags/ask-the-expert/page/13/index.html | 8 +-- .../tags/ask-the-expert/page/14/index.html | 8 +-- .../tags/ask-the-expert/page/15/index.html | 8 +-- .../tags/ask-the-expert/page/16/index.html | 8 +-- .../tags/ask-the-expert/page/2/index.html | 8 +-- .../tags/ask-the-expert/page/3/index.html | 8 +-- .../tags/ask-the-expert/page/4/index.html | 8 +-- .../tags/ask-the-expert/page/5/index.html | 8 +-- .../tags/ask-the-expert/page/6/index.html | 8 +-- .../tags/ask-the-expert/page/7/index.html | 8 +-- .../tags/ask-the-expert/page/8/index.html | 8 +-- .../tags/ask-the-expert/page/9/index.html | 8 +-- cnny-2023/tags/azure-dns/index.html | 8 +-- cnny-2023/tags/azure-key-vault/index.html | 8 +-- .../tags/azure-key-vault/page/2/index.html | 8 +-- .../tags/azure-kubernetes-service/index.html | 8 +-- .../page/10/index.html | 8 +-- .../page/11/index.html | 8 +-- .../page/12/index.html | 8 +-- .../page/13/index.html | 8 +-- .../page/14/index.html | 8 +-- .../page/15/index.html | 8 +-- .../page/16/index.html | 8 +-- .../page/17/index.html | 8 +-- .../page/18/index.html | 8 +-- .../page/19/index.html | 8 +-- .../page/2/index.html | 8 +-- .../page/20/index.html | 8 +-- .../page/21/index.html | 8 +-- .../page/3/index.html | 8 +-- .../page/4/index.html | 8 +-- .../page/5/index.html | 8 +-- .../page/6/index.html | 8 +-- .../page/7/index.html | 8 +-- .../page/8/index.html | 8 +-- .../page/9/index.html | 8 +-- .../tags/cloud-native-new-year/index.html | 8 +-- .../cloud-native-new-year/page/2/index.html | 8 +-- .../cloud-native-new-year/page/3/index.html | 8 +-- .../cloud-native-new-year/page/4/index.html | 8 +-- .../cloud-native-new-year/page/5/index.html | 8 +-- cnny-2023/tags/cloud-native/index.html | 8 +-- .../tags/cloud-native/page/10/index.html | 8 +-- .../tags/cloud-native/page/11/index.html | 8 +-- .../tags/cloud-native/page/12/index.html | 8 +-- .../tags/cloud-native/page/13/index.html | 8 +-- .../tags/cloud-native/page/14/index.html | 8 +-- .../tags/cloud-native/page/15/index.html | 8 +-- .../tags/cloud-native/page/16/index.html | 8 +-- cnny-2023/tags/cloud-native/page/2/index.html | 8 +-- cnny-2023/tags/cloud-native/page/3/index.html | 8 +-- cnny-2023/tags/cloud-native/page/4/index.html | 8 +-- cnny-2023/tags/cloud-native/page/5/index.html | 8 +-- cnny-2023/tags/cloud-native/page/6/index.html | 8 +-- cnny-2023/tags/cloud-native/page/7/index.html | 8 +-- cnny-2023/tags/cloud-native/page/8/index.html | 8 +-- cnny-2023/tags/cloud-native/page/9/index.html | 8 +-- cnny-2023/tags/configmaps/index.html | 8 +-- cnny-2023/tags/containers/index.html | 8 +-- cnny-2023/tags/containers/page/2/index.html | 8 +-- cnny-2023/tags/containers/page/3/index.html | 8 +-- cnny-2023/tags/extensions/index.html | 8 +-- cnny-2023/tags/index.html | 8 +-- cnny-2023/tags/ingress/index.html | 8 +-- cnny-2023/tags/ingress/page/2/index.html | 8 +-- cnny-2023/tags/kubernetes/index.html | 8 +-- cnny-2023/tags/kubernetes/page/2/index.html | 8 +-- cnny-2023/tags/kubernetes/page/3/index.html | 8 +-- cnny-2023/tags/kubernetes/page/4/index.html | 8 +-- cnny-2023/tags/kubernetes/page/5/index.html | 8 +-- cnny-2023/tags/microservices/index.html | 8 +-- .../tags/nginx-ingress-controller/index.html | 8 +-- cnny-2023/tags/notary/index.html | 8 +-- cnny-2023/tags/notation/index.html | 8 +-- cnny-2023/tags/persistent-storage/index.html | 8 +-- .../tags/persistent-volume-claims/index.html | 8 +-- cnny-2023/tags/persistent-volumes/index.html | 8 +-- cnny-2023/tags/secrets-management/index.html | 8 +-- cnny-2023/tags/secure-supply-chain/index.html | 8 +-- cnny-2023/tags/service/index.html | 8 +-- cnny-2023/tags/windows/index.html | 8 +-- cnny-2023/tags/workload-identity/index.html | 8 +-- cnny-2023/tags/zero-to-hero/index.html | 8 +-- .../tags/zero-to-hero/page/10/index.html | 8 +-- .../tags/zero-to-hero/page/11/index.html | 8 +-- .../tags/zero-to-hero/page/12/index.html | 8 +-- .../tags/zero-to-hero/page/13/index.html | 8 +-- .../tags/zero-to-hero/page/14/index.html | 8 +-- .../tags/zero-to-hero/page/15/index.html | 8 +-- .../tags/zero-to-hero/page/16/index.html | 8 +-- cnny-2023/tags/zero-to-hero/page/2/index.html | 8 +-- cnny-2023/tags/zero-to-hero/page/3/index.html | 8 +-- cnny-2023/tags/zero-to-hero/page/4/index.html | 8 +-- cnny-2023/tags/zero-to-hero/page/5/index.html | 8 +-- cnny-2023/tags/zero-to-hero/page/6/index.html | 8 +-- cnny-2023/tags/zero-to-hero/page/7/index.html | 8 +-- cnny-2023/tags/zero-to-hero/page/8/index.html | 8 +-- cnny-2023/tags/zero-to-hero/page/9/index.html | 8 +-- cnny-2023/windows-containers/index.html | 8 +-- docs/category/resources/index.html | 8 +-- docs/category/videos/index.html | 8 +-- docs/resources/devtools/index.html | 8 +-- docs/resources/intro/index.html | 8 +-- docs/resources/languages/index.html | 8 +-- docs/resources/serverless/index.html | 8 +-- docs/videos/intro/index.html | 8 +-- .../blogs/2023-09-27/blog-image-1-1.png | Bin 0 -> 268714 bytes .../blogs/2023-09-27/blog-image-1-10.png | Bin 0 -> 3372 bytes .../blogs/2023-09-27/blog-image-1-11.png | Bin 0 -> 65694 bytes .../blogs/2023-09-27/blog-image-1-2.png | Bin 0 -> 158984 bytes .../blogs/2023-09-27/blog-image-1-3.png | Bin 0 -> 53757 bytes .../blogs/2023-09-27/blog-image-1-4.png | Bin 0 -> 68277 bytes .../blogs/2023-09-27/blog-image-1-5.png | Bin 0 -> 211808 bytes .../blogs/2023-09-27/blog-image-1-6.png | Bin 0 -> 48738 bytes .../blogs/2023-09-27/blog-image-1-7.png | Bin 0 -> 81291 bytes .../blogs/2023-09-27/blog-image-1-8.png | Bin 0 -> 159949 bytes .../blogs/2023-09-27/blog-image-1-9.png | Bin 0 -> 2678 bytes .../blogs/2023-09-27/blog-image-2-1.jpeg | Bin 0 -> 31313 bytes .../blogs/2023-09-27/blog-image-2-2.png | Bin 0 -> 69516 bytes .../blogs/2023-09-27/blog-image-2-3.png | Bin 0 -> 71306 bytes index.html | 8 +-- .../30DaysOfServerless/index.html | 8 +-- serverless-september/AskTheExpert/index.html | 8 +-- serverless-september/CloudSkills/index.html | 8 +-- serverless-september/CommunityBuzz/index.html | 8 +-- .../ServerlessHacks/index.html | 8 +-- serverless-september/ZeroToHero/index.html | 8 +-- serverless-september/index.html | 8 +-- sitemap.xml | 2 +- 1108 files changed, 3859 insertions(+), 2869 deletions(-) create mode 100644 30daysofIA/build-your-first-intelligent-app-with-azure-ai-and-aks-1/index.html create mode 100644 30daysofIA/build-your-first-intelligent-app-with-azure-ai-and-aks-2/index.html create mode 100644 30daysofIA/page/10/index.html create mode 100644 30daysofIA/page/11/index.html create mode 100644 30daysofIA/tags/30-days-of-ia/page/10/index.html create mode 100644 30daysofIA/tags/30-days-of-ia/page/11/index.html create mode 100644 30daysofIA/tags/ask-the-expert/page/10/index.html create mode 100644 30daysofIA/tags/ask-the-expert/page/11/index.html create mode 100644 30daysofIA/tags/azure-container-apps/page/10/index.html create mode 100644 30daysofIA/tags/azure-container-apps/page/11/index.html create mode 100644 30daysofIA/tags/azure-cosmos-db/page/10/index.html create mode 100644 30daysofIA/tags/azure-cosmos-db/page/11/index.html create mode 100644 30daysofIA/tags/azure-functions/page/10/index.html create mode 100644 30daysofIA/tags/azure-functions/page/11/index.html create mode 100644 30daysofIA/tags/azure-kubernetes-service/page/10/index.html create mode 100644 30daysofIA/tags/azure-kubernetes-service/page/11/index.html create mode 100644 30daysofIA/tags/azure-openai/page/10/index.html create mode 100644 30daysofIA/tags/azure-openai/page/11/index.html create mode 100644 30daysofIA/tags/community-buzz/page/10/index.html create mode 100644 30daysofIA/tags/community-buzz/page/11/index.html create mode 100644 30daysofIA/tags/fall-for-ia/page/10/index.html create mode 100644 30daysofIA/tags/fall-for-ia/page/11/index.html create mode 100644 30daysofIA/tags/github-actions/page/10/index.html create mode 100644 30daysofIA/tags/github-actions/page/11/index.html create mode 100644 30daysofIA/tags/github-codespaces/page/10/index.html create mode 100644 30daysofIA/tags/github-codespaces/page/11/index.html create mode 100644 30daysofIA/tags/github-copilot/page/10/index.html create mode 100644 30daysofIA/tags/github-copilot/page/11/index.html create mode 100644 30daysofIA/tags/hack-together/page/10/index.html create mode 100644 30daysofIA/tags/hack-together/page/11/index.html create mode 100644 30daysofIA/tags/learn-live/page/10/index.html create mode 100644 30daysofIA/tags/learn-live/page/11/index.html create mode 100644 assets/images/blog-image-1-1-e8cf7713343a8855e8f9b05434148f9d.png create mode 100644 assets/images/blog-image-1-11-96c8a57ad6beab15a2d06d1a0de0ad97.png create mode 100644 assets/images/blog-image-1-2-49fb07b13652bc56001521b3312fae9d.png create mode 100644 assets/images/blog-image-1-3-5b57e295568b12f4b213cb9fc3d39e81.png create mode 100644 assets/images/blog-image-1-4-77065a4476c7a64b6b66d818a4c94a6c.png create mode 100644 assets/images/blog-image-1-5-efe95dbf3756c59403a30a3a9a2b0c7a.png create mode 100644 assets/images/blog-image-1-6-4e63cfb3a15c78ba383a4cdbdc221570.png create mode 100644 assets/images/blog-image-1-7-1d88b437ef046ee6829ccc174cc61f3a.png create mode 100644 assets/images/blog-image-1-8-f2e1dae9a99355268c4fc214e858ca32.png create mode 100644 assets/images/blog-image-2-1-974d3a64c66cbcd6e014779113bc1a4b.jpeg create mode 100644 assets/images/blog-image-2-2-fb13fe692dc47f7d90de361ec6d5b771.png create mode 100644 assets/images/blog-image-2-3-d8cd3a3026fb47d4589f3ee5baa3de5d.png delete mode 100644 assets/js/00984d5d.64d90ffb.js create mode 100644 assets/js/00984d5d.9e3ddb4e.js rename assets/js/{0099b9da.af23b11b.js => 0099b9da.c06b6456.js} (73%) delete mode 100644 assets/js/00ac6f8e.e3c9792d.js create mode 100644 assets/js/00ac6f8e.eb6db636.js rename assets/js/{00ff3ab8.b7b3802c.js => 00ff3ab8.caab9076.js} (73%) create mode 100644 assets/js/010f538e.14e91648.js delete mode 100644 assets/js/010f538e.db12abf4.js create mode 100644 assets/js/016aea77.b7f3c03d.js delete mode 100644 assets/js/016aea77.de8e6ee9.js create mode 100644 assets/js/017fab63.0aa7f6f0.js delete mode 100644 assets/js/017fab63.72123fac.js rename assets/js/{02eacc81.14f76dbc.js => 02eacc81.2a1132c0.js} (89%) create mode 100644 assets/js/03633680.0840e695.js delete mode 100644 assets/js/03633680.715dbb55.js create mode 100644 assets/js/03d787c9.6ad441d7.js create mode 100644 assets/js/046e34f6.40fe7f17.js rename assets/js/{04b79bd3.d727ac5e.js => 04b79bd3.f545816b.js} (72%) rename assets/js/{059e579b.c955f156.js => 059e579b.2f8f1cb2.js} (73%) delete mode 100644 assets/js/05b7df8f.2d4540ea.js create mode 100644 assets/js/05b7df8f.52a14524.js delete mode 100644 assets/js/06db7cdd.2c092a64.js create mode 100644 assets/js/06db7cdd.6d416718.js create mode 100644 assets/js/07104faf.8a9124c8.js rename assets/js/{07865a4c.b82f3be9.js => 07865a4c.66788ab0.js} (72%) delete mode 100644 assets/js/07a9616f.1d585d6d.js create mode 100644 assets/js/07a9616f.42467a5f.js create mode 100644 assets/js/09bb4c1b.60f2b797.js delete mode 100644 assets/js/09bb4c1b.b0d72bcb.js rename assets/js/{0a1ee2df.e815f469.js => 0a1ee2df.1f90514b.js} (72%) create mode 100644 assets/js/0a9d7c60.6d9a0e9d.js delete mode 100644 assets/js/0a9d7c60.74f6035d.js create mode 100644 assets/js/0b3e58d2.0b860b7c.js create mode 100644 assets/js/0ba01ce9.5ea69834.js delete mode 100644 assets/js/0ba01ce9.96711b0f.js create mode 100644 assets/js/0c929776.14e0a845.js delete mode 100644 assets/js/0c929776.f3c0971a.js rename assets/js/{0cc5e40a.a73c5335.js => 0cc5e40a.634e4e94.js} (95%) rename assets/js/{0db3c1f5.6f88c760.js => 0db3c1f5.881974f2.js} (73%) create mode 100644 assets/js/0dcb0a11.0135dcb7.js delete mode 100644 assets/js/0dcb0a11.d27a53ae.js delete mode 100644 assets/js/0dfdf1d3.0805e944.js create mode 100644 assets/js/0dfdf1d3.91931fff.js rename assets/js/{0e655584.9955d0b5.js => 0e655584.62e917d4.js} (89%) create mode 100644 assets/js/0f41348e.36120cad.js delete mode 100644 assets/js/11f27dd8.44e6cbc1.js create mode 100644 assets/js/11f27dd8.9bc2b0f1.js delete mode 100644 assets/js/1280d58f.23a1732b.js create mode 100644 assets/js/1280d58f.6de38eb5.js rename assets/js/{1314fad1.8ed9ee95.js => 1314fad1.46b934d3.js} (72%) delete mode 100644 assets/js/138b7335.082758d9.js create mode 100644 assets/js/138b7335.eb7fa37d.js create mode 100644 assets/js/1414d4c0.42afa8a5.js rename assets/js/{155d8733.7801785a.js => 155d8733.684da0fe.js} (72%) create mode 100644 assets/js/157ee2cb.798aeefb.js delete mode 100644 assets/js/157ee2cb.ffe6e9fa.js rename assets/js/{167f29d7.34a5755a.js => 167f29d7.1f535b6c.js} (73%) create mode 100644 assets/js/16a2dce4.f4990f35.js create mode 100644 assets/js/184bf20b.fdbb968a.js delete mode 100644 assets/js/1864d48a.46bb768d.js create mode 100644 assets/js/1864d48a.72ef5fc2.js create mode 100644 assets/js/1a4e3b56.94112cbb.js delete mode 100644 assets/js/1a4e3b56.ba710a9e.js create mode 100644 assets/js/1b60c5ab.01f23419.js delete mode 100644 assets/js/1b60c5ab.aa889c53.js create mode 100644 assets/js/1b66ef97.007c766a.js delete mode 100644 assets/js/1b66ef97.ceaa36a6.js delete mode 100644 assets/js/1b81fd5d.02474c25.js create mode 100644 assets/js/1b81fd5d.8e6bd07c.js delete mode 100644 assets/js/1ba2ee3a.57da460a.js create mode 100644 assets/js/1ba2ee3a.b9a76ace.js delete mode 100644 assets/js/1c8f664c.3ffc5caa.js create mode 100644 assets/js/1c8f664c.5e59f258.js rename assets/js/{1c94b949.b446e3f0.js => 1c94b949.504650e2.js} (72%) rename assets/js/{1e1d1ee9.63f3fb3f.js => 1e1d1ee9.0ca4cd05.js} (90%) rename assets/js/{1e8313b4.1b2c0ac8.js => 1e8313b4.0af591e7.js} (73%) create mode 100644 assets/js/1fbbba6a.67758059.js rename assets/js/{1fc5a212.b583e52d.js => 1fc5a212.dd7baf7c.js} (94%) rename assets/js/{209bfeb4.1eaf4f44.js => 209bfeb4.b791c5ad.js} (95%) create mode 100644 assets/js/22598362.42a670fd.js delete mode 100644 assets/js/22598362.827d1ce6.js create mode 100644 assets/js/239f6fb3.d9b7909e.js rename assets/js/{24634ed0.1840b116.js => 24634ed0.c21c945a.js} (72%) rename assets/js/{24c13bf4.483b2910.js => 24c13bf4.ab69752a.js} (74%) rename assets/js/{2508adfd.9d6e76bb.js => 2508adfd.16666c3c.js} (74%) rename assets/js/{259b3259.f50a6bd3.js => 259b3259.f38a6caf.js} (74%) create mode 100644 assets/js/259ea4c2.d38b1087.js rename assets/js/{59a2dd7b.c29a6888.js => 260068d7.43396f0e.js} (53%) delete mode 100644 assets/js/260068d7.535a13e7.js create mode 100644 assets/js/27ad03b1.04914118.js rename assets/js/{292dd6ab.2ed66b1f.js => 292dd6ab.96dd7aff.js} (95%) delete mode 100644 assets/js/2ab0497b.158cc56e.js create mode 100644 assets/js/2ab0497b.aedede75.js create mode 100644 assets/js/2bc88290.6bb9e2c5.js delete mode 100644 assets/js/2bc88290.dcadace3.js rename assets/js/{2bcfefbc.79af042e.js => 2bcfefbc.888cb0ce.js} (72%) rename assets/js/{2c72658d.ba4d97e4.js => 2c72658d.366b7f1b.js} (73%) delete mode 100644 assets/js/2d813f9f.0d024571.js create mode 100644 assets/js/2d813f9f.11f4ebb8.js create mode 100644 assets/js/2efdc0bf.9e7e15e4.js delete mode 100644 assets/js/2efdc0bf.a2651cf3.js create mode 100644 assets/js/30b4a191.1a460bb4.js delete mode 100644 assets/js/30b4a191.e753ac3a.js create mode 100644 assets/js/30f26a7a.56f65583.js delete mode 100644 assets/js/30f26a7a.b86dd9c1.js create mode 100644 assets/js/31098de5.8fc37c83.js delete mode 100644 assets/js/31098de5.d19d9574.js rename assets/js/{328b45c7.6b2a492c.js => 328b45c7.6a90e9b2.js} (95%) create mode 100644 assets/js/358af5d5.291d56e2.js delete mode 100644 assets/js/358af5d5.f315e1eb.js create mode 100644 assets/js/367b1faa.1f016f0b.js delete mode 100644 assets/js/367b1faa.5032b946.js rename assets/js/{36e282e0.2f1c327e.js => 36e282e0.9737ed73.js} (95%) create mode 100644 assets/js/36e9edc7.2c82a055.js rename assets/js/{38554ecb.f08d55bd.js => 38554ecb.c19b45fa.js} (72%) create mode 100644 assets/js/388c8f2b.7cfa4692.js delete mode 100644 assets/js/388c8f2b.a450e800.js rename assets/js/{39156339.e3ef9893.js => 39156339.7de5e275.js} (89%) delete mode 100644 assets/js/39200a92.792e3dd0.js create mode 100644 assets/js/39200a92.f4fd3199.js rename assets/js/{39792805.cbc408ac.js => 39792805.4f73ade9.js} (94%) create mode 100644 assets/js/3a1a5f0f.bdfcb3d3.js delete mode 100644 assets/js/3a1a5f0f.da9bb3b3.js create mode 100644 assets/js/3a894f2b.54db83b7.js delete mode 100644 assets/js/3a894f2b.8ae98511.js rename assets/js/{3ae3f3bb.1ef37446.js => 3ae3f3bb.137796ec.js} (73%) create mode 100644 assets/js/3bbe6001.60143556.js delete mode 100644 assets/js/3bbe6001.935c8b71.js create mode 100644 assets/js/3bfb947c.79788038.js delete mode 100644 assets/js/3bfb947c.b5293a1c.js create mode 100644 assets/js/3ccca367.66782563.js delete mode 100644 assets/js/3ccca367.6ff99c10.js delete mode 100644 assets/js/3e04afb2.1426ee55.js create mode 100644 assets/js/3e04afb2.7216156e.js create mode 100644 assets/js/3e675a42.127a98c0.js rename assets/js/{3f09b1d2.945667d7.js => 3f09b1d2.5981be58.js} (73%) rename assets/js/{3f5f9a5a.054d6ab3.js => 3f5f9a5a.59c1cdeb.js} (92%) create mode 100644 assets/js/411bca4b.58d1962b.js create mode 100644 assets/js/42421040.47ac63e8.js delete mode 100644 assets/js/42421040.72f2a6f9.js create mode 100644 assets/js/43949d84.c34a8a35.js create mode 100644 assets/js/460574f3.e2fea3ea.js create mode 100644 assets/js/48a6e2e6.09239c67.js delete mode 100644 assets/js/48a6e2e6.0a891306.js create mode 100644 assets/js/4982e737.9b94071c.js delete mode 100644 assets/js/4988404d.4fdc47ca.js create mode 100644 assets/js/4988404d.d1c1bdfa.js create mode 100644 assets/js/4b3f15eb.bb36c1c5.js delete mode 100644 assets/js/4b3f15eb.f1bc5b6e.js create mode 100644 assets/js/4c23d5c0.228a8dca.js create mode 100644 assets/js/4c3a8a2f.c17067ba.js rename assets/js/{4c3b9235.80f5d621.js => 4c3b9235.46d1b0b8.js} (72%) delete mode 100644 assets/js/4c3daab0.5a2aacb5.js create mode 100644 assets/js/4c3daab0.75136e9f.js delete mode 100644 assets/js/4d20e64b.a465a014.js create mode 100644 assets/js/4d20e64b.e0d96e14.js rename assets/js/{4d46cbe9.5f7cc02a.js => 4d46cbe9.d054f671.js} (92%) create mode 100644 assets/js/4f61748e.873488fb.js rename assets/js/{4f789287.9959b695.js => 4f789287.8ea82e3b.js} (74%) delete mode 100644 assets/js/50b61013.901e79cd.js create mode 100644 assets/js/50b61013.91303c96.js create mode 100644 assets/js/51f200c1.29807d7a.js delete mode 100644 assets/js/51f200c1.df545e82.js create mode 100644 assets/js/51fb1599.7c1dedad.js delete mode 100644 assets/js/51fb1599.7d31782f.js create mode 100644 assets/js/52094cec.588fb7e8.js delete mode 100644 assets/js/52094cec.85fb4be8.js create mode 100644 assets/js/52d53352.99ac5e15.js rename assets/js/{535cf760.097fca42.js => 535cf760.4e49de03.js} (92%) rename assets/js/{5361a0e4.2555ab44.js => 5361a0e4.67242618.js} (73%) create mode 100644 assets/js/53e9efec.60123bce.js rename assets/js/{54625fb2.b6386356.js => 54625fb2.408714cf.js} (72%) create mode 100644 assets/js/55183266.d6c4c584.js rename assets/js/{55404de1.f85c3d88.js => 55404de1.4e969025.js} (72%) rename assets/js/{55c9118e.5b5c147b.js => 55c9118e.a4a0b9ed.js} (73%) create mode 100644 assets/js/565dbc36.8e0e3747.js delete mode 100644 assets/js/57904ffd.08e3b8d5.js create mode 100644 assets/js/57904ffd.9957ff58.js rename assets/js/{587f2372.97f2dd26.js => 587f2372.f4c6dce4.js} (73%) rename assets/js/{599dc25b.eb24a7e7.js => 599dc25b.21b5c5c4.js} (95%) create mode 100644 assets/js/59a2dd7b.281bc225.js rename assets/js/{5ae2fd00.9d39a649.js => 5ae2fd00.c1ff0dc1.js} (74%) delete mode 100644 assets/js/5b107cfc.b7ee69c7.js create mode 100644 assets/js/5b107cfc.e11439d4.js rename assets/js/{5c3ab9e3.fefeff21.js => 5c3ab9e3.52677e48.js} (73%) rename assets/js/{5cab2412.1a05c5b6.js => 5cab2412.16e6edc0.js} (73%) delete mode 100644 assets/js/5cd45a8d.5a5793d7.js create mode 100644 assets/js/5cd45a8d.d9bc1b69.js delete mode 100644 assets/js/5e601653.5b8c60f6.js create mode 100644 assets/js/5e601653.c6820bf7.js create mode 100644 assets/js/5f63ac35.4fce26d7.js delete mode 100644 assets/js/5f63ac35.c9dce46d.js rename assets/js/{604b448d.a2da63bd.js => 604b448d.0e585a19.js} (72%) rename assets/js/{605b2ff8.eb682cc4.js => 605b2ff8.82b69a31.js} (72%) delete mode 100644 assets/js/647961e4.74c55fdf.js create mode 100644 assets/js/647961e4.bd66eaa3.js create mode 100644 assets/js/654f4d54.802219e0.js create mode 100644 assets/js/675273ae.6da76d91.js create mode 100644 assets/js/677498e0.35cb10c2.js delete mode 100644 assets/js/677498e0.5d24dafb.js rename assets/js/{67e85bba.ced7a540.js => 67e85bba.9649a011.js} (94%) rename assets/js/{6a20c0a4.d1c41a56.js => 6a20c0a4.722023e3.js} (96%) rename assets/js/{6ac3c29a.ad250b7a.js => 6ac3c29a.3193dde7.js} (96%) rename assets/js/{6b35c7b1.85494c4c.js => 6b35c7b1.c8498244.js} (92%) create mode 100644 assets/js/6b9868e6.16b7a1fe.js delete mode 100644 assets/js/6b9868e6.213a574f.js delete mode 100644 assets/js/6c2bf19a.02d06e0c.js create mode 100644 assets/js/6c2bf19a.d8a998d3.js rename assets/js/{6c32d8c3.f1b34cea.js => 6c32d8c3.21a56d1b.js} (72%) rename assets/js/{6daa3851.858c1add.js => 6daa3851.10a139a9.js} (72%) delete mode 100644 assets/js/6e52823d.2341626e.js create mode 100644 assets/js/6e52823d.e1847255.js create mode 100644 assets/js/6ed72a5d.5da27e48.js delete mode 100644 assets/js/6ed72a5d.875d7450.js create mode 100644 assets/js/6f46b4a6.62e0f8b1.js delete mode 100644 assets/js/6f46b4a6.c8bfd2ea.js delete mode 100644 assets/js/6f976579.e157b073.js create mode 100644 assets/js/6f976579.fb99fe0b.js rename assets/js/{6fa36db2.c30e6149.js => 6fa36db2.f9495fe1.js} (92%) create mode 100644 assets/js/6fc59d81.5dc3c211.js rename assets/js/{7166f236.0de6d3c9.js => 7166f236.99c6f063.js} (92%) delete mode 100644 assets/js/737c62d3.27e16507.js create mode 100644 assets/js/737c62d3.b70b1402.js create mode 100644 assets/js/74c62dc2.662efd6c.js delete mode 100644 assets/js/75acfd48.4c718337.js create mode 100644 assets/js/75acfd48.d5b75f35.js rename assets/js/{782e7d27.9d79171a.js => 782e7d27.8e79bd2a.js} (73%) delete mode 100644 assets/js/79170c02.e6a1b62c.js create mode 100644 assets/js/79170c02.edfc67cb.js rename assets/js/{7b7d445f.3c029ac8.js => 7b7d445f.7de464c0.js} (73%) rename assets/js/{7bd0b9bf.c472bb0f.js => 7bd0b9bf.d41919bb.js} (74%) create mode 100644 assets/js/7c838aa5.1d0ac9fa.js delete mode 100644 assets/js/7c838aa5.a104c113.js create mode 100644 assets/js/7d20b517.bda2cf93.js rename assets/js/{7d4a7cb7.d43df8ec.js => 7d4a7cb7.7672322c.js} (72%) create mode 100644 assets/js/7dba6303.c2fef4a8.js delete mode 100644 assets/js/7dba6303.dd59dd94.js rename assets/js/{7e3c7a3f.95768422.js => 7e3c7a3f.5f562377.js} (89%) rename assets/js/{807349ce.1a6a0103.js => 807349ce.2f17bbe8.js} (72%) delete mode 100644 assets/js/80aaee8e.3988266a.js create mode 100644 assets/js/80aaee8e.58d3b646.js create mode 100644 assets/js/824b618b.adaffeb7.js delete mode 100644 assets/js/8362f252.4d6de7d4.js create mode 100644 assets/js/8362f252.ca0cce64.js rename assets/js/{8412e158.5679da5f.js => 8412e158.2fe1ac2d.js} (95%) rename assets/js/{8445e33e.fae4094b.js => 8445e33e.c3f178d1.js} (96%) create mode 100644 assets/js/84806527.0bdc75b2.js delete mode 100644 assets/js/84806527.25945221.js rename assets/js/{84d9913c.d075cf3f.js => 84d9913c.0edfd53c.js} (73%) delete mode 100644 assets/js/84e7f16f.95745ba9.js create mode 100644 assets/js/84e7f16f.cf9c5f42.js create mode 100644 assets/js/854ddf44.35dc9a6f.js delete mode 100644 assets/js/854ddf44.d30be8b3.js rename assets/js/{85cba9e9.fc1d5689.js => 85cba9e9.47d358fd.js} (73%) create mode 100644 assets/js/86e280de.6daaa018.js rename assets/js/{87bbf9f8.32218a34.js => 87bbf9f8.7a2199ef.js} (72%) rename assets/js/{88357ceb.f272dfc8.js => 88357ceb.3eb258f5.js} (65%) delete mode 100644 assets/js/88665212.74095adf.js create mode 100644 assets/js/88665212.76f8067e.js rename assets/js/{8b115eee.453a46b1.js => 8b115eee.1be3c929.js} (94%) create mode 100644 assets/js/8b5ab4ba.0875bda3.js delete mode 100644 assets/js/8b5ab4ba.44ab3abd.js delete mode 100644 assets/js/8baf15a9.17bcf8b4.js create mode 100644 assets/js/8baf15a9.28a9b535.js rename assets/js/{8c3d2b1d.340adc88.js => 8c3d2b1d.6b291ca2.js} (72%) create mode 100644 assets/js/8ca49f56.4a007c7d.js delete mode 100644 assets/js/8ca49f56.69bc234a.js rename assets/js/{8cacefc1.1b7a94a3.js => 8cacefc1.23c0a970.js} (73%) rename assets/js/{8e80c8e1.abec1ca4.js => 8e80c8e1.5479c0dd.js} (72%) rename assets/js/{8f4add25.26ce8afd.js => 8f4add25.52cd3e38.js} (94%) create mode 100644 assets/js/8f65450d.b87b892f.js create mode 100644 assets/js/9089e36c.638f873c.js create mode 100644 assets/js/91e58248.d8e381ee.js create mode 100644 assets/js/92851fbb.33fa462d.js delete mode 100644 assets/js/92851fbb.bb33843c.js rename assets/js/{93465d98.11300f8e.js => 93465d98.73c1ce2f.js} (95%) delete mode 100644 assets/js/937ccad8.5a0babb2.js create mode 100644 assets/js/937ccad8.e7f7c42a.js create mode 100644 assets/js/9448e033.517999af.js create mode 100644 assets/js/9575c44b.2c0af6f4.js create mode 100644 assets/js/9648b421.11e4bdea.js rename assets/js/{96d6a951.d9386981.js => 96d6a951.77a40a44.js} (74%) delete mode 100644 assets/js/9762ef78.4d49e9aa.js create mode 100644 assets/js/9762ef78.7739d6af.js rename assets/js/{97a96520.5c957134.js => 97a96520.1dc3826e.js} (72%) rename assets/js/{97c179be.208177bf.js => 97c179be.5049607e.js} (72%) delete mode 100644 assets/js/97e02956.3fa27551.js create mode 100644 assets/js/97e02956.58298c71.js create mode 100644 assets/js/9a9cfa79.62f8a80d.js create mode 100644 assets/js/9aee8dd5.0ea02461.js delete mode 100644 assets/js/9aee8dd5.b79fba90.js create mode 100644 assets/js/9b3a3088.ae8c8777.js delete mode 100644 assets/js/9b60d2ea.a5ae680f.js create mode 100644 assets/js/9b60d2ea.e73b3058.js rename assets/js/{9bc306e8.b47a21be.js => 9bc306e8.917cb835.js} (72%) rename assets/js/{9c17a54a.a27405f9.js => 9c17a54a.c7677255.js} (95%) delete mode 100644 assets/js/9cdc6aec.0e31f359.js create mode 100644 assets/js/9cdc6aec.3387524a.js delete mode 100644 assets/js/9d6ed2de.b2a88d52.js create mode 100644 assets/js/9d6ed2de.b39d2901.js rename assets/js/{9e2f083a.e3f28d8b.js => 9e2f083a.eaf3c8af.js} (73%) rename assets/js/{9f14d4e5.1b829b26.js => 9f14d4e5.215dbdae.js} (73%) create mode 100644 assets/js/9f3b21c4.335fca1f.js create mode 100644 assets/js/9fec92ba.311f5814.js delete mode 100644 assets/js/9fec92ba.b4a0c45f.js rename assets/js/{a0a724b8.735f7453.js => a0a724b8.0640c6c7.js} (95%) rename assets/js/{a11db7eb.6715de61.js => a11db7eb.1fc7a15a.js} (94%) rename assets/js/{a344ef93.95f38638.js => a344ef93.057c2fb3.js} (74%) create mode 100644 assets/js/a356d3a5.9b9b7f7e.js create mode 100644 assets/js/a40c8e08.c73a9ffd.js delete mode 100644 assets/js/a40c8e08.e7b66816.js delete mode 100644 assets/js/a4bab584.56403a36.js create mode 100644 assets/js/a4bab584.fa105b35.js rename assets/js/{a506a500.9e951485.js => a506a500.519b9970.js} (72%) create mode 100644 assets/js/a5551a6a.2ccaa12a.js create mode 100644 assets/js/a60bbbfe.b43c9ef3.js delete mode 100644 assets/js/a60bbbfe.fb7caf18.js create mode 100644 assets/js/a682bc17.920f04db.js rename assets/js/{a6ad4213.5ec66a35.js => a6ad4213.f48cbaaf.js} (73%) delete mode 100644 assets/js/a787e11b.3cc48baa.js create mode 100644 assets/js/a787e11b.db1f02b3.js delete mode 100644 assets/js/a843e963.5e189999.js create mode 100644 assets/js/a843e963.e9dafc2d.js rename assets/js/{a89c2360.fcbd9632.js => a89c2360.e2b43333.js} (90%) delete mode 100644 assets/js/a8dbe756.cc6b938c.js create mode 100644 assets/js/a8dbe756.d6c565de.js rename assets/js/{aa8ea87d.0dae27aa.js => aa8ea87d.9b19d907.js} (74%) rename assets/js/{abf26052.e78788a5.js => abf26052.16b0ff59.js} (72%) rename assets/js/{ac0e80dd.bec66106.js => ac0e80dd.f0e06a6e.js} (75%) rename assets/js/{acfc78c4.3778ff8f.js => acfc78c4.f8739257.js} (95%) create mode 100644 assets/js/adc56754.88e46af7.js delete mode 100644 assets/js/adc56754.c85fd86a.js rename assets/js/{aeb62146.43d79204.js => aeb62146.59789cea.js} (89%) rename assets/js/{afc3e988.b4f9b01c.js => afc3e988.e856ccd5.js} (95%) rename assets/js/{b34c50e5.ef8d6e62.js => b34c50e5.3b84da93.js} (95%) rename assets/js/{b34f0f06.022baf69.js => b34f0f06.55109f43.js} (75%) create mode 100644 assets/js/b62d91fe.140a584e.js rename assets/js/{b6fb65e1.b5e71854.js => b6fb65e1.5e333588.js} (72%) delete mode 100644 assets/js/b70dd8a8.35052561.js create mode 100644 assets/js/b70dd8a8.c323d911.js rename assets/js/{b760ee9a.9cdd7bcd.js => b760ee9a.5414a064.js} (73%) create mode 100644 assets/js/b826954e.813adb62.js delete mode 100644 assets/js/b826954e.e7d16a2c.js rename assets/js/{b860d9a8.94caac31.js => b860d9a8.c31c5887.js} (94%) create mode 100644 assets/js/b95e1cc2.e4128be0.js create mode 100644 assets/js/bbae7a42.9919242e.js delete mode 100644 assets/js/bd2cd00b.5a65b5d3.js create mode 100644 assets/js/bd2cd00b.9171a2a1.js rename assets/js/{bd546f50.e1a8eedc.js => bd546f50.1939fe14.js} (74%) rename assets/js/{c0e56ffe.bf51f60d.js => c0e56ffe.2b3d51b5.js} (73%) rename assets/js/{c0facb88.ca2fd49c.js => c0facb88.0c3864d1.js} (94%) create mode 100644 assets/js/c141a364.3cb72f76.js create mode 100644 assets/js/c1fb8ad8.42c7a1ab.js delete mode 100644 assets/js/c1fb8ad8.71d94677.js rename assets/js/{c2319041.d383eb5c.js => c2319041.56343153.js} (72%) delete mode 100644 assets/js/c2e0ced8.43ee5c30.js create mode 100644 assets/js/c2e0ced8.5c7389e6.js create mode 100644 assets/js/c35448b1.2081a12b.js delete mode 100644 assets/js/c35448b1.b9fc8a3d.js create mode 100644 assets/js/c3591ce3.f7b7a154.js create mode 100644 assets/js/c5298e55.748e52f9.js delete mode 100644 assets/js/c5298e55.d8b0f9ee.js create mode 100644 assets/js/c580cfa2.9427a4b5.js delete mode 100644 assets/js/c580cfa2.d12e48c2.js create mode 100644 assets/js/c58e39ac.86584c6e.js rename assets/js/{c751643e.06c56971.js => c751643e.e3ae0270.js} (74%) rename assets/js/{c7f0b8e3.8bcb8248.js => c7f0b8e3.92f020e7.js} (74%) rename assets/js/{c8c60460.636e88ff.js => c8c60460.07a85934.js} (74%) create mode 100644 assets/js/caa33e0e.73a02ad0.js rename assets/js/{cafc3c94.07cfb138.js => cafc3c94.1e4690ca.js} (74%) create mode 100644 assets/js/cb997589.614d1f64.js delete mode 100644 assets/js/cb997589.92b1d6e0.js create mode 100644 assets/js/cd923978.66869174.js delete mode 100644 assets/js/cd923978.81ea0285.js create mode 100644 assets/js/ce5487d5.b32f6406.js create mode 100644 assets/js/cefbb4e5.5d3f7af4.js delete mode 100644 assets/js/cefbb4e5.dd998262.js delete mode 100644 assets/js/cf50db93.7a6a170b.js create mode 100644 assets/js/cf50db93.ece85fa1.js delete mode 100644 assets/js/cf9f52c6.2fe7da63.js create mode 100644 assets/js/cf9f52c6.ca1e4d5d.js delete mode 100644 assets/js/d0273d46.45fddbb3.js create mode 100644 assets/js/d0273d46.8ebabf4d.js create mode 100644 assets/js/d17a530e.735c308e.js delete mode 100644 assets/js/d17a530e.85d36f48.js rename assets/js/{d22a7198.cb837c7d.js => d22a7198.6455ea1b.js} (73%) create mode 100644 assets/js/d32fff51.fdaaaaa6.js create mode 100644 assets/js/d36d48ac.15749bf4.js delete mode 100644 assets/js/d37b1df8.3c0c8bee.js create mode 100644 assets/js/d37b1df8.fffa0321.js delete mode 100644 assets/js/d436e3ab.1513077e.js create mode 100644 assets/js/d436e3ab.16bd3b08.js rename assets/js/{d60c28fa.f5526b88.js => d60c28fa.9b2aab5c.js} (89%) rename assets/js/{d6e399c5.be2c326f.js => d6e399c5.fd1984b6.js} (72%) rename assets/js/{d70fd83e.e5c7a76e.js => d70fd83e.5d0a7f03.js} (72%) rename assets/js/{d721da33.133cf8a6.js => d721da33.7cdd1c40.js} (72%) rename assets/js/{d72f598b.1c9d82dc.js => d72f598b.b20b3e35.js} (72%) rename assets/js/{d763cd59.e3dfac6d.js => d763cd59.5be11f16.js} (73%) rename assets/js/{db6130e9.4e9c66a5.js => db6130e9.d3ee41a7.js} (92%) create mode 100644 assets/js/dd8667a0.6dae5430.js delete mode 100644 assets/js/dd8667a0.f1b53af5.js create mode 100644 assets/js/dfd81e36.1479e607.js delete mode 100644 assets/js/dfd81e36.ac9c856b.js rename assets/js/{e127deb5.9e5dc6c8.js => e127deb5.1a39730c.js} (92%) create mode 100644 assets/js/e137ac4d.61f2c30e.js delete mode 100644 assets/js/e137ac4d.cfeddd2a.js delete mode 100644 assets/js/e1405df5.5058cd75.js create mode 100644 assets/js/e1405df5.ce5146c7.js delete mode 100644 assets/js/e1daa54d.cae255b5.js create mode 100644 assets/js/e1daa54d.ee16c7ce.js create mode 100644 assets/js/e37c4032.16459f6f.js delete mode 100644 assets/js/e37c4032.1bb77cf7.js rename assets/js/{e49fdeb5.81940f11.js => e49fdeb5.2ef6a200.js} (92%) rename assets/js/{e5530f58.d3d059d5.js => e5530f58.5759edd6.js} (90%) delete mode 100644 assets/js/e57b34d0.e511a36d.js create mode 100644 assets/js/e57b34d0.fdf7b02e.js create mode 100644 assets/js/e645b9de.b8fdff07.js create mode 100644 assets/js/e705147d.cec3bc96.js delete mode 100644 assets/js/e705147d.e3965d85.js create mode 100644 assets/js/e73a1950.3fd7c02b.js create mode 100644 assets/js/e82f66e0.b417c3c4.js delete mode 100644 assets/js/e82f66e0.ecfbb01b.js delete mode 100644 assets/js/e85f9578.ac63cec0.js create mode 100644 assets/js/e85f9578.eec3600f.js create mode 100644 assets/js/e90a85bc.31f0cf99.js delete mode 100644 assets/js/e90a85bc.a37f7587.js delete mode 100644 assets/js/e9f92e0d.606dbcda.js create mode 100644 assets/js/e9f92e0d.811b9008.js create mode 100644 assets/js/ea15e570.24c52116.js delete mode 100644 assets/js/ea15e570.b26e2842.js create mode 100644 assets/js/ea89a27b.76738e76.js rename assets/js/{eb689fda.e9a3cd63.js => eb689fda.ee9aef96.js} (95%) rename assets/js/{ebb76b0d.6bc7151b.js => ebb76b0d.c0390620.js} (74%) create mode 100644 assets/js/ebba27e3.19f47424.js rename assets/js/{ef7c2797.1a6aaf54.js => ef7c2797.1b163603.js} (72%) rename assets/js/{efe016d3.68e5b7b0.js => efe016d3.13a13de6.js} (72%) delete mode 100644 assets/js/f0d25600.1df33c41.js create mode 100644 assets/js/f0d25600.5e8ec391.js rename assets/js/{f0e90793.bc58d687.js => f0e90793.0eac9db8.js} (95%) rename assets/js/{f12c2396.ebc79d26.js => f12c2396.20aed50e.js} (74%) create mode 100644 assets/js/f273b679.29701661.js create mode 100644 assets/js/f38d0525.5e24e53a.js create mode 100644 assets/js/f5611d39.2c8c96bd.js delete mode 100644 assets/js/f5611d39.55a4e068.js create mode 100644 assets/js/f5b7c6a9.716334e9.js delete mode 100644 assets/js/f5b7c6a9.eb909da9.js create mode 100644 assets/js/f6529765.07f885ca.js delete mode 100644 assets/js/f6de2cbe.506318cb.js create mode 100644 assets/js/f6de2cbe.84638608.js create mode 100644 assets/js/f7310d14.17d5fea5.js rename assets/js/{f74cbec7.647f04b0.js => f74cbec7.528f0994.js} (72%) create mode 100644 assets/js/f7b7ef7e.988c1d34.js delete mode 100644 assets/js/f7b7ef7e.e168f09f.js delete mode 100644 assets/js/f7fb9a31.3cf88f51.js create mode 100644 assets/js/f7fb9a31.ae335a1f.js create mode 100644 assets/js/f872f327.23de5059.js delete mode 100644 assets/js/f872f327.9d7eb238.js create mode 100644 assets/js/fa74e77e.245af8f3.js delete mode 100644 assets/js/fa74e77e.723f4085.js delete mode 100644 assets/js/faa3ccc1.2c725132.js create mode 100644 assets/js/faa3ccc1.8f3a55ed.js delete mode 100644 assets/js/fac73890.b25b35b1.js create mode 100644 assets/js/fac73890.c2d4c256.js rename assets/js/{fb5b9b20.a3b5736b.js => fb5b9b20.a2dd7017.js} (95%) rename assets/js/{fb71245d.907b7674.js => fb71245d.5d25eb96.js} (72%) rename assets/js/{fbdbf422.b27db388.js => fbdbf422.4cd21b16.js} (73%) delete mode 100644 assets/js/fd422a34.52ab7325.js create mode 100644 assets/js/fd422a34.a43285ba.js create mode 100644 assets/js/fd4ba951.23d19ee6.js delete mode 100644 assets/js/fd4ba951.a923f798.js delete mode 100644 assets/js/main.0f5b4036.js create mode 100644 assets/js/main.e96c061e.js rename assets/js/{main.0f5b4036.js.LICENSE.txt => main.e96c061e.js.LICENSE.txt} (100%) delete mode 100644 assets/js/runtime~main.877c649b.js create mode 100644 assets/js/runtime~main.ee8f3c68.js create mode 100644 img/fallforia/blogs/2023-09-27/blog-image-1-1.png create mode 100644 img/fallforia/blogs/2023-09-27/blog-image-1-10.png create mode 100644 img/fallforia/blogs/2023-09-27/blog-image-1-11.png create mode 100644 img/fallforia/blogs/2023-09-27/blog-image-1-2.png create mode 100644 img/fallforia/blogs/2023-09-27/blog-image-1-3.png create mode 100644 img/fallforia/blogs/2023-09-27/blog-image-1-4.png create mode 100644 img/fallforia/blogs/2023-09-27/blog-image-1-5.png create mode 100644 img/fallforia/blogs/2023-09-27/blog-image-1-6.png create mode 100644 img/fallforia/blogs/2023-09-27/blog-image-1-7.png create mode 100644 img/fallforia/blogs/2023-09-27/blog-image-1-8.png create mode 100644 img/fallforia/blogs/2023-09-27/blog-image-1-9.png create mode 100644 img/fallforia/blogs/2023-09-27/blog-image-2-1.jpeg create mode 100644 img/fallforia/blogs/2023-09-27/blog-image-2-2.png create mode 100644 img/fallforia/blogs/2023-09-27/blog-image-2-3.png diff --git a/30daysofIA/archive/index.html b/30daysofIA/archive/index.html index c6d6d9b05d..120883242d 100644 --- a/30daysofIA/archive/index.html +++ b/30daysofIA/archive/index.html @@ -14,13 +14,13 @@ - - + +
-
Skip to main content

Archive

Archive

- - +
Skip to main content

Archive

Archive

+ + \ No newline at end of file diff --git a/30daysofIA/atom.xml b/30daysofIA/atom.xml index 9596310548..1e0ba630b6 100644 --- a/30daysofIA/atom.xml +++ b/30daysofIA/atom.xml @@ -59,6 +59,58 @@ + + <![CDATA[2-1. Build Your First Intelligent App with Azure AI and AKS-1]]> + https://azure.github.io/Cloud-Native/30daysofIA/build-your-first-intelligent-app-with-azure-ai-and-aks-1 + + 2023-09-22T09:00:00.000Z + + Delve into creating an Intelligent App that leverages Azure AI Vision to analyze images and extract data. Develop an API to perform optical character recognition (OCR) on uploaded images and testing this API locally.

What We'll Cover:

  • Understanding Azure AI Vision and Azure Kubernetes Service
  • Build a Python Web API to perform OCR
  • Test the API locally

image depicting an Intelligent App using AI

Jumpstart Your AI Journey: Building Your First Intelligent App with Azure AI and AKS (1)

An Intelligent App is a software application that uses artificial intelligence (AI), such as machine learning (ML), to extract meaningful insights, enabling users to make informed decisions based on the extracted text, data, sound, images, and more.

Intelligent Apps can employ advanced algorithms and models to recognize objects, patterns, and features within images, allowing them to perform tasks like object identification, image classification, facial recognition, and image segmentation.

To explore the power of Intelligent Apps, let’s build a Python app that performs optical character recognition (OCR) on images containing numbers and performs aggregate functions on the extracted numbers, like sum and average. We’ll leverage Azure AI Services for the OCR functionality and Azure Kubernetes Service (AKS) to develop, publish, and maintain a cloud-based, scalable app in Azure.

Let’s get started!

Understanding Azure AI Vision and Azure Kubernetes Service

Azure AI Vision is a cloud-based service that offers a range of advanced computer vision capabilities enabling applications to interpret content, such as images and videos, using ML and other AI algorithms. Azure computer vision lets users integrate powerful vision features into their projects without ML experience.

OCR is one of the critical functionalities within Azure vision service, allowing applications to extract text from images. Within Azure AI Vision service, OCR offers several features, including text extraction, multilingual support, layout analysis of documents, and integration with Azure services, such as Azure Functions and Azure Machine Learning.

info

Watch the intelligent apps webinar on Driving Business Value by Modernizing with Cloud-Native & AI with Microsoft and Forrester on September 26.

AKS is Microsoft Azure’s container orchestration platform that enables us to quickly and efficiently deploy, manage, and scale containerized applications, such as our Intelligent App.

Let’s explore how to leverage these tools to build an Intelligent App that uses Azure AI Vision to analyze images and extract data.

Prerequisites

To follow this tutorial, ensure you have the following:

For a preview of this final project, take a look at the complete project code.

Building the API with Azure AI Vision Service

First, log in to your Azure account and navigate to the Azure Portal.

Click Create a resource and search for “resource group.” Create a new resource group named computer-vision.

image in Azure Portal of creating a new resource group

Return to the Azure Portal home page and click Create a resource.

image in Azure Portal of creating a new resource

Search for “computer vision” and select it from the results. Click Create.

image of searching for resource group in Azure Portal

Clicking Create displays the Create Computer Vision wizard. Select the resource group you just created as the Resource group from the dropdown. Enter a resource name similar to what is seen in the screenshot below. Note that this must be a unique name so choose one that is not currently in use. Select Standard S1 as the pricing tier and check the Responsible use of AI box in the field at the bottom.

Image of selecting the Responsible use of AI box

Image of project details

Click Review + Create, then click Create.

Next, get the API key and endpoint from the Azure AI services multi-service account you created. Once the resource is created, navigate to the resource details page. Under Resource Management, select Keys and Endpoint. Once on the Keys and Endpoint page, you’ll find the Key 1 and Endpoint values. These credentials are necessary to access the Azure AI APIs.

image of selecting Keys and Endpoint

second image of selecting Keys and Endpoint

Configuring the Local Environment Variables

Next, we need to define a local environment variable file. This convenient way to store our API keys in a simple text file enables us to manage sensitive information and separate configuration from our API code.

If you haven’t done so already, clone the starter project template from GitHub into your local computer. Open the starter project template in a code editor and create a new .env file in the root folder.

Note: Root folder in this case is the “/Microsoft_Series17-18_Code/intelligent-app-before" folder.

Add the following key-value pairs to the file, remembering to replace the placeholder values with those you obtained from the previous step.

VISION_KEY=<THE-KEY-1-VALUE-FROM-YOUR-AZURE-AI-SERVICE>
VISION_ENDPOINT=<THE-ENDPOINT-VALUE-FROM-YOUR-AZURE-AI-SERVICE>

Reviewing the Quickstart Code

Let’s review the app.py file in the starter project template.

The app.py file works as the entry point for the Flask application. It is responsible for initializing the web server, listening for incoming requests, dispatching them to the appropriate route handlers, and generating responses.

Below is the code contained in the app.py file:

import os
import json

from flask import Flask, request
from flask_restful import Resource, Api
from werkzeug.utils import secure_filename

app = Flask(__name__,
static_url_path='',
static_folder='static/files')

api = Api(app)

app = Flask(__name__)

app.config['UPLOAD_FOLDER'] = 'files'

api = Api(app)

class UploadHandler(Resource):

def allowed_file(self, filename):
return '.' in filename and \
filename.rsplit('.', 1)[1].lower() in {'png'}

def post(self):
form = request.form.to_dict()

if 'file' not in request.files:
return json.dumps({ "success": False, "error": "No file part"})

file = request.files.get("file")
if file and self.allowed_file(file.filename):
filename = secure_filename(file.filename)
upload_folder = "static/files"
if not os.path.exists(upload_folder):
os.makedirs(upload_folder)
local_file_path = os.path.join(upload_folder, filename)
file.save(local_file_path)

return f"File {filename} uploaded successfully to folder: {upload_folder}"

api.add_resource(UploadHandler, "/")

if __name__ == '__main__':
app.run(debug=True)

The UploadHandler class above is defined as a Flask RESTful resource to handle file uploads. The class contains two methods:

  • The allowed_file method checks whether the file extension is allowed for upload. In this case, only .png files are allowed.

  • The post method handles HTTP POST requests for file uploads. It saves the uploaded .png file to the static/files folder.

Finally, the application returns a text response informing us that the file has been uploaded successfully.

Implementing the REST API in Python

To implement image analysis in your REST API, open the starter project template in your terminal, create a virtual environment, and activate it.

Next, install the Azure AI Vision SDK:

pip install azure-ai-vision

Then, add the following line to the requirements.txt file to include the azure-ai-vision package:

azure-ai-vision==0.13.0b1

Now, create an ocr_helper.py file in the project’s root folder. This file is a Python module that provides functions for processing images using OCR. Add the following code to the file:

import os
from statistics import median
from decimal import Decimal
import azure.ai.vision as sdk

def process_ocr(source_image):
service_options = sdk.VisionServiceOptions(os.environ["VISION_ENDPOINT"],
os.environ["VISION_KEY"])

vision_source = sdk.VisionSource(filename=source_image)

analysis_options = sdk.ImageAnalysisOptions()

analysis_options.features = (
sdk.ImageAnalysisFeature.CAPTION |
sdk.ImageAnalysisFeature.TEXT
)

analysis_options.language = "en"

analysis_options.gender_neutral_caption = True

image_analyzer = sdk.ImageAnalyzer(service_options, vision_source, analysis_options)

result = image_analyzer.analyze()

ocr_result = get_ocr_result(result)

return ocr_result

def get_ocr_result(result):
string_list = []

if result.reason != sdk.ImageAnalysisResultReason.ANALYZED:
return sdk.ImageAnalysisErrorDetails.from_result(result)
else:
if result.text is not None:
for line in result.text.lines:
for word in line.words:
string_list.append(word.content)

number_list = convert_to_decimal_list(string_list)

aggregate_result = aggregate_operations(number_list)

return {
"aggregate_result": aggregate_result,
"numbers_read": string_list
}

def convert_to_decimal_list(string_list):
return list(map(Decimal, string_list))

def aggregate_operations(numbers):
result = {
'sum': sum(numbers),
'average': sum(numbers) / len(numbers),
'median': median(numbers),
'min': min(numbers),
'max': max(numbers)
`}`
`return result`

This module uses the azure-ai-vision package to analyze images, including capturing captions and extracting text from the image. The process_ocr function inputs an image file and performs the OCR analysis. The module is a convenient tool for analyzing text-based data in pictures and performing numerical computations based on that data.

Let’s review the different components of the ocr_helper.py module listed above and explore how its functions help our Intelligent App process images and perform aggregate operations:

  • The process_ocr function takes the parameter source_image, which is the path of the image to be processed. The function then initializes the VisionServiceOptions using environment variables VISION_ENDPOINT and VISION_KEY to connect to the Azure AI Vision API.
  • The process_ocr function creates a VisionSource object with the specified source_image file name. ImageAnalysisOptions specify the features to be analyzed, including CAPTION and TEXT. The language is set to English ("en"), and gender-neutral captions are enabled.
  • Finally, an ImageAnalyzer object is created with the service options, vision source, and analysis options. The image is then analyzed using the image_analyzer.analyze method to retrieve OCR results. The recognized text from the OCR result is extracted, with numerical values stored in the string_list variable.
  • The convert_to_decimal_list function converts the list of strings to a list of decimal numbers, which helps process numeric values extracted from the text.
  • The aggregate_operations function calculates various aggregate statistics (sum, average, median, minimum, maximum) from a list of numbers and returns the results in a dictionary.

Note that you must have the appropriate credentials (VISION_KEY) and endpoint (VISION_ENDPOINT) configured for the Azure AI Vision API to use this module.

Finally, we must modify the app.py file so our code can use the process_ocr function of the ocr_helper.py file.

Add the following import statement to the app.py file:

from ocr_helper import process_ocr

Then, replace this line:

return f"File {filename} uploaded successfully to folder: {upload_folder}"

With these two lines:

aggregates = process_ocr(local_file_path)
return json.dumps(aggregates, default=str)

Doing so allows our application to perform aggregate operations (sum, average, median, minimum, maximum) of any numeric values found in the text and return the operation’s result as a JSON response.

Running the Intelligent App Locally

Our final goal is to deploy the sample app to AKS, which will run the app in a container. However, as a good practice, let’s run the app in a container locally before moving it to AKS. Doing so ensures that our application and its dependencies are encapsulated and can be run consistently across different environments.

Deploying to Docker Desktop

For our application to run on Docker, it needs two additional files: a Dockerfile and docker-compose.yml. A Dockerfile creates a single container image by specifying the steps to build it, and a docker-compose.yml file manages multiple containers as a cohesive application, defining their configurations and relationships. Dockerfile builds the image, while docker-compose.yml orchestrates multi-container applications.

In the project root folder, add a file named Dockerfile with the content below:

# syntax=docker/dockerfile:1

FROM python:3.8-slim-buster

WORKDIR /intelligentapp

COPY requirements.txt requirements.txt
RUN pip3 install -r requirements.txt

RUN pip install debugpy

COPY . .

CMD [ "python3", "-m" , "flask", "run", "--host=0.0.0.0"]

Now create a file named docker-compose.yml file in the project’s root folder with the following code, replacing the VISION_KEY and the VISION_ENDPOINT according to the environment variables you configured earlier:

version: '3.8'
services:
intelligentapp:
build:
context: .
dockerfile: Dockerfile
image: intelligent-app
ports:
- 5000:5000
container_name: intelligent-app
environment:
- VISION_KEY=<THE-KEY-1-VALUE-FROM-YOUR-AZURE-AI-SERVICE>
- VISION_ENDPOINT=<THE-ENDPOINT-VALUE-FROM-YOUR-AZURE-AI-SERVICE>

Then, run the following command in the terminal to build the image and start a container for the Intelligent App services defined in the docker-compose.yml file:

docker-compose up --build --force-recreate

Next, open a new terminal and run the command below to list the image deployed to your local Docker:

docker images

docker images

Testing the Intelligent App Locally

Now, we’ll test the Intelligent App’s functionality using the following test image:

test image to test functionality

This image is provided as sample1.png in the sample app source files, so you can easily use it in the next step.

The Intelligent App will read the image above using OCR to extract numbers in the image and perform five aggregate functions:

  • Sum
  • Average
  • Median
  • Min
  • Max

To test the API, open Postman and fill out the fields as follows:

  • URL: http://localhost:5000/
  • Method: POST
  • Body:
    • Form-data
      • Key: File — Click the right end of the Key field and select File from the dropdown list.
      • Value: Click Select Files, then select the sample1.png file provided in the sample code.

image of selecting the sample file

Now click the Send button and review the result body:

"{\"aggregate_result\": {\"sum\": \"25821\", \"average\": \"5164.2\", \"median\": \"5622\", \"min\": \"1447\", \"max\": \"9802\"}, \"numbers_read\": [\"3049\", \"5901\", \"5622\", \"1447\", \"9802\"]}"

As we can see, the app running on a local container returned the correct results based on the sample image. So, we’re ready to prepare our Azure resources to run the Intelligent App on AKS.

Exercise

In this article, we explored the creation of an Intelligent App that leverages Azure AI Vision to analyze images and extract data. We learned how to build a Python Web API to perform OCR on uploaded images and subsequently test this API locally.

Head to the next part, to deploy this API via Azure Kubernetes Service.

]]>
+ + It's 30DaysOfIA + https://azure.github.io/Cloud-Native/Fall-For-IA/ + + + + + + + + + + + + + + + +
+ + <![CDATA[2-2. Build Your First Intelligent App with Azure AI and AKS-2]]> + https://azure.github.io/Cloud-Native/30daysofIA/build-your-first-intelligent-app-with-azure-ai-and-aks-2 + + 2023-09-22T09:00:00.000Z + + In the last article, we created an Intelligent App that leverages Azure AI Vision to analyze images and extract data. Develop an API to perform optical character recognition (OCR) on uploaded images and testing this API locally.

In this article, we will deploy the web API to the cloud platform using Azure Kubernetes Service.

What We'll Cover:

  • Understanding Azure AI Vision and Azure Kubernetes Service
  • Build a Python Web API to perform OCR
  • Test the API locally

image depicting an Intelligent App using AI

Jumpstart Your AI Journey: Building Your First Intelligent App with Azure AI and AKS (2)

In the previous article we explored the creation of an Intelligent App that leverages Azure AI Vision to analyze images and extract data. We learned how to build a Python Web API to perform OCR on uploaded images and subsequently test this API locally.

In this article we will use Azure Kubernetes Service (AKS) to develop, publish, and maintain our app in the cloud on Azure.

Let’s get started!

Prerequisites

To follow this tutorial, ensure you have completed Jumpstart Your AI Journey: Building Your First Intelligent App with Azure AI and AKS (1).

Pushing a Container Image to Azure Container Registry (ACR)

To start, open your CLI or terminal and type the following command:

az login

Follow the instructions displayed on your browser to enter your Azure credentials.

Once authenticated, you’ll initiate a secure connection between your local environment and Azure. This process grants you access to cloud services and resources.

Next, type the command below in your terminal to set up a new Azure Container Registry (ACR) to store your container images:

az acr create --resource-group computer-vision --name <name-of-azure-container-registry> --sku Basic

Remember to replace <name-of-azure-container-registry> with your container registry name. The name must be unique within Azure and comply with these rules.

The command above creates an Azure Container Registry (ACR) in the computer-vision resource group under the Basic SKU. This ACR is your secure and private repository for storing container images within Azure.

Next, log in to the registry with the following command:

az acr login -n <name-of-azure-container-registry>

The az acr login command above lets you securely authenticate and access the specified ACR without providing their Azure credentials each time.

Now, run the following command in your terminal. It will display the endpoint URL to log in to and interact with the ACR for pushing and pulling container images.

az acr show --name <name-of-azure-container-registry> --query loginServer --output table

This returns an endpoint URL as follows:

Result
----------------------------------
<name-of-azure-container-registry>.azurecr.io

Now, run the following command to show all container images, their repository, tags, and size:

docker images
REPOSITORYTAGIMAGE IDCREATEDSIZE
intelligent-applatesta7bf9f75361716 hours ago197MB

Tags are necessary to push Docker images to a remote registry like Azure Container Registry. They also let you differentiate between different versions of the same image and upload or download the one you want.

Run the following command to tag your Docker image:

docker tag intelligent-app <name-of-azure-container-registry>.azurecr.io/intelligent-app:v1

Then, run the docker images command again to check your tagged image:

docker images
REPOSITORYTAGIMAGE IDCREATEDSIZE
intelligent-applatestc52168039265About a minute ago197MB
<name-of-azure-container-registry>.azurecr.io/intelligent-appv1c52168039265About a minute ago197MB

Now run the following command so Docker can securely upload the image to your Azure Container Registry:

docker push <name-of-azure-container-registry>.azurecr.io/intelligent-app:v1

Once we've deployed the image to the container registry, AKS can access it during deployment.

info

Watch Episode 03 for the Learn Live session to experience a guided session on how to build, test and deploy an end-to-end intelligent app solution.

Deploying the Intelligent App on Azure Kubernetes Service (AKS)

Before we deploy our Intelligent App to AKS, we need to provision an AKS cluster and define Kubernetes manifests.

To provision an AKS cluster to host our application, we specify the desired configurations for the cluster, such as the number of nodes, node size, and networking options. But first, download and install the Kubernetes command-line tools (kubectl), a client credential plugin implementing Azure authentication:

az aks install-cli

If you’re using Linux, review this tutorial. Then run the following:

sudo az aks install-cli

Next, run the following command in your terminal to enable access to the networking-related resources and services provided by the `Microsoft.Network`` namespace in Azure:

az provider register --namespace Microsoft.Network

Now, we must create an AKS cluster. Run the following command to create an AKS cluster named aks-intelligent-app in the computer-vision resource group.

az aks create --resource-group computer-vision --name aks-intelligent-app --node-count 1 --generate-ssh-keys

The command above specifies the target resource group: computer-vision. The node pool is configured with one virtual machine (VM), and the secure shell (SSH) keys for secure node access are generated automatically.

Next, run the following command to update the AKS cluster you created by attaching it to your ACR. Doing so allows the AKS cluster to pull container images from the specified ACR when deploying workloads to the cluster.

az aks update -n aks-intelligent-app -g computer-vision --attach-acr <name-of-azure-container-registry>

Then, run the command below to configure kubectl to work with our AKS cluster in the computer-vision resource group.

az aks get-credentials --resource-group computer-vision --name aks-intelligent-app

The command above retrieves the necessary credentials and context information required for kubectl to communicate with the AKS cluster.

We still have to define the Kubernetes manifests written in YAML, describing the desired state of our application deployment, including containers, networking, and scaling rules. We’ll prepare these manifests, including Deployment and Service configurations, to define how our application should be deployed and exposed.

Create a folder named Deployment in the project root directory. Next, create two files in the deployment folder: deployment.yml and service.yml.

Add the following configuration to the deployment.yml file, replacing the <name-of-azure-container-registry> placeholder with your registry’s name:

apiVersion: apps/v1
kind: Deployment
metadata:
name: intelligent-app
spec:
replicas: 1
selector:
matchLabels:
app: intelligent-app
template:
metadata:
labels:
app: intelligent-app
spec:
nodeSelector:
kubernetes.io/os: linux
containers:
- name: intelligent-app
image: <name-of-azure-container-registry>.azurecr.io/intelligent-app:v1
resources:
limits:
memory: 512Mi
cpu: "1"
requests:
memory: 256Mi
cpu: "0.2"
ports:
- containerPort: 5000
env:
- name: FLASK_DEBUG
value: "1"
- name: VISION_KEY
value: <THE-KEY-1-VALUE-FROM-YOUR-AZURE-AI-SERVICE>
- name: VISION_ENDPOINT
value: <THE-ENDPOINT-VALUE-FROM-YOUR-AZURE-AI>

Additionally, edit the VISION_KEY and VISION_ENDPOINT environment variables above according to the API key and endpoint of your Azure AI Services instance.

Then, add the following configuration to the service.yml file:

apiVersion: v1
kind: Service
metadata:
name: intelligent-app-service
spec:
type: LoadBalancer
ports:
- protocol: TCP
port: 80
targetPort: 5000
name: port5000
selector:
app: intelligent-app

Now, we’ll deploy our application using kubectl. Applying the Kubernetes manifests creates the necessary resources and deploys our containerized application to the AKS cluster.

First, change the terminal to the deployment folder:

cd Deployment 

Then, run the following command to create or update Kubernetes resources defined in the deployment.yml file:

kubectl apply -f deployment.yml

Create a Kubernetes Service resource in a Kubernetes cluster based on the configuration defined in the service.yml file using the code below:

kubectl apply -f service.yml

Once you’ve applied the resource definition and the service configuration contained in the deployment.yml and the service.yml files, open the aks-intelligent-app Kubernetes Service in the Azure Portal, select Workloads under Kubernetes resources on the sidebar, and find the deployment named intelligent-app. It must have the status “Ready 1/1”. If you encounter an issue with this status, check out these troubleshooting resources.

image of selecting the deployment in Azure Portal

Testing the Intelligent App on AKS

To test the app on AKS, first, run the command below:

kubectl get services

This command lists the Services and their corresponding details, including the Service name, cluster IP address, external IP, and ports.

NAMETYPECLUSTER-IPEXTERNAL-IPPORT(S)AGE
intelligent-app-serviceLoadBalancer10.0.77.6020.121.76.15380:30936/TCP47s
kubernetesClusterIP10.0.0.1<none>443/TCP14m

The output above shows a Kubernetes Service named intelligent-app-service with a type set to LoadBalancer. It’s reachable from within the cluster using the cluster IP 10.0.77.60 and accessible externally via the external IP 20.121.76.153 on port 80 (mapped to port 30936).

Note: Your set of IP addresses will differ. Remember to use your unique external IP address when testing with Postman.

To test the deployed app, go to Postman, replace the URL with the external IP of the Kubernetes Service you just deployed, and click Send:

mage of sending the app in Postman

As we can see, our Intelligent App has successfully deployed to AKS and is functioning on the cloud as expected.

Exercise

Next Steps

In this two-part article, we explored the creation of an Intelligent App that leverages Azure AI Vision to analyze images and extract data. We learned how to build a Python Web API to perform OCR on uploaded images and subsequently deploy this API via Azure Kubernetes Service.

Besides OCR and Image Analysis, you can continue exploring Azure’s vast array of services and experiment further with Azure AI and AKS by applying various practical uses to your Intelligent Apps, including natural language processing, speech recognition and synthesis, sentiment analysis for customer feedback, and automated content moderation.

]]>
+ + It's 30DaysOfIA + https://azure.github.io/Cloud-Native/Fall-For-IA/ + + + + + + + + + + + + + + + +
<![CDATA[1-4. How Digital Natives leverage Generative AI]]> https://azure.github.io/Cloud-Native/30daysofIA/how-digital-natives-leverage-generative-ai diff --git a/30daysofIA/build-your-first-intelligent-app-with-azure-ai-and-aks-1/index.html b/30daysofIA/build-your-first-intelligent-app-with-azure-ai-and-aks-1/index.html new file mode 100644 index 0000000000..bf4f8172b6 --- /dev/null +++ b/30daysofIA/build-your-first-intelligent-app-with-azure-ai-and-aks-1/index.html @@ -0,0 +1,26 @@ + + + + + +2-1. Build Your First Intelligent App with Azure AI and AKS-1 | Build Intelligent Apps On Azure + + + + + + + + + + + + + + +
+
Skip to main content

2-1. Build Your First Intelligent App with Azure AI and AKS-1

· 12 min read
It's 30DaysOfIA

Delve into creating an Intelligent App that leverages Azure AI Vision to analyze images and extract data. Develop an API to perform optical character recognition (OCR) on uploaded images and testing this API locally.

What We'll Cover:

  • Understanding Azure AI Vision and Azure Kubernetes Service
  • Build a Python Web API to perform OCR
  • Test the API locally

image depicting an Intelligent App using AI

Jumpstart Your AI Journey: Building Your First Intelligent App with Azure AI and AKS (1)

An Intelligent App is a software application that uses artificial intelligence (AI), such as machine learning (ML), to extract meaningful insights, enabling users to make informed decisions based on the extracted text, data, sound, images, and more.

Intelligent Apps can employ advanced algorithms and models to recognize objects, patterns, and features within images, allowing them to perform tasks like object identification, image classification, facial recognition, and image segmentation.

To explore the power of Intelligent Apps, let’s build a Python app that performs optical character recognition (OCR) on images containing numbers and performs aggregate functions on the extracted numbers, like sum and average. We’ll leverage Azure AI Services for the OCR functionality and Azure Kubernetes Service (AKS) to develop, publish, and maintain a cloud-based, scalable app in Azure.

Let’s get started!

Understanding Azure AI Vision and Azure Kubernetes Service

Azure AI Vision is a cloud-based service that offers a range of advanced computer vision capabilities enabling applications to interpret content, such as images and videos, using ML and other AI algorithms. Azure computer vision lets users integrate powerful vision features into their projects without ML experience.

OCR is one of the critical functionalities within Azure vision service, allowing applications to extract text from images. Within Azure AI Vision service, OCR offers several features, including text extraction, multilingual support, layout analysis of documents, and integration with Azure services, such as Azure Functions and Azure Machine Learning.

info

Watch the intelligent apps webinar on Driving Business Value by Modernizing with Cloud-Native & AI with Microsoft and Forrester on September 26.

AKS is Microsoft Azure’s container orchestration platform that enables us to quickly and efficiently deploy, manage, and scale containerized applications, such as our Intelligent App.

Let’s explore how to leverage these tools to build an Intelligent App that uses Azure AI Vision to analyze images and extract data.

Prerequisites

To follow this tutorial, ensure you have the following:

For a preview of this final project, take a look at the complete project code.

Building the API with Azure AI Vision Service

First, log in to your Azure account and navigate to the Azure Portal.

Click Create a resource and search for “resource group.” Create a new resource group named computer-vision.

image in Azure Portal of creating a new resource group

Return to the Azure Portal home page and click Create a resource.

image in Azure Portal of creating a new resource

Search for “computer vision” and select it from the results. Click Create.

image of searching for resource group in Azure Portal

Clicking Create displays the Create Computer Vision wizard. Select the resource group you just created as the Resource group from the dropdown. Enter a resource name similar to what is seen in the screenshot below. Note that this must be a unique name so choose one that is not currently in use. Select Standard S1 as the pricing tier and check the Responsible use of AI box in the field at the bottom.

Image of selecting the Responsible use of AI box

Image of project details

Click Review + Create, then click Create.

Next, get the API key and endpoint from the Azure AI services multi-service account you created. Once the resource is created, navigate to the resource details page. Under Resource Management, select Keys and Endpoint. Once on the Keys and Endpoint page, you’ll find the Key 1 and Endpoint values. These credentials are necessary to access the Azure AI APIs.

image of selecting Keys and Endpoint

second image of selecting Keys and Endpoint

Configuring the Local Environment Variables

Next, we need to define a local environment variable file. This convenient way to store our API keys in a simple text file enables us to manage sensitive information and separate configuration from our API code.

If you haven’t done so already, clone the starter project template from GitHub into your local computer. Open the starter project template in a code editor and create a new .env file in the root folder.

Note: Root folder in this case is the “/Microsoft_Series17-18_Code/intelligent-app-before" folder.

Add the following key-value pairs to the file, remembering to replace the placeholder values with those you obtained from the previous step.

VISION_KEY=<THE-KEY-1-VALUE-FROM-YOUR-AZURE-AI-SERVICE>
VISION_ENDPOINT=<THE-ENDPOINT-VALUE-FROM-YOUR-AZURE-AI-SERVICE>

Reviewing the Quickstart Code

Let’s review the app.py file in the starter project template.

The app.py file works as the entry point for the Flask application. It is responsible for initializing the web server, listening for incoming requests, dispatching them to the appropriate route handlers, and generating responses.

Below is the code contained in the app.py file:

import os
import json

from flask import Flask, request
from flask_restful import Resource, Api
from werkzeug.utils import secure_filename

app = Flask(__name__,
static_url_path='',
static_folder='static/files')

api = Api(app)

app = Flask(__name__)

app.config['UPLOAD_FOLDER'] = 'files'

api = Api(app)

class UploadHandler(Resource):

def allowed_file(self, filename):
return '.' in filename and \
filename.rsplit('.', 1)[1].lower() in {'png'}

def post(self):
form = request.form.to_dict()

if 'file' not in request.files:
return json.dumps({ "success": False, "error": "No file part"})

file = request.files.get("file")
if file and self.allowed_file(file.filename):
filename = secure_filename(file.filename)
upload_folder = "static/files"
if not os.path.exists(upload_folder):
os.makedirs(upload_folder)
local_file_path = os.path.join(upload_folder, filename)
file.save(local_file_path)

return f"File {filename} uploaded successfully to folder: {upload_folder}"

api.add_resource(UploadHandler, "/")

if __name__ == '__main__':
app.run(debug=True)

The UploadHandler class above is defined as a Flask RESTful resource to handle file uploads. The class contains two methods:

  • The allowed_file method checks whether the file extension is allowed for upload. In this case, only .png files are allowed.

  • The post method handles HTTP POST requests for file uploads. It saves the uploaded .png file to the static/files folder.

Finally, the application returns a text response informing us that the file has been uploaded successfully.

Implementing the REST API in Python

To implement image analysis in your REST API, open the starter project template in your terminal, create a virtual environment, and activate it.

Next, install the Azure AI Vision SDK:

pip install azure-ai-vision

Then, add the following line to the requirements.txt file to include the azure-ai-vision package:

azure-ai-vision==0.13.0b1

Now, create an ocr_helper.py file in the project’s root folder. This file is a Python module that provides functions for processing images using OCR. Add the following code to the file:

import os
from statistics import median
from decimal import Decimal
import azure.ai.vision as sdk

def process_ocr(source_image):
service_options = sdk.VisionServiceOptions(os.environ["VISION_ENDPOINT"],
os.environ["VISION_KEY"])

vision_source = sdk.VisionSource(filename=source_image)

analysis_options = sdk.ImageAnalysisOptions()

analysis_options.features = (
sdk.ImageAnalysisFeature.CAPTION |
sdk.ImageAnalysisFeature.TEXT
)

analysis_options.language = "en"

analysis_options.gender_neutral_caption = True

image_analyzer = sdk.ImageAnalyzer(service_options, vision_source, analysis_options)

result = image_analyzer.analyze()

ocr_result = get_ocr_result(result)

return ocr_result

def get_ocr_result(result):
string_list = []

if result.reason != sdk.ImageAnalysisResultReason.ANALYZED:
return sdk.ImageAnalysisErrorDetails.from_result(result)
else:
if result.text is not None:
for line in result.text.lines:
for word in line.words:
string_list.append(word.content)

number_list = convert_to_decimal_list(string_list)

aggregate_result = aggregate_operations(number_list)

return {
"aggregate_result": aggregate_result,
"numbers_read": string_list
}

def convert_to_decimal_list(string_list):
return list(map(Decimal, string_list))

def aggregate_operations(numbers):
result = {
'sum': sum(numbers),
'average': sum(numbers) / len(numbers),
'median': median(numbers),
'min': min(numbers),
'max': max(numbers)
`}`
`return result`

This module uses the azure-ai-vision package to analyze images, including capturing captions and extracting text from the image. The process_ocr function inputs an image file and performs the OCR analysis. The module is a convenient tool for analyzing text-based data in pictures and performing numerical computations based on that data.

Let’s review the different components of the ocr_helper.py module listed above and explore how its functions help our Intelligent App process images and perform aggregate operations:

  • The process_ocr function takes the parameter source_image, which is the path of the image to be processed. The function then initializes the VisionServiceOptions using environment variables VISION_ENDPOINT and VISION_KEY to connect to the Azure AI Vision API.
  • The process_ocr function creates a VisionSource object with the specified source_image file name. ImageAnalysisOptions specify the features to be analyzed, including CAPTION and TEXT. The language is set to English ("en"), and gender-neutral captions are enabled.
  • Finally, an ImageAnalyzer object is created with the service options, vision source, and analysis options. The image is then analyzed using the image_analyzer.analyze method to retrieve OCR results. The recognized text from the OCR result is extracted, with numerical values stored in the string_list variable.
  • The convert_to_decimal_list function converts the list of strings to a list of decimal numbers, which helps process numeric values extracted from the text.
  • The aggregate_operations function calculates various aggregate statistics (sum, average, median, minimum, maximum) from a list of numbers and returns the results in a dictionary.

Note that you must have the appropriate credentials (VISION_KEY) and endpoint (VISION_ENDPOINT) configured for the Azure AI Vision API to use this module.

Finally, we must modify the app.py file so our code can use the process_ocr function of the ocr_helper.py file.

Add the following import statement to the app.py file:

from ocr_helper import process_ocr

Then, replace this line:

return f"File {filename} uploaded successfully to folder: {upload_folder}"

With these two lines:

aggregates = process_ocr(local_file_path)
return json.dumps(aggregates, default=str)

Doing so allows our application to perform aggregate operations (sum, average, median, minimum, maximum) of any numeric values found in the text and return the operation’s result as a JSON response.

Running the Intelligent App Locally

Our final goal is to deploy the sample app to AKS, which will run the app in a container. However, as a good practice, let’s run the app in a container locally before moving it to AKS. Doing so ensures that our application and its dependencies are encapsulated and can be run consistently across different environments.

Deploying to Docker Desktop

For our application to run on Docker, it needs two additional files: a Dockerfile and docker-compose.yml. A Dockerfile creates a single container image by specifying the steps to build it, and a docker-compose.yml file manages multiple containers as a cohesive application, defining their configurations and relationships. Dockerfile builds the image, while docker-compose.yml orchestrates multi-container applications.

In the project root folder, add a file named Dockerfile with the content below:

# syntax=docker/dockerfile:1

FROM python:3.8-slim-buster

WORKDIR /intelligentapp

COPY requirements.txt requirements.txt
RUN pip3 install -r requirements.txt

RUN pip install debugpy

COPY . .

CMD [ "python3", "-m" , "flask", "run", "--host=0.0.0.0"]

Now create a file named docker-compose.yml file in the project’s root folder with the following code, replacing the VISION_KEY and the VISION_ENDPOINT according to the environment variables you configured earlier:

version: '3.8'
services:
intelligentapp:
build:
context: .
dockerfile: Dockerfile
image: intelligent-app
ports:
- 5000:5000
container_name: intelligent-app
environment:
- VISION_KEY=<THE-KEY-1-VALUE-FROM-YOUR-AZURE-AI-SERVICE>
- VISION_ENDPOINT=<THE-ENDPOINT-VALUE-FROM-YOUR-AZURE-AI-SERVICE>

Then, run the following command in the terminal to build the image and start a container for the Intelligent App services defined in the docker-compose.yml file:

docker-compose up --build --force-recreate

Next, open a new terminal and run the command below to list the image deployed to your local Docker:

docker images

docker images

Testing the Intelligent App Locally

Now, we’ll test the Intelligent App’s functionality using the following test image:

test image to test functionality

This image is provided as sample1.png in the sample app source files, so you can easily use it in the next step.

The Intelligent App will read the image above using OCR to extract numbers in the image and perform five aggregate functions:

  • Sum
  • Average
  • Median
  • Min
  • Max

To test the API, open Postman and fill out the fields as follows:

  • URL: http://localhost:5000/
  • Method: POST
  • Body:
    • Form-data
      • Key: File — Click the right end of the Key field and select File from the dropdown list.
      • Value: Click Select Files, then select the sample1.png file provided in the sample code.

image of selecting the sample file

Now click the Send button and review the result body:

"{\"aggregate_result\": {\"sum\": \"25821\", \"average\": \"5164.2\", \"median\": \"5622\", \"min\": \"1447\", \"max\": \"9802\"}, \"numbers_read\": [\"3049\", \"5901\", \"5622\", \"1447\", \"9802\"]}"

As we can see, the app running on a local container returned the correct results based on the sample image. So, we’re ready to prepare our Azure resources to run the Intelligent App on AKS.

Exercise

In this article, we explored the creation of an Intelligent App that leverages Azure AI Vision to analyze images and extract data. We learned how to build a Python Web API to perform OCR on uploaded images and subsequently test this API locally.

Head to the next part, to deploy this API via Azure Kubernetes Service.

+ + + + \ No newline at end of file diff --git a/30daysofIA/build-your-first-intelligent-app-with-azure-ai-and-aks-2/index.html b/30daysofIA/build-your-first-intelligent-app-with-azure-ai-and-aks-2/index.html new file mode 100644 index 0000000000..120aca9bdf --- /dev/null +++ b/30daysofIA/build-your-first-intelligent-app-with-azure-ai-and-aks-2/index.html @@ -0,0 +1,26 @@ + + + + + +2-2. Build Your First Intelligent App with Azure AI and AKS-2 | Build Intelligent Apps On Azure + + + + + + + + + + + + + + +
+
Skip to main content

2-2. Build Your First Intelligent App with Azure AI and AKS-2

· 9 min read
It's 30DaysOfIA

In the last article, we created an Intelligent App that leverages Azure AI Vision to analyze images and extract data. Develop an API to perform optical character recognition (OCR) on uploaded images and testing this API locally.

In this article, we will deploy the web API to the cloud platform using Azure Kubernetes Service.

What We'll Cover:

  • Understanding Azure AI Vision and Azure Kubernetes Service
  • Build a Python Web API to perform OCR
  • Test the API locally

image depicting an Intelligent App using AI

Jumpstart Your AI Journey: Building Your First Intelligent App with Azure AI and AKS (2)

In the previous article we explored the creation of an Intelligent App that leverages Azure AI Vision to analyze images and extract data. We learned how to build a Python Web API to perform OCR on uploaded images and subsequently test this API locally.

In this article we will use Azure Kubernetes Service (AKS) to develop, publish, and maintain our app in the cloud on Azure.

Let’s get started!

Prerequisites

To follow this tutorial, ensure you have completed Jumpstart Your AI Journey: Building Your First Intelligent App with Azure AI and AKS (1).

Pushing a Container Image to Azure Container Registry (ACR)

To start, open your CLI or terminal and type the following command:

az login

Follow the instructions displayed on your browser to enter your Azure credentials.

Once authenticated, you’ll initiate a secure connection between your local environment and Azure. This process grants you access to cloud services and resources.

Next, type the command below in your terminal to set up a new Azure Container Registry (ACR) to store your container images:

az acr create --resource-group computer-vision --name <name-of-azure-container-registry> --sku Basic

Remember to replace <name-of-azure-container-registry> with your container registry name. The name must be unique within Azure and comply with these rules.

The command above creates an Azure Container Registry (ACR) in the computer-vision resource group under the Basic SKU. This ACR is your secure and private repository for storing container images within Azure.

Next, log in to the registry with the following command:

az acr login -n <name-of-azure-container-registry>

The az acr login command above lets you securely authenticate and access the specified ACR without providing their Azure credentials each time.

Now, run the following command in your terminal. It will display the endpoint URL to log in to and interact with the ACR for pushing and pulling container images.

az acr show --name <name-of-azure-container-registry> --query loginServer --output table

This returns an endpoint URL as follows:

Result
----------------------------------
<name-of-azure-container-registry>.azurecr.io

Now, run the following command to show all container images, their repository, tags, and size:

docker images
REPOSITORYTAGIMAGE IDCREATEDSIZE
intelligent-applatesta7bf9f75361716 hours ago197MB

Tags are necessary to push Docker images to a remote registry like Azure Container Registry. They also let you differentiate between different versions of the same image and upload or download the one you want.

Run the following command to tag your Docker image:

docker tag intelligent-app <name-of-azure-container-registry>.azurecr.io/intelligent-app:v1

Then, run the docker images command again to check your tagged image:

docker images
REPOSITORYTAGIMAGE IDCREATEDSIZE
intelligent-applatestc52168039265About a minute ago197MB
<name-of-azure-container-registry>.azurecr.io/intelligent-appv1c52168039265About a minute ago197MB

Now run the following command so Docker can securely upload the image to your Azure Container Registry:

docker push <name-of-azure-container-registry>.azurecr.io/intelligent-app:v1

Once we've deployed the image to the container registry, AKS can access it during deployment.

info

Watch Episode 03 for the Learn Live session to experience a guided session on how to build, test and deploy an end-to-end intelligent app solution.

Deploying the Intelligent App on Azure Kubernetes Service (AKS)

Before we deploy our Intelligent App to AKS, we need to provision an AKS cluster and define Kubernetes manifests.

To provision an AKS cluster to host our application, we specify the desired configurations for the cluster, such as the number of nodes, node size, and networking options. But first, download and install the Kubernetes command-line tools (kubectl), a client credential plugin implementing Azure authentication:

az aks install-cli

If you’re using Linux, review this tutorial. Then run the following:

sudo az aks install-cli

Next, run the following command in your terminal to enable access to the networking-related resources and services provided by the `Microsoft.Network`` namespace in Azure:

az provider register --namespace Microsoft.Network

Now, we must create an AKS cluster. Run the following command to create an AKS cluster named aks-intelligent-app in the computer-vision resource group.

az aks create --resource-group computer-vision --name aks-intelligent-app --node-count 1 --generate-ssh-keys

The command above specifies the target resource group: computer-vision. The node pool is configured with one virtual machine (VM), and the secure shell (SSH) keys for secure node access are generated automatically.

Next, run the following command to update the AKS cluster you created by attaching it to your ACR. Doing so allows the AKS cluster to pull container images from the specified ACR when deploying workloads to the cluster.

az aks update -n aks-intelligent-app -g computer-vision --attach-acr <name-of-azure-container-registry>

Then, run the command below to configure kubectl to work with our AKS cluster in the computer-vision resource group.

az aks get-credentials --resource-group computer-vision --name aks-intelligent-app

The command above retrieves the necessary credentials and context information required for kubectl to communicate with the AKS cluster.

We still have to define the Kubernetes manifests written in YAML, describing the desired state of our application deployment, including containers, networking, and scaling rules. We’ll prepare these manifests, including Deployment and Service configurations, to define how our application should be deployed and exposed.

Create a folder named Deployment in the project root directory. Next, create two files in the deployment folder: deployment.yml and service.yml.

Add the following configuration to the deployment.yml file, replacing the <name-of-azure-container-registry> placeholder with your registry’s name:

apiVersion: apps/v1
kind: Deployment
metadata:
name: intelligent-app
spec:
replicas: 1
selector:
matchLabels:
app: intelligent-app
template:
metadata:
labels:
app: intelligent-app
spec:
nodeSelector:
kubernetes.io/os: linux
containers:
- name: intelligent-app
image: <name-of-azure-container-registry>.azurecr.io/intelligent-app:v1
resources:
limits:
memory: 512Mi
cpu: "1"
requests:
memory: 256Mi
cpu: "0.2"
ports:
- containerPort: 5000
env:
- name: FLASK_DEBUG
value: "1"
- name: VISION_KEY
value: <THE-KEY-1-VALUE-FROM-YOUR-AZURE-AI-SERVICE>
- name: VISION_ENDPOINT
value: <THE-ENDPOINT-VALUE-FROM-YOUR-AZURE-AI>

Additionally, edit the VISION_KEY and VISION_ENDPOINT environment variables above according to the API key and endpoint of your Azure AI Services instance.

Then, add the following configuration to the service.yml file:

apiVersion: v1
kind: Service
metadata:
name: intelligent-app-service
spec:
type: LoadBalancer
ports:
- protocol: TCP
port: 80
targetPort: 5000
name: port5000
selector:
app: intelligent-app

Now, we’ll deploy our application using kubectl. Applying the Kubernetes manifests creates the necessary resources and deploys our containerized application to the AKS cluster.

First, change the terminal to the deployment folder:

cd Deployment 

Then, run the following command to create or update Kubernetes resources defined in the deployment.yml file:

kubectl apply -f deployment.yml

Create a Kubernetes Service resource in a Kubernetes cluster based on the configuration defined in the service.yml file using the code below:

kubectl apply -f service.yml

Once you’ve applied the resource definition and the service configuration contained in the deployment.yml and the service.yml files, open the aks-intelligent-app Kubernetes Service in the Azure Portal, select Workloads under Kubernetes resources on the sidebar, and find the deployment named intelligent-app. It must have the status “Ready 1/1”. If you encounter an issue with this status, check out these troubleshooting resources.

image of selecting the deployment in Azure Portal

Testing the Intelligent App on AKS

To test the app on AKS, first, run the command below:

kubectl get services

This command lists the Services and their corresponding details, including the Service name, cluster IP address, external IP, and ports.

NAMETYPECLUSTER-IPEXTERNAL-IPPORT(S)AGE
intelligent-app-serviceLoadBalancer10.0.77.6020.121.76.15380:30936/TCP47s
kubernetesClusterIP10.0.0.1<none>443/TCP14m

The output above shows a Kubernetes Service named intelligent-app-service with a type set to LoadBalancer. It’s reachable from within the cluster using the cluster IP 10.0.77.60 and accessible externally via the external IP 20.121.76.153 on port 80 (mapped to port 30936).

Note: Your set of IP addresses will differ. Remember to use your unique external IP address when testing with Postman.

To test the deployed app, go to Postman, replace the URL with the external IP of the Kubernetes Service you just deployed, and click Send:

mage of sending the app in Postman

As we can see, our Intelligent App has successfully deployed to AKS and is functioning on the cloud as expected.

Exercise

Next Steps

In this two-part article, we explored the creation of an Intelligent App that leverages Azure AI Vision to analyze images and extract data. We learned how to build a Python Web API to perform OCR on uploaded images and subsequently deploy this API via Azure Kubernetes Service.

Besides OCR and Image Analysis, you can continue exploring Azure’s vast array of services and experiment further with Azure AI and AKS by applying various practical uses to your Intelligent Apps, including natural language processing, speech recognition and synthesis, sentiment analysis for customer feedback, and automated content moderation.

+ + + + \ No newline at end of file diff --git a/30daysofIA/cultivating-a-culture-for-intelligent-apps/index.html b/30daysofIA/cultivating-a-culture-for-intelligent-apps/index.html index ab498b6075..223b4b5462 100644 --- a/30daysofIA/cultivating-a-culture-for-intelligent-apps/index.html +++ b/30daysofIA/cultivating-a-culture-for-intelligent-apps/index.html @@ -14,13 +14,13 @@ - - + +
-
Skip to main content

1-6. Cultivating a Culture for Intelligent Apps

· 10 min read
It's 30DaysOfIA

Prepare your organization to adopt intelligent apps by taking a change management approach. Assess your readiness, upskill teams, create a culture of change and overcome resistance, and prepare for future evolution to pave the way for a smooth transition into an AI-centric company.

What We'll Cover:

  • Role of change management in modernizing to intelligent apps
  • Skills needed for building intelligent apps in your organization
  • How to build a culture of change to embrace AI
  • How to prepare for the future of intelligent apps and continuous evolution

image of modernizing AI solutions for intelligent apps

Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management

As we’ve discussed throughout this series, intelligent apps and generative AI are rewriting the rules of business and application development. The first article of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” explored how intelligent apps can enhance user experiences and drive businesses growth. These advancements are opening new opportunities and markets for companies ready to embrace them.

However, embracing this digital transformation is more complex than flipping a switch. It requires adjustments to the technical side, which we explored in the series’ fifth article, “Preparing the Path for Intelligent Apps: Transitioning from On-Premises/IaaS to Cloud Native Applications.” It also demands a shift in how organizations adapt to change and approach app design and development. Let’s explore that mindset transition with tips to help you make it happen.

The Role of Change Management in Transitioning to Intelligent Apps

Transitioning to intelligent apps isn’t merely a technological change. It’s a profound shift in how businesses perceive and operate. This move toward artificial intelligence (AI)-centric approach isn’t meant to replace humans with machines or radically overhaul all business operations.

Instead, intelligent apps should augment human capabilities, streamline processes, and foster an environment where innovation flourishes. They expand toolkits and techniques to include AI-assisted development tools like GitHub Copilot and AI-enabled workflows, helping us design and deliver AI-enhanced experiences.

Effective change management strategies are crucial in helping employees adapt to these new ways of working with intelligent apps, ensuring that the transition doesn’t disrupt productivity or morale.

Team and organization strategies should: 

  • Assess the impact of any proposed changes.  
  • Communicate the benefits and reasons behind the change to all affected team members.  
  • Provide the necessary training and support.  
  • Integrate the changes into the culture and daily operations. 

By incorporating this robust change management approach, you can identify and mitigate potential transition risks early. Moreover, this strategy ensures that employees are engaged throughout the process, fostering a sense of involvement and ownership. This, in turn, cultivates a culture suited for innovation through intelligent apps. 

These organizational changes focus on the following aspects: organizational readiness, skillset, culture of change and overcoming resistance, and adaptability. Let’s explore them below.

info

Register for the intelligent apps webinar on Driving Business Value by Modernizing with Cloud-Native & AI with Microsoft and Forrester on September 26. 

Explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries. 

Identifying Organizational Readiness for Intelligent Apps

The first step in preparing your organization for intelligent apps is to assess your current readiness for change. Analyze various elements of your organization, from infrastructure and workforce skills to strategic vision and overall culture.

Can your apps and systems integrate with AI platforms like Microsoft Azure AI? Is your workforce comfortable working alongside AI, or are they fearful and resistant? Does your strategic vision incorporate AI’s transformative potential, or does it fail to acknowledge AI as a critical driver of future growth?

These questions identify the challenges of integrating AI into an organization and determine the company’s readiness for intelligent apps.

Embracing intelligent apps involves more than being ready with your technological infrastructure or having the right technology or tools. You need flexible organizational structures and processes driving your business to accommodate new ideas and capabilities. Your teams must feel empowered to own and drive them. Much like Microsoft’s shift to AI-powered services—like AI-powered Bing and Edge and Microsoft 365 Copilot—embracing AI organization-wide requires a holistic vision and willingness to innovate, with teams capable of delivering on the plan.

After accessing these critical areas for organizational readiness, you can start planning a strategy for this transition to an AI-ready organization. Identify skill gaps and work to bridge them while nurturing an environment that appreciates and harnesses the potential of AI and intelligent apps.

Nurturing the Indispensable Skills for Intelligent Apps

Tasking your AI specialists with preparing your organization to use and build intelligent apps isn’t enough. Accomplishing this goal requires a cross-functional effort and a diverse skill set.

Developing intelligent apps involves data scientists managing and interpreting vast amounts of data, machine learning engineers building and training the models, and traditional developers integrating these models into usable applications. However, the impact of generative AI spans beyond creating apps. AI fluency across the entire organization—from sales and marketing to accounting and legal departments—offers substantial benefits, sparking new ideas and solutions from sometimes unexpected sources.

Teams' AI skills might roughly fall into three levels: 

  • Fundamental skills — Being familiar and efficient with using AI tools, for example, knowing how to write effective prompts for large language models (LLMs).
  • Visionary skills — Recognizing AI’s strengths and weaknesses, for example, its creativity and hallucinations, that is, unexpected output.
  • Expert skills — Managing data and training models and integrating AI technology into apps, such as incorporating the OpenAI application programming interface (API) into an Intelligent App.

Based on these levels, you can gauge your team's skill readiness to pinpoint the most significant gaps, whether in general AI knowledge across your organization or within technical development teams.

As intelligent apps become increasingly prevalent, upskilling your workforce is crucial. This transition opens a new realm of skills and capabilities for your team to master. You can offer machine learning, data analysis, and cloud computing training programs—such as Microsoft’s Azure AI Fundamentals certification—to empower your team to confidently navigate the intelligent apps landscape.

Nurturing these skills within your organization may require hiring new talent, reskilling current employees, and creating an environment that promotes learning and experimentation. Specifically, if your team understands tools like the Microsoft Azure AI platform, they could contribute significantly to developing and maintaining intelligent apps.

An upskilled team is more than a functional unit. It’s a think tank to drive innovation and improve your organization’s competitive standing in the digital marketplace.

Building a Culture of Change to Embrace AI

Even with the right people and skills in place, change remains an uphill battle for organizations committed to traditional processes. 

Transitioning to intelligent apps is as much about mindset as technology and skills. It requires fostering a culture open to change and innovation that understands AI’s transformative potential and embraces it to enhance rather than replace human capabilities. 

Leaders play a pivotal role in cultivating this mindset. As they model a positive attitude toward AI, emphasize its benefits, and address any concerns transparently, leaders significantly contribute to the organization’s readiness to adopt intelligent apps. 

The most critical factor in this transition is enabling an AI-friendly culture. It’s about encouraging curiosity, rewarding innovative thinking, and creating an environment where people thrive on learning and experimentation. 

Empowering teams to leverage practical AI tools and AI-driven data insights to aid decision-making helps motivate employees to use and explore AI’s potential, improve their skills, and contribute to the successful integration of intelligent apps. Such an environment is fertile ground to grow breakthrough ideas and innovative solutions. 

Overcoming Resistance to Change and Adopting Intelligent Apps

Despite AI’s potential, change isn’t easy. It takes effort and is especially challenging for organizations used to existing processes. This resistance to change is a significant roadblock when transitioning to intelligent apps. 

Overcoming this resistance involves:  

  • Understanding employees’ concerns. 
  • Challenging underlying assumptions about traditional systems and outdated processes that hinder change. 
  • Providing comprehensive training for workers to use AI effectively.
  • Demonstrating the benefits and opportunities of intelligent apps. 
  • Clear and continuous communication to dispel fears and misconceptions about AI.

These steps will foster greater acceptance and enthusiasm for intelligent apps in your teams. 

Preparing for the Future of Intelligent Apps and Continuous Evolution

Finally, it’s essential to understand that generative AI is just beginning to gain momentum. Adjusting to a world of intelligent apps isn’t a one-time change—it’s a continuous evolution. 

As AI technologies mature and evolve and more businesses realize their benefits, we can expect the landscape of intelligent apps to expand further. Organizations can cultivate a mindset of continuous learning, flexibility, and adaptation to prepare for this future. Staying ahead in this dynamic field requires organizations to keep up with the latest AI advancements, continually reassess their strategies, and pivot when necessary. 

The domain of intelligent apps is rapidly advancing. Organizations must prepare to navigate this dynamic landscape to stay ahead of the curve.

Summary

Preparing for AI and intelligent apps is a strategic decision. It requires thoughtful preparation and a culture that embraces change.

With the proper preparation and mindset, adopting AI can become a smoother, collaborative path to innovation for the whole organization. Integrating AI into your organization does not discard what has worked in the past. It simply enhances and augments those processes for a better future.

Consider the points above to assess your organization’s readiness and prepare to welcome intelligent apps.

In the first week of this series, you learned about intelligent apps, how organizations today find success through intelligent apps, what AI-assisted application development looks like, ways to leverage the power of generative AI, the technical changes needed to prepare for intelligent apps, and, in this article, the organizational changes required to harness the power of AI.

The future is intelligent. Use this series as a guideline to navigate challenges while preparing for and embracing AI's opportunities for an intelligent tomorrow.

Exercise

- - +
Skip to main content

1-6. Cultivating a Culture for Intelligent Apps

· 10 min read
It's 30DaysOfIA

Prepare your organization to adopt intelligent apps by taking a change management approach. Assess your readiness, upskill teams, create a culture of change and overcome resistance, and prepare for future evolution to pave the way for a smooth transition into an AI-centric company.

What We'll Cover:

  • Role of change management in modernizing to intelligent apps
  • Skills needed for building intelligent apps in your organization
  • How to build a culture of change to embrace AI
  • How to prepare for the future of intelligent apps and continuous evolution

image of modernizing AI solutions for intelligent apps

Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management

As we’ve discussed throughout this series, intelligent apps and generative AI are rewriting the rules of business and application development. The first article of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” explored how intelligent apps can enhance user experiences and drive businesses growth. These advancements are opening new opportunities and markets for companies ready to embrace them.

However, embracing this digital transformation is more complex than flipping a switch. It requires adjustments to the technical side, which we explored in the series’ fifth article, “Preparing the Path for Intelligent Apps: Transitioning from On-Premises/IaaS to Cloud Native Applications.” It also demands a shift in how organizations adapt to change and approach app design and development. Let’s explore that mindset transition with tips to help you make it happen.

The Role of Change Management in Transitioning to Intelligent Apps

Transitioning to intelligent apps isn’t merely a technological change. It’s a profound shift in how businesses perceive and operate. This move toward artificial intelligence (AI)-centric approach isn’t meant to replace humans with machines or radically overhaul all business operations.

Instead, intelligent apps should augment human capabilities, streamline processes, and foster an environment where innovation flourishes. They expand toolkits and techniques to include AI-assisted development tools like GitHub Copilot and AI-enabled workflows, helping us design and deliver AI-enhanced experiences.

Effective change management strategies are crucial in helping employees adapt to these new ways of working with intelligent apps, ensuring that the transition doesn’t disrupt productivity or morale.

Team and organization strategies should: 

  • Assess the impact of any proposed changes.  
  • Communicate the benefits and reasons behind the change to all affected team members.  
  • Provide the necessary training and support.  
  • Integrate the changes into the culture and daily operations. 

By incorporating this robust change management approach, you can identify and mitigate potential transition risks early. Moreover, this strategy ensures that employees are engaged throughout the process, fostering a sense of involvement and ownership. This, in turn, cultivates a culture suited for innovation through intelligent apps. 

These organizational changes focus on the following aspects: organizational readiness, skillset, culture of change and overcoming resistance, and adaptability. Let’s explore them below.

info

Register for the intelligent apps webinar on Driving Business Value by Modernizing with Cloud-Native & AI with Microsoft and Forrester on September 26. 

Explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries. 

Identifying Organizational Readiness for Intelligent Apps

The first step in preparing your organization for intelligent apps is to assess your current readiness for change. Analyze various elements of your organization, from infrastructure and workforce skills to strategic vision and overall culture.

Can your apps and systems integrate with AI platforms like Microsoft Azure AI? Is your workforce comfortable working alongside AI, or are they fearful and resistant? Does your strategic vision incorporate AI’s transformative potential, or does it fail to acknowledge AI as a critical driver of future growth?

These questions identify the challenges of integrating AI into an organization and determine the company’s readiness for intelligent apps.

Embracing intelligent apps involves more than being ready with your technological infrastructure or having the right technology or tools. You need flexible organizational structures and processes driving your business to accommodate new ideas and capabilities. Your teams must feel empowered to own and drive them. Much like Microsoft’s shift to AI-powered services—like AI-powered Bing and Edge and Microsoft 365 Copilot—embracing AI organization-wide requires a holistic vision and willingness to innovate, with teams capable of delivering on the plan.

After accessing these critical areas for organizational readiness, you can start planning a strategy for this transition to an AI-ready organization. Identify skill gaps and work to bridge them while nurturing an environment that appreciates and harnesses the potential of AI and intelligent apps.

Nurturing the Indispensable Skills for Intelligent Apps

Tasking your AI specialists with preparing your organization to use and build intelligent apps isn’t enough. Accomplishing this goal requires a cross-functional effort and a diverse skill set.

Developing intelligent apps involves data scientists managing and interpreting vast amounts of data, machine learning engineers building and training the models, and traditional developers integrating these models into usable applications. However, the impact of generative AI spans beyond creating apps. AI fluency across the entire organization—from sales and marketing to accounting and legal departments—offers substantial benefits, sparking new ideas and solutions from sometimes unexpected sources.

Teams' AI skills might roughly fall into three levels: 

  • Fundamental skills — Being familiar and efficient with using AI tools, for example, knowing how to write effective prompts for large language models (LLMs).
  • Visionary skills — Recognizing AI’s strengths and weaknesses, for example, its creativity and hallucinations, that is, unexpected output.
  • Expert skills — Managing data and training models and integrating AI technology into apps, such as incorporating the OpenAI application programming interface (API) into an Intelligent App.

Based on these levels, you can gauge your team's skill readiness to pinpoint the most significant gaps, whether in general AI knowledge across your organization or within technical development teams.

As intelligent apps become increasingly prevalent, upskilling your workforce is crucial. This transition opens a new realm of skills and capabilities for your team to master. You can offer machine learning, data analysis, and cloud computing training programs—such as Microsoft’s Azure AI Fundamentals certification—to empower your team to confidently navigate the intelligent apps landscape.

Nurturing these skills within your organization may require hiring new talent, reskilling current employees, and creating an environment that promotes learning and experimentation. Specifically, if your team understands tools like the Microsoft Azure AI platform, they could contribute significantly to developing and maintaining intelligent apps.

An upskilled team is more than a functional unit. It’s a think tank to drive innovation and improve your organization’s competitive standing in the digital marketplace.

Building a Culture of Change to Embrace AI

Even with the right people and skills in place, change remains an uphill battle for organizations committed to traditional processes. 

Transitioning to intelligent apps is as much about mindset as technology and skills. It requires fostering a culture open to change and innovation that understands AI’s transformative potential and embraces it to enhance rather than replace human capabilities. 

Leaders play a pivotal role in cultivating this mindset. As they model a positive attitude toward AI, emphasize its benefits, and address any concerns transparently, leaders significantly contribute to the organization’s readiness to adopt intelligent apps. 

The most critical factor in this transition is enabling an AI-friendly culture. It’s about encouraging curiosity, rewarding innovative thinking, and creating an environment where people thrive on learning and experimentation. 

Empowering teams to leverage practical AI tools and AI-driven data insights to aid decision-making helps motivate employees to use and explore AI’s potential, improve their skills, and contribute to the successful integration of intelligent apps. Such an environment is fertile ground to grow breakthrough ideas and innovative solutions. 

Overcoming Resistance to Change and Adopting Intelligent Apps

Despite AI’s potential, change isn’t easy. It takes effort and is especially challenging for organizations used to existing processes. This resistance to change is a significant roadblock when transitioning to intelligent apps. 

Overcoming this resistance involves:  

  • Understanding employees’ concerns. 
  • Challenging underlying assumptions about traditional systems and outdated processes that hinder change. 
  • Providing comprehensive training for workers to use AI effectively.
  • Demonstrating the benefits and opportunities of intelligent apps. 
  • Clear and continuous communication to dispel fears and misconceptions about AI.

These steps will foster greater acceptance and enthusiasm for intelligent apps in your teams. 

Preparing for the Future of Intelligent Apps and Continuous Evolution

Finally, it’s essential to understand that generative AI is just beginning to gain momentum. Adjusting to a world of intelligent apps isn’t a one-time change—it’s a continuous evolution. 

As AI technologies mature and evolve and more businesses realize their benefits, we can expect the landscape of intelligent apps to expand further. Organizations can cultivate a mindset of continuous learning, flexibility, and adaptation to prepare for this future. Staying ahead in this dynamic field requires organizations to keep up with the latest AI advancements, continually reassess their strategies, and pivot when necessary. 

The domain of intelligent apps is rapidly advancing. Organizations must prepare to navigate this dynamic landscape to stay ahead of the curve.

Summary

Preparing for AI and intelligent apps is a strategic decision. It requires thoughtful preparation and a culture that embraces change.

With the proper preparation and mindset, adopting AI can become a smoother, collaborative path to innovation for the whole organization. Integrating AI into your organization does not discard what has worked in the past. It simply enhances and augments those processes for a better future.

Consider the points above to assess your organization’s readiness and prepare to welcome intelligent apps.

In the first week of this series, you learned about intelligent apps, how organizations today find success through intelligent apps, what AI-assisted application development looks like, ways to leverage the power of generative AI, the technical changes needed to prepare for intelligent apps, and, in this article, the organizational changes required to harness the power of AI.

The future is intelligent. Use this series as a guideline to navigate challenges while preparing for and embracing AI's opportunities for an intelligent tomorrow.

Exercise

+ + \ No newline at end of file diff --git a/30daysofIA/demystifying-intelligent-applications/index.html b/30daysofIA/demystifying-intelligent-applications/index.html index eec49138fa..0002bffadd 100644 --- a/30daysofIA/demystifying-intelligent-applications/index.html +++ b/30daysofIA/demystifying-intelligent-applications/index.html @@ -14,13 +14,13 @@ - - + +
-
Skip to main content

1-1. Demystifying Intelligent Applications

· 8 min read
It's 30DaysOfIA

Let’s start with understanding the power of intelligent applications.

What We'll Cover:

  • What are intelligent applications?
  • Categories of Intelligent Apps
  • Breaking down the role of AI in intelligent apps

A decorative header image for the blog

Demystifying Intelligent Applications: Leveraging AI in App Development

Until recent years, when you heard the term “artificial intelligence” (AI), you might have pictured autonomous robots from sci-fi films. Now, perhaps your mind jumps to big data and machine learning (ML). Maybe you’re even thinking about the latest ChatGPT plugins and transformer architectures. Today, AI is more than these imaginations: it’s a reality increasingly woven into our day-to-day routines through intelligent applications.

Intelligent apps combine cloud-scale compute, data, and predictive or generative AI. These applications help you automate tasks and make data-driven decisions, aiding in leveraging business intelligence to gain meaningful, practical outcomes. 

For developers, the AI landscape isn’t an abstract complexity. It’s a real, tangible opportunity for evolving traditional applications, making them “intelligent,” and delivering more value to users. And with tools like Azure Kubernetes Service, Azure Cosmos DB, and Azure AI Services, stepping into the world of intelligent apps isn’t a steep climb but a steady trek across a manageable learning curve.

The Three Categories of Intelligent Applications

Before we start building, it’s critical to understand what makes an app “intelligent.” It’s not a universal definition so much as an identifier for apps that fall into one or more of the three primary categories: 

  • Outcome-based — This designation represents the broadest category of intelligent apps, whose principal value for users is the intelligent outcomes they provide. These apps process data to provide insightful results that enhance decision-making or help with tasks like trend prediction, resource management, and customer relationship management.

  • Functionality-based — This type of intelligent app takes AI integration a degree further, integrating AI as a function of the app to create or process its outputs, enhancing the app’s overall interactivity and usefulness.

  • Feature-based — This is the narrowest category, in which the app’s AI/ML component is one of its core features and primary selling points

Let’s dive more deeply into each category.

Outcome-Based Apps: Intelligent Outcomes Drive Success

Outcome-based applications are all about the results provided to the user. They function beyond merely processing data to offer insightful outcomes that drive success.

Think of a personal fitness tracker app that uses AI to analyze the user’s activity and provides actionable recommendations for achieving a goal. This type of app not only tracks your steps, sleep patterns, and heart rate but uses that data to provide you with personalized health advice. It helps you make informed lifestyle choices — the “intelligent” outcome.

Another example is a social media marketing app that uses AI for time-saving data analysis, task automation, and trend prediction. AI-based sentiment analysis can gauge customer perception while natural language processing (NLP) accurately interprets customer inquiries. Task automation can schedule posts and automate responses to common customer queries, and trend forecasting helps model and predict customer behavior to further personalize targeted promotional efforts. Building this type of intelligence into your apps provides cost-effective, actionable insights that guide sales and marketing strategies.

Functionality-Based Apps: AI-Driven Features Enhance User Experiences

As the name suggests, functionality-based applications integrate AI into how the app functions, but the end user drives the action and outcome. In this category of apps, AI takes a more active role in the user’s interaction by allowing them to use AI-created or processed output. These apps leverage AI technologies like Azure AI Services for NLP, image recognition, and pattern recognition to improve user experience.

Consider a music app that generates personalized playlists based on your listening habits or your smartphone’s camera app that uses AI to recognize faces and optimize photo settings. Even more advanced are the language translation apps that translate text from one language to another and can even understand and maintain the context and subtleties of the conversation. They use NLP to accomplish these feats.

Similarly, consider the latest e-commerce apps, whose AI-driven functionalities enable customers to try on clothes virtually, significantly enhancing the online shopping experience.

Feature-Based Apps: Integrating Advanced AI/ML Components

Finally, there are feature-based intelligent apps. These apps incorporate advanced AI/ML components like neural networks, internal large language models (LLMs), and sophisticated algorithms as core features for the user. In this type of application, AI is the primary feature for users to engage with and the primary selling point for the app.

Some examples of feature-based applications are chatbots and virtual agents built using Azure Bot Service. These robo-representatives use AI to provide more natural and human-like conversation, balancing superior efficiency with a friendlier, more personalized user experience. The new Bing is a prime example of this implementation, integrating AI to give users a knowledgeable copilot for web searches.

Similarly, OpenAI’s ChatGPT allows users access to its state-of-the-art language models available as Microsoft’s Azure OpenAI Service. It leverages the power of the highly sophisticated GPT-3 LLM to perform tasks like drafting emails, writing code, answering general knowledge questions, translating between languages, and tutoring in numerous subjects.

These apps don’t just use AI to provide outputs or improve user experiences: They push the boundaries of what’s possible in app development by integrating AI/ML components.

info

Register for the live Q&A Ask The Expert session on September 26 with the Azure Functions product engineering team.

Breaking Down the Role of AI in Intelligent Applications

Intelligent apps reflect the merging of traditional software design with the dynamic capabilities of AI. Let’s explore how AI integrates and amplifies these applications.

At its core, AI augments applications with the capacity to self-learn and self-improve. It allows you to create applications that predict, react, and adapt to a constantly changing environment. In the power grid management system for a major city, an AI-infused application can forecast power usage trends, react to anomalies, and adapt to sudden demand surges, ensuring optimal efficiency and preventing blackouts.

But AI’s role doesn’t end at prediction or decision-making: It also automates tasks. In enterprise solutions, it has moved beyond simple automation to encompass intelligent process automation (IPA). In this arena, AI algorithms understand, learn, and then automate entire digital workflows. For developers, this is a fertile ground for creating applications that can dramatically improve the operational efficiency of businesses.

Even more, AI substantially aids in development tasks. For example, there’s GitHub Copilot, an AI-powered code assistant that offers suggestions based not only on syntax but on development best practices and the intent of the code. AI has evolved from a tool you use to build apps to a partner that helps in creating them. 

Using AI to build intelligent apps isn’t about throwing out what’s worked before. It’s about enhancing the components that have the most impact and achieve the best results. Incorporating AI in apps elevates their predictive abilities, streamlines complex tasks, and even aids in the development and creation process. When you build intelligent apps, you don’t just address current problems. You anticipate future challenges and craft solutions that adapt over time.

Summary

As developers the power of AI is in our hands, and it’s our responsibility to harness its full potential. Each category tells the story of AI's transformative potential, from applications that deliver intelligent outcomes to those with AI capabilities to enrich the user experience and those pushing boundaries with advanced AI/ML technology.

Accessible and robust platforms like Azure AI can simplify the process of developing Intelligent Applications. Whether you’re refining an existing app or building a new one, just entering the world of AI, or already familiar with the landscape, take a moment to appreciate the potential that AI integration offers. You’re embracing this exciting frontier and shaping the future of app development. 

Exercise

  • Complete the Cloud Skills Challenge to build on your apps and AI skills.
  • Watch Episode 01 of the Serverless Edition Learn Live session to learn how to build an end-to-end intelligent app solution.
- - +
Skip to main content

1-1. Demystifying Intelligent Applications

· 8 min read
It's 30DaysOfIA

Let’s start with understanding the power of intelligent applications.

What We'll Cover:

  • What are intelligent applications?
  • Categories of Intelligent Apps
  • Breaking down the role of AI in intelligent apps

A decorative header image for the blog

Demystifying Intelligent Applications: Leveraging AI in App Development

Until recent years, when you heard the term “artificial intelligence” (AI), you might have pictured autonomous robots from sci-fi films. Now, perhaps your mind jumps to big data and machine learning (ML). Maybe you’re even thinking about the latest ChatGPT plugins and transformer architectures. Today, AI is more than these imaginations: it’s a reality increasingly woven into our day-to-day routines through intelligent applications.

Intelligent apps combine cloud-scale compute, data, and predictive or generative AI. These applications help you automate tasks and make data-driven decisions, aiding in leveraging business intelligence to gain meaningful, practical outcomes. 

For developers, the AI landscape isn’t an abstract complexity. It’s a real, tangible opportunity for evolving traditional applications, making them “intelligent,” and delivering more value to users. And with tools like Azure Kubernetes Service, Azure Cosmos DB, and Azure AI Services, stepping into the world of intelligent apps isn’t a steep climb but a steady trek across a manageable learning curve.

The Three Categories of Intelligent Applications

Before we start building, it’s critical to understand what makes an app “intelligent.” It’s not a universal definition so much as an identifier for apps that fall into one or more of the three primary categories: 

  • Outcome-based — This designation represents the broadest category of intelligent apps, whose principal value for users is the intelligent outcomes they provide. These apps process data to provide insightful results that enhance decision-making or help with tasks like trend prediction, resource management, and customer relationship management.

  • Functionality-based — This type of intelligent app takes AI integration a degree further, integrating AI as a function of the app to create or process its outputs, enhancing the app’s overall interactivity and usefulness.

  • Feature-based — This is the narrowest category, in which the app’s AI/ML component is one of its core features and primary selling points

Let’s dive more deeply into each category.

Outcome-Based Apps: Intelligent Outcomes Drive Success

Outcome-based applications are all about the results provided to the user. They function beyond merely processing data to offer insightful outcomes that drive success.

Think of a personal fitness tracker app that uses AI to analyze the user’s activity and provides actionable recommendations for achieving a goal. This type of app not only tracks your steps, sleep patterns, and heart rate but uses that data to provide you with personalized health advice. It helps you make informed lifestyle choices — the “intelligent” outcome.

Another example is a social media marketing app that uses AI for time-saving data analysis, task automation, and trend prediction. AI-based sentiment analysis can gauge customer perception while natural language processing (NLP) accurately interprets customer inquiries. Task automation can schedule posts and automate responses to common customer queries, and trend forecasting helps model and predict customer behavior to further personalize targeted promotional efforts. Building this type of intelligence into your apps provides cost-effective, actionable insights that guide sales and marketing strategies.

Functionality-Based Apps: AI-Driven Features Enhance User Experiences

As the name suggests, functionality-based applications integrate AI into how the app functions, but the end user drives the action and outcome. In this category of apps, AI takes a more active role in the user’s interaction by allowing them to use AI-created or processed output. These apps leverage AI technologies like Azure AI Services for NLP, image recognition, and pattern recognition to improve user experience.

Consider a music app that generates personalized playlists based on your listening habits or your smartphone’s camera app that uses AI to recognize faces and optimize photo settings. Even more advanced are the language translation apps that translate text from one language to another and can even understand and maintain the context and subtleties of the conversation. They use NLP to accomplish these feats.

Similarly, consider the latest e-commerce apps, whose AI-driven functionalities enable customers to try on clothes virtually, significantly enhancing the online shopping experience.

Feature-Based Apps: Integrating Advanced AI/ML Components

Finally, there are feature-based intelligent apps. These apps incorporate advanced AI/ML components like neural networks, internal large language models (LLMs), and sophisticated algorithms as core features for the user. In this type of application, AI is the primary feature for users to engage with and the primary selling point for the app.

Some examples of feature-based applications are chatbots and virtual agents built using Azure Bot Service. These robo-representatives use AI to provide more natural and human-like conversation, balancing superior efficiency with a friendlier, more personalized user experience. The new Bing is a prime example of this implementation, integrating AI to give users a knowledgeable copilot for web searches.

Similarly, OpenAI’s ChatGPT allows users access to its state-of-the-art language models available as Microsoft’s Azure OpenAI Service. It leverages the power of the highly sophisticated GPT-3 LLM to perform tasks like drafting emails, writing code, answering general knowledge questions, translating between languages, and tutoring in numerous subjects.

These apps don’t just use AI to provide outputs or improve user experiences: They push the boundaries of what’s possible in app development by integrating AI/ML components.

info

Register for the live Q&A Ask The Expert session on September 26 with the Azure Functions product engineering team.

Breaking Down the Role of AI in Intelligent Applications

Intelligent apps reflect the merging of traditional software design with the dynamic capabilities of AI. Let’s explore how AI integrates and amplifies these applications.

At its core, AI augments applications with the capacity to self-learn and self-improve. It allows you to create applications that predict, react, and adapt to a constantly changing environment. In the power grid management system for a major city, an AI-infused application can forecast power usage trends, react to anomalies, and adapt to sudden demand surges, ensuring optimal efficiency and preventing blackouts.

But AI’s role doesn’t end at prediction or decision-making: It also automates tasks. In enterprise solutions, it has moved beyond simple automation to encompass intelligent process automation (IPA). In this arena, AI algorithms understand, learn, and then automate entire digital workflows. For developers, this is a fertile ground for creating applications that can dramatically improve the operational efficiency of businesses.

Even more, AI substantially aids in development tasks. For example, there’s GitHub Copilot, an AI-powered code assistant that offers suggestions based not only on syntax but on development best practices and the intent of the code. AI has evolved from a tool you use to build apps to a partner that helps in creating them. 

Using AI to build intelligent apps isn’t about throwing out what’s worked before. It’s about enhancing the components that have the most impact and achieve the best results. Incorporating AI in apps elevates their predictive abilities, streamlines complex tasks, and even aids in the development and creation process. When you build intelligent apps, you don’t just address current problems. You anticipate future challenges and craft solutions that adapt over time.

Summary

As developers the power of AI is in our hands, and it’s our responsibility to harness its full potential. Each category tells the story of AI's transformative potential, from applications that deliver intelligent outcomes to those with AI capabilities to enrich the user experience and those pushing boundaries with advanced AI/ML technology.

Accessible and robust platforms like Azure AI can simplify the process of developing Intelligent Applications. Whether you’re refining an existing app or building a new one, just entering the world of AI, or already familiar with the landscape, take a moment to appreciate the potential that AI integration offers. You’re embracing this exciting frontier and shaping the future of app development. 

Exercise

  • Complete the Cloud Skills Challenge to build on your apps and AI skills.
  • Watch Episode 01 of the Serverless Edition Learn Live session to learn how to build an end-to-end intelligent app solution.
+ + \ No newline at end of file diff --git a/30daysofIA/hacktogether-recap/index.html b/30daysofIA/hacktogether-recap/index.html index 0cdfd1c9da..106ffc7506 100644 --- a/30daysofIA/hacktogether-recap/index.html +++ b/30daysofIA/hacktogether-recap/index.html @@ -14,14 +14,14 @@ - - + +
-
Skip to main content

HackTogether Recap 🍂

· 4 min read
It's 30DaysOfIA

Continue The Learning Journey through Fall For Intelligent Apps! 🍂

What We'll Cover

Thank you! ♥️

image

It's hard to believe that JavaScript on Azure hack-together is ending! It seems like just yesterday that we launched this initiative, and yet here we are, 15 days later, with an incredible amount of learning and growth behind us. So... it's time for a wrap!

From the bottom of our hearts, we want to thank each and every one of you for your participation, engagement, and enthusiasm. It's been truly inspiring to see the passion and dedication from this strong community, and we're honored to be a part of it. ✨

Recap of The JavaScript on Azure Global Hack-Together

As we wrap up this exciting event, we wanted to take a moment to reflect on all that we've accomplished together. Over the last 15 days, we've covered a lot of ground, from the basics of contributing to Open source to the exploration of the Contoso Real Estate project from its Frontend to its Backend and future AI implementation.

Now that the hack-together is ending, we want to make sure that you have all the resources you need to continue honing your skills in the future. Whether you're looking to make your fist contribution to open source, become an open source maintainers, collaborate with others, or simply keep learning, there are plenty of resources out there to help you achieve your goals. So, let's dive in and explore all the ways you can continue to grow your JavaScript skills on Azure!

JSonAzure Hack-together Roadmap 📍:

hack-together-roadmap (2)

Recap on past Livestreams🌟:

Day 1️⃣: Opening Keynote (Hack-together Launch): Introduction to the Contoso Real Estate Open-source project!, managing complex and complex enterprise architecture, new announcements for JavaScript developers on Azure

Day 2️⃣: GitHub Copilot & Codespaces: Introduction to your AI pair programmer (GitHub Copilot) and your virtual developer environment on the cloud (GitHub Codespaces)

Day 6️⃣: Build your Frontend using Static Web Apps as part of a complex, modern composable frontends (or micro-frontends) and cloud-native applications.

Day 9️⃣: Build a Serverless Backend using Azure Functions

Day 1️⃣3️⃣: Easily connect to an Azure Cosmos DB, exploring its benefits and how to get started

Day 1️⃣5️⃣: Being in the AI Era, we dive into the Azure OpenAI Service and how you can start to build intelligent JavaScript applications

📖 Self-Learning Resources

  1. JavaScript on Azure Global Hack Together Module collection
  2. Lets #HackTogether: Javascript On Azure Keynote
  3. Step by Step Guide: Migrating v3 to v4 programming model for Azure Functions for Node.Js Application

Continue your journey with #FallForIntelligentApps

Join us this Fall on a learning journey to explore building intelligent apps. Combine the power of AI, cloud-native app development, and cloud-scale data to build highly differentiated digital experiences. Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure. Engage in a self-paced learning adventure by following along the #30Days of Intelligent Apps series, completing the Intelligent Apps Skills Challenge, joining the Product Group for a live Ask The Expert series or building an end to end solution architecture with a live guided experience through the Learn Live series. Discover more here.

Hands-on practice: Make your first contribution to open source!

Join our GitHUb Discussion Forum to connect with developers from every part of world, see contributions from other, find collaborators and make your first contribution to a real-world project! +

HackTogether Recap 🍂

· 4 min read
It's 30DaysOfIA

Continue The Learning Journey through Fall For Intelligent Apps! 🍂

What We'll Cover

Thank you! ♥️

image

It's hard to believe that JavaScript on Azure hack-together is ending! It seems like just yesterday that we launched this initiative, and yet here we are, 15 days later, with an incredible amount of learning and growth behind us. So... it's time for a wrap!

From the bottom of our hearts, we want to thank each and every one of you for your participation, engagement, and enthusiasm. It's been truly inspiring to see the passion and dedication from this strong community, and we're honored to be a part of it. ✨

Recap of The JavaScript on Azure Global Hack-Together

As we wrap up this exciting event, we wanted to take a moment to reflect on all that we've accomplished together. Over the last 15 days, we've covered a lot of ground, from the basics of contributing to Open source to the exploration of the Contoso Real Estate project from its Frontend to its Backend and future AI implementation.

Now that the hack-together is ending, we want to make sure that you have all the resources you need to continue honing your skills in the future. Whether you're looking to make your fist contribution to open source, become an open source maintainers, collaborate with others, or simply keep learning, there are plenty of resources out there to help you achieve your goals. So, let's dive in and explore all the ways you can continue to grow your JavaScript skills on Azure!

JSonAzure Hack-together Roadmap 📍:

hack-together-roadmap (2)

Recap on past Livestreams🌟:

Day 1️⃣: Opening Keynote (Hack-together Launch): Introduction to the Contoso Real Estate Open-source project!, managing complex and complex enterprise architecture, new announcements for JavaScript developers on Azure

Day 2️⃣: GitHub Copilot & Codespaces: Introduction to your AI pair programmer (GitHub Copilot) and your virtual developer environment on the cloud (GitHub Codespaces)

Day 6️⃣: Build your Frontend using Static Web Apps as part of a complex, modern composable frontends (or micro-frontends) and cloud-native applications.

Day 9️⃣: Build a Serverless Backend using Azure Functions

Day 1️⃣3️⃣: Easily connect to an Azure Cosmos DB, exploring its benefits and how to get started

Day 1️⃣5️⃣: Being in the AI Era, we dive into the Azure OpenAI Service and how you can start to build intelligent JavaScript applications

📖 Self-Learning Resources

  1. JavaScript on Azure Global Hack Together Module collection
  2. Lets #HackTogether: Javascript On Azure Keynote
  3. Step by Step Guide: Migrating v3 to v4 programming model for Azure Functions for Node.Js Application

Continue your journey with #FallForIntelligentApps

Join us this Fall on a learning journey to explore building intelligent apps. Combine the power of AI, cloud-native app development, and cloud-scale data to build highly differentiated digital experiences. Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure. Engage in a self-paced learning adventure by following along the #30Days of Intelligent Apps series, completing the Intelligent Apps Skills Challenge, joining the Product Group for a live Ask The Expert series or building an end to end solution architecture with a live guided experience through the Learn Live series. Discover more here.

Hands-on practice: Make your first contribution to open source!

Join our GitHUb Discussion Forum to connect with developers from every part of world, see contributions from other, find collaborators and make your first contribution to a real-world project! Don't forget to give the repo a star ⭐

Resources

All resources are accessible on our landing page

- - + + \ No newline at end of file diff --git a/30daysofIA/harnessing-the-power-of-intelligent-apps/index.html b/30daysofIA/harnessing-the-power-of-intelligent-apps/index.html index f287d9acbb..1ed8295ebd 100644 --- a/30daysofIA/harnessing-the-power-of-intelligent-apps/index.html +++ b/30daysofIA/harnessing-the-power-of-intelligent-apps/index.html @@ -14,13 +14,13 @@ - - + +
-

1-2. Harnessing the Power of Intelligent Apps

· 10 min read
It's 30DaysOfIA

Explore real-world examples of how Intelligent Apps revolutionize logistics, retail, finance, manufacturing, tech, and medicine.

What We'll Cover:

  • Real-world businesses revolutionizing operations with Intelligent Apps
  • High level architecture for industry scenarios
  • Implementation architecture overview across industries

A decorative header image for the blog

Real-world Success Stories

According to IBM’s 2022 Global AI Adoption Index, 35% of surveyed companies already use AI in their business, and 42% are exploring the idea. Organizations lagging behind the AI curve will quickly fall behind early adopters, who are already enjoying its benefits and boosting their investment in this technology.

In the first part of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” we explored how organizations can use AI-powered apps, or intelligent apps, to transform their operations, decision-making, and customer experiences, leaping ahead of the competition.

Intelligent apps significantly improve operational efficiency and customer engagement across industries like logistics, retail, finance, manufacturing, tech, and healthcare. In this article, we’ll spotlight real-world success stories of businesses revolutionizing operations with intelligent apps using Microsoft Azure’s app, data, and AI services.

info

Register for the Learn Live on September 21 for a live guided experience with SMEs on how to build end-to-end solution architecture for real world implementation of Intelligent Apps.

Streamlining Operations: Intelligent Apps in the Airline Industry

Aerospace pioneer Airbus leveraged intelligent apps to streamline its operations and innovate its services with two ground-breaking modernizations that use Azure apps, data, and AI services to reimagine pilot training and predict aircraft maintenance issues. 

Aviation technology’s rapid evolution compelled Airbus to reconceive their approach to training pilots. The aircraft manufacturer created a pilot training chatbot by combining Azure AI Services with Speech to Text and Text to Speech. The bot helps trainee pilots navigate over 6,000 pages of technical information. Pilots can ask questions verbally for quick answers, aiding their understanding of dense information and speeding up training. And when Airbus needed to accommodate its military consumers, deploying its Kubernetes cluster to a disconnected, “air-gapped” environment met the strict security requirements of military aircraft training.

A chatbot could look like the following diagram as Azure services work together to create a conversational experience. Its components could include security and governance, bot logic and user experience (UX), bot cognition and intelligence, data extract, transfer, and load (ETL) operations, quality assurance and enhancements, logging, and monitoring and reporting.

A diagram of a conversational experience enabled by Azure services

Airbus also used AI to improve its aircraft maintenance and safety. They deployed AI Anomaly Detector to gather and analyze telemetry data from multiple flights, enabling more profound insights into wear and tear on military aircraft operating in harsh conditions. This AI application helps Airbus better understand each airplane’s health. Airbus can predict and fix potential problems before they occur, improving the aircraft’s safety and operational readiness.

AI and Azure are changing how organizations like Airbus operate, even in strict, highly regulated markets with stringent security requirements. And by streamlining pilot training and staying ahead of aircraft maintenance, Airbus helps its military and commercial clients reliably deliver their goods across the globe, maintaining their critical supply chains.

Boosting Customer Service: Intelligent Apps in the Retail Industry

The retail industry has been among the first to adopt and benefit from intelligent apps. For example, the leading American used car retailer, CarMax, uses intelligent apps to transform the car shopping experience.

Before embracing AI, CarMax faced a significant challenge: Manually creating text summaries was time-consuming and labor-intensive. CarMax decided to leverage Azure OpenAI Service to automatically generate car research pages, offering customers valuable insights while enhancing the website’s search engine rankings.

This AI-driven approach has significantly streamlined the process, achieving in a few months what would have taken years manually. Also, the AI system condenses thousands of customer reviews into a few readable sentences, offering potential buyers a quick overview of others’ experiences.

CarMax has improved their customer experience while boosting operational efficiency through AI adoption. CarMax’s editorial staff focuses on creating strategic, longer-form content with the time saved, enhancing productivity, and driving more website traffic.

CarMax’s success story serves as a testament to the potential of intelligent apps in the retail industry.

Enhancing Cybersecurity: Intelligent Apps in Finance

Intelligent apps also impact the highly regulated financial services sector. Swift, a leading infrastructure provider for financial messaging services, uses intelligent apps to enhance decision-making and combat financial crime.

The massive growth of cross-border transactions and instant payment networks led to a surge in financial fraud that can cost the industry hundreds of billions of dollars every year. So, Swift and Microsoft partnered to build an anomaly detection model using federated learning techniques with Azure Machine Learning and Azure confidential computing.

Microsoft Purview helped govern data, as the federated learning method partially trained each model in a silo before aggregating them. This approach enabled Swift and Microsoft to develop a highly accurate model without copying or moving data from Swift members’ secure locations, ensuring the highest level of security and privacy.

Financial organizations can now deploy this trained ML model to run a real-time fraud detection system, lowering the cost of financial fraud.

Organizations can deploy a fraud detection system like the following example on Microsoft Azure. The system ingests data from streams or storage, runs analytics, and uses ML to detect fraud and store results. It then alerts users and other systems to the security issue.

A diagram of an example fraud detection system

Swift provides the innovative model to its banking partners, who can share data to further train the model. Azure confidential computing securely runs new models on current data to accumulate insights, generating an ongoing learning cycle. This iterative approach strengthens the global detection of fraudulent financial transactions through an increasingly faster and more accurate model. 

The collaboration between Swift and Microsoft demonstrates how combining various Azure services creates Intelligent Apps to enhance decision-making in finance. These apps solve real problems at an unprecedented global scale. 

Enhancing Efficiency: Intelligent Apps in Manufacturing

3M, known for Post-it Notes and other innovative products, wielded the power of Intelligent Apps to standardize, automate, and accelerate its sales forecasting. The company needed a unified, automated approach to replace its multiple manual methods.

It adopted Azure Machine Learning and other Azure AI Services to develop, train, and implement 1,500 custom models for each region and division, replacing manual methods. This solution delivers timely finance, sales, and marketing insights while freeing teams from time-consuming manual data manipulation tasks.

3M used Azure Machine Learning, automated machine learning (AutoML), and the Many Models Solution Accelerator to train and score numerous machine learning models in parallel. This approach significantly shortened the company’s development cycle time.

Then, 3M integrated Microsoft Power BI into their solution to compile and visualize results more effectively. Executives now have an unbiased and unfiltered view of data for better decision-making. With its improved ability to forecast sales, 3M can focus on manufacturing the products it needs efficiently.

3M’s success demonstrates how combining various Azure services can enhance efficiency in the manufacturing industry. These apps improve operational processes while enabling employees to focus on higher-level tasks. Strategic decisions are more targeted, and business conversations are more meaningful.

Driving Innovation: Intelligent Apps in the Technology Industry

As expected, the tech industry is at the forefront of leveraging Intelligent Apps to drive innovation. Elastic saw an opportunity to use intelligent apps to optimize its search services and meet customer needs more effectively. The company built a managed, secure, and scalable solution for its customers, Elastic Cloud, by leveraging Microsoft Azure.

Elastic Cloud uses Azure Machine Learning and other Azure AI Services to automate many basic management tasks, such as backing up data, upgrading, and scaling deployments. This approach enables Elastic’s customers to focus less on managing the software and more on serving their own clients. Customers can quickly deploy and manage Elastic Cloud on Kubernetes (ECK) using Azure Kubernetes Service.

As a result, Elastic’s cloud business is growing twice as fast as its on-premises solutions. Enterprise search users have reported significant improvements in system availability and employee satisfaction.

Additionally, Elastic incorporated Azure OpenAI Service into its Elasticsearch Relevance Engine (ESRE), providing more relevant answers to search queries. ESRE enables text, vector, and hybrid search, offering a more effective and accurate response to queries.

Elastic’s success story demonstrates how combining various Azure services can create intelligent Apps to drive innovation in the tech industry. These apps help improve operational processes and deliver better results quickly.

Revolutionizing Healthcare: Intelligent Apps in Medicine

Intelligent apps also drive transformative changes in the healthcare sector. Cambridgeshire and Peterborough NHS Foundation Trust, a healthcare provider in the United Kingdom, gradually shifted their on-premises systems to the cloud using Azure, improving flexibility, and lowering costs. They needed a solution for their electronic patient record (EPR) system that integrates 23 years of data across multiple systems and formats.

The Trust used Microsoft Azure and Azure Cognitive Search, including semantic search for understanding intent and contexts and cognitive capabilities for extracting insights from data, to standardize and automate data management. This approach made legacy, archive, and live data more discoverable, saving clinicians’ time. The Trust’s solution searches through millions of clinical documents in seconds, even recognizing handwritten notes without needing a separate optical character recognition (OCR) solution to extract text from images (Azure Cognitive Search automatically does it). Clinicians can now uncover previously inaccessible insights, enabling them to offer more personalized care.

The Trust also used Integration Services tools such as Azure Logic Apps to create workflows without writing code and Event Grid to connect Azure and third-party services through a publisher-subscriber model. They switched on a live feed from their new EPR system to Azure to feed real-time information into other applications. Researchers can now access anonymized data to fuel medical innovations beyond the NHS.

The healthcare provider also invested heavily in Power BI’s data visualization and analytical functions to sift through massive amounts of information. This level of data accessibility was previously impossible with their on-premises system. The new insights have significantly improved clinicians’ efficiency for better patient care.

Data accessibility and security are critical in the healthcare industry. Intelligent Apps empower organizations like the Trust to revolutionize medical care. AI capabilities helped the Trust improve its operational efficiency and user experiences, setting a new standard for healthcare in the digital age.

Summary

AI is driving transformation and real-world successes across industries. Intelligent apps are reshaping our world by streamlining operations in manufacturing and logistics, enhancing customer experience in the retail sector, improving decision-making in finance, driving innovation in tech, and revolutionizing healthcare.

As organizations adopt AI, they’re innovating in ways that were once too difficult, expensive, or outright impossible. Companies that wait to embrace intelligent apps risk the chance of falling behind. By embracing intelligent apps and leveraging the capabilities of Azure, you can harness the transformative potential of AI to remain on the cutting edge of technology.

Exercise

- - +

1-2. Harnessing the Power of Intelligent Apps

· 10 min read
It's 30DaysOfIA

Explore real-world examples of how Intelligent Apps revolutionize logistics, retail, finance, manufacturing, tech, and medicine.

What We'll Cover:

  • Real-world businesses revolutionizing operations with Intelligent Apps
  • High level architecture for industry scenarios
  • Implementation architecture overview across industries

A decorative header image for the blog

Real-world Success Stories

According to IBM’s 2022 Global AI Adoption Index, 35% of surveyed companies already use AI in their business, and 42% are exploring the idea. Organizations lagging behind the AI curve will quickly fall behind early adopters, who are already enjoying its benefits and boosting their investment in this technology.

In the first part of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” we explored how organizations can use AI-powered apps, or intelligent apps, to transform their operations, decision-making, and customer experiences, leaping ahead of the competition.

Intelligent apps significantly improve operational efficiency and customer engagement across industries like logistics, retail, finance, manufacturing, tech, and healthcare. In this article, we’ll spotlight real-world success stories of businesses revolutionizing operations with intelligent apps using Microsoft Azure’s app, data, and AI services.

info

Register for the Learn Live on September 21 for a live guided experience with SMEs on how to build end-to-end solution architecture for real world implementation of Intelligent Apps.

Streamlining Operations: Intelligent Apps in the Airline Industry

Aerospace pioneer Airbus leveraged intelligent apps to streamline its operations and innovate its services with two ground-breaking modernizations that use Azure apps, data, and AI services to reimagine pilot training and predict aircraft maintenance issues. 

Aviation technology’s rapid evolution compelled Airbus to reconceive their approach to training pilots. The aircraft manufacturer created a pilot training chatbot by combining Azure AI Services with Speech to Text and Text to Speech. The bot helps trainee pilots navigate over 6,000 pages of technical information. Pilots can ask questions verbally for quick answers, aiding their understanding of dense information and speeding up training. And when Airbus needed to accommodate its military consumers, deploying its Kubernetes cluster to a disconnected, “air-gapped” environment met the strict security requirements of military aircraft training.

A chatbot could look like the following diagram as Azure services work together to create a conversational experience. Its components could include security and governance, bot logic and user experience (UX), bot cognition and intelligence, data extract, transfer, and load (ETL) operations, quality assurance and enhancements, logging, and monitoring and reporting.

A diagram of a conversational experience enabled by Azure services

Airbus also used AI to improve its aircraft maintenance and safety. They deployed AI Anomaly Detector to gather and analyze telemetry data from multiple flights, enabling more profound insights into wear and tear on military aircraft operating in harsh conditions. This AI application helps Airbus better understand each airplane’s health. Airbus can predict and fix potential problems before they occur, improving the aircraft’s safety and operational readiness.

AI and Azure are changing how organizations like Airbus operate, even in strict, highly regulated markets with stringent security requirements. And by streamlining pilot training and staying ahead of aircraft maintenance, Airbus helps its military and commercial clients reliably deliver their goods across the globe, maintaining their critical supply chains.

Boosting Customer Service: Intelligent Apps in the Retail Industry

The retail industry has been among the first to adopt and benefit from intelligent apps. For example, the leading American used car retailer, CarMax, uses intelligent apps to transform the car shopping experience.

Before embracing AI, CarMax faced a significant challenge: Manually creating text summaries was time-consuming and labor-intensive. CarMax decided to leverage Azure OpenAI Service to automatically generate car research pages, offering customers valuable insights while enhancing the website’s search engine rankings.

This AI-driven approach has significantly streamlined the process, achieving in a few months what would have taken years manually. Also, the AI system condenses thousands of customer reviews into a few readable sentences, offering potential buyers a quick overview of others’ experiences.

CarMax has improved their customer experience while boosting operational efficiency through AI adoption. CarMax’s editorial staff focuses on creating strategic, longer-form content with the time saved, enhancing productivity, and driving more website traffic.

CarMax’s success story serves as a testament to the potential of intelligent apps in the retail industry.

Enhancing Cybersecurity: Intelligent Apps in Finance

Intelligent apps also impact the highly regulated financial services sector. Swift, a leading infrastructure provider for financial messaging services, uses intelligent apps to enhance decision-making and combat financial crime.

The massive growth of cross-border transactions and instant payment networks led to a surge in financial fraud that can cost the industry hundreds of billions of dollars every year. So, Swift and Microsoft partnered to build an anomaly detection model using federated learning techniques with Azure Machine Learning and Azure confidential computing.

Microsoft Purview helped govern data, as the federated learning method partially trained each model in a silo before aggregating them. This approach enabled Swift and Microsoft to develop a highly accurate model without copying or moving data from Swift members’ secure locations, ensuring the highest level of security and privacy.

Financial organizations can now deploy this trained ML model to run a real-time fraud detection system, lowering the cost of financial fraud.

Organizations can deploy a fraud detection system like the following example on Microsoft Azure. The system ingests data from streams or storage, runs analytics, and uses ML to detect fraud and store results. It then alerts users and other systems to the security issue.

A diagram of an example fraud detection system

Swift provides the innovative model to its banking partners, who can share data to further train the model. Azure confidential computing securely runs new models on current data to accumulate insights, generating an ongoing learning cycle. This iterative approach strengthens the global detection of fraudulent financial transactions through an increasingly faster and more accurate model. 

The collaboration between Swift and Microsoft demonstrates how combining various Azure services creates Intelligent Apps to enhance decision-making in finance. These apps solve real problems at an unprecedented global scale. 

Enhancing Efficiency: Intelligent Apps in Manufacturing

3M, known for Post-it Notes and other innovative products, wielded the power of Intelligent Apps to standardize, automate, and accelerate its sales forecasting. The company needed a unified, automated approach to replace its multiple manual methods.

It adopted Azure Machine Learning and other Azure AI Services to develop, train, and implement 1,500 custom models for each region and division, replacing manual methods. This solution delivers timely finance, sales, and marketing insights while freeing teams from time-consuming manual data manipulation tasks.

3M used Azure Machine Learning, automated machine learning (AutoML), and the Many Models Solution Accelerator to train and score numerous machine learning models in parallel. This approach significantly shortened the company’s development cycle time.

Then, 3M integrated Microsoft Power BI into their solution to compile and visualize results more effectively. Executives now have an unbiased and unfiltered view of data for better decision-making. With its improved ability to forecast sales, 3M can focus on manufacturing the products it needs efficiently.

3M’s success demonstrates how combining various Azure services can enhance efficiency in the manufacturing industry. These apps improve operational processes while enabling employees to focus on higher-level tasks. Strategic decisions are more targeted, and business conversations are more meaningful.

Driving Innovation: Intelligent Apps in the Technology Industry

As expected, the tech industry is at the forefront of leveraging Intelligent Apps to drive innovation. Elastic saw an opportunity to use intelligent apps to optimize its search services and meet customer needs more effectively. The company built a managed, secure, and scalable solution for its customers, Elastic Cloud, by leveraging Microsoft Azure.

Elastic Cloud uses Azure Machine Learning and other Azure AI Services to automate many basic management tasks, such as backing up data, upgrading, and scaling deployments. This approach enables Elastic’s customers to focus less on managing the software and more on serving their own clients. Customers can quickly deploy and manage Elastic Cloud on Kubernetes (ECK) using Azure Kubernetes Service.

As a result, Elastic’s cloud business is growing twice as fast as its on-premises solutions. Enterprise search users have reported significant improvements in system availability and employee satisfaction.

Additionally, Elastic incorporated Azure OpenAI Service into its Elasticsearch Relevance Engine (ESRE), providing more relevant answers to search queries. ESRE enables text, vector, and hybrid search, offering a more effective and accurate response to queries.

Elastic’s success story demonstrates how combining various Azure services can create intelligent Apps to drive innovation in the tech industry. These apps help improve operational processes and deliver better results quickly.

Revolutionizing Healthcare: Intelligent Apps in Medicine

Intelligent apps also drive transformative changes in the healthcare sector. Cambridgeshire and Peterborough NHS Foundation Trust, a healthcare provider in the United Kingdom, gradually shifted their on-premises systems to the cloud using Azure, improving flexibility, and lowering costs. They needed a solution for their electronic patient record (EPR) system that integrates 23 years of data across multiple systems and formats.

The Trust used Microsoft Azure and Azure Cognitive Search, including semantic search for understanding intent and contexts and cognitive capabilities for extracting insights from data, to standardize and automate data management. This approach made legacy, archive, and live data more discoverable, saving clinicians’ time. The Trust’s solution searches through millions of clinical documents in seconds, even recognizing handwritten notes without needing a separate optical character recognition (OCR) solution to extract text from images (Azure Cognitive Search automatically does it). Clinicians can now uncover previously inaccessible insights, enabling them to offer more personalized care.

The Trust also used Integration Services tools such as Azure Logic Apps to create workflows without writing code and Event Grid to connect Azure and third-party services through a publisher-subscriber model. They switched on a live feed from their new EPR system to Azure to feed real-time information into other applications. Researchers can now access anonymized data to fuel medical innovations beyond the NHS.

The healthcare provider also invested heavily in Power BI’s data visualization and analytical functions to sift through massive amounts of information. This level of data accessibility was previously impossible with their on-premises system. The new insights have significantly improved clinicians’ efficiency for better patient care.

Data accessibility and security are critical in the healthcare industry. Intelligent Apps empower organizations like the Trust to revolutionize medical care. AI capabilities helped the Trust improve its operational efficiency and user experiences, setting a new standard for healthcare in the digital age.

Summary

AI is driving transformation and real-world successes across industries. Intelligent apps are reshaping our world by streamlining operations in manufacturing and logistics, enhancing customer experience in the retail sector, improving decision-making in finance, driving innovation in tech, and revolutionizing healthcare.

As organizations adopt AI, they’re innovating in ways that were once too difficult, expensive, or outright impossible. Companies that wait to embrace intelligent apps risk the chance of falling behind. By embracing intelligent apps and leveraging the capabilities of Azure, you can harness the transformative potential of AI to remain on the cutting edge of technology.

Exercise

+ + \ No newline at end of file diff --git a/30daysofIA/how-digital-natives-leverage-generative-ai/index.html b/30daysofIA/how-digital-natives-leverage-generative-ai/index.html index 444415c738..3224f486ca 100644 --- a/30daysofIA/how-digital-natives-leverage-generative-ai/index.html +++ b/30daysofIA/how-digital-natives-leverage-generative-ai/index.html @@ -14,13 +14,13 @@ - - + +
-

1-4. How Digital Natives leverage Generative AI

· 10 min read
It's 30DaysOfIA

Explore how digital natives and Independent Software Vendors (ISVs) can transform their business operations and generate higher value for customers with generative AI and intelligent apps.

What We'll Cover:

  • Potential of Generative AI in application development
  • Challenges with Generative AI
  • Integrating Generative AI with business operations

image of generative AI and intelligent apps used in business

Unleashing Business Transformation: How Digital Natives Can Leverage Generative AI

Generative AI is no longer a technological flight of fancy. It’s here, and it’s rewriting the rules of business innovation. 

This type of AI leverages machine learning algorithms to create new data with similar characteristics to a given dataset. It goes beyond text generation using generative pretrained transformer (GPT) models, covering an array of applications that span from image synthesis to translations and more. It also powers a new class of intelligent applications, which combine AI capabilities with traditional software functions for enhanced user experiences and advanced business solutions. 

Generative AI is transforming how digital natives and independent software vendors (ISVs) operate. Generative AI offers new possibilities for creating unique business value, enhancing customer experiences, and driving growth. 

It plays a critical role in applications, improving functionality, aiding decision-making, and automating complex tasks to deliver dynamic, uniquely personalized user experiences. Generative AI also enables and automates data-driven outcomes, functions, and features.

Integrating AI capabilities into customer-facing apps and turning them into intelligent apps benefits both the customer and the business, as we discussed in Demystifying Intelligent Applications: Leveraging AI in App Development. You get faster product development workflows and better business intelligence for sales and marketing, while your users get more natural and context-aware features and interactions. 

That said, to realize generative AI’s potential, you need some strategy and knowledge of how and where it fits into your current business and operations. We’ll examine this technology’s potential to transform business operations, its risks, and rewards, and how to integrate it into your existing operations. 

Unpacking the Potential of Generative AI

Generative AI is creating new opportunities in the digital playing field. It empowers businesses to generate insights, design products, and automate processes in ways and at speeds that were previously unimaginable. You can now predict customer behavior more accurately, offer highly personalized user experiences, and prototype innovative ideas rapidly. 

For example, Microsoft used generative AI to reinvent web search in the new AI-powered Bing and Edge. The search experience has evolved from a solitary user flow of typing in keywords and scrolling through results. It’s now a conversational experience where Bing uses context to offer better quality and more personalized search queries and results. 

Similarly, Microsoft harnessed generative AI to transform its productivity suite into intelligent apps. Through Copilot for Microsoft 365, PowerPoint now empowers users to tell their stories by using DALL-E to generate images effortlessly. It also helps rephrase text-heavy slides to be more straightforward. Outlook now assists users to compose more effective emails, enabling better communication.

These are just some examples of how AI powers day-to-day work and operations. 

Businesses that ignore generative AI miss out on untapped market opportunities, letting competitors gain an edge in the digital race. By seeking ways to rethink assumptions and reinvent existing approaches, you can effectively use generative AI to create new opportunities, open products to new use cases and markets, unlock new growth and customer satisfaction levels, and increase your revenue.

info

Register for the intelligent apps webinar on Driving Business Value by Modernizing with Cloud-Native & AI with Microsoft and Forrester on September 26.

Explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries. 

The Crucial Role of Generative AI in Business Operations

Generative AI can be a powerful catalyst for business transformation. Let’s quickly tour some ways it can redefine business operations. 

Product development is traditionally a long and painstaking process. Now, tools like GitHub Copilot and TestPilot use generative AI to make coding and testing software easier, faster, and more robust. 

Similarly, machine learning as a service technology, such as Azure Machine Learning, democratizes the availability of machine learning. It helps collect, clean, and train models across datasets, making engineering and analyzing data more efficient and accessible.  

Generative AI also simplifies the process of creating intelligent apps. Previously, simulating thousands of prototypes, predicting their performance, and narrowing down to the most viable options could take weeks, months, or even years of iteration and data analysis. With generative AI, this process can be as quick as days, hours, or perhaps minutes. 

Customer experience is also ripe for transformation. Personalized recommendations drive engagement and sales, and generative AI can scale that personalization to every customer interaction using comprehensive and continually updated data. For instance, Microsoft leverages AI to enable personalization in Dynamics 365 Customer Insights. Using this tool, you can ask questions about your customers in natural language and gain valuable and actionable insights into your customer base. 

Lastly, marketing and sales can benefit from generative AI. For example, identifying current and future key markets and emerging trends helps companies stay ahead of the curve. Tools like Copilot in Power BI help you quickly understand and uncover insights about your business, enabling your team to craft targeted campaigns, create engaging content, and optimize your business strategy. 

Leveraging this full generative AI potential requires the right tools. GPT-4, for instance, can be instrumental in realizing effective business transformation. It excels in understanding context, generating human-like text, and learning from minimal data input, making it a formidable tool in any digital native's arsenal. Platforms like Azure OpenAI Service bring GPT-4’s power to your fingertips.

However, as with any powerful technology, it’s unwise to treat generative AI as a perfect solution. You must know the challenges and risks of not leveraging it effectively, including technical and human-factor considerations. 

Without generative AI, your company risks missing the opportunity to please customers via personalization, as discussed previously. You’ll miss sales opportunities via business intelligence insights, limiting your ability to grow revenue and keep up with the competition. 

One challenge of leveraging generative AI’s full potential is finding the right tools. Fortunately, you can implement generative AI in your organization without investing in infrastructure, computational power, and expertise. Platforms like Azure OpenAI Service and Azure AI Services make AI easily accessible. They also work with Azure compute and data services like Azure Functions and Azure Kubernetes Service (AKS), enabling you to build intelligent apps easily and quickly. 

However, generative AI can’t do everything. It may need human expertise and intervention to use it fully. 

For example, generative AI can create unique data. However, the data may have limited diversity and complexity. The AI may need help recognizing additional considerations or requirements specific to a problem. It may also lack context due to various factors, such as the information’s freshness. Also, since it’s an emerging field that’s potentially worth $4.4 trillion, we’re still learning about the nuances and potential regulatory impact of generative AI. 

Perhaps the most crucial and challenging aspect of adopting generative AI is an organization’s required cultural shift. Your teams must learn how to use AI as your company fosters an understanding of AI’s potential and limitations. 

Begin integrating AI literacy into your company’s DNA. Knowledge of its potential and the readily available tools can inspire your teams to incorporate generative AI into their daily workflows and business operations quickly, improve productivity, and deliver superior customer experiences. 

No company wants to be outpaced by its competitors. You can balance generative AI’s potential with your business operations by staying aware of and adapting to the dynamic AI landscape.

Achieving Harmony: Integrating Generative AI with Existing Business Operations

Integrating generative AI into existing business operations doesn’t have to be disruptive. With the correct strategy, you can do so seamlessly. Your goal should be to augment and enhance your operations, not disrupt and replace. 

For example, Microsoft Cloud technologies leveraged AI to relieve healthcare organizations’ challenges, such as talent shortage and workforce burnout. Providers automated their workflows, invested in specific applications and connectors, and enhanced support for data and operations. 

Azure AI Services, such as text analytics for health, enable healthcare organizations to understand their largely unstructured and untapped medical data. Project Health Insights leans on AI to help healthcare professionals gain actionable insights and inferences from patient data. 

Azure Health Bot, an Intelligent App, empowers organizations to build and deploy legally compliant, powerful conversational healthcare experiences to patients. 

A successful AI integration like this begins with understanding. Knowing what generative AI can do and setting realistic expectations can help you identify where and how AI can best serve your operations. 

Next, you can foster a culture of AI literacy across teams, speeding up adoption by helping them understand how it can aid their work. The more familiar your team is with AI and its possibilities, the smoother the transition. Training and education programs play a pivotal role here. 

Finally, integration requires a systematic approach. It’s not about overhauling everything at once — it's about identifying areas where AI can add the most value, then gradually experimenting and scaling its use. Successful integration stories often feature a phased approach, starting small and growing steadily. 

As we move toward a future where generative AI is a staple in business operations, remember that every journey is unique. Although there’s no one-size-fits-all solution, a strategic approach and a keen understanding of your business needs will help you navigate this new landscape successfully.

Conclusion

Generative AI offers plenty of opportunities for businesses that embrace it. It’s fueling the next generation of intelligent applications, transforming everything from product development to customer engagement to marketing. This technology is the key to unlocking unprecedented growth and innovation. 

While the journey may have challenges, the potential rewards make generative AI worth exploring. As generative AI advances, Azure AI’s broad range of tools and services will help you navigate this fascinating field.

Exercise

  • Watch Episode 02 of the serverless edition Learn Live session to learn how to build, test and deploy an end-to-end intelligent app solution.
  • Complete the Cloud Skills Challenge to build on your AI skills.
  • Register for Ask the Expert: Azure Functions on September 26 for a live Q&A with the Product Team on how to build AI apps using serverless computing.
- - +

1-4. How Digital Natives leverage Generative AI

· 10 min read
It's 30DaysOfIA

Explore how digital natives and Independent Software Vendors (ISVs) can transform their business operations and generate higher value for customers with generative AI and intelligent apps.

What We'll Cover:

  • Potential of Generative AI in application development
  • Challenges with Generative AI
  • Integrating Generative AI with business operations

image of generative AI and intelligent apps used in business

Unleashing Business Transformation: How Digital Natives Can Leverage Generative AI

Generative AI is no longer a technological flight of fancy. It’s here, and it’s rewriting the rules of business innovation. 

This type of AI leverages machine learning algorithms to create new data with similar characteristics to a given dataset. It goes beyond text generation using generative pretrained transformer (GPT) models, covering an array of applications that span from image synthesis to translations and more. It also powers a new class of intelligent applications, which combine AI capabilities with traditional software functions for enhanced user experiences and advanced business solutions. 

Generative AI is transforming how digital natives and independent software vendors (ISVs) operate. Generative AI offers new possibilities for creating unique business value, enhancing customer experiences, and driving growth. 

It plays a critical role in applications, improving functionality, aiding decision-making, and automating complex tasks to deliver dynamic, uniquely personalized user experiences. Generative AI also enables and automates data-driven outcomes, functions, and features.

Integrating AI capabilities into customer-facing apps and turning them into intelligent apps benefits both the customer and the business, as we discussed in Demystifying Intelligent Applications: Leveraging AI in App Development. You get faster product development workflows and better business intelligence for sales and marketing, while your users get more natural and context-aware features and interactions. 

That said, to realize generative AI’s potential, you need some strategy and knowledge of how and where it fits into your current business and operations. We’ll examine this technology’s potential to transform business operations, its risks, and rewards, and how to integrate it into your existing operations. 

Unpacking the Potential of Generative AI

Generative AI is creating new opportunities in the digital playing field. It empowers businesses to generate insights, design products, and automate processes in ways and at speeds that were previously unimaginable. You can now predict customer behavior more accurately, offer highly personalized user experiences, and prototype innovative ideas rapidly. 

For example, Microsoft used generative AI to reinvent web search in the new AI-powered Bing and Edge. The search experience has evolved from a solitary user flow of typing in keywords and scrolling through results. It’s now a conversational experience where Bing uses context to offer better quality and more personalized search queries and results. 

Similarly, Microsoft harnessed generative AI to transform its productivity suite into intelligent apps. Through Copilot for Microsoft 365, PowerPoint now empowers users to tell their stories by using DALL-E to generate images effortlessly. It also helps rephrase text-heavy slides to be more straightforward. Outlook now assists users to compose more effective emails, enabling better communication.

These are just some examples of how AI powers day-to-day work and operations. 

Businesses that ignore generative AI miss out on untapped market opportunities, letting competitors gain an edge in the digital race. By seeking ways to rethink assumptions and reinvent existing approaches, you can effectively use generative AI to create new opportunities, open products to new use cases and markets, unlock new growth and customer satisfaction levels, and increase your revenue.

info

Register for the intelligent apps webinar on Driving Business Value by Modernizing with Cloud-Native & AI with Microsoft and Forrester on September 26.

Explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries. 

The Crucial Role of Generative AI in Business Operations

Generative AI can be a powerful catalyst for business transformation. Let’s quickly tour some ways it can redefine business operations. 

Product development is traditionally a long and painstaking process. Now, tools like GitHub Copilot and TestPilot use generative AI to make coding and testing software easier, faster, and more robust. 

Similarly, machine learning as a service technology, such as Azure Machine Learning, democratizes the availability of machine learning. It helps collect, clean, and train models across datasets, making engineering and analyzing data more efficient and accessible.  

Generative AI also simplifies the process of creating intelligent apps. Previously, simulating thousands of prototypes, predicting their performance, and narrowing down to the most viable options could take weeks, months, or even years of iteration and data analysis. With generative AI, this process can be as quick as days, hours, or perhaps minutes. 

Customer experience is also ripe for transformation. Personalized recommendations drive engagement and sales, and generative AI can scale that personalization to every customer interaction using comprehensive and continually updated data. For instance, Microsoft leverages AI to enable personalization in Dynamics 365 Customer Insights. Using this tool, you can ask questions about your customers in natural language and gain valuable and actionable insights into your customer base. 

Lastly, marketing and sales can benefit from generative AI. For example, identifying current and future key markets and emerging trends helps companies stay ahead of the curve. Tools like Copilot in Power BI help you quickly understand and uncover insights about your business, enabling your team to craft targeted campaigns, create engaging content, and optimize your business strategy. 

Leveraging this full generative AI potential requires the right tools. GPT-4, for instance, can be instrumental in realizing effective business transformation. It excels in understanding context, generating human-like text, and learning from minimal data input, making it a formidable tool in any digital native's arsenal. Platforms like Azure OpenAI Service bring GPT-4’s power to your fingertips.

However, as with any powerful technology, it’s unwise to treat generative AI as a perfect solution. You must know the challenges and risks of not leveraging it effectively, including technical and human-factor considerations. 

Without generative AI, your company risks missing the opportunity to please customers via personalization, as discussed previously. You’ll miss sales opportunities via business intelligence insights, limiting your ability to grow revenue and keep up with the competition. 

One challenge of leveraging generative AI’s full potential is finding the right tools. Fortunately, you can implement generative AI in your organization without investing in infrastructure, computational power, and expertise. Platforms like Azure OpenAI Service and Azure AI Services make AI easily accessible. They also work with Azure compute and data services like Azure Functions and Azure Kubernetes Service (AKS), enabling you to build intelligent apps easily and quickly. 

However, generative AI can’t do everything. It may need human expertise and intervention to use it fully. 

For example, generative AI can create unique data. However, the data may have limited diversity and complexity. The AI may need help recognizing additional considerations or requirements specific to a problem. It may also lack context due to various factors, such as the information’s freshness. Also, since it’s an emerging field that’s potentially worth $4.4 trillion, we’re still learning about the nuances and potential regulatory impact of generative AI. 

Perhaps the most crucial and challenging aspect of adopting generative AI is an organization’s required cultural shift. Your teams must learn how to use AI as your company fosters an understanding of AI’s potential and limitations. 

Begin integrating AI literacy into your company’s DNA. Knowledge of its potential and the readily available tools can inspire your teams to incorporate generative AI into their daily workflows and business operations quickly, improve productivity, and deliver superior customer experiences. 

No company wants to be outpaced by its competitors. You can balance generative AI’s potential with your business operations by staying aware of and adapting to the dynamic AI landscape.

Achieving Harmony: Integrating Generative AI with Existing Business Operations

Integrating generative AI into existing business operations doesn’t have to be disruptive. With the correct strategy, you can do so seamlessly. Your goal should be to augment and enhance your operations, not disrupt and replace. 

For example, Microsoft Cloud technologies leveraged AI to relieve healthcare organizations’ challenges, such as talent shortage and workforce burnout. Providers automated their workflows, invested in specific applications and connectors, and enhanced support for data and operations. 

Azure AI Services, such as text analytics for health, enable healthcare organizations to understand their largely unstructured and untapped medical data. Project Health Insights leans on AI to help healthcare professionals gain actionable insights and inferences from patient data. 

Azure Health Bot, an Intelligent App, empowers organizations to build and deploy legally compliant, powerful conversational healthcare experiences to patients. 

A successful AI integration like this begins with understanding. Knowing what generative AI can do and setting realistic expectations can help you identify where and how AI can best serve your operations. 

Next, you can foster a culture of AI literacy across teams, speeding up adoption by helping them understand how it can aid their work. The more familiar your team is with AI and its possibilities, the smoother the transition. Training and education programs play a pivotal role here. 

Finally, integration requires a systematic approach. It’s not about overhauling everything at once — it's about identifying areas where AI can add the most value, then gradually experimenting and scaling its use. Successful integration stories often feature a phased approach, starting small and growing steadily. 

As we move toward a future where generative AI is a staple in business operations, remember that every journey is unique. Although there’s no one-size-fits-all solution, a strategic approach and a keen understanding of your business needs will help you navigate this new landscape successfully.

Conclusion

Generative AI offers plenty of opportunities for businesses that embrace it. It’s fueling the next generation of intelligent applications, transforming everything from product development to customer engagement to marketing. This technology is the key to unlocking unprecedented growth and innovation. 

While the journey may have challenges, the potential rewards make generative AI worth exploring. As generative AI advances, Azure AI’s broad range of tools and services will help you navigate this fascinating field.

Exercise

  • Watch Episode 02 of the serverless edition Learn Live session to learn how to build, test and deploy an end-to-end intelligent app solution.
  • Complete the Cloud Skills Challenge to build on your AI skills.
  • Register for Ask the Expert: Azure Functions on September 26 for a live Q&A with the Product Team on how to build AI apps using serverless computing.
+ + \ No newline at end of file diff --git a/30daysofIA/index.html b/30daysofIA/index.html index 4b34037ff6..3e5ff6423a 100644 --- a/30daysofIA/index.html +++ b/30daysofIA/index.html @@ -14,13 +14,13 @@ - - + +
-

· 10 min read
It's 30DaysOfIA

Prepare your organization to adopt intelligent apps by taking a change management approach. Assess your readiness, upskill teams, create a culture of change and overcome resistance, and prepare for future evolution to pave the way for a smooth transition into an AI-centric company.

What We'll Cover:

  • Role of change management in modernizing to intelligent apps
  • Skills needed for building intelligent apps in your organization
  • How to build a culture of change to embrace AI
  • How to prepare for the future of intelligent apps and continuous evolution

image of modernizing AI solutions for intelligent apps

Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management

As we’ve discussed throughout this series, intelligent apps and generative AI are rewriting the rules of business and application development. The first article of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” explored how intelligent apps can enhance user experiences and drive businesses growth. These advancements are opening new opportunities and markets for companies ready to embrace them.

However, embracing this digital transformation is more complex than flipping a switch. It requires adjustments to the technical side, which we explored in the series’ fifth article, “Preparing the Path for Intelligent Apps: Transitioning from On-Premises/IaaS to Cloud Native Applications.” It also demands a shift in how organizations adapt to change and approach app design and development. Let’s explore that mindset transition with tips to help you make it happen.

The Role of Change Management in Transitioning to Intelligent Apps

Transitioning to intelligent apps isn’t merely a technological change. It’s a profound shift in how businesses perceive and operate. This move toward artificial intelligence (AI)-centric approach isn’t meant to replace humans with machines or radically overhaul all business operations.

Instead, intelligent apps should augment human capabilities, streamline processes, and foster an environment where innovation flourishes. They expand toolkits and techniques to include AI-assisted development tools like GitHub Copilot and AI-enabled workflows, helping us design and deliver AI-enhanced experiences.

Effective change management strategies are crucial in helping employees adapt to these new ways of working with intelligent apps, ensuring that the transition doesn’t disrupt productivity or morale.

Team and organization strategies should: 

  • Assess the impact of any proposed changes.  
  • Communicate the benefits and reasons behind the change to all affected team members.  
  • Provide the necessary training and support.  
  • Integrate the changes into the culture and daily operations. 

By incorporating this robust change management approach, you can identify and mitigate potential transition risks early. Moreover, this strategy ensures that employees are engaged throughout the process, fostering a sense of involvement and ownership. This, in turn, cultivates a culture suited for innovation through intelligent apps. 

These organizational changes focus on the following aspects: organizational readiness, skillset, culture of change and overcoming resistance, and adaptability. Let’s explore them below.

info

Register for the intelligent apps webinar on Driving Business Value by Modernizing with Cloud-Native & AI with Microsoft and Forrester on September 26. 

Explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries. 

Identifying Organizational Readiness for Intelligent Apps

The first step in preparing your organization for intelligent apps is to assess your current readiness for change. Analyze various elements of your organization, from infrastructure and workforce skills to strategic vision and overall culture.

Can your apps and systems integrate with AI platforms like Microsoft Azure AI? Is your workforce comfortable working alongside AI, or are they fearful and resistant? Does your strategic vision incorporate AI’s transformative potential, or does it fail to acknowledge AI as a critical driver of future growth?

These questions identify the challenges of integrating AI into an organization and determine the company’s readiness for intelligent apps.

Embracing intelligent apps involves more than being ready with your technological infrastructure or having the right technology or tools. You need flexible organizational structures and processes driving your business to accommodate new ideas and capabilities. Your teams must feel empowered to own and drive them. Much like Microsoft’s shift to AI-powered services—like AI-powered Bing and Edge and Microsoft 365 Copilot—embracing AI organization-wide requires a holistic vision and willingness to innovate, with teams capable of delivering on the plan.

After accessing these critical areas for organizational readiness, you can start planning a strategy for this transition to an AI-ready organization. Identify skill gaps and work to bridge them while nurturing an environment that appreciates and harnesses the potential of AI and intelligent apps.

Nurturing the Indispensable Skills for Intelligent Apps

Tasking your AI specialists with preparing your organization to use and build intelligent apps isn’t enough. Accomplishing this goal requires a cross-functional effort and a diverse skill set.

Developing intelligent apps involves data scientists managing and interpreting vast amounts of data, machine learning engineers building and training the models, and traditional developers integrating these models into usable applications. However, the impact of generative AI spans beyond creating apps. AI fluency across the entire organization—from sales and marketing to accounting and legal departments—offers substantial benefits, sparking new ideas and solutions from sometimes unexpected sources.

Teams' AI skills might roughly fall into three levels: 

  • Fundamental skills — Being familiar and efficient with using AI tools, for example, knowing how to write effective prompts for large language models (LLMs).
  • Visionary skills — Recognizing AI’s strengths and weaknesses, for example, its creativity and hallucinations, that is, unexpected output.
  • Expert skills — Managing data and training models and integrating AI technology into apps, such as incorporating the OpenAI application programming interface (API) into an Intelligent App.

Based on these levels, you can gauge your team's skill readiness to pinpoint the most significant gaps, whether in general AI knowledge across your organization or within technical development teams.

As intelligent apps become increasingly prevalent, upskilling your workforce is crucial. This transition opens a new realm of skills and capabilities for your team to master. You can offer machine learning, data analysis, and cloud computing training programs—such as Microsoft’s Azure AI Fundamentals certification—to empower your team to confidently navigate the intelligent apps landscape.

Nurturing these skills within your organization may require hiring new talent, reskilling current employees, and creating an environment that promotes learning and experimentation. Specifically, if your team understands tools like the Microsoft Azure AI platform, they could contribute significantly to developing and maintaining intelligent apps.

An upskilled team is more than a functional unit. It’s a think tank to drive innovation and improve your organization’s competitive standing in the digital marketplace.

Building a Culture of Change to Embrace AI

Even with the right people and skills in place, change remains an uphill battle for organizations committed to traditional processes. 

Transitioning to intelligent apps is as much about mindset as technology and skills. It requires fostering a culture open to change and innovation that understands AI’s transformative potential and embraces it to enhance rather than replace human capabilities. 

Leaders play a pivotal role in cultivating this mindset. As they model a positive attitude toward AI, emphasize its benefits, and address any concerns transparently, leaders significantly contribute to the organization’s readiness to adopt intelligent apps. 

The most critical factor in this transition is enabling an AI-friendly culture. It’s about encouraging curiosity, rewarding innovative thinking, and creating an environment where people thrive on learning and experimentation. 

Empowering teams to leverage practical AI tools and AI-driven data insights to aid decision-making helps motivate employees to use and explore AI’s potential, improve their skills, and contribute to the successful integration of intelligent apps. Such an environment is fertile ground to grow breakthrough ideas and innovative solutions. 

Overcoming Resistance to Change and Adopting Intelligent Apps

Despite AI’s potential, change isn’t easy. It takes effort and is especially challenging for organizations used to existing processes. This resistance to change is a significant roadblock when transitioning to intelligent apps. 

Overcoming this resistance involves:  

  • Understanding employees’ concerns. 
  • Challenging underlying assumptions about traditional systems and outdated processes that hinder change. 
  • Providing comprehensive training for workers to use AI effectively.
  • Demonstrating the benefits and opportunities of intelligent apps. 
  • Clear and continuous communication to dispel fears and misconceptions about AI.

These steps will foster greater acceptance and enthusiasm for intelligent apps in your teams. 

Preparing for the Future of Intelligent Apps and Continuous Evolution

Finally, it’s essential to understand that generative AI is just beginning to gain momentum. Adjusting to a world of intelligent apps isn’t a one-time change—it’s a continuous evolution. 

As AI technologies mature and evolve and more businesses realize their benefits, we can expect the landscape of intelligent apps to expand further. Organizations can cultivate a mindset of continuous learning, flexibility, and adaptation to prepare for this future. Staying ahead in this dynamic field requires organizations to keep up with the latest AI advancements, continually reassess their strategies, and pivot when necessary. 

The domain of intelligent apps is rapidly advancing. Organizations must prepare to navigate this dynamic landscape to stay ahead of the curve.

Summary

Preparing for AI and intelligent apps is a strategic decision. It requires thoughtful preparation and a culture that embraces change.

With the proper preparation and mindset, adopting AI can become a smoother, collaborative path to innovation for the whole organization. Integrating AI into your organization does not discard what has worked in the past. It simply enhances and augments those processes for a better future.

Consider the points above to assess your organization’s readiness and prepare to welcome intelligent apps.

In the first week of this series, you learned about intelligent apps, how organizations today find success through intelligent apps, what AI-assisted application development looks like, ways to leverage the power of generative AI, the technical changes needed to prepare for intelligent apps, and, in this article, the organizational changes required to harness the power of AI.

The future is intelligent. Use this series as a guideline to navigate challenges while preparing for and embracing AI's opportunities for an intelligent tomorrow.

Exercise

- - +

· 10 min read
It's 30DaysOfIA

Prepare your organization to adopt intelligent apps by taking a change management approach. Assess your readiness, upskill teams, create a culture of change and overcome resistance, and prepare for future evolution to pave the way for a smooth transition into an AI-centric company.

What We'll Cover:

  • Role of change management in modernizing to intelligent apps
  • Skills needed for building intelligent apps in your organization
  • How to build a culture of change to embrace AI
  • How to prepare for the future of intelligent apps and continuous evolution

image of modernizing AI solutions for intelligent apps

Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management

As we’ve discussed throughout this series, intelligent apps and generative AI are rewriting the rules of business and application development. The first article of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” explored how intelligent apps can enhance user experiences and drive businesses growth. These advancements are opening new opportunities and markets for companies ready to embrace them.

However, embracing this digital transformation is more complex than flipping a switch. It requires adjustments to the technical side, which we explored in the series’ fifth article, “Preparing the Path for Intelligent Apps: Transitioning from On-Premises/IaaS to Cloud Native Applications.” It also demands a shift in how organizations adapt to change and approach app design and development. Let’s explore that mindset transition with tips to help you make it happen.

The Role of Change Management in Transitioning to Intelligent Apps

Transitioning to intelligent apps isn’t merely a technological change. It’s a profound shift in how businesses perceive and operate. This move toward artificial intelligence (AI)-centric approach isn’t meant to replace humans with machines or radically overhaul all business operations.

Instead, intelligent apps should augment human capabilities, streamline processes, and foster an environment where innovation flourishes. They expand toolkits and techniques to include AI-assisted development tools like GitHub Copilot and AI-enabled workflows, helping us design and deliver AI-enhanced experiences.

Effective change management strategies are crucial in helping employees adapt to these new ways of working with intelligent apps, ensuring that the transition doesn’t disrupt productivity or morale.

Team and organization strategies should: 

  • Assess the impact of any proposed changes.  
  • Communicate the benefits and reasons behind the change to all affected team members.  
  • Provide the necessary training and support.  
  • Integrate the changes into the culture and daily operations. 

By incorporating this robust change management approach, you can identify and mitigate potential transition risks early. Moreover, this strategy ensures that employees are engaged throughout the process, fostering a sense of involvement and ownership. This, in turn, cultivates a culture suited for innovation through intelligent apps. 

These organizational changes focus on the following aspects: organizational readiness, skillset, culture of change and overcoming resistance, and adaptability. Let’s explore them below.

info

Register for the intelligent apps webinar on Driving Business Value by Modernizing with Cloud-Native & AI with Microsoft and Forrester on September 26. 

Explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries. 

Identifying Organizational Readiness for Intelligent Apps

The first step in preparing your organization for intelligent apps is to assess your current readiness for change. Analyze various elements of your organization, from infrastructure and workforce skills to strategic vision and overall culture.

Can your apps and systems integrate with AI platforms like Microsoft Azure AI? Is your workforce comfortable working alongside AI, or are they fearful and resistant? Does your strategic vision incorporate AI’s transformative potential, or does it fail to acknowledge AI as a critical driver of future growth?

These questions identify the challenges of integrating AI into an organization and determine the company’s readiness for intelligent apps.

Embracing intelligent apps involves more than being ready with your technological infrastructure or having the right technology or tools. You need flexible organizational structures and processes driving your business to accommodate new ideas and capabilities. Your teams must feel empowered to own and drive them. Much like Microsoft’s shift to AI-powered services—like AI-powered Bing and Edge and Microsoft 365 Copilot—embracing AI organization-wide requires a holistic vision and willingness to innovate, with teams capable of delivering on the plan.

After accessing these critical areas for organizational readiness, you can start planning a strategy for this transition to an AI-ready organization. Identify skill gaps and work to bridge them while nurturing an environment that appreciates and harnesses the potential of AI and intelligent apps.

Nurturing the Indispensable Skills for Intelligent Apps

Tasking your AI specialists with preparing your organization to use and build intelligent apps isn’t enough. Accomplishing this goal requires a cross-functional effort and a diverse skill set.

Developing intelligent apps involves data scientists managing and interpreting vast amounts of data, machine learning engineers building and training the models, and traditional developers integrating these models into usable applications. However, the impact of generative AI spans beyond creating apps. AI fluency across the entire organization—from sales and marketing to accounting and legal departments—offers substantial benefits, sparking new ideas and solutions from sometimes unexpected sources.

Teams' AI skills might roughly fall into three levels: 

  • Fundamental skills — Being familiar and efficient with using AI tools, for example, knowing how to write effective prompts for large language models (LLMs).
  • Visionary skills — Recognizing AI’s strengths and weaknesses, for example, its creativity and hallucinations, that is, unexpected output.
  • Expert skills — Managing data and training models and integrating AI technology into apps, such as incorporating the OpenAI application programming interface (API) into an Intelligent App.

Based on these levels, you can gauge your team's skill readiness to pinpoint the most significant gaps, whether in general AI knowledge across your organization or within technical development teams.

As intelligent apps become increasingly prevalent, upskilling your workforce is crucial. This transition opens a new realm of skills and capabilities for your team to master. You can offer machine learning, data analysis, and cloud computing training programs—such as Microsoft’s Azure AI Fundamentals certification—to empower your team to confidently navigate the intelligent apps landscape.

Nurturing these skills within your organization may require hiring new talent, reskilling current employees, and creating an environment that promotes learning and experimentation. Specifically, if your team understands tools like the Microsoft Azure AI platform, they could contribute significantly to developing and maintaining intelligent apps.

An upskilled team is more than a functional unit. It’s a think tank to drive innovation and improve your organization’s competitive standing in the digital marketplace.

Building a Culture of Change to Embrace AI

Even with the right people and skills in place, change remains an uphill battle for organizations committed to traditional processes. 

Transitioning to intelligent apps is as much about mindset as technology and skills. It requires fostering a culture open to change and innovation that understands AI’s transformative potential and embraces it to enhance rather than replace human capabilities. 

Leaders play a pivotal role in cultivating this mindset. As they model a positive attitude toward AI, emphasize its benefits, and address any concerns transparently, leaders significantly contribute to the organization’s readiness to adopt intelligent apps. 

The most critical factor in this transition is enabling an AI-friendly culture. It’s about encouraging curiosity, rewarding innovative thinking, and creating an environment where people thrive on learning and experimentation. 

Empowering teams to leverage practical AI tools and AI-driven data insights to aid decision-making helps motivate employees to use and explore AI’s potential, improve their skills, and contribute to the successful integration of intelligent apps. Such an environment is fertile ground to grow breakthrough ideas and innovative solutions. 

Overcoming Resistance to Change and Adopting Intelligent Apps

Despite AI’s potential, change isn’t easy. It takes effort and is especially challenging for organizations used to existing processes. This resistance to change is a significant roadblock when transitioning to intelligent apps. 

Overcoming this resistance involves:  

  • Understanding employees’ concerns. 
  • Challenging underlying assumptions about traditional systems and outdated processes that hinder change. 
  • Providing comprehensive training for workers to use AI effectively.
  • Demonstrating the benefits and opportunities of intelligent apps. 
  • Clear and continuous communication to dispel fears and misconceptions about AI.

These steps will foster greater acceptance and enthusiasm for intelligent apps in your teams. 

Preparing for the Future of Intelligent Apps and Continuous Evolution

Finally, it’s essential to understand that generative AI is just beginning to gain momentum. Adjusting to a world of intelligent apps isn’t a one-time change—it’s a continuous evolution. 

As AI technologies mature and evolve and more businesses realize their benefits, we can expect the landscape of intelligent apps to expand further. Organizations can cultivate a mindset of continuous learning, flexibility, and adaptation to prepare for this future. Staying ahead in this dynamic field requires organizations to keep up with the latest AI advancements, continually reassess their strategies, and pivot when necessary. 

The domain of intelligent apps is rapidly advancing. Organizations must prepare to navigate this dynamic landscape to stay ahead of the curve.

Summary

Preparing for AI and intelligent apps is a strategic decision. It requires thoughtful preparation and a culture that embraces change.

With the proper preparation and mindset, adopting AI can become a smoother, collaborative path to innovation for the whole organization. Integrating AI into your organization does not discard what has worked in the past. It simply enhances and augments those processes for a better future.

Consider the points above to assess your organization’s readiness and prepare to welcome intelligent apps.

In the first week of this series, you learned about intelligent apps, how organizations today find success through intelligent apps, what AI-assisted application development looks like, ways to leverage the power of generative AI, the technical changes needed to prepare for intelligent apps, and, in this article, the organizational changes required to harness the power of AI.

The future is intelligent. Use this series as a guideline to navigate challenges while preparing for and embracing AI's opportunities for an intelligent tomorrow.

Exercise

+ + \ No newline at end of file diff --git a/30daysofIA/kick-off/index.html b/30daysofIA/kick-off/index.html index 6b5270d858..01393a5848 100644 --- a/30daysofIA/kick-off/index.html +++ b/30daysofIA/kick-off/index.html @@ -14,14 +14,14 @@ - - + +
-

Kick-off #30DaysofIA 🍂

· 4 min read
It's 30DaysOfIA

This Fall focus on building intelligent apps using AI and cloud-native technologies. #FallForIntelligentApps brings to you a learning journey to build your skills on creating differentiated experiences while modernizing your applications. It’s time to learn it all.

What We'll Cover

image

Get Ready To #FallForIntelligentApps starting September 18!

Today, we kick off the Fall season with content and activities to skill you up on all things Intelligent Apps or AI Apps on Azure with content, events, and community interactions! Read on to learn about what is coming!

Explore Our Initiatives

We have a number of initiatives planned for the month to help you learn and skill up on relevant technologies. Click on the links to visit the relevant pages for each

image

Register for the events!

What are 4 things you can do today, to jumpstart your learning journey?

#30Days Of Intelligent Apps

#30DaysOfIA is a series of daily blog posts grouped into themed weeks - taking you from core concepts to end-to-end solution examples in 30 days. Each article will provide conceptual lessons paired with exercises and resources to help you reinforce learnings and take next steps.

This series takes you through learning journey in six stages, each building on the previous week to help you skill up in a beginner-friendly way:

We will start with defining intelligent apps and then expand on how to build with cloud-native technologies like Azure Kubernetes Service, Azure Container Apps and Azure Functions, and take you through end-to-end scenarios for real world application development. Before we dive deep on intelligent apps, here is a high-level overview of the Intelligent Apps landscape on Azure:

image

Containers on Azure services offer you a wide range of capabilities, from simplicity to control to suit your different needs.

image +

Kick-off #30DaysofIA 🍂

· 4 min read
It's 30DaysOfIA

This Fall focus on building intelligent apps using AI and cloud-native technologies. #FallForIntelligentApps brings to you a learning journey to build your skills on creating differentiated experiences while modernizing your applications. It’s time to learn it all.

What We'll Cover

image

Get Ready To #FallForIntelligentApps starting September 18!

Today, we kick off the Fall season with content and activities to skill you up on all things Intelligent Apps or AI Apps on Azure with content, events, and community interactions! Read on to learn about what is coming!

Explore Our Initiatives

We have a number of initiatives planned for the month to help you learn and skill up on relevant technologies. Click on the links to visit the relevant pages for each

image

Register for the events!

What are 4 things you can do today, to jumpstart your learning journey?

#30Days Of Intelligent Apps

#30DaysOfIA is a series of daily blog posts grouped into themed weeks - taking you from core concepts to end-to-end solution examples in 30 days. Each article will provide conceptual lessons paired with exercises and resources to help you reinforce learnings and take next steps.

This series takes you through learning journey in six stages, each building on the previous week to help you skill up in a beginner-friendly way:

We will start with defining intelligent apps and then expand on how to build with cloud-native technologies like Azure Kubernetes Service, Azure Container Apps and Azure Functions, and take you through end-to-end scenarios for real world application development. Before we dive deep on intelligent apps, here is a high-level overview of the Intelligent Apps landscape on Azure:

image

Containers on Azure services offer you a wide range of capabilities, from simplicity to control to suit your different needs.

image image

To start with the basics for developing Kubernetes applications, explore #30DaysOfCloudNative.

Cloud-native development when paired with serverless computing enhances your solution architecture for building cost optimized, resilient applications.

image

To start with the basics for serverless computing, explore #30DaysOfServerless.

Let’s Get Started

Now you know everything! We hope you are as excited as we are to dive into a full month of active learning and doing! Don't forget to subscribe for updates in your favorite feed reader! And look out for our first Intelligent Apps post Monday!

- - + + \ No newline at end of file diff --git a/30daysofIA/page/10/index.html b/30daysofIA/page/10/index.html new file mode 100644 index 0000000000..98caf390ed --- /dev/null +++ b/30daysofIA/page/10/index.html @@ -0,0 +1,27 @@ + + + + + +Learn in #30DaysOfIA | Build Intelligent Apps On Azure + + + + + + + + + + + + + + +
+

· 4 min read
It's 30DaysOfIA

Continue The Learning Journey through Fall For Intelligent Apps! 🍂

What We'll Cover

Thank you! ♥️

image

It's hard to believe that JavaScript on Azure hack-together is ending! It seems like just yesterday that we launched this initiative, and yet here we are, 15 days later, with an incredible amount of learning and growth behind us. So... it's time for a wrap!

From the bottom of our hearts, we want to thank each and every one of you for your participation, engagement, and enthusiasm. It's been truly inspiring to see the passion and dedication from this strong community, and we're honored to be a part of it. ✨

Recap of The JavaScript on Azure Global Hack-Together

As we wrap up this exciting event, we wanted to take a moment to reflect on all that we've accomplished together. Over the last 15 days, we've covered a lot of ground, from the basics of contributing to Open source to the exploration of the Contoso Real Estate project from its Frontend to its Backend and future AI implementation.

Now that the hack-together is ending, we want to make sure that you have all the resources you need to continue honing your skills in the future. Whether you're looking to make your fist contribution to open source, become an open source maintainers, collaborate with others, or simply keep learning, there are plenty of resources out there to help you achieve your goals. So, let's dive in and explore all the ways you can continue to grow your JavaScript skills on Azure!

JSonAzure Hack-together Roadmap 📍:

hack-together-roadmap (2)

Recap on past Livestreams🌟:

Day 1️⃣: Opening Keynote (Hack-together Launch): Introduction to the Contoso Real Estate Open-source project!, managing complex and complex enterprise architecture, new announcements for JavaScript developers on Azure

Day 2️⃣: GitHub Copilot & Codespaces: Introduction to your AI pair programmer (GitHub Copilot) and your virtual developer environment on the cloud (GitHub Codespaces)

Day 6️⃣: Build your Frontend using Static Web Apps as part of a complex, modern composable frontends (or micro-frontends) and cloud-native applications.

Day 9️⃣: Build a Serverless Backend using Azure Functions

Day 1️⃣3️⃣: Easily connect to an Azure Cosmos DB, exploring its benefits and how to get started

Day 1️⃣5️⃣: Being in the AI Era, we dive into the Azure OpenAI Service and how you can start to build intelligent JavaScript applications

📖 Self-Learning Resources

  1. JavaScript on Azure Global Hack Together Module collection
  2. Lets #HackTogether: Javascript On Azure Keynote
  3. Step by Step Guide: Migrating v3 to v4 programming model for Azure Functions for Node.Js Application

Continue your journey with #FallForIntelligentApps

Join us this Fall on a learning journey to explore building intelligent apps. Combine the power of AI, cloud-native app development, and cloud-scale data to build highly differentiated digital experiences. Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure. Engage in a self-paced learning adventure by following along the #30Days of Intelligent Apps series, completing the Intelligent Apps Skills Challenge, joining the Product Group for a live Ask The Expert series or building an end to end solution architecture with a live guided experience through the Learn Live series. Discover more here.

Hands-on practice: Make your first contribution to open source!

Join our GitHUb Discussion Forum to connect with developers from every part of world, see contributions from other, find collaborators and make your first contribution to a real-world project! +Don't forget to give the repo a star ⭐

Resources

All resources are accessible on our landing page

+ + + + \ No newline at end of file diff --git a/30daysofIA/page/11/index.html b/30daysofIA/page/11/index.html new file mode 100644 index 0000000000..08a1391530 --- /dev/null +++ b/30daysofIA/page/11/index.html @@ -0,0 +1,26 @@ + + + + + +Learn in #30DaysOfIA | Build Intelligent Apps On Azure + + + + + + + + + + + + + + +
+

· 2 min read
It's 30DaysOfIA

September is almost here - and that can only mean one thing!! It's time to 🍂 Fall for something new and exciting and spend a few weeks skilling up on relevant tools, techologies and solutions!!

Last year, we focused on #ServerlessSeptember. This year, we're building on that theme with the addition of cloud-scale Data, cloud-native Technologies and cloud-based AI integrations to help you modernize and build intelligent apps for the enterprise!

Watch this space - and join us in September to learn more!

+ + + + \ No newline at end of file diff --git a/30daysofIA/page/2/index.html b/30daysofIA/page/2/index.html index e7f4e414db..d9bf7e3dec 100644 --- a/30daysofIA/page/2/index.html +++ b/30daysofIA/page/2/index.html @@ -14,13 +14,13 @@ - - + +
-

· 9 min read
It's 30DaysOfIA

Learn best practices for modernizing your on-premises or IaaS solutions to intelligent apps in the cloud.

What We'll Cover:

  • Traditional vs Intelligent Apps
  • On-Premises/IaaS vs. Cloud-Native Platforms
  • Modernization strategy for building Intelligent Apps

image of modernizing AI solutions for intelligent apps

Preparing the Path for Intelligent Apps: Transitioning from On-Premises/IaaS to Cloud-Native Applications

The proliferation of generative AI is paving the way for intelligent applications, software that leverages AI capabilities to deliver unparalleled functionality and user experience. As we covered in a previous article, “Demystifying Intelligent Applications: Leveraging AI in App Development,” intelligent Apps aren’t just products of advanced coding. They interact, learn, and evolve thanks to modern AI and machine learning (ML) breakthroughs. 

The paradigm shift from traditional to intelligent apps demands that we change our approach to the technical challenges of software and architecture design. It also necessitates adopting new ways of thinking and operating within an organization, which we explore in greater depth in “Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management.” 

This article explores the technical infrastructure powering intelligent apps, providing a roadmap to transition traditional on-premises or Infrastructure as a Service (IaaS) solutions to intelligent apps deployed to cloud native platforms and services. We’ll discover the differences between conventional and intelligent apps, options for re-architecting existing applications, and critical strategic considerations integral to modernizing the application-building approach. 

Understanding the Shift: Traditional vs. Intelligent Applications

Intelligent apps are more than just an incremental evolution from traditional apps. They involve a fundamental shift in software solution design, development, and deployment. A clear understanding of how each paradigm differs is crucial for comprehending the impact of this transition. 

Functionality and User Experience

Traditional applications, often rule-based and rigid, rely on preprogrammed operations. A traditional weather app, for example, typically presents structured, location-based forecasts using a predetermined data source. Meanwhile, an intelligent app leverages AI for data-driven decision-making and personalization. Rather than merely displaying the forecast, it integrates ML with additional data sources—like a user’s calendar and fitness data—to determine their favorite outdoor activities, provide relevant clothing suggestions, and even automatically design ideal vacations and times based on the user’s optimal weather preferences. 

Similarly, while traditional applications tend to offer a one-size-fits-all UI, intelligent apps use AI to create tailored interactions. For instance, an intelligent banking app might use generative AI to provide a voice-enabled UI that lets customers use natural language to ask about transactions and receive personalized financial advice.

Infrastructure

Intelligent apps integrating these sophisticated features require highly scalable, flexible infrastructure optimized for AI functionality and able to manage large volumes of real-time data. Unlike traditional applications deployed on-premises or in infrastructure as a service (IaaS) environments, intelligent apps demand the flexibility and scalability of cloud native architectures.

An on-premises or IaaS-based e-commerce application may struggle with connecting its data to AI services or scaling to accommodate voluminous real-time data and requests. But an intelligent app built with a cloud native approach and scalability at its core can handle variable demand much more efficiently. It also maintains its infrastructure components on demand, whether for collecting data for AI/ML consumption or user-facing features like AI-enabled customer support and personalization.

Consider an intelligent app built on Azure that creates ChatGPT-like experiences with custom data. It might connect Azure Open AI and Azure Cognitive Search to an app running on Azure Container Apps or Azure Kubernetes Service. 

The architecture might look like the following diagram based on this demo app:

diagram of a demo app architecture

While the cloud is well-equipped to handle the challenges associated with shifting to AI-centric infrastructure—handling real-time data processing and storage, integrating and managing AI services, configuring event-driven and microservices architectures, and ensuring security across the distributed environment—re-architecting a traditional app to an intelligent app requires careful planning and execution to ensure it benefits fully from the capabilities of AI and cloud computing.

On-Premises/IaaS vs. Cloud-Native Platforms

While on-premises and IaaS solutions provide substantial control over data and processes, this fine-grained control comes with setup and maintenance costs and may lack the scalability necessary for AI-driven Intelligent Apps. On-premises hosting involves purchasing and maintaining hardware, including servers and storage systems. IaaS involves leasing virtual hardware and then hiring and training IT personnel for the infrastructure’s operation, maintenance, and security.

Conversely, infrastructure built on cloud native app services with Microsoft Azure offers virtually unlimited scalability without ongoing hardware and maintenance costs. This feature is ideal for intelligent apps that need to adapt and scale to accommodate fluctuating workloads and data streams. And because these cloud infrastructures typically operate on a pay-as-you-go model, traditionally substantial upfront costs turn into predictable operating expenses, lowering both the cost and risk of launching applications.

Moreover, cloud service providers continually update their offerings, so building on sophisticated AI tools and services through the cloud, like the Azure AI Platform and orchestration capabilities with Azure Kubernetes Service, ensures that you have access to the latest AI and most advanced AI models and technologies.

Strategic Considerations for Transitioning to Intelligent Apps

When developing a strategy for transitioning to intelligent apps, you must consider costs, personnel skill development, and data management.

Monolithic architectures are unlikely to support the scale and ease of iteration required by intelligent apps, so you’ll likely need to implement code changes to support the transition. Your chosen hosting solution and the amount of data your app will handle affect cost and performance, in addition to determining privacy and compliance requirements.

An app that manages or stores terabytes or petabytes of data will require storage-optimized virtual machines or a managed database service hosting. Meanwhile, an app that processes video content or performs resource-intensive calculations should prioritize high-performance computing. Maximizing storage and performance can become extraordinarily costly.

Finally, existing applications shifting towards intelligent apps may encounter compatibility conflicts, depending on database type or the existing system’s underlying programming language. As you might imagine, a traditional app running on-premises and using a shared local database may need significant restructuring to support a cloud native architecture. You need plans for data migration, potential downtime, and training.

Next, let’s look at this transformation process.

Re-Architecting Monolithic Apps into Microservices

Shifting from monolithic applications to microservices is pivotal for transitioning to Intelligent Apps, and you can use this shift as an opportunity to transform your legacy software into intelligent apps that leverage the flexibility and power of microservices-enabled AI solutions.

Traditionally, a monolithic application (let’s use an online marketplace as an example) might handle all activities—from serving webpages to processing payments—within an on-premises server or a cloud server. However, this approach is challenging to scale and remains vulnerable to network failures or cyberattacks.

Breaking these applications into multiple services with specific responsibilities, such as authentication or notifications, can overcome these limitations. These services are typically deployed as microservices within container environments like Azure Kubernetes Service or serverless platforms like Azure Container Apps or Azure Functions using automated processes. This approach offers scalability, robustness, and improved performance, as each microservice can run independently and use its own database.

Shifting towards smaller, independent microservices in app architectures allows us to reap the benefits of AI-powered intelligent apps, for example, by using microservices to handle AI tasks such as processing natural language input, analyzing behavioral data, or personalizing content.

info

Watch Episode 1 and Episode 2 of the Learn Live series on how to build, test and deploy an end-to-end intelligent app solution using the Contoso Real Estate Sample.

Event-Driven Architectures: Knowing When to Use Them

Event-Driven Architectures (EDAs) can significantly enhance the performance and cost-effectiveness of intelligent apps. EDAs, typically implemented with microservices, respond in real time to events or state changes ranging from user interactions to real-time analytics.

Consider an intelligent app designed for personalized customer engagement. Instead of designing the application to check continuously for changes in user data, an EDA can react to such changes immediately, triggering appropriate microservices. For example, if users modify their preferences, an event could trigger an AI-driven recommendation microservice to update its suggestions immediately. This real-time responsiveness, enabled by services like Azure Event Grid, can improve user experiences and make intelligent apps more adaptive and proactive.

However, it’s worth noting that while EDAs provide significant benefits, they can introduce unique complexities, such as maintaining event consistency and debugging issues in event chains across multiple microservices. Therefore, it’s important to assess your application’s needs so you can understand where it can benefit from an EDA.

Conclusion

Transitioning to intelligent apps represents a shift in strategy, architecture, and infrastructure. By designing and re-architecting for cloud-based infrastructure, your apps gain scalability, cost-effectiveness, robust data management, access to sophisticated AI tools, enhanced security, and reduced operational overhead.  

But this transition extends well beyond its technical components. Organizations and the people working within them need equal attention. You can explore these facets in greater depth in “Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management.” 

The power of generative AI is here, and preparing for the move toward cloud-based intelligent applications will ensure that your apps remain performative and scalable and—most importantly—that your business is ready for the newest era of app innovation.

Exercise

- - +

· 9 min read
It's 30DaysOfIA

Learn best practices for modernizing your on-premises or IaaS solutions to intelligent apps in the cloud.

What We'll Cover:

  • Traditional vs Intelligent Apps
  • On-Premises/IaaS vs. Cloud-Native Platforms
  • Modernization strategy for building Intelligent Apps

image of modernizing AI solutions for intelligent apps

Preparing the Path for Intelligent Apps: Transitioning from On-Premises/IaaS to Cloud-Native Applications

The proliferation of generative AI is paving the way for intelligent applications, software that leverages AI capabilities to deliver unparalleled functionality and user experience. As we covered in a previous article, “Demystifying Intelligent Applications: Leveraging AI in App Development,” intelligent Apps aren’t just products of advanced coding. They interact, learn, and evolve thanks to modern AI and machine learning (ML) breakthroughs. 

The paradigm shift from traditional to intelligent apps demands that we change our approach to the technical challenges of software and architecture design. It also necessitates adopting new ways of thinking and operating within an organization, which we explore in greater depth in “Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management.” 

This article explores the technical infrastructure powering intelligent apps, providing a roadmap to transition traditional on-premises or Infrastructure as a Service (IaaS) solutions to intelligent apps deployed to cloud native platforms and services. We’ll discover the differences between conventional and intelligent apps, options for re-architecting existing applications, and critical strategic considerations integral to modernizing the application-building approach. 

Understanding the Shift: Traditional vs. Intelligent Applications

Intelligent apps are more than just an incremental evolution from traditional apps. They involve a fundamental shift in software solution design, development, and deployment. A clear understanding of how each paradigm differs is crucial for comprehending the impact of this transition. 

Functionality and User Experience

Traditional applications, often rule-based and rigid, rely on preprogrammed operations. A traditional weather app, for example, typically presents structured, location-based forecasts using a predetermined data source. Meanwhile, an intelligent app leverages AI for data-driven decision-making and personalization. Rather than merely displaying the forecast, it integrates ML with additional data sources—like a user’s calendar and fitness data—to determine their favorite outdoor activities, provide relevant clothing suggestions, and even automatically design ideal vacations and times based on the user’s optimal weather preferences. 

Similarly, while traditional applications tend to offer a one-size-fits-all UI, intelligent apps use AI to create tailored interactions. For instance, an intelligent banking app might use generative AI to provide a voice-enabled UI that lets customers use natural language to ask about transactions and receive personalized financial advice.

Infrastructure

Intelligent apps integrating these sophisticated features require highly scalable, flexible infrastructure optimized for AI functionality and able to manage large volumes of real-time data. Unlike traditional applications deployed on-premises or in infrastructure as a service (IaaS) environments, intelligent apps demand the flexibility and scalability of cloud native architectures.

An on-premises or IaaS-based e-commerce application may struggle with connecting its data to AI services or scaling to accommodate voluminous real-time data and requests. But an intelligent app built with a cloud native approach and scalability at its core can handle variable demand much more efficiently. It also maintains its infrastructure components on demand, whether for collecting data for AI/ML consumption or user-facing features like AI-enabled customer support and personalization.

Consider an intelligent app built on Azure that creates ChatGPT-like experiences with custom data. It might connect Azure Open AI and Azure Cognitive Search to an app running on Azure Container Apps or Azure Kubernetes Service. 

The architecture might look like the following diagram based on this demo app:

diagram of a demo app architecture

While the cloud is well-equipped to handle the challenges associated with shifting to AI-centric infrastructure—handling real-time data processing and storage, integrating and managing AI services, configuring event-driven and microservices architectures, and ensuring security across the distributed environment—re-architecting a traditional app to an intelligent app requires careful planning and execution to ensure it benefits fully from the capabilities of AI and cloud computing.

On-Premises/IaaS vs. Cloud-Native Platforms

While on-premises and IaaS solutions provide substantial control over data and processes, this fine-grained control comes with setup and maintenance costs and may lack the scalability necessary for AI-driven Intelligent Apps. On-premises hosting involves purchasing and maintaining hardware, including servers and storage systems. IaaS involves leasing virtual hardware and then hiring and training IT personnel for the infrastructure’s operation, maintenance, and security.

Conversely, infrastructure built on cloud native app services with Microsoft Azure offers virtually unlimited scalability without ongoing hardware and maintenance costs. This feature is ideal for intelligent apps that need to adapt and scale to accommodate fluctuating workloads and data streams. And because these cloud infrastructures typically operate on a pay-as-you-go model, traditionally substantial upfront costs turn into predictable operating expenses, lowering both the cost and risk of launching applications.

Moreover, cloud service providers continually update their offerings, so building on sophisticated AI tools and services through the cloud, like the Azure AI Platform and orchestration capabilities with Azure Kubernetes Service, ensures that you have access to the latest AI and most advanced AI models and technologies.

Strategic Considerations for Transitioning to Intelligent Apps

When developing a strategy for transitioning to intelligent apps, you must consider costs, personnel skill development, and data management.

Monolithic architectures are unlikely to support the scale and ease of iteration required by intelligent apps, so you’ll likely need to implement code changes to support the transition. Your chosen hosting solution and the amount of data your app will handle affect cost and performance, in addition to determining privacy and compliance requirements.

An app that manages or stores terabytes or petabytes of data will require storage-optimized virtual machines or a managed database service hosting. Meanwhile, an app that processes video content or performs resource-intensive calculations should prioritize high-performance computing. Maximizing storage and performance can become extraordinarily costly.

Finally, existing applications shifting towards intelligent apps may encounter compatibility conflicts, depending on database type or the existing system’s underlying programming language. As you might imagine, a traditional app running on-premises and using a shared local database may need significant restructuring to support a cloud native architecture. You need plans for data migration, potential downtime, and training.

Next, let’s look at this transformation process.

Re-Architecting Monolithic Apps into Microservices

Shifting from monolithic applications to microservices is pivotal for transitioning to Intelligent Apps, and you can use this shift as an opportunity to transform your legacy software into intelligent apps that leverage the flexibility and power of microservices-enabled AI solutions.

Traditionally, a monolithic application (let’s use an online marketplace as an example) might handle all activities—from serving webpages to processing payments—within an on-premises server or a cloud server. However, this approach is challenging to scale and remains vulnerable to network failures or cyberattacks.

Breaking these applications into multiple services with specific responsibilities, such as authentication or notifications, can overcome these limitations. These services are typically deployed as microservices within container environments like Azure Kubernetes Service or serverless platforms like Azure Container Apps or Azure Functions using automated processes. This approach offers scalability, robustness, and improved performance, as each microservice can run independently and use its own database.

Shifting towards smaller, independent microservices in app architectures allows us to reap the benefits of AI-powered intelligent apps, for example, by using microservices to handle AI tasks such as processing natural language input, analyzing behavioral data, or personalizing content.

info

Watch Episode 1 and Episode 2 of the Learn Live series on how to build, test and deploy an end-to-end intelligent app solution using the Contoso Real Estate Sample.

Event-Driven Architectures: Knowing When to Use Them

Event-Driven Architectures (EDAs) can significantly enhance the performance and cost-effectiveness of intelligent apps. EDAs, typically implemented with microservices, respond in real time to events or state changes ranging from user interactions to real-time analytics.

Consider an intelligent app designed for personalized customer engagement. Instead of designing the application to check continuously for changes in user data, an EDA can react to such changes immediately, triggering appropriate microservices. For example, if users modify their preferences, an event could trigger an AI-driven recommendation microservice to update its suggestions immediately. This real-time responsiveness, enabled by services like Azure Event Grid, can improve user experiences and make intelligent apps more adaptive and proactive.

However, it’s worth noting that while EDAs provide significant benefits, they can introduce unique complexities, such as maintaining event consistency and debugging issues in event chains across multiple microservices. Therefore, it’s important to assess your application’s needs so you can understand where it can benefit from an EDA.

Conclusion

Transitioning to intelligent apps represents a shift in strategy, architecture, and infrastructure. By designing and re-architecting for cloud-based infrastructure, your apps gain scalability, cost-effectiveness, robust data management, access to sophisticated AI tools, enhanced security, and reduced operational overhead.  

But this transition extends well beyond its technical components. Organizations and the people working within them need equal attention. You can explore these facets in greater depth in “Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management.” 

The power of generative AI is here, and preparing for the move toward cloud-based intelligent applications will ensure that your apps remain performative and scalable and—most importantly—that your business is ready for the newest era of app innovation.

Exercise

+ + \ No newline at end of file diff --git a/30daysofIA/page/3/index.html b/30daysofIA/page/3/index.html index e204d445b2..5aad0ec48a 100644 --- a/30daysofIA/page/3/index.html +++ b/30daysofIA/page/3/index.html @@ -3,7 +3,7 @@ -Learn in #30DaysOfIA | Build Intelligent Apps On Azure +Learn in #30DaysOfIA | Build Intelligent Apps On Azure @@ -14,13 +14,13 @@ - - + +
-

· 10 min read
It's 30DaysOfIA

Explore how digital natives and Independent Software Vendors (ISVs) can transform their business operations and generate higher value for customers with generative AI and intelligent apps.

What We'll Cover:

  • Potential of Generative AI in application development
  • Challenges with Generative AI
  • Integrating Generative AI with business operations

image of generative AI and intelligent apps used in business

Unleashing Business Transformation: How Digital Natives Can Leverage Generative AI

Generative AI is no longer a technological flight of fancy. It’s here, and it’s rewriting the rules of business innovation. 

This type of AI leverages machine learning algorithms to create new data with similar characteristics to a given dataset. It goes beyond text generation using generative pretrained transformer (GPT) models, covering an array of applications that span from image synthesis to translations and more. It also powers a new class of intelligent applications, which combine AI capabilities with traditional software functions for enhanced user experiences and advanced business solutions. 

Generative AI is transforming how digital natives and independent software vendors (ISVs) operate. Generative AI offers new possibilities for creating unique business value, enhancing customer experiences, and driving growth. 

It plays a critical role in applications, improving functionality, aiding decision-making, and automating complex tasks to deliver dynamic, uniquely personalized user experiences. Generative AI also enables and automates data-driven outcomes, functions, and features.

Integrating AI capabilities into customer-facing apps and turning them into intelligent apps benefits both the customer and the business, as we discussed in Demystifying Intelligent Applications: Leveraging AI in App Development. You get faster product development workflows and better business intelligence for sales and marketing, while your users get more natural and context-aware features and interactions. 

That said, to realize generative AI’s potential, you need some strategy and knowledge of how and where it fits into your current business and operations. We’ll examine this technology’s potential to transform business operations, its risks, and rewards, and how to integrate it into your existing operations. 

Unpacking the Potential of Generative AI

Generative AI is creating new opportunities in the digital playing field. It empowers businesses to generate insights, design products, and automate processes in ways and at speeds that were previously unimaginable. You can now predict customer behavior more accurately, offer highly personalized user experiences, and prototype innovative ideas rapidly. 

For example, Microsoft used generative AI to reinvent web search in the new AI-powered Bing and Edge. The search experience has evolved from a solitary user flow of typing in keywords and scrolling through results. It’s now a conversational experience where Bing uses context to offer better quality and more personalized search queries and results. 

Similarly, Microsoft harnessed generative AI to transform its productivity suite into intelligent apps. Through Copilot for Microsoft 365, PowerPoint now empowers users to tell their stories by using DALL-E to generate images effortlessly. It also helps rephrase text-heavy slides to be more straightforward. Outlook now assists users to compose more effective emails, enabling better communication.

These are just some examples of how AI powers day-to-day work and operations. 

Businesses that ignore generative AI miss out on untapped market opportunities, letting competitors gain an edge in the digital race. By seeking ways to rethink assumptions and reinvent existing approaches, you can effectively use generative AI to create new opportunities, open products to new use cases and markets, unlock new growth and customer satisfaction levels, and increase your revenue.

info

Register for the intelligent apps webinar on Driving Business Value by Modernizing with Cloud-Native & AI with Microsoft and Forrester on September 26.

Explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries. 

The Crucial Role of Generative AI in Business Operations

Generative AI can be a powerful catalyst for business transformation. Let’s quickly tour some ways it can redefine business operations. 

Product development is traditionally a long and painstaking process. Now, tools like GitHub Copilot and TestPilot use generative AI to make coding and testing software easier, faster, and more robust. 

Similarly, machine learning as a service technology, such as Azure Machine Learning, democratizes the availability of machine learning. It helps collect, clean, and train models across datasets, making engineering and analyzing data more efficient and accessible.  

Generative AI also simplifies the process of creating intelligent apps. Previously, simulating thousands of prototypes, predicting their performance, and narrowing down to the most viable options could take weeks, months, or even years of iteration and data analysis. With generative AI, this process can be as quick as days, hours, or perhaps minutes. 

Customer experience is also ripe for transformation. Personalized recommendations drive engagement and sales, and generative AI can scale that personalization to every customer interaction using comprehensive and continually updated data. For instance, Microsoft leverages AI to enable personalization in Dynamics 365 Customer Insights. Using this tool, you can ask questions about your customers in natural language and gain valuable and actionable insights into your customer base. 

Lastly, marketing and sales can benefit from generative AI. For example, identifying current and future key markets and emerging trends helps companies stay ahead of the curve. Tools like Copilot in Power BI help you quickly understand and uncover insights about your business, enabling your team to craft targeted campaigns, create engaging content, and optimize your business strategy. 

Leveraging this full generative AI potential requires the right tools. GPT-4, for instance, can be instrumental in realizing effective business transformation. It excels in understanding context, generating human-like text, and learning from minimal data input, making it a formidable tool in any digital native's arsenal. Platforms like Azure OpenAI Service bring GPT-4’s power to your fingertips.

However, as with any powerful technology, it’s unwise to treat generative AI as a perfect solution. You must know the challenges and risks of not leveraging it effectively, including technical and human-factor considerations. 

Without generative AI, your company risks missing the opportunity to please customers via personalization, as discussed previously. You’ll miss sales opportunities via business intelligence insights, limiting your ability to grow revenue and keep up with the competition. 

One challenge of leveraging generative AI’s full potential is finding the right tools. Fortunately, you can implement generative AI in your organization without investing in infrastructure, computational power, and expertise. Platforms like Azure OpenAI Service and Azure AI Services make AI easily accessible. They also work with Azure compute and data services like Azure Functions and Azure Kubernetes Service (AKS), enabling you to build intelligent apps easily and quickly. 

However, generative AI can’t do everything. It may need human expertise and intervention to use it fully. 

For example, generative AI can create unique data. However, the data may have limited diversity and complexity. The AI may need help recognizing additional considerations or requirements specific to a problem. It may also lack context due to various factors, such as the information’s freshness. Also, since it’s an emerging field that’s potentially worth $4.4 trillion, we’re still learning about the nuances and potential regulatory impact of generative AI. 

Perhaps the most crucial and challenging aspect of adopting generative AI is an organization’s required cultural shift. Your teams must learn how to use AI as your company fosters an understanding of AI’s potential and limitations. 

Begin integrating AI literacy into your company’s DNA. Knowledge of its potential and the readily available tools can inspire your teams to incorporate generative AI into their daily workflows and business operations quickly, improve productivity, and deliver superior customer experiences. 

No company wants to be outpaced by its competitors. You can balance generative AI’s potential with your business operations by staying aware of and adapting to the dynamic AI landscape.

Achieving Harmony: Integrating Generative AI with Existing Business Operations

Integrating generative AI into existing business operations doesn’t have to be disruptive. With the correct strategy, you can do so seamlessly. Your goal should be to augment and enhance your operations, not disrupt and replace. 

For example, Microsoft Cloud technologies leveraged AI to relieve healthcare organizations’ challenges, such as talent shortage and workforce burnout. Providers automated their workflows, invested in specific applications and connectors, and enhanced support for data and operations. 

Azure AI Services, such as text analytics for health, enable healthcare organizations to understand their largely unstructured and untapped medical data. Project Health Insights leans on AI to help healthcare professionals gain actionable insights and inferences from patient data. 

Azure Health Bot, an Intelligent App, empowers organizations to build and deploy legally compliant, powerful conversational healthcare experiences to patients. 

A successful AI integration like this begins with understanding. Knowing what generative AI can do and setting realistic expectations can help you identify where and how AI can best serve your operations. 

Next, you can foster a culture of AI literacy across teams, speeding up adoption by helping them understand how it can aid their work. The more familiar your team is with AI and its possibilities, the smoother the transition. Training and education programs play a pivotal role here. 

Finally, integration requires a systematic approach. It’s not about overhauling everything at once — it's about identifying areas where AI can add the most value, then gradually experimenting and scaling its use. Successful integration stories often feature a phased approach, starting small and growing steadily. 

As we move toward a future where generative AI is a staple in business operations, remember that every journey is unique. Although there’s no one-size-fits-all solution, a strategic approach and a keen understanding of your business needs will help you navigate this new landscape successfully.

Conclusion

Generative AI offers plenty of opportunities for businesses that embrace it. It’s fueling the next generation of intelligent applications, transforming everything from product development to customer engagement to marketing. This technology is the key to unlocking unprecedented growth and innovation. 

While the journey may have challenges, the potential rewards make generative AI worth exploring. As generative AI advances, Azure AI’s broad range of tools and services will help you navigate this fascinating field.

Exercise

  • Watch Episode 02 of the serverless edition Learn Live session to learn how to build, test and deploy an end-to-end intelligent app solution.
  • Complete the Cloud Skills Challenge to build on your AI skills.
  • Register for Ask the Expert: Azure Functions on September 26 for a live Q&A with the Product Team on how to build AI apps using serverless computing.
- - +

· 12 min read
It's 30DaysOfIA

Delve into creating an Intelligent App that leverages Azure AI Vision to analyze images and extract data. Develop an API to perform optical character recognition (OCR) on uploaded images and testing this API locally.

What We'll Cover:

  • Understanding Azure AI Vision and Azure Kubernetes Service
  • Build a Python Web API to perform OCR
  • Test the API locally

image depicting an Intelligent App using AI

Jumpstart Your AI Journey: Building Your First Intelligent App with Azure AI and AKS (1)

An Intelligent App is a software application that uses artificial intelligence (AI), such as machine learning (ML), to extract meaningful insights, enabling users to make informed decisions based on the extracted text, data, sound, images, and more.

Intelligent Apps can employ advanced algorithms and models to recognize objects, patterns, and features within images, allowing them to perform tasks like object identification, image classification, facial recognition, and image segmentation.

To explore the power of Intelligent Apps, let’s build a Python app that performs optical character recognition (OCR) on images containing numbers and performs aggregate functions on the extracted numbers, like sum and average. We’ll leverage Azure AI Services for the OCR functionality and Azure Kubernetes Service (AKS) to develop, publish, and maintain a cloud-based, scalable app in Azure.

Let’s get started!

Understanding Azure AI Vision and Azure Kubernetes Service

Azure AI Vision is a cloud-based service that offers a range of advanced computer vision capabilities enabling applications to interpret content, such as images and videos, using ML and other AI algorithms. Azure computer vision lets users integrate powerful vision features into their projects without ML experience.

OCR is one of the critical functionalities within Azure vision service, allowing applications to extract text from images. Within Azure AI Vision service, OCR offers several features, including text extraction, multilingual support, layout analysis of documents, and integration with Azure services, such as Azure Functions and Azure Machine Learning.

info

Watch the intelligent apps webinar on Driving Business Value by Modernizing with Cloud-Native & AI with Microsoft and Forrester on September 26.

AKS is Microsoft Azure’s container orchestration platform that enables us to quickly and efficiently deploy, manage, and scale containerized applications, such as our Intelligent App.

Let’s explore how to leverage these tools to build an Intelligent App that uses Azure AI Vision to analyze images and extract data.

Prerequisites

To follow this tutorial, ensure you have the following:

For a preview of this final project, take a look at the complete project code.

Building the API with Azure AI Vision Service

First, log in to your Azure account and navigate to the Azure Portal.

Click Create a resource and search for “resource group.” Create a new resource group named computer-vision.

image in Azure Portal of creating a new resource group

Return to the Azure Portal home page and click Create a resource.

image in Azure Portal of creating a new resource

Search for “computer vision” and select it from the results. Click Create.

image of searching for resource group in Azure Portal

Clicking Create displays the Create Computer Vision wizard. Select the resource group you just created as the Resource group from the dropdown. Enter a resource name similar to what is seen in the screenshot below. Note that this must be a unique name so choose one that is not currently in use. Select Standard S1 as the pricing tier and check the Responsible use of AI box in the field at the bottom.

Image of selecting the Responsible use of AI box

Image of project details

Click Review + Create, then click Create.

Next, get the API key and endpoint from the Azure AI services multi-service account you created. Once the resource is created, navigate to the resource details page. Under Resource Management, select Keys and Endpoint. Once on the Keys and Endpoint page, you’ll find the Key 1 and Endpoint values. These credentials are necessary to access the Azure AI APIs.

image of selecting Keys and Endpoint

second image of selecting Keys and Endpoint

Configuring the Local Environment Variables

Next, we need to define a local environment variable file. This convenient way to store our API keys in a simple text file enables us to manage sensitive information and separate configuration from our API code.

If you haven’t done so already, clone the starter project template from GitHub into your local computer. Open the starter project template in a code editor and create a new .env file in the root folder.

Note: Root folder in this case is the “/Microsoft_Series17-18_Code/intelligent-app-before" folder.

Add the following key-value pairs to the file, remembering to replace the placeholder values with those you obtained from the previous step.

VISION_KEY=<THE-KEY-1-VALUE-FROM-YOUR-AZURE-AI-SERVICE>
VISION_ENDPOINT=<THE-ENDPOINT-VALUE-FROM-YOUR-AZURE-AI-SERVICE>

Reviewing the Quickstart Code

Let’s review the app.py file in the starter project template.

The app.py file works as the entry point for the Flask application. It is responsible for initializing the web server, listening for incoming requests, dispatching them to the appropriate route handlers, and generating responses.

Below is the code contained in the app.py file:

import os
import json

from flask import Flask, request
from flask_restful import Resource, Api
from werkzeug.utils import secure_filename

app = Flask(__name__,
static_url_path='',
static_folder='static/files')

api = Api(app)

app = Flask(__name__)

app.config['UPLOAD_FOLDER'] = 'files'

api = Api(app)

class UploadHandler(Resource):

def allowed_file(self, filename):
return '.' in filename and \
filename.rsplit('.', 1)[1].lower() in {'png'}

def post(self):
form = request.form.to_dict()

if 'file' not in request.files:
return json.dumps({ "success": False, "error": "No file part"})

file = request.files.get("file")
if file and self.allowed_file(file.filename):
filename = secure_filename(file.filename)
upload_folder = "static/files"
if not os.path.exists(upload_folder):
os.makedirs(upload_folder)
local_file_path = os.path.join(upload_folder, filename)
file.save(local_file_path)

return f"File {filename} uploaded successfully to folder: {upload_folder}"

api.add_resource(UploadHandler, "/")

if __name__ == '__main__':
app.run(debug=True)

The UploadHandler class above is defined as a Flask RESTful resource to handle file uploads. The class contains two methods:

  • The allowed_file method checks whether the file extension is allowed for upload. In this case, only .png files are allowed.

  • The post method handles HTTP POST requests for file uploads. It saves the uploaded .png file to the static/files folder.

Finally, the application returns a text response informing us that the file has been uploaded successfully.

Implementing the REST API in Python

To implement image analysis in your REST API, open the starter project template in your terminal, create a virtual environment, and activate it.

Next, install the Azure AI Vision SDK:

pip install azure-ai-vision

Then, add the following line to the requirements.txt file to include the azure-ai-vision package:

azure-ai-vision==0.13.0b1

Now, create an ocr_helper.py file in the project’s root folder. This file is a Python module that provides functions for processing images using OCR. Add the following code to the file:

import os
from statistics import median
from decimal import Decimal
import azure.ai.vision as sdk

def process_ocr(source_image):
service_options = sdk.VisionServiceOptions(os.environ["VISION_ENDPOINT"],
os.environ["VISION_KEY"])

vision_source = sdk.VisionSource(filename=source_image)

analysis_options = sdk.ImageAnalysisOptions()

analysis_options.features = (
sdk.ImageAnalysisFeature.CAPTION |
sdk.ImageAnalysisFeature.TEXT
)

analysis_options.language = "en"

analysis_options.gender_neutral_caption = True

image_analyzer = sdk.ImageAnalyzer(service_options, vision_source, analysis_options)

result = image_analyzer.analyze()

ocr_result = get_ocr_result(result)

return ocr_result

def get_ocr_result(result):
string_list = []

if result.reason != sdk.ImageAnalysisResultReason.ANALYZED:
return sdk.ImageAnalysisErrorDetails.from_result(result)
else:
if result.text is not None:
for line in result.text.lines:
for word in line.words:
string_list.append(word.content)

number_list = convert_to_decimal_list(string_list)

aggregate_result = aggregate_operations(number_list)

return {
"aggregate_result": aggregate_result,
"numbers_read": string_list
}

def convert_to_decimal_list(string_list):
return list(map(Decimal, string_list))

def aggregate_operations(numbers):
result = {
'sum': sum(numbers),
'average': sum(numbers) / len(numbers),
'median': median(numbers),
'min': min(numbers),
'max': max(numbers)
`}`
`return result`

This module uses the azure-ai-vision package to analyze images, including capturing captions and extracting text from the image. The process_ocr function inputs an image file and performs the OCR analysis. The module is a convenient tool for analyzing text-based data in pictures and performing numerical computations based on that data.

Let’s review the different components of the ocr_helper.py module listed above and explore how its functions help our Intelligent App process images and perform aggregate operations:

  • The process_ocr function takes the parameter source_image, which is the path of the image to be processed. The function then initializes the VisionServiceOptions using environment variables VISION_ENDPOINT and VISION_KEY to connect to the Azure AI Vision API.
  • The process_ocr function creates a VisionSource object with the specified source_image file name. ImageAnalysisOptions specify the features to be analyzed, including CAPTION and TEXT. The language is set to English ("en"), and gender-neutral captions are enabled.
  • Finally, an ImageAnalyzer object is created with the service options, vision source, and analysis options. The image is then analyzed using the image_analyzer.analyze method to retrieve OCR results. The recognized text from the OCR result is extracted, with numerical values stored in the string_list variable.
  • The convert_to_decimal_list function converts the list of strings to a list of decimal numbers, which helps process numeric values extracted from the text.
  • The aggregate_operations function calculates various aggregate statistics (sum, average, median, minimum, maximum) from a list of numbers and returns the results in a dictionary.

Note that you must have the appropriate credentials (VISION_KEY) and endpoint (VISION_ENDPOINT) configured for the Azure AI Vision API to use this module.

Finally, we must modify the app.py file so our code can use the process_ocr function of the ocr_helper.py file.

Add the following import statement to the app.py file:

from ocr_helper import process_ocr

Then, replace this line:

return f"File {filename} uploaded successfully to folder: {upload_folder}"

With these two lines:

aggregates = process_ocr(local_file_path)
return json.dumps(aggregates, default=str)

Doing so allows our application to perform aggregate operations (sum, average, median, minimum, maximum) of any numeric values found in the text and return the operation’s result as a JSON response.

Running the Intelligent App Locally

Our final goal is to deploy the sample app to AKS, which will run the app in a container. However, as a good practice, let’s run the app in a container locally before moving it to AKS. Doing so ensures that our application and its dependencies are encapsulated and can be run consistently across different environments.

Deploying to Docker Desktop

For our application to run on Docker, it needs two additional files: a Dockerfile and docker-compose.yml. A Dockerfile creates a single container image by specifying the steps to build it, and a docker-compose.yml file manages multiple containers as a cohesive application, defining their configurations and relationships. Dockerfile builds the image, while docker-compose.yml orchestrates multi-container applications.

In the project root folder, add a file named Dockerfile with the content below:

# syntax=docker/dockerfile:1

FROM python:3.8-slim-buster

WORKDIR /intelligentapp

COPY requirements.txt requirements.txt
RUN pip3 install -r requirements.txt

RUN pip install debugpy

COPY . .

CMD [ "python3", "-m" , "flask", "run", "--host=0.0.0.0"]

Now create a file named docker-compose.yml file in the project’s root folder with the following code, replacing the VISION_KEY and the VISION_ENDPOINT according to the environment variables you configured earlier:

version: '3.8'
services:
intelligentapp:
build:
context: .
dockerfile: Dockerfile
image: intelligent-app
ports:
- 5000:5000
container_name: intelligent-app
environment:
- VISION_KEY=<THE-KEY-1-VALUE-FROM-YOUR-AZURE-AI-SERVICE>
- VISION_ENDPOINT=<THE-ENDPOINT-VALUE-FROM-YOUR-AZURE-AI-SERVICE>

Then, run the following command in the terminal to build the image and start a container for the Intelligent App services defined in the docker-compose.yml file:

docker-compose up --build --force-recreate

Next, open a new terminal and run the command below to list the image deployed to your local Docker:

docker images

docker images

Testing the Intelligent App Locally

Now, we’ll test the Intelligent App’s functionality using the following test image:

test image to test functionality

This image is provided as sample1.png in the sample app source files, so you can easily use it in the next step.

The Intelligent App will read the image above using OCR to extract numbers in the image and perform five aggregate functions:

  • Sum
  • Average
  • Median
  • Min
  • Max

To test the API, open Postman and fill out the fields as follows:

  • URL: http://localhost:5000/
  • Method: POST
  • Body:
    • Form-data
      • Key: File — Click the right end of the Key field and select File from the dropdown list.
      • Value: Click Select Files, then select the sample1.png file provided in the sample code.

image of selecting the sample file

Now click the Send button and review the result body:

"{\"aggregate_result\": {\"sum\": \"25821\", \"average\": \"5164.2\", \"median\": \"5622\", \"min\": \"1447\", \"max\": \"9802\"}, \"numbers_read\": [\"3049\", \"5901\", \"5622\", \"1447\", \"9802\"]}"

As we can see, the app running on a local container returned the correct results based on the sample image. So, we’re ready to prepare our Azure resources to run the Intelligent App on AKS.

Exercise

In this article, we explored the creation of an Intelligent App that leverages Azure AI Vision to analyze images and extract data. We learned how to build a Python Web API to perform OCR on uploaded images and subsequently test this API locally.

Head to the next part, to deploy this API via Azure Kubernetes Service.

+ + \ No newline at end of file diff --git a/30daysofIA/page/4/index.html b/30daysofIA/page/4/index.html index 9a69c3090a..6679e716eb 100644 --- a/30daysofIA/page/4/index.html +++ b/30daysofIA/page/4/index.html @@ -3,7 +3,7 @@ -Learn in #30DaysOfIA | Build Intelligent Apps On Azure +Learn in #30DaysOfIA | Build Intelligent Apps On Azure @@ -14,13 +14,13 @@ - - + +
-

· 9 min read
It's 30DaysOfIA

In the part of our series on intelligent apps, we’ll explore how AI is transforming application development, from design and architecture to building.

What We'll Cover:

  • Revolutionizing application development using AI
  • Infusing AI in application design architecture
  • AI Assisted Pair Programming
  • Future of App Development

A decorative image of application development with AI

Reimagining Application Development With AI: A New Paradigm

Artificial intelligence (AI) is impacting every industry. AI-powered development tools offer new functionalities and assist software developers throughout the development process, revolutionizing app development. From initial design and architecture to coding and debugging, AI reshapes how we approach application conception, creation, and iteration.

In part one of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” we explored the new breed of intelligent apps and how they use AI. In this article, we’ll see how AI technologies enhance developer toolkits and shape the future of app development, particularly through AI-enabled coding assistance and debugging.

The Dawn of Intelligent Apps: Revolutionizing Application Development

AI is more than just another addition to the development toolbox—it represents a revolution bringing unprecedented efficiency, innovation, and user engagement to our applications. 

What sets intelligent apps apart is how they can adapt and learn, helping us deliver unique, real-time user experiences.  

One example is an AI-driven health application that can track biometric data, predict potential health issues, provide personalized health advice, and even guide emergency services during a medical crisis. An AI-enabled language learning app that adapts to our learning speeds, recognizes our problem areas, and customizes its content to help us improve our language proficiencies is another example.  

Intelligent apps like these don’t just use data for recommendations—they can make critical decisions and provide sophisticated services that were previously impossible. 

For developers, intelligent apps mean we’re no longer just coding—we’re teaching applications how to think, adapt, and make decisions. AI is reshaping every step of the development journey, helping us to build more intuitive and user-focused products. 

Next, we’ll examine the impact of AI on application design and architecture and how AI-assisted “pair programming” changes software development.

info

Register for the FREE webinar on Intelligent Apps with Microsoft and Forrester on September 26 to explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries.

There will be a showcase of real-world use cases that demonstrate how AI can be seamlessly integrated into cloud-native environments driving tangible business value.

AI in Design and Architecture: Fueling Creativity and Efficiency

AI technologies help developers automate and streamline their processes and provide new ways to think about design.  

In traditional application development, the design and architecture phase can take significant time, effort, and expertise. For instance, adopting a monolithic architecture or a microservices-based design can significantly increase coding, testing, and debugging complexity. The app may even behave differently when hosted locally than in a deployed environment. It may take hours to track down an issue manually—time developers could spend elsewhere. 

Conversely, we can generate numerous design iterations in seconds with AI, each optimized to fulfill specific user needs and business goals. AI tools can quickly model various scenarios' impacts and software design patterns in the architectural design phase. It provides insights into performance and potential issues and suggests improvements. AI can also generate skeleton code, allowing developers to prototype different solutions or test specific components and technologies rapidly. By reviewing thousands of lines of code in seconds, AI allows us to focus on creating apps that serve users through feature development instead of searching for bugs.  

Integrating AI into the design and architecture phase can even extend our creative capabilities. By leveraging the power of generative AI—like large language models (LLMs)—and their broad knowledge and context going beyond software, we can better brainstorm apps and feature ideas. We can go beyond code and software architecture and get ideas about user experience, logo and branding, or marketing concepts. And as AI tools deliver insights and ideas we would have never considered; it encourages more innovation.

info

Watch Episode 01 of the Learn Live session to learn how to build an end-to-end intelligent app solution architecture.

The Power of AI-Assisted Pair Programming in Building Applications

AI-assisted pair programming is a collaborative coding approach where an AI system—like GitHub Copilot or TestPilot—assists developers during coding. It’s an increasingly common approach that significantly impacts developer productivity. In fact, GitHub Copilot is now behind an average of 46 percent of developers’ code and users are seeing 55 percent faster task completion on average. 

For new software developers, or those interested in learning new skills, AI-assisted pair programming are training wheels for coding. With the benefits of code snippet suggestions, developers can avoid struggling with beginner pitfalls like language syntax. Tools like ChatGPT can act as a personal, on-demand tutor—answering questions, generating code samples, and explaining complex code syntax and logic. These tools dramatically speed the learning process and help developers gain confidence in their coding abilities. 

Building applications with AI tools hastens development and provides more robust code. For example, GitHub Copilot helps us write new code accurately. It also quickly makes sense of existing code, suggests whole new lines or blocks of code, and even detects errors and proposes fixes. It’s like having a second coder working alongside us—one who never tires and is trained on billions of lines of code. 

However, it’s also important to acknowledge the limitations of AI. While generative AI-assistance technologies like GitHub Copilot are impressive, they’re still imperfect. They can sometimes produce erroneous, non-functional, or vulnerable code. They can also lack nuanced, domain-specific knowledge.  

So, although these tools are helpful, we can’t rely on them fully—these are programming aids rather than definitive solutions. AI-assisted pair programming is about augmenting the human developer’s skills, not replacing them.

Debugging and Improving Code With AI: Enhancing Application Quality

Identifying and correcting code errors is often time-consuming and challenging for developers. AI streamlines the debugging process by automatically detecting anomalies, suggesting fixes, and learning from previous bugs to prevent them from recurring. 

We can use generative AI to create unit tests from code snippets, from simple parameter checking and input validation to complex mocks of database and network services. Automated AI testing improves code coverage and robustness. It saves significant time compared to manually crafting unit tests. 

AI also revolutionizes code reviews by scanning and highlighting potential issues—like code patterns leading to a bug or a coding standards violation—and suggesting ways to simplify or refactor the code for readability and performance. This reduces human error and makes the code review process more efficient. Its benefits extend to small teams, where the availability of developers is crucial to deadlines, and large teams, where effective collaboration depends on readability and code consistency. AI further saves time by triaging issues so developers can prioritize the most mission-critical ones. 

Aside from improving code quality, AI is critical to understanding system behavior. Intelligent AI-assisted software development tools can monitor and audit system behavior, giving developers a better insight into how their code performs in different scenarios. AI can track patterns, identify inefficiencies, and provide alerts when they detect unusual behavior—enabling developers to enhance their code further. 

By predicting and identifying potential vulnerabilities, intelligent AI-assisted coding apps like Microsoft Security Copilot are pivotal in ensuring application functionality, reliability, and security. These AI tools are continually trained and upgraded with large codebases and user feedback that refines their responses. This ability to learn from past mistakes makes AI an incredibly powerful tool that will continue to expand its capabilities in the future.

info

Complete the AI Cloud Skills Challenge to build on you AI application development skills with paired programming using generative AI and GitHub Copilot.

AI and the Future of Application Development

The rapid advancements in AI and machine learning technologies point to an exciting future for application development. 

In “The Future of Applications,” software developer Mike Cann illustrates how LLMs might revolutionize application development. Cann that suggests fully-autonomous coding—AI-generated applications created entirely from prompts—may supersede traditional hand-crafted applications. Dynamically generated user interfaces could offer personalized and customizable experiences while we can design APIs for machine consumption and efficiency rather than human readability. Furthermore, we can store data in less structured, more adaptable, and flexible forms. 

AI is already making application development more accessible. With AI handling the complex, technical aspects of development, less-technical individuals can take a greater role in building applications. Consequently, AI can potentially democratize software engineering. 

Intelligent apps will continue to evolve, becoming more efficient, adaptable, and capable of learning from user interactions. Similarly, AI will inevitably become integral in every stage of app development.

Summary

AI has already created a new development paradigm—one in which machine intelligence and human creativity come together to produce innovative, efficient, and robust applications. AI already assists us at every stage of app development, from design and architecture to coding and debugging. 

Developers who embrace AI tools and integrate them into their processes can enhance their productivity, improve the quality of their applications, and expand the possibilities of their application development.  

The future of development is automated and AI-driven, and it’s here today. Getting started is easy—try an AI-powered development tool like GitHub Copilot to see how AI-assisted pair programming can enhance your productivity and code quality.

Exercise

- - +

· 9 min read
It's 30DaysOfIA

In the last article, we created an Intelligent App that leverages Azure AI Vision to analyze images and extract data. Develop an API to perform optical character recognition (OCR) on uploaded images and testing this API locally.

In this article, we will deploy the web API to the cloud platform using Azure Kubernetes Service.

What We'll Cover:

  • Understanding Azure AI Vision and Azure Kubernetes Service
  • Build a Python Web API to perform OCR
  • Test the API locally

image depicting an Intelligent App using AI

Jumpstart Your AI Journey: Building Your First Intelligent App with Azure AI and AKS (2)

In the previous article we explored the creation of an Intelligent App that leverages Azure AI Vision to analyze images and extract data. We learned how to build a Python Web API to perform OCR on uploaded images and subsequently test this API locally.

In this article we will use Azure Kubernetes Service (AKS) to develop, publish, and maintain our app in the cloud on Azure.

Let’s get started!

Prerequisites

To follow this tutorial, ensure you have completed Jumpstart Your AI Journey: Building Your First Intelligent App with Azure AI and AKS (1).

Pushing a Container Image to Azure Container Registry (ACR)

To start, open your CLI or terminal and type the following command:

az login

Follow the instructions displayed on your browser to enter your Azure credentials.

Once authenticated, you’ll initiate a secure connection between your local environment and Azure. This process grants you access to cloud services and resources.

Next, type the command below in your terminal to set up a new Azure Container Registry (ACR) to store your container images:

az acr create --resource-group computer-vision --name <name-of-azure-container-registry> --sku Basic

Remember to replace <name-of-azure-container-registry> with your container registry name. The name must be unique within Azure and comply with these rules.

The command above creates an Azure Container Registry (ACR) in the computer-vision resource group under the Basic SKU. This ACR is your secure and private repository for storing container images within Azure.

Next, log in to the registry with the following command:

az acr login -n <name-of-azure-container-registry>

The az acr login command above lets you securely authenticate and access the specified ACR without providing their Azure credentials each time.

Now, run the following command in your terminal. It will display the endpoint URL to log in to and interact with the ACR for pushing and pulling container images.

az acr show --name <name-of-azure-container-registry> --query loginServer --output table

This returns an endpoint URL as follows:

Result
----------------------------------
<name-of-azure-container-registry>.azurecr.io

Now, run the following command to show all container images, their repository, tags, and size:

docker images
REPOSITORYTAGIMAGE IDCREATEDSIZE
intelligent-applatesta7bf9f75361716 hours ago197MB

Tags are necessary to push Docker images to a remote registry like Azure Container Registry. They also let you differentiate between different versions of the same image and upload or download the one you want.

Run the following command to tag your Docker image:

docker tag intelligent-app <name-of-azure-container-registry>.azurecr.io/intelligent-app:v1

Then, run the docker images command again to check your tagged image:

docker images
REPOSITORYTAGIMAGE IDCREATEDSIZE
intelligent-applatestc52168039265About a minute ago197MB
<name-of-azure-container-registry>.azurecr.io/intelligent-appv1c52168039265About a minute ago197MB

Now run the following command so Docker can securely upload the image to your Azure Container Registry:

docker push <name-of-azure-container-registry>.azurecr.io/intelligent-app:v1

Once we've deployed the image to the container registry, AKS can access it during deployment.

info

Watch Episode 03 for the Learn Live session to experience a guided session on how to build, test and deploy an end-to-end intelligent app solution.

Deploying the Intelligent App on Azure Kubernetes Service (AKS)

Before we deploy our Intelligent App to AKS, we need to provision an AKS cluster and define Kubernetes manifests.

To provision an AKS cluster to host our application, we specify the desired configurations for the cluster, such as the number of nodes, node size, and networking options. But first, download and install the Kubernetes command-line tools (kubectl), a client credential plugin implementing Azure authentication:

az aks install-cli

If you’re using Linux, review this tutorial. Then run the following:

sudo az aks install-cli

Next, run the following command in your terminal to enable access to the networking-related resources and services provided by the `Microsoft.Network`` namespace in Azure:

az provider register --namespace Microsoft.Network

Now, we must create an AKS cluster. Run the following command to create an AKS cluster named aks-intelligent-app in the computer-vision resource group.

az aks create --resource-group computer-vision --name aks-intelligent-app --node-count 1 --generate-ssh-keys

The command above specifies the target resource group: computer-vision. The node pool is configured with one virtual machine (VM), and the secure shell (SSH) keys for secure node access are generated automatically.

Next, run the following command to update the AKS cluster you created by attaching it to your ACR. Doing so allows the AKS cluster to pull container images from the specified ACR when deploying workloads to the cluster.

az aks update -n aks-intelligent-app -g computer-vision --attach-acr <name-of-azure-container-registry>

Then, run the command below to configure kubectl to work with our AKS cluster in the computer-vision resource group.

az aks get-credentials --resource-group computer-vision --name aks-intelligent-app

The command above retrieves the necessary credentials and context information required for kubectl to communicate with the AKS cluster.

We still have to define the Kubernetes manifests written in YAML, describing the desired state of our application deployment, including containers, networking, and scaling rules. We’ll prepare these manifests, including Deployment and Service configurations, to define how our application should be deployed and exposed.

Create a folder named Deployment in the project root directory. Next, create two files in the deployment folder: deployment.yml and service.yml.

Add the following configuration to the deployment.yml file, replacing the <name-of-azure-container-registry> placeholder with your registry’s name:

apiVersion: apps/v1
kind: Deployment
metadata:
name: intelligent-app
spec:
replicas: 1
selector:
matchLabels:
app: intelligent-app
template:
metadata:
labels:
app: intelligent-app
spec:
nodeSelector:
kubernetes.io/os: linux
containers:
- name: intelligent-app
image: <name-of-azure-container-registry>.azurecr.io/intelligent-app:v1
resources:
limits:
memory: 512Mi
cpu: "1"
requests:
memory: 256Mi
cpu: "0.2"
ports:
- containerPort: 5000
env:
- name: FLASK_DEBUG
value: "1"
- name: VISION_KEY
value: <THE-KEY-1-VALUE-FROM-YOUR-AZURE-AI-SERVICE>
- name: VISION_ENDPOINT
value: <THE-ENDPOINT-VALUE-FROM-YOUR-AZURE-AI>

Additionally, edit the VISION_KEY and VISION_ENDPOINT environment variables above according to the API key and endpoint of your Azure AI Services instance.

Then, add the following configuration to the service.yml file:

apiVersion: v1
kind: Service
metadata:
name: intelligent-app-service
spec:
type: LoadBalancer
ports:
- protocol: TCP
port: 80
targetPort: 5000
name: port5000
selector:
app: intelligent-app

Now, we’ll deploy our application using kubectl. Applying the Kubernetes manifests creates the necessary resources and deploys our containerized application to the AKS cluster.

First, change the terminal to the deployment folder:

cd Deployment 

Then, run the following command to create or update Kubernetes resources defined in the deployment.yml file:

kubectl apply -f deployment.yml

Create a Kubernetes Service resource in a Kubernetes cluster based on the configuration defined in the service.yml file using the code below:

kubectl apply -f service.yml

Once you’ve applied the resource definition and the service configuration contained in the deployment.yml and the service.yml files, open the aks-intelligent-app Kubernetes Service in the Azure Portal, select Workloads under Kubernetes resources on the sidebar, and find the deployment named intelligent-app. It must have the status “Ready 1/1”. If you encounter an issue with this status, check out these troubleshooting resources.

image of selecting the deployment in Azure Portal

Testing the Intelligent App on AKS

To test the app on AKS, first, run the command below:

kubectl get services

This command lists the Services and their corresponding details, including the Service name, cluster IP address, external IP, and ports.

NAMETYPECLUSTER-IPEXTERNAL-IPPORT(S)AGE
intelligent-app-serviceLoadBalancer10.0.77.6020.121.76.15380:30936/TCP47s
kubernetesClusterIP10.0.0.1<none>443/TCP14m

The output above shows a Kubernetes Service named intelligent-app-service with a type set to LoadBalancer. It’s reachable from within the cluster using the cluster IP 10.0.77.60 and accessible externally via the external IP 20.121.76.153 on port 80 (mapped to port 30936).

Note: Your set of IP addresses will differ. Remember to use your unique external IP address when testing with Postman.

To test the deployed app, go to Postman, replace the URL with the external IP of the Kubernetes Service you just deployed, and click Send:

mage of sending the app in Postman

As we can see, our Intelligent App has successfully deployed to AKS and is functioning on the cloud as expected.

Exercise

Next Steps

In this two-part article, we explored the creation of an Intelligent App that leverages Azure AI Vision to analyze images and extract data. We learned how to build a Python Web API to perform OCR on uploaded images and subsequently deploy this API via Azure Kubernetes Service.

Besides OCR and Image Analysis, you can continue exploring Azure’s vast array of services and experiment further with Azure AI and AKS by applying various practical uses to your Intelligent Apps, including natural language processing, speech recognition and synthesis, sentiment analysis for customer feedback, and automated content moderation.

+ + \ No newline at end of file diff --git a/30daysofIA/page/5/index.html b/30daysofIA/page/5/index.html index 932a34418f..05d56e324e 100644 --- a/30daysofIA/page/5/index.html +++ b/30daysofIA/page/5/index.html @@ -3,7 +3,7 @@ -Learn in #30DaysOfIA | Build Intelligent Apps On Azure +Learn in #30DaysOfIA | Build Intelligent Apps On Azure @@ -14,13 +14,13 @@ - - + +
-

· 10 min read
It's 30DaysOfIA

Explore real-world examples of how Intelligent Apps revolutionize logistics, retail, finance, manufacturing, tech, and medicine.

What We'll Cover:

  • Real-world businesses revolutionizing operations with Intelligent Apps
  • High level architecture for industry scenarios
  • Implementation architecture overview across industries

A decorative header image for the blog

Real-world Success Stories

According to IBM’s 2022 Global AI Adoption Index, 35% of surveyed companies already use AI in their business, and 42% are exploring the idea. Organizations lagging behind the AI curve will quickly fall behind early adopters, who are already enjoying its benefits and boosting their investment in this technology.

In the first part of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” we explored how organizations can use AI-powered apps, or intelligent apps, to transform their operations, decision-making, and customer experiences, leaping ahead of the competition.

Intelligent apps significantly improve operational efficiency and customer engagement across industries like logistics, retail, finance, manufacturing, tech, and healthcare. In this article, we’ll spotlight real-world success stories of businesses revolutionizing operations with intelligent apps using Microsoft Azure’s app, data, and AI services.

info

Register for the Learn Live on September 21 for a live guided experience with SMEs on how to build end-to-end solution architecture for real world implementation of Intelligent Apps.

Streamlining Operations: Intelligent Apps in the Airline Industry

Aerospace pioneer Airbus leveraged intelligent apps to streamline its operations and innovate its services with two ground-breaking modernizations that use Azure apps, data, and AI services to reimagine pilot training and predict aircraft maintenance issues. 

Aviation technology’s rapid evolution compelled Airbus to reconceive their approach to training pilots. The aircraft manufacturer created a pilot training chatbot by combining Azure AI Services with Speech to Text and Text to Speech. The bot helps trainee pilots navigate over 6,000 pages of technical information. Pilots can ask questions verbally for quick answers, aiding their understanding of dense information and speeding up training. And when Airbus needed to accommodate its military consumers, deploying its Kubernetes cluster to a disconnected, “air-gapped” environment met the strict security requirements of military aircraft training.

A chatbot could look like the following diagram as Azure services work together to create a conversational experience. Its components could include security and governance, bot logic and user experience (UX), bot cognition and intelligence, data extract, transfer, and load (ETL) operations, quality assurance and enhancements, logging, and monitoring and reporting.

A diagram of a conversational experience enabled by Azure services

Airbus also used AI to improve its aircraft maintenance and safety. They deployed AI Anomaly Detector to gather and analyze telemetry data from multiple flights, enabling more profound insights into wear and tear on military aircraft operating in harsh conditions. This AI application helps Airbus better understand each airplane’s health. Airbus can predict and fix potential problems before they occur, improving the aircraft’s safety and operational readiness.

AI and Azure are changing how organizations like Airbus operate, even in strict, highly regulated markets with stringent security requirements. And by streamlining pilot training and staying ahead of aircraft maintenance, Airbus helps its military and commercial clients reliably deliver their goods across the globe, maintaining their critical supply chains.

Boosting Customer Service: Intelligent Apps in the Retail Industry

The retail industry has been among the first to adopt and benefit from intelligent apps. For example, the leading American used car retailer, CarMax, uses intelligent apps to transform the car shopping experience.

Before embracing AI, CarMax faced a significant challenge: Manually creating text summaries was time-consuming and labor-intensive. CarMax decided to leverage Azure OpenAI Service to automatically generate car research pages, offering customers valuable insights while enhancing the website’s search engine rankings.

This AI-driven approach has significantly streamlined the process, achieving in a few months what would have taken years manually. Also, the AI system condenses thousands of customer reviews into a few readable sentences, offering potential buyers a quick overview of others’ experiences.

CarMax has improved their customer experience while boosting operational efficiency through AI adoption. CarMax’s editorial staff focuses on creating strategic, longer-form content with the time saved, enhancing productivity, and driving more website traffic.

CarMax’s success story serves as a testament to the potential of intelligent apps in the retail industry.

Enhancing Cybersecurity: Intelligent Apps in Finance

Intelligent apps also impact the highly regulated financial services sector. Swift, a leading infrastructure provider for financial messaging services, uses intelligent apps to enhance decision-making and combat financial crime.

The massive growth of cross-border transactions and instant payment networks led to a surge in financial fraud that can cost the industry hundreds of billions of dollars every year. So, Swift and Microsoft partnered to build an anomaly detection model using federated learning techniques with Azure Machine Learning and Azure confidential computing.

Microsoft Purview helped govern data, as the federated learning method partially trained each model in a silo before aggregating them. This approach enabled Swift and Microsoft to develop a highly accurate model without copying or moving data from Swift members’ secure locations, ensuring the highest level of security and privacy.

Financial organizations can now deploy this trained ML model to run a real-time fraud detection system, lowering the cost of financial fraud.

Organizations can deploy a fraud detection system like the following example on Microsoft Azure. The system ingests data from streams or storage, runs analytics, and uses ML to detect fraud and store results. It then alerts users and other systems to the security issue.

A diagram of an example fraud detection system

Swift provides the innovative model to its banking partners, who can share data to further train the model. Azure confidential computing securely runs new models on current data to accumulate insights, generating an ongoing learning cycle. This iterative approach strengthens the global detection of fraudulent financial transactions through an increasingly faster and more accurate model. 

The collaboration between Swift and Microsoft demonstrates how combining various Azure services creates Intelligent Apps to enhance decision-making in finance. These apps solve real problems at an unprecedented global scale. 

Enhancing Efficiency: Intelligent Apps in Manufacturing

3M, known for Post-it Notes and other innovative products, wielded the power of Intelligent Apps to standardize, automate, and accelerate its sales forecasting. The company needed a unified, automated approach to replace its multiple manual methods.

It adopted Azure Machine Learning and other Azure AI Services to develop, train, and implement 1,500 custom models for each region and division, replacing manual methods. This solution delivers timely finance, sales, and marketing insights while freeing teams from time-consuming manual data manipulation tasks.

3M used Azure Machine Learning, automated machine learning (AutoML), and the Many Models Solution Accelerator to train and score numerous machine learning models in parallel. This approach significantly shortened the company’s development cycle time.

Then, 3M integrated Microsoft Power BI into their solution to compile and visualize results more effectively. Executives now have an unbiased and unfiltered view of data for better decision-making. With its improved ability to forecast sales, 3M can focus on manufacturing the products it needs efficiently.

3M’s success demonstrates how combining various Azure services can enhance efficiency in the manufacturing industry. These apps improve operational processes while enabling employees to focus on higher-level tasks. Strategic decisions are more targeted, and business conversations are more meaningful.

Driving Innovation: Intelligent Apps in the Technology Industry

As expected, the tech industry is at the forefront of leveraging Intelligent Apps to drive innovation. Elastic saw an opportunity to use intelligent apps to optimize its search services and meet customer needs more effectively. The company built a managed, secure, and scalable solution for its customers, Elastic Cloud, by leveraging Microsoft Azure.

Elastic Cloud uses Azure Machine Learning and other Azure AI Services to automate many basic management tasks, such as backing up data, upgrading, and scaling deployments. This approach enables Elastic’s customers to focus less on managing the software and more on serving their own clients. Customers can quickly deploy and manage Elastic Cloud on Kubernetes (ECK) using Azure Kubernetes Service.

As a result, Elastic’s cloud business is growing twice as fast as its on-premises solutions. Enterprise search users have reported significant improvements in system availability and employee satisfaction.

Additionally, Elastic incorporated Azure OpenAI Service into its Elasticsearch Relevance Engine (ESRE), providing more relevant answers to search queries. ESRE enables text, vector, and hybrid search, offering a more effective and accurate response to queries.

Elastic’s success story demonstrates how combining various Azure services can create intelligent Apps to drive innovation in the tech industry. These apps help improve operational processes and deliver better results quickly.

Revolutionizing Healthcare: Intelligent Apps in Medicine

Intelligent apps also drive transformative changes in the healthcare sector. Cambridgeshire and Peterborough NHS Foundation Trust, a healthcare provider in the United Kingdom, gradually shifted their on-premises systems to the cloud using Azure, improving flexibility, and lowering costs. They needed a solution for their electronic patient record (EPR) system that integrates 23 years of data across multiple systems and formats.

The Trust used Microsoft Azure and Azure Cognitive Search, including semantic search for understanding intent and contexts and cognitive capabilities for extracting insights from data, to standardize and automate data management. This approach made legacy, archive, and live data more discoverable, saving clinicians’ time. The Trust’s solution searches through millions of clinical documents in seconds, even recognizing handwritten notes without needing a separate optical character recognition (OCR) solution to extract text from images (Azure Cognitive Search automatically does it). Clinicians can now uncover previously inaccessible insights, enabling them to offer more personalized care.

The Trust also used Integration Services tools such as Azure Logic Apps to create workflows without writing code and Event Grid to connect Azure and third-party services through a publisher-subscriber model. They switched on a live feed from their new EPR system to Azure to feed real-time information into other applications. Researchers can now access anonymized data to fuel medical innovations beyond the NHS.

The healthcare provider also invested heavily in Power BI’s data visualization and analytical functions to sift through massive amounts of information. This level of data accessibility was previously impossible with their on-premises system. The new insights have significantly improved clinicians’ efficiency for better patient care.

Data accessibility and security are critical in the healthcare industry. Intelligent Apps empower organizations like the Trust to revolutionize medical care. AI capabilities helped the Trust improve its operational efficiency and user experiences, setting a new standard for healthcare in the digital age.

Summary

AI is driving transformation and real-world successes across industries. Intelligent apps are reshaping our world by streamlining operations in manufacturing and logistics, enhancing customer experience in the retail sector, improving decision-making in finance, driving innovation in tech, and revolutionizing healthcare.

As organizations adopt AI, they’re innovating in ways that were once too difficult, expensive, or outright impossible. Companies that wait to embrace intelligent apps risk the chance of falling behind. By embracing intelligent apps and leveraging the capabilities of Azure, you can harness the transformative potential of AI to remain on the cutting edge of technology.

Exercise

- - +

· 10 min read
It's 30DaysOfIA

Explore how digital natives and Independent Software Vendors (ISVs) can transform their business operations and generate higher value for customers with generative AI and intelligent apps.

What We'll Cover:

  • Potential of Generative AI in application development
  • Challenges with Generative AI
  • Integrating Generative AI with business operations

image of generative AI and intelligent apps used in business

Unleashing Business Transformation: How Digital Natives Can Leverage Generative AI

Generative AI is no longer a technological flight of fancy. It’s here, and it’s rewriting the rules of business innovation. 

This type of AI leverages machine learning algorithms to create new data with similar characteristics to a given dataset. It goes beyond text generation using generative pretrained transformer (GPT) models, covering an array of applications that span from image synthesis to translations and more. It also powers a new class of intelligent applications, which combine AI capabilities with traditional software functions for enhanced user experiences and advanced business solutions. 

Generative AI is transforming how digital natives and independent software vendors (ISVs) operate. Generative AI offers new possibilities for creating unique business value, enhancing customer experiences, and driving growth. 

It plays a critical role in applications, improving functionality, aiding decision-making, and automating complex tasks to deliver dynamic, uniquely personalized user experiences. Generative AI also enables and automates data-driven outcomes, functions, and features.

Integrating AI capabilities into customer-facing apps and turning them into intelligent apps benefits both the customer and the business, as we discussed in Demystifying Intelligent Applications: Leveraging AI in App Development. You get faster product development workflows and better business intelligence for sales and marketing, while your users get more natural and context-aware features and interactions. 

That said, to realize generative AI’s potential, you need some strategy and knowledge of how and where it fits into your current business and operations. We’ll examine this technology’s potential to transform business operations, its risks, and rewards, and how to integrate it into your existing operations. 

Unpacking the Potential of Generative AI

Generative AI is creating new opportunities in the digital playing field. It empowers businesses to generate insights, design products, and automate processes in ways and at speeds that were previously unimaginable. You can now predict customer behavior more accurately, offer highly personalized user experiences, and prototype innovative ideas rapidly. 

For example, Microsoft used generative AI to reinvent web search in the new AI-powered Bing and Edge. The search experience has evolved from a solitary user flow of typing in keywords and scrolling through results. It’s now a conversational experience where Bing uses context to offer better quality and more personalized search queries and results. 

Similarly, Microsoft harnessed generative AI to transform its productivity suite into intelligent apps. Through Copilot for Microsoft 365, PowerPoint now empowers users to tell their stories by using DALL-E to generate images effortlessly. It also helps rephrase text-heavy slides to be more straightforward. Outlook now assists users to compose more effective emails, enabling better communication.

These are just some examples of how AI powers day-to-day work and operations. 

Businesses that ignore generative AI miss out on untapped market opportunities, letting competitors gain an edge in the digital race. By seeking ways to rethink assumptions and reinvent existing approaches, you can effectively use generative AI to create new opportunities, open products to new use cases and markets, unlock new growth and customer satisfaction levels, and increase your revenue.

info

Register for the intelligent apps webinar on Driving Business Value by Modernizing with Cloud-Native & AI with Microsoft and Forrester on September 26.

Explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries. 

The Crucial Role of Generative AI in Business Operations

Generative AI can be a powerful catalyst for business transformation. Let’s quickly tour some ways it can redefine business operations. 

Product development is traditionally a long and painstaking process. Now, tools like GitHub Copilot and TestPilot use generative AI to make coding and testing software easier, faster, and more robust. 

Similarly, machine learning as a service technology, such as Azure Machine Learning, democratizes the availability of machine learning. It helps collect, clean, and train models across datasets, making engineering and analyzing data more efficient and accessible.  

Generative AI also simplifies the process of creating intelligent apps. Previously, simulating thousands of prototypes, predicting their performance, and narrowing down to the most viable options could take weeks, months, or even years of iteration and data analysis. With generative AI, this process can be as quick as days, hours, or perhaps minutes. 

Customer experience is also ripe for transformation. Personalized recommendations drive engagement and sales, and generative AI can scale that personalization to every customer interaction using comprehensive and continually updated data. For instance, Microsoft leverages AI to enable personalization in Dynamics 365 Customer Insights. Using this tool, you can ask questions about your customers in natural language and gain valuable and actionable insights into your customer base. 

Lastly, marketing and sales can benefit from generative AI. For example, identifying current and future key markets and emerging trends helps companies stay ahead of the curve. Tools like Copilot in Power BI help you quickly understand and uncover insights about your business, enabling your team to craft targeted campaigns, create engaging content, and optimize your business strategy. 

Leveraging this full generative AI potential requires the right tools. GPT-4, for instance, can be instrumental in realizing effective business transformation. It excels in understanding context, generating human-like text, and learning from minimal data input, making it a formidable tool in any digital native's arsenal. Platforms like Azure OpenAI Service bring GPT-4’s power to your fingertips.

However, as with any powerful technology, it’s unwise to treat generative AI as a perfect solution. You must know the challenges and risks of not leveraging it effectively, including technical and human-factor considerations. 

Without generative AI, your company risks missing the opportunity to please customers via personalization, as discussed previously. You’ll miss sales opportunities via business intelligence insights, limiting your ability to grow revenue and keep up with the competition. 

One challenge of leveraging generative AI’s full potential is finding the right tools. Fortunately, you can implement generative AI in your organization without investing in infrastructure, computational power, and expertise. Platforms like Azure OpenAI Service and Azure AI Services make AI easily accessible. They also work with Azure compute and data services like Azure Functions and Azure Kubernetes Service (AKS), enabling you to build intelligent apps easily and quickly. 

However, generative AI can’t do everything. It may need human expertise and intervention to use it fully. 

For example, generative AI can create unique data. However, the data may have limited diversity and complexity. The AI may need help recognizing additional considerations or requirements specific to a problem. It may also lack context due to various factors, such as the information’s freshness. Also, since it’s an emerging field that’s potentially worth $4.4 trillion, we’re still learning about the nuances and potential regulatory impact of generative AI. 

Perhaps the most crucial and challenging aspect of adopting generative AI is an organization’s required cultural shift. Your teams must learn how to use AI as your company fosters an understanding of AI’s potential and limitations. 

Begin integrating AI literacy into your company’s DNA. Knowledge of its potential and the readily available tools can inspire your teams to incorporate generative AI into their daily workflows and business operations quickly, improve productivity, and deliver superior customer experiences. 

No company wants to be outpaced by its competitors. You can balance generative AI’s potential with your business operations by staying aware of and adapting to the dynamic AI landscape.

Achieving Harmony: Integrating Generative AI with Existing Business Operations

Integrating generative AI into existing business operations doesn’t have to be disruptive. With the correct strategy, you can do so seamlessly. Your goal should be to augment and enhance your operations, not disrupt and replace. 

For example, Microsoft Cloud technologies leveraged AI to relieve healthcare organizations’ challenges, such as talent shortage and workforce burnout. Providers automated their workflows, invested in specific applications and connectors, and enhanced support for data and operations. 

Azure AI Services, such as text analytics for health, enable healthcare organizations to understand their largely unstructured and untapped medical data. Project Health Insights leans on AI to help healthcare professionals gain actionable insights and inferences from patient data. 

Azure Health Bot, an Intelligent App, empowers organizations to build and deploy legally compliant, powerful conversational healthcare experiences to patients. 

A successful AI integration like this begins with understanding. Knowing what generative AI can do and setting realistic expectations can help you identify where and how AI can best serve your operations. 

Next, you can foster a culture of AI literacy across teams, speeding up adoption by helping them understand how it can aid their work. The more familiar your team is with AI and its possibilities, the smoother the transition. Training and education programs play a pivotal role here. 

Finally, integration requires a systematic approach. It’s not about overhauling everything at once — it's about identifying areas where AI can add the most value, then gradually experimenting and scaling its use. Successful integration stories often feature a phased approach, starting small and growing steadily. 

As we move toward a future where generative AI is a staple in business operations, remember that every journey is unique. Although there’s no one-size-fits-all solution, a strategic approach and a keen understanding of your business needs will help you navigate this new landscape successfully.

Conclusion

Generative AI offers plenty of opportunities for businesses that embrace it. It’s fueling the next generation of intelligent applications, transforming everything from product development to customer engagement to marketing. This technology is the key to unlocking unprecedented growth and innovation. 

While the journey may have challenges, the potential rewards make generative AI worth exploring. As generative AI advances, Azure AI’s broad range of tools and services will help you navigate this fascinating field.

Exercise

  • Watch Episode 02 of the serverless edition Learn Live session to learn how to build, test and deploy an end-to-end intelligent app solution.
  • Complete the Cloud Skills Challenge to build on your AI skills.
  • Register for Ask the Expert: Azure Functions on September 26 for a live Q&A with the Product Team on how to build AI apps using serverless computing.
+ + \ No newline at end of file diff --git a/30daysofIA/page/6/index.html b/30daysofIA/page/6/index.html index e088a7a6c1..bdd0421da6 100644 --- a/30daysofIA/page/6/index.html +++ b/30daysofIA/page/6/index.html @@ -3,7 +3,7 @@ -Learn in #30DaysOfIA | Build Intelligent Apps On Azure +Learn in #30DaysOfIA | Build Intelligent Apps On Azure @@ -14,13 +14,13 @@ - - + +
-

· 8 min read
It's 30DaysOfIA

Let’s start with understanding the power of intelligent applications.

What We'll Cover:

  • What are intelligent applications?
  • Categories of Intelligent Apps
  • Breaking down the role of AI in intelligent apps

A decorative header image for the blog

Demystifying Intelligent Applications: Leveraging AI in App Development

Until recent years, when you heard the term “artificial intelligence” (AI), you might have pictured autonomous robots from sci-fi films. Now, perhaps your mind jumps to big data and machine learning (ML). Maybe you’re even thinking about the latest ChatGPT plugins and transformer architectures. Today, AI is more than these imaginations: it’s a reality increasingly woven into our day-to-day routines through intelligent applications.

Intelligent apps combine cloud-scale compute, data, and predictive or generative AI. These applications help you automate tasks and make data-driven decisions, aiding in leveraging business intelligence to gain meaningful, practical outcomes. 

For developers, the AI landscape isn’t an abstract complexity. It’s a real, tangible opportunity for evolving traditional applications, making them “intelligent,” and delivering more value to users. And with tools like Azure Kubernetes Service, Azure Cosmos DB, and Azure AI Services, stepping into the world of intelligent apps isn’t a steep climb but a steady trek across a manageable learning curve.

The Three Categories of Intelligent Applications

Before we start building, it’s critical to understand what makes an app “intelligent.” It’s not a universal definition so much as an identifier for apps that fall into one or more of the three primary categories: 

  • Outcome-based — This designation represents the broadest category of intelligent apps, whose principal value for users is the intelligent outcomes they provide. These apps process data to provide insightful results that enhance decision-making or help with tasks like trend prediction, resource management, and customer relationship management.

  • Functionality-based — This type of intelligent app takes AI integration a degree further, integrating AI as a function of the app to create or process its outputs, enhancing the app’s overall interactivity and usefulness.

  • Feature-based — This is the narrowest category, in which the app’s AI/ML component is one of its core features and primary selling points

Let’s dive more deeply into each category.

Outcome-Based Apps: Intelligent Outcomes Drive Success

Outcome-based applications are all about the results provided to the user. They function beyond merely processing data to offer insightful outcomes that drive success.

Think of a personal fitness tracker app that uses AI to analyze the user’s activity and provides actionable recommendations for achieving a goal. This type of app not only tracks your steps, sleep patterns, and heart rate but uses that data to provide you with personalized health advice. It helps you make informed lifestyle choices — the “intelligent” outcome.

Another example is a social media marketing app that uses AI for time-saving data analysis, task automation, and trend prediction. AI-based sentiment analysis can gauge customer perception while natural language processing (NLP) accurately interprets customer inquiries. Task automation can schedule posts and automate responses to common customer queries, and trend forecasting helps model and predict customer behavior to further personalize targeted promotional efforts. Building this type of intelligence into your apps provides cost-effective, actionable insights that guide sales and marketing strategies.

Functionality-Based Apps: AI-Driven Features Enhance User Experiences

As the name suggests, functionality-based applications integrate AI into how the app functions, but the end user drives the action and outcome. In this category of apps, AI takes a more active role in the user’s interaction by allowing them to use AI-created or processed output. These apps leverage AI technologies like Azure AI Services for NLP, image recognition, and pattern recognition to improve user experience.

Consider a music app that generates personalized playlists based on your listening habits or your smartphone’s camera app that uses AI to recognize faces and optimize photo settings. Even more advanced are the language translation apps that translate text from one language to another and can even understand and maintain the context and subtleties of the conversation. They use NLP to accomplish these feats.

Similarly, consider the latest e-commerce apps, whose AI-driven functionalities enable customers to try on clothes virtually, significantly enhancing the online shopping experience.

Feature-Based Apps: Integrating Advanced AI/ML Components

Finally, there are feature-based intelligent apps. These apps incorporate advanced AI/ML components like neural networks, internal large language models (LLMs), and sophisticated algorithms as core features for the user. In this type of application, AI is the primary feature for users to engage with and the primary selling point for the app.

Some examples of feature-based applications are chatbots and virtual agents built using Azure Bot Service. These robo-representatives use AI to provide more natural and human-like conversation, balancing superior efficiency with a friendlier, more personalized user experience. The new Bing is a prime example of this implementation, integrating AI to give users a knowledgeable copilot for web searches.

Similarly, OpenAI’s ChatGPT allows users access to its state-of-the-art language models available as Microsoft’s Azure OpenAI Service. It leverages the power of the highly sophisticated GPT-3 LLM to perform tasks like drafting emails, writing code, answering general knowledge questions, translating between languages, and tutoring in numerous subjects.

These apps don’t just use AI to provide outputs or improve user experiences: They push the boundaries of what’s possible in app development by integrating AI/ML components.

info

Register for the live Q&A Ask The Expert session on September 26 with the Azure Functions product engineering team.

Breaking Down the Role of AI in Intelligent Applications

Intelligent apps reflect the merging of traditional software design with the dynamic capabilities of AI. Let’s explore how AI integrates and amplifies these applications.

At its core, AI augments applications with the capacity to self-learn and self-improve. It allows you to create applications that predict, react, and adapt to a constantly changing environment. In the power grid management system for a major city, an AI-infused application can forecast power usage trends, react to anomalies, and adapt to sudden demand surges, ensuring optimal efficiency and preventing blackouts.

But AI’s role doesn’t end at prediction or decision-making: It also automates tasks. In enterprise solutions, it has moved beyond simple automation to encompass intelligent process automation (IPA). In this arena, AI algorithms understand, learn, and then automate entire digital workflows. For developers, this is a fertile ground for creating applications that can dramatically improve the operational efficiency of businesses.

Even more, AI substantially aids in development tasks. For example, there’s GitHub Copilot, an AI-powered code assistant that offers suggestions based not only on syntax but on development best practices and the intent of the code. AI has evolved from a tool you use to build apps to a partner that helps in creating them. 

Using AI to build intelligent apps isn’t about throwing out what’s worked before. It’s about enhancing the components that have the most impact and achieve the best results. Incorporating AI in apps elevates their predictive abilities, streamlines complex tasks, and even aids in the development and creation process. When you build intelligent apps, you don’t just address current problems. You anticipate future challenges and craft solutions that adapt over time.

Summary

As developers the power of AI is in our hands, and it’s our responsibility to harness its full potential. Each category tells the story of AI's transformative potential, from applications that deliver intelligent outcomes to those with AI capabilities to enrich the user experience and those pushing boundaries with advanced AI/ML technology.

Accessible and robust platforms like Azure AI can simplify the process of developing Intelligent Applications. Whether you’re refining an existing app or building a new one, just entering the world of AI, or already familiar with the landscape, take a moment to appreciate the potential that AI integration offers. You’re embracing this exciting frontier and shaping the future of app development. 

Exercise

  • Complete the Cloud Skills Challenge to build on your apps and AI skills.
  • Watch Episode 01 of the Serverless Edition Learn Live session to learn how to build an end-to-end intelligent app solution.
- - +
Skip to main content

· 9 min read
It's 30DaysOfIA

In the part of our series on intelligent apps, we’ll explore how AI is transforming application development, from design and architecture to building.

What We'll Cover:

  • Revolutionizing application development using AI
  • Infusing AI in application design architecture
  • AI Assisted Pair Programming
  • Future of App Development

A decorative image of application development with AI

Reimagining Application Development With AI: A New Paradigm

Artificial intelligence (AI) is impacting every industry. AI-powered development tools offer new functionalities and assist software developers throughout the development process, revolutionizing app development. From initial design and architecture to coding and debugging, AI reshapes how we approach application conception, creation, and iteration.

In part one of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” we explored the new breed of intelligent apps and how they use AI. In this article, we’ll see how AI technologies enhance developer toolkits and shape the future of app development, particularly through AI-enabled coding assistance and debugging.

The Dawn of Intelligent Apps: Revolutionizing Application Development

AI is more than just another addition to the development toolbox—it represents a revolution bringing unprecedented efficiency, innovation, and user engagement to our applications. 

What sets intelligent apps apart is how they can adapt and learn, helping us deliver unique, real-time user experiences.  

One example is an AI-driven health application that can track biometric data, predict potential health issues, provide personalized health advice, and even guide emergency services during a medical crisis. An AI-enabled language learning app that adapts to our learning speeds, recognizes our problem areas, and customizes its content to help us improve our language proficiencies is another example.  

Intelligent apps like these don’t just use data for recommendations—they can make critical decisions and provide sophisticated services that were previously impossible. 

For developers, intelligent apps mean we’re no longer just coding—we’re teaching applications how to think, adapt, and make decisions. AI is reshaping every step of the development journey, helping us to build more intuitive and user-focused products. 

Next, we’ll examine the impact of AI on application design and architecture and how AI-assisted “pair programming” changes software development.

info

Register for the FREE webinar on Intelligent Apps with Microsoft and Forrester on September 26 to explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries.

There will be a showcase of real-world use cases that demonstrate how AI can be seamlessly integrated into cloud-native environments driving tangible business value.

AI in Design and Architecture: Fueling Creativity and Efficiency

AI technologies help developers automate and streamline their processes and provide new ways to think about design.  

In traditional application development, the design and architecture phase can take significant time, effort, and expertise. For instance, adopting a monolithic architecture or a microservices-based design can significantly increase coding, testing, and debugging complexity. The app may even behave differently when hosted locally than in a deployed environment. It may take hours to track down an issue manually—time developers could spend elsewhere. 

Conversely, we can generate numerous design iterations in seconds with AI, each optimized to fulfill specific user needs and business goals. AI tools can quickly model various scenarios' impacts and software design patterns in the architectural design phase. It provides insights into performance and potential issues and suggests improvements. AI can also generate skeleton code, allowing developers to prototype different solutions or test specific components and technologies rapidly. By reviewing thousands of lines of code in seconds, AI allows us to focus on creating apps that serve users through feature development instead of searching for bugs.  

Integrating AI into the design and architecture phase can even extend our creative capabilities. By leveraging the power of generative AI—like large language models (LLMs)—and their broad knowledge and context going beyond software, we can better brainstorm apps and feature ideas. We can go beyond code and software architecture and get ideas about user experience, logo and branding, or marketing concepts. And as AI tools deliver insights and ideas we would have never considered; it encourages more innovation.

info

Watch Episode 01 of the Learn Live session to learn how to build an end-to-end intelligent app solution architecture.

The Power of AI-Assisted Pair Programming in Building Applications

AI-assisted pair programming is a collaborative coding approach where an AI system—like GitHub Copilot or TestPilot—assists developers during coding. It’s an increasingly common approach that significantly impacts developer productivity. In fact, GitHub Copilot is now behind an average of 46 percent of developers’ code and users are seeing 55 percent faster task completion on average. 

For new software developers, or those interested in learning new skills, AI-assisted pair programming are training wheels for coding. With the benefits of code snippet suggestions, developers can avoid struggling with beginner pitfalls like language syntax. Tools like ChatGPT can act as a personal, on-demand tutor—answering questions, generating code samples, and explaining complex code syntax and logic. These tools dramatically speed the learning process and help developers gain confidence in their coding abilities. 

Building applications with AI tools hastens development and provides more robust code. For example, GitHub Copilot helps us write new code accurately. It also quickly makes sense of existing code, suggests whole new lines or blocks of code, and even detects errors and proposes fixes. It’s like having a second coder working alongside us—one who never tires and is trained on billions of lines of code. 

However, it’s also important to acknowledge the limitations of AI. While generative AI-assistance technologies like GitHub Copilot are impressive, they’re still imperfect. They can sometimes produce erroneous, non-functional, or vulnerable code. They can also lack nuanced, domain-specific knowledge.  

So, although these tools are helpful, we can’t rely on them fully—these are programming aids rather than definitive solutions. AI-assisted pair programming is about augmenting the human developer’s skills, not replacing them.

Debugging and Improving Code With AI: Enhancing Application Quality

Identifying and correcting code errors is often time-consuming and challenging for developers. AI streamlines the debugging process by automatically detecting anomalies, suggesting fixes, and learning from previous bugs to prevent them from recurring. 

We can use generative AI to create unit tests from code snippets, from simple parameter checking and input validation to complex mocks of database and network services. Automated AI testing improves code coverage and robustness. It saves significant time compared to manually crafting unit tests. 

AI also revolutionizes code reviews by scanning and highlighting potential issues—like code patterns leading to a bug or a coding standards violation—and suggesting ways to simplify or refactor the code for readability and performance. This reduces human error and makes the code review process more efficient. Its benefits extend to small teams, where the availability of developers is crucial to deadlines, and large teams, where effective collaboration depends on readability and code consistency. AI further saves time by triaging issues so developers can prioritize the most mission-critical ones. 

Aside from improving code quality, AI is critical to understanding system behavior. Intelligent AI-assisted software development tools can monitor and audit system behavior, giving developers a better insight into how their code performs in different scenarios. AI can track patterns, identify inefficiencies, and provide alerts when they detect unusual behavior—enabling developers to enhance their code further. 

By predicting and identifying potential vulnerabilities, intelligent AI-assisted coding apps like Microsoft Security Copilot are pivotal in ensuring application functionality, reliability, and security. These AI tools are continually trained and upgraded with large codebases and user feedback that refines their responses. This ability to learn from past mistakes makes AI an incredibly powerful tool that will continue to expand its capabilities in the future.

info

Complete the AI Cloud Skills Challenge to build on you AI application development skills with paired programming using generative AI and GitHub Copilot.

AI and the Future of Application Development

The rapid advancements in AI and machine learning technologies point to an exciting future for application development. 

In “The Future of Applications,” software developer Mike Cann illustrates how LLMs might revolutionize application development. Cann that suggests fully-autonomous coding—AI-generated applications created entirely from prompts—may supersede traditional hand-crafted applications. Dynamically generated user interfaces could offer personalized and customizable experiences while we can design APIs for machine consumption and efficiency rather than human readability. Furthermore, we can store data in less structured, more adaptable, and flexible forms. 

AI is already making application development more accessible. With AI handling the complex, technical aspects of development, less-technical individuals can take a greater role in building applications. Consequently, AI can potentially democratize software engineering. 

Intelligent apps will continue to evolve, becoming more efficient, adaptable, and capable of learning from user interactions. Similarly, AI will inevitably become integral in every stage of app development.

Summary

AI has already created a new development paradigm—one in which machine intelligence and human creativity come together to produce innovative, efficient, and robust applications. AI already assists us at every stage of app development, from design and architecture to coding and debugging. 

Developers who embrace AI tools and integrate them into their processes can enhance their productivity, improve the quality of their applications, and expand the possibilities of their application development.  

The future of development is automated and AI-driven, and it’s here today. Getting started is easy—try an AI-powered development tool like GitHub Copilot to see how AI-assisted pair programming can enhance your productivity and code quality.

Exercise

+ + \ No newline at end of file diff --git a/30daysofIA/page/7/index.html b/30daysofIA/page/7/index.html index f9c0d822a8..5a663ff496 100644 --- a/30daysofIA/page/7/index.html +++ b/30daysofIA/page/7/index.html @@ -3,7 +3,7 @@ -Learn in #30DaysOfIA | Build Intelligent Apps On Azure +Learn in #30DaysOfIA | Build Intelligent Apps On Azure @@ -14,14 +14,13 @@ - - + +
-
Skip to main content

· 4 min read
It's 30DaysOfIA

This Fall focus on building intelligent apps using AI and cloud-native technologies. #FallForIntelligentApps brings to you a learning journey to build your skills on creating differentiated experiences while modernizing your applications. It’s time to learn it all.

What We'll Cover

image

Get Ready To #FallForIntelligentApps starting September 18!

Today, we kick off the Fall season with content and activities to skill you up on all things Intelligent Apps or AI Apps on Azure with content, events, and community interactions! Read on to learn about what is coming!

Explore Our Initiatives

We have a number of initiatives planned for the month to help you learn and skill up on relevant technologies. Click on the links to visit the relevant pages for each

image

Register for the events!

What are 4 things you can do today, to jumpstart your learning journey?

#30Days Of Intelligent Apps

#30DaysOfIA is a series of daily blog posts grouped into themed weeks - taking you from core concepts to end-to-end solution examples in 30 days. Each article will provide conceptual lessons paired with exercises and resources to help you reinforce learnings and take next steps.

This series takes you through learning journey in six stages, each building on the previous week to help you skill up in a beginner-friendly way:

We will start with defining intelligent apps and then expand on how to build with cloud-native technologies like Azure Kubernetes Service, Azure Container Apps and Azure Functions, and take you through end-to-end scenarios for real world application development. Before we dive deep on intelligent apps, here is a high-level overview of the Intelligent Apps landscape on Azure:

image

Containers on Azure services offer you a wide range of capabilities, from simplicity to control to suit your different needs.

image -image

To start with the basics for developing Kubernetes applications, explore #30DaysOfCloudNative.

Cloud-native development when paired with serverless computing enhances your solution architecture for building cost optimized, resilient applications.

image

To start with the basics for serverless computing, explore #30DaysOfServerless.

Let’s Get Started

Now you know everything! We hope you are as excited as we are to dive into a full month of active learning and doing! Don't forget to subscribe for updates in your favorite feed reader! And look out for our first Intelligent Apps post Monday!

- - +
Skip to main content

· 10 min read
It's 30DaysOfIA

Explore real-world examples of how Intelligent Apps revolutionize logistics, retail, finance, manufacturing, tech, and medicine.

What We'll Cover:

  • Real-world businesses revolutionizing operations with Intelligent Apps
  • High level architecture for industry scenarios
  • Implementation architecture overview across industries

A decorative header image for the blog

Real-world Success Stories

According to IBM’s 2022 Global AI Adoption Index, 35% of surveyed companies already use AI in their business, and 42% are exploring the idea. Organizations lagging behind the AI curve will quickly fall behind early adopters, who are already enjoying its benefits and boosting their investment in this technology.

In the first part of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” we explored how organizations can use AI-powered apps, or intelligent apps, to transform their operations, decision-making, and customer experiences, leaping ahead of the competition.

Intelligent apps significantly improve operational efficiency and customer engagement across industries like logistics, retail, finance, manufacturing, tech, and healthcare. In this article, we’ll spotlight real-world success stories of businesses revolutionizing operations with intelligent apps using Microsoft Azure’s app, data, and AI services.

info

Register for the Learn Live on September 21 for a live guided experience with SMEs on how to build end-to-end solution architecture for real world implementation of Intelligent Apps.

Streamlining Operations: Intelligent Apps in the Airline Industry

Aerospace pioneer Airbus leveraged intelligent apps to streamline its operations and innovate its services with two ground-breaking modernizations that use Azure apps, data, and AI services to reimagine pilot training and predict aircraft maintenance issues. 

Aviation technology’s rapid evolution compelled Airbus to reconceive their approach to training pilots. The aircraft manufacturer created a pilot training chatbot by combining Azure AI Services with Speech to Text and Text to Speech. The bot helps trainee pilots navigate over 6,000 pages of technical information. Pilots can ask questions verbally for quick answers, aiding their understanding of dense information and speeding up training. And when Airbus needed to accommodate its military consumers, deploying its Kubernetes cluster to a disconnected, “air-gapped” environment met the strict security requirements of military aircraft training.

A chatbot could look like the following diagram as Azure services work together to create a conversational experience. Its components could include security and governance, bot logic and user experience (UX), bot cognition and intelligence, data extract, transfer, and load (ETL) operations, quality assurance and enhancements, logging, and monitoring and reporting.

A diagram of a conversational experience enabled by Azure services

Airbus also used AI to improve its aircraft maintenance and safety. They deployed AI Anomaly Detector to gather and analyze telemetry data from multiple flights, enabling more profound insights into wear and tear on military aircraft operating in harsh conditions. This AI application helps Airbus better understand each airplane’s health. Airbus can predict and fix potential problems before they occur, improving the aircraft’s safety and operational readiness.

AI and Azure are changing how organizations like Airbus operate, even in strict, highly regulated markets with stringent security requirements. And by streamlining pilot training and staying ahead of aircraft maintenance, Airbus helps its military and commercial clients reliably deliver their goods across the globe, maintaining their critical supply chains.

Boosting Customer Service: Intelligent Apps in the Retail Industry

The retail industry has been among the first to adopt and benefit from intelligent apps. For example, the leading American used car retailer, CarMax, uses intelligent apps to transform the car shopping experience.

Before embracing AI, CarMax faced a significant challenge: Manually creating text summaries was time-consuming and labor-intensive. CarMax decided to leverage Azure OpenAI Service to automatically generate car research pages, offering customers valuable insights while enhancing the website’s search engine rankings.

This AI-driven approach has significantly streamlined the process, achieving in a few months what would have taken years manually. Also, the AI system condenses thousands of customer reviews into a few readable sentences, offering potential buyers a quick overview of others’ experiences.

CarMax has improved their customer experience while boosting operational efficiency through AI adoption. CarMax’s editorial staff focuses on creating strategic, longer-form content with the time saved, enhancing productivity, and driving more website traffic.

CarMax’s success story serves as a testament to the potential of intelligent apps in the retail industry.

Enhancing Cybersecurity: Intelligent Apps in Finance

Intelligent apps also impact the highly regulated financial services sector. Swift, a leading infrastructure provider for financial messaging services, uses intelligent apps to enhance decision-making and combat financial crime.

The massive growth of cross-border transactions and instant payment networks led to a surge in financial fraud that can cost the industry hundreds of billions of dollars every year. So, Swift and Microsoft partnered to build an anomaly detection model using federated learning techniques with Azure Machine Learning and Azure confidential computing.

Microsoft Purview helped govern data, as the federated learning method partially trained each model in a silo before aggregating them. This approach enabled Swift and Microsoft to develop a highly accurate model without copying or moving data from Swift members’ secure locations, ensuring the highest level of security and privacy.

Financial organizations can now deploy this trained ML model to run a real-time fraud detection system, lowering the cost of financial fraud.

Organizations can deploy a fraud detection system like the following example on Microsoft Azure. The system ingests data from streams or storage, runs analytics, and uses ML to detect fraud and store results. It then alerts users and other systems to the security issue.

A diagram of an example fraud detection system

Swift provides the innovative model to its banking partners, who can share data to further train the model. Azure confidential computing securely runs new models on current data to accumulate insights, generating an ongoing learning cycle. This iterative approach strengthens the global detection of fraudulent financial transactions through an increasingly faster and more accurate model. 

The collaboration between Swift and Microsoft demonstrates how combining various Azure services creates Intelligent Apps to enhance decision-making in finance. These apps solve real problems at an unprecedented global scale. 

Enhancing Efficiency: Intelligent Apps in Manufacturing

3M, known for Post-it Notes and other innovative products, wielded the power of Intelligent Apps to standardize, automate, and accelerate its sales forecasting. The company needed a unified, automated approach to replace its multiple manual methods.

It adopted Azure Machine Learning and other Azure AI Services to develop, train, and implement 1,500 custom models for each region and division, replacing manual methods. This solution delivers timely finance, sales, and marketing insights while freeing teams from time-consuming manual data manipulation tasks.

3M used Azure Machine Learning, automated machine learning (AutoML), and the Many Models Solution Accelerator to train and score numerous machine learning models in parallel. This approach significantly shortened the company’s development cycle time.

Then, 3M integrated Microsoft Power BI into their solution to compile and visualize results more effectively. Executives now have an unbiased and unfiltered view of data for better decision-making. With its improved ability to forecast sales, 3M can focus on manufacturing the products it needs efficiently.

3M’s success demonstrates how combining various Azure services can enhance efficiency in the manufacturing industry. These apps improve operational processes while enabling employees to focus on higher-level tasks. Strategic decisions are more targeted, and business conversations are more meaningful.

Driving Innovation: Intelligent Apps in the Technology Industry

As expected, the tech industry is at the forefront of leveraging Intelligent Apps to drive innovation. Elastic saw an opportunity to use intelligent apps to optimize its search services and meet customer needs more effectively. The company built a managed, secure, and scalable solution for its customers, Elastic Cloud, by leveraging Microsoft Azure.

Elastic Cloud uses Azure Machine Learning and other Azure AI Services to automate many basic management tasks, such as backing up data, upgrading, and scaling deployments. This approach enables Elastic’s customers to focus less on managing the software and more on serving their own clients. Customers can quickly deploy and manage Elastic Cloud on Kubernetes (ECK) using Azure Kubernetes Service.

As a result, Elastic’s cloud business is growing twice as fast as its on-premises solutions. Enterprise search users have reported significant improvements in system availability and employee satisfaction.

Additionally, Elastic incorporated Azure OpenAI Service into its Elasticsearch Relevance Engine (ESRE), providing more relevant answers to search queries. ESRE enables text, vector, and hybrid search, offering a more effective and accurate response to queries.

Elastic’s success story demonstrates how combining various Azure services can create intelligent Apps to drive innovation in the tech industry. These apps help improve operational processes and deliver better results quickly.

Revolutionizing Healthcare: Intelligent Apps in Medicine

Intelligent apps also drive transformative changes in the healthcare sector. Cambridgeshire and Peterborough NHS Foundation Trust, a healthcare provider in the United Kingdom, gradually shifted their on-premises systems to the cloud using Azure, improving flexibility, and lowering costs. They needed a solution for their electronic patient record (EPR) system that integrates 23 years of data across multiple systems and formats.

The Trust used Microsoft Azure and Azure Cognitive Search, including semantic search for understanding intent and contexts and cognitive capabilities for extracting insights from data, to standardize and automate data management. This approach made legacy, archive, and live data more discoverable, saving clinicians’ time. The Trust’s solution searches through millions of clinical documents in seconds, even recognizing handwritten notes without needing a separate optical character recognition (OCR) solution to extract text from images (Azure Cognitive Search automatically does it). Clinicians can now uncover previously inaccessible insights, enabling them to offer more personalized care.

The Trust also used Integration Services tools such as Azure Logic Apps to create workflows without writing code and Event Grid to connect Azure and third-party services through a publisher-subscriber model. They switched on a live feed from their new EPR system to Azure to feed real-time information into other applications. Researchers can now access anonymized data to fuel medical innovations beyond the NHS.

The healthcare provider also invested heavily in Power BI’s data visualization and analytical functions to sift through massive amounts of information. This level of data accessibility was previously impossible with their on-premises system. The new insights have significantly improved clinicians’ efficiency for better patient care.

Data accessibility and security are critical in the healthcare industry. Intelligent Apps empower organizations like the Trust to revolutionize medical care. AI capabilities helped the Trust improve its operational efficiency and user experiences, setting a new standard for healthcare in the digital age.

Summary

AI is driving transformation and real-world successes across industries. Intelligent apps are reshaping our world by streamlining operations in manufacturing and logistics, enhancing customer experience in the retail sector, improving decision-making in finance, driving innovation in tech, and revolutionizing healthcare.

As organizations adopt AI, they’re innovating in ways that were once too difficult, expensive, or outright impossible. Companies that wait to embrace intelligent apps risk the chance of falling behind. By embracing intelligent apps and leveraging the capabilities of Azure, you can harness the transformative potential of AI to remain on the cutting edge of technology.

Exercise

+ + \ No newline at end of file diff --git a/30daysofIA/page/8/index.html b/30daysofIA/page/8/index.html index 0349666870..f6620b71f1 100644 --- a/30daysofIA/page/8/index.html +++ b/30daysofIA/page/8/index.html @@ -3,7 +3,7 @@ -Learn in #30DaysOfIA | Build Intelligent Apps On Azure +Learn in #30DaysOfIA | Build Intelligent Apps On Azure @@ -14,14 +14,13 @@ - - + +
-
Skip to main content

· 4 min read
It's 30DaysOfIA

Continue The Learning Journey through Fall For Intelligent Apps! 🍂

What We'll Cover

Thank you! ♥️

image

It's hard to believe that JavaScript on Azure hack-together is ending! It seems like just yesterday that we launched this initiative, and yet here we are, 15 days later, with an incredible amount of learning and growth behind us. So... it's time for a wrap!

From the bottom of our hearts, we want to thank each and every one of you for your participation, engagement, and enthusiasm. It's been truly inspiring to see the passion and dedication from this strong community, and we're honored to be a part of it. ✨

Recap of The JavaScript on Azure Global Hack-Together

As we wrap up this exciting event, we wanted to take a moment to reflect on all that we've accomplished together. Over the last 15 days, we've covered a lot of ground, from the basics of contributing to Open source to the exploration of the Contoso Real Estate project from its Frontend to its Backend and future AI implementation.

Now that the hack-together is ending, we want to make sure that you have all the resources you need to continue honing your skills in the future. Whether you're looking to make your fist contribution to open source, become an open source maintainers, collaborate with others, or simply keep learning, there are plenty of resources out there to help you achieve your goals. So, let's dive in and explore all the ways you can continue to grow your JavaScript skills on Azure!

JSonAzure Hack-together Roadmap 📍:

hack-together-roadmap (2)

Recap on past Livestreams🌟:

Day 1️⃣: Opening Keynote (Hack-together Launch): Introduction to the Contoso Real Estate Open-source project!, managing complex and complex enterprise architecture, new announcements for JavaScript developers on Azure

Day 2️⃣: GitHub Copilot & Codespaces: Introduction to your AI pair programmer (GitHub Copilot) and your virtual developer environment on the cloud (GitHub Codespaces)

Day 6️⃣: Build your Frontend using Static Web Apps as part of a complex, modern composable frontends (or micro-frontends) and cloud-native applications.

Day 9️⃣: Build a Serverless Backend using Azure Functions

Day 1️⃣3️⃣: Easily connect to an Azure Cosmos DB, exploring its benefits and how to get started

Day 1️⃣5️⃣: Being in the AI Era, we dive into the Azure OpenAI Service and how you can start to build intelligent JavaScript applications

📖 Self-Learning Resources

  1. JavaScript on Azure Global Hack Together Module collection
  2. Lets #HackTogether: Javascript On Azure Keynote
  3. Step by Step Guide: Migrating v3 to v4 programming model for Azure Functions for Node.Js Application

Continue your journey with #FallForIntelligentApps

Join us this Fall on a learning journey to explore building intelligent apps. Combine the power of AI, cloud-native app development, and cloud-scale data to build highly differentiated digital experiences. Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure. Engage in a self-paced learning adventure by following along the #30Days of Intelligent Apps series, completing the Intelligent Apps Skills Challenge, joining the Product Group for a live Ask The Expert series or building an end to end solution architecture with a live guided experience through the Learn Live series. Discover more here.

Hands-on practice: Make your first contribution to open source!

Join our GitHUb Discussion Forum to connect with developers from every part of world, see contributions from other, find collaborators and make your first contribution to a real-world project! -Don't forget to give the repo a star ⭐

Resources

All resources are accessible on our landing page

- - +
Skip to main content

· 8 min read
It's 30DaysOfIA

Let’s start with understanding the power of intelligent applications.

What We'll Cover:

  • What are intelligent applications?
  • Categories of Intelligent Apps
  • Breaking down the role of AI in intelligent apps

A decorative header image for the blog

Demystifying Intelligent Applications: Leveraging AI in App Development

Until recent years, when you heard the term “artificial intelligence” (AI), you might have pictured autonomous robots from sci-fi films. Now, perhaps your mind jumps to big data and machine learning (ML). Maybe you’re even thinking about the latest ChatGPT plugins and transformer architectures. Today, AI is more than these imaginations: it’s a reality increasingly woven into our day-to-day routines through intelligent applications.

Intelligent apps combine cloud-scale compute, data, and predictive or generative AI. These applications help you automate tasks and make data-driven decisions, aiding in leveraging business intelligence to gain meaningful, practical outcomes. 

For developers, the AI landscape isn’t an abstract complexity. It’s a real, tangible opportunity for evolving traditional applications, making them “intelligent,” and delivering more value to users. And with tools like Azure Kubernetes Service, Azure Cosmos DB, and Azure AI Services, stepping into the world of intelligent apps isn’t a steep climb but a steady trek across a manageable learning curve.

The Three Categories of Intelligent Applications

Before we start building, it’s critical to understand what makes an app “intelligent.” It’s not a universal definition so much as an identifier for apps that fall into one or more of the three primary categories: 

  • Outcome-based — This designation represents the broadest category of intelligent apps, whose principal value for users is the intelligent outcomes they provide. These apps process data to provide insightful results that enhance decision-making or help with tasks like trend prediction, resource management, and customer relationship management.

  • Functionality-based — This type of intelligent app takes AI integration a degree further, integrating AI as a function of the app to create or process its outputs, enhancing the app’s overall interactivity and usefulness.

  • Feature-based — This is the narrowest category, in which the app’s AI/ML component is one of its core features and primary selling points

Let’s dive more deeply into each category.

Outcome-Based Apps: Intelligent Outcomes Drive Success

Outcome-based applications are all about the results provided to the user. They function beyond merely processing data to offer insightful outcomes that drive success.

Think of a personal fitness tracker app that uses AI to analyze the user’s activity and provides actionable recommendations for achieving a goal. This type of app not only tracks your steps, sleep patterns, and heart rate but uses that data to provide you with personalized health advice. It helps you make informed lifestyle choices — the “intelligent” outcome.

Another example is a social media marketing app that uses AI for time-saving data analysis, task automation, and trend prediction. AI-based sentiment analysis can gauge customer perception while natural language processing (NLP) accurately interprets customer inquiries. Task automation can schedule posts and automate responses to common customer queries, and trend forecasting helps model and predict customer behavior to further personalize targeted promotional efforts. Building this type of intelligence into your apps provides cost-effective, actionable insights that guide sales and marketing strategies.

Functionality-Based Apps: AI-Driven Features Enhance User Experiences

As the name suggests, functionality-based applications integrate AI into how the app functions, but the end user drives the action and outcome. In this category of apps, AI takes a more active role in the user’s interaction by allowing them to use AI-created or processed output. These apps leverage AI technologies like Azure AI Services for NLP, image recognition, and pattern recognition to improve user experience.

Consider a music app that generates personalized playlists based on your listening habits or your smartphone’s camera app that uses AI to recognize faces and optimize photo settings. Even more advanced are the language translation apps that translate text from one language to another and can even understand and maintain the context and subtleties of the conversation. They use NLP to accomplish these feats.

Similarly, consider the latest e-commerce apps, whose AI-driven functionalities enable customers to try on clothes virtually, significantly enhancing the online shopping experience.

Feature-Based Apps: Integrating Advanced AI/ML Components

Finally, there are feature-based intelligent apps. These apps incorporate advanced AI/ML components like neural networks, internal large language models (LLMs), and sophisticated algorithms as core features for the user. In this type of application, AI is the primary feature for users to engage with and the primary selling point for the app.

Some examples of feature-based applications are chatbots and virtual agents built using Azure Bot Service. These robo-representatives use AI to provide more natural and human-like conversation, balancing superior efficiency with a friendlier, more personalized user experience. The new Bing is a prime example of this implementation, integrating AI to give users a knowledgeable copilot for web searches.

Similarly, OpenAI’s ChatGPT allows users access to its state-of-the-art language models available as Microsoft’s Azure OpenAI Service. It leverages the power of the highly sophisticated GPT-3 LLM to perform tasks like drafting emails, writing code, answering general knowledge questions, translating between languages, and tutoring in numerous subjects.

These apps don’t just use AI to provide outputs or improve user experiences: They push the boundaries of what’s possible in app development by integrating AI/ML components.

info

Register for the live Q&A Ask The Expert session on September 26 with the Azure Functions product engineering team.

Breaking Down the Role of AI in Intelligent Applications

Intelligent apps reflect the merging of traditional software design with the dynamic capabilities of AI. Let’s explore how AI integrates and amplifies these applications.

At its core, AI augments applications with the capacity to self-learn and self-improve. It allows you to create applications that predict, react, and adapt to a constantly changing environment. In the power grid management system for a major city, an AI-infused application can forecast power usage trends, react to anomalies, and adapt to sudden demand surges, ensuring optimal efficiency and preventing blackouts.

But AI’s role doesn’t end at prediction or decision-making: It also automates tasks. In enterprise solutions, it has moved beyond simple automation to encompass intelligent process automation (IPA). In this arena, AI algorithms understand, learn, and then automate entire digital workflows. For developers, this is a fertile ground for creating applications that can dramatically improve the operational efficiency of businesses.

Even more, AI substantially aids in development tasks. For example, there’s GitHub Copilot, an AI-powered code assistant that offers suggestions based not only on syntax but on development best practices and the intent of the code. AI has evolved from a tool you use to build apps to a partner that helps in creating them. 

Using AI to build intelligent apps isn’t about throwing out what’s worked before. It’s about enhancing the components that have the most impact and achieve the best results. Incorporating AI in apps elevates their predictive abilities, streamlines complex tasks, and even aids in the development and creation process. When you build intelligent apps, you don’t just address current problems. You anticipate future challenges and craft solutions that adapt over time.

Summary

As developers the power of AI is in our hands, and it’s our responsibility to harness its full potential. Each category tells the story of AI's transformative potential, from applications that deliver intelligent outcomes to those with AI capabilities to enrich the user experience and those pushing boundaries with advanced AI/ML technology.

Accessible and robust platforms like Azure AI can simplify the process of developing Intelligent Applications. Whether you’re refining an existing app or building a new one, just entering the world of AI, or already familiar with the landscape, take a moment to appreciate the potential that AI integration offers. You’re embracing this exciting frontier and shaping the future of app development. 

Exercise

  • Complete the Cloud Skills Challenge to build on your apps and AI skills.
  • Watch Episode 01 of the Serverless Edition Learn Live session to learn how to build an end-to-end intelligent app solution.
+ + \ No newline at end of file diff --git a/30daysofIA/page/9/index.html b/30daysofIA/page/9/index.html index 88540e61e6..eed495c041 100644 --- a/30daysofIA/page/9/index.html +++ b/30daysofIA/page/9/index.html @@ -3,7 +3,7 @@ -Learn in #30DaysOfIA | Build Intelligent Apps On Azure +Learn in #30DaysOfIA | Build Intelligent Apps On Azure @@ -14,13 +14,14 @@ - - + +
-
Skip to main content

· 2 min read
It's 30DaysOfIA

September is almost here - and that can only mean one thing!! It's time to 🍂 Fall for something new and exciting and spend a few weeks skilling up on relevant tools, techologies and solutions!!

Last year, we focused on #ServerlessSeptember. This year, we're building on that theme with the addition of cloud-scale Data, cloud-native Technologies and cloud-based AI integrations to help you modernize and build intelligent apps for the enterprise!

Watch this space - and join us in September to learn more!

- - +
Skip to main content

· 4 min read
It's 30DaysOfIA

This Fall focus on building intelligent apps using AI and cloud-native technologies. #FallForIntelligentApps brings to you a learning journey to build your skills on creating differentiated experiences while modernizing your applications. It’s time to learn it all.

What We'll Cover

image

Get Ready To #FallForIntelligentApps starting September 18!

Today, we kick off the Fall season with content and activities to skill you up on all things Intelligent Apps or AI Apps on Azure with content, events, and community interactions! Read on to learn about what is coming!

Explore Our Initiatives

We have a number of initiatives planned for the month to help you learn and skill up on relevant technologies. Click on the links to visit the relevant pages for each

image

Register for the events!

What are 4 things you can do today, to jumpstart your learning journey?

#30Days Of Intelligent Apps

#30DaysOfIA is a series of daily blog posts grouped into themed weeks - taking you from core concepts to end-to-end solution examples in 30 days. Each article will provide conceptual lessons paired with exercises and resources to help you reinforce learnings and take next steps.

This series takes you through learning journey in six stages, each building on the previous week to help you skill up in a beginner-friendly way:

We will start with defining intelligent apps and then expand on how to build with cloud-native technologies like Azure Kubernetes Service, Azure Container Apps and Azure Functions, and take you through end-to-end scenarios for real world application development. Before we dive deep on intelligent apps, here is a high-level overview of the Intelligent Apps landscape on Azure:

image

Containers on Azure services offer you a wide range of capabilities, from simplicity to control to suit your different needs.

image +image

To start with the basics for developing Kubernetes applications, explore #30DaysOfCloudNative.

Cloud-native development when paired with serverless computing enhances your solution architecture for building cost optimized, resilient applications.

image

To start with the basics for serverless computing, explore #30DaysOfServerless.

Let’s Get Started

Now you know everything! We hope you are as excited as we are to dive into a full month of active learning and doing! Don't forget to subscribe for updates in your favorite feed reader! And look out for our first Intelligent Apps post Monday!

+ + \ No newline at end of file diff --git a/30daysofIA/preparing-the-path-for-intelligent-apps/index.html b/30daysofIA/preparing-the-path-for-intelligent-apps/index.html index e2c52b89f5..62b06c2412 100644 --- a/30daysofIA/preparing-the-path-for-intelligent-apps/index.html +++ b/30daysofIA/preparing-the-path-for-intelligent-apps/index.html @@ -14,13 +14,13 @@ - - + +
-
Skip to main content

1-5. Preparing the Path for Intelligent Apps

· 9 min read
It's 30DaysOfIA

Learn best practices for modernizing your on-premises or IaaS solutions to intelligent apps in the cloud.

What We'll Cover:

  • Traditional vs Intelligent Apps
  • On-Premises/IaaS vs. Cloud-Native Platforms
  • Modernization strategy for building Intelligent Apps

image of modernizing AI solutions for intelligent apps

Preparing the Path for Intelligent Apps: Transitioning from On-Premises/IaaS to Cloud-Native Applications

The proliferation of generative AI is paving the way for intelligent applications, software that leverages AI capabilities to deliver unparalleled functionality and user experience. As we covered in a previous article, “Demystifying Intelligent Applications: Leveraging AI in App Development,” intelligent Apps aren’t just products of advanced coding. They interact, learn, and evolve thanks to modern AI and machine learning (ML) breakthroughs. 

The paradigm shift from traditional to intelligent apps demands that we change our approach to the technical challenges of software and architecture design. It also necessitates adopting new ways of thinking and operating within an organization, which we explore in greater depth in “Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management.” 

This article explores the technical infrastructure powering intelligent apps, providing a roadmap to transition traditional on-premises or Infrastructure as a Service (IaaS) solutions to intelligent apps deployed to cloud native platforms and services. We’ll discover the differences between conventional and intelligent apps, options for re-architecting existing applications, and critical strategic considerations integral to modernizing the application-building approach. 

Understanding the Shift: Traditional vs. Intelligent Applications

Intelligent apps are more than just an incremental evolution from traditional apps. They involve a fundamental shift in software solution design, development, and deployment. A clear understanding of how each paradigm differs is crucial for comprehending the impact of this transition. 

Functionality and User Experience

Traditional applications, often rule-based and rigid, rely on preprogrammed operations. A traditional weather app, for example, typically presents structured, location-based forecasts using a predetermined data source. Meanwhile, an intelligent app leverages AI for data-driven decision-making and personalization. Rather than merely displaying the forecast, it integrates ML with additional data sources—like a user’s calendar and fitness data—to determine their favorite outdoor activities, provide relevant clothing suggestions, and even automatically design ideal vacations and times based on the user’s optimal weather preferences. 

Similarly, while traditional applications tend to offer a one-size-fits-all UI, intelligent apps use AI to create tailored interactions. For instance, an intelligent banking app might use generative AI to provide a voice-enabled UI that lets customers use natural language to ask about transactions and receive personalized financial advice.

Infrastructure

Intelligent apps integrating these sophisticated features require highly scalable, flexible infrastructure optimized for AI functionality and able to manage large volumes of real-time data. Unlike traditional applications deployed on-premises or in infrastructure as a service (IaaS) environments, intelligent apps demand the flexibility and scalability of cloud native architectures.

An on-premises or IaaS-based e-commerce application may struggle with connecting its data to AI services or scaling to accommodate voluminous real-time data and requests. But an intelligent app built with a cloud native approach and scalability at its core can handle variable demand much more efficiently. It also maintains its infrastructure components on demand, whether for collecting data for AI/ML consumption or user-facing features like AI-enabled customer support and personalization.

Consider an intelligent app built on Azure that creates ChatGPT-like experiences with custom data. It might connect Azure Open AI and Azure Cognitive Search to an app running on Azure Container Apps or Azure Kubernetes Service. 

The architecture might look like the following diagram based on this demo app:

diagram of a demo app architecture

While the cloud is well-equipped to handle the challenges associated with shifting to AI-centric infrastructure—handling real-time data processing and storage, integrating and managing AI services, configuring event-driven and microservices architectures, and ensuring security across the distributed environment—re-architecting a traditional app to an intelligent app requires careful planning and execution to ensure it benefits fully from the capabilities of AI and cloud computing.

On-Premises/IaaS vs. Cloud-Native Platforms

While on-premises and IaaS solutions provide substantial control over data and processes, this fine-grained control comes with setup and maintenance costs and may lack the scalability necessary for AI-driven Intelligent Apps. On-premises hosting involves purchasing and maintaining hardware, including servers and storage systems. IaaS involves leasing virtual hardware and then hiring and training IT personnel for the infrastructure’s operation, maintenance, and security.

Conversely, infrastructure built on cloud native app services with Microsoft Azure offers virtually unlimited scalability without ongoing hardware and maintenance costs. This feature is ideal for intelligent apps that need to adapt and scale to accommodate fluctuating workloads and data streams. And because these cloud infrastructures typically operate on a pay-as-you-go model, traditionally substantial upfront costs turn into predictable operating expenses, lowering both the cost and risk of launching applications.

Moreover, cloud service providers continually update their offerings, so building on sophisticated AI tools and services through the cloud, like the Azure AI Platform and orchestration capabilities with Azure Kubernetes Service, ensures that you have access to the latest AI and most advanced AI models and technologies.

Strategic Considerations for Transitioning to Intelligent Apps

When developing a strategy for transitioning to intelligent apps, you must consider costs, personnel skill development, and data management.

Monolithic architectures are unlikely to support the scale and ease of iteration required by intelligent apps, so you’ll likely need to implement code changes to support the transition. Your chosen hosting solution and the amount of data your app will handle affect cost and performance, in addition to determining privacy and compliance requirements.

An app that manages or stores terabytes or petabytes of data will require storage-optimized virtual machines or a managed database service hosting. Meanwhile, an app that processes video content or performs resource-intensive calculations should prioritize high-performance computing. Maximizing storage and performance can become extraordinarily costly.

Finally, existing applications shifting towards intelligent apps may encounter compatibility conflicts, depending on database type or the existing system’s underlying programming language. As you might imagine, a traditional app running on-premises and using a shared local database may need significant restructuring to support a cloud native architecture. You need plans for data migration, potential downtime, and training.

Next, let’s look at this transformation process.

Re-Architecting Monolithic Apps into Microservices

Shifting from monolithic applications to microservices is pivotal for transitioning to Intelligent Apps, and you can use this shift as an opportunity to transform your legacy software into intelligent apps that leverage the flexibility and power of microservices-enabled AI solutions.

Traditionally, a monolithic application (let’s use an online marketplace as an example) might handle all activities—from serving webpages to processing payments—within an on-premises server or a cloud server. However, this approach is challenging to scale and remains vulnerable to network failures or cyberattacks.

Breaking these applications into multiple services with specific responsibilities, such as authentication or notifications, can overcome these limitations. These services are typically deployed as microservices within container environments like Azure Kubernetes Service or serverless platforms like Azure Container Apps or Azure Functions using automated processes. This approach offers scalability, robustness, and improved performance, as each microservice can run independently and use its own database.

Shifting towards smaller, independent microservices in app architectures allows us to reap the benefits of AI-powered intelligent apps, for example, by using microservices to handle AI tasks such as processing natural language input, analyzing behavioral data, or personalizing content.

info

Watch Episode 1 and Episode 2 of the Learn Live series on how to build, test and deploy an end-to-end intelligent app solution using the Contoso Real Estate Sample.

Event-Driven Architectures: Knowing When to Use Them

Event-Driven Architectures (EDAs) can significantly enhance the performance and cost-effectiveness of intelligent apps. EDAs, typically implemented with microservices, respond in real time to events or state changes ranging from user interactions to real-time analytics.

Consider an intelligent app designed for personalized customer engagement. Instead of designing the application to check continuously for changes in user data, an EDA can react to such changes immediately, triggering appropriate microservices. For example, if users modify their preferences, an event could trigger an AI-driven recommendation microservice to update its suggestions immediately. This real-time responsiveness, enabled by services like Azure Event Grid, can improve user experiences and make intelligent apps more adaptive and proactive.

However, it’s worth noting that while EDAs provide significant benefits, they can introduce unique complexities, such as maintaining event consistency and debugging issues in event chains across multiple microservices. Therefore, it’s important to assess your application’s needs so you can understand where it can benefit from an EDA.

Conclusion

Transitioning to intelligent apps represents a shift in strategy, architecture, and infrastructure. By designing and re-architecting for cloud-based infrastructure, your apps gain scalability, cost-effectiveness, robust data management, access to sophisticated AI tools, enhanced security, and reduced operational overhead.  

But this transition extends well beyond its technical components. Organizations and the people working within them need equal attention. You can explore these facets in greater depth in “Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management.” 

The power of generative AI is here, and preparing for the move toward cloud-based intelligent applications will ensure that your apps remain performative and scalable and—most importantly—that your business is ready for the newest era of app innovation.

Exercise

- - +
Skip to main content

1-5. Preparing the Path for Intelligent Apps

· 9 min read
It's 30DaysOfIA

Learn best practices for modernizing your on-premises or IaaS solutions to intelligent apps in the cloud.

What We'll Cover:

  • Traditional vs Intelligent Apps
  • On-Premises/IaaS vs. Cloud-Native Platforms
  • Modernization strategy for building Intelligent Apps

image of modernizing AI solutions for intelligent apps

Preparing the Path for Intelligent Apps: Transitioning from On-Premises/IaaS to Cloud-Native Applications

The proliferation of generative AI is paving the way for intelligent applications, software that leverages AI capabilities to deliver unparalleled functionality and user experience. As we covered in a previous article, “Demystifying Intelligent Applications: Leveraging AI in App Development,” intelligent Apps aren’t just products of advanced coding. They interact, learn, and evolve thanks to modern AI and machine learning (ML) breakthroughs. 

The paradigm shift from traditional to intelligent apps demands that we change our approach to the technical challenges of software and architecture design. It also necessitates adopting new ways of thinking and operating within an organization, which we explore in greater depth in “Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management.” 

This article explores the technical infrastructure powering intelligent apps, providing a roadmap to transition traditional on-premises or Infrastructure as a Service (IaaS) solutions to intelligent apps deployed to cloud native platforms and services. We’ll discover the differences between conventional and intelligent apps, options for re-architecting existing applications, and critical strategic considerations integral to modernizing the application-building approach. 

Understanding the Shift: Traditional vs. Intelligent Applications

Intelligent apps are more than just an incremental evolution from traditional apps. They involve a fundamental shift in software solution design, development, and deployment. A clear understanding of how each paradigm differs is crucial for comprehending the impact of this transition. 

Functionality and User Experience

Traditional applications, often rule-based and rigid, rely on preprogrammed operations. A traditional weather app, for example, typically presents structured, location-based forecasts using a predetermined data source. Meanwhile, an intelligent app leverages AI for data-driven decision-making and personalization. Rather than merely displaying the forecast, it integrates ML with additional data sources—like a user’s calendar and fitness data—to determine their favorite outdoor activities, provide relevant clothing suggestions, and even automatically design ideal vacations and times based on the user’s optimal weather preferences. 

Similarly, while traditional applications tend to offer a one-size-fits-all UI, intelligent apps use AI to create tailored interactions. For instance, an intelligent banking app might use generative AI to provide a voice-enabled UI that lets customers use natural language to ask about transactions and receive personalized financial advice.

Infrastructure

Intelligent apps integrating these sophisticated features require highly scalable, flexible infrastructure optimized for AI functionality and able to manage large volumes of real-time data. Unlike traditional applications deployed on-premises or in infrastructure as a service (IaaS) environments, intelligent apps demand the flexibility and scalability of cloud native architectures.

An on-premises or IaaS-based e-commerce application may struggle with connecting its data to AI services or scaling to accommodate voluminous real-time data and requests. But an intelligent app built with a cloud native approach and scalability at its core can handle variable demand much more efficiently. It also maintains its infrastructure components on demand, whether for collecting data for AI/ML consumption or user-facing features like AI-enabled customer support and personalization.

Consider an intelligent app built on Azure that creates ChatGPT-like experiences with custom data. It might connect Azure Open AI and Azure Cognitive Search to an app running on Azure Container Apps or Azure Kubernetes Service. 

The architecture might look like the following diagram based on this demo app:

diagram of a demo app architecture

While the cloud is well-equipped to handle the challenges associated with shifting to AI-centric infrastructure—handling real-time data processing and storage, integrating and managing AI services, configuring event-driven and microservices architectures, and ensuring security across the distributed environment—re-architecting a traditional app to an intelligent app requires careful planning and execution to ensure it benefits fully from the capabilities of AI and cloud computing.

On-Premises/IaaS vs. Cloud-Native Platforms

While on-premises and IaaS solutions provide substantial control over data and processes, this fine-grained control comes with setup and maintenance costs and may lack the scalability necessary for AI-driven Intelligent Apps. On-premises hosting involves purchasing and maintaining hardware, including servers and storage systems. IaaS involves leasing virtual hardware and then hiring and training IT personnel for the infrastructure’s operation, maintenance, and security.

Conversely, infrastructure built on cloud native app services with Microsoft Azure offers virtually unlimited scalability without ongoing hardware and maintenance costs. This feature is ideal for intelligent apps that need to adapt and scale to accommodate fluctuating workloads and data streams. And because these cloud infrastructures typically operate on a pay-as-you-go model, traditionally substantial upfront costs turn into predictable operating expenses, lowering both the cost and risk of launching applications.

Moreover, cloud service providers continually update their offerings, so building on sophisticated AI tools and services through the cloud, like the Azure AI Platform and orchestration capabilities with Azure Kubernetes Service, ensures that you have access to the latest AI and most advanced AI models and technologies.

Strategic Considerations for Transitioning to Intelligent Apps

When developing a strategy for transitioning to intelligent apps, you must consider costs, personnel skill development, and data management.

Monolithic architectures are unlikely to support the scale and ease of iteration required by intelligent apps, so you’ll likely need to implement code changes to support the transition. Your chosen hosting solution and the amount of data your app will handle affect cost and performance, in addition to determining privacy and compliance requirements.

An app that manages or stores terabytes or petabytes of data will require storage-optimized virtual machines or a managed database service hosting. Meanwhile, an app that processes video content or performs resource-intensive calculations should prioritize high-performance computing. Maximizing storage and performance can become extraordinarily costly.

Finally, existing applications shifting towards intelligent apps may encounter compatibility conflicts, depending on database type or the existing system’s underlying programming language. As you might imagine, a traditional app running on-premises and using a shared local database may need significant restructuring to support a cloud native architecture. You need plans for data migration, potential downtime, and training.

Next, let’s look at this transformation process.

Re-Architecting Monolithic Apps into Microservices

Shifting from monolithic applications to microservices is pivotal for transitioning to Intelligent Apps, and you can use this shift as an opportunity to transform your legacy software into intelligent apps that leverage the flexibility and power of microservices-enabled AI solutions.

Traditionally, a monolithic application (let’s use an online marketplace as an example) might handle all activities—from serving webpages to processing payments—within an on-premises server or a cloud server. However, this approach is challenging to scale and remains vulnerable to network failures or cyberattacks.

Breaking these applications into multiple services with specific responsibilities, such as authentication or notifications, can overcome these limitations. These services are typically deployed as microservices within container environments like Azure Kubernetes Service or serverless platforms like Azure Container Apps or Azure Functions using automated processes. This approach offers scalability, robustness, and improved performance, as each microservice can run independently and use its own database.

Shifting towards smaller, independent microservices in app architectures allows us to reap the benefits of AI-powered intelligent apps, for example, by using microservices to handle AI tasks such as processing natural language input, analyzing behavioral data, or personalizing content.

info

Watch Episode 1 and Episode 2 of the Learn Live series on how to build, test and deploy an end-to-end intelligent app solution using the Contoso Real Estate Sample.

Event-Driven Architectures: Knowing When to Use Them

Event-Driven Architectures (EDAs) can significantly enhance the performance and cost-effectiveness of intelligent apps. EDAs, typically implemented with microservices, respond in real time to events or state changes ranging from user interactions to real-time analytics.

Consider an intelligent app designed for personalized customer engagement. Instead of designing the application to check continuously for changes in user data, an EDA can react to such changes immediately, triggering appropriate microservices. For example, if users modify their preferences, an event could trigger an AI-driven recommendation microservice to update its suggestions immediately. This real-time responsiveness, enabled by services like Azure Event Grid, can improve user experiences and make intelligent apps more adaptive and proactive.

However, it’s worth noting that while EDAs provide significant benefits, they can introduce unique complexities, such as maintaining event consistency and debugging issues in event chains across multiple microservices. Therefore, it’s important to assess your application’s needs so you can understand where it can benefit from an EDA.

Conclusion

Transitioning to intelligent apps represents a shift in strategy, architecture, and infrastructure. By designing and re-architecting for cloud-based infrastructure, your apps gain scalability, cost-effectiveness, robust data management, access to sophisticated AI tools, enhanced security, and reduced operational overhead.  

But this transition extends well beyond its technical components. Organizations and the people working within them need equal attention. You can explore these facets in greater depth in “Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management.” 

The power of generative AI is here, and preparing for the move toward cloud-based intelligent applications will ensure that your apps remain performative and scalable and—most importantly—that your business is ready for the newest era of app innovation.

Exercise

+ + \ No newline at end of file diff --git a/30daysofIA/reimagine-app-development-with-ai/index.html b/30daysofIA/reimagine-app-development-with-ai/index.html index 50665906ad..92050bc39f 100644 --- a/30daysofIA/reimagine-app-development-with-ai/index.html +++ b/30daysofIA/reimagine-app-development-with-ai/index.html @@ -14,13 +14,13 @@ - - + +
-
Skip to main content

1-3. Reimagine App Development with AI

· 9 min read
It's 30DaysOfIA

In the part of our series on intelligent apps, we’ll explore how AI is transforming application development, from design and architecture to building.

What We'll Cover:

  • Revolutionizing application development using AI
  • Infusing AI in application design architecture
  • AI Assisted Pair Programming
  • Future of App Development

A decorative image of application development with AI

Reimagining Application Development With AI: A New Paradigm

Artificial intelligence (AI) is impacting every industry. AI-powered development tools offer new functionalities and assist software developers throughout the development process, revolutionizing app development. From initial design and architecture to coding and debugging, AI reshapes how we approach application conception, creation, and iteration.

In part one of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” we explored the new breed of intelligent apps and how they use AI. In this article, we’ll see how AI technologies enhance developer toolkits and shape the future of app development, particularly through AI-enabled coding assistance and debugging.

The Dawn of Intelligent Apps: Revolutionizing Application Development

AI is more than just another addition to the development toolbox—it represents a revolution bringing unprecedented efficiency, innovation, and user engagement to our applications. 

What sets intelligent apps apart is how they can adapt and learn, helping us deliver unique, real-time user experiences.  

One example is an AI-driven health application that can track biometric data, predict potential health issues, provide personalized health advice, and even guide emergency services during a medical crisis. An AI-enabled language learning app that adapts to our learning speeds, recognizes our problem areas, and customizes its content to help us improve our language proficiencies is another example.  

Intelligent apps like these don’t just use data for recommendations—they can make critical decisions and provide sophisticated services that were previously impossible. 

For developers, intelligent apps mean we’re no longer just coding—we’re teaching applications how to think, adapt, and make decisions. AI is reshaping every step of the development journey, helping us to build more intuitive and user-focused products. 

Next, we’ll examine the impact of AI on application design and architecture and how AI-assisted “pair programming” changes software development.

info

Register for the FREE webinar on Intelligent Apps with Microsoft and Forrester on September 26 to explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries.

There will be a showcase of real-world use cases that demonstrate how AI can be seamlessly integrated into cloud-native environments driving tangible business value.

AI in Design and Architecture: Fueling Creativity and Efficiency

AI technologies help developers automate and streamline their processes and provide new ways to think about design.  

In traditional application development, the design and architecture phase can take significant time, effort, and expertise. For instance, adopting a monolithic architecture or a microservices-based design can significantly increase coding, testing, and debugging complexity. The app may even behave differently when hosted locally than in a deployed environment. It may take hours to track down an issue manually—time developers could spend elsewhere. 

Conversely, we can generate numerous design iterations in seconds with AI, each optimized to fulfill specific user needs and business goals. AI tools can quickly model various scenarios' impacts and software design patterns in the architectural design phase. It provides insights into performance and potential issues and suggests improvements. AI can also generate skeleton code, allowing developers to prototype different solutions or test specific components and technologies rapidly. By reviewing thousands of lines of code in seconds, AI allows us to focus on creating apps that serve users through feature development instead of searching for bugs.  

Integrating AI into the design and architecture phase can even extend our creative capabilities. By leveraging the power of generative AI—like large language models (LLMs)—and their broad knowledge and context going beyond software, we can better brainstorm apps and feature ideas. We can go beyond code and software architecture and get ideas about user experience, logo and branding, or marketing concepts. And as AI tools deliver insights and ideas we would have never considered; it encourages more innovation.

info

Watch Episode 01 of the Learn Live session to learn how to build an end-to-end intelligent app solution architecture.

The Power of AI-Assisted Pair Programming in Building Applications

AI-assisted pair programming is a collaborative coding approach where an AI system—like GitHub Copilot or TestPilot—assists developers during coding. It’s an increasingly common approach that significantly impacts developer productivity. In fact, GitHub Copilot is now behind an average of 46 percent of developers’ code and users are seeing 55 percent faster task completion on average. 

For new software developers, or those interested in learning new skills, AI-assisted pair programming are training wheels for coding. With the benefits of code snippet suggestions, developers can avoid struggling with beginner pitfalls like language syntax. Tools like ChatGPT can act as a personal, on-demand tutor—answering questions, generating code samples, and explaining complex code syntax and logic. These tools dramatically speed the learning process and help developers gain confidence in their coding abilities. 

Building applications with AI tools hastens development and provides more robust code. For example, GitHub Copilot helps us write new code accurately. It also quickly makes sense of existing code, suggests whole new lines or blocks of code, and even detects errors and proposes fixes. It’s like having a second coder working alongside us—one who never tires and is trained on billions of lines of code. 

However, it’s also important to acknowledge the limitations of AI. While generative AI-assistance technologies like GitHub Copilot are impressive, they’re still imperfect. They can sometimes produce erroneous, non-functional, or vulnerable code. They can also lack nuanced, domain-specific knowledge.  

So, although these tools are helpful, we can’t rely on them fully—these are programming aids rather than definitive solutions. AI-assisted pair programming is about augmenting the human developer’s skills, not replacing them.

Debugging and Improving Code With AI: Enhancing Application Quality

Identifying and correcting code errors is often time-consuming and challenging for developers. AI streamlines the debugging process by automatically detecting anomalies, suggesting fixes, and learning from previous bugs to prevent them from recurring. 

We can use generative AI to create unit tests from code snippets, from simple parameter checking and input validation to complex mocks of database and network services. Automated AI testing improves code coverage and robustness. It saves significant time compared to manually crafting unit tests. 

AI also revolutionizes code reviews by scanning and highlighting potential issues—like code patterns leading to a bug or a coding standards violation—and suggesting ways to simplify or refactor the code for readability and performance. This reduces human error and makes the code review process more efficient. Its benefits extend to small teams, where the availability of developers is crucial to deadlines, and large teams, where effective collaboration depends on readability and code consistency. AI further saves time by triaging issues so developers can prioritize the most mission-critical ones. 

Aside from improving code quality, AI is critical to understanding system behavior. Intelligent AI-assisted software development tools can monitor and audit system behavior, giving developers a better insight into how their code performs in different scenarios. AI can track patterns, identify inefficiencies, and provide alerts when they detect unusual behavior—enabling developers to enhance their code further. 

By predicting and identifying potential vulnerabilities, intelligent AI-assisted coding apps like Microsoft Security Copilot are pivotal in ensuring application functionality, reliability, and security. These AI tools are continually trained and upgraded with large codebases and user feedback that refines their responses. This ability to learn from past mistakes makes AI an incredibly powerful tool that will continue to expand its capabilities in the future.

info

Complete the AI Cloud Skills Challenge to build on you AI application development skills with paired programming using generative AI and GitHub Copilot.

AI and the Future of Application Development

The rapid advancements in AI and machine learning technologies point to an exciting future for application development. 

In “The Future of Applications,” software developer Mike Cann illustrates how LLMs might revolutionize application development. Cann that suggests fully-autonomous coding—AI-generated applications created entirely from prompts—may supersede traditional hand-crafted applications. Dynamically generated user interfaces could offer personalized and customizable experiences while we can design APIs for machine consumption and efficiency rather than human readability. Furthermore, we can store data in less structured, more adaptable, and flexible forms. 

AI is already making application development more accessible. With AI handling the complex, technical aspects of development, less-technical individuals can take a greater role in building applications. Consequently, AI can potentially democratize software engineering. 

Intelligent apps will continue to evolve, becoming more efficient, adaptable, and capable of learning from user interactions. Similarly, AI will inevitably become integral in every stage of app development.

Summary

AI has already created a new development paradigm—one in which machine intelligence and human creativity come together to produce innovative, efficient, and robust applications. AI already assists us at every stage of app development, from design and architecture to coding and debugging. 

Developers who embrace AI tools and integrate them into their processes can enhance their productivity, improve the quality of their applications, and expand the possibilities of their application development.  

The future of development is automated and AI-driven, and it’s here today. Getting started is easy—try an AI-powered development tool like GitHub Copilot to see how AI-assisted pair programming can enhance your productivity and code quality.

Exercise

- - +
Skip to main content

1-3. Reimagine App Development with AI

· 9 min read
It's 30DaysOfIA

In the part of our series on intelligent apps, we’ll explore how AI is transforming application development, from design and architecture to building.

What We'll Cover:

  • Revolutionizing application development using AI
  • Infusing AI in application design architecture
  • AI Assisted Pair Programming
  • Future of App Development

A decorative image of application development with AI

Reimagining Application Development With AI: A New Paradigm

Artificial intelligence (AI) is impacting every industry. AI-powered development tools offer new functionalities and assist software developers throughout the development process, revolutionizing app development. From initial design and architecture to coding and debugging, AI reshapes how we approach application conception, creation, and iteration.

In part one of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” we explored the new breed of intelligent apps and how they use AI. In this article, we’ll see how AI technologies enhance developer toolkits and shape the future of app development, particularly through AI-enabled coding assistance and debugging.

The Dawn of Intelligent Apps: Revolutionizing Application Development

AI is more than just another addition to the development toolbox—it represents a revolution bringing unprecedented efficiency, innovation, and user engagement to our applications. 

What sets intelligent apps apart is how they can adapt and learn, helping us deliver unique, real-time user experiences.  

One example is an AI-driven health application that can track biometric data, predict potential health issues, provide personalized health advice, and even guide emergency services during a medical crisis. An AI-enabled language learning app that adapts to our learning speeds, recognizes our problem areas, and customizes its content to help us improve our language proficiencies is another example.  

Intelligent apps like these don’t just use data for recommendations—they can make critical decisions and provide sophisticated services that were previously impossible. 

For developers, intelligent apps mean we’re no longer just coding—we’re teaching applications how to think, adapt, and make decisions. AI is reshaping every step of the development journey, helping us to build more intuitive and user-focused products. 

Next, we’ll examine the impact of AI on application design and architecture and how AI-assisted “pair programming” changes software development.

info

Register for the FREE webinar on Intelligent Apps with Microsoft and Forrester on September 26 to explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries.

There will be a showcase of real-world use cases that demonstrate how AI can be seamlessly integrated into cloud-native environments driving tangible business value.

AI in Design and Architecture: Fueling Creativity and Efficiency

AI technologies help developers automate and streamline their processes and provide new ways to think about design.  

In traditional application development, the design and architecture phase can take significant time, effort, and expertise. For instance, adopting a monolithic architecture or a microservices-based design can significantly increase coding, testing, and debugging complexity. The app may even behave differently when hosted locally than in a deployed environment. It may take hours to track down an issue manually—time developers could spend elsewhere. 

Conversely, we can generate numerous design iterations in seconds with AI, each optimized to fulfill specific user needs and business goals. AI tools can quickly model various scenarios' impacts and software design patterns in the architectural design phase. It provides insights into performance and potential issues and suggests improvements. AI can also generate skeleton code, allowing developers to prototype different solutions or test specific components and technologies rapidly. By reviewing thousands of lines of code in seconds, AI allows us to focus on creating apps that serve users through feature development instead of searching for bugs.  

Integrating AI into the design and architecture phase can even extend our creative capabilities. By leveraging the power of generative AI—like large language models (LLMs)—and their broad knowledge and context going beyond software, we can better brainstorm apps and feature ideas. We can go beyond code and software architecture and get ideas about user experience, logo and branding, or marketing concepts. And as AI tools deliver insights and ideas we would have never considered; it encourages more innovation.

info

Watch Episode 01 of the Learn Live session to learn how to build an end-to-end intelligent app solution architecture.

The Power of AI-Assisted Pair Programming in Building Applications

AI-assisted pair programming is a collaborative coding approach where an AI system—like GitHub Copilot or TestPilot—assists developers during coding. It’s an increasingly common approach that significantly impacts developer productivity. In fact, GitHub Copilot is now behind an average of 46 percent of developers’ code and users are seeing 55 percent faster task completion on average. 

For new software developers, or those interested in learning new skills, AI-assisted pair programming are training wheels for coding. With the benefits of code snippet suggestions, developers can avoid struggling with beginner pitfalls like language syntax. Tools like ChatGPT can act as a personal, on-demand tutor—answering questions, generating code samples, and explaining complex code syntax and logic. These tools dramatically speed the learning process and help developers gain confidence in their coding abilities. 

Building applications with AI tools hastens development and provides more robust code. For example, GitHub Copilot helps us write new code accurately. It also quickly makes sense of existing code, suggests whole new lines or blocks of code, and even detects errors and proposes fixes. It’s like having a second coder working alongside us—one who never tires and is trained on billions of lines of code. 

However, it’s also important to acknowledge the limitations of AI. While generative AI-assistance technologies like GitHub Copilot are impressive, they’re still imperfect. They can sometimes produce erroneous, non-functional, or vulnerable code. They can also lack nuanced, domain-specific knowledge.  

So, although these tools are helpful, we can’t rely on them fully—these are programming aids rather than definitive solutions. AI-assisted pair programming is about augmenting the human developer’s skills, not replacing them.

Debugging and Improving Code With AI: Enhancing Application Quality

Identifying and correcting code errors is often time-consuming and challenging for developers. AI streamlines the debugging process by automatically detecting anomalies, suggesting fixes, and learning from previous bugs to prevent them from recurring. 

We can use generative AI to create unit tests from code snippets, from simple parameter checking and input validation to complex mocks of database and network services. Automated AI testing improves code coverage and robustness. It saves significant time compared to manually crafting unit tests. 

AI also revolutionizes code reviews by scanning and highlighting potential issues—like code patterns leading to a bug or a coding standards violation—and suggesting ways to simplify or refactor the code for readability and performance. This reduces human error and makes the code review process more efficient. Its benefits extend to small teams, where the availability of developers is crucial to deadlines, and large teams, where effective collaboration depends on readability and code consistency. AI further saves time by triaging issues so developers can prioritize the most mission-critical ones. 

Aside from improving code quality, AI is critical to understanding system behavior. Intelligent AI-assisted software development tools can monitor and audit system behavior, giving developers a better insight into how their code performs in different scenarios. AI can track patterns, identify inefficiencies, and provide alerts when they detect unusual behavior—enabling developers to enhance their code further. 

By predicting and identifying potential vulnerabilities, intelligent AI-assisted coding apps like Microsoft Security Copilot are pivotal in ensuring application functionality, reliability, and security. These AI tools are continually trained and upgraded with large codebases and user feedback that refines their responses. This ability to learn from past mistakes makes AI an incredibly powerful tool that will continue to expand its capabilities in the future.

info

Complete the AI Cloud Skills Challenge to build on you AI application development skills with paired programming using generative AI and GitHub Copilot.

AI and the Future of Application Development

The rapid advancements in AI and machine learning technologies point to an exciting future for application development. 

In “The Future of Applications,” software developer Mike Cann illustrates how LLMs might revolutionize application development. Cann that suggests fully-autonomous coding—AI-generated applications created entirely from prompts—may supersede traditional hand-crafted applications. Dynamically generated user interfaces could offer personalized and customizable experiences while we can design APIs for machine consumption and efficiency rather than human readability. Furthermore, we can store data in less structured, more adaptable, and flexible forms. 

AI is already making application development more accessible. With AI handling the complex, technical aspects of development, less-technical individuals can take a greater role in building applications. Consequently, AI can potentially democratize software engineering. 

Intelligent apps will continue to evolve, becoming more efficient, adaptable, and capable of learning from user interactions. Similarly, AI will inevitably become integral in every stage of app development.

Summary

AI has already created a new development paradigm—one in which machine intelligence and human creativity come together to produce innovative, efficient, and robust applications. AI already assists us at every stage of app development, from design and architecture to coding and debugging. 

Developers who embrace AI tools and integrate them into their processes can enhance their productivity, improve the quality of their applications, and expand the possibilities of their application development.  

The future of development is automated and AI-driven, and it’s here today. Getting started is easy—try an AI-powered development tool like GitHub Copilot to see how AI-assisted pair programming can enhance your productivity and code quality.

Exercise

+ + \ No newline at end of file diff --git a/30daysofIA/road-to-fallforIA/index.html b/30daysofIA/road-to-fallforIA/index.html index 40b2e60102..4b16d3f086 100644 --- a/30daysofIA/road-to-fallforIA/index.html +++ b/30daysofIA/road-to-fallforIA/index.html @@ -14,13 +14,13 @@ - - + +
-
Skip to main content

Fall is Coming! 🍂

· 2 min read
It's 30DaysOfIA

September is almost here - and that can only mean one thing!! It's time to 🍂 Fall for something new and exciting and spend a few weeks skilling up on relevant tools, techologies and solutions!!

Last year, we focused on #ServerlessSeptember. This year, we're building on that theme with the addition of cloud-scale Data, cloud-native Technologies and cloud-based AI integrations to help you modernize and build intelligent apps for the enterprise!

Watch this space - and join us in September to learn more!

- - +
Skip to main content

Fall is Coming! 🍂

· 2 min read
It's 30DaysOfIA

September is almost here - and that can only mean one thing!! It's time to 🍂 Fall for something new and exciting and spend a few weeks skilling up on relevant tools, techologies and solutions!!

Last year, we focused on #ServerlessSeptember. This year, we're building on that theme with the addition of cloud-scale Data, cloud-native Technologies and cloud-based AI integrations to help you modernize and build intelligent apps for the enterprise!

Watch this space - and join us in September to learn more!

+ + \ No newline at end of file diff --git a/30daysofIA/rss.xml b/30daysofIA/rss.xml index 4c4cb9a773..0197e7c004 100644 --- a/30daysofIA/rss.xml +++ b/30daysofIA/rss.xml @@ -52,6 +52,50 @@ github-codespaces github-actions + + <![CDATA[2-1. Build Your First Intelligent App with Azure AI and AKS-1]]> + https://azure.github.io/Cloud-Native/30daysofIA/build-your-first-intelligent-app-with-azure-ai-and-aks-1 + https://azure.github.io/Cloud-Native/30daysofIA/build-your-first-intelligent-app-with-azure-ai-and-aks-1 + Fri, 22 Sep 2023 09:00:00 GMT + + Delve into creating an Intelligent App that leverages Azure AI Vision to analyze images and extract data. Develop an API to perform optical character recognition (OCR) on uploaded images and testing this API locally.

What We'll Cover:

  • Understanding Azure AI Vision and Azure Kubernetes Service
  • Build a Python Web API to perform OCR
  • Test the API locally

image depicting an Intelligent App using AI

Jumpstart Your AI Journey: Building Your First Intelligent App with Azure AI and AKS (1)

An Intelligent App is a software application that uses artificial intelligence (AI), such as machine learning (ML), to extract meaningful insights, enabling users to make informed decisions based on the extracted text, data, sound, images, and more.

Intelligent Apps can employ advanced algorithms and models to recognize objects, patterns, and features within images, allowing them to perform tasks like object identification, image classification, facial recognition, and image segmentation.

To explore the power of Intelligent Apps, let’s build a Python app that performs optical character recognition (OCR) on images containing numbers and performs aggregate functions on the extracted numbers, like sum and average. We’ll leverage Azure AI Services for the OCR functionality and Azure Kubernetes Service (AKS) to develop, publish, and maintain a cloud-based, scalable app in Azure.

Let’s get started!

Understanding Azure AI Vision and Azure Kubernetes Service

Azure AI Vision is a cloud-based service that offers a range of advanced computer vision capabilities enabling applications to interpret content, such as images and videos, using ML and other AI algorithms. Azure computer vision lets users integrate powerful vision features into their projects without ML experience.

OCR is one of the critical functionalities within Azure vision service, allowing applications to extract text from images. Within Azure AI Vision service, OCR offers several features, including text extraction, multilingual support, layout analysis of documents, and integration with Azure services, such as Azure Functions and Azure Machine Learning.

info

Watch the intelligent apps webinar on Driving Business Value by Modernizing with Cloud-Native & AI with Microsoft and Forrester on September 26.

AKS is Microsoft Azure’s container orchestration platform that enables us to quickly and efficiently deploy, manage, and scale containerized applications, such as our Intelligent App.

Let’s explore how to leverage these tools to build an Intelligent App that uses Azure AI Vision to analyze images and extract data.

Prerequisites

To follow this tutorial, ensure you have the following:

For a preview of this final project, take a look at the complete project code.

Building the API with Azure AI Vision Service

First, log in to your Azure account and navigate to the Azure Portal.

Click Create a resource and search for “resource group.” Create a new resource group named computer-vision.

image in Azure Portal of creating a new resource group

Return to the Azure Portal home page and click Create a resource.

image in Azure Portal of creating a new resource

Search for “computer vision” and select it from the results. Click Create.

image of searching for resource group in Azure Portal

Clicking Create displays the Create Computer Vision wizard. Select the resource group you just created as the Resource group from the dropdown. Enter a resource name similar to what is seen in the screenshot below. Note that this must be a unique name so choose one that is not currently in use. Select Standard S1 as the pricing tier and check the Responsible use of AI box in the field at the bottom.

Image of selecting the Responsible use of AI box

Image of project details

Click Review + Create, then click Create.

Next, get the API key and endpoint from the Azure AI services multi-service account you created. Once the resource is created, navigate to the resource details page. Under Resource Management, select Keys and Endpoint. Once on the Keys and Endpoint page, you’ll find the Key 1 and Endpoint values. These credentials are necessary to access the Azure AI APIs.

image of selecting Keys and Endpoint

second image of selecting Keys and Endpoint

Configuring the Local Environment Variables

Next, we need to define a local environment variable file. This convenient way to store our API keys in a simple text file enables us to manage sensitive information and separate configuration from our API code.

If you haven’t done so already, clone the starter project template from GitHub into your local computer. Open the starter project template in a code editor and create a new .env file in the root folder.

Note: Root folder in this case is the “/Microsoft_Series17-18_Code/intelligent-app-before" folder.

Add the following key-value pairs to the file, remembering to replace the placeholder values with those you obtained from the previous step.

VISION_KEY=<THE-KEY-1-VALUE-FROM-YOUR-AZURE-AI-SERVICE>
VISION_ENDPOINT=<THE-ENDPOINT-VALUE-FROM-YOUR-AZURE-AI-SERVICE>

Reviewing the Quickstart Code

Let’s review the app.py file in the starter project template.

The app.py file works as the entry point for the Flask application. It is responsible for initializing the web server, listening for incoming requests, dispatching them to the appropriate route handlers, and generating responses.

Below is the code contained in the app.py file:

import os
import json

from flask import Flask, request
from flask_restful import Resource, Api
from werkzeug.utils import secure_filename

app = Flask(__name__,
static_url_path='',
static_folder='static/files')

api = Api(app)

app = Flask(__name__)

app.config['UPLOAD_FOLDER'] = 'files'

api = Api(app)

class UploadHandler(Resource):

def allowed_file(self, filename):
return '.' in filename and \
filename.rsplit('.', 1)[1].lower() in {'png'}

def post(self):
form = request.form.to_dict()

if 'file' not in request.files:
return json.dumps({ "success": False, "error": "No file part"})

file = request.files.get("file")
if file and self.allowed_file(file.filename):
filename = secure_filename(file.filename)
upload_folder = "static/files"
if not os.path.exists(upload_folder):
os.makedirs(upload_folder)
local_file_path = os.path.join(upload_folder, filename)
file.save(local_file_path)

return f"File {filename} uploaded successfully to folder: {upload_folder}"

api.add_resource(UploadHandler, "/")

if __name__ == '__main__':
app.run(debug=True)

The UploadHandler class above is defined as a Flask RESTful resource to handle file uploads. The class contains two methods:

  • The allowed_file method checks whether the file extension is allowed for upload. In this case, only .png files are allowed.

  • The post method handles HTTP POST requests for file uploads. It saves the uploaded .png file to the static/files folder.

Finally, the application returns a text response informing us that the file has been uploaded successfully.

Implementing the REST API in Python

To implement image analysis in your REST API, open the starter project template in your terminal, create a virtual environment, and activate it.

Next, install the Azure AI Vision SDK:

pip install azure-ai-vision

Then, add the following line to the requirements.txt file to include the azure-ai-vision package:

azure-ai-vision==0.13.0b1

Now, create an ocr_helper.py file in the project’s root folder. This file is a Python module that provides functions for processing images using OCR. Add the following code to the file:

import os
from statistics import median
from decimal import Decimal
import azure.ai.vision as sdk

def process_ocr(source_image):
service_options = sdk.VisionServiceOptions(os.environ["VISION_ENDPOINT"],
os.environ["VISION_KEY"])

vision_source = sdk.VisionSource(filename=source_image)

analysis_options = sdk.ImageAnalysisOptions()

analysis_options.features = (
sdk.ImageAnalysisFeature.CAPTION |
sdk.ImageAnalysisFeature.TEXT
)

analysis_options.language = "en"

analysis_options.gender_neutral_caption = True

image_analyzer = sdk.ImageAnalyzer(service_options, vision_source, analysis_options)

result = image_analyzer.analyze()

ocr_result = get_ocr_result(result)

return ocr_result

def get_ocr_result(result):
string_list = []

if result.reason != sdk.ImageAnalysisResultReason.ANALYZED:
return sdk.ImageAnalysisErrorDetails.from_result(result)
else:
if result.text is not None:
for line in result.text.lines:
for word in line.words:
string_list.append(word.content)

number_list = convert_to_decimal_list(string_list)

aggregate_result = aggregate_operations(number_list)

return {
"aggregate_result": aggregate_result,
"numbers_read": string_list
}

def convert_to_decimal_list(string_list):
return list(map(Decimal, string_list))

def aggregate_operations(numbers):
result = {
'sum': sum(numbers),
'average': sum(numbers) / len(numbers),
'median': median(numbers),
'min': min(numbers),
'max': max(numbers)
`}`
`return result`

This module uses the azure-ai-vision package to analyze images, including capturing captions and extracting text from the image. The process_ocr function inputs an image file and performs the OCR analysis. The module is a convenient tool for analyzing text-based data in pictures and performing numerical computations based on that data.

Let’s review the different components of the ocr_helper.py module listed above and explore how its functions help our Intelligent App process images and perform aggregate operations:

  • The process_ocr function takes the parameter source_image, which is the path of the image to be processed. The function then initializes the VisionServiceOptions using environment variables VISION_ENDPOINT and VISION_KEY to connect to the Azure AI Vision API.
  • The process_ocr function creates a VisionSource object with the specified source_image file name. ImageAnalysisOptions specify the features to be analyzed, including CAPTION and TEXT. The language is set to English ("en"), and gender-neutral captions are enabled.
  • Finally, an ImageAnalyzer object is created with the service options, vision source, and analysis options. The image is then analyzed using the image_analyzer.analyze method to retrieve OCR results. The recognized text from the OCR result is extracted, with numerical values stored in the string_list variable.
  • The convert_to_decimal_list function converts the list of strings to a list of decimal numbers, which helps process numeric values extracted from the text.
  • The aggregate_operations function calculates various aggregate statistics (sum, average, median, minimum, maximum) from a list of numbers and returns the results in a dictionary.

Note that you must have the appropriate credentials (VISION_KEY) and endpoint (VISION_ENDPOINT) configured for the Azure AI Vision API to use this module.

Finally, we must modify the app.py file so our code can use the process_ocr function of the ocr_helper.py file.

Add the following import statement to the app.py file:

from ocr_helper import process_ocr

Then, replace this line:

return f"File {filename} uploaded successfully to folder: {upload_folder}"

With these two lines:

aggregates = process_ocr(local_file_path)
return json.dumps(aggregates, default=str)

Doing so allows our application to perform aggregate operations (sum, average, median, minimum, maximum) of any numeric values found in the text and return the operation’s result as a JSON response.

Running the Intelligent App Locally

Our final goal is to deploy the sample app to AKS, which will run the app in a container. However, as a good practice, let’s run the app in a container locally before moving it to AKS. Doing so ensures that our application and its dependencies are encapsulated and can be run consistently across different environments.

Deploying to Docker Desktop

For our application to run on Docker, it needs two additional files: a Dockerfile and docker-compose.yml. A Dockerfile creates a single container image by specifying the steps to build it, and a docker-compose.yml file manages multiple containers as a cohesive application, defining their configurations and relationships. Dockerfile builds the image, while docker-compose.yml orchestrates multi-container applications.

In the project root folder, add a file named Dockerfile with the content below:

# syntax=docker/dockerfile:1

FROM python:3.8-slim-buster

WORKDIR /intelligentapp

COPY requirements.txt requirements.txt
RUN pip3 install -r requirements.txt

RUN pip install debugpy

COPY . .

CMD [ "python3", "-m" , "flask", "run", "--host=0.0.0.0"]

Now create a file named docker-compose.yml file in the project’s root folder with the following code, replacing the VISION_KEY and the VISION_ENDPOINT according to the environment variables you configured earlier:

version: '3.8'
services:
intelligentapp:
build:
context: .
dockerfile: Dockerfile
image: intelligent-app
ports:
- 5000:5000
container_name: intelligent-app
environment:
- VISION_KEY=<THE-KEY-1-VALUE-FROM-YOUR-AZURE-AI-SERVICE>
- VISION_ENDPOINT=<THE-ENDPOINT-VALUE-FROM-YOUR-AZURE-AI-SERVICE>

Then, run the following command in the terminal to build the image and start a container for the Intelligent App services defined in the docker-compose.yml file:

docker-compose up --build --force-recreate

Next, open a new terminal and run the command below to list the image deployed to your local Docker:

docker images

docker images

Testing the Intelligent App Locally

Now, we’ll test the Intelligent App’s functionality using the following test image:

test image to test functionality

This image is provided as sample1.png in the sample app source files, so you can easily use it in the next step.

The Intelligent App will read the image above using OCR to extract numbers in the image and perform five aggregate functions:

  • Sum
  • Average
  • Median
  • Min
  • Max

To test the API, open Postman and fill out the fields as follows:

  • URL: http://localhost:5000/
  • Method: POST
  • Body:
    • Form-data
      • Key: File — Click the right end of the Key field and select File from the dropdown list.
      • Value: Click Select Files, then select the sample1.png file provided in the sample code.

image of selecting the sample file

Now click the Send button and review the result body:

"{\"aggregate_result\": {\"sum\": \"25821\", \"average\": \"5164.2\", \"median\": \"5622\", \"min\": \"1447\", \"max\": \"9802\"}, \"numbers_read\": [\"3049\", \"5901\", \"5622\", \"1447\", \"9802\"]}"

As we can see, the app running on a local container returned the correct results based on the sample image. So, we’re ready to prepare our Azure resources to run the Intelligent App on AKS.

Exercise

In this article, we explored the creation of an Intelligent App that leverages Azure AI Vision to analyze images and extract data. We learned how to build a Python Web API to perform OCR on uploaded images and subsequently test this API locally.

Head to the next part, to deploy this API via Azure Kubernetes Service.

]]>
+ Fall-For-IA + 30-days-of-IA + learn-live + hack-together + community-buzz + ask-the-expert + azure-kubernetes-service + azure-functions + azure-openai + azure-container-apps + azure-cosmos-db + github-copilot + github-codespaces + github-actions +
+ + <![CDATA[2-2. Build Your First Intelligent App with Azure AI and AKS-2]]> + https://azure.github.io/Cloud-Native/30daysofIA/build-your-first-intelligent-app-with-azure-ai-and-aks-2 + https://azure.github.io/Cloud-Native/30daysofIA/build-your-first-intelligent-app-with-azure-ai-and-aks-2 + Fri, 22 Sep 2023 09:00:00 GMT + + In the last article, we created an Intelligent App that leverages Azure AI Vision to analyze images and extract data. Develop an API to perform optical character recognition (OCR) on uploaded images and testing this API locally.

In this article, we will deploy the web API to the cloud platform using Azure Kubernetes Service.

What We'll Cover:

  • Understanding Azure AI Vision and Azure Kubernetes Service
  • Build a Python Web API to perform OCR
  • Test the API locally

image depicting an Intelligent App using AI

Jumpstart Your AI Journey: Building Your First Intelligent App with Azure AI and AKS (2)

In the previous article we explored the creation of an Intelligent App that leverages Azure AI Vision to analyze images and extract data. We learned how to build a Python Web API to perform OCR on uploaded images and subsequently test this API locally.

In this article we will use Azure Kubernetes Service (AKS) to develop, publish, and maintain our app in the cloud on Azure.

Let’s get started!

Prerequisites

To follow this tutorial, ensure you have completed Jumpstart Your AI Journey: Building Your First Intelligent App with Azure AI and AKS (1).

Pushing a Container Image to Azure Container Registry (ACR)

To start, open your CLI or terminal and type the following command:

az login

Follow the instructions displayed on your browser to enter your Azure credentials.

Once authenticated, you’ll initiate a secure connection between your local environment and Azure. This process grants you access to cloud services and resources.

Next, type the command below in your terminal to set up a new Azure Container Registry (ACR) to store your container images:

az acr create --resource-group computer-vision --name <name-of-azure-container-registry> --sku Basic

Remember to replace <name-of-azure-container-registry> with your container registry name. The name must be unique within Azure and comply with these rules.

The command above creates an Azure Container Registry (ACR) in the computer-vision resource group under the Basic SKU. This ACR is your secure and private repository for storing container images within Azure.

Next, log in to the registry with the following command:

az acr login -n <name-of-azure-container-registry>

The az acr login command above lets you securely authenticate and access the specified ACR without providing their Azure credentials each time.

Now, run the following command in your terminal. It will display the endpoint URL to log in to and interact with the ACR for pushing and pulling container images.

az acr show --name <name-of-azure-container-registry> --query loginServer --output table

This returns an endpoint URL as follows:

Result
----------------------------------
<name-of-azure-container-registry>.azurecr.io

Now, run the following command to show all container images, their repository, tags, and size:

docker images
REPOSITORYTAGIMAGE IDCREATEDSIZE
intelligent-applatesta7bf9f75361716 hours ago197MB

Tags are necessary to push Docker images to a remote registry like Azure Container Registry. They also let you differentiate between different versions of the same image and upload or download the one you want.

Run the following command to tag your Docker image:

docker tag intelligent-app <name-of-azure-container-registry>.azurecr.io/intelligent-app:v1

Then, run the docker images command again to check your tagged image:

docker images
REPOSITORYTAGIMAGE IDCREATEDSIZE
intelligent-applatestc52168039265About a minute ago197MB
<name-of-azure-container-registry>.azurecr.io/intelligent-appv1c52168039265About a minute ago197MB

Now run the following command so Docker can securely upload the image to your Azure Container Registry:

docker push <name-of-azure-container-registry>.azurecr.io/intelligent-app:v1

Once we've deployed the image to the container registry, AKS can access it during deployment.

info

Watch Episode 03 for the Learn Live session to experience a guided session on how to build, test and deploy an end-to-end intelligent app solution.

Deploying the Intelligent App on Azure Kubernetes Service (AKS)

Before we deploy our Intelligent App to AKS, we need to provision an AKS cluster and define Kubernetes manifests.

To provision an AKS cluster to host our application, we specify the desired configurations for the cluster, such as the number of nodes, node size, and networking options. But first, download and install the Kubernetes command-line tools (kubectl), a client credential plugin implementing Azure authentication:

az aks install-cli

If you’re using Linux, review this tutorial. Then run the following:

sudo az aks install-cli

Next, run the following command in your terminal to enable access to the networking-related resources and services provided by the `Microsoft.Network`` namespace in Azure:

az provider register --namespace Microsoft.Network

Now, we must create an AKS cluster. Run the following command to create an AKS cluster named aks-intelligent-app in the computer-vision resource group.

az aks create --resource-group computer-vision --name aks-intelligent-app --node-count 1 --generate-ssh-keys

The command above specifies the target resource group: computer-vision. The node pool is configured with one virtual machine (VM), and the secure shell (SSH) keys for secure node access are generated automatically.

Next, run the following command to update the AKS cluster you created by attaching it to your ACR. Doing so allows the AKS cluster to pull container images from the specified ACR when deploying workloads to the cluster.

az aks update -n aks-intelligent-app -g computer-vision --attach-acr <name-of-azure-container-registry>

Then, run the command below to configure kubectl to work with our AKS cluster in the computer-vision resource group.

az aks get-credentials --resource-group computer-vision --name aks-intelligent-app

The command above retrieves the necessary credentials and context information required for kubectl to communicate with the AKS cluster.

We still have to define the Kubernetes manifests written in YAML, describing the desired state of our application deployment, including containers, networking, and scaling rules. We’ll prepare these manifests, including Deployment and Service configurations, to define how our application should be deployed and exposed.

Create a folder named Deployment in the project root directory. Next, create two files in the deployment folder: deployment.yml and service.yml.

Add the following configuration to the deployment.yml file, replacing the <name-of-azure-container-registry> placeholder with your registry’s name:

apiVersion: apps/v1
kind: Deployment
metadata:
name: intelligent-app
spec:
replicas: 1
selector:
matchLabels:
app: intelligent-app
template:
metadata:
labels:
app: intelligent-app
spec:
nodeSelector:
kubernetes.io/os: linux
containers:
- name: intelligent-app
image: <name-of-azure-container-registry>.azurecr.io/intelligent-app:v1
resources:
limits:
memory: 512Mi
cpu: "1"
requests:
memory: 256Mi
cpu: "0.2"
ports:
- containerPort: 5000
env:
- name: FLASK_DEBUG
value: "1"
- name: VISION_KEY
value: <THE-KEY-1-VALUE-FROM-YOUR-AZURE-AI-SERVICE>
- name: VISION_ENDPOINT
value: <THE-ENDPOINT-VALUE-FROM-YOUR-AZURE-AI>

Additionally, edit the VISION_KEY and VISION_ENDPOINT environment variables above according to the API key and endpoint of your Azure AI Services instance.

Then, add the following configuration to the service.yml file:

apiVersion: v1
kind: Service
metadata:
name: intelligent-app-service
spec:
type: LoadBalancer
ports:
- protocol: TCP
port: 80
targetPort: 5000
name: port5000
selector:
app: intelligent-app

Now, we’ll deploy our application using kubectl. Applying the Kubernetes manifests creates the necessary resources and deploys our containerized application to the AKS cluster.

First, change the terminal to the deployment folder:

cd Deployment 

Then, run the following command to create or update Kubernetes resources defined in the deployment.yml file:

kubectl apply -f deployment.yml

Create a Kubernetes Service resource in a Kubernetes cluster based on the configuration defined in the service.yml file using the code below:

kubectl apply -f service.yml

Once you’ve applied the resource definition and the service configuration contained in the deployment.yml and the service.yml files, open the aks-intelligent-app Kubernetes Service in the Azure Portal, select Workloads under Kubernetes resources on the sidebar, and find the deployment named intelligent-app. It must have the status “Ready 1/1”. If you encounter an issue with this status, check out these troubleshooting resources.

image of selecting the deployment in Azure Portal

Testing the Intelligent App on AKS

To test the app on AKS, first, run the command below:

kubectl get services

This command lists the Services and their corresponding details, including the Service name, cluster IP address, external IP, and ports.

NAMETYPECLUSTER-IPEXTERNAL-IPPORT(S)AGE
intelligent-app-serviceLoadBalancer10.0.77.6020.121.76.15380:30936/TCP47s
kubernetesClusterIP10.0.0.1<none>443/TCP14m

The output above shows a Kubernetes Service named intelligent-app-service with a type set to LoadBalancer. It’s reachable from within the cluster using the cluster IP 10.0.77.60 and accessible externally via the external IP 20.121.76.153 on port 80 (mapped to port 30936).

Note: Your set of IP addresses will differ. Remember to use your unique external IP address when testing with Postman.

To test the deployed app, go to Postman, replace the URL with the external IP of the Kubernetes Service you just deployed, and click Send:

mage of sending the app in Postman

As we can see, our Intelligent App has successfully deployed to AKS and is functioning on the cloud as expected.

Exercise

Next Steps

In this two-part article, we explored the creation of an Intelligent App that leverages Azure AI Vision to analyze images and extract data. We learned how to build a Python Web API to perform OCR on uploaded images and subsequently deploy this API via Azure Kubernetes Service.

Besides OCR and Image Analysis, you can continue exploring Azure’s vast array of services and experiment further with Azure AI and AKS by applying various practical uses to your Intelligent Apps, including natural language processing, speech recognition and synthesis, sentiment analysis for customer feedback, and automated content moderation.

]]>
+ Fall-For-IA + 30-days-of-IA + learn-live + hack-together + community-buzz + ask-the-expert + azure-kubernetes-service + azure-functions + azure-openai + azure-container-apps + azure-cosmos-db + github-copilot + github-codespaces + github-actions +
<![CDATA[1-4. How Digital Natives leverage Generative AI]]> https://azure.github.io/Cloud-Native/30daysofIA/how-digital-natives-leverage-generative-ai diff --git a/30daysofIA/tags/30-days-of-ia/index.html b/30daysofIA/tags/30-days-of-ia/index.html index 8c638391de..3aabffb1ed 100644 --- a/30daysofIA/tags/30-days-of-ia/index.html +++ b/30daysofIA/tags/30-days-of-ia/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "30-days-of-IA" | Build Intelligent Apps On Azure +11 posts tagged with "30-days-of-IA" | Build Intelligent Apps On Azure @@ -14,13 +14,13 @@ - - + +
-

9 posts tagged with "30-days-of-IA"

View All Tags

· 10 min read
It's 30DaysOfIA

Prepare your organization to adopt intelligent apps by taking a change management approach. Assess your readiness, upskill teams, create a culture of change and overcome resistance, and prepare for future evolution to pave the way for a smooth transition into an AI-centric company.

What We'll Cover:

  • Role of change management in modernizing to intelligent apps
  • Skills needed for building intelligent apps in your organization
  • How to build a culture of change to embrace AI
  • How to prepare for the future of intelligent apps and continuous evolution

image of modernizing AI solutions for intelligent apps

Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management

As we’ve discussed throughout this series, intelligent apps and generative AI are rewriting the rules of business and application development. The first article of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” explored how intelligent apps can enhance user experiences and drive businesses growth. These advancements are opening new opportunities and markets for companies ready to embrace them.

However, embracing this digital transformation is more complex than flipping a switch. It requires adjustments to the technical side, which we explored in the series’ fifth article, “Preparing the Path for Intelligent Apps: Transitioning from On-Premises/IaaS to Cloud Native Applications.” It also demands a shift in how organizations adapt to change and approach app design and development. Let’s explore that mindset transition with tips to help you make it happen.

The Role of Change Management in Transitioning to Intelligent Apps

Transitioning to intelligent apps isn’t merely a technological change. It’s a profound shift in how businesses perceive and operate. This move toward artificial intelligence (AI)-centric approach isn’t meant to replace humans with machines or radically overhaul all business operations.

Instead, intelligent apps should augment human capabilities, streamline processes, and foster an environment where innovation flourishes. They expand toolkits and techniques to include AI-assisted development tools like GitHub Copilot and AI-enabled workflows, helping us design and deliver AI-enhanced experiences.

Effective change management strategies are crucial in helping employees adapt to these new ways of working with intelligent apps, ensuring that the transition doesn’t disrupt productivity or morale.

Team and organization strategies should: 

  • Assess the impact of any proposed changes.  
  • Communicate the benefits and reasons behind the change to all affected team members.  
  • Provide the necessary training and support.  
  • Integrate the changes into the culture and daily operations. 

By incorporating this robust change management approach, you can identify and mitigate potential transition risks early. Moreover, this strategy ensures that employees are engaged throughout the process, fostering a sense of involvement and ownership. This, in turn, cultivates a culture suited for innovation through intelligent apps. 

These organizational changes focus on the following aspects: organizational readiness, skillset, culture of change and overcoming resistance, and adaptability. Let’s explore them below.

info

Register for the intelligent apps webinar on Driving Business Value by Modernizing with Cloud-Native & AI with Microsoft and Forrester on September 26. 

Explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries. 

Identifying Organizational Readiness for Intelligent Apps

The first step in preparing your organization for intelligent apps is to assess your current readiness for change. Analyze various elements of your organization, from infrastructure and workforce skills to strategic vision and overall culture.

Can your apps and systems integrate with AI platforms like Microsoft Azure AI? Is your workforce comfortable working alongside AI, or are they fearful and resistant? Does your strategic vision incorporate AI’s transformative potential, or does it fail to acknowledge AI as a critical driver of future growth?

These questions identify the challenges of integrating AI into an organization and determine the company’s readiness for intelligent apps.

Embracing intelligent apps involves more than being ready with your technological infrastructure or having the right technology or tools. You need flexible organizational structures and processes driving your business to accommodate new ideas and capabilities. Your teams must feel empowered to own and drive them. Much like Microsoft’s shift to AI-powered services—like AI-powered Bing and Edge and Microsoft 365 Copilot—embracing AI organization-wide requires a holistic vision and willingness to innovate, with teams capable of delivering on the plan.

After accessing these critical areas for organizational readiness, you can start planning a strategy for this transition to an AI-ready organization. Identify skill gaps and work to bridge them while nurturing an environment that appreciates and harnesses the potential of AI and intelligent apps.

Nurturing the Indispensable Skills for Intelligent Apps

Tasking your AI specialists with preparing your organization to use and build intelligent apps isn’t enough. Accomplishing this goal requires a cross-functional effort and a diverse skill set.

Developing intelligent apps involves data scientists managing and interpreting vast amounts of data, machine learning engineers building and training the models, and traditional developers integrating these models into usable applications. However, the impact of generative AI spans beyond creating apps. AI fluency across the entire organization—from sales and marketing to accounting and legal departments—offers substantial benefits, sparking new ideas and solutions from sometimes unexpected sources.

Teams' AI skills might roughly fall into three levels: 

  • Fundamental skills — Being familiar and efficient with using AI tools, for example, knowing how to write effective prompts for large language models (LLMs).
  • Visionary skills — Recognizing AI’s strengths and weaknesses, for example, its creativity and hallucinations, that is, unexpected output.
  • Expert skills — Managing data and training models and integrating AI technology into apps, such as incorporating the OpenAI application programming interface (API) into an Intelligent App.

Based on these levels, you can gauge your team's skill readiness to pinpoint the most significant gaps, whether in general AI knowledge across your organization or within technical development teams.

As intelligent apps become increasingly prevalent, upskilling your workforce is crucial. This transition opens a new realm of skills and capabilities for your team to master. You can offer machine learning, data analysis, and cloud computing training programs—such as Microsoft’s Azure AI Fundamentals certification—to empower your team to confidently navigate the intelligent apps landscape.

Nurturing these skills within your organization may require hiring new talent, reskilling current employees, and creating an environment that promotes learning and experimentation. Specifically, if your team understands tools like the Microsoft Azure AI platform, they could contribute significantly to developing and maintaining intelligent apps.

An upskilled team is more than a functional unit. It’s a think tank to drive innovation and improve your organization’s competitive standing in the digital marketplace.

Building a Culture of Change to Embrace AI

Even with the right people and skills in place, change remains an uphill battle for organizations committed to traditional processes. 

Transitioning to intelligent apps is as much about mindset as technology and skills. It requires fostering a culture open to change and innovation that understands AI’s transformative potential and embraces it to enhance rather than replace human capabilities. 

Leaders play a pivotal role in cultivating this mindset. As they model a positive attitude toward AI, emphasize its benefits, and address any concerns transparently, leaders significantly contribute to the organization’s readiness to adopt intelligent apps. 

The most critical factor in this transition is enabling an AI-friendly culture. It’s about encouraging curiosity, rewarding innovative thinking, and creating an environment where people thrive on learning and experimentation. 

Empowering teams to leverage practical AI tools and AI-driven data insights to aid decision-making helps motivate employees to use and explore AI’s potential, improve their skills, and contribute to the successful integration of intelligent apps. Such an environment is fertile ground to grow breakthrough ideas and innovative solutions. 

Overcoming Resistance to Change and Adopting Intelligent Apps

Despite AI’s potential, change isn’t easy. It takes effort and is especially challenging for organizations used to existing processes. This resistance to change is a significant roadblock when transitioning to intelligent apps. 

Overcoming this resistance involves:  

  • Understanding employees’ concerns. 
  • Challenging underlying assumptions about traditional systems and outdated processes that hinder change. 
  • Providing comprehensive training for workers to use AI effectively.
  • Demonstrating the benefits and opportunities of intelligent apps. 
  • Clear and continuous communication to dispel fears and misconceptions about AI.

These steps will foster greater acceptance and enthusiasm for intelligent apps in your teams. 

Preparing for the Future of Intelligent Apps and Continuous Evolution

Finally, it’s essential to understand that generative AI is just beginning to gain momentum. Adjusting to a world of intelligent apps isn’t a one-time change—it’s a continuous evolution. 

As AI technologies mature and evolve and more businesses realize their benefits, we can expect the landscape of intelligent apps to expand further. Organizations can cultivate a mindset of continuous learning, flexibility, and adaptation to prepare for this future. Staying ahead in this dynamic field requires organizations to keep up with the latest AI advancements, continually reassess their strategies, and pivot when necessary. 

The domain of intelligent apps is rapidly advancing. Organizations must prepare to navigate this dynamic landscape to stay ahead of the curve.

Summary

Preparing for AI and intelligent apps is a strategic decision. It requires thoughtful preparation and a culture that embraces change.

With the proper preparation and mindset, adopting AI can become a smoother, collaborative path to innovation for the whole organization. Integrating AI into your organization does not discard what has worked in the past. It simply enhances and augments those processes for a better future.

Consider the points above to assess your organization’s readiness and prepare to welcome intelligent apps.

In the first week of this series, you learned about intelligent apps, how organizations today find success through intelligent apps, what AI-assisted application development looks like, ways to leverage the power of generative AI, the technical changes needed to prepare for intelligent apps, and, in this article, the organizational changes required to harness the power of AI.

The future is intelligent. Use this series as a guideline to navigate challenges while preparing for and embracing AI's opportunities for an intelligent tomorrow.

Exercise

- - +
Skip to main content

11 posts tagged with "30-days-of-IA"

View All Tags

· 10 min read
It's 30DaysOfIA

Prepare your organization to adopt intelligent apps by taking a change management approach. Assess your readiness, upskill teams, create a culture of change and overcome resistance, and prepare for future evolution to pave the way for a smooth transition into an AI-centric company.

What We'll Cover:

  • Role of change management in modernizing to intelligent apps
  • Skills needed for building intelligent apps in your organization
  • How to build a culture of change to embrace AI
  • How to prepare for the future of intelligent apps and continuous evolution

image of modernizing AI solutions for intelligent apps

Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management

As we’ve discussed throughout this series, intelligent apps and generative AI are rewriting the rules of business and application development. The first article of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” explored how intelligent apps can enhance user experiences and drive businesses growth. These advancements are opening new opportunities and markets for companies ready to embrace them.

However, embracing this digital transformation is more complex than flipping a switch. It requires adjustments to the technical side, which we explored in the series’ fifth article, “Preparing the Path for Intelligent Apps: Transitioning from On-Premises/IaaS to Cloud Native Applications.” It also demands a shift in how organizations adapt to change and approach app design and development. Let’s explore that mindset transition with tips to help you make it happen.

The Role of Change Management in Transitioning to Intelligent Apps

Transitioning to intelligent apps isn’t merely a technological change. It’s a profound shift in how businesses perceive and operate. This move toward artificial intelligence (AI)-centric approach isn’t meant to replace humans with machines or radically overhaul all business operations.

Instead, intelligent apps should augment human capabilities, streamline processes, and foster an environment where innovation flourishes. They expand toolkits and techniques to include AI-assisted development tools like GitHub Copilot and AI-enabled workflows, helping us design and deliver AI-enhanced experiences.

Effective change management strategies are crucial in helping employees adapt to these new ways of working with intelligent apps, ensuring that the transition doesn’t disrupt productivity or morale.

Team and organization strategies should: 

  • Assess the impact of any proposed changes.  
  • Communicate the benefits and reasons behind the change to all affected team members.  
  • Provide the necessary training and support.  
  • Integrate the changes into the culture and daily operations. 

By incorporating this robust change management approach, you can identify and mitigate potential transition risks early. Moreover, this strategy ensures that employees are engaged throughout the process, fostering a sense of involvement and ownership. This, in turn, cultivates a culture suited for innovation through intelligent apps. 

These organizational changes focus on the following aspects: organizational readiness, skillset, culture of change and overcoming resistance, and adaptability. Let’s explore them below.

info

Register for the intelligent apps webinar on Driving Business Value by Modernizing with Cloud-Native & AI with Microsoft and Forrester on September 26. 

Explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries. 

Identifying Organizational Readiness for Intelligent Apps

The first step in preparing your organization for intelligent apps is to assess your current readiness for change. Analyze various elements of your organization, from infrastructure and workforce skills to strategic vision and overall culture.

Can your apps and systems integrate with AI platforms like Microsoft Azure AI? Is your workforce comfortable working alongside AI, or are they fearful and resistant? Does your strategic vision incorporate AI’s transformative potential, or does it fail to acknowledge AI as a critical driver of future growth?

These questions identify the challenges of integrating AI into an organization and determine the company’s readiness for intelligent apps.

Embracing intelligent apps involves more than being ready with your technological infrastructure or having the right technology or tools. You need flexible organizational structures and processes driving your business to accommodate new ideas and capabilities. Your teams must feel empowered to own and drive them. Much like Microsoft’s shift to AI-powered services—like AI-powered Bing and Edge and Microsoft 365 Copilot—embracing AI organization-wide requires a holistic vision and willingness to innovate, with teams capable of delivering on the plan.

After accessing these critical areas for organizational readiness, you can start planning a strategy for this transition to an AI-ready organization. Identify skill gaps and work to bridge them while nurturing an environment that appreciates and harnesses the potential of AI and intelligent apps.

Nurturing the Indispensable Skills for Intelligent Apps

Tasking your AI specialists with preparing your organization to use and build intelligent apps isn’t enough. Accomplishing this goal requires a cross-functional effort and a diverse skill set.

Developing intelligent apps involves data scientists managing and interpreting vast amounts of data, machine learning engineers building and training the models, and traditional developers integrating these models into usable applications. However, the impact of generative AI spans beyond creating apps. AI fluency across the entire organization—from sales and marketing to accounting and legal departments—offers substantial benefits, sparking new ideas and solutions from sometimes unexpected sources.

Teams' AI skills might roughly fall into three levels: 

  • Fundamental skills — Being familiar and efficient with using AI tools, for example, knowing how to write effective prompts for large language models (LLMs).
  • Visionary skills — Recognizing AI’s strengths and weaknesses, for example, its creativity and hallucinations, that is, unexpected output.
  • Expert skills — Managing data and training models and integrating AI technology into apps, such as incorporating the OpenAI application programming interface (API) into an Intelligent App.

Based on these levels, you can gauge your team's skill readiness to pinpoint the most significant gaps, whether in general AI knowledge across your organization or within technical development teams.

As intelligent apps become increasingly prevalent, upskilling your workforce is crucial. This transition opens a new realm of skills and capabilities for your team to master. You can offer machine learning, data analysis, and cloud computing training programs—such as Microsoft’s Azure AI Fundamentals certification—to empower your team to confidently navigate the intelligent apps landscape.

Nurturing these skills within your organization may require hiring new talent, reskilling current employees, and creating an environment that promotes learning and experimentation. Specifically, if your team understands tools like the Microsoft Azure AI platform, they could contribute significantly to developing and maintaining intelligent apps.

An upskilled team is more than a functional unit. It’s a think tank to drive innovation and improve your organization’s competitive standing in the digital marketplace.

Building a Culture of Change to Embrace AI

Even with the right people and skills in place, change remains an uphill battle for organizations committed to traditional processes. 

Transitioning to intelligent apps is as much about mindset as technology and skills. It requires fostering a culture open to change and innovation that understands AI’s transformative potential and embraces it to enhance rather than replace human capabilities. 

Leaders play a pivotal role in cultivating this mindset. As they model a positive attitude toward AI, emphasize its benefits, and address any concerns transparently, leaders significantly contribute to the organization’s readiness to adopt intelligent apps. 

The most critical factor in this transition is enabling an AI-friendly culture. It’s about encouraging curiosity, rewarding innovative thinking, and creating an environment where people thrive on learning and experimentation. 

Empowering teams to leverage practical AI tools and AI-driven data insights to aid decision-making helps motivate employees to use and explore AI’s potential, improve their skills, and contribute to the successful integration of intelligent apps. Such an environment is fertile ground to grow breakthrough ideas and innovative solutions. 

Overcoming Resistance to Change and Adopting Intelligent Apps

Despite AI’s potential, change isn’t easy. It takes effort and is especially challenging for organizations used to existing processes. This resistance to change is a significant roadblock when transitioning to intelligent apps. 

Overcoming this resistance involves:  

  • Understanding employees’ concerns. 
  • Challenging underlying assumptions about traditional systems and outdated processes that hinder change. 
  • Providing comprehensive training for workers to use AI effectively.
  • Demonstrating the benefits and opportunities of intelligent apps. 
  • Clear and continuous communication to dispel fears and misconceptions about AI.

These steps will foster greater acceptance and enthusiasm for intelligent apps in your teams. 

Preparing for the Future of Intelligent Apps and Continuous Evolution

Finally, it’s essential to understand that generative AI is just beginning to gain momentum. Adjusting to a world of intelligent apps isn’t a one-time change—it’s a continuous evolution. 

As AI technologies mature and evolve and more businesses realize their benefits, we can expect the landscape of intelligent apps to expand further. Organizations can cultivate a mindset of continuous learning, flexibility, and adaptation to prepare for this future. Staying ahead in this dynamic field requires organizations to keep up with the latest AI advancements, continually reassess their strategies, and pivot when necessary. 

The domain of intelligent apps is rapidly advancing. Organizations must prepare to navigate this dynamic landscape to stay ahead of the curve.

Summary

Preparing for AI and intelligent apps is a strategic decision. It requires thoughtful preparation and a culture that embraces change.

With the proper preparation and mindset, adopting AI can become a smoother, collaborative path to innovation for the whole organization. Integrating AI into your organization does not discard what has worked in the past. It simply enhances and augments those processes for a better future.

Consider the points above to assess your organization’s readiness and prepare to welcome intelligent apps.

In the first week of this series, you learned about intelligent apps, how organizations today find success through intelligent apps, what AI-assisted application development looks like, ways to leverage the power of generative AI, the technical changes needed to prepare for intelligent apps, and, in this article, the organizational changes required to harness the power of AI.

The future is intelligent. Use this series as a guideline to navigate challenges while preparing for and embracing AI's opportunities for an intelligent tomorrow.

Exercise

+ + \ No newline at end of file diff --git a/30daysofIA/tags/30-days-of-ia/page/10/index.html b/30daysofIA/tags/30-days-of-ia/page/10/index.html new file mode 100644 index 0000000000..4a18a617da --- /dev/null +++ b/30daysofIA/tags/30-days-of-ia/page/10/index.html @@ -0,0 +1,27 @@ + + + + + +11 posts tagged with "30-days-of-IA" | Build Intelligent Apps On Azure + + + + + + + + + + + + + + +
+

11 posts tagged with "30-days-of-IA"

View All Tags

· 4 min read
It's 30DaysOfIA

Continue The Learning Journey through Fall For Intelligent Apps! 🍂

What We'll Cover

Thank you! ♥️

image

It's hard to believe that JavaScript on Azure hack-together is ending! It seems like just yesterday that we launched this initiative, and yet here we are, 15 days later, with an incredible amount of learning and growth behind us. So... it's time for a wrap!

From the bottom of our hearts, we want to thank each and every one of you for your participation, engagement, and enthusiasm. It's been truly inspiring to see the passion and dedication from this strong community, and we're honored to be a part of it. ✨

Recap of The JavaScript on Azure Global Hack-Together

As we wrap up this exciting event, we wanted to take a moment to reflect on all that we've accomplished together. Over the last 15 days, we've covered a lot of ground, from the basics of contributing to Open source to the exploration of the Contoso Real Estate project from its Frontend to its Backend and future AI implementation.

Now that the hack-together is ending, we want to make sure that you have all the resources you need to continue honing your skills in the future. Whether you're looking to make your fist contribution to open source, become an open source maintainers, collaborate with others, or simply keep learning, there are plenty of resources out there to help you achieve your goals. So, let's dive in and explore all the ways you can continue to grow your JavaScript skills on Azure!

JSonAzure Hack-together Roadmap 📍:

hack-together-roadmap (2)

Recap on past Livestreams🌟:

Day 1️⃣: Opening Keynote (Hack-together Launch): Introduction to the Contoso Real Estate Open-source project!, managing complex and complex enterprise architecture, new announcements for JavaScript developers on Azure

Day 2️⃣: GitHub Copilot & Codespaces: Introduction to your AI pair programmer (GitHub Copilot) and your virtual developer environment on the cloud (GitHub Codespaces)

Day 6️⃣: Build your Frontend using Static Web Apps as part of a complex, modern composable frontends (or micro-frontends) and cloud-native applications.

Day 9️⃣: Build a Serverless Backend using Azure Functions

Day 1️⃣3️⃣: Easily connect to an Azure Cosmos DB, exploring its benefits and how to get started

Day 1️⃣5️⃣: Being in the AI Era, we dive into the Azure OpenAI Service and how you can start to build intelligent JavaScript applications

📖 Self-Learning Resources

  1. JavaScript on Azure Global Hack Together Module collection
  2. Lets #HackTogether: Javascript On Azure Keynote
  3. Step by Step Guide: Migrating v3 to v4 programming model for Azure Functions for Node.Js Application

Continue your journey with #FallForIntelligentApps

Join us this Fall on a learning journey to explore building intelligent apps. Combine the power of AI, cloud-native app development, and cloud-scale data to build highly differentiated digital experiences. Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure. Engage in a self-paced learning adventure by following along the #30Days of Intelligent Apps series, completing the Intelligent Apps Skills Challenge, joining the Product Group for a live Ask The Expert series or building an end to end solution architecture with a live guided experience through the Learn Live series. Discover more here.

Hands-on practice: Make your first contribution to open source!

Join our GitHUb Discussion Forum to connect with developers from every part of world, see contributions from other, find collaborators and make your first contribution to a real-world project! +Don't forget to give the repo a star ⭐

Resources

All resources are accessible on our landing page

+ + + + \ No newline at end of file diff --git a/30daysofIA/tags/30-days-of-ia/page/11/index.html b/30daysofIA/tags/30-days-of-ia/page/11/index.html new file mode 100644 index 0000000000..fcd78850fe --- /dev/null +++ b/30daysofIA/tags/30-days-of-ia/page/11/index.html @@ -0,0 +1,26 @@ + + + + + +11 posts tagged with "30-days-of-IA" | Build Intelligent Apps On Azure + + + + + + + + + + + + + + +
+

11 posts tagged with "30-days-of-IA"

View All Tags

· 2 min read
It's 30DaysOfIA

September is almost here - and that can only mean one thing!! It's time to 🍂 Fall for something new and exciting and spend a few weeks skilling up on relevant tools, techologies and solutions!!

Last year, we focused on #ServerlessSeptember. This year, we're building on that theme with the addition of cloud-scale Data, cloud-native Technologies and cloud-based AI integrations to help you modernize and build intelligent apps for the enterprise!

Watch this space - and join us in September to learn more!

+ + + + \ No newline at end of file diff --git a/30daysofIA/tags/30-days-of-ia/page/2/index.html b/30daysofIA/tags/30-days-of-ia/page/2/index.html index 7c66dca634..c8a978865d 100644 --- a/30daysofIA/tags/30-days-of-ia/page/2/index.html +++ b/30daysofIA/tags/30-days-of-ia/page/2/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "30-days-of-IA" | Build Intelligent Apps On Azure +11 posts tagged with "30-days-of-IA" | Build Intelligent Apps On Azure @@ -14,13 +14,13 @@ - - + +
-

9 posts tagged with "30-days-of-IA"

View All Tags

· 9 min read
It's 30DaysOfIA

Learn best practices for modernizing your on-premises or IaaS solutions to intelligent apps in the cloud.

What We'll Cover:

  • Traditional vs Intelligent Apps
  • On-Premises/IaaS vs. Cloud-Native Platforms
  • Modernization strategy for building Intelligent Apps

image of modernizing AI solutions for intelligent apps

Preparing the Path for Intelligent Apps: Transitioning from On-Premises/IaaS to Cloud-Native Applications

The proliferation of generative AI is paving the way for intelligent applications, software that leverages AI capabilities to deliver unparalleled functionality and user experience. As we covered in a previous article, “Demystifying Intelligent Applications: Leveraging AI in App Development,” intelligent Apps aren’t just products of advanced coding. They interact, learn, and evolve thanks to modern AI and machine learning (ML) breakthroughs. 

The paradigm shift from traditional to intelligent apps demands that we change our approach to the technical challenges of software and architecture design. It also necessitates adopting new ways of thinking and operating within an organization, which we explore in greater depth in “Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management.” 

This article explores the technical infrastructure powering intelligent apps, providing a roadmap to transition traditional on-premises or Infrastructure as a Service (IaaS) solutions to intelligent apps deployed to cloud native platforms and services. We’ll discover the differences between conventional and intelligent apps, options for re-architecting existing applications, and critical strategic considerations integral to modernizing the application-building approach. 

Understanding the Shift: Traditional vs. Intelligent Applications

Intelligent apps are more than just an incremental evolution from traditional apps. They involve a fundamental shift in software solution design, development, and deployment. A clear understanding of how each paradigm differs is crucial for comprehending the impact of this transition. 

Functionality and User Experience

Traditional applications, often rule-based and rigid, rely on preprogrammed operations. A traditional weather app, for example, typically presents structured, location-based forecasts using a predetermined data source. Meanwhile, an intelligent app leverages AI for data-driven decision-making and personalization. Rather than merely displaying the forecast, it integrates ML with additional data sources—like a user’s calendar and fitness data—to determine their favorite outdoor activities, provide relevant clothing suggestions, and even automatically design ideal vacations and times based on the user’s optimal weather preferences. 

Similarly, while traditional applications tend to offer a one-size-fits-all UI, intelligent apps use AI to create tailored interactions. For instance, an intelligent banking app might use generative AI to provide a voice-enabled UI that lets customers use natural language to ask about transactions and receive personalized financial advice.

Infrastructure

Intelligent apps integrating these sophisticated features require highly scalable, flexible infrastructure optimized for AI functionality and able to manage large volumes of real-time data. Unlike traditional applications deployed on-premises or in infrastructure as a service (IaaS) environments, intelligent apps demand the flexibility and scalability of cloud native architectures.

An on-premises or IaaS-based e-commerce application may struggle with connecting its data to AI services or scaling to accommodate voluminous real-time data and requests. But an intelligent app built with a cloud native approach and scalability at its core can handle variable demand much more efficiently. It also maintains its infrastructure components on demand, whether for collecting data for AI/ML consumption or user-facing features like AI-enabled customer support and personalization.

Consider an intelligent app built on Azure that creates ChatGPT-like experiences with custom data. It might connect Azure Open AI and Azure Cognitive Search to an app running on Azure Container Apps or Azure Kubernetes Service. 

The architecture might look like the following diagram based on this demo app:

diagram of a demo app architecture

While the cloud is well-equipped to handle the challenges associated with shifting to AI-centric infrastructure—handling real-time data processing and storage, integrating and managing AI services, configuring event-driven and microservices architectures, and ensuring security across the distributed environment—re-architecting a traditional app to an intelligent app requires careful planning and execution to ensure it benefits fully from the capabilities of AI and cloud computing.

On-Premises/IaaS vs. Cloud-Native Platforms

While on-premises and IaaS solutions provide substantial control over data and processes, this fine-grained control comes with setup and maintenance costs and may lack the scalability necessary for AI-driven Intelligent Apps. On-premises hosting involves purchasing and maintaining hardware, including servers and storage systems. IaaS involves leasing virtual hardware and then hiring and training IT personnel for the infrastructure’s operation, maintenance, and security.

Conversely, infrastructure built on cloud native app services with Microsoft Azure offers virtually unlimited scalability without ongoing hardware and maintenance costs. This feature is ideal for intelligent apps that need to adapt and scale to accommodate fluctuating workloads and data streams. And because these cloud infrastructures typically operate on a pay-as-you-go model, traditionally substantial upfront costs turn into predictable operating expenses, lowering both the cost and risk of launching applications.

Moreover, cloud service providers continually update their offerings, so building on sophisticated AI tools and services through the cloud, like the Azure AI Platform and orchestration capabilities with Azure Kubernetes Service, ensures that you have access to the latest AI and most advanced AI models and technologies.

Strategic Considerations for Transitioning to Intelligent Apps

When developing a strategy for transitioning to intelligent apps, you must consider costs, personnel skill development, and data management.

Monolithic architectures are unlikely to support the scale and ease of iteration required by intelligent apps, so you’ll likely need to implement code changes to support the transition. Your chosen hosting solution and the amount of data your app will handle affect cost and performance, in addition to determining privacy and compliance requirements.

An app that manages or stores terabytes or petabytes of data will require storage-optimized virtual machines or a managed database service hosting. Meanwhile, an app that processes video content or performs resource-intensive calculations should prioritize high-performance computing. Maximizing storage and performance can become extraordinarily costly.

Finally, existing applications shifting towards intelligent apps may encounter compatibility conflicts, depending on database type or the existing system’s underlying programming language. As you might imagine, a traditional app running on-premises and using a shared local database may need significant restructuring to support a cloud native architecture. You need plans for data migration, potential downtime, and training.

Next, let’s look at this transformation process.

Re-Architecting Monolithic Apps into Microservices

Shifting from monolithic applications to microservices is pivotal for transitioning to Intelligent Apps, and you can use this shift as an opportunity to transform your legacy software into intelligent apps that leverage the flexibility and power of microservices-enabled AI solutions.

Traditionally, a monolithic application (let’s use an online marketplace as an example) might handle all activities—from serving webpages to processing payments—within an on-premises server or a cloud server. However, this approach is challenging to scale and remains vulnerable to network failures or cyberattacks.

Breaking these applications into multiple services with specific responsibilities, such as authentication or notifications, can overcome these limitations. These services are typically deployed as microservices within container environments like Azure Kubernetes Service or serverless platforms like Azure Container Apps or Azure Functions using automated processes. This approach offers scalability, robustness, and improved performance, as each microservice can run independently and use its own database.

Shifting towards smaller, independent microservices in app architectures allows us to reap the benefits of AI-powered intelligent apps, for example, by using microservices to handle AI tasks such as processing natural language input, analyzing behavioral data, or personalizing content.

info

Watch Episode 1 and Episode 2 of the Learn Live series on how to build, test and deploy an end-to-end intelligent app solution using the Contoso Real Estate Sample.

Event-Driven Architectures: Knowing When to Use Them

Event-Driven Architectures (EDAs) can significantly enhance the performance and cost-effectiveness of intelligent apps. EDAs, typically implemented with microservices, respond in real time to events or state changes ranging from user interactions to real-time analytics.

Consider an intelligent app designed for personalized customer engagement. Instead of designing the application to check continuously for changes in user data, an EDA can react to such changes immediately, triggering appropriate microservices. For example, if users modify their preferences, an event could trigger an AI-driven recommendation microservice to update its suggestions immediately. This real-time responsiveness, enabled by services like Azure Event Grid, can improve user experiences and make intelligent apps more adaptive and proactive.

However, it’s worth noting that while EDAs provide significant benefits, they can introduce unique complexities, such as maintaining event consistency and debugging issues in event chains across multiple microservices. Therefore, it’s important to assess your application’s needs so you can understand where it can benefit from an EDA.

Conclusion

Transitioning to intelligent apps represents a shift in strategy, architecture, and infrastructure. By designing and re-architecting for cloud-based infrastructure, your apps gain scalability, cost-effectiveness, robust data management, access to sophisticated AI tools, enhanced security, and reduced operational overhead.  

But this transition extends well beyond its technical components. Organizations and the people working within them need equal attention. You can explore these facets in greater depth in “Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management.” 

The power of generative AI is here, and preparing for the move toward cloud-based intelligent applications will ensure that your apps remain performative and scalable and—most importantly—that your business is ready for the newest era of app innovation.

Exercise

- - +
Skip to main content

11 posts tagged with "30-days-of-IA"

View All Tags

· 9 min read
It's 30DaysOfIA

Learn best practices for modernizing your on-premises or IaaS solutions to intelligent apps in the cloud.

What We'll Cover:

  • Traditional vs Intelligent Apps
  • On-Premises/IaaS vs. Cloud-Native Platforms
  • Modernization strategy for building Intelligent Apps

image of modernizing AI solutions for intelligent apps

Preparing the Path for Intelligent Apps: Transitioning from On-Premises/IaaS to Cloud-Native Applications

The proliferation of generative AI is paving the way for intelligent applications, software that leverages AI capabilities to deliver unparalleled functionality and user experience. As we covered in a previous article, “Demystifying Intelligent Applications: Leveraging AI in App Development,” intelligent Apps aren’t just products of advanced coding. They interact, learn, and evolve thanks to modern AI and machine learning (ML) breakthroughs. 

The paradigm shift from traditional to intelligent apps demands that we change our approach to the technical challenges of software and architecture design. It also necessitates adopting new ways of thinking and operating within an organization, which we explore in greater depth in “Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management.” 

This article explores the technical infrastructure powering intelligent apps, providing a roadmap to transition traditional on-premises or Infrastructure as a Service (IaaS) solutions to intelligent apps deployed to cloud native platforms and services. We’ll discover the differences between conventional and intelligent apps, options for re-architecting existing applications, and critical strategic considerations integral to modernizing the application-building approach. 

Understanding the Shift: Traditional vs. Intelligent Applications

Intelligent apps are more than just an incremental evolution from traditional apps. They involve a fundamental shift in software solution design, development, and deployment. A clear understanding of how each paradigm differs is crucial for comprehending the impact of this transition. 

Functionality and User Experience

Traditional applications, often rule-based and rigid, rely on preprogrammed operations. A traditional weather app, for example, typically presents structured, location-based forecasts using a predetermined data source. Meanwhile, an intelligent app leverages AI for data-driven decision-making and personalization. Rather than merely displaying the forecast, it integrates ML with additional data sources—like a user’s calendar and fitness data—to determine their favorite outdoor activities, provide relevant clothing suggestions, and even automatically design ideal vacations and times based on the user’s optimal weather preferences. 

Similarly, while traditional applications tend to offer a one-size-fits-all UI, intelligent apps use AI to create tailored interactions. For instance, an intelligent banking app might use generative AI to provide a voice-enabled UI that lets customers use natural language to ask about transactions and receive personalized financial advice.

Infrastructure

Intelligent apps integrating these sophisticated features require highly scalable, flexible infrastructure optimized for AI functionality and able to manage large volumes of real-time data. Unlike traditional applications deployed on-premises or in infrastructure as a service (IaaS) environments, intelligent apps demand the flexibility and scalability of cloud native architectures.

An on-premises or IaaS-based e-commerce application may struggle with connecting its data to AI services or scaling to accommodate voluminous real-time data and requests. But an intelligent app built with a cloud native approach and scalability at its core can handle variable demand much more efficiently. It also maintains its infrastructure components on demand, whether for collecting data for AI/ML consumption or user-facing features like AI-enabled customer support and personalization.

Consider an intelligent app built on Azure that creates ChatGPT-like experiences with custom data. It might connect Azure Open AI and Azure Cognitive Search to an app running on Azure Container Apps or Azure Kubernetes Service. 

The architecture might look like the following diagram based on this demo app:

diagram of a demo app architecture

While the cloud is well-equipped to handle the challenges associated with shifting to AI-centric infrastructure—handling real-time data processing and storage, integrating and managing AI services, configuring event-driven and microservices architectures, and ensuring security across the distributed environment—re-architecting a traditional app to an intelligent app requires careful planning and execution to ensure it benefits fully from the capabilities of AI and cloud computing.

On-Premises/IaaS vs. Cloud-Native Platforms

While on-premises and IaaS solutions provide substantial control over data and processes, this fine-grained control comes with setup and maintenance costs and may lack the scalability necessary for AI-driven Intelligent Apps. On-premises hosting involves purchasing and maintaining hardware, including servers and storage systems. IaaS involves leasing virtual hardware and then hiring and training IT personnel for the infrastructure’s operation, maintenance, and security.

Conversely, infrastructure built on cloud native app services with Microsoft Azure offers virtually unlimited scalability without ongoing hardware and maintenance costs. This feature is ideal for intelligent apps that need to adapt and scale to accommodate fluctuating workloads and data streams. And because these cloud infrastructures typically operate on a pay-as-you-go model, traditionally substantial upfront costs turn into predictable operating expenses, lowering both the cost and risk of launching applications.

Moreover, cloud service providers continually update their offerings, so building on sophisticated AI tools and services through the cloud, like the Azure AI Platform and orchestration capabilities with Azure Kubernetes Service, ensures that you have access to the latest AI and most advanced AI models and technologies.

Strategic Considerations for Transitioning to Intelligent Apps

When developing a strategy for transitioning to intelligent apps, you must consider costs, personnel skill development, and data management.

Monolithic architectures are unlikely to support the scale and ease of iteration required by intelligent apps, so you’ll likely need to implement code changes to support the transition. Your chosen hosting solution and the amount of data your app will handle affect cost and performance, in addition to determining privacy and compliance requirements.

An app that manages or stores terabytes or petabytes of data will require storage-optimized virtual machines or a managed database service hosting. Meanwhile, an app that processes video content or performs resource-intensive calculations should prioritize high-performance computing. Maximizing storage and performance can become extraordinarily costly.

Finally, existing applications shifting towards intelligent apps may encounter compatibility conflicts, depending on database type or the existing system’s underlying programming language. As you might imagine, a traditional app running on-premises and using a shared local database may need significant restructuring to support a cloud native architecture. You need plans for data migration, potential downtime, and training.

Next, let’s look at this transformation process.

Re-Architecting Monolithic Apps into Microservices

Shifting from monolithic applications to microservices is pivotal for transitioning to Intelligent Apps, and you can use this shift as an opportunity to transform your legacy software into intelligent apps that leverage the flexibility and power of microservices-enabled AI solutions.

Traditionally, a monolithic application (let’s use an online marketplace as an example) might handle all activities—from serving webpages to processing payments—within an on-premises server or a cloud server. However, this approach is challenging to scale and remains vulnerable to network failures or cyberattacks.

Breaking these applications into multiple services with specific responsibilities, such as authentication or notifications, can overcome these limitations. These services are typically deployed as microservices within container environments like Azure Kubernetes Service or serverless platforms like Azure Container Apps or Azure Functions using automated processes. This approach offers scalability, robustness, and improved performance, as each microservice can run independently and use its own database.

Shifting towards smaller, independent microservices in app architectures allows us to reap the benefits of AI-powered intelligent apps, for example, by using microservices to handle AI tasks such as processing natural language input, analyzing behavioral data, or personalizing content.

info

Watch Episode 1 and Episode 2 of the Learn Live series on how to build, test and deploy an end-to-end intelligent app solution using the Contoso Real Estate Sample.

Event-Driven Architectures: Knowing When to Use Them

Event-Driven Architectures (EDAs) can significantly enhance the performance and cost-effectiveness of intelligent apps. EDAs, typically implemented with microservices, respond in real time to events or state changes ranging from user interactions to real-time analytics.

Consider an intelligent app designed for personalized customer engagement. Instead of designing the application to check continuously for changes in user data, an EDA can react to such changes immediately, triggering appropriate microservices. For example, if users modify their preferences, an event could trigger an AI-driven recommendation microservice to update its suggestions immediately. This real-time responsiveness, enabled by services like Azure Event Grid, can improve user experiences and make intelligent apps more adaptive and proactive.

However, it’s worth noting that while EDAs provide significant benefits, they can introduce unique complexities, such as maintaining event consistency and debugging issues in event chains across multiple microservices. Therefore, it’s important to assess your application’s needs so you can understand where it can benefit from an EDA.

Conclusion

Transitioning to intelligent apps represents a shift in strategy, architecture, and infrastructure. By designing and re-architecting for cloud-based infrastructure, your apps gain scalability, cost-effectiveness, robust data management, access to sophisticated AI tools, enhanced security, and reduced operational overhead.  

But this transition extends well beyond its technical components. Organizations and the people working within them need equal attention. You can explore these facets in greater depth in “Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management.” 

The power of generative AI is here, and preparing for the move toward cloud-based intelligent applications will ensure that your apps remain performative and scalable and—most importantly—that your business is ready for the newest era of app innovation.

Exercise

+ + \ No newline at end of file diff --git a/30daysofIA/tags/30-days-of-ia/page/3/index.html b/30daysofIA/tags/30-days-of-ia/page/3/index.html index ab75b8a929..993880014c 100644 --- a/30daysofIA/tags/30-days-of-ia/page/3/index.html +++ b/30daysofIA/tags/30-days-of-ia/page/3/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "30-days-of-IA" | Build Intelligent Apps On Azure +11 posts tagged with "30-days-of-IA" | Build Intelligent Apps On Azure @@ -14,13 +14,13 @@ - - + +
-

9 posts tagged with "30-days-of-IA"

View All Tags

· 10 min read
It's 30DaysOfIA

Explore how digital natives and Independent Software Vendors (ISVs) can transform their business operations and generate higher value for customers with generative AI and intelligent apps.

What We'll Cover:

  • Potential of Generative AI in application development
  • Challenges with Generative AI
  • Integrating Generative AI with business operations

image of generative AI and intelligent apps used in business

Unleashing Business Transformation: How Digital Natives Can Leverage Generative AI

Generative AI is no longer a technological flight of fancy. It’s here, and it’s rewriting the rules of business innovation. 

This type of AI leverages machine learning algorithms to create new data with similar characteristics to a given dataset. It goes beyond text generation using generative pretrained transformer (GPT) models, covering an array of applications that span from image synthesis to translations and more. It also powers a new class of intelligent applications, which combine AI capabilities with traditional software functions for enhanced user experiences and advanced business solutions. 

Generative AI is transforming how digital natives and independent software vendors (ISVs) operate. Generative AI offers new possibilities for creating unique business value, enhancing customer experiences, and driving growth. 

It plays a critical role in applications, improving functionality, aiding decision-making, and automating complex tasks to deliver dynamic, uniquely personalized user experiences. Generative AI also enables and automates data-driven outcomes, functions, and features.

Integrating AI capabilities into customer-facing apps and turning them into intelligent apps benefits both the customer and the business, as we discussed in Demystifying Intelligent Applications: Leveraging AI in App Development. You get faster product development workflows and better business intelligence for sales and marketing, while your users get more natural and context-aware features and interactions. 

That said, to realize generative AI’s potential, you need some strategy and knowledge of how and where it fits into your current business and operations. We’ll examine this technology’s potential to transform business operations, its risks, and rewards, and how to integrate it into your existing operations. 

Unpacking the Potential of Generative AI

Generative AI is creating new opportunities in the digital playing field. It empowers businesses to generate insights, design products, and automate processes in ways and at speeds that were previously unimaginable. You can now predict customer behavior more accurately, offer highly personalized user experiences, and prototype innovative ideas rapidly. 

For example, Microsoft used generative AI to reinvent web search in the new AI-powered Bing and Edge. The search experience has evolved from a solitary user flow of typing in keywords and scrolling through results. It’s now a conversational experience where Bing uses context to offer better quality and more personalized search queries and results. 

Similarly, Microsoft harnessed generative AI to transform its productivity suite into intelligent apps. Through Copilot for Microsoft 365, PowerPoint now empowers users to tell their stories by using DALL-E to generate images effortlessly. It also helps rephrase text-heavy slides to be more straightforward. Outlook now assists users to compose more effective emails, enabling better communication.

These are just some examples of how AI powers day-to-day work and operations. 

Businesses that ignore generative AI miss out on untapped market opportunities, letting competitors gain an edge in the digital race. By seeking ways to rethink assumptions and reinvent existing approaches, you can effectively use generative AI to create new opportunities, open products to new use cases and markets, unlock new growth and customer satisfaction levels, and increase your revenue.

info

Register for the intelligent apps webinar on Driving Business Value by Modernizing with Cloud-Native & AI with Microsoft and Forrester on September 26.

Explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries. 

The Crucial Role of Generative AI in Business Operations

Generative AI can be a powerful catalyst for business transformation. Let’s quickly tour some ways it can redefine business operations. 

Product development is traditionally a long and painstaking process. Now, tools like GitHub Copilot and TestPilot use generative AI to make coding and testing software easier, faster, and more robust. 

Similarly, machine learning as a service technology, such as Azure Machine Learning, democratizes the availability of machine learning. It helps collect, clean, and train models across datasets, making engineering and analyzing data more efficient and accessible.  

Generative AI also simplifies the process of creating intelligent apps. Previously, simulating thousands of prototypes, predicting their performance, and narrowing down to the most viable options could take weeks, months, or even years of iteration and data analysis. With generative AI, this process can be as quick as days, hours, or perhaps minutes. 

Customer experience is also ripe for transformation. Personalized recommendations drive engagement and sales, and generative AI can scale that personalization to every customer interaction using comprehensive and continually updated data. For instance, Microsoft leverages AI to enable personalization in Dynamics 365 Customer Insights. Using this tool, you can ask questions about your customers in natural language and gain valuable and actionable insights into your customer base. 

Lastly, marketing and sales can benefit from generative AI. For example, identifying current and future key markets and emerging trends helps companies stay ahead of the curve. Tools like Copilot in Power BI help you quickly understand and uncover insights about your business, enabling your team to craft targeted campaigns, create engaging content, and optimize your business strategy. 

Leveraging this full generative AI potential requires the right tools. GPT-4, for instance, can be instrumental in realizing effective business transformation. It excels in understanding context, generating human-like text, and learning from minimal data input, making it a formidable tool in any digital native's arsenal. Platforms like Azure OpenAI Service bring GPT-4’s power to your fingertips.

However, as with any powerful technology, it’s unwise to treat generative AI as a perfect solution. You must know the challenges and risks of not leveraging it effectively, including technical and human-factor considerations. 

Without generative AI, your company risks missing the opportunity to please customers via personalization, as discussed previously. You’ll miss sales opportunities via business intelligence insights, limiting your ability to grow revenue and keep up with the competition. 

One challenge of leveraging generative AI’s full potential is finding the right tools. Fortunately, you can implement generative AI in your organization without investing in infrastructure, computational power, and expertise. Platforms like Azure OpenAI Service and Azure AI Services make AI easily accessible. They also work with Azure compute and data services like Azure Functions and Azure Kubernetes Service (AKS), enabling you to build intelligent apps easily and quickly. 

However, generative AI can’t do everything. It may need human expertise and intervention to use it fully. 

For example, generative AI can create unique data. However, the data may have limited diversity and complexity. The AI may need help recognizing additional considerations or requirements specific to a problem. It may also lack context due to various factors, such as the information’s freshness. Also, since it’s an emerging field that’s potentially worth $4.4 trillion, we’re still learning about the nuances and potential regulatory impact of generative AI. 

Perhaps the most crucial and challenging aspect of adopting generative AI is an organization’s required cultural shift. Your teams must learn how to use AI as your company fosters an understanding of AI’s potential and limitations. 

Begin integrating AI literacy into your company’s DNA. Knowledge of its potential and the readily available tools can inspire your teams to incorporate generative AI into their daily workflows and business operations quickly, improve productivity, and deliver superior customer experiences. 

No company wants to be outpaced by its competitors. You can balance generative AI’s potential with your business operations by staying aware of and adapting to the dynamic AI landscape.

Achieving Harmony: Integrating Generative AI with Existing Business Operations

Integrating generative AI into existing business operations doesn’t have to be disruptive. With the correct strategy, you can do so seamlessly. Your goal should be to augment and enhance your operations, not disrupt and replace. 

For example, Microsoft Cloud technologies leveraged AI to relieve healthcare organizations’ challenges, such as talent shortage and workforce burnout. Providers automated their workflows, invested in specific applications and connectors, and enhanced support for data and operations. 

Azure AI Services, such as text analytics for health, enable healthcare organizations to understand their largely unstructured and untapped medical data. Project Health Insights leans on AI to help healthcare professionals gain actionable insights and inferences from patient data. 

Azure Health Bot, an Intelligent App, empowers organizations to build and deploy legally compliant, powerful conversational healthcare experiences to patients. 

A successful AI integration like this begins with understanding. Knowing what generative AI can do and setting realistic expectations can help you identify where and how AI can best serve your operations. 

Next, you can foster a culture of AI literacy across teams, speeding up adoption by helping them understand how it can aid their work. The more familiar your team is with AI and its possibilities, the smoother the transition. Training and education programs play a pivotal role here. 

Finally, integration requires a systematic approach. It’s not about overhauling everything at once — it's about identifying areas where AI can add the most value, then gradually experimenting and scaling its use. Successful integration stories often feature a phased approach, starting small and growing steadily. 

As we move toward a future where generative AI is a staple in business operations, remember that every journey is unique. Although there’s no one-size-fits-all solution, a strategic approach and a keen understanding of your business needs will help you navigate this new landscape successfully.

Conclusion

Generative AI offers plenty of opportunities for businesses that embrace it. It’s fueling the next generation of intelligent applications, transforming everything from product development to customer engagement to marketing. This technology is the key to unlocking unprecedented growth and innovation. 

While the journey may have challenges, the potential rewards make generative AI worth exploring. As generative AI advances, Azure AI’s broad range of tools and services will help you navigate this fascinating field.

Exercise

  • Watch Episode 02 of the serverless edition Learn Live session to learn how to build, test and deploy an end-to-end intelligent app solution.
  • Complete the Cloud Skills Challenge to build on your AI skills.
  • Register for Ask the Expert: Azure Functions on September 26 for a live Q&A with the Product Team on how to build AI apps using serverless computing.
- - +
Skip to main content

11 posts tagged with "30-days-of-IA"

View All Tags

· 12 min read
It's 30DaysOfIA

Delve into creating an Intelligent App that leverages Azure AI Vision to analyze images and extract data. Develop an API to perform optical character recognition (OCR) on uploaded images and testing this API locally.

What We'll Cover:

  • Understanding Azure AI Vision and Azure Kubernetes Service
  • Build a Python Web API to perform OCR
  • Test the API locally

image depicting an Intelligent App using AI

Jumpstart Your AI Journey: Building Your First Intelligent App with Azure AI and AKS (1)

An Intelligent App is a software application that uses artificial intelligence (AI), such as machine learning (ML), to extract meaningful insights, enabling users to make informed decisions based on the extracted text, data, sound, images, and more.

Intelligent Apps can employ advanced algorithms and models to recognize objects, patterns, and features within images, allowing them to perform tasks like object identification, image classification, facial recognition, and image segmentation.

To explore the power of Intelligent Apps, let’s build a Python app that performs optical character recognition (OCR) on images containing numbers and performs aggregate functions on the extracted numbers, like sum and average. We’ll leverage Azure AI Services for the OCR functionality and Azure Kubernetes Service (AKS) to develop, publish, and maintain a cloud-based, scalable app in Azure.

Let’s get started!

Understanding Azure AI Vision and Azure Kubernetes Service

Azure AI Vision is a cloud-based service that offers a range of advanced computer vision capabilities enabling applications to interpret content, such as images and videos, using ML and other AI algorithms. Azure computer vision lets users integrate powerful vision features into their projects without ML experience.

OCR is one of the critical functionalities within Azure vision service, allowing applications to extract text from images. Within Azure AI Vision service, OCR offers several features, including text extraction, multilingual support, layout analysis of documents, and integration with Azure services, such as Azure Functions and Azure Machine Learning.

info

Watch the intelligent apps webinar on Driving Business Value by Modernizing with Cloud-Native & AI with Microsoft and Forrester on September 26.

AKS is Microsoft Azure’s container orchestration platform that enables us to quickly and efficiently deploy, manage, and scale containerized applications, such as our Intelligent App.

Let’s explore how to leverage these tools to build an Intelligent App that uses Azure AI Vision to analyze images and extract data.

Prerequisites

To follow this tutorial, ensure you have the following:

For a preview of this final project, take a look at the complete project code.

Building the API with Azure AI Vision Service

First, log in to your Azure account and navigate to the Azure Portal.

Click Create a resource and search for “resource group.” Create a new resource group named computer-vision.

image in Azure Portal of creating a new resource group

Return to the Azure Portal home page and click Create a resource.

image in Azure Portal of creating a new resource

Search for “computer vision” and select it from the results. Click Create.

image of searching for resource group in Azure Portal

Clicking Create displays the Create Computer Vision wizard. Select the resource group you just created as the Resource group from the dropdown. Enter a resource name similar to what is seen in the screenshot below. Note that this must be a unique name so choose one that is not currently in use. Select Standard S1 as the pricing tier and check the Responsible use of AI box in the field at the bottom.

Image of selecting the Responsible use of AI box

Image of project details

Click Review + Create, then click Create.

Next, get the API key and endpoint from the Azure AI services multi-service account you created. Once the resource is created, navigate to the resource details page. Under Resource Management, select Keys and Endpoint. Once on the Keys and Endpoint page, you’ll find the Key 1 and Endpoint values. These credentials are necessary to access the Azure AI APIs.

image of selecting Keys and Endpoint

second image of selecting Keys and Endpoint

Configuring the Local Environment Variables

Next, we need to define a local environment variable file. This convenient way to store our API keys in a simple text file enables us to manage sensitive information and separate configuration from our API code.

If you haven’t done so already, clone the starter project template from GitHub into your local computer. Open the starter project template in a code editor and create a new .env file in the root folder.

Note: Root folder in this case is the “/Microsoft_Series17-18_Code/intelligent-app-before" folder.

Add the following key-value pairs to the file, remembering to replace the placeholder values with those you obtained from the previous step.

VISION_KEY=<THE-KEY-1-VALUE-FROM-YOUR-AZURE-AI-SERVICE>
VISION_ENDPOINT=<THE-ENDPOINT-VALUE-FROM-YOUR-AZURE-AI-SERVICE>

Reviewing the Quickstart Code

Let’s review the app.py file in the starter project template.

The app.py file works as the entry point for the Flask application. It is responsible for initializing the web server, listening for incoming requests, dispatching them to the appropriate route handlers, and generating responses.

Below is the code contained in the app.py file:

import os
import json

from flask import Flask, request
from flask_restful import Resource, Api
from werkzeug.utils import secure_filename

app = Flask(__name__,
static_url_path='',
static_folder='static/files')

api = Api(app)

app = Flask(__name__)

app.config['UPLOAD_FOLDER'] = 'files'

api = Api(app)

class UploadHandler(Resource):

def allowed_file(self, filename):
return '.' in filename and \
filename.rsplit('.', 1)[1].lower() in {'png'}

def post(self):
form = request.form.to_dict()

if 'file' not in request.files:
return json.dumps({ "success": False, "error": "No file part"})

file = request.files.get("file")
if file and self.allowed_file(file.filename):
filename = secure_filename(file.filename)
upload_folder = "static/files"
if not os.path.exists(upload_folder):
os.makedirs(upload_folder)
local_file_path = os.path.join(upload_folder, filename)
file.save(local_file_path)

return f"File {filename} uploaded successfully to folder: {upload_folder}"

api.add_resource(UploadHandler, "/")

if __name__ == '__main__':
app.run(debug=True)

The UploadHandler class above is defined as a Flask RESTful resource to handle file uploads. The class contains two methods:

  • The allowed_file method checks whether the file extension is allowed for upload. In this case, only .png files are allowed.

  • The post method handles HTTP POST requests for file uploads. It saves the uploaded .png file to the static/files folder.

Finally, the application returns a text response informing us that the file has been uploaded successfully.

Implementing the REST API in Python

To implement image analysis in your REST API, open the starter project template in your terminal, create a virtual environment, and activate it.

Next, install the Azure AI Vision SDK:

pip install azure-ai-vision

Then, add the following line to the requirements.txt file to include the azure-ai-vision package:

azure-ai-vision==0.13.0b1

Now, create an ocr_helper.py file in the project’s root folder. This file is a Python module that provides functions for processing images using OCR. Add the following code to the file:

import os
from statistics import median
from decimal import Decimal
import azure.ai.vision as sdk

def process_ocr(source_image):
service_options = sdk.VisionServiceOptions(os.environ["VISION_ENDPOINT"],
os.environ["VISION_KEY"])

vision_source = sdk.VisionSource(filename=source_image)

analysis_options = sdk.ImageAnalysisOptions()

analysis_options.features = (
sdk.ImageAnalysisFeature.CAPTION |
sdk.ImageAnalysisFeature.TEXT
)

analysis_options.language = "en"

analysis_options.gender_neutral_caption = True

image_analyzer = sdk.ImageAnalyzer(service_options, vision_source, analysis_options)

result = image_analyzer.analyze()

ocr_result = get_ocr_result(result)

return ocr_result

def get_ocr_result(result):
string_list = []

if result.reason != sdk.ImageAnalysisResultReason.ANALYZED:
return sdk.ImageAnalysisErrorDetails.from_result(result)
else:
if result.text is not None:
for line in result.text.lines:
for word in line.words:
string_list.append(word.content)

number_list = convert_to_decimal_list(string_list)

aggregate_result = aggregate_operations(number_list)

return {
"aggregate_result": aggregate_result,
"numbers_read": string_list
}

def convert_to_decimal_list(string_list):
return list(map(Decimal, string_list))

def aggregate_operations(numbers):
result = {
'sum': sum(numbers),
'average': sum(numbers) / len(numbers),
'median': median(numbers),
'min': min(numbers),
'max': max(numbers)
`}`
`return result`

This module uses the azure-ai-vision package to analyze images, including capturing captions and extracting text from the image. The process_ocr function inputs an image file and performs the OCR analysis. The module is a convenient tool for analyzing text-based data in pictures and performing numerical computations based on that data.

Let’s review the different components of the ocr_helper.py module listed above and explore how its functions help our Intelligent App process images and perform aggregate operations:

  • The process_ocr function takes the parameter source_image, which is the path of the image to be processed. The function then initializes the VisionServiceOptions using environment variables VISION_ENDPOINT and VISION_KEY to connect to the Azure AI Vision API.
  • The process_ocr function creates a VisionSource object with the specified source_image file name. ImageAnalysisOptions specify the features to be analyzed, including CAPTION and TEXT. The language is set to English ("en"), and gender-neutral captions are enabled.
  • Finally, an ImageAnalyzer object is created with the service options, vision source, and analysis options. The image is then analyzed using the image_analyzer.analyze method to retrieve OCR results. The recognized text from the OCR result is extracted, with numerical values stored in the string_list variable.
  • The convert_to_decimal_list function converts the list of strings to a list of decimal numbers, which helps process numeric values extracted from the text.
  • The aggregate_operations function calculates various aggregate statistics (sum, average, median, minimum, maximum) from a list of numbers and returns the results in a dictionary.

Note that you must have the appropriate credentials (VISION_KEY) and endpoint (VISION_ENDPOINT) configured for the Azure AI Vision API to use this module.

Finally, we must modify the app.py file so our code can use the process_ocr function of the ocr_helper.py file.

Add the following import statement to the app.py file:

from ocr_helper import process_ocr

Then, replace this line:

return f"File {filename} uploaded successfully to folder: {upload_folder}"

With these two lines:

aggregates = process_ocr(local_file_path)
return json.dumps(aggregates, default=str)

Doing so allows our application to perform aggregate operations (sum, average, median, minimum, maximum) of any numeric values found in the text and return the operation’s result as a JSON response.

Running the Intelligent App Locally

Our final goal is to deploy the sample app to AKS, which will run the app in a container. However, as a good practice, let’s run the app in a container locally before moving it to AKS. Doing so ensures that our application and its dependencies are encapsulated and can be run consistently across different environments.

Deploying to Docker Desktop

For our application to run on Docker, it needs two additional files: a Dockerfile and docker-compose.yml. A Dockerfile creates a single container image by specifying the steps to build it, and a docker-compose.yml file manages multiple containers as a cohesive application, defining their configurations and relationships. Dockerfile builds the image, while docker-compose.yml orchestrates multi-container applications.

In the project root folder, add a file named Dockerfile with the content below:

# syntax=docker/dockerfile:1

FROM python:3.8-slim-buster

WORKDIR /intelligentapp

COPY requirements.txt requirements.txt
RUN pip3 install -r requirements.txt

RUN pip install debugpy

COPY . .

CMD [ "python3", "-m" , "flask", "run", "--host=0.0.0.0"]

Now create a file named docker-compose.yml file in the project’s root folder with the following code, replacing the VISION_KEY and the VISION_ENDPOINT according to the environment variables you configured earlier:

version: '3.8'
services:
intelligentapp:
build:
context: .
dockerfile: Dockerfile
image: intelligent-app
ports:
- 5000:5000
container_name: intelligent-app
environment:
- VISION_KEY=<THE-KEY-1-VALUE-FROM-YOUR-AZURE-AI-SERVICE>
- VISION_ENDPOINT=<THE-ENDPOINT-VALUE-FROM-YOUR-AZURE-AI-SERVICE>

Then, run the following command in the terminal to build the image and start a container for the Intelligent App services defined in the docker-compose.yml file:

docker-compose up --build --force-recreate

Next, open a new terminal and run the command below to list the image deployed to your local Docker:

docker images

docker images

Testing the Intelligent App Locally

Now, we’ll test the Intelligent App’s functionality using the following test image:

test image to test functionality

This image is provided as sample1.png in the sample app source files, so you can easily use it in the next step.

The Intelligent App will read the image above using OCR to extract numbers in the image and perform five aggregate functions:

  • Sum
  • Average
  • Median
  • Min
  • Max

To test the API, open Postman and fill out the fields as follows:

  • URL: http://localhost:5000/
  • Method: POST
  • Body:
    • Form-data
      • Key: File — Click the right end of the Key field and select File from the dropdown list.
      • Value: Click Select Files, then select the sample1.png file provided in the sample code.

image of selecting the sample file

Now click the Send button and review the result body:

"{\"aggregate_result\": {\"sum\": \"25821\", \"average\": \"5164.2\", \"median\": \"5622\", \"min\": \"1447\", \"max\": \"9802\"}, \"numbers_read\": [\"3049\", \"5901\", \"5622\", \"1447\", \"9802\"]}"

As we can see, the app running on a local container returned the correct results based on the sample image. So, we’re ready to prepare our Azure resources to run the Intelligent App on AKS.

Exercise

In this article, we explored the creation of an Intelligent App that leverages Azure AI Vision to analyze images and extract data. We learned how to build a Python Web API to perform OCR on uploaded images and subsequently test this API locally.

Head to the next part, to deploy this API via Azure Kubernetes Service.

+ + \ No newline at end of file diff --git a/30daysofIA/tags/30-days-of-ia/page/4/index.html b/30daysofIA/tags/30-days-of-ia/page/4/index.html index 30024a5307..464bbfddd3 100644 --- a/30daysofIA/tags/30-days-of-ia/page/4/index.html +++ b/30daysofIA/tags/30-days-of-ia/page/4/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "30-days-of-IA" | Build Intelligent Apps On Azure +11 posts tagged with "30-days-of-IA" | Build Intelligent Apps On Azure @@ -14,13 +14,13 @@ - - + +
-

9 posts tagged with "30-days-of-IA"

View All Tags

· 9 min read
It's 30DaysOfIA

In the part of our series on intelligent apps, we’ll explore how AI is transforming application development, from design and architecture to building.

What We'll Cover:

  • Revolutionizing application development using AI
  • Infusing AI in application design architecture
  • AI Assisted Pair Programming
  • Future of App Development

A decorative image of application development with AI

Reimagining Application Development With AI: A New Paradigm

Artificial intelligence (AI) is impacting every industry. AI-powered development tools offer new functionalities and assist software developers throughout the development process, revolutionizing app development. From initial design and architecture to coding and debugging, AI reshapes how we approach application conception, creation, and iteration.

In part one of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” we explored the new breed of intelligent apps and how they use AI. In this article, we’ll see how AI technologies enhance developer toolkits and shape the future of app development, particularly through AI-enabled coding assistance and debugging.

The Dawn of Intelligent Apps: Revolutionizing Application Development

AI is more than just another addition to the development toolbox—it represents a revolution bringing unprecedented efficiency, innovation, and user engagement to our applications. 

What sets intelligent apps apart is how they can adapt and learn, helping us deliver unique, real-time user experiences.  

One example is an AI-driven health application that can track biometric data, predict potential health issues, provide personalized health advice, and even guide emergency services during a medical crisis. An AI-enabled language learning app that adapts to our learning speeds, recognizes our problem areas, and customizes its content to help us improve our language proficiencies is another example.  

Intelligent apps like these don’t just use data for recommendations—they can make critical decisions and provide sophisticated services that were previously impossible. 

For developers, intelligent apps mean we’re no longer just coding—we’re teaching applications how to think, adapt, and make decisions. AI is reshaping every step of the development journey, helping us to build more intuitive and user-focused products. 

Next, we’ll examine the impact of AI on application design and architecture and how AI-assisted “pair programming” changes software development.

info

Register for the FREE webinar on Intelligent Apps with Microsoft and Forrester on September 26 to explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries.

There will be a showcase of real-world use cases that demonstrate how AI can be seamlessly integrated into cloud-native environments driving tangible business value.

AI in Design and Architecture: Fueling Creativity and Efficiency

AI technologies help developers automate and streamline their processes and provide new ways to think about design.  

In traditional application development, the design and architecture phase can take significant time, effort, and expertise. For instance, adopting a monolithic architecture or a microservices-based design can significantly increase coding, testing, and debugging complexity. The app may even behave differently when hosted locally than in a deployed environment. It may take hours to track down an issue manually—time developers could spend elsewhere. 

Conversely, we can generate numerous design iterations in seconds with AI, each optimized to fulfill specific user needs and business goals. AI tools can quickly model various scenarios' impacts and software design patterns in the architectural design phase. It provides insights into performance and potential issues and suggests improvements. AI can also generate skeleton code, allowing developers to prototype different solutions or test specific components and technologies rapidly. By reviewing thousands of lines of code in seconds, AI allows us to focus on creating apps that serve users through feature development instead of searching for bugs.  

Integrating AI into the design and architecture phase can even extend our creative capabilities. By leveraging the power of generative AI—like large language models (LLMs)—and their broad knowledge and context going beyond software, we can better brainstorm apps and feature ideas. We can go beyond code and software architecture and get ideas about user experience, logo and branding, or marketing concepts. And as AI tools deliver insights and ideas we would have never considered; it encourages more innovation.

info

Watch Episode 01 of the Learn Live session to learn how to build an end-to-end intelligent app solution architecture.

The Power of AI-Assisted Pair Programming in Building Applications

AI-assisted pair programming is a collaborative coding approach where an AI system—like GitHub Copilot or TestPilot—assists developers during coding. It’s an increasingly common approach that significantly impacts developer productivity. In fact, GitHub Copilot is now behind an average of 46 percent of developers’ code and users are seeing 55 percent faster task completion on average. 

For new software developers, or those interested in learning new skills, AI-assisted pair programming are training wheels for coding. With the benefits of code snippet suggestions, developers can avoid struggling with beginner pitfalls like language syntax. Tools like ChatGPT can act as a personal, on-demand tutor—answering questions, generating code samples, and explaining complex code syntax and logic. These tools dramatically speed the learning process and help developers gain confidence in their coding abilities. 

Building applications with AI tools hastens development and provides more robust code. For example, GitHub Copilot helps us write new code accurately. It also quickly makes sense of existing code, suggests whole new lines or blocks of code, and even detects errors and proposes fixes. It’s like having a second coder working alongside us—one who never tires and is trained on billions of lines of code. 

However, it’s also important to acknowledge the limitations of AI. While generative AI-assistance technologies like GitHub Copilot are impressive, they’re still imperfect. They can sometimes produce erroneous, non-functional, or vulnerable code. They can also lack nuanced, domain-specific knowledge.  

So, although these tools are helpful, we can’t rely on them fully—these are programming aids rather than definitive solutions. AI-assisted pair programming is about augmenting the human developer’s skills, not replacing them.

Debugging and Improving Code With AI: Enhancing Application Quality

Identifying and correcting code errors is often time-consuming and challenging for developers. AI streamlines the debugging process by automatically detecting anomalies, suggesting fixes, and learning from previous bugs to prevent them from recurring. 

We can use generative AI to create unit tests from code snippets, from simple parameter checking and input validation to complex mocks of database and network services. Automated AI testing improves code coverage and robustness. It saves significant time compared to manually crafting unit tests. 

AI also revolutionizes code reviews by scanning and highlighting potential issues—like code patterns leading to a bug or a coding standards violation—and suggesting ways to simplify or refactor the code for readability and performance. This reduces human error and makes the code review process more efficient. Its benefits extend to small teams, where the availability of developers is crucial to deadlines, and large teams, where effective collaboration depends on readability and code consistency. AI further saves time by triaging issues so developers can prioritize the most mission-critical ones. 

Aside from improving code quality, AI is critical to understanding system behavior. Intelligent AI-assisted software development tools can monitor and audit system behavior, giving developers a better insight into how their code performs in different scenarios. AI can track patterns, identify inefficiencies, and provide alerts when they detect unusual behavior—enabling developers to enhance their code further. 

By predicting and identifying potential vulnerabilities, intelligent AI-assisted coding apps like Microsoft Security Copilot are pivotal in ensuring application functionality, reliability, and security. These AI tools are continually trained and upgraded with large codebases and user feedback that refines their responses. This ability to learn from past mistakes makes AI an incredibly powerful tool that will continue to expand its capabilities in the future.

info

Complete the AI Cloud Skills Challenge to build on you AI application development skills with paired programming using generative AI and GitHub Copilot.

AI and the Future of Application Development

The rapid advancements in AI and machine learning technologies point to an exciting future for application development. 

In “The Future of Applications,” software developer Mike Cann illustrates how LLMs might revolutionize application development. Cann that suggests fully-autonomous coding—AI-generated applications created entirely from prompts—may supersede traditional hand-crafted applications. Dynamically generated user interfaces could offer personalized and customizable experiences while we can design APIs for machine consumption and efficiency rather than human readability. Furthermore, we can store data in less structured, more adaptable, and flexible forms. 

AI is already making application development more accessible. With AI handling the complex, technical aspects of development, less-technical individuals can take a greater role in building applications. Consequently, AI can potentially democratize software engineering. 

Intelligent apps will continue to evolve, becoming more efficient, adaptable, and capable of learning from user interactions. Similarly, AI will inevitably become integral in every stage of app development.

Summary

AI has already created a new development paradigm—one in which machine intelligence and human creativity come together to produce innovative, efficient, and robust applications. AI already assists us at every stage of app development, from design and architecture to coding and debugging. 

Developers who embrace AI tools and integrate them into their processes can enhance their productivity, improve the quality of their applications, and expand the possibilities of their application development.  

The future of development is automated and AI-driven, and it’s here today. Getting started is easy—try an AI-powered development tool like GitHub Copilot to see how AI-assisted pair programming can enhance your productivity and code quality.

Exercise

- - +
Skip to main content

11 posts tagged with "30-days-of-IA"

View All Tags

· 9 min read
It's 30DaysOfIA

In the last article, we created an Intelligent App that leverages Azure AI Vision to analyze images and extract data. Develop an API to perform optical character recognition (OCR) on uploaded images and testing this API locally.

In this article, we will deploy the web API to the cloud platform using Azure Kubernetes Service.

What We'll Cover:

  • Understanding Azure AI Vision and Azure Kubernetes Service
  • Build a Python Web API to perform OCR
  • Test the API locally

image depicting an Intelligent App using AI

Jumpstart Your AI Journey: Building Your First Intelligent App with Azure AI and AKS (2)

In the previous article we explored the creation of an Intelligent App that leverages Azure AI Vision to analyze images and extract data. We learned how to build a Python Web API to perform OCR on uploaded images and subsequently test this API locally.

In this article we will use Azure Kubernetes Service (AKS) to develop, publish, and maintain our app in the cloud on Azure.

Let’s get started!

Prerequisites

To follow this tutorial, ensure you have completed Jumpstart Your AI Journey: Building Your First Intelligent App with Azure AI and AKS (1).

Pushing a Container Image to Azure Container Registry (ACR)

To start, open your CLI or terminal and type the following command:

az login

Follow the instructions displayed on your browser to enter your Azure credentials.

Once authenticated, you’ll initiate a secure connection between your local environment and Azure. This process grants you access to cloud services and resources.

Next, type the command below in your terminal to set up a new Azure Container Registry (ACR) to store your container images:

az acr create --resource-group computer-vision --name <name-of-azure-container-registry> --sku Basic

Remember to replace <name-of-azure-container-registry> with your container registry name. The name must be unique within Azure and comply with these rules.

The command above creates an Azure Container Registry (ACR) in the computer-vision resource group under the Basic SKU. This ACR is your secure and private repository for storing container images within Azure.

Next, log in to the registry with the following command:

az acr login -n <name-of-azure-container-registry>

The az acr login command above lets you securely authenticate and access the specified ACR without providing their Azure credentials each time.

Now, run the following command in your terminal. It will display the endpoint URL to log in to and interact with the ACR for pushing and pulling container images.

az acr show --name <name-of-azure-container-registry> --query loginServer --output table

This returns an endpoint URL as follows:

Result
----------------------------------
<name-of-azure-container-registry>.azurecr.io

Now, run the following command to show all container images, their repository, tags, and size:

docker images
REPOSITORYTAGIMAGE IDCREATEDSIZE
intelligent-applatesta7bf9f75361716 hours ago197MB

Tags are necessary to push Docker images to a remote registry like Azure Container Registry. They also let you differentiate between different versions of the same image and upload or download the one you want.

Run the following command to tag your Docker image:

docker tag intelligent-app <name-of-azure-container-registry>.azurecr.io/intelligent-app:v1

Then, run the docker images command again to check your tagged image:

docker images
REPOSITORYTAGIMAGE IDCREATEDSIZE
intelligent-applatestc52168039265About a minute ago197MB
<name-of-azure-container-registry>.azurecr.io/intelligent-appv1c52168039265About a minute ago197MB

Now run the following command so Docker can securely upload the image to your Azure Container Registry:

docker push <name-of-azure-container-registry>.azurecr.io/intelligent-app:v1

Once we've deployed the image to the container registry, AKS can access it during deployment.

info

Watch Episode 03 for the Learn Live session to experience a guided session on how to build, test and deploy an end-to-end intelligent app solution.

Deploying the Intelligent App on Azure Kubernetes Service (AKS)

Before we deploy our Intelligent App to AKS, we need to provision an AKS cluster and define Kubernetes manifests.

To provision an AKS cluster to host our application, we specify the desired configurations for the cluster, such as the number of nodes, node size, and networking options. But first, download and install the Kubernetes command-line tools (kubectl), a client credential plugin implementing Azure authentication:

az aks install-cli

If you’re using Linux, review this tutorial. Then run the following:

sudo az aks install-cli

Next, run the following command in your terminal to enable access to the networking-related resources and services provided by the `Microsoft.Network`` namespace in Azure:

az provider register --namespace Microsoft.Network

Now, we must create an AKS cluster. Run the following command to create an AKS cluster named aks-intelligent-app in the computer-vision resource group.

az aks create --resource-group computer-vision --name aks-intelligent-app --node-count 1 --generate-ssh-keys

The command above specifies the target resource group: computer-vision. The node pool is configured with one virtual machine (VM), and the secure shell (SSH) keys for secure node access are generated automatically.

Next, run the following command to update the AKS cluster you created by attaching it to your ACR. Doing so allows the AKS cluster to pull container images from the specified ACR when deploying workloads to the cluster.

az aks update -n aks-intelligent-app -g computer-vision --attach-acr <name-of-azure-container-registry>

Then, run the command below to configure kubectl to work with our AKS cluster in the computer-vision resource group.

az aks get-credentials --resource-group computer-vision --name aks-intelligent-app

The command above retrieves the necessary credentials and context information required for kubectl to communicate with the AKS cluster.

We still have to define the Kubernetes manifests written in YAML, describing the desired state of our application deployment, including containers, networking, and scaling rules. We’ll prepare these manifests, including Deployment and Service configurations, to define how our application should be deployed and exposed.

Create a folder named Deployment in the project root directory. Next, create two files in the deployment folder: deployment.yml and service.yml.

Add the following configuration to the deployment.yml file, replacing the <name-of-azure-container-registry> placeholder with your registry’s name:

apiVersion: apps/v1
kind: Deployment
metadata:
name: intelligent-app
spec:
replicas: 1
selector:
matchLabels:
app: intelligent-app
template:
metadata:
labels:
app: intelligent-app
spec:
nodeSelector:
kubernetes.io/os: linux
containers:
- name: intelligent-app
image: <name-of-azure-container-registry>.azurecr.io/intelligent-app:v1
resources:
limits:
memory: 512Mi
cpu: "1"
requests:
memory: 256Mi
cpu: "0.2"
ports:
- containerPort: 5000
env:
- name: FLASK_DEBUG
value: "1"
- name: VISION_KEY
value: <THE-KEY-1-VALUE-FROM-YOUR-AZURE-AI-SERVICE>
- name: VISION_ENDPOINT
value: <THE-ENDPOINT-VALUE-FROM-YOUR-AZURE-AI>

Additionally, edit the VISION_KEY and VISION_ENDPOINT environment variables above according to the API key and endpoint of your Azure AI Services instance.

Then, add the following configuration to the service.yml file:

apiVersion: v1
kind: Service
metadata:
name: intelligent-app-service
spec:
type: LoadBalancer
ports:
- protocol: TCP
port: 80
targetPort: 5000
name: port5000
selector:
app: intelligent-app

Now, we’ll deploy our application using kubectl. Applying the Kubernetes manifests creates the necessary resources and deploys our containerized application to the AKS cluster.

First, change the terminal to the deployment folder:

cd Deployment 

Then, run the following command to create or update Kubernetes resources defined in the deployment.yml file:

kubectl apply -f deployment.yml

Create a Kubernetes Service resource in a Kubernetes cluster based on the configuration defined in the service.yml file using the code below:

kubectl apply -f service.yml

Once you’ve applied the resource definition and the service configuration contained in the deployment.yml and the service.yml files, open the aks-intelligent-app Kubernetes Service in the Azure Portal, select Workloads under Kubernetes resources on the sidebar, and find the deployment named intelligent-app. It must have the status “Ready 1/1”. If you encounter an issue with this status, check out these troubleshooting resources.

image of selecting the deployment in Azure Portal

Testing the Intelligent App on AKS

To test the app on AKS, first, run the command below:

kubectl get services

This command lists the Services and their corresponding details, including the Service name, cluster IP address, external IP, and ports.

NAMETYPECLUSTER-IPEXTERNAL-IPPORT(S)AGE
intelligent-app-serviceLoadBalancer10.0.77.6020.121.76.15380:30936/TCP47s
kubernetesClusterIP10.0.0.1<none>443/TCP14m

The output above shows a Kubernetes Service named intelligent-app-service with a type set to LoadBalancer. It’s reachable from within the cluster using the cluster IP 10.0.77.60 and accessible externally via the external IP 20.121.76.153 on port 80 (mapped to port 30936).

Note: Your set of IP addresses will differ. Remember to use your unique external IP address when testing with Postman.

To test the deployed app, go to Postman, replace the URL with the external IP of the Kubernetes Service you just deployed, and click Send:

mage of sending the app in Postman

As we can see, our Intelligent App has successfully deployed to AKS and is functioning on the cloud as expected.

Exercise

Next Steps

In this two-part article, we explored the creation of an Intelligent App that leverages Azure AI Vision to analyze images and extract data. We learned how to build a Python Web API to perform OCR on uploaded images and subsequently deploy this API via Azure Kubernetes Service.

Besides OCR and Image Analysis, you can continue exploring Azure’s vast array of services and experiment further with Azure AI and AKS by applying various practical uses to your Intelligent Apps, including natural language processing, speech recognition and synthesis, sentiment analysis for customer feedback, and automated content moderation.

+ + \ No newline at end of file diff --git a/30daysofIA/tags/30-days-of-ia/page/5/index.html b/30daysofIA/tags/30-days-of-ia/page/5/index.html index ebbe418fc4..f71b465b37 100644 --- a/30daysofIA/tags/30-days-of-ia/page/5/index.html +++ b/30daysofIA/tags/30-days-of-ia/page/5/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "30-days-of-IA" | Build Intelligent Apps On Azure +11 posts tagged with "30-days-of-IA" | Build Intelligent Apps On Azure @@ -14,13 +14,13 @@ - - + +
-

9 posts tagged with "30-days-of-IA"

View All Tags

· 10 min read
It's 30DaysOfIA

Explore real-world examples of how Intelligent Apps revolutionize logistics, retail, finance, manufacturing, tech, and medicine.

What We'll Cover:

  • Real-world businesses revolutionizing operations with Intelligent Apps
  • High level architecture for industry scenarios
  • Implementation architecture overview across industries

A decorative header image for the blog

Real-world Success Stories

According to IBM’s 2022 Global AI Adoption Index, 35% of surveyed companies already use AI in their business, and 42% are exploring the idea. Organizations lagging behind the AI curve will quickly fall behind early adopters, who are already enjoying its benefits and boosting their investment in this technology.

In the first part of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” we explored how organizations can use AI-powered apps, or intelligent apps, to transform their operations, decision-making, and customer experiences, leaping ahead of the competition.

Intelligent apps significantly improve operational efficiency and customer engagement across industries like logistics, retail, finance, manufacturing, tech, and healthcare. In this article, we’ll spotlight real-world success stories of businesses revolutionizing operations with intelligent apps using Microsoft Azure’s app, data, and AI services.

info

Register for the Learn Live on September 21 for a live guided experience with SMEs on how to build end-to-end solution architecture for real world implementation of Intelligent Apps.

Streamlining Operations: Intelligent Apps in the Airline Industry

Aerospace pioneer Airbus leveraged intelligent apps to streamline its operations and innovate its services with two ground-breaking modernizations that use Azure apps, data, and AI services to reimagine pilot training and predict aircraft maintenance issues. 

Aviation technology’s rapid evolution compelled Airbus to reconceive their approach to training pilots. The aircraft manufacturer created a pilot training chatbot by combining Azure AI Services with Speech to Text and Text to Speech. The bot helps trainee pilots navigate over 6,000 pages of technical information. Pilots can ask questions verbally for quick answers, aiding their understanding of dense information and speeding up training. And when Airbus needed to accommodate its military consumers, deploying its Kubernetes cluster to a disconnected, “air-gapped” environment met the strict security requirements of military aircraft training.

A chatbot could look like the following diagram as Azure services work together to create a conversational experience. Its components could include security and governance, bot logic and user experience (UX), bot cognition and intelligence, data extract, transfer, and load (ETL) operations, quality assurance and enhancements, logging, and monitoring and reporting.

A diagram of a conversational experience enabled by Azure services

Airbus also used AI to improve its aircraft maintenance and safety. They deployed AI Anomaly Detector to gather and analyze telemetry data from multiple flights, enabling more profound insights into wear and tear on military aircraft operating in harsh conditions. This AI application helps Airbus better understand each airplane’s health. Airbus can predict and fix potential problems before they occur, improving the aircraft’s safety and operational readiness.

AI and Azure are changing how organizations like Airbus operate, even in strict, highly regulated markets with stringent security requirements. And by streamlining pilot training and staying ahead of aircraft maintenance, Airbus helps its military and commercial clients reliably deliver their goods across the globe, maintaining their critical supply chains.

Boosting Customer Service: Intelligent Apps in the Retail Industry

The retail industry has been among the first to adopt and benefit from intelligent apps. For example, the leading American used car retailer, CarMax, uses intelligent apps to transform the car shopping experience.

Before embracing AI, CarMax faced a significant challenge: Manually creating text summaries was time-consuming and labor-intensive. CarMax decided to leverage Azure OpenAI Service to automatically generate car research pages, offering customers valuable insights while enhancing the website’s search engine rankings.

This AI-driven approach has significantly streamlined the process, achieving in a few months what would have taken years manually. Also, the AI system condenses thousands of customer reviews into a few readable sentences, offering potential buyers a quick overview of others’ experiences.

CarMax has improved their customer experience while boosting operational efficiency through AI adoption. CarMax’s editorial staff focuses on creating strategic, longer-form content with the time saved, enhancing productivity, and driving more website traffic.

CarMax’s success story serves as a testament to the potential of intelligent apps in the retail industry.

Enhancing Cybersecurity: Intelligent Apps in Finance

Intelligent apps also impact the highly regulated financial services sector. Swift, a leading infrastructure provider for financial messaging services, uses intelligent apps to enhance decision-making and combat financial crime.

The massive growth of cross-border transactions and instant payment networks led to a surge in financial fraud that can cost the industry hundreds of billions of dollars every year. So, Swift and Microsoft partnered to build an anomaly detection model using federated learning techniques with Azure Machine Learning and Azure confidential computing.

Microsoft Purview helped govern data, as the federated learning method partially trained each model in a silo before aggregating them. This approach enabled Swift and Microsoft to develop a highly accurate model without copying or moving data from Swift members’ secure locations, ensuring the highest level of security and privacy.

Financial organizations can now deploy this trained ML model to run a real-time fraud detection system, lowering the cost of financial fraud.

Organizations can deploy a fraud detection system like the following example on Microsoft Azure. The system ingests data from streams or storage, runs analytics, and uses ML to detect fraud and store results. It then alerts users and other systems to the security issue.

A diagram of an example fraud detection system

Swift provides the innovative model to its banking partners, who can share data to further train the model. Azure confidential computing securely runs new models on current data to accumulate insights, generating an ongoing learning cycle. This iterative approach strengthens the global detection of fraudulent financial transactions through an increasingly faster and more accurate model. 

The collaboration between Swift and Microsoft demonstrates how combining various Azure services creates Intelligent Apps to enhance decision-making in finance. These apps solve real problems at an unprecedented global scale. 

Enhancing Efficiency: Intelligent Apps in Manufacturing

3M, known for Post-it Notes and other innovative products, wielded the power of Intelligent Apps to standardize, automate, and accelerate its sales forecasting. The company needed a unified, automated approach to replace its multiple manual methods.

It adopted Azure Machine Learning and other Azure AI Services to develop, train, and implement 1,500 custom models for each region and division, replacing manual methods. This solution delivers timely finance, sales, and marketing insights while freeing teams from time-consuming manual data manipulation tasks.

3M used Azure Machine Learning, automated machine learning (AutoML), and the Many Models Solution Accelerator to train and score numerous machine learning models in parallel. This approach significantly shortened the company’s development cycle time.

Then, 3M integrated Microsoft Power BI into their solution to compile and visualize results more effectively. Executives now have an unbiased and unfiltered view of data for better decision-making. With its improved ability to forecast sales, 3M can focus on manufacturing the products it needs efficiently.

3M’s success demonstrates how combining various Azure services can enhance efficiency in the manufacturing industry. These apps improve operational processes while enabling employees to focus on higher-level tasks. Strategic decisions are more targeted, and business conversations are more meaningful.

Driving Innovation: Intelligent Apps in the Technology Industry

As expected, the tech industry is at the forefront of leveraging Intelligent Apps to drive innovation. Elastic saw an opportunity to use intelligent apps to optimize its search services and meet customer needs more effectively. The company built a managed, secure, and scalable solution for its customers, Elastic Cloud, by leveraging Microsoft Azure.

Elastic Cloud uses Azure Machine Learning and other Azure AI Services to automate many basic management tasks, such as backing up data, upgrading, and scaling deployments. This approach enables Elastic’s customers to focus less on managing the software and more on serving their own clients. Customers can quickly deploy and manage Elastic Cloud on Kubernetes (ECK) using Azure Kubernetes Service.

As a result, Elastic’s cloud business is growing twice as fast as its on-premises solutions. Enterprise search users have reported significant improvements in system availability and employee satisfaction.

Additionally, Elastic incorporated Azure OpenAI Service into its Elasticsearch Relevance Engine (ESRE), providing more relevant answers to search queries. ESRE enables text, vector, and hybrid search, offering a more effective and accurate response to queries.

Elastic’s success story demonstrates how combining various Azure services can create intelligent Apps to drive innovation in the tech industry. These apps help improve operational processes and deliver better results quickly.

Revolutionizing Healthcare: Intelligent Apps in Medicine

Intelligent apps also drive transformative changes in the healthcare sector. Cambridgeshire and Peterborough NHS Foundation Trust, a healthcare provider in the United Kingdom, gradually shifted their on-premises systems to the cloud using Azure, improving flexibility, and lowering costs. They needed a solution for their electronic patient record (EPR) system that integrates 23 years of data across multiple systems and formats.

The Trust used Microsoft Azure and Azure Cognitive Search, including semantic search for understanding intent and contexts and cognitive capabilities for extracting insights from data, to standardize and automate data management. This approach made legacy, archive, and live data more discoverable, saving clinicians’ time. The Trust’s solution searches through millions of clinical documents in seconds, even recognizing handwritten notes without needing a separate optical character recognition (OCR) solution to extract text from images (Azure Cognitive Search automatically does it). Clinicians can now uncover previously inaccessible insights, enabling them to offer more personalized care.

The Trust also used Integration Services tools such as Azure Logic Apps to create workflows without writing code and Event Grid to connect Azure and third-party services through a publisher-subscriber model. They switched on a live feed from their new EPR system to Azure to feed real-time information into other applications. Researchers can now access anonymized data to fuel medical innovations beyond the NHS.

The healthcare provider also invested heavily in Power BI’s data visualization and analytical functions to sift through massive amounts of information. This level of data accessibility was previously impossible with their on-premises system. The new insights have significantly improved clinicians’ efficiency for better patient care.

Data accessibility and security are critical in the healthcare industry. Intelligent Apps empower organizations like the Trust to revolutionize medical care. AI capabilities helped the Trust improve its operational efficiency and user experiences, setting a new standard for healthcare in the digital age.

Summary

AI is driving transformation and real-world successes across industries. Intelligent apps are reshaping our world by streamlining operations in manufacturing and logistics, enhancing customer experience in the retail sector, improving decision-making in finance, driving innovation in tech, and revolutionizing healthcare.

As organizations adopt AI, they’re innovating in ways that were once too difficult, expensive, or outright impossible. Companies that wait to embrace intelligent apps risk the chance of falling behind. By embracing intelligent apps and leveraging the capabilities of Azure, you can harness the transformative potential of AI to remain on the cutting edge of technology.

Exercise

- - +
Skip to main content

11 posts tagged with "30-days-of-IA"

View All Tags

· 10 min read
It's 30DaysOfIA

Explore how digital natives and Independent Software Vendors (ISVs) can transform their business operations and generate higher value for customers with generative AI and intelligent apps.

What We'll Cover:

  • Potential of Generative AI in application development
  • Challenges with Generative AI
  • Integrating Generative AI with business operations

image of generative AI and intelligent apps used in business

Unleashing Business Transformation: How Digital Natives Can Leverage Generative AI

Generative AI is no longer a technological flight of fancy. It’s here, and it’s rewriting the rules of business innovation. 

This type of AI leverages machine learning algorithms to create new data with similar characteristics to a given dataset. It goes beyond text generation using generative pretrained transformer (GPT) models, covering an array of applications that span from image synthesis to translations and more. It also powers a new class of intelligent applications, which combine AI capabilities with traditional software functions for enhanced user experiences and advanced business solutions. 

Generative AI is transforming how digital natives and independent software vendors (ISVs) operate. Generative AI offers new possibilities for creating unique business value, enhancing customer experiences, and driving growth. 

It plays a critical role in applications, improving functionality, aiding decision-making, and automating complex tasks to deliver dynamic, uniquely personalized user experiences. Generative AI also enables and automates data-driven outcomes, functions, and features.

Integrating AI capabilities into customer-facing apps and turning them into intelligent apps benefits both the customer and the business, as we discussed in Demystifying Intelligent Applications: Leveraging AI in App Development. You get faster product development workflows and better business intelligence for sales and marketing, while your users get more natural and context-aware features and interactions. 

That said, to realize generative AI’s potential, you need some strategy and knowledge of how and where it fits into your current business and operations. We’ll examine this technology’s potential to transform business operations, its risks, and rewards, and how to integrate it into your existing operations. 

Unpacking the Potential of Generative AI

Generative AI is creating new opportunities in the digital playing field. It empowers businesses to generate insights, design products, and automate processes in ways and at speeds that were previously unimaginable. You can now predict customer behavior more accurately, offer highly personalized user experiences, and prototype innovative ideas rapidly. 

For example, Microsoft used generative AI to reinvent web search in the new AI-powered Bing and Edge. The search experience has evolved from a solitary user flow of typing in keywords and scrolling through results. It’s now a conversational experience where Bing uses context to offer better quality and more personalized search queries and results. 

Similarly, Microsoft harnessed generative AI to transform its productivity suite into intelligent apps. Through Copilot for Microsoft 365, PowerPoint now empowers users to tell their stories by using DALL-E to generate images effortlessly. It also helps rephrase text-heavy slides to be more straightforward. Outlook now assists users to compose more effective emails, enabling better communication.

These are just some examples of how AI powers day-to-day work and operations. 

Businesses that ignore generative AI miss out on untapped market opportunities, letting competitors gain an edge in the digital race. By seeking ways to rethink assumptions and reinvent existing approaches, you can effectively use generative AI to create new opportunities, open products to new use cases and markets, unlock new growth and customer satisfaction levels, and increase your revenue.

info

Register for the intelligent apps webinar on Driving Business Value by Modernizing with Cloud-Native & AI with Microsoft and Forrester on September 26.

Explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries. 

The Crucial Role of Generative AI in Business Operations

Generative AI can be a powerful catalyst for business transformation. Let’s quickly tour some ways it can redefine business operations. 

Product development is traditionally a long and painstaking process. Now, tools like GitHub Copilot and TestPilot use generative AI to make coding and testing software easier, faster, and more robust. 

Similarly, machine learning as a service technology, such as Azure Machine Learning, democratizes the availability of machine learning. It helps collect, clean, and train models across datasets, making engineering and analyzing data more efficient and accessible.  

Generative AI also simplifies the process of creating intelligent apps. Previously, simulating thousands of prototypes, predicting their performance, and narrowing down to the most viable options could take weeks, months, or even years of iteration and data analysis. With generative AI, this process can be as quick as days, hours, or perhaps minutes. 

Customer experience is also ripe for transformation. Personalized recommendations drive engagement and sales, and generative AI can scale that personalization to every customer interaction using comprehensive and continually updated data. For instance, Microsoft leverages AI to enable personalization in Dynamics 365 Customer Insights. Using this tool, you can ask questions about your customers in natural language and gain valuable and actionable insights into your customer base. 

Lastly, marketing and sales can benefit from generative AI. For example, identifying current and future key markets and emerging trends helps companies stay ahead of the curve. Tools like Copilot in Power BI help you quickly understand and uncover insights about your business, enabling your team to craft targeted campaigns, create engaging content, and optimize your business strategy. 

Leveraging this full generative AI potential requires the right tools. GPT-4, for instance, can be instrumental in realizing effective business transformation. It excels in understanding context, generating human-like text, and learning from minimal data input, making it a formidable tool in any digital native's arsenal. Platforms like Azure OpenAI Service bring GPT-4’s power to your fingertips.

However, as with any powerful technology, it’s unwise to treat generative AI as a perfect solution. You must know the challenges and risks of not leveraging it effectively, including technical and human-factor considerations. 

Without generative AI, your company risks missing the opportunity to please customers via personalization, as discussed previously. You’ll miss sales opportunities via business intelligence insights, limiting your ability to grow revenue and keep up with the competition. 

One challenge of leveraging generative AI’s full potential is finding the right tools. Fortunately, you can implement generative AI in your organization without investing in infrastructure, computational power, and expertise. Platforms like Azure OpenAI Service and Azure AI Services make AI easily accessible. They also work with Azure compute and data services like Azure Functions and Azure Kubernetes Service (AKS), enabling you to build intelligent apps easily and quickly. 

However, generative AI can’t do everything. It may need human expertise and intervention to use it fully. 

For example, generative AI can create unique data. However, the data may have limited diversity and complexity. The AI may need help recognizing additional considerations or requirements specific to a problem. It may also lack context due to various factors, such as the information’s freshness. Also, since it’s an emerging field that’s potentially worth $4.4 trillion, we’re still learning about the nuances and potential regulatory impact of generative AI. 

Perhaps the most crucial and challenging aspect of adopting generative AI is an organization’s required cultural shift. Your teams must learn how to use AI as your company fosters an understanding of AI’s potential and limitations. 

Begin integrating AI literacy into your company’s DNA. Knowledge of its potential and the readily available tools can inspire your teams to incorporate generative AI into their daily workflows and business operations quickly, improve productivity, and deliver superior customer experiences. 

No company wants to be outpaced by its competitors. You can balance generative AI’s potential with your business operations by staying aware of and adapting to the dynamic AI landscape.

Achieving Harmony: Integrating Generative AI with Existing Business Operations

Integrating generative AI into existing business operations doesn’t have to be disruptive. With the correct strategy, you can do so seamlessly. Your goal should be to augment and enhance your operations, not disrupt and replace. 

For example, Microsoft Cloud technologies leveraged AI to relieve healthcare organizations’ challenges, such as talent shortage and workforce burnout. Providers automated their workflows, invested in specific applications and connectors, and enhanced support for data and operations. 

Azure AI Services, such as text analytics for health, enable healthcare organizations to understand their largely unstructured and untapped medical data. Project Health Insights leans on AI to help healthcare professionals gain actionable insights and inferences from patient data. 

Azure Health Bot, an Intelligent App, empowers organizations to build and deploy legally compliant, powerful conversational healthcare experiences to patients. 

A successful AI integration like this begins with understanding. Knowing what generative AI can do and setting realistic expectations can help you identify where and how AI can best serve your operations. 

Next, you can foster a culture of AI literacy across teams, speeding up adoption by helping them understand how it can aid their work. The more familiar your team is with AI and its possibilities, the smoother the transition. Training and education programs play a pivotal role here. 

Finally, integration requires a systematic approach. It’s not about overhauling everything at once — it's about identifying areas where AI can add the most value, then gradually experimenting and scaling its use. Successful integration stories often feature a phased approach, starting small and growing steadily. 

As we move toward a future where generative AI is a staple in business operations, remember that every journey is unique. Although there’s no one-size-fits-all solution, a strategic approach and a keen understanding of your business needs will help you navigate this new landscape successfully.

Conclusion

Generative AI offers plenty of opportunities for businesses that embrace it. It’s fueling the next generation of intelligent applications, transforming everything from product development to customer engagement to marketing. This technology is the key to unlocking unprecedented growth and innovation. 

While the journey may have challenges, the potential rewards make generative AI worth exploring. As generative AI advances, Azure AI’s broad range of tools and services will help you navigate this fascinating field.

Exercise

  • Watch Episode 02 of the serverless edition Learn Live session to learn how to build, test and deploy an end-to-end intelligent app solution.
  • Complete the Cloud Skills Challenge to build on your AI skills.
  • Register for Ask the Expert: Azure Functions on September 26 for a live Q&A with the Product Team on how to build AI apps using serverless computing.
+ + \ No newline at end of file diff --git a/30daysofIA/tags/30-days-of-ia/page/6/index.html b/30daysofIA/tags/30-days-of-ia/page/6/index.html index 2337f42821..b27707db99 100644 --- a/30daysofIA/tags/30-days-of-ia/page/6/index.html +++ b/30daysofIA/tags/30-days-of-ia/page/6/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "30-days-of-IA" | Build Intelligent Apps On Azure +11 posts tagged with "30-days-of-IA" | Build Intelligent Apps On Azure @@ -14,13 +14,13 @@ - - + +
-

9 posts tagged with "30-days-of-IA"

View All Tags

· 8 min read
It's 30DaysOfIA

Let’s start with understanding the power of intelligent applications.

What We'll Cover:

  • What are intelligent applications?
  • Categories of Intelligent Apps
  • Breaking down the role of AI in intelligent apps

A decorative header image for the blog

Demystifying Intelligent Applications: Leveraging AI in App Development

Until recent years, when you heard the term “artificial intelligence” (AI), you might have pictured autonomous robots from sci-fi films. Now, perhaps your mind jumps to big data and machine learning (ML). Maybe you’re even thinking about the latest ChatGPT plugins and transformer architectures. Today, AI is more than these imaginations: it’s a reality increasingly woven into our day-to-day routines through intelligent applications.

Intelligent apps combine cloud-scale compute, data, and predictive or generative AI. These applications help you automate tasks and make data-driven decisions, aiding in leveraging business intelligence to gain meaningful, practical outcomes. 

For developers, the AI landscape isn’t an abstract complexity. It’s a real, tangible opportunity for evolving traditional applications, making them “intelligent,” and delivering more value to users. And with tools like Azure Kubernetes Service, Azure Cosmos DB, and Azure AI Services, stepping into the world of intelligent apps isn’t a steep climb but a steady trek across a manageable learning curve.

The Three Categories of Intelligent Applications

Before we start building, it’s critical to understand what makes an app “intelligent.” It’s not a universal definition so much as an identifier for apps that fall into one or more of the three primary categories: 

  • Outcome-based — This designation represents the broadest category of intelligent apps, whose principal value for users is the intelligent outcomes they provide. These apps process data to provide insightful results that enhance decision-making or help with tasks like trend prediction, resource management, and customer relationship management.

  • Functionality-based — This type of intelligent app takes AI integration a degree further, integrating AI as a function of the app to create or process its outputs, enhancing the app’s overall interactivity and usefulness.

  • Feature-based — This is the narrowest category, in which the app’s AI/ML component is one of its core features and primary selling points

Let’s dive more deeply into each category.

Outcome-Based Apps: Intelligent Outcomes Drive Success

Outcome-based applications are all about the results provided to the user. They function beyond merely processing data to offer insightful outcomes that drive success.

Think of a personal fitness tracker app that uses AI to analyze the user’s activity and provides actionable recommendations for achieving a goal. This type of app not only tracks your steps, sleep patterns, and heart rate but uses that data to provide you with personalized health advice. It helps you make informed lifestyle choices — the “intelligent” outcome.

Another example is a social media marketing app that uses AI for time-saving data analysis, task automation, and trend prediction. AI-based sentiment analysis can gauge customer perception while natural language processing (NLP) accurately interprets customer inquiries. Task automation can schedule posts and automate responses to common customer queries, and trend forecasting helps model and predict customer behavior to further personalize targeted promotional efforts. Building this type of intelligence into your apps provides cost-effective, actionable insights that guide sales and marketing strategies.

Functionality-Based Apps: AI-Driven Features Enhance User Experiences

As the name suggests, functionality-based applications integrate AI into how the app functions, but the end user drives the action and outcome. In this category of apps, AI takes a more active role in the user’s interaction by allowing them to use AI-created or processed output. These apps leverage AI technologies like Azure AI Services for NLP, image recognition, and pattern recognition to improve user experience.

Consider a music app that generates personalized playlists based on your listening habits or your smartphone’s camera app that uses AI to recognize faces and optimize photo settings. Even more advanced are the language translation apps that translate text from one language to another and can even understand and maintain the context and subtleties of the conversation. They use NLP to accomplish these feats.

Similarly, consider the latest e-commerce apps, whose AI-driven functionalities enable customers to try on clothes virtually, significantly enhancing the online shopping experience.

Feature-Based Apps: Integrating Advanced AI/ML Components

Finally, there are feature-based intelligent apps. These apps incorporate advanced AI/ML components like neural networks, internal large language models (LLMs), and sophisticated algorithms as core features for the user. In this type of application, AI is the primary feature for users to engage with and the primary selling point for the app.

Some examples of feature-based applications are chatbots and virtual agents built using Azure Bot Service. These robo-representatives use AI to provide more natural and human-like conversation, balancing superior efficiency with a friendlier, more personalized user experience. The new Bing is a prime example of this implementation, integrating AI to give users a knowledgeable copilot for web searches.

Similarly, OpenAI’s ChatGPT allows users access to its state-of-the-art language models available as Microsoft’s Azure OpenAI Service. It leverages the power of the highly sophisticated GPT-3 LLM to perform tasks like drafting emails, writing code, answering general knowledge questions, translating between languages, and tutoring in numerous subjects.

These apps don’t just use AI to provide outputs or improve user experiences: They push the boundaries of what’s possible in app development by integrating AI/ML components.

info

Register for the live Q&A Ask The Expert session on September 26 with the Azure Functions product engineering team.

Breaking Down the Role of AI in Intelligent Applications

Intelligent apps reflect the merging of traditional software design with the dynamic capabilities of AI. Let’s explore how AI integrates and amplifies these applications.

At its core, AI augments applications with the capacity to self-learn and self-improve. It allows you to create applications that predict, react, and adapt to a constantly changing environment. In the power grid management system for a major city, an AI-infused application can forecast power usage trends, react to anomalies, and adapt to sudden demand surges, ensuring optimal efficiency and preventing blackouts.

But AI’s role doesn’t end at prediction or decision-making: It also automates tasks. In enterprise solutions, it has moved beyond simple automation to encompass intelligent process automation (IPA). In this arena, AI algorithms understand, learn, and then automate entire digital workflows. For developers, this is a fertile ground for creating applications that can dramatically improve the operational efficiency of businesses.

Even more, AI substantially aids in development tasks. For example, there’s GitHub Copilot, an AI-powered code assistant that offers suggestions based not only on syntax but on development best practices and the intent of the code. AI has evolved from a tool you use to build apps to a partner that helps in creating them. 

Using AI to build intelligent apps isn’t about throwing out what’s worked before. It’s about enhancing the components that have the most impact and achieve the best results. Incorporating AI in apps elevates their predictive abilities, streamlines complex tasks, and even aids in the development and creation process. When you build intelligent apps, you don’t just address current problems. You anticipate future challenges and craft solutions that adapt over time.

Summary

As developers the power of AI is in our hands, and it’s our responsibility to harness its full potential. Each category tells the story of AI's transformative potential, from applications that deliver intelligent outcomes to those with AI capabilities to enrich the user experience and those pushing boundaries with advanced AI/ML technology.

Accessible and robust platforms like Azure AI can simplify the process of developing Intelligent Applications. Whether you’re refining an existing app or building a new one, just entering the world of AI, or already familiar with the landscape, take a moment to appreciate the potential that AI integration offers. You’re embracing this exciting frontier and shaping the future of app development. 

Exercise

  • Complete the Cloud Skills Challenge to build on your apps and AI skills.
  • Watch Episode 01 of the Serverless Edition Learn Live session to learn how to build an end-to-end intelligent app solution.
- - +
Skip to main content

11 posts tagged with "30-days-of-IA"

View All Tags

· 9 min read
It's 30DaysOfIA

In the part of our series on intelligent apps, we’ll explore how AI is transforming application development, from design and architecture to building.

What We'll Cover:

  • Revolutionizing application development using AI
  • Infusing AI in application design architecture
  • AI Assisted Pair Programming
  • Future of App Development

A decorative image of application development with AI

Reimagining Application Development With AI: A New Paradigm

Artificial intelligence (AI) is impacting every industry. AI-powered development tools offer new functionalities and assist software developers throughout the development process, revolutionizing app development. From initial design and architecture to coding and debugging, AI reshapes how we approach application conception, creation, and iteration.

In part one of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” we explored the new breed of intelligent apps and how they use AI. In this article, we’ll see how AI technologies enhance developer toolkits and shape the future of app development, particularly through AI-enabled coding assistance and debugging.

The Dawn of Intelligent Apps: Revolutionizing Application Development

AI is more than just another addition to the development toolbox—it represents a revolution bringing unprecedented efficiency, innovation, and user engagement to our applications. 

What sets intelligent apps apart is how they can adapt and learn, helping us deliver unique, real-time user experiences.  

One example is an AI-driven health application that can track biometric data, predict potential health issues, provide personalized health advice, and even guide emergency services during a medical crisis. An AI-enabled language learning app that adapts to our learning speeds, recognizes our problem areas, and customizes its content to help us improve our language proficiencies is another example.  

Intelligent apps like these don’t just use data for recommendations—they can make critical decisions and provide sophisticated services that were previously impossible. 

For developers, intelligent apps mean we’re no longer just coding—we’re teaching applications how to think, adapt, and make decisions. AI is reshaping every step of the development journey, helping us to build more intuitive and user-focused products. 

Next, we’ll examine the impact of AI on application design and architecture and how AI-assisted “pair programming” changes software development.

info

Register for the FREE webinar on Intelligent Apps with Microsoft and Forrester on September 26 to explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries.

There will be a showcase of real-world use cases that demonstrate how AI can be seamlessly integrated into cloud-native environments driving tangible business value.

AI in Design and Architecture: Fueling Creativity and Efficiency

AI technologies help developers automate and streamline their processes and provide new ways to think about design.  

In traditional application development, the design and architecture phase can take significant time, effort, and expertise. For instance, adopting a monolithic architecture or a microservices-based design can significantly increase coding, testing, and debugging complexity. The app may even behave differently when hosted locally than in a deployed environment. It may take hours to track down an issue manually—time developers could spend elsewhere. 

Conversely, we can generate numerous design iterations in seconds with AI, each optimized to fulfill specific user needs and business goals. AI tools can quickly model various scenarios' impacts and software design patterns in the architectural design phase. It provides insights into performance and potential issues and suggests improvements. AI can also generate skeleton code, allowing developers to prototype different solutions or test specific components and technologies rapidly. By reviewing thousands of lines of code in seconds, AI allows us to focus on creating apps that serve users through feature development instead of searching for bugs.  

Integrating AI into the design and architecture phase can even extend our creative capabilities. By leveraging the power of generative AI—like large language models (LLMs)—and their broad knowledge and context going beyond software, we can better brainstorm apps and feature ideas. We can go beyond code and software architecture and get ideas about user experience, logo and branding, or marketing concepts. And as AI tools deliver insights and ideas we would have never considered; it encourages more innovation.

info

Watch Episode 01 of the Learn Live session to learn how to build an end-to-end intelligent app solution architecture.

The Power of AI-Assisted Pair Programming in Building Applications

AI-assisted pair programming is a collaborative coding approach where an AI system—like GitHub Copilot or TestPilot—assists developers during coding. It’s an increasingly common approach that significantly impacts developer productivity. In fact, GitHub Copilot is now behind an average of 46 percent of developers’ code and users are seeing 55 percent faster task completion on average. 

For new software developers, or those interested in learning new skills, AI-assisted pair programming are training wheels for coding. With the benefits of code snippet suggestions, developers can avoid struggling with beginner pitfalls like language syntax. Tools like ChatGPT can act as a personal, on-demand tutor—answering questions, generating code samples, and explaining complex code syntax and logic. These tools dramatically speed the learning process and help developers gain confidence in their coding abilities. 

Building applications with AI tools hastens development and provides more robust code. For example, GitHub Copilot helps us write new code accurately. It also quickly makes sense of existing code, suggests whole new lines or blocks of code, and even detects errors and proposes fixes. It’s like having a second coder working alongside us—one who never tires and is trained on billions of lines of code. 

However, it’s also important to acknowledge the limitations of AI. While generative AI-assistance technologies like GitHub Copilot are impressive, they’re still imperfect. They can sometimes produce erroneous, non-functional, or vulnerable code. They can also lack nuanced, domain-specific knowledge.  

So, although these tools are helpful, we can’t rely on them fully—these are programming aids rather than definitive solutions. AI-assisted pair programming is about augmenting the human developer’s skills, not replacing them.

Debugging and Improving Code With AI: Enhancing Application Quality

Identifying and correcting code errors is often time-consuming and challenging for developers. AI streamlines the debugging process by automatically detecting anomalies, suggesting fixes, and learning from previous bugs to prevent them from recurring. 

We can use generative AI to create unit tests from code snippets, from simple parameter checking and input validation to complex mocks of database and network services. Automated AI testing improves code coverage and robustness. It saves significant time compared to manually crafting unit tests. 

AI also revolutionizes code reviews by scanning and highlighting potential issues—like code patterns leading to a bug or a coding standards violation—and suggesting ways to simplify or refactor the code for readability and performance. This reduces human error and makes the code review process more efficient. Its benefits extend to small teams, where the availability of developers is crucial to deadlines, and large teams, where effective collaboration depends on readability and code consistency. AI further saves time by triaging issues so developers can prioritize the most mission-critical ones. 

Aside from improving code quality, AI is critical to understanding system behavior. Intelligent AI-assisted software development tools can monitor and audit system behavior, giving developers a better insight into how their code performs in different scenarios. AI can track patterns, identify inefficiencies, and provide alerts when they detect unusual behavior—enabling developers to enhance their code further. 

By predicting and identifying potential vulnerabilities, intelligent AI-assisted coding apps like Microsoft Security Copilot are pivotal in ensuring application functionality, reliability, and security. These AI tools are continually trained and upgraded with large codebases and user feedback that refines their responses. This ability to learn from past mistakes makes AI an incredibly powerful tool that will continue to expand its capabilities in the future.

info

Complete the AI Cloud Skills Challenge to build on you AI application development skills with paired programming using generative AI and GitHub Copilot.

AI and the Future of Application Development

The rapid advancements in AI and machine learning technologies point to an exciting future for application development. 

In “The Future of Applications,” software developer Mike Cann illustrates how LLMs might revolutionize application development. Cann that suggests fully-autonomous coding—AI-generated applications created entirely from prompts—may supersede traditional hand-crafted applications. Dynamically generated user interfaces could offer personalized and customizable experiences while we can design APIs for machine consumption and efficiency rather than human readability. Furthermore, we can store data in less structured, more adaptable, and flexible forms. 

AI is already making application development more accessible. With AI handling the complex, technical aspects of development, less-technical individuals can take a greater role in building applications. Consequently, AI can potentially democratize software engineering. 

Intelligent apps will continue to evolve, becoming more efficient, adaptable, and capable of learning from user interactions. Similarly, AI will inevitably become integral in every stage of app development.

Summary

AI has already created a new development paradigm—one in which machine intelligence and human creativity come together to produce innovative, efficient, and robust applications. AI already assists us at every stage of app development, from design and architecture to coding and debugging. 

Developers who embrace AI tools and integrate them into their processes can enhance their productivity, improve the quality of their applications, and expand the possibilities of their application development.  

The future of development is automated and AI-driven, and it’s here today. Getting started is easy—try an AI-powered development tool like GitHub Copilot to see how AI-assisted pair programming can enhance your productivity and code quality.

Exercise

+ + \ No newline at end of file diff --git a/30daysofIA/tags/30-days-of-ia/page/7/index.html b/30daysofIA/tags/30-days-of-ia/page/7/index.html index 055ac9b12c..010bbc24df 100644 --- a/30daysofIA/tags/30-days-of-ia/page/7/index.html +++ b/30daysofIA/tags/30-days-of-ia/page/7/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "30-days-of-IA" | Build Intelligent Apps On Azure +11 posts tagged with "30-days-of-IA" | Build Intelligent Apps On Azure @@ -14,14 +14,13 @@ - - + +
-

9 posts tagged with "30-days-of-IA"

View All Tags

· 4 min read
It's 30DaysOfIA

This Fall focus on building intelligent apps using AI and cloud-native technologies. #FallForIntelligentApps brings to you a learning journey to build your skills on creating differentiated experiences while modernizing your applications. It’s time to learn it all.

What We'll Cover

image

Get Ready To #FallForIntelligentApps starting September 18!

Today, we kick off the Fall season with content and activities to skill you up on all things Intelligent Apps or AI Apps on Azure with content, events, and community interactions! Read on to learn about what is coming!

Explore Our Initiatives

We have a number of initiatives planned for the month to help you learn and skill up on relevant technologies. Click on the links to visit the relevant pages for each

image

Register for the events!

What are 4 things you can do today, to jumpstart your learning journey?

#30Days Of Intelligent Apps

#30DaysOfIA is a series of daily blog posts grouped into themed weeks - taking you from core concepts to end-to-end solution examples in 30 days. Each article will provide conceptual lessons paired with exercises and resources to help you reinforce learnings and take next steps.

This series takes you through learning journey in six stages, each building on the previous week to help you skill up in a beginner-friendly way:

We will start with defining intelligent apps and then expand on how to build with cloud-native technologies like Azure Kubernetes Service, Azure Container Apps and Azure Functions, and take you through end-to-end scenarios for real world application development. Before we dive deep on intelligent apps, here is a high-level overview of the Intelligent Apps landscape on Azure:

image

Containers on Azure services offer you a wide range of capabilities, from simplicity to control to suit your different needs.

image -image

To start with the basics for developing Kubernetes applications, explore #30DaysOfCloudNative.

Cloud-native development when paired with serverless computing enhances your solution architecture for building cost optimized, resilient applications.

image

To start with the basics for serverless computing, explore #30DaysOfServerless.

Let’s Get Started

Now you know everything! We hope you are as excited as we are to dive into a full month of active learning and doing! Don't forget to subscribe for updates in your favorite feed reader! And look out for our first Intelligent Apps post Monday!

- - +
Skip to main content

11 posts tagged with "30-days-of-IA"

View All Tags

· 10 min read
It's 30DaysOfIA

Explore real-world examples of how Intelligent Apps revolutionize logistics, retail, finance, manufacturing, tech, and medicine.

What We'll Cover:

  • Real-world businesses revolutionizing operations with Intelligent Apps
  • High level architecture for industry scenarios
  • Implementation architecture overview across industries

A decorative header image for the blog

Real-world Success Stories

According to IBM’s 2022 Global AI Adoption Index, 35% of surveyed companies already use AI in their business, and 42% are exploring the idea. Organizations lagging behind the AI curve will quickly fall behind early adopters, who are already enjoying its benefits and boosting their investment in this technology.

In the first part of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” we explored how organizations can use AI-powered apps, or intelligent apps, to transform their operations, decision-making, and customer experiences, leaping ahead of the competition.

Intelligent apps significantly improve operational efficiency and customer engagement across industries like logistics, retail, finance, manufacturing, tech, and healthcare. In this article, we’ll spotlight real-world success stories of businesses revolutionizing operations with intelligent apps using Microsoft Azure’s app, data, and AI services.

info

Register for the Learn Live on September 21 for a live guided experience with SMEs on how to build end-to-end solution architecture for real world implementation of Intelligent Apps.

Streamlining Operations: Intelligent Apps in the Airline Industry

Aerospace pioneer Airbus leveraged intelligent apps to streamline its operations and innovate its services with two ground-breaking modernizations that use Azure apps, data, and AI services to reimagine pilot training and predict aircraft maintenance issues. 

Aviation technology’s rapid evolution compelled Airbus to reconceive their approach to training pilots. The aircraft manufacturer created a pilot training chatbot by combining Azure AI Services with Speech to Text and Text to Speech. The bot helps trainee pilots navigate over 6,000 pages of technical information. Pilots can ask questions verbally for quick answers, aiding their understanding of dense information and speeding up training. And when Airbus needed to accommodate its military consumers, deploying its Kubernetes cluster to a disconnected, “air-gapped” environment met the strict security requirements of military aircraft training.

A chatbot could look like the following diagram as Azure services work together to create a conversational experience. Its components could include security and governance, bot logic and user experience (UX), bot cognition and intelligence, data extract, transfer, and load (ETL) operations, quality assurance and enhancements, logging, and monitoring and reporting.

A diagram of a conversational experience enabled by Azure services

Airbus also used AI to improve its aircraft maintenance and safety. They deployed AI Anomaly Detector to gather and analyze telemetry data from multiple flights, enabling more profound insights into wear and tear on military aircraft operating in harsh conditions. This AI application helps Airbus better understand each airplane’s health. Airbus can predict and fix potential problems before they occur, improving the aircraft’s safety and operational readiness.

AI and Azure are changing how organizations like Airbus operate, even in strict, highly regulated markets with stringent security requirements. And by streamlining pilot training and staying ahead of aircraft maintenance, Airbus helps its military and commercial clients reliably deliver their goods across the globe, maintaining their critical supply chains.

Boosting Customer Service: Intelligent Apps in the Retail Industry

The retail industry has been among the first to adopt and benefit from intelligent apps. For example, the leading American used car retailer, CarMax, uses intelligent apps to transform the car shopping experience.

Before embracing AI, CarMax faced a significant challenge: Manually creating text summaries was time-consuming and labor-intensive. CarMax decided to leverage Azure OpenAI Service to automatically generate car research pages, offering customers valuable insights while enhancing the website’s search engine rankings.

This AI-driven approach has significantly streamlined the process, achieving in a few months what would have taken years manually. Also, the AI system condenses thousands of customer reviews into a few readable sentences, offering potential buyers a quick overview of others’ experiences.

CarMax has improved their customer experience while boosting operational efficiency through AI adoption. CarMax’s editorial staff focuses on creating strategic, longer-form content with the time saved, enhancing productivity, and driving more website traffic.

CarMax’s success story serves as a testament to the potential of intelligent apps in the retail industry.

Enhancing Cybersecurity: Intelligent Apps in Finance

Intelligent apps also impact the highly regulated financial services sector. Swift, a leading infrastructure provider for financial messaging services, uses intelligent apps to enhance decision-making and combat financial crime.

The massive growth of cross-border transactions and instant payment networks led to a surge in financial fraud that can cost the industry hundreds of billions of dollars every year. So, Swift and Microsoft partnered to build an anomaly detection model using federated learning techniques with Azure Machine Learning and Azure confidential computing.

Microsoft Purview helped govern data, as the federated learning method partially trained each model in a silo before aggregating them. This approach enabled Swift and Microsoft to develop a highly accurate model without copying or moving data from Swift members’ secure locations, ensuring the highest level of security and privacy.

Financial organizations can now deploy this trained ML model to run a real-time fraud detection system, lowering the cost of financial fraud.

Organizations can deploy a fraud detection system like the following example on Microsoft Azure. The system ingests data from streams or storage, runs analytics, and uses ML to detect fraud and store results. It then alerts users and other systems to the security issue.

A diagram of an example fraud detection system

Swift provides the innovative model to its banking partners, who can share data to further train the model. Azure confidential computing securely runs new models on current data to accumulate insights, generating an ongoing learning cycle. This iterative approach strengthens the global detection of fraudulent financial transactions through an increasingly faster and more accurate model. 

The collaboration between Swift and Microsoft demonstrates how combining various Azure services creates Intelligent Apps to enhance decision-making in finance. These apps solve real problems at an unprecedented global scale. 

Enhancing Efficiency: Intelligent Apps in Manufacturing

3M, known for Post-it Notes and other innovative products, wielded the power of Intelligent Apps to standardize, automate, and accelerate its sales forecasting. The company needed a unified, automated approach to replace its multiple manual methods.

It adopted Azure Machine Learning and other Azure AI Services to develop, train, and implement 1,500 custom models for each region and division, replacing manual methods. This solution delivers timely finance, sales, and marketing insights while freeing teams from time-consuming manual data manipulation tasks.

3M used Azure Machine Learning, automated machine learning (AutoML), and the Many Models Solution Accelerator to train and score numerous machine learning models in parallel. This approach significantly shortened the company’s development cycle time.

Then, 3M integrated Microsoft Power BI into their solution to compile and visualize results more effectively. Executives now have an unbiased and unfiltered view of data for better decision-making. With its improved ability to forecast sales, 3M can focus on manufacturing the products it needs efficiently.

3M’s success demonstrates how combining various Azure services can enhance efficiency in the manufacturing industry. These apps improve operational processes while enabling employees to focus on higher-level tasks. Strategic decisions are more targeted, and business conversations are more meaningful.

Driving Innovation: Intelligent Apps in the Technology Industry

As expected, the tech industry is at the forefront of leveraging Intelligent Apps to drive innovation. Elastic saw an opportunity to use intelligent apps to optimize its search services and meet customer needs more effectively. The company built a managed, secure, and scalable solution for its customers, Elastic Cloud, by leveraging Microsoft Azure.

Elastic Cloud uses Azure Machine Learning and other Azure AI Services to automate many basic management tasks, such as backing up data, upgrading, and scaling deployments. This approach enables Elastic’s customers to focus less on managing the software and more on serving their own clients. Customers can quickly deploy and manage Elastic Cloud on Kubernetes (ECK) using Azure Kubernetes Service.

As a result, Elastic’s cloud business is growing twice as fast as its on-premises solutions. Enterprise search users have reported significant improvements in system availability and employee satisfaction.

Additionally, Elastic incorporated Azure OpenAI Service into its Elasticsearch Relevance Engine (ESRE), providing more relevant answers to search queries. ESRE enables text, vector, and hybrid search, offering a more effective and accurate response to queries.

Elastic’s success story demonstrates how combining various Azure services can create intelligent Apps to drive innovation in the tech industry. These apps help improve operational processes and deliver better results quickly.

Revolutionizing Healthcare: Intelligent Apps in Medicine

Intelligent apps also drive transformative changes in the healthcare sector. Cambridgeshire and Peterborough NHS Foundation Trust, a healthcare provider in the United Kingdom, gradually shifted their on-premises systems to the cloud using Azure, improving flexibility, and lowering costs. They needed a solution for their electronic patient record (EPR) system that integrates 23 years of data across multiple systems and formats.

The Trust used Microsoft Azure and Azure Cognitive Search, including semantic search for understanding intent and contexts and cognitive capabilities for extracting insights from data, to standardize and automate data management. This approach made legacy, archive, and live data more discoverable, saving clinicians’ time. The Trust’s solution searches through millions of clinical documents in seconds, even recognizing handwritten notes without needing a separate optical character recognition (OCR) solution to extract text from images (Azure Cognitive Search automatically does it). Clinicians can now uncover previously inaccessible insights, enabling them to offer more personalized care.

The Trust also used Integration Services tools such as Azure Logic Apps to create workflows without writing code and Event Grid to connect Azure and third-party services through a publisher-subscriber model. They switched on a live feed from their new EPR system to Azure to feed real-time information into other applications. Researchers can now access anonymized data to fuel medical innovations beyond the NHS.

The healthcare provider also invested heavily in Power BI’s data visualization and analytical functions to sift through massive amounts of information. This level of data accessibility was previously impossible with their on-premises system. The new insights have significantly improved clinicians’ efficiency for better patient care.

Data accessibility and security are critical in the healthcare industry. Intelligent Apps empower organizations like the Trust to revolutionize medical care. AI capabilities helped the Trust improve its operational efficiency and user experiences, setting a new standard for healthcare in the digital age.

Summary

AI is driving transformation and real-world successes across industries. Intelligent apps are reshaping our world by streamlining operations in manufacturing and logistics, enhancing customer experience in the retail sector, improving decision-making in finance, driving innovation in tech, and revolutionizing healthcare.

As organizations adopt AI, they’re innovating in ways that were once too difficult, expensive, or outright impossible. Companies that wait to embrace intelligent apps risk the chance of falling behind. By embracing intelligent apps and leveraging the capabilities of Azure, you can harness the transformative potential of AI to remain on the cutting edge of technology.

Exercise

+ + \ No newline at end of file diff --git a/30daysofIA/tags/30-days-of-ia/page/8/index.html b/30daysofIA/tags/30-days-of-ia/page/8/index.html index cc8e5203d1..db9b68e6e9 100644 --- a/30daysofIA/tags/30-days-of-ia/page/8/index.html +++ b/30daysofIA/tags/30-days-of-ia/page/8/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "30-days-of-IA" | Build Intelligent Apps On Azure +11 posts tagged with "30-days-of-IA" | Build Intelligent Apps On Azure @@ -14,14 +14,13 @@ - - + +
-

9 posts tagged with "30-days-of-IA"

View All Tags

· 4 min read
It's 30DaysOfIA

Continue The Learning Journey through Fall For Intelligent Apps! 🍂

What We'll Cover

Thank you! ♥️

image

It's hard to believe that JavaScript on Azure hack-together is ending! It seems like just yesterday that we launched this initiative, and yet here we are, 15 days later, with an incredible amount of learning and growth behind us. So... it's time for a wrap!

From the bottom of our hearts, we want to thank each and every one of you for your participation, engagement, and enthusiasm. It's been truly inspiring to see the passion and dedication from this strong community, and we're honored to be a part of it. ✨

Recap of The JavaScript on Azure Global Hack-Together

As we wrap up this exciting event, we wanted to take a moment to reflect on all that we've accomplished together. Over the last 15 days, we've covered a lot of ground, from the basics of contributing to Open source to the exploration of the Contoso Real Estate project from its Frontend to its Backend and future AI implementation.

Now that the hack-together is ending, we want to make sure that you have all the resources you need to continue honing your skills in the future. Whether you're looking to make your fist contribution to open source, become an open source maintainers, collaborate with others, or simply keep learning, there are plenty of resources out there to help you achieve your goals. So, let's dive in and explore all the ways you can continue to grow your JavaScript skills on Azure!

JSonAzure Hack-together Roadmap 📍:

hack-together-roadmap (2)

Recap on past Livestreams🌟:

Day 1️⃣: Opening Keynote (Hack-together Launch): Introduction to the Contoso Real Estate Open-source project!, managing complex and complex enterprise architecture, new announcements for JavaScript developers on Azure

Day 2️⃣: GitHub Copilot & Codespaces: Introduction to your AI pair programmer (GitHub Copilot) and your virtual developer environment on the cloud (GitHub Codespaces)

Day 6️⃣: Build your Frontend using Static Web Apps as part of a complex, modern composable frontends (or micro-frontends) and cloud-native applications.

Day 9️⃣: Build a Serverless Backend using Azure Functions

Day 1️⃣3️⃣: Easily connect to an Azure Cosmos DB, exploring its benefits and how to get started

Day 1️⃣5️⃣: Being in the AI Era, we dive into the Azure OpenAI Service and how you can start to build intelligent JavaScript applications

📖 Self-Learning Resources

  1. JavaScript on Azure Global Hack Together Module collection
  2. Lets #HackTogether: Javascript On Azure Keynote
  3. Step by Step Guide: Migrating v3 to v4 programming model for Azure Functions for Node.Js Application

Continue your journey with #FallForIntelligentApps

Join us this Fall on a learning journey to explore building intelligent apps. Combine the power of AI, cloud-native app development, and cloud-scale data to build highly differentiated digital experiences. Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure. Engage in a self-paced learning adventure by following along the #30Days of Intelligent Apps series, completing the Intelligent Apps Skills Challenge, joining the Product Group for a live Ask The Expert series or building an end to end solution architecture with a live guided experience through the Learn Live series. Discover more here.

Hands-on practice: Make your first contribution to open source!

Join our GitHUb Discussion Forum to connect with developers from every part of world, see contributions from other, find collaborators and make your first contribution to a real-world project! -Don't forget to give the repo a star ⭐

Resources

All resources are accessible on our landing page

- - +
Skip to main content

11 posts tagged with "30-days-of-IA"

View All Tags

· 8 min read
It's 30DaysOfIA

Let’s start with understanding the power of intelligent applications.

What We'll Cover:

  • What are intelligent applications?
  • Categories of Intelligent Apps
  • Breaking down the role of AI in intelligent apps

A decorative header image for the blog

Demystifying Intelligent Applications: Leveraging AI in App Development

Until recent years, when you heard the term “artificial intelligence” (AI), you might have pictured autonomous robots from sci-fi films. Now, perhaps your mind jumps to big data and machine learning (ML). Maybe you’re even thinking about the latest ChatGPT plugins and transformer architectures. Today, AI is more than these imaginations: it’s a reality increasingly woven into our day-to-day routines through intelligent applications.

Intelligent apps combine cloud-scale compute, data, and predictive or generative AI. These applications help you automate tasks and make data-driven decisions, aiding in leveraging business intelligence to gain meaningful, practical outcomes. 

For developers, the AI landscape isn’t an abstract complexity. It’s a real, tangible opportunity for evolving traditional applications, making them “intelligent,” and delivering more value to users. And with tools like Azure Kubernetes Service, Azure Cosmos DB, and Azure AI Services, stepping into the world of intelligent apps isn’t a steep climb but a steady trek across a manageable learning curve.

The Three Categories of Intelligent Applications

Before we start building, it’s critical to understand what makes an app “intelligent.” It’s not a universal definition so much as an identifier for apps that fall into one or more of the three primary categories: 

  • Outcome-based — This designation represents the broadest category of intelligent apps, whose principal value for users is the intelligent outcomes they provide. These apps process data to provide insightful results that enhance decision-making or help with tasks like trend prediction, resource management, and customer relationship management.

  • Functionality-based — This type of intelligent app takes AI integration a degree further, integrating AI as a function of the app to create or process its outputs, enhancing the app’s overall interactivity and usefulness.

  • Feature-based — This is the narrowest category, in which the app’s AI/ML component is one of its core features and primary selling points

Let’s dive more deeply into each category.

Outcome-Based Apps: Intelligent Outcomes Drive Success

Outcome-based applications are all about the results provided to the user. They function beyond merely processing data to offer insightful outcomes that drive success.

Think of a personal fitness tracker app that uses AI to analyze the user’s activity and provides actionable recommendations for achieving a goal. This type of app not only tracks your steps, sleep patterns, and heart rate but uses that data to provide you with personalized health advice. It helps you make informed lifestyle choices — the “intelligent” outcome.

Another example is a social media marketing app that uses AI for time-saving data analysis, task automation, and trend prediction. AI-based sentiment analysis can gauge customer perception while natural language processing (NLP) accurately interprets customer inquiries. Task automation can schedule posts and automate responses to common customer queries, and trend forecasting helps model and predict customer behavior to further personalize targeted promotional efforts. Building this type of intelligence into your apps provides cost-effective, actionable insights that guide sales and marketing strategies.

Functionality-Based Apps: AI-Driven Features Enhance User Experiences

As the name suggests, functionality-based applications integrate AI into how the app functions, but the end user drives the action and outcome. In this category of apps, AI takes a more active role in the user’s interaction by allowing them to use AI-created or processed output. These apps leverage AI technologies like Azure AI Services for NLP, image recognition, and pattern recognition to improve user experience.

Consider a music app that generates personalized playlists based on your listening habits or your smartphone’s camera app that uses AI to recognize faces and optimize photo settings. Even more advanced are the language translation apps that translate text from one language to another and can even understand and maintain the context and subtleties of the conversation. They use NLP to accomplish these feats.

Similarly, consider the latest e-commerce apps, whose AI-driven functionalities enable customers to try on clothes virtually, significantly enhancing the online shopping experience.

Feature-Based Apps: Integrating Advanced AI/ML Components

Finally, there are feature-based intelligent apps. These apps incorporate advanced AI/ML components like neural networks, internal large language models (LLMs), and sophisticated algorithms as core features for the user. In this type of application, AI is the primary feature for users to engage with and the primary selling point for the app.

Some examples of feature-based applications are chatbots and virtual agents built using Azure Bot Service. These robo-representatives use AI to provide more natural and human-like conversation, balancing superior efficiency with a friendlier, more personalized user experience. The new Bing is a prime example of this implementation, integrating AI to give users a knowledgeable copilot for web searches.

Similarly, OpenAI’s ChatGPT allows users access to its state-of-the-art language models available as Microsoft’s Azure OpenAI Service. It leverages the power of the highly sophisticated GPT-3 LLM to perform tasks like drafting emails, writing code, answering general knowledge questions, translating between languages, and tutoring in numerous subjects.

These apps don’t just use AI to provide outputs or improve user experiences: They push the boundaries of what’s possible in app development by integrating AI/ML components.

info

Register for the live Q&A Ask The Expert session on September 26 with the Azure Functions product engineering team.

Breaking Down the Role of AI in Intelligent Applications

Intelligent apps reflect the merging of traditional software design with the dynamic capabilities of AI. Let’s explore how AI integrates and amplifies these applications.

At its core, AI augments applications with the capacity to self-learn and self-improve. It allows you to create applications that predict, react, and adapt to a constantly changing environment. In the power grid management system for a major city, an AI-infused application can forecast power usage trends, react to anomalies, and adapt to sudden demand surges, ensuring optimal efficiency and preventing blackouts.

But AI’s role doesn’t end at prediction or decision-making: It also automates tasks. In enterprise solutions, it has moved beyond simple automation to encompass intelligent process automation (IPA). In this arena, AI algorithms understand, learn, and then automate entire digital workflows. For developers, this is a fertile ground for creating applications that can dramatically improve the operational efficiency of businesses.

Even more, AI substantially aids in development tasks. For example, there’s GitHub Copilot, an AI-powered code assistant that offers suggestions based not only on syntax but on development best practices and the intent of the code. AI has evolved from a tool you use to build apps to a partner that helps in creating them. 

Using AI to build intelligent apps isn’t about throwing out what’s worked before. It’s about enhancing the components that have the most impact and achieve the best results. Incorporating AI in apps elevates their predictive abilities, streamlines complex tasks, and even aids in the development and creation process. When you build intelligent apps, you don’t just address current problems. You anticipate future challenges and craft solutions that adapt over time.

Summary

As developers the power of AI is in our hands, and it’s our responsibility to harness its full potential. Each category tells the story of AI's transformative potential, from applications that deliver intelligent outcomes to those with AI capabilities to enrich the user experience and those pushing boundaries with advanced AI/ML technology.

Accessible and robust platforms like Azure AI can simplify the process of developing Intelligent Applications. Whether you’re refining an existing app or building a new one, just entering the world of AI, or already familiar with the landscape, take a moment to appreciate the potential that AI integration offers. You’re embracing this exciting frontier and shaping the future of app development. 

Exercise

  • Complete the Cloud Skills Challenge to build on your apps and AI skills.
  • Watch Episode 01 of the Serverless Edition Learn Live session to learn how to build an end-to-end intelligent app solution.
+ + \ No newline at end of file diff --git a/30daysofIA/tags/30-days-of-ia/page/9/index.html b/30daysofIA/tags/30-days-of-ia/page/9/index.html index 757f29a4f6..5ecc023724 100644 --- a/30daysofIA/tags/30-days-of-ia/page/9/index.html +++ b/30daysofIA/tags/30-days-of-ia/page/9/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "30-days-of-IA" | Build Intelligent Apps On Azure +11 posts tagged with "30-days-of-IA" | Build Intelligent Apps On Azure @@ -14,13 +14,14 @@ - - + +
-

9 posts tagged with "30-days-of-IA"

View All Tags

· 2 min read
It's 30DaysOfIA

September is almost here - and that can only mean one thing!! It's time to 🍂 Fall for something new and exciting and spend a few weeks skilling up on relevant tools, techologies and solutions!!

Last year, we focused on #ServerlessSeptember. This year, we're building on that theme with the addition of cloud-scale Data, cloud-native Technologies and cloud-based AI integrations to help you modernize and build intelligent apps for the enterprise!

Watch this space - and join us in September to learn more!

- - +
Skip to main content

11 posts tagged with "30-days-of-IA"

View All Tags

· 4 min read
It's 30DaysOfIA

This Fall focus on building intelligent apps using AI and cloud-native technologies. #FallForIntelligentApps brings to you a learning journey to build your skills on creating differentiated experiences while modernizing your applications. It’s time to learn it all.

What We'll Cover

image

Get Ready To #FallForIntelligentApps starting September 18!

Today, we kick off the Fall season with content and activities to skill you up on all things Intelligent Apps or AI Apps on Azure with content, events, and community interactions! Read on to learn about what is coming!

Explore Our Initiatives

We have a number of initiatives planned for the month to help you learn and skill up on relevant technologies. Click on the links to visit the relevant pages for each

image

Register for the events!

What are 4 things you can do today, to jumpstart your learning journey?

#30Days Of Intelligent Apps

#30DaysOfIA is a series of daily blog posts grouped into themed weeks - taking you from core concepts to end-to-end solution examples in 30 days. Each article will provide conceptual lessons paired with exercises and resources to help you reinforce learnings and take next steps.

This series takes you through learning journey in six stages, each building on the previous week to help you skill up in a beginner-friendly way:

We will start with defining intelligent apps and then expand on how to build with cloud-native technologies like Azure Kubernetes Service, Azure Container Apps and Azure Functions, and take you through end-to-end scenarios for real world application development. Before we dive deep on intelligent apps, here is a high-level overview of the Intelligent Apps landscape on Azure:

image

Containers on Azure services offer you a wide range of capabilities, from simplicity to control to suit your different needs.

image +image

To start with the basics for developing Kubernetes applications, explore #30DaysOfCloudNative.

Cloud-native development when paired with serverless computing enhances your solution architecture for building cost optimized, resilient applications.

image

To start with the basics for serverless computing, explore #30DaysOfServerless.

Let’s Get Started

Now you know everything! We hope you are as excited as we are to dive into a full month of active learning and doing! Don't forget to subscribe for updates in your favorite feed reader! And look out for our first Intelligent Apps post Monday!

+ + \ No newline at end of file diff --git a/30daysofIA/tags/ask-the-expert/index.html b/30daysofIA/tags/ask-the-expert/index.html index a4f4f6d8bf..a46d55b2a5 100644 --- a/30daysofIA/tags/ask-the-expert/index.html +++ b/30daysofIA/tags/ask-the-expert/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "ask-the-expert" | Build Intelligent Apps On Azure +11 posts tagged with "ask-the-expert" | Build Intelligent Apps On Azure @@ -14,13 +14,13 @@ - - + +
-

9 posts tagged with "ask-the-expert"

View All Tags

· 10 min read
It's 30DaysOfIA

Prepare your organization to adopt intelligent apps by taking a change management approach. Assess your readiness, upskill teams, create a culture of change and overcome resistance, and prepare for future evolution to pave the way for a smooth transition into an AI-centric company.

What We'll Cover:

  • Role of change management in modernizing to intelligent apps
  • Skills needed for building intelligent apps in your organization
  • How to build a culture of change to embrace AI
  • How to prepare for the future of intelligent apps and continuous evolution

image of modernizing AI solutions for intelligent apps

Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management

As we’ve discussed throughout this series, intelligent apps and generative AI are rewriting the rules of business and application development. The first article of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” explored how intelligent apps can enhance user experiences and drive businesses growth. These advancements are opening new opportunities and markets for companies ready to embrace them.

However, embracing this digital transformation is more complex than flipping a switch. It requires adjustments to the technical side, which we explored in the series’ fifth article, “Preparing the Path for Intelligent Apps: Transitioning from On-Premises/IaaS to Cloud Native Applications.” It also demands a shift in how organizations adapt to change and approach app design and development. Let’s explore that mindset transition with tips to help you make it happen.

The Role of Change Management in Transitioning to Intelligent Apps

Transitioning to intelligent apps isn’t merely a technological change. It’s a profound shift in how businesses perceive and operate. This move toward artificial intelligence (AI)-centric approach isn’t meant to replace humans with machines or radically overhaul all business operations.

Instead, intelligent apps should augment human capabilities, streamline processes, and foster an environment where innovation flourishes. They expand toolkits and techniques to include AI-assisted development tools like GitHub Copilot and AI-enabled workflows, helping us design and deliver AI-enhanced experiences.

Effective change management strategies are crucial in helping employees adapt to these new ways of working with intelligent apps, ensuring that the transition doesn’t disrupt productivity or morale.

Team and organization strategies should: 

  • Assess the impact of any proposed changes.  
  • Communicate the benefits and reasons behind the change to all affected team members.  
  • Provide the necessary training and support.  
  • Integrate the changes into the culture and daily operations. 

By incorporating this robust change management approach, you can identify and mitigate potential transition risks early. Moreover, this strategy ensures that employees are engaged throughout the process, fostering a sense of involvement and ownership. This, in turn, cultivates a culture suited for innovation through intelligent apps. 

These organizational changes focus on the following aspects: organizational readiness, skillset, culture of change and overcoming resistance, and adaptability. Let’s explore them below.

info

Register for the intelligent apps webinar on Driving Business Value by Modernizing with Cloud-Native & AI with Microsoft and Forrester on September 26. 

Explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries. 

Identifying Organizational Readiness for Intelligent Apps

The first step in preparing your organization for intelligent apps is to assess your current readiness for change. Analyze various elements of your organization, from infrastructure and workforce skills to strategic vision and overall culture.

Can your apps and systems integrate with AI platforms like Microsoft Azure AI? Is your workforce comfortable working alongside AI, or are they fearful and resistant? Does your strategic vision incorporate AI’s transformative potential, or does it fail to acknowledge AI as a critical driver of future growth?

These questions identify the challenges of integrating AI into an organization and determine the company’s readiness for intelligent apps.

Embracing intelligent apps involves more than being ready with your technological infrastructure or having the right technology or tools. You need flexible organizational structures and processes driving your business to accommodate new ideas and capabilities. Your teams must feel empowered to own and drive them. Much like Microsoft’s shift to AI-powered services—like AI-powered Bing and Edge and Microsoft 365 Copilot—embracing AI organization-wide requires a holistic vision and willingness to innovate, with teams capable of delivering on the plan.

After accessing these critical areas for organizational readiness, you can start planning a strategy for this transition to an AI-ready organization. Identify skill gaps and work to bridge them while nurturing an environment that appreciates and harnesses the potential of AI and intelligent apps.

Nurturing the Indispensable Skills for Intelligent Apps

Tasking your AI specialists with preparing your organization to use and build intelligent apps isn’t enough. Accomplishing this goal requires a cross-functional effort and a diverse skill set.

Developing intelligent apps involves data scientists managing and interpreting vast amounts of data, machine learning engineers building and training the models, and traditional developers integrating these models into usable applications. However, the impact of generative AI spans beyond creating apps. AI fluency across the entire organization—from sales and marketing to accounting and legal departments—offers substantial benefits, sparking new ideas and solutions from sometimes unexpected sources.

Teams' AI skills might roughly fall into three levels: 

  • Fundamental skills — Being familiar and efficient with using AI tools, for example, knowing how to write effective prompts for large language models (LLMs).
  • Visionary skills — Recognizing AI’s strengths and weaknesses, for example, its creativity and hallucinations, that is, unexpected output.
  • Expert skills — Managing data and training models and integrating AI technology into apps, such as incorporating the OpenAI application programming interface (API) into an Intelligent App.

Based on these levels, you can gauge your team's skill readiness to pinpoint the most significant gaps, whether in general AI knowledge across your organization or within technical development teams.

As intelligent apps become increasingly prevalent, upskilling your workforce is crucial. This transition opens a new realm of skills and capabilities for your team to master. You can offer machine learning, data analysis, and cloud computing training programs—such as Microsoft’s Azure AI Fundamentals certification—to empower your team to confidently navigate the intelligent apps landscape.

Nurturing these skills within your organization may require hiring new talent, reskilling current employees, and creating an environment that promotes learning and experimentation. Specifically, if your team understands tools like the Microsoft Azure AI platform, they could contribute significantly to developing and maintaining intelligent apps.

An upskilled team is more than a functional unit. It’s a think tank to drive innovation and improve your organization’s competitive standing in the digital marketplace.

Building a Culture of Change to Embrace AI

Even with the right people and skills in place, change remains an uphill battle for organizations committed to traditional processes. 

Transitioning to intelligent apps is as much about mindset as technology and skills. It requires fostering a culture open to change and innovation that understands AI’s transformative potential and embraces it to enhance rather than replace human capabilities. 

Leaders play a pivotal role in cultivating this mindset. As they model a positive attitude toward AI, emphasize its benefits, and address any concerns transparently, leaders significantly contribute to the organization’s readiness to adopt intelligent apps. 

The most critical factor in this transition is enabling an AI-friendly culture. It’s about encouraging curiosity, rewarding innovative thinking, and creating an environment where people thrive on learning and experimentation. 

Empowering teams to leverage practical AI tools and AI-driven data insights to aid decision-making helps motivate employees to use and explore AI’s potential, improve their skills, and contribute to the successful integration of intelligent apps. Such an environment is fertile ground to grow breakthrough ideas and innovative solutions. 

Overcoming Resistance to Change and Adopting Intelligent Apps

Despite AI’s potential, change isn’t easy. It takes effort and is especially challenging for organizations used to existing processes. This resistance to change is a significant roadblock when transitioning to intelligent apps. 

Overcoming this resistance involves:  

  • Understanding employees’ concerns. 
  • Challenging underlying assumptions about traditional systems and outdated processes that hinder change. 
  • Providing comprehensive training for workers to use AI effectively.
  • Demonstrating the benefits and opportunities of intelligent apps. 
  • Clear and continuous communication to dispel fears and misconceptions about AI.

These steps will foster greater acceptance and enthusiasm for intelligent apps in your teams. 

Preparing for the Future of Intelligent Apps and Continuous Evolution

Finally, it’s essential to understand that generative AI is just beginning to gain momentum. Adjusting to a world of intelligent apps isn’t a one-time change—it’s a continuous evolution. 

As AI technologies mature and evolve and more businesses realize their benefits, we can expect the landscape of intelligent apps to expand further. Organizations can cultivate a mindset of continuous learning, flexibility, and adaptation to prepare for this future. Staying ahead in this dynamic field requires organizations to keep up with the latest AI advancements, continually reassess their strategies, and pivot when necessary. 

The domain of intelligent apps is rapidly advancing. Organizations must prepare to navigate this dynamic landscape to stay ahead of the curve.

Summary

Preparing for AI and intelligent apps is a strategic decision. It requires thoughtful preparation and a culture that embraces change.

With the proper preparation and mindset, adopting AI can become a smoother, collaborative path to innovation for the whole organization. Integrating AI into your organization does not discard what has worked in the past. It simply enhances and augments those processes for a better future.

Consider the points above to assess your organization’s readiness and prepare to welcome intelligent apps.

In the first week of this series, you learned about intelligent apps, how organizations today find success through intelligent apps, what AI-assisted application development looks like, ways to leverage the power of generative AI, the technical changes needed to prepare for intelligent apps, and, in this article, the organizational changes required to harness the power of AI.

The future is intelligent. Use this series as a guideline to navigate challenges while preparing for and embracing AI's opportunities for an intelligent tomorrow.

Exercise

- - +
Skip to main content

11 posts tagged with "ask-the-expert"

View All Tags

· 10 min read
It's 30DaysOfIA

Prepare your organization to adopt intelligent apps by taking a change management approach. Assess your readiness, upskill teams, create a culture of change and overcome resistance, and prepare for future evolution to pave the way for a smooth transition into an AI-centric company.

What We'll Cover:

  • Role of change management in modernizing to intelligent apps
  • Skills needed for building intelligent apps in your organization
  • How to build a culture of change to embrace AI
  • How to prepare for the future of intelligent apps and continuous evolution

image of modernizing AI solutions for intelligent apps

Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management

As we’ve discussed throughout this series, intelligent apps and generative AI are rewriting the rules of business and application development. The first article of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” explored how intelligent apps can enhance user experiences and drive businesses growth. These advancements are opening new opportunities and markets for companies ready to embrace them.

However, embracing this digital transformation is more complex than flipping a switch. It requires adjustments to the technical side, which we explored in the series’ fifth article, “Preparing the Path for Intelligent Apps: Transitioning from On-Premises/IaaS to Cloud Native Applications.” It also demands a shift in how organizations adapt to change and approach app design and development. Let’s explore that mindset transition with tips to help you make it happen.

The Role of Change Management in Transitioning to Intelligent Apps

Transitioning to intelligent apps isn’t merely a technological change. It’s a profound shift in how businesses perceive and operate. This move toward artificial intelligence (AI)-centric approach isn’t meant to replace humans with machines or radically overhaul all business operations.

Instead, intelligent apps should augment human capabilities, streamline processes, and foster an environment where innovation flourishes. They expand toolkits and techniques to include AI-assisted development tools like GitHub Copilot and AI-enabled workflows, helping us design and deliver AI-enhanced experiences.

Effective change management strategies are crucial in helping employees adapt to these new ways of working with intelligent apps, ensuring that the transition doesn’t disrupt productivity or morale.

Team and organization strategies should: 

  • Assess the impact of any proposed changes.  
  • Communicate the benefits and reasons behind the change to all affected team members.  
  • Provide the necessary training and support.  
  • Integrate the changes into the culture and daily operations. 

By incorporating this robust change management approach, you can identify and mitigate potential transition risks early. Moreover, this strategy ensures that employees are engaged throughout the process, fostering a sense of involvement and ownership. This, in turn, cultivates a culture suited for innovation through intelligent apps. 

These organizational changes focus on the following aspects: organizational readiness, skillset, culture of change and overcoming resistance, and adaptability. Let’s explore them below.

info

Register for the intelligent apps webinar on Driving Business Value by Modernizing with Cloud-Native & AI with Microsoft and Forrester on September 26. 

Explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries. 

Identifying Organizational Readiness for Intelligent Apps

The first step in preparing your organization for intelligent apps is to assess your current readiness for change. Analyze various elements of your organization, from infrastructure and workforce skills to strategic vision and overall culture.

Can your apps and systems integrate with AI platforms like Microsoft Azure AI? Is your workforce comfortable working alongside AI, or are they fearful and resistant? Does your strategic vision incorporate AI’s transformative potential, or does it fail to acknowledge AI as a critical driver of future growth?

These questions identify the challenges of integrating AI into an organization and determine the company’s readiness for intelligent apps.

Embracing intelligent apps involves more than being ready with your technological infrastructure or having the right technology or tools. You need flexible organizational structures and processes driving your business to accommodate new ideas and capabilities. Your teams must feel empowered to own and drive them. Much like Microsoft’s shift to AI-powered services—like AI-powered Bing and Edge and Microsoft 365 Copilot—embracing AI organization-wide requires a holistic vision and willingness to innovate, with teams capable of delivering on the plan.

After accessing these critical areas for organizational readiness, you can start planning a strategy for this transition to an AI-ready organization. Identify skill gaps and work to bridge them while nurturing an environment that appreciates and harnesses the potential of AI and intelligent apps.

Nurturing the Indispensable Skills for Intelligent Apps

Tasking your AI specialists with preparing your organization to use and build intelligent apps isn’t enough. Accomplishing this goal requires a cross-functional effort and a diverse skill set.

Developing intelligent apps involves data scientists managing and interpreting vast amounts of data, machine learning engineers building and training the models, and traditional developers integrating these models into usable applications. However, the impact of generative AI spans beyond creating apps. AI fluency across the entire organization—from sales and marketing to accounting and legal departments—offers substantial benefits, sparking new ideas and solutions from sometimes unexpected sources.

Teams' AI skills might roughly fall into three levels: 

  • Fundamental skills — Being familiar and efficient with using AI tools, for example, knowing how to write effective prompts for large language models (LLMs).
  • Visionary skills — Recognizing AI’s strengths and weaknesses, for example, its creativity and hallucinations, that is, unexpected output.
  • Expert skills — Managing data and training models and integrating AI technology into apps, such as incorporating the OpenAI application programming interface (API) into an Intelligent App.

Based on these levels, you can gauge your team's skill readiness to pinpoint the most significant gaps, whether in general AI knowledge across your organization or within technical development teams.

As intelligent apps become increasingly prevalent, upskilling your workforce is crucial. This transition opens a new realm of skills and capabilities for your team to master. You can offer machine learning, data analysis, and cloud computing training programs—such as Microsoft’s Azure AI Fundamentals certification—to empower your team to confidently navigate the intelligent apps landscape.

Nurturing these skills within your organization may require hiring new talent, reskilling current employees, and creating an environment that promotes learning and experimentation. Specifically, if your team understands tools like the Microsoft Azure AI platform, they could contribute significantly to developing and maintaining intelligent apps.

An upskilled team is more than a functional unit. It’s a think tank to drive innovation and improve your organization’s competitive standing in the digital marketplace.

Building a Culture of Change to Embrace AI

Even with the right people and skills in place, change remains an uphill battle for organizations committed to traditional processes. 

Transitioning to intelligent apps is as much about mindset as technology and skills. It requires fostering a culture open to change and innovation that understands AI’s transformative potential and embraces it to enhance rather than replace human capabilities. 

Leaders play a pivotal role in cultivating this mindset. As they model a positive attitude toward AI, emphasize its benefits, and address any concerns transparently, leaders significantly contribute to the organization’s readiness to adopt intelligent apps. 

The most critical factor in this transition is enabling an AI-friendly culture. It’s about encouraging curiosity, rewarding innovative thinking, and creating an environment where people thrive on learning and experimentation. 

Empowering teams to leverage practical AI tools and AI-driven data insights to aid decision-making helps motivate employees to use and explore AI’s potential, improve their skills, and contribute to the successful integration of intelligent apps. Such an environment is fertile ground to grow breakthrough ideas and innovative solutions. 

Overcoming Resistance to Change and Adopting Intelligent Apps

Despite AI’s potential, change isn’t easy. It takes effort and is especially challenging for organizations used to existing processes. This resistance to change is a significant roadblock when transitioning to intelligent apps. 

Overcoming this resistance involves:  

  • Understanding employees’ concerns. 
  • Challenging underlying assumptions about traditional systems and outdated processes that hinder change. 
  • Providing comprehensive training for workers to use AI effectively.
  • Demonstrating the benefits and opportunities of intelligent apps. 
  • Clear and continuous communication to dispel fears and misconceptions about AI.

These steps will foster greater acceptance and enthusiasm for intelligent apps in your teams. 

Preparing for the Future of Intelligent Apps and Continuous Evolution

Finally, it’s essential to understand that generative AI is just beginning to gain momentum. Adjusting to a world of intelligent apps isn’t a one-time change—it’s a continuous evolution. 

As AI technologies mature and evolve and more businesses realize their benefits, we can expect the landscape of intelligent apps to expand further. Organizations can cultivate a mindset of continuous learning, flexibility, and adaptation to prepare for this future. Staying ahead in this dynamic field requires organizations to keep up with the latest AI advancements, continually reassess their strategies, and pivot when necessary. 

The domain of intelligent apps is rapidly advancing. Organizations must prepare to navigate this dynamic landscape to stay ahead of the curve.

Summary

Preparing for AI and intelligent apps is a strategic decision. It requires thoughtful preparation and a culture that embraces change.

With the proper preparation and mindset, adopting AI can become a smoother, collaborative path to innovation for the whole organization. Integrating AI into your organization does not discard what has worked in the past. It simply enhances and augments those processes for a better future.

Consider the points above to assess your organization’s readiness and prepare to welcome intelligent apps.

In the first week of this series, you learned about intelligent apps, how organizations today find success through intelligent apps, what AI-assisted application development looks like, ways to leverage the power of generative AI, the technical changes needed to prepare for intelligent apps, and, in this article, the organizational changes required to harness the power of AI.

The future is intelligent. Use this series as a guideline to navigate challenges while preparing for and embracing AI's opportunities for an intelligent tomorrow.

Exercise

+ + \ No newline at end of file diff --git a/30daysofIA/tags/ask-the-expert/page/10/index.html b/30daysofIA/tags/ask-the-expert/page/10/index.html new file mode 100644 index 0000000000..6e3af61ab6 --- /dev/null +++ b/30daysofIA/tags/ask-the-expert/page/10/index.html @@ -0,0 +1,27 @@ + + + + + +11 posts tagged with "ask-the-expert" | Build Intelligent Apps On Azure + + + + + + + + + + + + + + +
+

11 posts tagged with "ask-the-expert"

View All Tags

· 4 min read
It's 30DaysOfIA

Continue The Learning Journey through Fall For Intelligent Apps! 🍂

What We'll Cover

Thank you! ♥️

image

It's hard to believe that JavaScript on Azure hack-together is ending! It seems like just yesterday that we launched this initiative, and yet here we are, 15 days later, with an incredible amount of learning and growth behind us. So... it's time for a wrap!

From the bottom of our hearts, we want to thank each and every one of you for your participation, engagement, and enthusiasm. It's been truly inspiring to see the passion and dedication from this strong community, and we're honored to be a part of it. ✨

Recap of The JavaScript on Azure Global Hack-Together

As we wrap up this exciting event, we wanted to take a moment to reflect on all that we've accomplished together. Over the last 15 days, we've covered a lot of ground, from the basics of contributing to Open source to the exploration of the Contoso Real Estate project from its Frontend to its Backend and future AI implementation.

Now that the hack-together is ending, we want to make sure that you have all the resources you need to continue honing your skills in the future. Whether you're looking to make your fist contribution to open source, become an open source maintainers, collaborate with others, or simply keep learning, there are plenty of resources out there to help you achieve your goals. So, let's dive in and explore all the ways you can continue to grow your JavaScript skills on Azure!

JSonAzure Hack-together Roadmap 📍:

hack-together-roadmap (2)

Recap on past Livestreams🌟:

Day 1️⃣: Opening Keynote (Hack-together Launch): Introduction to the Contoso Real Estate Open-source project!, managing complex and complex enterprise architecture, new announcements for JavaScript developers on Azure

Day 2️⃣: GitHub Copilot & Codespaces: Introduction to your AI pair programmer (GitHub Copilot) and your virtual developer environment on the cloud (GitHub Codespaces)

Day 6️⃣: Build your Frontend using Static Web Apps as part of a complex, modern composable frontends (or micro-frontends) and cloud-native applications.

Day 9️⃣: Build a Serverless Backend using Azure Functions

Day 1️⃣3️⃣: Easily connect to an Azure Cosmos DB, exploring its benefits and how to get started

Day 1️⃣5️⃣: Being in the AI Era, we dive into the Azure OpenAI Service and how you can start to build intelligent JavaScript applications

📖 Self-Learning Resources

  1. JavaScript on Azure Global Hack Together Module collection
  2. Lets #HackTogether: Javascript On Azure Keynote
  3. Step by Step Guide: Migrating v3 to v4 programming model for Azure Functions for Node.Js Application

Continue your journey with #FallForIntelligentApps

Join us this Fall on a learning journey to explore building intelligent apps. Combine the power of AI, cloud-native app development, and cloud-scale data to build highly differentiated digital experiences. Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure. Engage in a self-paced learning adventure by following along the #30Days of Intelligent Apps series, completing the Intelligent Apps Skills Challenge, joining the Product Group for a live Ask The Expert series or building an end to end solution architecture with a live guided experience through the Learn Live series. Discover more here.

Hands-on practice: Make your first contribution to open source!

Join our GitHUb Discussion Forum to connect with developers from every part of world, see contributions from other, find collaborators and make your first contribution to a real-world project! +Don't forget to give the repo a star ⭐

Resources

All resources are accessible on our landing page

+ + + + \ No newline at end of file diff --git a/30daysofIA/tags/ask-the-expert/page/11/index.html b/30daysofIA/tags/ask-the-expert/page/11/index.html new file mode 100644 index 0000000000..5fee8739e0 --- /dev/null +++ b/30daysofIA/tags/ask-the-expert/page/11/index.html @@ -0,0 +1,26 @@ + + + + + +11 posts tagged with "ask-the-expert" | Build Intelligent Apps On Azure + + + + + + + + + + + + + + +
+

11 posts tagged with "ask-the-expert"

View All Tags

· 2 min read
It's 30DaysOfIA

September is almost here - and that can only mean one thing!! It's time to 🍂 Fall for something new and exciting and spend a few weeks skilling up on relevant tools, techologies and solutions!!

Last year, we focused on #ServerlessSeptember. This year, we're building on that theme with the addition of cloud-scale Data, cloud-native Technologies and cloud-based AI integrations to help you modernize and build intelligent apps for the enterprise!

Watch this space - and join us in September to learn more!

+ + + + \ No newline at end of file diff --git a/30daysofIA/tags/ask-the-expert/page/2/index.html b/30daysofIA/tags/ask-the-expert/page/2/index.html index c63172db03..b0253b895d 100644 --- a/30daysofIA/tags/ask-the-expert/page/2/index.html +++ b/30daysofIA/tags/ask-the-expert/page/2/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "ask-the-expert" | Build Intelligent Apps On Azure +11 posts tagged with "ask-the-expert" | Build Intelligent Apps On Azure @@ -14,13 +14,13 @@ - - + +
-

9 posts tagged with "ask-the-expert"

View All Tags

· 9 min read
It's 30DaysOfIA

Learn best practices for modernizing your on-premises or IaaS solutions to intelligent apps in the cloud.

What We'll Cover:

  • Traditional vs Intelligent Apps
  • On-Premises/IaaS vs. Cloud-Native Platforms
  • Modernization strategy for building Intelligent Apps

image of modernizing AI solutions for intelligent apps

Preparing the Path for Intelligent Apps: Transitioning from On-Premises/IaaS to Cloud-Native Applications

The proliferation of generative AI is paving the way for intelligent applications, software that leverages AI capabilities to deliver unparalleled functionality and user experience. As we covered in a previous article, “Demystifying Intelligent Applications: Leveraging AI in App Development,” intelligent Apps aren’t just products of advanced coding. They interact, learn, and evolve thanks to modern AI and machine learning (ML) breakthroughs. 

The paradigm shift from traditional to intelligent apps demands that we change our approach to the technical challenges of software and architecture design. It also necessitates adopting new ways of thinking and operating within an organization, which we explore in greater depth in “Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management.” 

This article explores the technical infrastructure powering intelligent apps, providing a roadmap to transition traditional on-premises or Infrastructure as a Service (IaaS) solutions to intelligent apps deployed to cloud native platforms and services. We’ll discover the differences between conventional and intelligent apps, options for re-architecting existing applications, and critical strategic considerations integral to modernizing the application-building approach. 

Understanding the Shift: Traditional vs. Intelligent Applications

Intelligent apps are more than just an incremental evolution from traditional apps. They involve a fundamental shift in software solution design, development, and deployment. A clear understanding of how each paradigm differs is crucial for comprehending the impact of this transition. 

Functionality and User Experience

Traditional applications, often rule-based and rigid, rely on preprogrammed operations. A traditional weather app, for example, typically presents structured, location-based forecasts using a predetermined data source. Meanwhile, an intelligent app leverages AI for data-driven decision-making and personalization. Rather than merely displaying the forecast, it integrates ML with additional data sources—like a user’s calendar and fitness data—to determine their favorite outdoor activities, provide relevant clothing suggestions, and even automatically design ideal vacations and times based on the user’s optimal weather preferences. 

Similarly, while traditional applications tend to offer a one-size-fits-all UI, intelligent apps use AI to create tailored interactions. For instance, an intelligent banking app might use generative AI to provide a voice-enabled UI that lets customers use natural language to ask about transactions and receive personalized financial advice.

Infrastructure

Intelligent apps integrating these sophisticated features require highly scalable, flexible infrastructure optimized for AI functionality and able to manage large volumes of real-time data. Unlike traditional applications deployed on-premises or in infrastructure as a service (IaaS) environments, intelligent apps demand the flexibility and scalability of cloud native architectures.

An on-premises or IaaS-based e-commerce application may struggle with connecting its data to AI services or scaling to accommodate voluminous real-time data and requests. But an intelligent app built with a cloud native approach and scalability at its core can handle variable demand much more efficiently. It also maintains its infrastructure components on demand, whether for collecting data for AI/ML consumption or user-facing features like AI-enabled customer support and personalization.

Consider an intelligent app built on Azure that creates ChatGPT-like experiences with custom data. It might connect Azure Open AI and Azure Cognitive Search to an app running on Azure Container Apps or Azure Kubernetes Service. 

The architecture might look like the following diagram based on this demo app:

diagram of a demo app architecture

While the cloud is well-equipped to handle the challenges associated with shifting to AI-centric infrastructure—handling real-time data processing and storage, integrating and managing AI services, configuring event-driven and microservices architectures, and ensuring security across the distributed environment—re-architecting a traditional app to an intelligent app requires careful planning and execution to ensure it benefits fully from the capabilities of AI and cloud computing.

On-Premises/IaaS vs. Cloud-Native Platforms

While on-premises and IaaS solutions provide substantial control over data and processes, this fine-grained control comes with setup and maintenance costs and may lack the scalability necessary for AI-driven Intelligent Apps. On-premises hosting involves purchasing and maintaining hardware, including servers and storage systems. IaaS involves leasing virtual hardware and then hiring and training IT personnel for the infrastructure’s operation, maintenance, and security.

Conversely, infrastructure built on cloud native app services with Microsoft Azure offers virtually unlimited scalability without ongoing hardware and maintenance costs. This feature is ideal for intelligent apps that need to adapt and scale to accommodate fluctuating workloads and data streams. And because these cloud infrastructures typically operate on a pay-as-you-go model, traditionally substantial upfront costs turn into predictable operating expenses, lowering both the cost and risk of launching applications.

Moreover, cloud service providers continually update their offerings, so building on sophisticated AI tools and services through the cloud, like the Azure AI Platform and orchestration capabilities with Azure Kubernetes Service, ensures that you have access to the latest AI and most advanced AI models and technologies.

Strategic Considerations for Transitioning to Intelligent Apps

When developing a strategy for transitioning to intelligent apps, you must consider costs, personnel skill development, and data management.

Monolithic architectures are unlikely to support the scale and ease of iteration required by intelligent apps, so you’ll likely need to implement code changes to support the transition. Your chosen hosting solution and the amount of data your app will handle affect cost and performance, in addition to determining privacy and compliance requirements.

An app that manages or stores terabytes or petabytes of data will require storage-optimized virtual machines or a managed database service hosting. Meanwhile, an app that processes video content or performs resource-intensive calculations should prioritize high-performance computing. Maximizing storage and performance can become extraordinarily costly.

Finally, existing applications shifting towards intelligent apps may encounter compatibility conflicts, depending on database type or the existing system’s underlying programming language. As you might imagine, a traditional app running on-premises and using a shared local database may need significant restructuring to support a cloud native architecture. You need plans for data migration, potential downtime, and training.

Next, let’s look at this transformation process.

Re-Architecting Monolithic Apps into Microservices

Shifting from monolithic applications to microservices is pivotal for transitioning to Intelligent Apps, and you can use this shift as an opportunity to transform your legacy software into intelligent apps that leverage the flexibility and power of microservices-enabled AI solutions.

Traditionally, a monolithic application (let’s use an online marketplace as an example) might handle all activities—from serving webpages to processing payments—within an on-premises server or a cloud server. However, this approach is challenging to scale and remains vulnerable to network failures or cyberattacks.

Breaking these applications into multiple services with specific responsibilities, such as authentication or notifications, can overcome these limitations. These services are typically deployed as microservices within container environments like Azure Kubernetes Service or serverless platforms like Azure Container Apps or Azure Functions using automated processes. This approach offers scalability, robustness, and improved performance, as each microservice can run independently and use its own database.

Shifting towards smaller, independent microservices in app architectures allows us to reap the benefits of AI-powered intelligent apps, for example, by using microservices to handle AI tasks such as processing natural language input, analyzing behavioral data, or personalizing content.

info

Watch Episode 1 and Episode 2 of the Learn Live series on how to build, test and deploy an end-to-end intelligent app solution using the Contoso Real Estate Sample.

Event-Driven Architectures: Knowing When to Use Them

Event-Driven Architectures (EDAs) can significantly enhance the performance and cost-effectiveness of intelligent apps. EDAs, typically implemented with microservices, respond in real time to events or state changes ranging from user interactions to real-time analytics.

Consider an intelligent app designed for personalized customer engagement. Instead of designing the application to check continuously for changes in user data, an EDA can react to such changes immediately, triggering appropriate microservices. For example, if users modify their preferences, an event could trigger an AI-driven recommendation microservice to update its suggestions immediately. This real-time responsiveness, enabled by services like Azure Event Grid, can improve user experiences and make intelligent apps more adaptive and proactive.

However, it’s worth noting that while EDAs provide significant benefits, they can introduce unique complexities, such as maintaining event consistency and debugging issues in event chains across multiple microservices. Therefore, it’s important to assess your application’s needs so you can understand where it can benefit from an EDA.

Conclusion

Transitioning to intelligent apps represents a shift in strategy, architecture, and infrastructure. By designing and re-architecting for cloud-based infrastructure, your apps gain scalability, cost-effectiveness, robust data management, access to sophisticated AI tools, enhanced security, and reduced operational overhead.  

But this transition extends well beyond its technical components. Organizations and the people working within them need equal attention. You can explore these facets in greater depth in “Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management.” 

The power of generative AI is here, and preparing for the move toward cloud-based intelligent applications will ensure that your apps remain performative and scalable and—most importantly—that your business is ready for the newest era of app innovation.

Exercise

- - +
Skip to main content

11 posts tagged with "ask-the-expert"

View All Tags

· 9 min read
It's 30DaysOfIA

Learn best practices for modernizing your on-premises or IaaS solutions to intelligent apps in the cloud.

What We'll Cover:

  • Traditional vs Intelligent Apps
  • On-Premises/IaaS vs. Cloud-Native Platforms
  • Modernization strategy for building Intelligent Apps

image of modernizing AI solutions for intelligent apps

Preparing the Path for Intelligent Apps: Transitioning from On-Premises/IaaS to Cloud-Native Applications

The proliferation of generative AI is paving the way for intelligent applications, software that leverages AI capabilities to deliver unparalleled functionality and user experience. As we covered in a previous article, “Demystifying Intelligent Applications: Leveraging AI in App Development,” intelligent Apps aren’t just products of advanced coding. They interact, learn, and evolve thanks to modern AI and machine learning (ML) breakthroughs. 

The paradigm shift from traditional to intelligent apps demands that we change our approach to the technical challenges of software and architecture design. It also necessitates adopting new ways of thinking and operating within an organization, which we explore in greater depth in “Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management.” 

This article explores the technical infrastructure powering intelligent apps, providing a roadmap to transition traditional on-premises or Infrastructure as a Service (IaaS) solutions to intelligent apps deployed to cloud native platforms and services. We’ll discover the differences between conventional and intelligent apps, options for re-architecting existing applications, and critical strategic considerations integral to modernizing the application-building approach. 

Understanding the Shift: Traditional vs. Intelligent Applications

Intelligent apps are more than just an incremental evolution from traditional apps. They involve a fundamental shift in software solution design, development, and deployment. A clear understanding of how each paradigm differs is crucial for comprehending the impact of this transition. 

Functionality and User Experience

Traditional applications, often rule-based and rigid, rely on preprogrammed operations. A traditional weather app, for example, typically presents structured, location-based forecasts using a predetermined data source. Meanwhile, an intelligent app leverages AI for data-driven decision-making and personalization. Rather than merely displaying the forecast, it integrates ML with additional data sources—like a user’s calendar and fitness data—to determine their favorite outdoor activities, provide relevant clothing suggestions, and even automatically design ideal vacations and times based on the user’s optimal weather preferences. 

Similarly, while traditional applications tend to offer a one-size-fits-all UI, intelligent apps use AI to create tailored interactions. For instance, an intelligent banking app might use generative AI to provide a voice-enabled UI that lets customers use natural language to ask about transactions and receive personalized financial advice.

Infrastructure

Intelligent apps integrating these sophisticated features require highly scalable, flexible infrastructure optimized for AI functionality and able to manage large volumes of real-time data. Unlike traditional applications deployed on-premises or in infrastructure as a service (IaaS) environments, intelligent apps demand the flexibility and scalability of cloud native architectures.

An on-premises or IaaS-based e-commerce application may struggle with connecting its data to AI services or scaling to accommodate voluminous real-time data and requests. But an intelligent app built with a cloud native approach and scalability at its core can handle variable demand much more efficiently. It also maintains its infrastructure components on demand, whether for collecting data for AI/ML consumption or user-facing features like AI-enabled customer support and personalization.

Consider an intelligent app built on Azure that creates ChatGPT-like experiences with custom data. It might connect Azure Open AI and Azure Cognitive Search to an app running on Azure Container Apps or Azure Kubernetes Service. 

The architecture might look like the following diagram based on this demo app:

diagram of a demo app architecture

While the cloud is well-equipped to handle the challenges associated with shifting to AI-centric infrastructure—handling real-time data processing and storage, integrating and managing AI services, configuring event-driven and microservices architectures, and ensuring security across the distributed environment—re-architecting a traditional app to an intelligent app requires careful planning and execution to ensure it benefits fully from the capabilities of AI and cloud computing.

On-Premises/IaaS vs. Cloud-Native Platforms

While on-premises and IaaS solutions provide substantial control over data and processes, this fine-grained control comes with setup and maintenance costs and may lack the scalability necessary for AI-driven Intelligent Apps. On-premises hosting involves purchasing and maintaining hardware, including servers and storage systems. IaaS involves leasing virtual hardware and then hiring and training IT personnel for the infrastructure’s operation, maintenance, and security.

Conversely, infrastructure built on cloud native app services with Microsoft Azure offers virtually unlimited scalability without ongoing hardware and maintenance costs. This feature is ideal for intelligent apps that need to adapt and scale to accommodate fluctuating workloads and data streams. And because these cloud infrastructures typically operate on a pay-as-you-go model, traditionally substantial upfront costs turn into predictable operating expenses, lowering both the cost and risk of launching applications.

Moreover, cloud service providers continually update their offerings, so building on sophisticated AI tools and services through the cloud, like the Azure AI Platform and orchestration capabilities with Azure Kubernetes Service, ensures that you have access to the latest AI and most advanced AI models and technologies.

Strategic Considerations for Transitioning to Intelligent Apps

When developing a strategy for transitioning to intelligent apps, you must consider costs, personnel skill development, and data management.

Monolithic architectures are unlikely to support the scale and ease of iteration required by intelligent apps, so you’ll likely need to implement code changes to support the transition. Your chosen hosting solution and the amount of data your app will handle affect cost and performance, in addition to determining privacy and compliance requirements.

An app that manages or stores terabytes or petabytes of data will require storage-optimized virtual machines or a managed database service hosting. Meanwhile, an app that processes video content or performs resource-intensive calculations should prioritize high-performance computing. Maximizing storage and performance can become extraordinarily costly.

Finally, existing applications shifting towards intelligent apps may encounter compatibility conflicts, depending on database type or the existing system’s underlying programming language. As you might imagine, a traditional app running on-premises and using a shared local database may need significant restructuring to support a cloud native architecture. You need plans for data migration, potential downtime, and training.

Next, let’s look at this transformation process.

Re-Architecting Monolithic Apps into Microservices

Shifting from monolithic applications to microservices is pivotal for transitioning to Intelligent Apps, and you can use this shift as an opportunity to transform your legacy software into intelligent apps that leverage the flexibility and power of microservices-enabled AI solutions.

Traditionally, a monolithic application (let’s use an online marketplace as an example) might handle all activities—from serving webpages to processing payments—within an on-premises server or a cloud server. However, this approach is challenging to scale and remains vulnerable to network failures or cyberattacks.

Breaking these applications into multiple services with specific responsibilities, such as authentication or notifications, can overcome these limitations. These services are typically deployed as microservices within container environments like Azure Kubernetes Service or serverless platforms like Azure Container Apps or Azure Functions using automated processes. This approach offers scalability, robustness, and improved performance, as each microservice can run independently and use its own database.

Shifting towards smaller, independent microservices in app architectures allows us to reap the benefits of AI-powered intelligent apps, for example, by using microservices to handle AI tasks such as processing natural language input, analyzing behavioral data, or personalizing content.

info

Watch Episode 1 and Episode 2 of the Learn Live series on how to build, test and deploy an end-to-end intelligent app solution using the Contoso Real Estate Sample.

Event-Driven Architectures: Knowing When to Use Them

Event-Driven Architectures (EDAs) can significantly enhance the performance and cost-effectiveness of intelligent apps. EDAs, typically implemented with microservices, respond in real time to events or state changes ranging from user interactions to real-time analytics.

Consider an intelligent app designed for personalized customer engagement. Instead of designing the application to check continuously for changes in user data, an EDA can react to such changes immediately, triggering appropriate microservices. For example, if users modify their preferences, an event could trigger an AI-driven recommendation microservice to update its suggestions immediately. This real-time responsiveness, enabled by services like Azure Event Grid, can improve user experiences and make intelligent apps more adaptive and proactive.

However, it’s worth noting that while EDAs provide significant benefits, they can introduce unique complexities, such as maintaining event consistency and debugging issues in event chains across multiple microservices. Therefore, it’s important to assess your application’s needs so you can understand where it can benefit from an EDA.

Conclusion

Transitioning to intelligent apps represents a shift in strategy, architecture, and infrastructure. By designing and re-architecting for cloud-based infrastructure, your apps gain scalability, cost-effectiveness, robust data management, access to sophisticated AI tools, enhanced security, and reduced operational overhead.  

But this transition extends well beyond its technical components. Organizations and the people working within them need equal attention. You can explore these facets in greater depth in “Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management.” 

The power of generative AI is here, and preparing for the move toward cloud-based intelligent applications will ensure that your apps remain performative and scalable and—most importantly—that your business is ready for the newest era of app innovation.

Exercise

+ + \ No newline at end of file diff --git a/30daysofIA/tags/ask-the-expert/page/3/index.html b/30daysofIA/tags/ask-the-expert/page/3/index.html index 9ecfe5d231..2e20d6cae6 100644 --- a/30daysofIA/tags/ask-the-expert/page/3/index.html +++ b/30daysofIA/tags/ask-the-expert/page/3/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "ask-the-expert" | Build Intelligent Apps On Azure +11 posts tagged with "ask-the-expert" | Build Intelligent Apps On Azure @@ -14,13 +14,13 @@ - - + +
-

9 posts tagged with "ask-the-expert"

View All Tags

· 10 min read
It's 30DaysOfIA

Explore how digital natives and Independent Software Vendors (ISVs) can transform their business operations and generate higher value for customers with generative AI and intelligent apps.

What We'll Cover:

  • Potential of Generative AI in application development
  • Challenges with Generative AI
  • Integrating Generative AI with business operations

image of generative AI and intelligent apps used in business

Unleashing Business Transformation: How Digital Natives Can Leverage Generative AI

Generative AI is no longer a technological flight of fancy. It’s here, and it’s rewriting the rules of business innovation. 

This type of AI leverages machine learning algorithms to create new data with similar characteristics to a given dataset. It goes beyond text generation using generative pretrained transformer (GPT) models, covering an array of applications that span from image synthesis to translations and more. It also powers a new class of intelligent applications, which combine AI capabilities with traditional software functions for enhanced user experiences and advanced business solutions. 

Generative AI is transforming how digital natives and independent software vendors (ISVs) operate. Generative AI offers new possibilities for creating unique business value, enhancing customer experiences, and driving growth. 

It plays a critical role in applications, improving functionality, aiding decision-making, and automating complex tasks to deliver dynamic, uniquely personalized user experiences. Generative AI also enables and automates data-driven outcomes, functions, and features.

Integrating AI capabilities into customer-facing apps and turning them into intelligent apps benefits both the customer and the business, as we discussed in Demystifying Intelligent Applications: Leveraging AI in App Development. You get faster product development workflows and better business intelligence for sales and marketing, while your users get more natural and context-aware features and interactions. 

That said, to realize generative AI’s potential, you need some strategy and knowledge of how and where it fits into your current business and operations. We’ll examine this technology’s potential to transform business operations, its risks, and rewards, and how to integrate it into your existing operations. 

Unpacking the Potential of Generative AI

Generative AI is creating new opportunities in the digital playing field. It empowers businesses to generate insights, design products, and automate processes in ways and at speeds that were previously unimaginable. You can now predict customer behavior more accurately, offer highly personalized user experiences, and prototype innovative ideas rapidly. 

For example, Microsoft used generative AI to reinvent web search in the new AI-powered Bing and Edge. The search experience has evolved from a solitary user flow of typing in keywords and scrolling through results. It’s now a conversational experience where Bing uses context to offer better quality and more personalized search queries and results. 

Similarly, Microsoft harnessed generative AI to transform its productivity suite into intelligent apps. Through Copilot for Microsoft 365, PowerPoint now empowers users to tell their stories by using DALL-E to generate images effortlessly. It also helps rephrase text-heavy slides to be more straightforward. Outlook now assists users to compose more effective emails, enabling better communication.

These are just some examples of how AI powers day-to-day work and operations. 

Businesses that ignore generative AI miss out on untapped market opportunities, letting competitors gain an edge in the digital race. By seeking ways to rethink assumptions and reinvent existing approaches, you can effectively use generative AI to create new opportunities, open products to new use cases and markets, unlock new growth and customer satisfaction levels, and increase your revenue.

info

Register for the intelligent apps webinar on Driving Business Value by Modernizing with Cloud-Native & AI with Microsoft and Forrester on September 26.

Explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries. 

The Crucial Role of Generative AI in Business Operations

Generative AI can be a powerful catalyst for business transformation. Let’s quickly tour some ways it can redefine business operations. 

Product development is traditionally a long and painstaking process. Now, tools like GitHub Copilot and TestPilot use generative AI to make coding and testing software easier, faster, and more robust. 

Similarly, machine learning as a service technology, such as Azure Machine Learning, democratizes the availability of machine learning. It helps collect, clean, and train models across datasets, making engineering and analyzing data more efficient and accessible.  

Generative AI also simplifies the process of creating intelligent apps. Previously, simulating thousands of prototypes, predicting their performance, and narrowing down to the most viable options could take weeks, months, or even years of iteration and data analysis. With generative AI, this process can be as quick as days, hours, or perhaps minutes. 

Customer experience is also ripe for transformation. Personalized recommendations drive engagement and sales, and generative AI can scale that personalization to every customer interaction using comprehensive and continually updated data. For instance, Microsoft leverages AI to enable personalization in Dynamics 365 Customer Insights. Using this tool, you can ask questions about your customers in natural language and gain valuable and actionable insights into your customer base. 

Lastly, marketing and sales can benefit from generative AI. For example, identifying current and future key markets and emerging trends helps companies stay ahead of the curve. Tools like Copilot in Power BI help you quickly understand and uncover insights about your business, enabling your team to craft targeted campaigns, create engaging content, and optimize your business strategy. 

Leveraging this full generative AI potential requires the right tools. GPT-4, for instance, can be instrumental in realizing effective business transformation. It excels in understanding context, generating human-like text, and learning from minimal data input, making it a formidable tool in any digital native's arsenal. Platforms like Azure OpenAI Service bring GPT-4’s power to your fingertips.

However, as with any powerful technology, it’s unwise to treat generative AI as a perfect solution. You must know the challenges and risks of not leveraging it effectively, including technical and human-factor considerations. 

Without generative AI, your company risks missing the opportunity to please customers via personalization, as discussed previously. You’ll miss sales opportunities via business intelligence insights, limiting your ability to grow revenue and keep up with the competition. 

One challenge of leveraging generative AI’s full potential is finding the right tools. Fortunately, you can implement generative AI in your organization without investing in infrastructure, computational power, and expertise. Platforms like Azure OpenAI Service and Azure AI Services make AI easily accessible. They also work with Azure compute and data services like Azure Functions and Azure Kubernetes Service (AKS), enabling you to build intelligent apps easily and quickly. 

However, generative AI can’t do everything. It may need human expertise and intervention to use it fully. 

For example, generative AI can create unique data. However, the data may have limited diversity and complexity. The AI may need help recognizing additional considerations or requirements specific to a problem. It may also lack context due to various factors, such as the information’s freshness. Also, since it’s an emerging field that’s potentially worth $4.4 trillion, we’re still learning about the nuances and potential regulatory impact of generative AI. 

Perhaps the most crucial and challenging aspect of adopting generative AI is an organization’s required cultural shift. Your teams must learn how to use AI as your company fosters an understanding of AI’s potential and limitations. 

Begin integrating AI literacy into your company’s DNA. Knowledge of its potential and the readily available tools can inspire your teams to incorporate generative AI into their daily workflows and business operations quickly, improve productivity, and deliver superior customer experiences. 

No company wants to be outpaced by its competitors. You can balance generative AI’s potential with your business operations by staying aware of and adapting to the dynamic AI landscape.

Achieving Harmony: Integrating Generative AI with Existing Business Operations

Integrating generative AI into existing business operations doesn’t have to be disruptive. With the correct strategy, you can do so seamlessly. Your goal should be to augment and enhance your operations, not disrupt and replace. 

For example, Microsoft Cloud technologies leveraged AI to relieve healthcare organizations’ challenges, such as talent shortage and workforce burnout. Providers automated their workflows, invested in specific applications and connectors, and enhanced support for data and operations. 

Azure AI Services, such as text analytics for health, enable healthcare organizations to understand their largely unstructured and untapped medical data. Project Health Insights leans on AI to help healthcare professionals gain actionable insights and inferences from patient data. 

Azure Health Bot, an Intelligent App, empowers organizations to build and deploy legally compliant, powerful conversational healthcare experiences to patients. 

A successful AI integration like this begins with understanding. Knowing what generative AI can do and setting realistic expectations can help you identify where and how AI can best serve your operations. 

Next, you can foster a culture of AI literacy across teams, speeding up adoption by helping them understand how it can aid their work. The more familiar your team is with AI and its possibilities, the smoother the transition. Training and education programs play a pivotal role here. 

Finally, integration requires a systematic approach. It’s not about overhauling everything at once — it's about identifying areas where AI can add the most value, then gradually experimenting and scaling its use. Successful integration stories often feature a phased approach, starting small and growing steadily. 

As we move toward a future where generative AI is a staple in business operations, remember that every journey is unique. Although there’s no one-size-fits-all solution, a strategic approach and a keen understanding of your business needs will help you navigate this new landscape successfully.

Conclusion

Generative AI offers plenty of opportunities for businesses that embrace it. It’s fueling the next generation of intelligent applications, transforming everything from product development to customer engagement to marketing. This technology is the key to unlocking unprecedented growth and innovation. 

While the journey may have challenges, the potential rewards make generative AI worth exploring. As generative AI advances, Azure AI’s broad range of tools and services will help you navigate this fascinating field.

Exercise

  • Watch Episode 02 of the serverless edition Learn Live session to learn how to build, test and deploy an end-to-end intelligent app solution.
  • Complete the Cloud Skills Challenge to build on your AI skills.
  • Register for Ask the Expert: Azure Functions on September 26 for a live Q&A with the Product Team on how to build AI apps using serverless computing.
- - +
Skip to main content

11 posts tagged with "ask-the-expert"

View All Tags

· 12 min read
It's 30DaysOfIA

Delve into creating an Intelligent App that leverages Azure AI Vision to analyze images and extract data. Develop an API to perform optical character recognition (OCR) on uploaded images and testing this API locally.

What We'll Cover:

  • Understanding Azure AI Vision and Azure Kubernetes Service
  • Build a Python Web API to perform OCR
  • Test the API locally

image depicting an Intelligent App using AI

Jumpstart Your AI Journey: Building Your First Intelligent App with Azure AI and AKS (1)

An Intelligent App is a software application that uses artificial intelligence (AI), such as machine learning (ML), to extract meaningful insights, enabling users to make informed decisions based on the extracted text, data, sound, images, and more.

Intelligent Apps can employ advanced algorithms and models to recognize objects, patterns, and features within images, allowing them to perform tasks like object identification, image classification, facial recognition, and image segmentation.

To explore the power of Intelligent Apps, let’s build a Python app that performs optical character recognition (OCR) on images containing numbers and performs aggregate functions on the extracted numbers, like sum and average. We’ll leverage Azure AI Services for the OCR functionality and Azure Kubernetes Service (AKS) to develop, publish, and maintain a cloud-based, scalable app in Azure.

Let’s get started!

Understanding Azure AI Vision and Azure Kubernetes Service

Azure AI Vision is a cloud-based service that offers a range of advanced computer vision capabilities enabling applications to interpret content, such as images and videos, using ML and other AI algorithms. Azure computer vision lets users integrate powerful vision features into their projects without ML experience.

OCR is one of the critical functionalities within Azure vision service, allowing applications to extract text from images. Within Azure AI Vision service, OCR offers several features, including text extraction, multilingual support, layout analysis of documents, and integration with Azure services, such as Azure Functions and Azure Machine Learning.

info

Watch the intelligent apps webinar on Driving Business Value by Modernizing with Cloud-Native & AI with Microsoft and Forrester on September 26.

AKS is Microsoft Azure’s container orchestration platform that enables us to quickly and efficiently deploy, manage, and scale containerized applications, such as our Intelligent App.

Let’s explore how to leverage these tools to build an Intelligent App that uses Azure AI Vision to analyze images and extract data.

Prerequisites

To follow this tutorial, ensure you have the following:

For a preview of this final project, take a look at the complete project code.

Building the API with Azure AI Vision Service

First, log in to your Azure account and navigate to the Azure Portal.

Click Create a resource and search for “resource group.” Create a new resource group named computer-vision.

image in Azure Portal of creating a new resource group

Return to the Azure Portal home page and click Create a resource.

image in Azure Portal of creating a new resource

Search for “computer vision” and select it from the results. Click Create.

image of searching for resource group in Azure Portal

Clicking Create displays the Create Computer Vision wizard. Select the resource group you just created as the Resource group from the dropdown. Enter a resource name similar to what is seen in the screenshot below. Note that this must be a unique name so choose one that is not currently in use. Select Standard S1 as the pricing tier and check the Responsible use of AI box in the field at the bottom.

Image of selecting the Responsible use of AI box

Image of project details

Click Review + Create, then click Create.

Next, get the API key and endpoint from the Azure AI services multi-service account you created. Once the resource is created, navigate to the resource details page. Under Resource Management, select Keys and Endpoint. Once on the Keys and Endpoint page, you’ll find the Key 1 and Endpoint values. These credentials are necessary to access the Azure AI APIs.

image of selecting Keys and Endpoint

second image of selecting Keys and Endpoint

Configuring the Local Environment Variables

Next, we need to define a local environment variable file. This convenient way to store our API keys in a simple text file enables us to manage sensitive information and separate configuration from our API code.

If you haven’t done so already, clone the starter project template from GitHub into your local computer. Open the starter project template in a code editor and create a new .env file in the root folder.

Note: Root folder in this case is the “/Microsoft_Series17-18_Code/intelligent-app-before" folder.

Add the following key-value pairs to the file, remembering to replace the placeholder values with those you obtained from the previous step.

VISION_KEY=<THE-KEY-1-VALUE-FROM-YOUR-AZURE-AI-SERVICE>
VISION_ENDPOINT=<THE-ENDPOINT-VALUE-FROM-YOUR-AZURE-AI-SERVICE>

Reviewing the Quickstart Code

Let’s review the app.py file in the starter project template.

The app.py file works as the entry point for the Flask application. It is responsible for initializing the web server, listening for incoming requests, dispatching them to the appropriate route handlers, and generating responses.

Below is the code contained in the app.py file:

import os
import json

from flask import Flask, request
from flask_restful import Resource, Api
from werkzeug.utils import secure_filename

app = Flask(__name__,
static_url_path='',
static_folder='static/files')

api = Api(app)

app = Flask(__name__)

app.config['UPLOAD_FOLDER'] = 'files'

api = Api(app)

class UploadHandler(Resource):

def allowed_file(self, filename):
return '.' in filename and \
filename.rsplit('.', 1)[1].lower() in {'png'}

def post(self):
form = request.form.to_dict()

if 'file' not in request.files:
return json.dumps({ "success": False, "error": "No file part"})

file = request.files.get("file")
if file and self.allowed_file(file.filename):
filename = secure_filename(file.filename)
upload_folder = "static/files"
if not os.path.exists(upload_folder):
os.makedirs(upload_folder)
local_file_path = os.path.join(upload_folder, filename)
file.save(local_file_path)

return f"File {filename} uploaded successfully to folder: {upload_folder}"

api.add_resource(UploadHandler, "/")

if __name__ == '__main__':
app.run(debug=True)

The UploadHandler class above is defined as a Flask RESTful resource to handle file uploads. The class contains two methods:

  • The allowed_file method checks whether the file extension is allowed for upload. In this case, only .png files are allowed.

  • The post method handles HTTP POST requests for file uploads. It saves the uploaded .png file to the static/files folder.

Finally, the application returns a text response informing us that the file has been uploaded successfully.

Implementing the REST API in Python

To implement image analysis in your REST API, open the starter project template in your terminal, create a virtual environment, and activate it.

Next, install the Azure AI Vision SDK:

pip install azure-ai-vision

Then, add the following line to the requirements.txt file to include the azure-ai-vision package:

azure-ai-vision==0.13.0b1

Now, create an ocr_helper.py file in the project’s root folder. This file is a Python module that provides functions for processing images using OCR. Add the following code to the file:

import os
from statistics import median
from decimal import Decimal
import azure.ai.vision as sdk

def process_ocr(source_image):
service_options = sdk.VisionServiceOptions(os.environ["VISION_ENDPOINT"],
os.environ["VISION_KEY"])

vision_source = sdk.VisionSource(filename=source_image)

analysis_options = sdk.ImageAnalysisOptions()

analysis_options.features = (
sdk.ImageAnalysisFeature.CAPTION |
sdk.ImageAnalysisFeature.TEXT
)

analysis_options.language = "en"

analysis_options.gender_neutral_caption = True

image_analyzer = sdk.ImageAnalyzer(service_options, vision_source, analysis_options)

result = image_analyzer.analyze()

ocr_result = get_ocr_result(result)

return ocr_result

def get_ocr_result(result):
string_list = []

if result.reason != sdk.ImageAnalysisResultReason.ANALYZED:
return sdk.ImageAnalysisErrorDetails.from_result(result)
else:
if result.text is not None:
for line in result.text.lines:
for word in line.words:
string_list.append(word.content)

number_list = convert_to_decimal_list(string_list)

aggregate_result = aggregate_operations(number_list)

return {
"aggregate_result": aggregate_result,
"numbers_read": string_list
}

def convert_to_decimal_list(string_list):
return list(map(Decimal, string_list))

def aggregate_operations(numbers):
result = {
'sum': sum(numbers),
'average': sum(numbers) / len(numbers),
'median': median(numbers),
'min': min(numbers),
'max': max(numbers)
`}`
`return result`

This module uses the azure-ai-vision package to analyze images, including capturing captions and extracting text from the image. The process_ocr function inputs an image file and performs the OCR analysis. The module is a convenient tool for analyzing text-based data in pictures and performing numerical computations based on that data.

Let’s review the different components of the ocr_helper.py module listed above and explore how its functions help our Intelligent App process images and perform aggregate operations:

  • The process_ocr function takes the parameter source_image, which is the path of the image to be processed. The function then initializes the VisionServiceOptions using environment variables VISION_ENDPOINT and VISION_KEY to connect to the Azure AI Vision API.
  • The process_ocr function creates a VisionSource object with the specified source_image file name. ImageAnalysisOptions specify the features to be analyzed, including CAPTION and TEXT. The language is set to English ("en"), and gender-neutral captions are enabled.
  • Finally, an ImageAnalyzer object is created with the service options, vision source, and analysis options. The image is then analyzed using the image_analyzer.analyze method to retrieve OCR results. The recognized text from the OCR result is extracted, with numerical values stored in the string_list variable.
  • The convert_to_decimal_list function converts the list of strings to a list of decimal numbers, which helps process numeric values extracted from the text.
  • The aggregate_operations function calculates various aggregate statistics (sum, average, median, minimum, maximum) from a list of numbers and returns the results in a dictionary.

Note that you must have the appropriate credentials (VISION_KEY) and endpoint (VISION_ENDPOINT) configured for the Azure AI Vision API to use this module.

Finally, we must modify the app.py file so our code can use the process_ocr function of the ocr_helper.py file.

Add the following import statement to the app.py file:

from ocr_helper import process_ocr

Then, replace this line:

return f"File {filename} uploaded successfully to folder: {upload_folder}"

With these two lines:

aggregates = process_ocr(local_file_path)
return json.dumps(aggregates, default=str)

Doing so allows our application to perform aggregate operations (sum, average, median, minimum, maximum) of any numeric values found in the text and return the operation’s result as a JSON response.

Running the Intelligent App Locally

Our final goal is to deploy the sample app to AKS, which will run the app in a container. However, as a good practice, let’s run the app in a container locally before moving it to AKS. Doing so ensures that our application and its dependencies are encapsulated and can be run consistently across different environments.

Deploying to Docker Desktop

For our application to run on Docker, it needs two additional files: a Dockerfile and docker-compose.yml. A Dockerfile creates a single container image by specifying the steps to build it, and a docker-compose.yml file manages multiple containers as a cohesive application, defining their configurations and relationships. Dockerfile builds the image, while docker-compose.yml orchestrates multi-container applications.

In the project root folder, add a file named Dockerfile with the content below:

# syntax=docker/dockerfile:1

FROM python:3.8-slim-buster

WORKDIR /intelligentapp

COPY requirements.txt requirements.txt
RUN pip3 install -r requirements.txt

RUN pip install debugpy

COPY . .

CMD [ "python3", "-m" , "flask", "run", "--host=0.0.0.0"]

Now create a file named docker-compose.yml file in the project’s root folder with the following code, replacing the VISION_KEY and the VISION_ENDPOINT according to the environment variables you configured earlier:

version: '3.8'
services:
intelligentapp:
build:
context: .
dockerfile: Dockerfile
image: intelligent-app
ports:
- 5000:5000
container_name: intelligent-app
environment:
- VISION_KEY=<THE-KEY-1-VALUE-FROM-YOUR-AZURE-AI-SERVICE>
- VISION_ENDPOINT=<THE-ENDPOINT-VALUE-FROM-YOUR-AZURE-AI-SERVICE>

Then, run the following command in the terminal to build the image and start a container for the Intelligent App services defined in the docker-compose.yml file:

docker-compose up --build --force-recreate

Next, open a new terminal and run the command below to list the image deployed to your local Docker:

docker images

docker images

Testing the Intelligent App Locally

Now, we’ll test the Intelligent App’s functionality using the following test image:

test image to test functionality

This image is provided as sample1.png in the sample app source files, so you can easily use it in the next step.

The Intelligent App will read the image above using OCR to extract numbers in the image and perform five aggregate functions:

  • Sum
  • Average
  • Median
  • Min
  • Max

To test the API, open Postman and fill out the fields as follows:

  • URL: http://localhost:5000/
  • Method: POST
  • Body:
    • Form-data
      • Key: File — Click the right end of the Key field and select File from the dropdown list.
      • Value: Click Select Files, then select the sample1.png file provided in the sample code.

image of selecting the sample file

Now click the Send button and review the result body:

"{\"aggregate_result\": {\"sum\": \"25821\", \"average\": \"5164.2\", \"median\": \"5622\", \"min\": \"1447\", \"max\": \"9802\"}, \"numbers_read\": [\"3049\", \"5901\", \"5622\", \"1447\", \"9802\"]}"

As we can see, the app running on a local container returned the correct results based on the sample image. So, we’re ready to prepare our Azure resources to run the Intelligent App on AKS.

Exercise

In this article, we explored the creation of an Intelligent App that leverages Azure AI Vision to analyze images and extract data. We learned how to build a Python Web API to perform OCR on uploaded images and subsequently test this API locally.

Head to the next part, to deploy this API via Azure Kubernetes Service.

+ + \ No newline at end of file diff --git a/30daysofIA/tags/ask-the-expert/page/4/index.html b/30daysofIA/tags/ask-the-expert/page/4/index.html index 4901f3902a..f7e09a113d 100644 --- a/30daysofIA/tags/ask-the-expert/page/4/index.html +++ b/30daysofIA/tags/ask-the-expert/page/4/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "ask-the-expert" | Build Intelligent Apps On Azure +11 posts tagged with "ask-the-expert" | Build Intelligent Apps On Azure @@ -14,13 +14,13 @@ - - + +
-

9 posts tagged with "ask-the-expert"

View All Tags

· 9 min read
It's 30DaysOfIA

In the part of our series on intelligent apps, we’ll explore how AI is transforming application development, from design and architecture to building.

What We'll Cover:

  • Revolutionizing application development using AI
  • Infusing AI in application design architecture
  • AI Assisted Pair Programming
  • Future of App Development

A decorative image of application development with AI

Reimagining Application Development With AI: A New Paradigm

Artificial intelligence (AI) is impacting every industry. AI-powered development tools offer new functionalities and assist software developers throughout the development process, revolutionizing app development. From initial design and architecture to coding and debugging, AI reshapes how we approach application conception, creation, and iteration.

In part one of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” we explored the new breed of intelligent apps and how they use AI. In this article, we’ll see how AI technologies enhance developer toolkits and shape the future of app development, particularly through AI-enabled coding assistance and debugging.

The Dawn of Intelligent Apps: Revolutionizing Application Development

AI is more than just another addition to the development toolbox—it represents a revolution bringing unprecedented efficiency, innovation, and user engagement to our applications. 

What sets intelligent apps apart is how they can adapt and learn, helping us deliver unique, real-time user experiences.  

One example is an AI-driven health application that can track biometric data, predict potential health issues, provide personalized health advice, and even guide emergency services during a medical crisis. An AI-enabled language learning app that adapts to our learning speeds, recognizes our problem areas, and customizes its content to help us improve our language proficiencies is another example.  

Intelligent apps like these don’t just use data for recommendations—they can make critical decisions and provide sophisticated services that were previously impossible. 

For developers, intelligent apps mean we’re no longer just coding—we’re teaching applications how to think, adapt, and make decisions. AI is reshaping every step of the development journey, helping us to build more intuitive and user-focused products. 

Next, we’ll examine the impact of AI on application design and architecture and how AI-assisted “pair programming” changes software development.

info

Register for the FREE webinar on Intelligent Apps with Microsoft and Forrester on September 26 to explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries.

There will be a showcase of real-world use cases that demonstrate how AI can be seamlessly integrated into cloud-native environments driving tangible business value.

AI in Design and Architecture: Fueling Creativity and Efficiency

AI technologies help developers automate and streamline their processes and provide new ways to think about design.  

In traditional application development, the design and architecture phase can take significant time, effort, and expertise. For instance, adopting a monolithic architecture or a microservices-based design can significantly increase coding, testing, and debugging complexity. The app may even behave differently when hosted locally than in a deployed environment. It may take hours to track down an issue manually—time developers could spend elsewhere. 

Conversely, we can generate numerous design iterations in seconds with AI, each optimized to fulfill specific user needs and business goals. AI tools can quickly model various scenarios' impacts and software design patterns in the architectural design phase. It provides insights into performance and potential issues and suggests improvements. AI can also generate skeleton code, allowing developers to prototype different solutions or test specific components and technologies rapidly. By reviewing thousands of lines of code in seconds, AI allows us to focus on creating apps that serve users through feature development instead of searching for bugs.  

Integrating AI into the design and architecture phase can even extend our creative capabilities. By leveraging the power of generative AI—like large language models (LLMs)—and their broad knowledge and context going beyond software, we can better brainstorm apps and feature ideas. We can go beyond code and software architecture and get ideas about user experience, logo and branding, or marketing concepts. And as AI tools deliver insights and ideas we would have never considered; it encourages more innovation.

info

Watch Episode 01 of the Learn Live session to learn how to build an end-to-end intelligent app solution architecture.

The Power of AI-Assisted Pair Programming in Building Applications

AI-assisted pair programming is a collaborative coding approach where an AI system—like GitHub Copilot or TestPilot—assists developers during coding. It’s an increasingly common approach that significantly impacts developer productivity. In fact, GitHub Copilot is now behind an average of 46 percent of developers’ code and users are seeing 55 percent faster task completion on average. 

For new software developers, or those interested in learning new skills, AI-assisted pair programming are training wheels for coding. With the benefits of code snippet suggestions, developers can avoid struggling with beginner pitfalls like language syntax. Tools like ChatGPT can act as a personal, on-demand tutor—answering questions, generating code samples, and explaining complex code syntax and logic. These tools dramatically speed the learning process and help developers gain confidence in their coding abilities. 

Building applications with AI tools hastens development and provides more robust code. For example, GitHub Copilot helps us write new code accurately. It also quickly makes sense of existing code, suggests whole new lines or blocks of code, and even detects errors and proposes fixes. It’s like having a second coder working alongside us—one who never tires and is trained on billions of lines of code. 

However, it’s also important to acknowledge the limitations of AI. While generative AI-assistance technologies like GitHub Copilot are impressive, they’re still imperfect. They can sometimes produce erroneous, non-functional, or vulnerable code. They can also lack nuanced, domain-specific knowledge.  

So, although these tools are helpful, we can’t rely on them fully—these are programming aids rather than definitive solutions. AI-assisted pair programming is about augmenting the human developer’s skills, not replacing them.

Debugging and Improving Code With AI: Enhancing Application Quality

Identifying and correcting code errors is often time-consuming and challenging for developers. AI streamlines the debugging process by automatically detecting anomalies, suggesting fixes, and learning from previous bugs to prevent them from recurring. 

We can use generative AI to create unit tests from code snippets, from simple parameter checking and input validation to complex mocks of database and network services. Automated AI testing improves code coverage and robustness. It saves significant time compared to manually crafting unit tests. 

AI also revolutionizes code reviews by scanning and highlighting potential issues—like code patterns leading to a bug or a coding standards violation—and suggesting ways to simplify or refactor the code for readability and performance. This reduces human error and makes the code review process more efficient. Its benefits extend to small teams, where the availability of developers is crucial to deadlines, and large teams, where effective collaboration depends on readability and code consistency. AI further saves time by triaging issues so developers can prioritize the most mission-critical ones. 

Aside from improving code quality, AI is critical to understanding system behavior. Intelligent AI-assisted software development tools can monitor and audit system behavior, giving developers a better insight into how their code performs in different scenarios. AI can track patterns, identify inefficiencies, and provide alerts when they detect unusual behavior—enabling developers to enhance their code further. 

By predicting and identifying potential vulnerabilities, intelligent AI-assisted coding apps like Microsoft Security Copilot are pivotal in ensuring application functionality, reliability, and security. These AI tools are continually trained and upgraded with large codebases and user feedback that refines their responses. This ability to learn from past mistakes makes AI an incredibly powerful tool that will continue to expand its capabilities in the future.

info

Complete the AI Cloud Skills Challenge to build on you AI application development skills with paired programming using generative AI and GitHub Copilot.

AI and the Future of Application Development

The rapid advancements in AI and machine learning technologies point to an exciting future for application development. 

In “The Future of Applications,” software developer Mike Cann illustrates how LLMs might revolutionize application development. Cann that suggests fully-autonomous coding—AI-generated applications created entirely from prompts—may supersede traditional hand-crafted applications. Dynamically generated user interfaces could offer personalized and customizable experiences while we can design APIs for machine consumption and efficiency rather than human readability. Furthermore, we can store data in less structured, more adaptable, and flexible forms. 

AI is already making application development more accessible. With AI handling the complex, technical aspects of development, less-technical individuals can take a greater role in building applications. Consequently, AI can potentially democratize software engineering. 

Intelligent apps will continue to evolve, becoming more efficient, adaptable, and capable of learning from user interactions. Similarly, AI will inevitably become integral in every stage of app development.

Summary

AI has already created a new development paradigm—one in which machine intelligence and human creativity come together to produce innovative, efficient, and robust applications. AI already assists us at every stage of app development, from design and architecture to coding and debugging. 

Developers who embrace AI tools and integrate them into their processes can enhance their productivity, improve the quality of their applications, and expand the possibilities of their application development.  

The future of development is automated and AI-driven, and it’s here today. Getting started is easy—try an AI-powered development tool like GitHub Copilot to see how AI-assisted pair programming can enhance your productivity and code quality.

Exercise

- - +
Skip to main content

11 posts tagged with "ask-the-expert"

View All Tags

· 9 min read
It's 30DaysOfIA

In the last article, we created an Intelligent App that leverages Azure AI Vision to analyze images and extract data. Develop an API to perform optical character recognition (OCR) on uploaded images and testing this API locally.

In this article, we will deploy the web API to the cloud platform using Azure Kubernetes Service.

What We'll Cover:

  • Understanding Azure AI Vision and Azure Kubernetes Service
  • Build a Python Web API to perform OCR
  • Test the API locally

image depicting an Intelligent App using AI

Jumpstart Your AI Journey: Building Your First Intelligent App with Azure AI and AKS (2)

In the previous article we explored the creation of an Intelligent App that leverages Azure AI Vision to analyze images and extract data. We learned how to build a Python Web API to perform OCR on uploaded images and subsequently test this API locally.

In this article we will use Azure Kubernetes Service (AKS) to develop, publish, and maintain our app in the cloud on Azure.

Let’s get started!

Prerequisites

To follow this tutorial, ensure you have completed Jumpstart Your AI Journey: Building Your First Intelligent App with Azure AI and AKS (1).

Pushing a Container Image to Azure Container Registry (ACR)

To start, open your CLI or terminal and type the following command:

az login

Follow the instructions displayed on your browser to enter your Azure credentials.

Once authenticated, you’ll initiate a secure connection between your local environment and Azure. This process grants you access to cloud services and resources.

Next, type the command below in your terminal to set up a new Azure Container Registry (ACR) to store your container images:

az acr create --resource-group computer-vision --name <name-of-azure-container-registry> --sku Basic

Remember to replace <name-of-azure-container-registry> with your container registry name. The name must be unique within Azure and comply with these rules.

The command above creates an Azure Container Registry (ACR) in the computer-vision resource group under the Basic SKU. This ACR is your secure and private repository for storing container images within Azure.

Next, log in to the registry with the following command:

az acr login -n <name-of-azure-container-registry>

The az acr login command above lets you securely authenticate and access the specified ACR without providing their Azure credentials each time.

Now, run the following command in your terminal. It will display the endpoint URL to log in to and interact with the ACR for pushing and pulling container images.

az acr show --name <name-of-azure-container-registry> --query loginServer --output table

This returns an endpoint URL as follows:

Result
----------------------------------
<name-of-azure-container-registry>.azurecr.io

Now, run the following command to show all container images, their repository, tags, and size:

docker images
REPOSITORYTAGIMAGE IDCREATEDSIZE
intelligent-applatesta7bf9f75361716 hours ago197MB

Tags are necessary to push Docker images to a remote registry like Azure Container Registry. They also let you differentiate between different versions of the same image and upload or download the one you want.

Run the following command to tag your Docker image:

docker tag intelligent-app <name-of-azure-container-registry>.azurecr.io/intelligent-app:v1

Then, run the docker images command again to check your tagged image:

docker images
REPOSITORYTAGIMAGE IDCREATEDSIZE
intelligent-applatestc52168039265About a minute ago197MB
<name-of-azure-container-registry>.azurecr.io/intelligent-appv1c52168039265About a minute ago197MB

Now run the following command so Docker can securely upload the image to your Azure Container Registry:

docker push <name-of-azure-container-registry>.azurecr.io/intelligent-app:v1

Once we've deployed the image to the container registry, AKS can access it during deployment.

info

Watch Episode 03 for the Learn Live session to experience a guided session on how to build, test and deploy an end-to-end intelligent app solution.

Deploying the Intelligent App on Azure Kubernetes Service (AKS)

Before we deploy our Intelligent App to AKS, we need to provision an AKS cluster and define Kubernetes manifests.

To provision an AKS cluster to host our application, we specify the desired configurations for the cluster, such as the number of nodes, node size, and networking options. But first, download and install the Kubernetes command-line tools (kubectl), a client credential plugin implementing Azure authentication:

az aks install-cli

If you’re using Linux, review this tutorial. Then run the following:

sudo az aks install-cli

Next, run the following command in your terminal to enable access to the networking-related resources and services provided by the `Microsoft.Network`` namespace in Azure:

az provider register --namespace Microsoft.Network

Now, we must create an AKS cluster. Run the following command to create an AKS cluster named aks-intelligent-app in the computer-vision resource group.

az aks create --resource-group computer-vision --name aks-intelligent-app --node-count 1 --generate-ssh-keys

The command above specifies the target resource group: computer-vision. The node pool is configured with one virtual machine (VM), and the secure shell (SSH) keys for secure node access are generated automatically.

Next, run the following command to update the AKS cluster you created by attaching it to your ACR. Doing so allows the AKS cluster to pull container images from the specified ACR when deploying workloads to the cluster.

az aks update -n aks-intelligent-app -g computer-vision --attach-acr <name-of-azure-container-registry>

Then, run the command below to configure kubectl to work with our AKS cluster in the computer-vision resource group.

az aks get-credentials --resource-group computer-vision --name aks-intelligent-app

The command above retrieves the necessary credentials and context information required for kubectl to communicate with the AKS cluster.

We still have to define the Kubernetes manifests written in YAML, describing the desired state of our application deployment, including containers, networking, and scaling rules. We’ll prepare these manifests, including Deployment and Service configurations, to define how our application should be deployed and exposed.

Create a folder named Deployment in the project root directory. Next, create two files in the deployment folder: deployment.yml and service.yml.

Add the following configuration to the deployment.yml file, replacing the <name-of-azure-container-registry> placeholder with your registry’s name:

apiVersion: apps/v1
kind: Deployment
metadata:
name: intelligent-app
spec:
replicas: 1
selector:
matchLabels:
app: intelligent-app
template:
metadata:
labels:
app: intelligent-app
spec:
nodeSelector:
kubernetes.io/os: linux
containers:
- name: intelligent-app
image: <name-of-azure-container-registry>.azurecr.io/intelligent-app:v1
resources:
limits:
memory: 512Mi
cpu: "1"
requests:
memory: 256Mi
cpu: "0.2"
ports:
- containerPort: 5000
env:
- name: FLASK_DEBUG
value: "1"
- name: VISION_KEY
value: <THE-KEY-1-VALUE-FROM-YOUR-AZURE-AI-SERVICE>
- name: VISION_ENDPOINT
value: <THE-ENDPOINT-VALUE-FROM-YOUR-AZURE-AI>

Additionally, edit the VISION_KEY and VISION_ENDPOINT environment variables above according to the API key and endpoint of your Azure AI Services instance.

Then, add the following configuration to the service.yml file:

apiVersion: v1
kind: Service
metadata:
name: intelligent-app-service
spec:
type: LoadBalancer
ports:
- protocol: TCP
port: 80
targetPort: 5000
name: port5000
selector:
app: intelligent-app

Now, we’ll deploy our application using kubectl. Applying the Kubernetes manifests creates the necessary resources and deploys our containerized application to the AKS cluster.

First, change the terminal to the deployment folder:

cd Deployment 

Then, run the following command to create or update Kubernetes resources defined in the deployment.yml file:

kubectl apply -f deployment.yml

Create a Kubernetes Service resource in a Kubernetes cluster based on the configuration defined in the service.yml file using the code below:

kubectl apply -f service.yml

Once you’ve applied the resource definition and the service configuration contained in the deployment.yml and the service.yml files, open the aks-intelligent-app Kubernetes Service in the Azure Portal, select Workloads under Kubernetes resources on the sidebar, and find the deployment named intelligent-app. It must have the status “Ready 1/1”. If you encounter an issue with this status, check out these troubleshooting resources.

image of selecting the deployment in Azure Portal

Testing the Intelligent App on AKS

To test the app on AKS, first, run the command below:

kubectl get services

This command lists the Services and their corresponding details, including the Service name, cluster IP address, external IP, and ports.

NAMETYPECLUSTER-IPEXTERNAL-IPPORT(S)AGE
intelligent-app-serviceLoadBalancer10.0.77.6020.121.76.15380:30936/TCP47s
kubernetesClusterIP10.0.0.1<none>443/TCP14m

The output above shows a Kubernetes Service named intelligent-app-service with a type set to LoadBalancer. It’s reachable from within the cluster using the cluster IP 10.0.77.60 and accessible externally via the external IP 20.121.76.153 on port 80 (mapped to port 30936).

Note: Your set of IP addresses will differ. Remember to use your unique external IP address when testing with Postman.

To test the deployed app, go to Postman, replace the URL with the external IP of the Kubernetes Service you just deployed, and click Send:

mage of sending the app in Postman

As we can see, our Intelligent App has successfully deployed to AKS and is functioning on the cloud as expected.

Exercise

Next Steps

In this two-part article, we explored the creation of an Intelligent App that leverages Azure AI Vision to analyze images and extract data. We learned how to build a Python Web API to perform OCR on uploaded images and subsequently deploy this API via Azure Kubernetes Service.

Besides OCR and Image Analysis, you can continue exploring Azure’s vast array of services and experiment further with Azure AI and AKS by applying various practical uses to your Intelligent Apps, including natural language processing, speech recognition and synthesis, sentiment analysis for customer feedback, and automated content moderation.

+ + \ No newline at end of file diff --git a/30daysofIA/tags/ask-the-expert/page/5/index.html b/30daysofIA/tags/ask-the-expert/page/5/index.html index 84cdf78eff..a62e3055f1 100644 --- a/30daysofIA/tags/ask-the-expert/page/5/index.html +++ b/30daysofIA/tags/ask-the-expert/page/5/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "ask-the-expert" | Build Intelligent Apps On Azure +11 posts tagged with "ask-the-expert" | Build Intelligent Apps On Azure @@ -14,13 +14,13 @@ - - + +
-

9 posts tagged with "ask-the-expert"

View All Tags

· 10 min read
It's 30DaysOfIA

Explore real-world examples of how Intelligent Apps revolutionize logistics, retail, finance, manufacturing, tech, and medicine.

What We'll Cover:

  • Real-world businesses revolutionizing operations with Intelligent Apps
  • High level architecture for industry scenarios
  • Implementation architecture overview across industries

A decorative header image for the blog

Real-world Success Stories

According to IBM’s 2022 Global AI Adoption Index, 35% of surveyed companies already use AI in their business, and 42% are exploring the idea. Organizations lagging behind the AI curve will quickly fall behind early adopters, who are already enjoying its benefits and boosting their investment in this technology.

In the first part of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” we explored how organizations can use AI-powered apps, or intelligent apps, to transform their operations, decision-making, and customer experiences, leaping ahead of the competition.

Intelligent apps significantly improve operational efficiency and customer engagement across industries like logistics, retail, finance, manufacturing, tech, and healthcare. In this article, we’ll spotlight real-world success stories of businesses revolutionizing operations with intelligent apps using Microsoft Azure’s app, data, and AI services.

info

Register for the Learn Live on September 21 for a live guided experience with SMEs on how to build end-to-end solution architecture for real world implementation of Intelligent Apps.

Streamlining Operations: Intelligent Apps in the Airline Industry

Aerospace pioneer Airbus leveraged intelligent apps to streamline its operations and innovate its services with two ground-breaking modernizations that use Azure apps, data, and AI services to reimagine pilot training and predict aircraft maintenance issues. 

Aviation technology’s rapid evolution compelled Airbus to reconceive their approach to training pilots. The aircraft manufacturer created a pilot training chatbot by combining Azure AI Services with Speech to Text and Text to Speech. The bot helps trainee pilots navigate over 6,000 pages of technical information. Pilots can ask questions verbally for quick answers, aiding their understanding of dense information and speeding up training. And when Airbus needed to accommodate its military consumers, deploying its Kubernetes cluster to a disconnected, “air-gapped” environment met the strict security requirements of military aircraft training.

A chatbot could look like the following diagram as Azure services work together to create a conversational experience. Its components could include security and governance, bot logic and user experience (UX), bot cognition and intelligence, data extract, transfer, and load (ETL) operations, quality assurance and enhancements, logging, and monitoring and reporting.

A diagram of a conversational experience enabled by Azure services

Airbus also used AI to improve its aircraft maintenance and safety. They deployed AI Anomaly Detector to gather and analyze telemetry data from multiple flights, enabling more profound insights into wear and tear on military aircraft operating in harsh conditions. This AI application helps Airbus better understand each airplane’s health. Airbus can predict and fix potential problems before they occur, improving the aircraft’s safety and operational readiness.

AI and Azure are changing how organizations like Airbus operate, even in strict, highly regulated markets with stringent security requirements. And by streamlining pilot training and staying ahead of aircraft maintenance, Airbus helps its military and commercial clients reliably deliver their goods across the globe, maintaining their critical supply chains.

Boosting Customer Service: Intelligent Apps in the Retail Industry

The retail industry has been among the first to adopt and benefit from intelligent apps. For example, the leading American used car retailer, CarMax, uses intelligent apps to transform the car shopping experience.

Before embracing AI, CarMax faced a significant challenge: Manually creating text summaries was time-consuming and labor-intensive. CarMax decided to leverage Azure OpenAI Service to automatically generate car research pages, offering customers valuable insights while enhancing the website’s search engine rankings.

This AI-driven approach has significantly streamlined the process, achieving in a few months what would have taken years manually. Also, the AI system condenses thousands of customer reviews into a few readable sentences, offering potential buyers a quick overview of others’ experiences.

CarMax has improved their customer experience while boosting operational efficiency through AI adoption. CarMax’s editorial staff focuses on creating strategic, longer-form content with the time saved, enhancing productivity, and driving more website traffic.

CarMax’s success story serves as a testament to the potential of intelligent apps in the retail industry.

Enhancing Cybersecurity: Intelligent Apps in Finance

Intelligent apps also impact the highly regulated financial services sector. Swift, a leading infrastructure provider for financial messaging services, uses intelligent apps to enhance decision-making and combat financial crime.

The massive growth of cross-border transactions and instant payment networks led to a surge in financial fraud that can cost the industry hundreds of billions of dollars every year. So, Swift and Microsoft partnered to build an anomaly detection model using federated learning techniques with Azure Machine Learning and Azure confidential computing.

Microsoft Purview helped govern data, as the federated learning method partially trained each model in a silo before aggregating them. This approach enabled Swift and Microsoft to develop a highly accurate model without copying or moving data from Swift members’ secure locations, ensuring the highest level of security and privacy.

Financial organizations can now deploy this trained ML model to run a real-time fraud detection system, lowering the cost of financial fraud.

Organizations can deploy a fraud detection system like the following example on Microsoft Azure. The system ingests data from streams or storage, runs analytics, and uses ML to detect fraud and store results. It then alerts users and other systems to the security issue.

A diagram of an example fraud detection system

Swift provides the innovative model to its banking partners, who can share data to further train the model. Azure confidential computing securely runs new models on current data to accumulate insights, generating an ongoing learning cycle. This iterative approach strengthens the global detection of fraudulent financial transactions through an increasingly faster and more accurate model. 

The collaboration between Swift and Microsoft demonstrates how combining various Azure services creates Intelligent Apps to enhance decision-making in finance. These apps solve real problems at an unprecedented global scale. 

Enhancing Efficiency: Intelligent Apps in Manufacturing

3M, known for Post-it Notes and other innovative products, wielded the power of Intelligent Apps to standardize, automate, and accelerate its sales forecasting. The company needed a unified, automated approach to replace its multiple manual methods.

It adopted Azure Machine Learning and other Azure AI Services to develop, train, and implement 1,500 custom models for each region and division, replacing manual methods. This solution delivers timely finance, sales, and marketing insights while freeing teams from time-consuming manual data manipulation tasks.

3M used Azure Machine Learning, automated machine learning (AutoML), and the Many Models Solution Accelerator to train and score numerous machine learning models in parallel. This approach significantly shortened the company’s development cycle time.

Then, 3M integrated Microsoft Power BI into their solution to compile and visualize results more effectively. Executives now have an unbiased and unfiltered view of data for better decision-making. With its improved ability to forecast sales, 3M can focus on manufacturing the products it needs efficiently.

3M’s success demonstrates how combining various Azure services can enhance efficiency in the manufacturing industry. These apps improve operational processes while enabling employees to focus on higher-level tasks. Strategic decisions are more targeted, and business conversations are more meaningful.

Driving Innovation: Intelligent Apps in the Technology Industry

As expected, the tech industry is at the forefront of leveraging Intelligent Apps to drive innovation. Elastic saw an opportunity to use intelligent apps to optimize its search services and meet customer needs more effectively. The company built a managed, secure, and scalable solution for its customers, Elastic Cloud, by leveraging Microsoft Azure.

Elastic Cloud uses Azure Machine Learning and other Azure AI Services to automate many basic management tasks, such as backing up data, upgrading, and scaling deployments. This approach enables Elastic’s customers to focus less on managing the software and more on serving their own clients. Customers can quickly deploy and manage Elastic Cloud on Kubernetes (ECK) using Azure Kubernetes Service.

As a result, Elastic’s cloud business is growing twice as fast as its on-premises solutions. Enterprise search users have reported significant improvements in system availability and employee satisfaction.

Additionally, Elastic incorporated Azure OpenAI Service into its Elasticsearch Relevance Engine (ESRE), providing more relevant answers to search queries. ESRE enables text, vector, and hybrid search, offering a more effective and accurate response to queries.

Elastic’s success story demonstrates how combining various Azure services can create intelligent Apps to drive innovation in the tech industry. These apps help improve operational processes and deliver better results quickly.

Revolutionizing Healthcare: Intelligent Apps in Medicine

Intelligent apps also drive transformative changes in the healthcare sector. Cambridgeshire and Peterborough NHS Foundation Trust, a healthcare provider in the United Kingdom, gradually shifted their on-premises systems to the cloud using Azure, improving flexibility, and lowering costs. They needed a solution for their electronic patient record (EPR) system that integrates 23 years of data across multiple systems and formats.

The Trust used Microsoft Azure and Azure Cognitive Search, including semantic search for understanding intent and contexts and cognitive capabilities for extracting insights from data, to standardize and automate data management. This approach made legacy, archive, and live data more discoverable, saving clinicians’ time. The Trust’s solution searches through millions of clinical documents in seconds, even recognizing handwritten notes without needing a separate optical character recognition (OCR) solution to extract text from images (Azure Cognitive Search automatically does it). Clinicians can now uncover previously inaccessible insights, enabling them to offer more personalized care.

The Trust also used Integration Services tools such as Azure Logic Apps to create workflows without writing code and Event Grid to connect Azure and third-party services through a publisher-subscriber model. They switched on a live feed from their new EPR system to Azure to feed real-time information into other applications. Researchers can now access anonymized data to fuel medical innovations beyond the NHS.

The healthcare provider also invested heavily in Power BI’s data visualization and analytical functions to sift through massive amounts of information. This level of data accessibility was previously impossible with their on-premises system. The new insights have significantly improved clinicians’ efficiency for better patient care.

Data accessibility and security are critical in the healthcare industry. Intelligent Apps empower organizations like the Trust to revolutionize medical care. AI capabilities helped the Trust improve its operational efficiency and user experiences, setting a new standard for healthcare in the digital age.

Summary

AI is driving transformation and real-world successes across industries. Intelligent apps are reshaping our world by streamlining operations in manufacturing and logistics, enhancing customer experience in the retail sector, improving decision-making in finance, driving innovation in tech, and revolutionizing healthcare.

As organizations adopt AI, they’re innovating in ways that were once too difficult, expensive, or outright impossible. Companies that wait to embrace intelligent apps risk the chance of falling behind. By embracing intelligent apps and leveraging the capabilities of Azure, you can harness the transformative potential of AI to remain on the cutting edge of technology.

Exercise

- - +
Skip to main content

11 posts tagged with "ask-the-expert"

View All Tags

· 10 min read
It's 30DaysOfIA

Explore how digital natives and Independent Software Vendors (ISVs) can transform their business operations and generate higher value for customers with generative AI and intelligent apps.

What We'll Cover:

  • Potential of Generative AI in application development
  • Challenges with Generative AI
  • Integrating Generative AI with business operations

image of generative AI and intelligent apps used in business

Unleashing Business Transformation: How Digital Natives Can Leverage Generative AI

Generative AI is no longer a technological flight of fancy. It’s here, and it’s rewriting the rules of business innovation. 

This type of AI leverages machine learning algorithms to create new data with similar characteristics to a given dataset. It goes beyond text generation using generative pretrained transformer (GPT) models, covering an array of applications that span from image synthesis to translations and more. It also powers a new class of intelligent applications, which combine AI capabilities with traditional software functions for enhanced user experiences and advanced business solutions. 

Generative AI is transforming how digital natives and independent software vendors (ISVs) operate. Generative AI offers new possibilities for creating unique business value, enhancing customer experiences, and driving growth. 

It plays a critical role in applications, improving functionality, aiding decision-making, and automating complex tasks to deliver dynamic, uniquely personalized user experiences. Generative AI also enables and automates data-driven outcomes, functions, and features.

Integrating AI capabilities into customer-facing apps and turning them into intelligent apps benefits both the customer and the business, as we discussed in Demystifying Intelligent Applications: Leveraging AI in App Development. You get faster product development workflows and better business intelligence for sales and marketing, while your users get more natural and context-aware features and interactions. 

That said, to realize generative AI’s potential, you need some strategy and knowledge of how and where it fits into your current business and operations. We’ll examine this technology’s potential to transform business operations, its risks, and rewards, and how to integrate it into your existing operations. 

Unpacking the Potential of Generative AI

Generative AI is creating new opportunities in the digital playing field. It empowers businesses to generate insights, design products, and automate processes in ways and at speeds that were previously unimaginable. You can now predict customer behavior more accurately, offer highly personalized user experiences, and prototype innovative ideas rapidly. 

For example, Microsoft used generative AI to reinvent web search in the new AI-powered Bing and Edge. The search experience has evolved from a solitary user flow of typing in keywords and scrolling through results. It’s now a conversational experience where Bing uses context to offer better quality and more personalized search queries and results. 

Similarly, Microsoft harnessed generative AI to transform its productivity suite into intelligent apps. Through Copilot for Microsoft 365, PowerPoint now empowers users to tell their stories by using DALL-E to generate images effortlessly. It also helps rephrase text-heavy slides to be more straightforward. Outlook now assists users to compose more effective emails, enabling better communication.

These are just some examples of how AI powers day-to-day work and operations. 

Businesses that ignore generative AI miss out on untapped market opportunities, letting competitors gain an edge in the digital race. By seeking ways to rethink assumptions and reinvent existing approaches, you can effectively use generative AI to create new opportunities, open products to new use cases and markets, unlock new growth and customer satisfaction levels, and increase your revenue.

info

Register for the intelligent apps webinar on Driving Business Value by Modernizing with Cloud-Native & AI with Microsoft and Forrester on September 26.

Explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries. 

The Crucial Role of Generative AI in Business Operations

Generative AI can be a powerful catalyst for business transformation. Let’s quickly tour some ways it can redefine business operations. 

Product development is traditionally a long and painstaking process. Now, tools like GitHub Copilot and TestPilot use generative AI to make coding and testing software easier, faster, and more robust. 

Similarly, machine learning as a service technology, such as Azure Machine Learning, democratizes the availability of machine learning. It helps collect, clean, and train models across datasets, making engineering and analyzing data more efficient and accessible.  

Generative AI also simplifies the process of creating intelligent apps. Previously, simulating thousands of prototypes, predicting their performance, and narrowing down to the most viable options could take weeks, months, or even years of iteration and data analysis. With generative AI, this process can be as quick as days, hours, or perhaps minutes. 

Customer experience is also ripe for transformation. Personalized recommendations drive engagement and sales, and generative AI can scale that personalization to every customer interaction using comprehensive and continually updated data. For instance, Microsoft leverages AI to enable personalization in Dynamics 365 Customer Insights. Using this tool, you can ask questions about your customers in natural language and gain valuable and actionable insights into your customer base. 

Lastly, marketing and sales can benefit from generative AI. For example, identifying current and future key markets and emerging trends helps companies stay ahead of the curve. Tools like Copilot in Power BI help you quickly understand and uncover insights about your business, enabling your team to craft targeted campaigns, create engaging content, and optimize your business strategy. 

Leveraging this full generative AI potential requires the right tools. GPT-4, for instance, can be instrumental in realizing effective business transformation. It excels in understanding context, generating human-like text, and learning from minimal data input, making it a formidable tool in any digital native's arsenal. Platforms like Azure OpenAI Service bring GPT-4’s power to your fingertips.

However, as with any powerful technology, it’s unwise to treat generative AI as a perfect solution. You must know the challenges and risks of not leveraging it effectively, including technical and human-factor considerations. 

Without generative AI, your company risks missing the opportunity to please customers via personalization, as discussed previously. You’ll miss sales opportunities via business intelligence insights, limiting your ability to grow revenue and keep up with the competition. 

One challenge of leveraging generative AI’s full potential is finding the right tools. Fortunately, you can implement generative AI in your organization without investing in infrastructure, computational power, and expertise. Platforms like Azure OpenAI Service and Azure AI Services make AI easily accessible. They also work with Azure compute and data services like Azure Functions and Azure Kubernetes Service (AKS), enabling you to build intelligent apps easily and quickly. 

However, generative AI can’t do everything. It may need human expertise and intervention to use it fully. 

For example, generative AI can create unique data. However, the data may have limited diversity and complexity. The AI may need help recognizing additional considerations or requirements specific to a problem. It may also lack context due to various factors, such as the information’s freshness. Also, since it’s an emerging field that’s potentially worth $4.4 trillion, we’re still learning about the nuances and potential regulatory impact of generative AI. 

Perhaps the most crucial and challenging aspect of adopting generative AI is an organization’s required cultural shift. Your teams must learn how to use AI as your company fosters an understanding of AI’s potential and limitations. 

Begin integrating AI literacy into your company’s DNA. Knowledge of its potential and the readily available tools can inspire your teams to incorporate generative AI into their daily workflows and business operations quickly, improve productivity, and deliver superior customer experiences. 

No company wants to be outpaced by its competitors. You can balance generative AI’s potential with your business operations by staying aware of and adapting to the dynamic AI landscape.

Achieving Harmony: Integrating Generative AI with Existing Business Operations

Integrating generative AI into existing business operations doesn’t have to be disruptive. With the correct strategy, you can do so seamlessly. Your goal should be to augment and enhance your operations, not disrupt and replace. 

For example, Microsoft Cloud technologies leveraged AI to relieve healthcare organizations’ challenges, such as talent shortage and workforce burnout. Providers automated their workflows, invested in specific applications and connectors, and enhanced support for data and operations. 

Azure AI Services, such as text analytics for health, enable healthcare organizations to understand their largely unstructured and untapped medical data. Project Health Insights leans on AI to help healthcare professionals gain actionable insights and inferences from patient data. 

Azure Health Bot, an Intelligent App, empowers organizations to build and deploy legally compliant, powerful conversational healthcare experiences to patients. 

A successful AI integration like this begins with understanding. Knowing what generative AI can do and setting realistic expectations can help you identify where and how AI can best serve your operations. 

Next, you can foster a culture of AI literacy across teams, speeding up adoption by helping them understand how it can aid their work. The more familiar your team is with AI and its possibilities, the smoother the transition. Training and education programs play a pivotal role here. 

Finally, integration requires a systematic approach. It’s not about overhauling everything at once — it's about identifying areas where AI can add the most value, then gradually experimenting and scaling its use. Successful integration stories often feature a phased approach, starting small and growing steadily. 

As we move toward a future where generative AI is a staple in business operations, remember that every journey is unique. Although there’s no one-size-fits-all solution, a strategic approach and a keen understanding of your business needs will help you navigate this new landscape successfully.

Conclusion

Generative AI offers plenty of opportunities for businesses that embrace it. It’s fueling the next generation of intelligent applications, transforming everything from product development to customer engagement to marketing. This technology is the key to unlocking unprecedented growth and innovation. 

While the journey may have challenges, the potential rewards make generative AI worth exploring. As generative AI advances, Azure AI’s broad range of tools and services will help you navigate this fascinating field.

Exercise

  • Watch Episode 02 of the serverless edition Learn Live session to learn how to build, test and deploy an end-to-end intelligent app solution.
  • Complete the Cloud Skills Challenge to build on your AI skills.
  • Register for Ask the Expert: Azure Functions on September 26 for a live Q&A with the Product Team on how to build AI apps using serverless computing.
+ + \ No newline at end of file diff --git a/30daysofIA/tags/ask-the-expert/page/6/index.html b/30daysofIA/tags/ask-the-expert/page/6/index.html index bd3767426e..9ce238307c 100644 --- a/30daysofIA/tags/ask-the-expert/page/6/index.html +++ b/30daysofIA/tags/ask-the-expert/page/6/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "ask-the-expert" | Build Intelligent Apps On Azure +11 posts tagged with "ask-the-expert" | Build Intelligent Apps On Azure @@ -14,13 +14,13 @@ - - + +
-

9 posts tagged with "ask-the-expert"

View All Tags

· 8 min read
It's 30DaysOfIA

Let’s start with understanding the power of intelligent applications.

What We'll Cover:

  • What are intelligent applications?
  • Categories of Intelligent Apps
  • Breaking down the role of AI in intelligent apps

A decorative header image for the blog

Demystifying Intelligent Applications: Leveraging AI in App Development

Until recent years, when you heard the term “artificial intelligence” (AI), you might have pictured autonomous robots from sci-fi films. Now, perhaps your mind jumps to big data and machine learning (ML). Maybe you’re even thinking about the latest ChatGPT plugins and transformer architectures. Today, AI is more than these imaginations: it’s a reality increasingly woven into our day-to-day routines through intelligent applications.

Intelligent apps combine cloud-scale compute, data, and predictive or generative AI. These applications help you automate tasks and make data-driven decisions, aiding in leveraging business intelligence to gain meaningful, practical outcomes. 

For developers, the AI landscape isn’t an abstract complexity. It’s a real, tangible opportunity for evolving traditional applications, making them “intelligent,” and delivering more value to users. And with tools like Azure Kubernetes Service, Azure Cosmos DB, and Azure AI Services, stepping into the world of intelligent apps isn’t a steep climb but a steady trek across a manageable learning curve.

The Three Categories of Intelligent Applications

Before we start building, it’s critical to understand what makes an app “intelligent.” It’s not a universal definition so much as an identifier for apps that fall into one or more of the three primary categories: 

  • Outcome-based — This designation represents the broadest category of intelligent apps, whose principal value for users is the intelligent outcomes they provide. These apps process data to provide insightful results that enhance decision-making or help with tasks like trend prediction, resource management, and customer relationship management.

  • Functionality-based — This type of intelligent app takes AI integration a degree further, integrating AI as a function of the app to create or process its outputs, enhancing the app’s overall interactivity and usefulness.

  • Feature-based — This is the narrowest category, in which the app’s AI/ML component is one of its core features and primary selling points

Let’s dive more deeply into each category.

Outcome-Based Apps: Intelligent Outcomes Drive Success

Outcome-based applications are all about the results provided to the user. They function beyond merely processing data to offer insightful outcomes that drive success.

Think of a personal fitness tracker app that uses AI to analyze the user’s activity and provides actionable recommendations for achieving a goal. This type of app not only tracks your steps, sleep patterns, and heart rate but uses that data to provide you with personalized health advice. It helps you make informed lifestyle choices — the “intelligent” outcome.

Another example is a social media marketing app that uses AI for time-saving data analysis, task automation, and trend prediction. AI-based sentiment analysis can gauge customer perception while natural language processing (NLP) accurately interprets customer inquiries. Task automation can schedule posts and automate responses to common customer queries, and trend forecasting helps model and predict customer behavior to further personalize targeted promotional efforts. Building this type of intelligence into your apps provides cost-effective, actionable insights that guide sales and marketing strategies.

Functionality-Based Apps: AI-Driven Features Enhance User Experiences

As the name suggests, functionality-based applications integrate AI into how the app functions, but the end user drives the action and outcome. In this category of apps, AI takes a more active role in the user’s interaction by allowing them to use AI-created or processed output. These apps leverage AI technologies like Azure AI Services for NLP, image recognition, and pattern recognition to improve user experience.

Consider a music app that generates personalized playlists based on your listening habits or your smartphone’s camera app that uses AI to recognize faces and optimize photo settings. Even more advanced are the language translation apps that translate text from one language to another and can even understand and maintain the context and subtleties of the conversation. They use NLP to accomplish these feats.

Similarly, consider the latest e-commerce apps, whose AI-driven functionalities enable customers to try on clothes virtually, significantly enhancing the online shopping experience.

Feature-Based Apps: Integrating Advanced AI/ML Components

Finally, there are feature-based intelligent apps. These apps incorporate advanced AI/ML components like neural networks, internal large language models (LLMs), and sophisticated algorithms as core features for the user. In this type of application, AI is the primary feature for users to engage with and the primary selling point for the app.

Some examples of feature-based applications are chatbots and virtual agents built using Azure Bot Service. These robo-representatives use AI to provide more natural and human-like conversation, balancing superior efficiency with a friendlier, more personalized user experience. The new Bing is a prime example of this implementation, integrating AI to give users a knowledgeable copilot for web searches.

Similarly, OpenAI’s ChatGPT allows users access to its state-of-the-art language models available as Microsoft’s Azure OpenAI Service. It leverages the power of the highly sophisticated GPT-3 LLM to perform tasks like drafting emails, writing code, answering general knowledge questions, translating between languages, and tutoring in numerous subjects.

These apps don’t just use AI to provide outputs or improve user experiences: They push the boundaries of what’s possible in app development by integrating AI/ML components.

info

Register for the live Q&A Ask The Expert session on September 26 with the Azure Functions product engineering team.

Breaking Down the Role of AI in Intelligent Applications

Intelligent apps reflect the merging of traditional software design with the dynamic capabilities of AI. Let’s explore how AI integrates and amplifies these applications.

At its core, AI augments applications with the capacity to self-learn and self-improve. It allows you to create applications that predict, react, and adapt to a constantly changing environment. In the power grid management system for a major city, an AI-infused application can forecast power usage trends, react to anomalies, and adapt to sudden demand surges, ensuring optimal efficiency and preventing blackouts.

But AI’s role doesn’t end at prediction or decision-making: It also automates tasks. In enterprise solutions, it has moved beyond simple automation to encompass intelligent process automation (IPA). In this arena, AI algorithms understand, learn, and then automate entire digital workflows. For developers, this is a fertile ground for creating applications that can dramatically improve the operational efficiency of businesses.

Even more, AI substantially aids in development tasks. For example, there’s GitHub Copilot, an AI-powered code assistant that offers suggestions based not only on syntax but on development best practices and the intent of the code. AI has evolved from a tool you use to build apps to a partner that helps in creating them. 

Using AI to build intelligent apps isn’t about throwing out what’s worked before. It’s about enhancing the components that have the most impact and achieve the best results. Incorporating AI in apps elevates their predictive abilities, streamlines complex tasks, and even aids in the development and creation process. When you build intelligent apps, you don’t just address current problems. You anticipate future challenges and craft solutions that adapt over time.

Summary

As developers the power of AI is in our hands, and it’s our responsibility to harness its full potential. Each category tells the story of AI's transformative potential, from applications that deliver intelligent outcomes to those with AI capabilities to enrich the user experience and those pushing boundaries with advanced AI/ML technology.

Accessible and robust platforms like Azure AI can simplify the process of developing Intelligent Applications. Whether you’re refining an existing app or building a new one, just entering the world of AI, or already familiar with the landscape, take a moment to appreciate the potential that AI integration offers. You’re embracing this exciting frontier and shaping the future of app development. 

Exercise

  • Complete the Cloud Skills Challenge to build on your apps and AI skills.
  • Watch Episode 01 of the Serverless Edition Learn Live session to learn how to build an end-to-end intelligent app solution.
- - +
Skip to main content

11 posts tagged with "ask-the-expert"

View All Tags

· 9 min read
It's 30DaysOfIA

In the part of our series on intelligent apps, we’ll explore how AI is transforming application development, from design and architecture to building.

What We'll Cover:

  • Revolutionizing application development using AI
  • Infusing AI in application design architecture
  • AI Assisted Pair Programming
  • Future of App Development

A decorative image of application development with AI

Reimagining Application Development With AI: A New Paradigm

Artificial intelligence (AI) is impacting every industry. AI-powered development tools offer new functionalities and assist software developers throughout the development process, revolutionizing app development. From initial design and architecture to coding and debugging, AI reshapes how we approach application conception, creation, and iteration.

In part one of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” we explored the new breed of intelligent apps and how they use AI. In this article, we’ll see how AI technologies enhance developer toolkits and shape the future of app development, particularly through AI-enabled coding assistance and debugging.

The Dawn of Intelligent Apps: Revolutionizing Application Development

AI is more than just another addition to the development toolbox—it represents a revolution bringing unprecedented efficiency, innovation, and user engagement to our applications. 

What sets intelligent apps apart is how they can adapt and learn, helping us deliver unique, real-time user experiences.  

One example is an AI-driven health application that can track biometric data, predict potential health issues, provide personalized health advice, and even guide emergency services during a medical crisis. An AI-enabled language learning app that adapts to our learning speeds, recognizes our problem areas, and customizes its content to help us improve our language proficiencies is another example.  

Intelligent apps like these don’t just use data for recommendations—they can make critical decisions and provide sophisticated services that were previously impossible. 

For developers, intelligent apps mean we’re no longer just coding—we’re teaching applications how to think, adapt, and make decisions. AI is reshaping every step of the development journey, helping us to build more intuitive and user-focused products. 

Next, we’ll examine the impact of AI on application design and architecture and how AI-assisted “pair programming” changes software development.

info

Register for the FREE webinar on Intelligent Apps with Microsoft and Forrester on September 26 to explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries.

There will be a showcase of real-world use cases that demonstrate how AI can be seamlessly integrated into cloud-native environments driving tangible business value.

AI in Design and Architecture: Fueling Creativity and Efficiency

AI technologies help developers automate and streamline their processes and provide new ways to think about design.  

In traditional application development, the design and architecture phase can take significant time, effort, and expertise. For instance, adopting a monolithic architecture or a microservices-based design can significantly increase coding, testing, and debugging complexity. The app may even behave differently when hosted locally than in a deployed environment. It may take hours to track down an issue manually—time developers could spend elsewhere. 

Conversely, we can generate numerous design iterations in seconds with AI, each optimized to fulfill specific user needs and business goals. AI tools can quickly model various scenarios' impacts and software design patterns in the architectural design phase. It provides insights into performance and potential issues and suggests improvements. AI can also generate skeleton code, allowing developers to prototype different solutions or test specific components and technologies rapidly. By reviewing thousands of lines of code in seconds, AI allows us to focus on creating apps that serve users through feature development instead of searching for bugs.  

Integrating AI into the design and architecture phase can even extend our creative capabilities. By leveraging the power of generative AI—like large language models (LLMs)—and their broad knowledge and context going beyond software, we can better brainstorm apps and feature ideas. We can go beyond code and software architecture and get ideas about user experience, logo and branding, or marketing concepts. And as AI tools deliver insights and ideas we would have never considered; it encourages more innovation.

info

Watch Episode 01 of the Learn Live session to learn how to build an end-to-end intelligent app solution architecture.

The Power of AI-Assisted Pair Programming in Building Applications

AI-assisted pair programming is a collaborative coding approach where an AI system—like GitHub Copilot or TestPilot—assists developers during coding. It’s an increasingly common approach that significantly impacts developer productivity. In fact, GitHub Copilot is now behind an average of 46 percent of developers’ code and users are seeing 55 percent faster task completion on average. 

For new software developers, or those interested in learning new skills, AI-assisted pair programming are training wheels for coding. With the benefits of code snippet suggestions, developers can avoid struggling with beginner pitfalls like language syntax. Tools like ChatGPT can act as a personal, on-demand tutor—answering questions, generating code samples, and explaining complex code syntax and logic. These tools dramatically speed the learning process and help developers gain confidence in their coding abilities. 

Building applications with AI tools hastens development and provides more robust code. For example, GitHub Copilot helps us write new code accurately. It also quickly makes sense of existing code, suggests whole new lines or blocks of code, and even detects errors and proposes fixes. It’s like having a second coder working alongside us—one who never tires and is trained on billions of lines of code. 

However, it’s also important to acknowledge the limitations of AI. While generative AI-assistance technologies like GitHub Copilot are impressive, they’re still imperfect. They can sometimes produce erroneous, non-functional, or vulnerable code. They can also lack nuanced, domain-specific knowledge.  

So, although these tools are helpful, we can’t rely on them fully—these are programming aids rather than definitive solutions. AI-assisted pair programming is about augmenting the human developer’s skills, not replacing them.

Debugging and Improving Code With AI: Enhancing Application Quality

Identifying and correcting code errors is often time-consuming and challenging for developers. AI streamlines the debugging process by automatically detecting anomalies, suggesting fixes, and learning from previous bugs to prevent them from recurring. 

We can use generative AI to create unit tests from code snippets, from simple parameter checking and input validation to complex mocks of database and network services. Automated AI testing improves code coverage and robustness. It saves significant time compared to manually crafting unit tests. 

AI also revolutionizes code reviews by scanning and highlighting potential issues—like code patterns leading to a bug or a coding standards violation—and suggesting ways to simplify or refactor the code for readability and performance. This reduces human error and makes the code review process more efficient. Its benefits extend to small teams, where the availability of developers is crucial to deadlines, and large teams, where effective collaboration depends on readability and code consistency. AI further saves time by triaging issues so developers can prioritize the most mission-critical ones. 

Aside from improving code quality, AI is critical to understanding system behavior. Intelligent AI-assisted software development tools can monitor and audit system behavior, giving developers a better insight into how their code performs in different scenarios. AI can track patterns, identify inefficiencies, and provide alerts when they detect unusual behavior—enabling developers to enhance their code further. 

By predicting and identifying potential vulnerabilities, intelligent AI-assisted coding apps like Microsoft Security Copilot are pivotal in ensuring application functionality, reliability, and security. These AI tools are continually trained and upgraded with large codebases and user feedback that refines their responses. This ability to learn from past mistakes makes AI an incredibly powerful tool that will continue to expand its capabilities in the future.

info

Complete the AI Cloud Skills Challenge to build on you AI application development skills with paired programming using generative AI and GitHub Copilot.

AI and the Future of Application Development

The rapid advancements in AI and machine learning technologies point to an exciting future for application development. 

In “The Future of Applications,” software developer Mike Cann illustrates how LLMs might revolutionize application development. Cann that suggests fully-autonomous coding—AI-generated applications created entirely from prompts—may supersede traditional hand-crafted applications. Dynamically generated user interfaces could offer personalized and customizable experiences while we can design APIs for machine consumption and efficiency rather than human readability. Furthermore, we can store data in less structured, more adaptable, and flexible forms. 

AI is already making application development more accessible. With AI handling the complex, technical aspects of development, less-technical individuals can take a greater role in building applications. Consequently, AI can potentially democratize software engineering. 

Intelligent apps will continue to evolve, becoming more efficient, adaptable, and capable of learning from user interactions. Similarly, AI will inevitably become integral in every stage of app development.

Summary

AI has already created a new development paradigm—one in which machine intelligence and human creativity come together to produce innovative, efficient, and robust applications. AI already assists us at every stage of app development, from design and architecture to coding and debugging. 

Developers who embrace AI tools and integrate them into their processes can enhance their productivity, improve the quality of their applications, and expand the possibilities of their application development.  

The future of development is automated and AI-driven, and it’s here today. Getting started is easy—try an AI-powered development tool like GitHub Copilot to see how AI-assisted pair programming can enhance your productivity and code quality.

Exercise

+ + \ No newline at end of file diff --git a/30daysofIA/tags/ask-the-expert/page/7/index.html b/30daysofIA/tags/ask-the-expert/page/7/index.html index aa55a73c1d..b075898207 100644 --- a/30daysofIA/tags/ask-the-expert/page/7/index.html +++ b/30daysofIA/tags/ask-the-expert/page/7/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "ask-the-expert" | Build Intelligent Apps On Azure +11 posts tagged with "ask-the-expert" | Build Intelligent Apps On Azure @@ -14,14 +14,13 @@ - - + +
-

9 posts tagged with "ask-the-expert"

View All Tags

· 4 min read
It's 30DaysOfIA

This Fall focus on building intelligent apps using AI and cloud-native technologies. #FallForIntelligentApps brings to you a learning journey to build your skills on creating differentiated experiences while modernizing your applications. It’s time to learn it all.

What We'll Cover

image

Get Ready To #FallForIntelligentApps starting September 18!

Today, we kick off the Fall season with content and activities to skill you up on all things Intelligent Apps or AI Apps on Azure with content, events, and community interactions! Read on to learn about what is coming!

Explore Our Initiatives

We have a number of initiatives planned for the month to help you learn and skill up on relevant technologies. Click on the links to visit the relevant pages for each

image

Register for the events!

What are 4 things you can do today, to jumpstart your learning journey?

#30Days Of Intelligent Apps

#30DaysOfIA is a series of daily blog posts grouped into themed weeks - taking you from core concepts to end-to-end solution examples in 30 days. Each article will provide conceptual lessons paired with exercises and resources to help you reinforce learnings and take next steps.

This series takes you through learning journey in six stages, each building on the previous week to help you skill up in a beginner-friendly way:

We will start with defining intelligent apps and then expand on how to build with cloud-native technologies like Azure Kubernetes Service, Azure Container Apps and Azure Functions, and take you through end-to-end scenarios for real world application development. Before we dive deep on intelligent apps, here is a high-level overview of the Intelligent Apps landscape on Azure:

image

Containers on Azure services offer you a wide range of capabilities, from simplicity to control to suit your different needs.

image -image

To start with the basics for developing Kubernetes applications, explore #30DaysOfCloudNative.

Cloud-native development when paired with serverless computing enhances your solution architecture for building cost optimized, resilient applications.

image

To start with the basics for serverless computing, explore #30DaysOfServerless.

Let’s Get Started

Now you know everything! We hope you are as excited as we are to dive into a full month of active learning and doing! Don't forget to subscribe for updates in your favorite feed reader! And look out for our first Intelligent Apps post Monday!

- - +
Skip to main content

11 posts tagged with "ask-the-expert"

View All Tags

· 10 min read
It's 30DaysOfIA

Explore real-world examples of how Intelligent Apps revolutionize logistics, retail, finance, manufacturing, tech, and medicine.

What We'll Cover:

  • Real-world businesses revolutionizing operations with Intelligent Apps
  • High level architecture for industry scenarios
  • Implementation architecture overview across industries

A decorative header image for the blog

Real-world Success Stories

According to IBM’s 2022 Global AI Adoption Index, 35% of surveyed companies already use AI in their business, and 42% are exploring the idea. Organizations lagging behind the AI curve will quickly fall behind early adopters, who are already enjoying its benefits and boosting their investment in this technology.

In the first part of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” we explored how organizations can use AI-powered apps, or intelligent apps, to transform their operations, decision-making, and customer experiences, leaping ahead of the competition.

Intelligent apps significantly improve operational efficiency and customer engagement across industries like logistics, retail, finance, manufacturing, tech, and healthcare. In this article, we’ll spotlight real-world success stories of businesses revolutionizing operations with intelligent apps using Microsoft Azure’s app, data, and AI services.

info

Register for the Learn Live on September 21 for a live guided experience with SMEs on how to build end-to-end solution architecture for real world implementation of Intelligent Apps.

Streamlining Operations: Intelligent Apps in the Airline Industry

Aerospace pioneer Airbus leveraged intelligent apps to streamline its operations and innovate its services with two ground-breaking modernizations that use Azure apps, data, and AI services to reimagine pilot training and predict aircraft maintenance issues. 

Aviation technology’s rapid evolution compelled Airbus to reconceive their approach to training pilots. The aircraft manufacturer created a pilot training chatbot by combining Azure AI Services with Speech to Text and Text to Speech. The bot helps trainee pilots navigate over 6,000 pages of technical information. Pilots can ask questions verbally for quick answers, aiding their understanding of dense information and speeding up training. And when Airbus needed to accommodate its military consumers, deploying its Kubernetes cluster to a disconnected, “air-gapped” environment met the strict security requirements of military aircraft training.

A chatbot could look like the following diagram as Azure services work together to create a conversational experience. Its components could include security and governance, bot logic and user experience (UX), bot cognition and intelligence, data extract, transfer, and load (ETL) operations, quality assurance and enhancements, logging, and monitoring and reporting.

A diagram of a conversational experience enabled by Azure services

Airbus also used AI to improve its aircraft maintenance and safety. They deployed AI Anomaly Detector to gather and analyze telemetry data from multiple flights, enabling more profound insights into wear and tear on military aircraft operating in harsh conditions. This AI application helps Airbus better understand each airplane’s health. Airbus can predict and fix potential problems before they occur, improving the aircraft’s safety and operational readiness.

AI and Azure are changing how organizations like Airbus operate, even in strict, highly regulated markets with stringent security requirements. And by streamlining pilot training and staying ahead of aircraft maintenance, Airbus helps its military and commercial clients reliably deliver their goods across the globe, maintaining their critical supply chains.

Boosting Customer Service: Intelligent Apps in the Retail Industry

The retail industry has been among the first to adopt and benefit from intelligent apps. For example, the leading American used car retailer, CarMax, uses intelligent apps to transform the car shopping experience.

Before embracing AI, CarMax faced a significant challenge: Manually creating text summaries was time-consuming and labor-intensive. CarMax decided to leverage Azure OpenAI Service to automatically generate car research pages, offering customers valuable insights while enhancing the website’s search engine rankings.

This AI-driven approach has significantly streamlined the process, achieving in a few months what would have taken years manually. Also, the AI system condenses thousands of customer reviews into a few readable sentences, offering potential buyers a quick overview of others’ experiences.

CarMax has improved their customer experience while boosting operational efficiency through AI adoption. CarMax’s editorial staff focuses on creating strategic, longer-form content with the time saved, enhancing productivity, and driving more website traffic.

CarMax’s success story serves as a testament to the potential of intelligent apps in the retail industry.

Enhancing Cybersecurity: Intelligent Apps in Finance

Intelligent apps also impact the highly regulated financial services sector. Swift, a leading infrastructure provider for financial messaging services, uses intelligent apps to enhance decision-making and combat financial crime.

The massive growth of cross-border transactions and instant payment networks led to a surge in financial fraud that can cost the industry hundreds of billions of dollars every year. So, Swift and Microsoft partnered to build an anomaly detection model using federated learning techniques with Azure Machine Learning and Azure confidential computing.

Microsoft Purview helped govern data, as the federated learning method partially trained each model in a silo before aggregating them. This approach enabled Swift and Microsoft to develop a highly accurate model without copying or moving data from Swift members’ secure locations, ensuring the highest level of security and privacy.

Financial organizations can now deploy this trained ML model to run a real-time fraud detection system, lowering the cost of financial fraud.

Organizations can deploy a fraud detection system like the following example on Microsoft Azure. The system ingests data from streams or storage, runs analytics, and uses ML to detect fraud and store results. It then alerts users and other systems to the security issue.

A diagram of an example fraud detection system

Swift provides the innovative model to its banking partners, who can share data to further train the model. Azure confidential computing securely runs new models on current data to accumulate insights, generating an ongoing learning cycle. This iterative approach strengthens the global detection of fraudulent financial transactions through an increasingly faster and more accurate model. 

The collaboration between Swift and Microsoft demonstrates how combining various Azure services creates Intelligent Apps to enhance decision-making in finance. These apps solve real problems at an unprecedented global scale. 

Enhancing Efficiency: Intelligent Apps in Manufacturing

3M, known for Post-it Notes and other innovative products, wielded the power of Intelligent Apps to standardize, automate, and accelerate its sales forecasting. The company needed a unified, automated approach to replace its multiple manual methods.

It adopted Azure Machine Learning and other Azure AI Services to develop, train, and implement 1,500 custom models for each region and division, replacing manual methods. This solution delivers timely finance, sales, and marketing insights while freeing teams from time-consuming manual data manipulation tasks.

3M used Azure Machine Learning, automated machine learning (AutoML), and the Many Models Solution Accelerator to train and score numerous machine learning models in parallel. This approach significantly shortened the company’s development cycle time.

Then, 3M integrated Microsoft Power BI into their solution to compile and visualize results more effectively. Executives now have an unbiased and unfiltered view of data for better decision-making. With its improved ability to forecast sales, 3M can focus on manufacturing the products it needs efficiently.

3M’s success demonstrates how combining various Azure services can enhance efficiency in the manufacturing industry. These apps improve operational processes while enabling employees to focus on higher-level tasks. Strategic decisions are more targeted, and business conversations are more meaningful.

Driving Innovation: Intelligent Apps in the Technology Industry

As expected, the tech industry is at the forefront of leveraging Intelligent Apps to drive innovation. Elastic saw an opportunity to use intelligent apps to optimize its search services and meet customer needs more effectively. The company built a managed, secure, and scalable solution for its customers, Elastic Cloud, by leveraging Microsoft Azure.

Elastic Cloud uses Azure Machine Learning and other Azure AI Services to automate many basic management tasks, such as backing up data, upgrading, and scaling deployments. This approach enables Elastic’s customers to focus less on managing the software and more on serving their own clients. Customers can quickly deploy and manage Elastic Cloud on Kubernetes (ECK) using Azure Kubernetes Service.

As a result, Elastic’s cloud business is growing twice as fast as its on-premises solutions. Enterprise search users have reported significant improvements in system availability and employee satisfaction.

Additionally, Elastic incorporated Azure OpenAI Service into its Elasticsearch Relevance Engine (ESRE), providing more relevant answers to search queries. ESRE enables text, vector, and hybrid search, offering a more effective and accurate response to queries.

Elastic’s success story demonstrates how combining various Azure services can create intelligent Apps to drive innovation in the tech industry. These apps help improve operational processes and deliver better results quickly.

Revolutionizing Healthcare: Intelligent Apps in Medicine

Intelligent apps also drive transformative changes in the healthcare sector. Cambridgeshire and Peterborough NHS Foundation Trust, a healthcare provider in the United Kingdom, gradually shifted their on-premises systems to the cloud using Azure, improving flexibility, and lowering costs. They needed a solution for their electronic patient record (EPR) system that integrates 23 years of data across multiple systems and formats.

The Trust used Microsoft Azure and Azure Cognitive Search, including semantic search for understanding intent and contexts and cognitive capabilities for extracting insights from data, to standardize and automate data management. This approach made legacy, archive, and live data more discoverable, saving clinicians’ time. The Trust’s solution searches through millions of clinical documents in seconds, even recognizing handwritten notes without needing a separate optical character recognition (OCR) solution to extract text from images (Azure Cognitive Search automatically does it). Clinicians can now uncover previously inaccessible insights, enabling them to offer more personalized care.

The Trust also used Integration Services tools such as Azure Logic Apps to create workflows without writing code and Event Grid to connect Azure and third-party services through a publisher-subscriber model. They switched on a live feed from their new EPR system to Azure to feed real-time information into other applications. Researchers can now access anonymized data to fuel medical innovations beyond the NHS.

The healthcare provider also invested heavily in Power BI’s data visualization and analytical functions to sift through massive amounts of information. This level of data accessibility was previously impossible with their on-premises system. The new insights have significantly improved clinicians’ efficiency for better patient care.

Data accessibility and security are critical in the healthcare industry. Intelligent Apps empower organizations like the Trust to revolutionize medical care. AI capabilities helped the Trust improve its operational efficiency and user experiences, setting a new standard for healthcare in the digital age.

Summary

AI is driving transformation and real-world successes across industries. Intelligent apps are reshaping our world by streamlining operations in manufacturing and logistics, enhancing customer experience in the retail sector, improving decision-making in finance, driving innovation in tech, and revolutionizing healthcare.

As organizations adopt AI, they’re innovating in ways that were once too difficult, expensive, or outright impossible. Companies that wait to embrace intelligent apps risk the chance of falling behind. By embracing intelligent apps and leveraging the capabilities of Azure, you can harness the transformative potential of AI to remain on the cutting edge of technology.

Exercise

+ + \ No newline at end of file diff --git a/30daysofIA/tags/ask-the-expert/page/8/index.html b/30daysofIA/tags/ask-the-expert/page/8/index.html index 21d2568112..ce7f9deaf8 100644 --- a/30daysofIA/tags/ask-the-expert/page/8/index.html +++ b/30daysofIA/tags/ask-the-expert/page/8/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "ask-the-expert" | Build Intelligent Apps On Azure +11 posts tagged with "ask-the-expert" | Build Intelligent Apps On Azure @@ -14,14 +14,13 @@ - - + +
-

9 posts tagged with "ask-the-expert"

View All Tags

· 4 min read
It's 30DaysOfIA

Continue The Learning Journey through Fall For Intelligent Apps! 🍂

What We'll Cover

Thank you! ♥️

image

It's hard to believe that JavaScript on Azure hack-together is ending! It seems like just yesterday that we launched this initiative, and yet here we are, 15 days later, with an incredible amount of learning and growth behind us. So... it's time for a wrap!

From the bottom of our hearts, we want to thank each and every one of you for your participation, engagement, and enthusiasm. It's been truly inspiring to see the passion and dedication from this strong community, and we're honored to be a part of it. ✨

Recap of The JavaScript on Azure Global Hack-Together

As we wrap up this exciting event, we wanted to take a moment to reflect on all that we've accomplished together. Over the last 15 days, we've covered a lot of ground, from the basics of contributing to Open source to the exploration of the Contoso Real Estate project from its Frontend to its Backend and future AI implementation.

Now that the hack-together is ending, we want to make sure that you have all the resources you need to continue honing your skills in the future. Whether you're looking to make your fist contribution to open source, become an open source maintainers, collaborate with others, or simply keep learning, there are plenty of resources out there to help you achieve your goals. So, let's dive in and explore all the ways you can continue to grow your JavaScript skills on Azure!

JSonAzure Hack-together Roadmap 📍:

hack-together-roadmap (2)

Recap on past Livestreams🌟:

Day 1️⃣: Opening Keynote (Hack-together Launch): Introduction to the Contoso Real Estate Open-source project!, managing complex and complex enterprise architecture, new announcements for JavaScript developers on Azure

Day 2️⃣: GitHub Copilot & Codespaces: Introduction to your AI pair programmer (GitHub Copilot) and your virtual developer environment on the cloud (GitHub Codespaces)

Day 6️⃣: Build your Frontend using Static Web Apps as part of a complex, modern composable frontends (or micro-frontends) and cloud-native applications.

Day 9️⃣: Build a Serverless Backend using Azure Functions

Day 1️⃣3️⃣: Easily connect to an Azure Cosmos DB, exploring its benefits and how to get started

Day 1️⃣5️⃣: Being in the AI Era, we dive into the Azure OpenAI Service and how you can start to build intelligent JavaScript applications

📖 Self-Learning Resources

  1. JavaScript on Azure Global Hack Together Module collection
  2. Lets #HackTogether: Javascript On Azure Keynote
  3. Step by Step Guide: Migrating v3 to v4 programming model for Azure Functions for Node.Js Application

Continue your journey with #FallForIntelligentApps

Join us this Fall on a learning journey to explore building intelligent apps. Combine the power of AI, cloud-native app development, and cloud-scale data to build highly differentiated digital experiences. Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure. Engage in a self-paced learning adventure by following along the #30Days of Intelligent Apps series, completing the Intelligent Apps Skills Challenge, joining the Product Group for a live Ask The Expert series or building an end to end solution architecture with a live guided experience through the Learn Live series. Discover more here.

Hands-on practice: Make your first contribution to open source!

Join our GitHUb Discussion Forum to connect with developers from every part of world, see contributions from other, find collaborators and make your first contribution to a real-world project! -Don't forget to give the repo a star ⭐

Resources

All resources are accessible on our landing page

- - +
Skip to main content

11 posts tagged with "ask-the-expert"

View All Tags

· 8 min read
It's 30DaysOfIA

Let’s start with understanding the power of intelligent applications.

What We'll Cover:

  • What are intelligent applications?
  • Categories of Intelligent Apps
  • Breaking down the role of AI in intelligent apps

A decorative header image for the blog

Demystifying Intelligent Applications: Leveraging AI in App Development

Until recent years, when you heard the term “artificial intelligence” (AI), you might have pictured autonomous robots from sci-fi films. Now, perhaps your mind jumps to big data and machine learning (ML). Maybe you’re even thinking about the latest ChatGPT plugins and transformer architectures. Today, AI is more than these imaginations: it’s a reality increasingly woven into our day-to-day routines through intelligent applications.

Intelligent apps combine cloud-scale compute, data, and predictive or generative AI. These applications help you automate tasks and make data-driven decisions, aiding in leveraging business intelligence to gain meaningful, practical outcomes. 

For developers, the AI landscape isn’t an abstract complexity. It’s a real, tangible opportunity for evolving traditional applications, making them “intelligent,” and delivering more value to users. And with tools like Azure Kubernetes Service, Azure Cosmos DB, and Azure AI Services, stepping into the world of intelligent apps isn’t a steep climb but a steady trek across a manageable learning curve.

The Three Categories of Intelligent Applications

Before we start building, it’s critical to understand what makes an app “intelligent.” It’s not a universal definition so much as an identifier for apps that fall into one or more of the three primary categories: 

  • Outcome-based — This designation represents the broadest category of intelligent apps, whose principal value for users is the intelligent outcomes they provide. These apps process data to provide insightful results that enhance decision-making or help with tasks like trend prediction, resource management, and customer relationship management.

  • Functionality-based — This type of intelligent app takes AI integration a degree further, integrating AI as a function of the app to create or process its outputs, enhancing the app’s overall interactivity and usefulness.

  • Feature-based — This is the narrowest category, in which the app’s AI/ML component is one of its core features and primary selling points

Let’s dive more deeply into each category.

Outcome-Based Apps: Intelligent Outcomes Drive Success

Outcome-based applications are all about the results provided to the user. They function beyond merely processing data to offer insightful outcomes that drive success.

Think of a personal fitness tracker app that uses AI to analyze the user’s activity and provides actionable recommendations for achieving a goal. This type of app not only tracks your steps, sleep patterns, and heart rate but uses that data to provide you with personalized health advice. It helps you make informed lifestyle choices — the “intelligent” outcome.

Another example is a social media marketing app that uses AI for time-saving data analysis, task automation, and trend prediction. AI-based sentiment analysis can gauge customer perception while natural language processing (NLP) accurately interprets customer inquiries. Task automation can schedule posts and automate responses to common customer queries, and trend forecasting helps model and predict customer behavior to further personalize targeted promotional efforts. Building this type of intelligence into your apps provides cost-effective, actionable insights that guide sales and marketing strategies.

Functionality-Based Apps: AI-Driven Features Enhance User Experiences

As the name suggests, functionality-based applications integrate AI into how the app functions, but the end user drives the action and outcome. In this category of apps, AI takes a more active role in the user’s interaction by allowing them to use AI-created or processed output. These apps leverage AI technologies like Azure AI Services for NLP, image recognition, and pattern recognition to improve user experience.

Consider a music app that generates personalized playlists based on your listening habits or your smartphone’s camera app that uses AI to recognize faces and optimize photo settings. Even more advanced are the language translation apps that translate text from one language to another and can even understand and maintain the context and subtleties of the conversation. They use NLP to accomplish these feats.

Similarly, consider the latest e-commerce apps, whose AI-driven functionalities enable customers to try on clothes virtually, significantly enhancing the online shopping experience.

Feature-Based Apps: Integrating Advanced AI/ML Components

Finally, there are feature-based intelligent apps. These apps incorporate advanced AI/ML components like neural networks, internal large language models (LLMs), and sophisticated algorithms as core features for the user. In this type of application, AI is the primary feature for users to engage with and the primary selling point for the app.

Some examples of feature-based applications are chatbots and virtual agents built using Azure Bot Service. These robo-representatives use AI to provide more natural and human-like conversation, balancing superior efficiency with a friendlier, more personalized user experience. The new Bing is a prime example of this implementation, integrating AI to give users a knowledgeable copilot for web searches.

Similarly, OpenAI’s ChatGPT allows users access to its state-of-the-art language models available as Microsoft’s Azure OpenAI Service. It leverages the power of the highly sophisticated GPT-3 LLM to perform tasks like drafting emails, writing code, answering general knowledge questions, translating between languages, and tutoring in numerous subjects.

These apps don’t just use AI to provide outputs or improve user experiences: They push the boundaries of what’s possible in app development by integrating AI/ML components.

info

Register for the live Q&A Ask The Expert session on September 26 with the Azure Functions product engineering team.

Breaking Down the Role of AI in Intelligent Applications

Intelligent apps reflect the merging of traditional software design with the dynamic capabilities of AI. Let’s explore how AI integrates and amplifies these applications.

At its core, AI augments applications with the capacity to self-learn and self-improve. It allows you to create applications that predict, react, and adapt to a constantly changing environment. In the power grid management system for a major city, an AI-infused application can forecast power usage trends, react to anomalies, and adapt to sudden demand surges, ensuring optimal efficiency and preventing blackouts.

But AI’s role doesn’t end at prediction or decision-making: It also automates tasks. In enterprise solutions, it has moved beyond simple automation to encompass intelligent process automation (IPA). In this arena, AI algorithms understand, learn, and then automate entire digital workflows. For developers, this is a fertile ground for creating applications that can dramatically improve the operational efficiency of businesses.

Even more, AI substantially aids in development tasks. For example, there’s GitHub Copilot, an AI-powered code assistant that offers suggestions based not only on syntax but on development best practices and the intent of the code. AI has evolved from a tool you use to build apps to a partner that helps in creating them. 

Using AI to build intelligent apps isn’t about throwing out what’s worked before. It’s about enhancing the components that have the most impact and achieve the best results. Incorporating AI in apps elevates their predictive abilities, streamlines complex tasks, and even aids in the development and creation process. When you build intelligent apps, you don’t just address current problems. You anticipate future challenges and craft solutions that adapt over time.

Summary

As developers the power of AI is in our hands, and it’s our responsibility to harness its full potential. Each category tells the story of AI's transformative potential, from applications that deliver intelligent outcomes to those with AI capabilities to enrich the user experience and those pushing boundaries with advanced AI/ML technology.

Accessible and robust platforms like Azure AI can simplify the process of developing Intelligent Applications. Whether you’re refining an existing app or building a new one, just entering the world of AI, or already familiar with the landscape, take a moment to appreciate the potential that AI integration offers. You’re embracing this exciting frontier and shaping the future of app development. 

Exercise

  • Complete the Cloud Skills Challenge to build on your apps and AI skills.
  • Watch Episode 01 of the Serverless Edition Learn Live session to learn how to build an end-to-end intelligent app solution.
+ + \ No newline at end of file diff --git a/30daysofIA/tags/ask-the-expert/page/9/index.html b/30daysofIA/tags/ask-the-expert/page/9/index.html index e409817220..1f499eadac 100644 --- a/30daysofIA/tags/ask-the-expert/page/9/index.html +++ b/30daysofIA/tags/ask-the-expert/page/9/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "ask-the-expert" | Build Intelligent Apps On Azure +11 posts tagged with "ask-the-expert" | Build Intelligent Apps On Azure @@ -14,13 +14,14 @@ - - + +
-

9 posts tagged with "ask-the-expert"

View All Tags

· 2 min read
It's 30DaysOfIA

September is almost here - and that can only mean one thing!! It's time to 🍂 Fall for something new and exciting and spend a few weeks skilling up on relevant tools, techologies and solutions!!

Last year, we focused on #ServerlessSeptember. This year, we're building on that theme with the addition of cloud-scale Data, cloud-native Technologies and cloud-based AI integrations to help you modernize and build intelligent apps for the enterprise!

Watch this space - and join us in September to learn more!

- - +
Skip to main content

11 posts tagged with "ask-the-expert"

View All Tags

· 4 min read
It's 30DaysOfIA

This Fall focus on building intelligent apps using AI and cloud-native technologies. #FallForIntelligentApps brings to you a learning journey to build your skills on creating differentiated experiences while modernizing your applications. It’s time to learn it all.

What We'll Cover

image

Get Ready To #FallForIntelligentApps starting September 18!

Today, we kick off the Fall season with content and activities to skill you up on all things Intelligent Apps or AI Apps on Azure with content, events, and community interactions! Read on to learn about what is coming!

Explore Our Initiatives

We have a number of initiatives planned for the month to help you learn and skill up on relevant technologies. Click on the links to visit the relevant pages for each

image

Register for the events!

What are 4 things you can do today, to jumpstart your learning journey?

#30Days Of Intelligent Apps

#30DaysOfIA is a series of daily blog posts grouped into themed weeks - taking you from core concepts to end-to-end solution examples in 30 days. Each article will provide conceptual lessons paired with exercises and resources to help you reinforce learnings and take next steps.

This series takes you through learning journey in six stages, each building on the previous week to help you skill up in a beginner-friendly way:

We will start with defining intelligent apps and then expand on how to build with cloud-native technologies like Azure Kubernetes Service, Azure Container Apps and Azure Functions, and take you through end-to-end scenarios for real world application development. Before we dive deep on intelligent apps, here is a high-level overview of the Intelligent Apps landscape on Azure:

image

Containers on Azure services offer you a wide range of capabilities, from simplicity to control to suit your different needs.

image +image

To start with the basics for developing Kubernetes applications, explore #30DaysOfCloudNative.

Cloud-native development when paired with serverless computing enhances your solution architecture for building cost optimized, resilient applications.

image

To start with the basics for serverless computing, explore #30DaysOfServerless.

Let’s Get Started

Now you know everything! We hope you are as excited as we are to dive into a full month of active learning and doing! Don't forget to subscribe for updates in your favorite feed reader! And look out for our first Intelligent Apps post Monday!

+ + \ No newline at end of file diff --git a/30daysofIA/tags/azure-container-apps/index.html b/30daysofIA/tags/azure-container-apps/index.html index 131c7bb3f7..ceeabc8dba 100644 --- a/30daysofIA/tags/azure-container-apps/index.html +++ b/30daysofIA/tags/azure-container-apps/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "azure-container-apps" | Build Intelligent Apps On Azure +11 posts tagged with "azure-container-apps" | Build Intelligent Apps On Azure @@ -14,13 +14,13 @@ - - + +
-

9 posts tagged with "azure-container-apps"

View All Tags

· 10 min read
It's 30DaysOfIA

Prepare your organization to adopt intelligent apps by taking a change management approach. Assess your readiness, upskill teams, create a culture of change and overcome resistance, and prepare for future evolution to pave the way for a smooth transition into an AI-centric company.

What We'll Cover:

  • Role of change management in modernizing to intelligent apps
  • Skills needed for building intelligent apps in your organization
  • How to build a culture of change to embrace AI
  • How to prepare for the future of intelligent apps and continuous evolution

image of modernizing AI solutions for intelligent apps

Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management

As we’ve discussed throughout this series, intelligent apps and generative AI are rewriting the rules of business and application development. The first article of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” explored how intelligent apps can enhance user experiences and drive businesses growth. These advancements are opening new opportunities and markets for companies ready to embrace them.

However, embracing this digital transformation is more complex than flipping a switch. It requires adjustments to the technical side, which we explored in the series’ fifth article, “Preparing the Path for Intelligent Apps: Transitioning from On-Premises/IaaS to Cloud Native Applications.” It also demands a shift in how organizations adapt to change and approach app design and development. Let’s explore that mindset transition with tips to help you make it happen.

The Role of Change Management in Transitioning to Intelligent Apps

Transitioning to intelligent apps isn’t merely a technological change. It’s a profound shift in how businesses perceive and operate. This move toward artificial intelligence (AI)-centric approach isn’t meant to replace humans with machines or radically overhaul all business operations.

Instead, intelligent apps should augment human capabilities, streamline processes, and foster an environment where innovation flourishes. They expand toolkits and techniques to include AI-assisted development tools like GitHub Copilot and AI-enabled workflows, helping us design and deliver AI-enhanced experiences.

Effective change management strategies are crucial in helping employees adapt to these new ways of working with intelligent apps, ensuring that the transition doesn’t disrupt productivity or morale.

Team and organization strategies should: 

  • Assess the impact of any proposed changes.  
  • Communicate the benefits and reasons behind the change to all affected team members.  
  • Provide the necessary training and support.  
  • Integrate the changes into the culture and daily operations. 

By incorporating this robust change management approach, you can identify and mitigate potential transition risks early. Moreover, this strategy ensures that employees are engaged throughout the process, fostering a sense of involvement and ownership. This, in turn, cultivates a culture suited for innovation through intelligent apps. 

These organizational changes focus on the following aspects: organizational readiness, skillset, culture of change and overcoming resistance, and adaptability. Let’s explore them below.

info

Register for the intelligent apps webinar on Driving Business Value by Modernizing with Cloud-Native & AI with Microsoft and Forrester on September 26. 

Explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries. 

Identifying Organizational Readiness for Intelligent Apps

The first step in preparing your organization for intelligent apps is to assess your current readiness for change. Analyze various elements of your organization, from infrastructure and workforce skills to strategic vision and overall culture.

Can your apps and systems integrate with AI platforms like Microsoft Azure AI? Is your workforce comfortable working alongside AI, or are they fearful and resistant? Does your strategic vision incorporate AI’s transformative potential, or does it fail to acknowledge AI as a critical driver of future growth?

These questions identify the challenges of integrating AI into an organization and determine the company’s readiness for intelligent apps.

Embracing intelligent apps involves more than being ready with your technological infrastructure or having the right technology or tools. You need flexible organizational structures and processes driving your business to accommodate new ideas and capabilities. Your teams must feel empowered to own and drive them. Much like Microsoft’s shift to AI-powered services—like AI-powered Bing and Edge and Microsoft 365 Copilot—embracing AI organization-wide requires a holistic vision and willingness to innovate, with teams capable of delivering on the plan.

After accessing these critical areas for organizational readiness, you can start planning a strategy for this transition to an AI-ready organization. Identify skill gaps and work to bridge them while nurturing an environment that appreciates and harnesses the potential of AI and intelligent apps.

Nurturing the Indispensable Skills for Intelligent Apps

Tasking your AI specialists with preparing your organization to use and build intelligent apps isn’t enough. Accomplishing this goal requires a cross-functional effort and a diverse skill set.

Developing intelligent apps involves data scientists managing and interpreting vast amounts of data, machine learning engineers building and training the models, and traditional developers integrating these models into usable applications. However, the impact of generative AI spans beyond creating apps. AI fluency across the entire organization—from sales and marketing to accounting and legal departments—offers substantial benefits, sparking new ideas and solutions from sometimes unexpected sources.

Teams' AI skills might roughly fall into three levels: 

  • Fundamental skills — Being familiar and efficient with using AI tools, for example, knowing how to write effective prompts for large language models (LLMs).
  • Visionary skills — Recognizing AI’s strengths and weaknesses, for example, its creativity and hallucinations, that is, unexpected output.
  • Expert skills — Managing data and training models and integrating AI technology into apps, such as incorporating the OpenAI application programming interface (API) into an Intelligent App.

Based on these levels, you can gauge your team's skill readiness to pinpoint the most significant gaps, whether in general AI knowledge across your organization or within technical development teams.

As intelligent apps become increasingly prevalent, upskilling your workforce is crucial. This transition opens a new realm of skills and capabilities for your team to master. You can offer machine learning, data analysis, and cloud computing training programs—such as Microsoft’s Azure AI Fundamentals certification—to empower your team to confidently navigate the intelligent apps landscape.

Nurturing these skills within your organization may require hiring new talent, reskilling current employees, and creating an environment that promotes learning and experimentation. Specifically, if your team understands tools like the Microsoft Azure AI platform, they could contribute significantly to developing and maintaining intelligent apps.

An upskilled team is more than a functional unit. It’s a think tank to drive innovation and improve your organization’s competitive standing in the digital marketplace.

Building a Culture of Change to Embrace AI

Even with the right people and skills in place, change remains an uphill battle for organizations committed to traditional processes. 

Transitioning to intelligent apps is as much about mindset as technology and skills. It requires fostering a culture open to change and innovation that understands AI’s transformative potential and embraces it to enhance rather than replace human capabilities. 

Leaders play a pivotal role in cultivating this mindset. As they model a positive attitude toward AI, emphasize its benefits, and address any concerns transparently, leaders significantly contribute to the organization’s readiness to adopt intelligent apps. 

The most critical factor in this transition is enabling an AI-friendly culture. It’s about encouraging curiosity, rewarding innovative thinking, and creating an environment where people thrive on learning and experimentation. 

Empowering teams to leverage practical AI tools and AI-driven data insights to aid decision-making helps motivate employees to use and explore AI’s potential, improve their skills, and contribute to the successful integration of intelligent apps. Such an environment is fertile ground to grow breakthrough ideas and innovative solutions. 

Overcoming Resistance to Change and Adopting Intelligent Apps

Despite AI’s potential, change isn’t easy. It takes effort and is especially challenging for organizations used to existing processes. This resistance to change is a significant roadblock when transitioning to intelligent apps. 

Overcoming this resistance involves:  

  • Understanding employees’ concerns. 
  • Challenging underlying assumptions about traditional systems and outdated processes that hinder change. 
  • Providing comprehensive training for workers to use AI effectively.
  • Demonstrating the benefits and opportunities of intelligent apps. 
  • Clear and continuous communication to dispel fears and misconceptions about AI.

These steps will foster greater acceptance and enthusiasm for intelligent apps in your teams. 

Preparing for the Future of Intelligent Apps and Continuous Evolution

Finally, it’s essential to understand that generative AI is just beginning to gain momentum. Adjusting to a world of intelligent apps isn’t a one-time change—it’s a continuous evolution. 

As AI technologies mature and evolve and more businesses realize their benefits, we can expect the landscape of intelligent apps to expand further. Organizations can cultivate a mindset of continuous learning, flexibility, and adaptation to prepare for this future. Staying ahead in this dynamic field requires organizations to keep up with the latest AI advancements, continually reassess their strategies, and pivot when necessary. 

The domain of intelligent apps is rapidly advancing. Organizations must prepare to navigate this dynamic landscape to stay ahead of the curve.

Summary

Preparing for AI and intelligent apps is a strategic decision. It requires thoughtful preparation and a culture that embraces change.

With the proper preparation and mindset, adopting AI can become a smoother, collaborative path to innovation for the whole organization. Integrating AI into your organization does not discard what has worked in the past. It simply enhances and augments those processes for a better future.

Consider the points above to assess your organization’s readiness and prepare to welcome intelligent apps.

In the first week of this series, you learned about intelligent apps, how organizations today find success through intelligent apps, what AI-assisted application development looks like, ways to leverage the power of generative AI, the technical changes needed to prepare for intelligent apps, and, in this article, the organizational changes required to harness the power of AI.

The future is intelligent. Use this series as a guideline to navigate challenges while preparing for and embracing AI's opportunities for an intelligent tomorrow.

Exercise

- - +
Skip to main content

11 posts tagged with "azure-container-apps"

View All Tags

· 10 min read
It's 30DaysOfIA

Prepare your organization to adopt intelligent apps by taking a change management approach. Assess your readiness, upskill teams, create a culture of change and overcome resistance, and prepare for future evolution to pave the way for a smooth transition into an AI-centric company.

What We'll Cover:

  • Role of change management in modernizing to intelligent apps
  • Skills needed for building intelligent apps in your organization
  • How to build a culture of change to embrace AI
  • How to prepare for the future of intelligent apps and continuous evolution

image of modernizing AI solutions for intelligent apps

Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management

As we’ve discussed throughout this series, intelligent apps and generative AI are rewriting the rules of business and application development. The first article of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” explored how intelligent apps can enhance user experiences and drive businesses growth. These advancements are opening new opportunities and markets for companies ready to embrace them.

However, embracing this digital transformation is more complex than flipping a switch. It requires adjustments to the technical side, which we explored in the series’ fifth article, “Preparing the Path for Intelligent Apps: Transitioning from On-Premises/IaaS to Cloud Native Applications.” It also demands a shift in how organizations adapt to change and approach app design and development. Let’s explore that mindset transition with tips to help you make it happen.

The Role of Change Management in Transitioning to Intelligent Apps

Transitioning to intelligent apps isn’t merely a technological change. It’s a profound shift in how businesses perceive and operate. This move toward artificial intelligence (AI)-centric approach isn’t meant to replace humans with machines or radically overhaul all business operations.

Instead, intelligent apps should augment human capabilities, streamline processes, and foster an environment where innovation flourishes. They expand toolkits and techniques to include AI-assisted development tools like GitHub Copilot and AI-enabled workflows, helping us design and deliver AI-enhanced experiences.

Effective change management strategies are crucial in helping employees adapt to these new ways of working with intelligent apps, ensuring that the transition doesn’t disrupt productivity or morale.

Team and organization strategies should: 

  • Assess the impact of any proposed changes.  
  • Communicate the benefits and reasons behind the change to all affected team members.  
  • Provide the necessary training and support.  
  • Integrate the changes into the culture and daily operations. 

By incorporating this robust change management approach, you can identify and mitigate potential transition risks early. Moreover, this strategy ensures that employees are engaged throughout the process, fostering a sense of involvement and ownership. This, in turn, cultivates a culture suited for innovation through intelligent apps. 

These organizational changes focus on the following aspects: organizational readiness, skillset, culture of change and overcoming resistance, and adaptability. Let’s explore them below.

info

Register for the intelligent apps webinar on Driving Business Value by Modernizing with Cloud-Native & AI with Microsoft and Forrester on September 26. 

Explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries. 

Identifying Organizational Readiness for Intelligent Apps

The first step in preparing your organization for intelligent apps is to assess your current readiness for change. Analyze various elements of your organization, from infrastructure and workforce skills to strategic vision and overall culture.

Can your apps and systems integrate with AI platforms like Microsoft Azure AI? Is your workforce comfortable working alongside AI, or are they fearful and resistant? Does your strategic vision incorporate AI’s transformative potential, or does it fail to acknowledge AI as a critical driver of future growth?

These questions identify the challenges of integrating AI into an organization and determine the company’s readiness for intelligent apps.

Embracing intelligent apps involves more than being ready with your technological infrastructure or having the right technology or tools. You need flexible organizational structures and processes driving your business to accommodate new ideas and capabilities. Your teams must feel empowered to own and drive them. Much like Microsoft’s shift to AI-powered services—like AI-powered Bing and Edge and Microsoft 365 Copilot—embracing AI organization-wide requires a holistic vision and willingness to innovate, with teams capable of delivering on the plan.

After accessing these critical areas for organizational readiness, you can start planning a strategy for this transition to an AI-ready organization. Identify skill gaps and work to bridge them while nurturing an environment that appreciates and harnesses the potential of AI and intelligent apps.

Nurturing the Indispensable Skills for Intelligent Apps

Tasking your AI specialists with preparing your organization to use and build intelligent apps isn’t enough. Accomplishing this goal requires a cross-functional effort and a diverse skill set.

Developing intelligent apps involves data scientists managing and interpreting vast amounts of data, machine learning engineers building and training the models, and traditional developers integrating these models into usable applications. However, the impact of generative AI spans beyond creating apps. AI fluency across the entire organization—from sales and marketing to accounting and legal departments—offers substantial benefits, sparking new ideas and solutions from sometimes unexpected sources.

Teams' AI skills might roughly fall into three levels: 

  • Fundamental skills — Being familiar and efficient with using AI tools, for example, knowing how to write effective prompts for large language models (LLMs).
  • Visionary skills — Recognizing AI’s strengths and weaknesses, for example, its creativity and hallucinations, that is, unexpected output.
  • Expert skills — Managing data and training models and integrating AI technology into apps, such as incorporating the OpenAI application programming interface (API) into an Intelligent App.

Based on these levels, you can gauge your team's skill readiness to pinpoint the most significant gaps, whether in general AI knowledge across your organization or within technical development teams.

As intelligent apps become increasingly prevalent, upskilling your workforce is crucial. This transition opens a new realm of skills and capabilities for your team to master. You can offer machine learning, data analysis, and cloud computing training programs—such as Microsoft’s Azure AI Fundamentals certification—to empower your team to confidently navigate the intelligent apps landscape.

Nurturing these skills within your organization may require hiring new talent, reskilling current employees, and creating an environment that promotes learning and experimentation. Specifically, if your team understands tools like the Microsoft Azure AI platform, they could contribute significantly to developing and maintaining intelligent apps.

An upskilled team is more than a functional unit. It’s a think tank to drive innovation and improve your organization’s competitive standing in the digital marketplace.

Building a Culture of Change to Embrace AI

Even with the right people and skills in place, change remains an uphill battle for organizations committed to traditional processes. 

Transitioning to intelligent apps is as much about mindset as technology and skills. It requires fostering a culture open to change and innovation that understands AI’s transformative potential and embraces it to enhance rather than replace human capabilities. 

Leaders play a pivotal role in cultivating this mindset. As they model a positive attitude toward AI, emphasize its benefits, and address any concerns transparently, leaders significantly contribute to the organization’s readiness to adopt intelligent apps. 

The most critical factor in this transition is enabling an AI-friendly culture. It’s about encouraging curiosity, rewarding innovative thinking, and creating an environment where people thrive on learning and experimentation. 

Empowering teams to leverage practical AI tools and AI-driven data insights to aid decision-making helps motivate employees to use and explore AI’s potential, improve their skills, and contribute to the successful integration of intelligent apps. Such an environment is fertile ground to grow breakthrough ideas and innovative solutions. 

Overcoming Resistance to Change and Adopting Intelligent Apps

Despite AI’s potential, change isn’t easy. It takes effort and is especially challenging for organizations used to existing processes. This resistance to change is a significant roadblock when transitioning to intelligent apps. 

Overcoming this resistance involves:  

  • Understanding employees’ concerns. 
  • Challenging underlying assumptions about traditional systems and outdated processes that hinder change. 
  • Providing comprehensive training for workers to use AI effectively.
  • Demonstrating the benefits and opportunities of intelligent apps. 
  • Clear and continuous communication to dispel fears and misconceptions about AI.

These steps will foster greater acceptance and enthusiasm for intelligent apps in your teams. 

Preparing for the Future of Intelligent Apps and Continuous Evolution

Finally, it’s essential to understand that generative AI is just beginning to gain momentum. Adjusting to a world of intelligent apps isn’t a one-time change—it’s a continuous evolution. 

As AI technologies mature and evolve and more businesses realize their benefits, we can expect the landscape of intelligent apps to expand further. Organizations can cultivate a mindset of continuous learning, flexibility, and adaptation to prepare for this future. Staying ahead in this dynamic field requires organizations to keep up with the latest AI advancements, continually reassess their strategies, and pivot when necessary. 

The domain of intelligent apps is rapidly advancing. Organizations must prepare to navigate this dynamic landscape to stay ahead of the curve.

Summary

Preparing for AI and intelligent apps is a strategic decision. It requires thoughtful preparation and a culture that embraces change.

With the proper preparation and mindset, adopting AI can become a smoother, collaborative path to innovation for the whole organization. Integrating AI into your organization does not discard what has worked in the past. It simply enhances and augments those processes for a better future.

Consider the points above to assess your organization’s readiness and prepare to welcome intelligent apps.

In the first week of this series, you learned about intelligent apps, how organizations today find success through intelligent apps, what AI-assisted application development looks like, ways to leverage the power of generative AI, the technical changes needed to prepare for intelligent apps, and, in this article, the organizational changes required to harness the power of AI.

The future is intelligent. Use this series as a guideline to navigate challenges while preparing for and embracing AI's opportunities for an intelligent tomorrow.

Exercise

+ + \ No newline at end of file diff --git a/30daysofIA/tags/azure-container-apps/page/10/index.html b/30daysofIA/tags/azure-container-apps/page/10/index.html new file mode 100644 index 0000000000..5485e0f2c8 --- /dev/null +++ b/30daysofIA/tags/azure-container-apps/page/10/index.html @@ -0,0 +1,27 @@ + + + + + +11 posts tagged with "azure-container-apps" | Build Intelligent Apps On Azure + + + + + + + + + + + + + + +
+

11 posts tagged with "azure-container-apps"

View All Tags

· 4 min read
It's 30DaysOfIA

Continue The Learning Journey through Fall For Intelligent Apps! 🍂

What We'll Cover

Thank you! ♥️

image

It's hard to believe that JavaScript on Azure hack-together is ending! It seems like just yesterday that we launched this initiative, and yet here we are, 15 days later, with an incredible amount of learning and growth behind us. So... it's time for a wrap!

From the bottom of our hearts, we want to thank each and every one of you for your participation, engagement, and enthusiasm. It's been truly inspiring to see the passion and dedication from this strong community, and we're honored to be a part of it. ✨

Recap of The JavaScript on Azure Global Hack-Together

As we wrap up this exciting event, we wanted to take a moment to reflect on all that we've accomplished together. Over the last 15 days, we've covered a lot of ground, from the basics of contributing to Open source to the exploration of the Contoso Real Estate project from its Frontend to its Backend and future AI implementation.

Now that the hack-together is ending, we want to make sure that you have all the resources you need to continue honing your skills in the future. Whether you're looking to make your fist contribution to open source, become an open source maintainers, collaborate with others, or simply keep learning, there are plenty of resources out there to help you achieve your goals. So, let's dive in and explore all the ways you can continue to grow your JavaScript skills on Azure!

JSonAzure Hack-together Roadmap 📍:

hack-together-roadmap (2)

Recap on past Livestreams🌟:

Day 1️⃣: Opening Keynote (Hack-together Launch): Introduction to the Contoso Real Estate Open-source project!, managing complex and complex enterprise architecture, new announcements for JavaScript developers on Azure

Day 2️⃣: GitHub Copilot & Codespaces: Introduction to your AI pair programmer (GitHub Copilot) and your virtual developer environment on the cloud (GitHub Codespaces)

Day 6️⃣: Build your Frontend using Static Web Apps as part of a complex, modern composable frontends (or micro-frontends) and cloud-native applications.

Day 9️⃣: Build a Serverless Backend using Azure Functions

Day 1️⃣3️⃣: Easily connect to an Azure Cosmos DB, exploring its benefits and how to get started

Day 1️⃣5️⃣: Being in the AI Era, we dive into the Azure OpenAI Service and how you can start to build intelligent JavaScript applications

📖 Self-Learning Resources

  1. JavaScript on Azure Global Hack Together Module collection
  2. Lets #HackTogether: Javascript On Azure Keynote
  3. Step by Step Guide: Migrating v3 to v4 programming model for Azure Functions for Node.Js Application

Continue your journey with #FallForIntelligentApps

Join us this Fall on a learning journey to explore building intelligent apps. Combine the power of AI, cloud-native app development, and cloud-scale data to build highly differentiated digital experiences. Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure. Engage in a self-paced learning adventure by following along the #30Days of Intelligent Apps series, completing the Intelligent Apps Skills Challenge, joining the Product Group for a live Ask The Expert series or building an end to end solution architecture with a live guided experience through the Learn Live series. Discover more here.

Hands-on practice: Make your first contribution to open source!

Join our GitHUb Discussion Forum to connect with developers from every part of world, see contributions from other, find collaborators and make your first contribution to a real-world project! +Don't forget to give the repo a star ⭐

Resources

All resources are accessible on our landing page

+ + + + \ No newline at end of file diff --git a/30daysofIA/tags/azure-container-apps/page/11/index.html b/30daysofIA/tags/azure-container-apps/page/11/index.html new file mode 100644 index 0000000000..676f525a1b --- /dev/null +++ b/30daysofIA/tags/azure-container-apps/page/11/index.html @@ -0,0 +1,26 @@ + + + + + +11 posts tagged with "azure-container-apps" | Build Intelligent Apps On Azure + + + + + + + + + + + + + + +
+

11 posts tagged with "azure-container-apps"

View All Tags

· 2 min read
It's 30DaysOfIA

September is almost here - and that can only mean one thing!! It's time to 🍂 Fall for something new and exciting and spend a few weeks skilling up on relevant tools, techologies and solutions!!

Last year, we focused on #ServerlessSeptember. This year, we're building on that theme with the addition of cloud-scale Data, cloud-native Technologies and cloud-based AI integrations to help you modernize and build intelligent apps for the enterprise!

Watch this space - and join us in September to learn more!

+ + + + \ No newline at end of file diff --git a/30daysofIA/tags/azure-container-apps/page/2/index.html b/30daysofIA/tags/azure-container-apps/page/2/index.html index 41f8b0c55f..700d3d64c2 100644 --- a/30daysofIA/tags/azure-container-apps/page/2/index.html +++ b/30daysofIA/tags/azure-container-apps/page/2/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "azure-container-apps" | Build Intelligent Apps On Azure +11 posts tagged with "azure-container-apps" | Build Intelligent Apps On Azure @@ -14,13 +14,13 @@ - - + +
-

9 posts tagged with "azure-container-apps"

View All Tags

· 9 min read
It's 30DaysOfIA

Learn best practices for modernizing your on-premises or IaaS solutions to intelligent apps in the cloud.

What We'll Cover:

  • Traditional vs Intelligent Apps
  • On-Premises/IaaS vs. Cloud-Native Platforms
  • Modernization strategy for building Intelligent Apps

image of modernizing AI solutions for intelligent apps

Preparing the Path for Intelligent Apps: Transitioning from On-Premises/IaaS to Cloud-Native Applications

The proliferation of generative AI is paving the way for intelligent applications, software that leverages AI capabilities to deliver unparalleled functionality and user experience. As we covered in a previous article, “Demystifying Intelligent Applications: Leveraging AI in App Development,” intelligent Apps aren’t just products of advanced coding. They interact, learn, and evolve thanks to modern AI and machine learning (ML) breakthroughs. 

The paradigm shift from traditional to intelligent apps demands that we change our approach to the technical challenges of software and architecture design. It also necessitates adopting new ways of thinking and operating within an organization, which we explore in greater depth in “Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management.” 

This article explores the technical infrastructure powering intelligent apps, providing a roadmap to transition traditional on-premises or Infrastructure as a Service (IaaS) solutions to intelligent apps deployed to cloud native platforms and services. We’ll discover the differences between conventional and intelligent apps, options for re-architecting existing applications, and critical strategic considerations integral to modernizing the application-building approach. 

Understanding the Shift: Traditional vs. Intelligent Applications

Intelligent apps are more than just an incremental evolution from traditional apps. They involve a fundamental shift in software solution design, development, and deployment. A clear understanding of how each paradigm differs is crucial for comprehending the impact of this transition. 

Functionality and User Experience

Traditional applications, often rule-based and rigid, rely on preprogrammed operations. A traditional weather app, for example, typically presents structured, location-based forecasts using a predetermined data source. Meanwhile, an intelligent app leverages AI for data-driven decision-making and personalization. Rather than merely displaying the forecast, it integrates ML with additional data sources—like a user’s calendar and fitness data—to determine their favorite outdoor activities, provide relevant clothing suggestions, and even automatically design ideal vacations and times based on the user’s optimal weather preferences. 

Similarly, while traditional applications tend to offer a one-size-fits-all UI, intelligent apps use AI to create tailored interactions. For instance, an intelligent banking app might use generative AI to provide a voice-enabled UI that lets customers use natural language to ask about transactions and receive personalized financial advice.

Infrastructure

Intelligent apps integrating these sophisticated features require highly scalable, flexible infrastructure optimized for AI functionality and able to manage large volumes of real-time data. Unlike traditional applications deployed on-premises or in infrastructure as a service (IaaS) environments, intelligent apps demand the flexibility and scalability of cloud native architectures.

An on-premises or IaaS-based e-commerce application may struggle with connecting its data to AI services or scaling to accommodate voluminous real-time data and requests. But an intelligent app built with a cloud native approach and scalability at its core can handle variable demand much more efficiently. It also maintains its infrastructure components on demand, whether for collecting data for AI/ML consumption or user-facing features like AI-enabled customer support and personalization.

Consider an intelligent app built on Azure that creates ChatGPT-like experiences with custom data. It might connect Azure Open AI and Azure Cognitive Search to an app running on Azure Container Apps or Azure Kubernetes Service. 

The architecture might look like the following diagram based on this demo app:

diagram of a demo app architecture

While the cloud is well-equipped to handle the challenges associated with shifting to AI-centric infrastructure—handling real-time data processing and storage, integrating and managing AI services, configuring event-driven and microservices architectures, and ensuring security across the distributed environment—re-architecting a traditional app to an intelligent app requires careful planning and execution to ensure it benefits fully from the capabilities of AI and cloud computing.

On-Premises/IaaS vs. Cloud-Native Platforms

While on-premises and IaaS solutions provide substantial control over data and processes, this fine-grained control comes with setup and maintenance costs and may lack the scalability necessary for AI-driven Intelligent Apps. On-premises hosting involves purchasing and maintaining hardware, including servers and storage systems. IaaS involves leasing virtual hardware and then hiring and training IT personnel for the infrastructure’s operation, maintenance, and security.

Conversely, infrastructure built on cloud native app services with Microsoft Azure offers virtually unlimited scalability without ongoing hardware and maintenance costs. This feature is ideal for intelligent apps that need to adapt and scale to accommodate fluctuating workloads and data streams. And because these cloud infrastructures typically operate on a pay-as-you-go model, traditionally substantial upfront costs turn into predictable operating expenses, lowering both the cost and risk of launching applications.

Moreover, cloud service providers continually update their offerings, so building on sophisticated AI tools and services through the cloud, like the Azure AI Platform and orchestration capabilities with Azure Kubernetes Service, ensures that you have access to the latest AI and most advanced AI models and technologies.

Strategic Considerations for Transitioning to Intelligent Apps

When developing a strategy for transitioning to intelligent apps, you must consider costs, personnel skill development, and data management.

Monolithic architectures are unlikely to support the scale and ease of iteration required by intelligent apps, so you’ll likely need to implement code changes to support the transition. Your chosen hosting solution and the amount of data your app will handle affect cost and performance, in addition to determining privacy and compliance requirements.

An app that manages or stores terabytes or petabytes of data will require storage-optimized virtual machines or a managed database service hosting. Meanwhile, an app that processes video content or performs resource-intensive calculations should prioritize high-performance computing. Maximizing storage and performance can become extraordinarily costly.

Finally, existing applications shifting towards intelligent apps may encounter compatibility conflicts, depending on database type or the existing system’s underlying programming language. As you might imagine, a traditional app running on-premises and using a shared local database may need significant restructuring to support a cloud native architecture. You need plans for data migration, potential downtime, and training.

Next, let’s look at this transformation process.

Re-Architecting Monolithic Apps into Microservices

Shifting from monolithic applications to microservices is pivotal for transitioning to Intelligent Apps, and you can use this shift as an opportunity to transform your legacy software into intelligent apps that leverage the flexibility and power of microservices-enabled AI solutions.

Traditionally, a monolithic application (let’s use an online marketplace as an example) might handle all activities—from serving webpages to processing payments—within an on-premises server or a cloud server. However, this approach is challenging to scale and remains vulnerable to network failures or cyberattacks.

Breaking these applications into multiple services with specific responsibilities, such as authentication or notifications, can overcome these limitations. These services are typically deployed as microservices within container environments like Azure Kubernetes Service or serverless platforms like Azure Container Apps or Azure Functions using automated processes. This approach offers scalability, robustness, and improved performance, as each microservice can run independently and use its own database.

Shifting towards smaller, independent microservices in app architectures allows us to reap the benefits of AI-powered intelligent apps, for example, by using microservices to handle AI tasks such as processing natural language input, analyzing behavioral data, or personalizing content.

info

Watch Episode 1 and Episode 2 of the Learn Live series on how to build, test and deploy an end-to-end intelligent app solution using the Contoso Real Estate Sample.

Event-Driven Architectures: Knowing When to Use Them

Event-Driven Architectures (EDAs) can significantly enhance the performance and cost-effectiveness of intelligent apps. EDAs, typically implemented with microservices, respond in real time to events or state changes ranging from user interactions to real-time analytics.

Consider an intelligent app designed for personalized customer engagement. Instead of designing the application to check continuously for changes in user data, an EDA can react to such changes immediately, triggering appropriate microservices. For example, if users modify their preferences, an event could trigger an AI-driven recommendation microservice to update its suggestions immediately. This real-time responsiveness, enabled by services like Azure Event Grid, can improve user experiences and make intelligent apps more adaptive and proactive.

However, it’s worth noting that while EDAs provide significant benefits, they can introduce unique complexities, such as maintaining event consistency and debugging issues in event chains across multiple microservices. Therefore, it’s important to assess your application’s needs so you can understand where it can benefit from an EDA.

Conclusion

Transitioning to intelligent apps represents a shift in strategy, architecture, and infrastructure. By designing and re-architecting for cloud-based infrastructure, your apps gain scalability, cost-effectiveness, robust data management, access to sophisticated AI tools, enhanced security, and reduced operational overhead.  

But this transition extends well beyond its technical components. Organizations and the people working within them need equal attention. You can explore these facets in greater depth in “Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management.” 

The power of generative AI is here, and preparing for the move toward cloud-based intelligent applications will ensure that your apps remain performative and scalable and—most importantly—that your business is ready for the newest era of app innovation.

Exercise

- - +
Skip to main content

11 posts tagged with "azure-container-apps"

View All Tags

· 9 min read
It's 30DaysOfIA

Learn best practices for modernizing your on-premises or IaaS solutions to intelligent apps in the cloud.

What We'll Cover:

  • Traditional vs Intelligent Apps
  • On-Premises/IaaS vs. Cloud-Native Platforms
  • Modernization strategy for building Intelligent Apps

image of modernizing AI solutions for intelligent apps

Preparing the Path for Intelligent Apps: Transitioning from On-Premises/IaaS to Cloud-Native Applications

The proliferation of generative AI is paving the way for intelligent applications, software that leverages AI capabilities to deliver unparalleled functionality and user experience. As we covered in a previous article, “Demystifying Intelligent Applications: Leveraging AI in App Development,” intelligent Apps aren’t just products of advanced coding. They interact, learn, and evolve thanks to modern AI and machine learning (ML) breakthroughs. 

The paradigm shift from traditional to intelligent apps demands that we change our approach to the technical challenges of software and architecture design. It also necessitates adopting new ways of thinking and operating within an organization, which we explore in greater depth in “Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management.” 

This article explores the technical infrastructure powering intelligent apps, providing a roadmap to transition traditional on-premises or Infrastructure as a Service (IaaS) solutions to intelligent apps deployed to cloud native platforms and services. We’ll discover the differences between conventional and intelligent apps, options for re-architecting existing applications, and critical strategic considerations integral to modernizing the application-building approach. 

Understanding the Shift: Traditional vs. Intelligent Applications

Intelligent apps are more than just an incremental evolution from traditional apps. They involve a fundamental shift in software solution design, development, and deployment. A clear understanding of how each paradigm differs is crucial for comprehending the impact of this transition. 

Functionality and User Experience

Traditional applications, often rule-based and rigid, rely on preprogrammed operations. A traditional weather app, for example, typically presents structured, location-based forecasts using a predetermined data source. Meanwhile, an intelligent app leverages AI for data-driven decision-making and personalization. Rather than merely displaying the forecast, it integrates ML with additional data sources—like a user’s calendar and fitness data—to determine their favorite outdoor activities, provide relevant clothing suggestions, and even automatically design ideal vacations and times based on the user’s optimal weather preferences. 

Similarly, while traditional applications tend to offer a one-size-fits-all UI, intelligent apps use AI to create tailored interactions. For instance, an intelligent banking app might use generative AI to provide a voice-enabled UI that lets customers use natural language to ask about transactions and receive personalized financial advice.

Infrastructure

Intelligent apps integrating these sophisticated features require highly scalable, flexible infrastructure optimized for AI functionality and able to manage large volumes of real-time data. Unlike traditional applications deployed on-premises or in infrastructure as a service (IaaS) environments, intelligent apps demand the flexibility and scalability of cloud native architectures.

An on-premises or IaaS-based e-commerce application may struggle with connecting its data to AI services or scaling to accommodate voluminous real-time data and requests. But an intelligent app built with a cloud native approach and scalability at its core can handle variable demand much more efficiently. It also maintains its infrastructure components on demand, whether for collecting data for AI/ML consumption or user-facing features like AI-enabled customer support and personalization.

Consider an intelligent app built on Azure that creates ChatGPT-like experiences with custom data. It might connect Azure Open AI and Azure Cognitive Search to an app running on Azure Container Apps or Azure Kubernetes Service. 

The architecture might look like the following diagram based on this demo app:

diagram of a demo app architecture

While the cloud is well-equipped to handle the challenges associated with shifting to AI-centric infrastructure—handling real-time data processing and storage, integrating and managing AI services, configuring event-driven and microservices architectures, and ensuring security across the distributed environment—re-architecting a traditional app to an intelligent app requires careful planning and execution to ensure it benefits fully from the capabilities of AI and cloud computing.

On-Premises/IaaS vs. Cloud-Native Platforms

While on-premises and IaaS solutions provide substantial control over data and processes, this fine-grained control comes with setup and maintenance costs and may lack the scalability necessary for AI-driven Intelligent Apps. On-premises hosting involves purchasing and maintaining hardware, including servers and storage systems. IaaS involves leasing virtual hardware and then hiring and training IT personnel for the infrastructure’s operation, maintenance, and security.

Conversely, infrastructure built on cloud native app services with Microsoft Azure offers virtually unlimited scalability without ongoing hardware and maintenance costs. This feature is ideal for intelligent apps that need to adapt and scale to accommodate fluctuating workloads and data streams. And because these cloud infrastructures typically operate on a pay-as-you-go model, traditionally substantial upfront costs turn into predictable operating expenses, lowering both the cost and risk of launching applications.

Moreover, cloud service providers continually update their offerings, so building on sophisticated AI tools and services through the cloud, like the Azure AI Platform and orchestration capabilities with Azure Kubernetes Service, ensures that you have access to the latest AI and most advanced AI models and technologies.

Strategic Considerations for Transitioning to Intelligent Apps

When developing a strategy for transitioning to intelligent apps, you must consider costs, personnel skill development, and data management.

Monolithic architectures are unlikely to support the scale and ease of iteration required by intelligent apps, so you’ll likely need to implement code changes to support the transition. Your chosen hosting solution and the amount of data your app will handle affect cost and performance, in addition to determining privacy and compliance requirements.

An app that manages or stores terabytes or petabytes of data will require storage-optimized virtual machines or a managed database service hosting. Meanwhile, an app that processes video content or performs resource-intensive calculations should prioritize high-performance computing. Maximizing storage and performance can become extraordinarily costly.

Finally, existing applications shifting towards intelligent apps may encounter compatibility conflicts, depending on database type or the existing system’s underlying programming language. As you might imagine, a traditional app running on-premises and using a shared local database may need significant restructuring to support a cloud native architecture. You need plans for data migration, potential downtime, and training.

Next, let’s look at this transformation process.

Re-Architecting Monolithic Apps into Microservices

Shifting from monolithic applications to microservices is pivotal for transitioning to Intelligent Apps, and you can use this shift as an opportunity to transform your legacy software into intelligent apps that leverage the flexibility and power of microservices-enabled AI solutions.

Traditionally, a monolithic application (let’s use an online marketplace as an example) might handle all activities—from serving webpages to processing payments—within an on-premises server or a cloud server. However, this approach is challenging to scale and remains vulnerable to network failures or cyberattacks.

Breaking these applications into multiple services with specific responsibilities, such as authentication or notifications, can overcome these limitations. These services are typically deployed as microservices within container environments like Azure Kubernetes Service or serverless platforms like Azure Container Apps or Azure Functions using automated processes. This approach offers scalability, robustness, and improved performance, as each microservice can run independently and use its own database.

Shifting towards smaller, independent microservices in app architectures allows us to reap the benefits of AI-powered intelligent apps, for example, by using microservices to handle AI tasks such as processing natural language input, analyzing behavioral data, or personalizing content.

info

Watch Episode 1 and Episode 2 of the Learn Live series on how to build, test and deploy an end-to-end intelligent app solution using the Contoso Real Estate Sample.

Event-Driven Architectures: Knowing When to Use Them

Event-Driven Architectures (EDAs) can significantly enhance the performance and cost-effectiveness of intelligent apps. EDAs, typically implemented with microservices, respond in real time to events or state changes ranging from user interactions to real-time analytics.

Consider an intelligent app designed for personalized customer engagement. Instead of designing the application to check continuously for changes in user data, an EDA can react to such changes immediately, triggering appropriate microservices. For example, if users modify their preferences, an event could trigger an AI-driven recommendation microservice to update its suggestions immediately. This real-time responsiveness, enabled by services like Azure Event Grid, can improve user experiences and make intelligent apps more adaptive and proactive.

However, it’s worth noting that while EDAs provide significant benefits, they can introduce unique complexities, such as maintaining event consistency and debugging issues in event chains across multiple microservices. Therefore, it’s important to assess your application’s needs so you can understand where it can benefit from an EDA.

Conclusion

Transitioning to intelligent apps represents a shift in strategy, architecture, and infrastructure. By designing and re-architecting for cloud-based infrastructure, your apps gain scalability, cost-effectiveness, robust data management, access to sophisticated AI tools, enhanced security, and reduced operational overhead.  

But this transition extends well beyond its technical components. Organizations and the people working within them need equal attention. You can explore these facets in greater depth in “Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management.” 

The power of generative AI is here, and preparing for the move toward cloud-based intelligent applications will ensure that your apps remain performative and scalable and—most importantly—that your business is ready for the newest era of app innovation.

Exercise

+ + \ No newline at end of file diff --git a/30daysofIA/tags/azure-container-apps/page/3/index.html b/30daysofIA/tags/azure-container-apps/page/3/index.html index 308f7e7e3a..4362816fba 100644 --- a/30daysofIA/tags/azure-container-apps/page/3/index.html +++ b/30daysofIA/tags/azure-container-apps/page/3/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "azure-container-apps" | Build Intelligent Apps On Azure +11 posts tagged with "azure-container-apps" | Build Intelligent Apps On Azure @@ -14,13 +14,13 @@ - - + +
-

9 posts tagged with "azure-container-apps"

View All Tags

· 10 min read
It's 30DaysOfIA

Explore how digital natives and Independent Software Vendors (ISVs) can transform their business operations and generate higher value for customers with generative AI and intelligent apps.

What We'll Cover:

  • Potential of Generative AI in application development
  • Challenges with Generative AI
  • Integrating Generative AI with business operations

image of generative AI and intelligent apps used in business

Unleashing Business Transformation: How Digital Natives Can Leverage Generative AI

Generative AI is no longer a technological flight of fancy. It’s here, and it’s rewriting the rules of business innovation. 

This type of AI leverages machine learning algorithms to create new data with similar characteristics to a given dataset. It goes beyond text generation using generative pretrained transformer (GPT) models, covering an array of applications that span from image synthesis to translations and more. It also powers a new class of intelligent applications, which combine AI capabilities with traditional software functions for enhanced user experiences and advanced business solutions. 

Generative AI is transforming how digital natives and independent software vendors (ISVs) operate. Generative AI offers new possibilities for creating unique business value, enhancing customer experiences, and driving growth. 

It plays a critical role in applications, improving functionality, aiding decision-making, and automating complex tasks to deliver dynamic, uniquely personalized user experiences. Generative AI also enables and automates data-driven outcomes, functions, and features.

Integrating AI capabilities into customer-facing apps and turning them into intelligent apps benefits both the customer and the business, as we discussed in Demystifying Intelligent Applications: Leveraging AI in App Development. You get faster product development workflows and better business intelligence for sales and marketing, while your users get more natural and context-aware features and interactions. 

That said, to realize generative AI’s potential, you need some strategy and knowledge of how and where it fits into your current business and operations. We’ll examine this technology’s potential to transform business operations, its risks, and rewards, and how to integrate it into your existing operations. 

Unpacking the Potential of Generative AI

Generative AI is creating new opportunities in the digital playing field. It empowers businesses to generate insights, design products, and automate processes in ways and at speeds that were previously unimaginable. You can now predict customer behavior more accurately, offer highly personalized user experiences, and prototype innovative ideas rapidly. 

For example, Microsoft used generative AI to reinvent web search in the new AI-powered Bing and Edge. The search experience has evolved from a solitary user flow of typing in keywords and scrolling through results. It’s now a conversational experience where Bing uses context to offer better quality and more personalized search queries and results. 

Similarly, Microsoft harnessed generative AI to transform its productivity suite into intelligent apps. Through Copilot for Microsoft 365, PowerPoint now empowers users to tell their stories by using DALL-E to generate images effortlessly. It also helps rephrase text-heavy slides to be more straightforward. Outlook now assists users to compose more effective emails, enabling better communication.

These are just some examples of how AI powers day-to-day work and operations. 

Businesses that ignore generative AI miss out on untapped market opportunities, letting competitors gain an edge in the digital race. By seeking ways to rethink assumptions and reinvent existing approaches, you can effectively use generative AI to create new opportunities, open products to new use cases and markets, unlock new growth and customer satisfaction levels, and increase your revenue.

info

Register for the intelligent apps webinar on Driving Business Value by Modernizing with Cloud-Native & AI with Microsoft and Forrester on September 26.

Explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries. 

The Crucial Role of Generative AI in Business Operations

Generative AI can be a powerful catalyst for business transformation. Let’s quickly tour some ways it can redefine business operations. 

Product development is traditionally a long and painstaking process. Now, tools like GitHub Copilot and TestPilot use generative AI to make coding and testing software easier, faster, and more robust. 

Similarly, machine learning as a service technology, such as Azure Machine Learning, democratizes the availability of machine learning. It helps collect, clean, and train models across datasets, making engineering and analyzing data more efficient and accessible.  

Generative AI also simplifies the process of creating intelligent apps. Previously, simulating thousands of prototypes, predicting their performance, and narrowing down to the most viable options could take weeks, months, or even years of iteration and data analysis. With generative AI, this process can be as quick as days, hours, or perhaps minutes. 

Customer experience is also ripe for transformation. Personalized recommendations drive engagement and sales, and generative AI can scale that personalization to every customer interaction using comprehensive and continually updated data. For instance, Microsoft leverages AI to enable personalization in Dynamics 365 Customer Insights. Using this tool, you can ask questions about your customers in natural language and gain valuable and actionable insights into your customer base. 

Lastly, marketing and sales can benefit from generative AI. For example, identifying current and future key markets and emerging trends helps companies stay ahead of the curve. Tools like Copilot in Power BI help you quickly understand and uncover insights about your business, enabling your team to craft targeted campaigns, create engaging content, and optimize your business strategy. 

Leveraging this full generative AI potential requires the right tools. GPT-4, for instance, can be instrumental in realizing effective business transformation. It excels in understanding context, generating human-like text, and learning from minimal data input, making it a formidable tool in any digital native's arsenal. Platforms like Azure OpenAI Service bring GPT-4’s power to your fingertips.

However, as with any powerful technology, it’s unwise to treat generative AI as a perfect solution. You must know the challenges and risks of not leveraging it effectively, including technical and human-factor considerations. 

Without generative AI, your company risks missing the opportunity to please customers via personalization, as discussed previously. You’ll miss sales opportunities via business intelligence insights, limiting your ability to grow revenue and keep up with the competition. 

One challenge of leveraging generative AI’s full potential is finding the right tools. Fortunately, you can implement generative AI in your organization without investing in infrastructure, computational power, and expertise. Platforms like Azure OpenAI Service and Azure AI Services make AI easily accessible. They also work with Azure compute and data services like Azure Functions and Azure Kubernetes Service (AKS), enabling you to build intelligent apps easily and quickly. 

However, generative AI can’t do everything. It may need human expertise and intervention to use it fully. 

For example, generative AI can create unique data. However, the data may have limited diversity and complexity. The AI may need help recognizing additional considerations or requirements specific to a problem. It may also lack context due to various factors, such as the information’s freshness. Also, since it’s an emerging field that’s potentially worth $4.4 trillion, we’re still learning about the nuances and potential regulatory impact of generative AI. 

Perhaps the most crucial and challenging aspect of adopting generative AI is an organization’s required cultural shift. Your teams must learn how to use AI as your company fosters an understanding of AI’s potential and limitations. 

Begin integrating AI literacy into your company’s DNA. Knowledge of its potential and the readily available tools can inspire your teams to incorporate generative AI into their daily workflows and business operations quickly, improve productivity, and deliver superior customer experiences. 

No company wants to be outpaced by its competitors. You can balance generative AI’s potential with your business operations by staying aware of and adapting to the dynamic AI landscape.

Achieving Harmony: Integrating Generative AI with Existing Business Operations

Integrating generative AI into existing business operations doesn’t have to be disruptive. With the correct strategy, you can do so seamlessly. Your goal should be to augment and enhance your operations, not disrupt and replace. 

For example, Microsoft Cloud technologies leveraged AI to relieve healthcare organizations’ challenges, such as talent shortage and workforce burnout. Providers automated their workflows, invested in specific applications and connectors, and enhanced support for data and operations. 

Azure AI Services, such as text analytics for health, enable healthcare organizations to understand their largely unstructured and untapped medical data. Project Health Insights leans on AI to help healthcare professionals gain actionable insights and inferences from patient data. 

Azure Health Bot, an Intelligent App, empowers organizations to build and deploy legally compliant, powerful conversational healthcare experiences to patients. 

A successful AI integration like this begins with understanding. Knowing what generative AI can do and setting realistic expectations can help you identify where and how AI can best serve your operations. 

Next, you can foster a culture of AI literacy across teams, speeding up adoption by helping them understand how it can aid their work. The more familiar your team is with AI and its possibilities, the smoother the transition. Training and education programs play a pivotal role here. 

Finally, integration requires a systematic approach. It’s not about overhauling everything at once — it's about identifying areas where AI can add the most value, then gradually experimenting and scaling its use. Successful integration stories often feature a phased approach, starting small and growing steadily. 

As we move toward a future where generative AI is a staple in business operations, remember that every journey is unique. Although there’s no one-size-fits-all solution, a strategic approach and a keen understanding of your business needs will help you navigate this new landscape successfully.

Conclusion

Generative AI offers plenty of opportunities for businesses that embrace it. It’s fueling the next generation of intelligent applications, transforming everything from product development to customer engagement to marketing. This technology is the key to unlocking unprecedented growth and innovation. 

While the journey may have challenges, the potential rewards make generative AI worth exploring. As generative AI advances, Azure AI’s broad range of tools and services will help you navigate this fascinating field.

Exercise

  • Watch Episode 02 of the serverless edition Learn Live session to learn how to build, test and deploy an end-to-end intelligent app solution.
  • Complete the Cloud Skills Challenge to build on your AI skills.
  • Register for Ask the Expert: Azure Functions on September 26 for a live Q&A with the Product Team on how to build AI apps using serverless computing.
- - +
Skip to main content

11 posts tagged with "azure-container-apps"

View All Tags

· 12 min read
It's 30DaysOfIA

Delve into creating an Intelligent App that leverages Azure AI Vision to analyze images and extract data. Develop an API to perform optical character recognition (OCR) on uploaded images and testing this API locally.

What We'll Cover:

  • Understanding Azure AI Vision and Azure Kubernetes Service
  • Build a Python Web API to perform OCR
  • Test the API locally

image depicting an Intelligent App using AI

Jumpstart Your AI Journey: Building Your First Intelligent App with Azure AI and AKS (1)

An Intelligent App is a software application that uses artificial intelligence (AI), such as machine learning (ML), to extract meaningful insights, enabling users to make informed decisions based on the extracted text, data, sound, images, and more.

Intelligent Apps can employ advanced algorithms and models to recognize objects, patterns, and features within images, allowing them to perform tasks like object identification, image classification, facial recognition, and image segmentation.

To explore the power of Intelligent Apps, let’s build a Python app that performs optical character recognition (OCR) on images containing numbers and performs aggregate functions on the extracted numbers, like sum and average. We’ll leverage Azure AI Services for the OCR functionality and Azure Kubernetes Service (AKS) to develop, publish, and maintain a cloud-based, scalable app in Azure.

Let’s get started!

Understanding Azure AI Vision and Azure Kubernetes Service

Azure AI Vision is a cloud-based service that offers a range of advanced computer vision capabilities enabling applications to interpret content, such as images and videos, using ML and other AI algorithms. Azure computer vision lets users integrate powerful vision features into their projects without ML experience.

OCR is one of the critical functionalities within Azure vision service, allowing applications to extract text from images. Within Azure AI Vision service, OCR offers several features, including text extraction, multilingual support, layout analysis of documents, and integration with Azure services, such as Azure Functions and Azure Machine Learning.

info

Watch the intelligent apps webinar on Driving Business Value by Modernizing with Cloud-Native & AI with Microsoft and Forrester on September 26.

AKS is Microsoft Azure’s container orchestration platform that enables us to quickly and efficiently deploy, manage, and scale containerized applications, such as our Intelligent App.

Let’s explore how to leverage these tools to build an Intelligent App that uses Azure AI Vision to analyze images and extract data.

Prerequisites

To follow this tutorial, ensure you have the following:

For a preview of this final project, take a look at the complete project code.

Building the API with Azure AI Vision Service

First, log in to your Azure account and navigate to the Azure Portal.

Click Create a resource and search for “resource group.” Create a new resource group named computer-vision.

image in Azure Portal of creating a new resource group

Return to the Azure Portal home page and click Create a resource.

image in Azure Portal of creating a new resource

Search for “computer vision” and select it from the results. Click Create.

image of searching for resource group in Azure Portal

Clicking Create displays the Create Computer Vision wizard. Select the resource group you just created as the Resource group from the dropdown. Enter a resource name similar to what is seen in the screenshot below. Note that this must be a unique name so choose one that is not currently in use. Select Standard S1 as the pricing tier and check the Responsible use of AI box in the field at the bottom.

Image of selecting the Responsible use of AI box

Image of project details

Click Review + Create, then click Create.

Next, get the API key and endpoint from the Azure AI services multi-service account you created. Once the resource is created, navigate to the resource details page. Under Resource Management, select Keys and Endpoint. Once on the Keys and Endpoint page, you’ll find the Key 1 and Endpoint values. These credentials are necessary to access the Azure AI APIs.

image of selecting Keys and Endpoint

second image of selecting Keys and Endpoint

Configuring the Local Environment Variables

Next, we need to define a local environment variable file. This convenient way to store our API keys in a simple text file enables us to manage sensitive information and separate configuration from our API code.

If you haven’t done so already, clone the starter project template from GitHub into your local computer. Open the starter project template in a code editor and create a new .env file in the root folder.

Note: Root folder in this case is the “/Microsoft_Series17-18_Code/intelligent-app-before" folder.

Add the following key-value pairs to the file, remembering to replace the placeholder values with those you obtained from the previous step.

VISION_KEY=<THE-KEY-1-VALUE-FROM-YOUR-AZURE-AI-SERVICE>
VISION_ENDPOINT=<THE-ENDPOINT-VALUE-FROM-YOUR-AZURE-AI-SERVICE>

Reviewing the Quickstart Code

Let’s review the app.py file in the starter project template.

The app.py file works as the entry point for the Flask application. It is responsible for initializing the web server, listening for incoming requests, dispatching them to the appropriate route handlers, and generating responses.

Below is the code contained in the app.py file:

import os
import json

from flask import Flask, request
from flask_restful import Resource, Api
from werkzeug.utils import secure_filename

app = Flask(__name__,
static_url_path='',
static_folder='static/files')

api = Api(app)

app = Flask(__name__)

app.config['UPLOAD_FOLDER'] = 'files'

api = Api(app)

class UploadHandler(Resource):

def allowed_file(self, filename):
return '.' in filename and \
filename.rsplit('.', 1)[1].lower() in {'png'}

def post(self):
form = request.form.to_dict()

if 'file' not in request.files:
return json.dumps({ "success": False, "error": "No file part"})

file = request.files.get("file")
if file and self.allowed_file(file.filename):
filename = secure_filename(file.filename)
upload_folder = "static/files"
if not os.path.exists(upload_folder):
os.makedirs(upload_folder)
local_file_path = os.path.join(upload_folder, filename)
file.save(local_file_path)

return f"File {filename} uploaded successfully to folder: {upload_folder}"

api.add_resource(UploadHandler, "/")

if __name__ == '__main__':
app.run(debug=True)

The UploadHandler class above is defined as a Flask RESTful resource to handle file uploads. The class contains two methods:

  • The allowed_file method checks whether the file extension is allowed for upload. In this case, only .png files are allowed.

  • The post method handles HTTP POST requests for file uploads. It saves the uploaded .png file to the static/files folder.

Finally, the application returns a text response informing us that the file has been uploaded successfully.

Implementing the REST API in Python

To implement image analysis in your REST API, open the starter project template in your terminal, create a virtual environment, and activate it.

Next, install the Azure AI Vision SDK:

pip install azure-ai-vision

Then, add the following line to the requirements.txt file to include the azure-ai-vision package:

azure-ai-vision==0.13.0b1

Now, create an ocr_helper.py file in the project’s root folder. This file is a Python module that provides functions for processing images using OCR. Add the following code to the file:

import os
from statistics import median
from decimal import Decimal
import azure.ai.vision as sdk

def process_ocr(source_image):
service_options = sdk.VisionServiceOptions(os.environ["VISION_ENDPOINT"],
os.environ["VISION_KEY"])

vision_source = sdk.VisionSource(filename=source_image)

analysis_options = sdk.ImageAnalysisOptions()

analysis_options.features = (
sdk.ImageAnalysisFeature.CAPTION |
sdk.ImageAnalysisFeature.TEXT
)

analysis_options.language = "en"

analysis_options.gender_neutral_caption = True

image_analyzer = sdk.ImageAnalyzer(service_options, vision_source, analysis_options)

result = image_analyzer.analyze()

ocr_result = get_ocr_result(result)

return ocr_result

def get_ocr_result(result):
string_list = []

if result.reason != sdk.ImageAnalysisResultReason.ANALYZED:
return sdk.ImageAnalysisErrorDetails.from_result(result)
else:
if result.text is not None:
for line in result.text.lines:
for word in line.words:
string_list.append(word.content)

number_list = convert_to_decimal_list(string_list)

aggregate_result = aggregate_operations(number_list)

return {
"aggregate_result": aggregate_result,
"numbers_read": string_list
}

def convert_to_decimal_list(string_list):
return list(map(Decimal, string_list))

def aggregate_operations(numbers):
result = {
'sum': sum(numbers),
'average': sum(numbers) / len(numbers),
'median': median(numbers),
'min': min(numbers),
'max': max(numbers)
`}`
`return result`

This module uses the azure-ai-vision package to analyze images, including capturing captions and extracting text from the image. The process_ocr function inputs an image file and performs the OCR analysis. The module is a convenient tool for analyzing text-based data in pictures and performing numerical computations based on that data.

Let’s review the different components of the ocr_helper.py module listed above and explore how its functions help our Intelligent App process images and perform aggregate operations:

  • The process_ocr function takes the parameter source_image, which is the path of the image to be processed. The function then initializes the VisionServiceOptions using environment variables VISION_ENDPOINT and VISION_KEY to connect to the Azure AI Vision API.
  • The process_ocr function creates a VisionSource object with the specified source_image file name. ImageAnalysisOptions specify the features to be analyzed, including CAPTION and TEXT. The language is set to English ("en"), and gender-neutral captions are enabled.
  • Finally, an ImageAnalyzer object is created with the service options, vision source, and analysis options. The image is then analyzed using the image_analyzer.analyze method to retrieve OCR results. The recognized text from the OCR result is extracted, with numerical values stored in the string_list variable.
  • The convert_to_decimal_list function converts the list of strings to a list of decimal numbers, which helps process numeric values extracted from the text.
  • The aggregate_operations function calculates various aggregate statistics (sum, average, median, minimum, maximum) from a list of numbers and returns the results in a dictionary.

Note that you must have the appropriate credentials (VISION_KEY) and endpoint (VISION_ENDPOINT) configured for the Azure AI Vision API to use this module.

Finally, we must modify the app.py file so our code can use the process_ocr function of the ocr_helper.py file.

Add the following import statement to the app.py file:

from ocr_helper import process_ocr

Then, replace this line:

return f"File {filename} uploaded successfully to folder: {upload_folder}"

With these two lines:

aggregates = process_ocr(local_file_path)
return json.dumps(aggregates, default=str)

Doing so allows our application to perform aggregate operations (sum, average, median, minimum, maximum) of any numeric values found in the text and return the operation’s result as a JSON response.

Running the Intelligent App Locally

Our final goal is to deploy the sample app to AKS, which will run the app in a container. However, as a good practice, let’s run the app in a container locally before moving it to AKS. Doing so ensures that our application and its dependencies are encapsulated and can be run consistently across different environments.

Deploying to Docker Desktop

For our application to run on Docker, it needs two additional files: a Dockerfile and docker-compose.yml. A Dockerfile creates a single container image by specifying the steps to build it, and a docker-compose.yml file manages multiple containers as a cohesive application, defining their configurations and relationships. Dockerfile builds the image, while docker-compose.yml orchestrates multi-container applications.

In the project root folder, add a file named Dockerfile with the content below:

# syntax=docker/dockerfile:1

FROM python:3.8-slim-buster

WORKDIR /intelligentapp

COPY requirements.txt requirements.txt
RUN pip3 install -r requirements.txt

RUN pip install debugpy

COPY . .

CMD [ "python3", "-m" , "flask", "run", "--host=0.0.0.0"]

Now create a file named docker-compose.yml file in the project’s root folder with the following code, replacing the VISION_KEY and the VISION_ENDPOINT according to the environment variables you configured earlier:

version: '3.8'
services:
intelligentapp:
build:
context: .
dockerfile: Dockerfile
image: intelligent-app
ports:
- 5000:5000
container_name: intelligent-app
environment:
- VISION_KEY=<THE-KEY-1-VALUE-FROM-YOUR-AZURE-AI-SERVICE>
- VISION_ENDPOINT=<THE-ENDPOINT-VALUE-FROM-YOUR-AZURE-AI-SERVICE>

Then, run the following command in the terminal to build the image and start a container for the Intelligent App services defined in the docker-compose.yml file:

docker-compose up --build --force-recreate

Next, open a new terminal and run the command below to list the image deployed to your local Docker:

docker images

docker images

Testing the Intelligent App Locally

Now, we’ll test the Intelligent App’s functionality using the following test image:

test image to test functionality

This image is provided as sample1.png in the sample app source files, so you can easily use it in the next step.

The Intelligent App will read the image above using OCR to extract numbers in the image and perform five aggregate functions:

  • Sum
  • Average
  • Median
  • Min
  • Max

To test the API, open Postman and fill out the fields as follows:

  • URL: http://localhost:5000/
  • Method: POST
  • Body:
    • Form-data
      • Key: File — Click the right end of the Key field and select File from the dropdown list.
      • Value: Click Select Files, then select the sample1.png file provided in the sample code.

image of selecting the sample file

Now click the Send button and review the result body:

"{\"aggregate_result\": {\"sum\": \"25821\", \"average\": \"5164.2\", \"median\": \"5622\", \"min\": \"1447\", \"max\": \"9802\"}, \"numbers_read\": [\"3049\", \"5901\", \"5622\", \"1447\", \"9802\"]}"

As we can see, the app running on a local container returned the correct results based on the sample image. So, we’re ready to prepare our Azure resources to run the Intelligent App on AKS.

Exercise

In this article, we explored the creation of an Intelligent App that leverages Azure AI Vision to analyze images and extract data. We learned how to build a Python Web API to perform OCR on uploaded images and subsequently test this API locally.

Head to the next part, to deploy this API via Azure Kubernetes Service.

+ + \ No newline at end of file diff --git a/30daysofIA/tags/azure-container-apps/page/4/index.html b/30daysofIA/tags/azure-container-apps/page/4/index.html index 1d9fa4971a..4b186112a9 100644 --- a/30daysofIA/tags/azure-container-apps/page/4/index.html +++ b/30daysofIA/tags/azure-container-apps/page/4/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "azure-container-apps" | Build Intelligent Apps On Azure +11 posts tagged with "azure-container-apps" | Build Intelligent Apps On Azure @@ -14,13 +14,13 @@ - - + +
-

9 posts tagged with "azure-container-apps"

View All Tags

· 9 min read
It's 30DaysOfIA

In the part of our series on intelligent apps, we’ll explore how AI is transforming application development, from design and architecture to building.

What We'll Cover:

  • Revolutionizing application development using AI
  • Infusing AI in application design architecture
  • AI Assisted Pair Programming
  • Future of App Development

A decorative image of application development with AI

Reimagining Application Development With AI: A New Paradigm

Artificial intelligence (AI) is impacting every industry. AI-powered development tools offer new functionalities and assist software developers throughout the development process, revolutionizing app development. From initial design and architecture to coding and debugging, AI reshapes how we approach application conception, creation, and iteration.

In part one of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” we explored the new breed of intelligent apps and how they use AI. In this article, we’ll see how AI technologies enhance developer toolkits and shape the future of app development, particularly through AI-enabled coding assistance and debugging.

The Dawn of Intelligent Apps: Revolutionizing Application Development

AI is more than just another addition to the development toolbox—it represents a revolution bringing unprecedented efficiency, innovation, and user engagement to our applications. 

What sets intelligent apps apart is how they can adapt and learn, helping us deliver unique, real-time user experiences.  

One example is an AI-driven health application that can track biometric data, predict potential health issues, provide personalized health advice, and even guide emergency services during a medical crisis. An AI-enabled language learning app that adapts to our learning speeds, recognizes our problem areas, and customizes its content to help us improve our language proficiencies is another example.  

Intelligent apps like these don’t just use data for recommendations—they can make critical decisions and provide sophisticated services that were previously impossible. 

For developers, intelligent apps mean we’re no longer just coding—we’re teaching applications how to think, adapt, and make decisions. AI is reshaping every step of the development journey, helping us to build more intuitive and user-focused products. 

Next, we’ll examine the impact of AI on application design and architecture and how AI-assisted “pair programming” changes software development.

info

Register for the FREE webinar on Intelligent Apps with Microsoft and Forrester on September 26 to explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries.

There will be a showcase of real-world use cases that demonstrate how AI can be seamlessly integrated into cloud-native environments driving tangible business value.

AI in Design and Architecture: Fueling Creativity and Efficiency

AI technologies help developers automate and streamline their processes and provide new ways to think about design.  

In traditional application development, the design and architecture phase can take significant time, effort, and expertise. For instance, adopting a monolithic architecture or a microservices-based design can significantly increase coding, testing, and debugging complexity. The app may even behave differently when hosted locally than in a deployed environment. It may take hours to track down an issue manually—time developers could spend elsewhere. 

Conversely, we can generate numerous design iterations in seconds with AI, each optimized to fulfill specific user needs and business goals. AI tools can quickly model various scenarios' impacts and software design patterns in the architectural design phase. It provides insights into performance and potential issues and suggests improvements. AI can also generate skeleton code, allowing developers to prototype different solutions or test specific components and technologies rapidly. By reviewing thousands of lines of code in seconds, AI allows us to focus on creating apps that serve users through feature development instead of searching for bugs.  

Integrating AI into the design and architecture phase can even extend our creative capabilities. By leveraging the power of generative AI—like large language models (LLMs)—and their broad knowledge and context going beyond software, we can better brainstorm apps and feature ideas. We can go beyond code and software architecture and get ideas about user experience, logo and branding, or marketing concepts. And as AI tools deliver insights and ideas we would have never considered; it encourages more innovation.

info

Watch Episode 01 of the Learn Live session to learn how to build an end-to-end intelligent app solution architecture.

The Power of AI-Assisted Pair Programming in Building Applications

AI-assisted pair programming is a collaborative coding approach where an AI system—like GitHub Copilot or TestPilot—assists developers during coding. It’s an increasingly common approach that significantly impacts developer productivity. In fact, GitHub Copilot is now behind an average of 46 percent of developers’ code and users are seeing 55 percent faster task completion on average. 

For new software developers, or those interested in learning new skills, AI-assisted pair programming are training wheels for coding. With the benefits of code snippet suggestions, developers can avoid struggling with beginner pitfalls like language syntax. Tools like ChatGPT can act as a personal, on-demand tutor—answering questions, generating code samples, and explaining complex code syntax and logic. These tools dramatically speed the learning process and help developers gain confidence in their coding abilities. 

Building applications with AI tools hastens development and provides more robust code. For example, GitHub Copilot helps us write new code accurately. It also quickly makes sense of existing code, suggests whole new lines or blocks of code, and even detects errors and proposes fixes. It’s like having a second coder working alongside us—one who never tires and is trained on billions of lines of code. 

However, it’s also important to acknowledge the limitations of AI. While generative AI-assistance technologies like GitHub Copilot are impressive, they’re still imperfect. They can sometimes produce erroneous, non-functional, or vulnerable code. They can also lack nuanced, domain-specific knowledge.  

So, although these tools are helpful, we can’t rely on them fully—these are programming aids rather than definitive solutions. AI-assisted pair programming is about augmenting the human developer’s skills, not replacing them.

Debugging and Improving Code With AI: Enhancing Application Quality

Identifying and correcting code errors is often time-consuming and challenging for developers. AI streamlines the debugging process by automatically detecting anomalies, suggesting fixes, and learning from previous bugs to prevent them from recurring. 

We can use generative AI to create unit tests from code snippets, from simple parameter checking and input validation to complex mocks of database and network services. Automated AI testing improves code coverage and robustness. It saves significant time compared to manually crafting unit tests. 

AI also revolutionizes code reviews by scanning and highlighting potential issues—like code patterns leading to a bug or a coding standards violation—and suggesting ways to simplify or refactor the code for readability and performance. This reduces human error and makes the code review process more efficient. Its benefits extend to small teams, where the availability of developers is crucial to deadlines, and large teams, where effective collaboration depends on readability and code consistency. AI further saves time by triaging issues so developers can prioritize the most mission-critical ones. 

Aside from improving code quality, AI is critical to understanding system behavior. Intelligent AI-assisted software development tools can monitor and audit system behavior, giving developers a better insight into how their code performs in different scenarios. AI can track patterns, identify inefficiencies, and provide alerts when they detect unusual behavior—enabling developers to enhance their code further. 

By predicting and identifying potential vulnerabilities, intelligent AI-assisted coding apps like Microsoft Security Copilot are pivotal in ensuring application functionality, reliability, and security. These AI tools are continually trained and upgraded with large codebases and user feedback that refines their responses. This ability to learn from past mistakes makes AI an incredibly powerful tool that will continue to expand its capabilities in the future.

info

Complete the AI Cloud Skills Challenge to build on you AI application development skills with paired programming using generative AI and GitHub Copilot.

AI and the Future of Application Development

The rapid advancements in AI and machine learning technologies point to an exciting future for application development. 

In “The Future of Applications,” software developer Mike Cann illustrates how LLMs might revolutionize application development. Cann that suggests fully-autonomous coding—AI-generated applications created entirely from prompts—may supersede traditional hand-crafted applications. Dynamically generated user interfaces could offer personalized and customizable experiences while we can design APIs for machine consumption and efficiency rather than human readability. Furthermore, we can store data in less structured, more adaptable, and flexible forms. 

AI is already making application development more accessible. With AI handling the complex, technical aspects of development, less-technical individuals can take a greater role in building applications. Consequently, AI can potentially democratize software engineering. 

Intelligent apps will continue to evolve, becoming more efficient, adaptable, and capable of learning from user interactions. Similarly, AI will inevitably become integral in every stage of app development.

Summary

AI has already created a new development paradigm—one in which machine intelligence and human creativity come together to produce innovative, efficient, and robust applications. AI already assists us at every stage of app development, from design and architecture to coding and debugging. 

Developers who embrace AI tools and integrate them into their processes can enhance their productivity, improve the quality of their applications, and expand the possibilities of their application development.  

The future of development is automated and AI-driven, and it’s here today. Getting started is easy—try an AI-powered development tool like GitHub Copilot to see how AI-assisted pair programming can enhance your productivity and code quality.

Exercise

- - +
Skip to main content

11 posts tagged with "azure-container-apps"

View All Tags

· 9 min read
It's 30DaysOfIA

In the last article, we created an Intelligent App that leverages Azure AI Vision to analyze images and extract data. Develop an API to perform optical character recognition (OCR) on uploaded images and testing this API locally.

In this article, we will deploy the web API to the cloud platform using Azure Kubernetes Service.

What We'll Cover:

  • Understanding Azure AI Vision and Azure Kubernetes Service
  • Build a Python Web API to perform OCR
  • Test the API locally

image depicting an Intelligent App using AI

Jumpstart Your AI Journey: Building Your First Intelligent App with Azure AI and AKS (2)

In the previous article we explored the creation of an Intelligent App that leverages Azure AI Vision to analyze images and extract data. We learned how to build a Python Web API to perform OCR on uploaded images and subsequently test this API locally.

In this article we will use Azure Kubernetes Service (AKS) to develop, publish, and maintain our app in the cloud on Azure.

Let’s get started!

Prerequisites

To follow this tutorial, ensure you have completed Jumpstart Your AI Journey: Building Your First Intelligent App with Azure AI and AKS (1).

Pushing a Container Image to Azure Container Registry (ACR)

To start, open your CLI or terminal and type the following command:

az login

Follow the instructions displayed on your browser to enter your Azure credentials.

Once authenticated, you’ll initiate a secure connection between your local environment and Azure. This process grants you access to cloud services and resources.

Next, type the command below in your terminal to set up a new Azure Container Registry (ACR) to store your container images:

az acr create --resource-group computer-vision --name <name-of-azure-container-registry> --sku Basic

Remember to replace <name-of-azure-container-registry> with your container registry name. The name must be unique within Azure and comply with these rules.

The command above creates an Azure Container Registry (ACR) in the computer-vision resource group under the Basic SKU. This ACR is your secure and private repository for storing container images within Azure.

Next, log in to the registry with the following command:

az acr login -n <name-of-azure-container-registry>

The az acr login command above lets you securely authenticate and access the specified ACR without providing their Azure credentials each time.

Now, run the following command in your terminal. It will display the endpoint URL to log in to and interact with the ACR for pushing and pulling container images.

az acr show --name <name-of-azure-container-registry> --query loginServer --output table

This returns an endpoint URL as follows:

Result
----------------------------------
<name-of-azure-container-registry>.azurecr.io

Now, run the following command to show all container images, their repository, tags, and size:

docker images
REPOSITORYTAGIMAGE IDCREATEDSIZE
intelligent-applatesta7bf9f75361716 hours ago197MB

Tags are necessary to push Docker images to a remote registry like Azure Container Registry. They also let you differentiate between different versions of the same image and upload or download the one you want.

Run the following command to tag your Docker image:

docker tag intelligent-app <name-of-azure-container-registry>.azurecr.io/intelligent-app:v1

Then, run the docker images command again to check your tagged image:

docker images
REPOSITORYTAGIMAGE IDCREATEDSIZE
intelligent-applatestc52168039265About a minute ago197MB
<name-of-azure-container-registry>.azurecr.io/intelligent-appv1c52168039265About a minute ago197MB

Now run the following command so Docker can securely upload the image to your Azure Container Registry:

docker push <name-of-azure-container-registry>.azurecr.io/intelligent-app:v1

Once we've deployed the image to the container registry, AKS can access it during deployment.

info

Watch Episode 03 for the Learn Live session to experience a guided session on how to build, test and deploy an end-to-end intelligent app solution.

Deploying the Intelligent App on Azure Kubernetes Service (AKS)

Before we deploy our Intelligent App to AKS, we need to provision an AKS cluster and define Kubernetes manifests.

To provision an AKS cluster to host our application, we specify the desired configurations for the cluster, such as the number of nodes, node size, and networking options. But first, download and install the Kubernetes command-line tools (kubectl), a client credential plugin implementing Azure authentication:

az aks install-cli

If you’re using Linux, review this tutorial. Then run the following:

sudo az aks install-cli

Next, run the following command in your terminal to enable access to the networking-related resources and services provided by the `Microsoft.Network`` namespace in Azure:

az provider register --namespace Microsoft.Network

Now, we must create an AKS cluster. Run the following command to create an AKS cluster named aks-intelligent-app in the computer-vision resource group.

az aks create --resource-group computer-vision --name aks-intelligent-app --node-count 1 --generate-ssh-keys

The command above specifies the target resource group: computer-vision. The node pool is configured with one virtual machine (VM), and the secure shell (SSH) keys for secure node access are generated automatically.

Next, run the following command to update the AKS cluster you created by attaching it to your ACR. Doing so allows the AKS cluster to pull container images from the specified ACR when deploying workloads to the cluster.

az aks update -n aks-intelligent-app -g computer-vision --attach-acr <name-of-azure-container-registry>

Then, run the command below to configure kubectl to work with our AKS cluster in the computer-vision resource group.

az aks get-credentials --resource-group computer-vision --name aks-intelligent-app

The command above retrieves the necessary credentials and context information required for kubectl to communicate with the AKS cluster.

We still have to define the Kubernetes manifests written in YAML, describing the desired state of our application deployment, including containers, networking, and scaling rules. We’ll prepare these manifests, including Deployment and Service configurations, to define how our application should be deployed and exposed.

Create a folder named Deployment in the project root directory. Next, create two files in the deployment folder: deployment.yml and service.yml.

Add the following configuration to the deployment.yml file, replacing the <name-of-azure-container-registry> placeholder with your registry’s name:

apiVersion: apps/v1
kind: Deployment
metadata:
name: intelligent-app
spec:
replicas: 1
selector:
matchLabels:
app: intelligent-app
template:
metadata:
labels:
app: intelligent-app
spec:
nodeSelector:
kubernetes.io/os: linux
containers:
- name: intelligent-app
image: <name-of-azure-container-registry>.azurecr.io/intelligent-app:v1
resources:
limits:
memory: 512Mi
cpu: "1"
requests:
memory: 256Mi
cpu: "0.2"
ports:
- containerPort: 5000
env:
- name: FLASK_DEBUG
value: "1"
- name: VISION_KEY
value: <THE-KEY-1-VALUE-FROM-YOUR-AZURE-AI-SERVICE>
- name: VISION_ENDPOINT
value: <THE-ENDPOINT-VALUE-FROM-YOUR-AZURE-AI>

Additionally, edit the VISION_KEY and VISION_ENDPOINT environment variables above according to the API key and endpoint of your Azure AI Services instance.

Then, add the following configuration to the service.yml file:

apiVersion: v1
kind: Service
metadata:
name: intelligent-app-service
spec:
type: LoadBalancer
ports:
- protocol: TCP
port: 80
targetPort: 5000
name: port5000
selector:
app: intelligent-app

Now, we’ll deploy our application using kubectl. Applying the Kubernetes manifests creates the necessary resources and deploys our containerized application to the AKS cluster.

First, change the terminal to the deployment folder:

cd Deployment 

Then, run the following command to create or update Kubernetes resources defined in the deployment.yml file:

kubectl apply -f deployment.yml

Create a Kubernetes Service resource in a Kubernetes cluster based on the configuration defined in the service.yml file using the code below:

kubectl apply -f service.yml

Once you’ve applied the resource definition and the service configuration contained in the deployment.yml and the service.yml files, open the aks-intelligent-app Kubernetes Service in the Azure Portal, select Workloads under Kubernetes resources on the sidebar, and find the deployment named intelligent-app. It must have the status “Ready 1/1”. If you encounter an issue with this status, check out these troubleshooting resources.

image of selecting the deployment in Azure Portal

Testing the Intelligent App on AKS

To test the app on AKS, first, run the command below:

kubectl get services

This command lists the Services and their corresponding details, including the Service name, cluster IP address, external IP, and ports.

NAMETYPECLUSTER-IPEXTERNAL-IPPORT(S)AGE
intelligent-app-serviceLoadBalancer10.0.77.6020.121.76.15380:30936/TCP47s
kubernetesClusterIP10.0.0.1<none>443/TCP14m

The output above shows a Kubernetes Service named intelligent-app-service with a type set to LoadBalancer. It’s reachable from within the cluster using the cluster IP 10.0.77.60 and accessible externally via the external IP 20.121.76.153 on port 80 (mapped to port 30936).

Note: Your set of IP addresses will differ. Remember to use your unique external IP address when testing with Postman.

To test the deployed app, go to Postman, replace the URL with the external IP of the Kubernetes Service you just deployed, and click Send:

mage of sending the app in Postman

As we can see, our Intelligent App has successfully deployed to AKS and is functioning on the cloud as expected.

Exercise

Next Steps

In this two-part article, we explored the creation of an Intelligent App that leverages Azure AI Vision to analyze images and extract data. We learned how to build a Python Web API to perform OCR on uploaded images and subsequently deploy this API via Azure Kubernetes Service.

Besides OCR and Image Analysis, you can continue exploring Azure’s vast array of services and experiment further with Azure AI and AKS by applying various practical uses to your Intelligent Apps, including natural language processing, speech recognition and synthesis, sentiment analysis for customer feedback, and automated content moderation.

+ + \ No newline at end of file diff --git a/30daysofIA/tags/azure-container-apps/page/5/index.html b/30daysofIA/tags/azure-container-apps/page/5/index.html index 7005281657..d01ad0aac4 100644 --- a/30daysofIA/tags/azure-container-apps/page/5/index.html +++ b/30daysofIA/tags/azure-container-apps/page/5/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "azure-container-apps" | Build Intelligent Apps On Azure +11 posts tagged with "azure-container-apps" | Build Intelligent Apps On Azure @@ -14,13 +14,13 @@ - - + +
-

9 posts tagged with "azure-container-apps"

View All Tags

· 10 min read
It's 30DaysOfIA

Explore real-world examples of how Intelligent Apps revolutionize logistics, retail, finance, manufacturing, tech, and medicine.

What We'll Cover:

  • Real-world businesses revolutionizing operations with Intelligent Apps
  • High level architecture for industry scenarios
  • Implementation architecture overview across industries

A decorative header image for the blog

Real-world Success Stories

According to IBM’s 2022 Global AI Adoption Index, 35% of surveyed companies already use AI in their business, and 42% are exploring the idea. Organizations lagging behind the AI curve will quickly fall behind early adopters, who are already enjoying its benefits and boosting their investment in this technology.

In the first part of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” we explored how organizations can use AI-powered apps, or intelligent apps, to transform their operations, decision-making, and customer experiences, leaping ahead of the competition.

Intelligent apps significantly improve operational efficiency and customer engagement across industries like logistics, retail, finance, manufacturing, tech, and healthcare. In this article, we’ll spotlight real-world success stories of businesses revolutionizing operations with intelligent apps using Microsoft Azure’s app, data, and AI services.

info

Register for the Learn Live on September 21 for a live guided experience with SMEs on how to build end-to-end solution architecture for real world implementation of Intelligent Apps.

Streamlining Operations: Intelligent Apps in the Airline Industry

Aerospace pioneer Airbus leveraged intelligent apps to streamline its operations and innovate its services with two ground-breaking modernizations that use Azure apps, data, and AI services to reimagine pilot training and predict aircraft maintenance issues. 

Aviation technology’s rapid evolution compelled Airbus to reconceive their approach to training pilots. The aircraft manufacturer created a pilot training chatbot by combining Azure AI Services with Speech to Text and Text to Speech. The bot helps trainee pilots navigate over 6,000 pages of technical information. Pilots can ask questions verbally for quick answers, aiding their understanding of dense information and speeding up training. And when Airbus needed to accommodate its military consumers, deploying its Kubernetes cluster to a disconnected, “air-gapped” environment met the strict security requirements of military aircraft training.

A chatbot could look like the following diagram as Azure services work together to create a conversational experience. Its components could include security and governance, bot logic and user experience (UX), bot cognition and intelligence, data extract, transfer, and load (ETL) operations, quality assurance and enhancements, logging, and monitoring and reporting.

A diagram of a conversational experience enabled by Azure services

Airbus also used AI to improve its aircraft maintenance and safety. They deployed AI Anomaly Detector to gather and analyze telemetry data from multiple flights, enabling more profound insights into wear and tear on military aircraft operating in harsh conditions. This AI application helps Airbus better understand each airplane’s health. Airbus can predict and fix potential problems before they occur, improving the aircraft’s safety and operational readiness.

AI and Azure are changing how organizations like Airbus operate, even in strict, highly regulated markets with stringent security requirements. And by streamlining pilot training and staying ahead of aircraft maintenance, Airbus helps its military and commercial clients reliably deliver their goods across the globe, maintaining their critical supply chains.

Boosting Customer Service: Intelligent Apps in the Retail Industry

The retail industry has been among the first to adopt and benefit from intelligent apps. For example, the leading American used car retailer, CarMax, uses intelligent apps to transform the car shopping experience.

Before embracing AI, CarMax faced a significant challenge: Manually creating text summaries was time-consuming and labor-intensive. CarMax decided to leverage Azure OpenAI Service to automatically generate car research pages, offering customers valuable insights while enhancing the website’s search engine rankings.

This AI-driven approach has significantly streamlined the process, achieving in a few months what would have taken years manually. Also, the AI system condenses thousands of customer reviews into a few readable sentences, offering potential buyers a quick overview of others’ experiences.

CarMax has improved their customer experience while boosting operational efficiency through AI adoption. CarMax’s editorial staff focuses on creating strategic, longer-form content with the time saved, enhancing productivity, and driving more website traffic.

CarMax’s success story serves as a testament to the potential of intelligent apps in the retail industry.

Enhancing Cybersecurity: Intelligent Apps in Finance

Intelligent apps also impact the highly regulated financial services sector. Swift, a leading infrastructure provider for financial messaging services, uses intelligent apps to enhance decision-making and combat financial crime.

The massive growth of cross-border transactions and instant payment networks led to a surge in financial fraud that can cost the industry hundreds of billions of dollars every year. So, Swift and Microsoft partnered to build an anomaly detection model using federated learning techniques with Azure Machine Learning and Azure confidential computing.

Microsoft Purview helped govern data, as the federated learning method partially trained each model in a silo before aggregating them. This approach enabled Swift and Microsoft to develop a highly accurate model without copying or moving data from Swift members’ secure locations, ensuring the highest level of security and privacy.

Financial organizations can now deploy this trained ML model to run a real-time fraud detection system, lowering the cost of financial fraud.

Organizations can deploy a fraud detection system like the following example on Microsoft Azure. The system ingests data from streams or storage, runs analytics, and uses ML to detect fraud and store results. It then alerts users and other systems to the security issue.

A diagram of an example fraud detection system

Swift provides the innovative model to its banking partners, who can share data to further train the model. Azure confidential computing securely runs new models on current data to accumulate insights, generating an ongoing learning cycle. This iterative approach strengthens the global detection of fraudulent financial transactions through an increasingly faster and more accurate model. 

The collaboration between Swift and Microsoft demonstrates how combining various Azure services creates Intelligent Apps to enhance decision-making in finance. These apps solve real problems at an unprecedented global scale. 

Enhancing Efficiency: Intelligent Apps in Manufacturing

3M, known for Post-it Notes and other innovative products, wielded the power of Intelligent Apps to standardize, automate, and accelerate its sales forecasting. The company needed a unified, automated approach to replace its multiple manual methods.

It adopted Azure Machine Learning and other Azure AI Services to develop, train, and implement 1,500 custom models for each region and division, replacing manual methods. This solution delivers timely finance, sales, and marketing insights while freeing teams from time-consuming manual data manipulation tasks.

3M used Azure Machine Learning, automated machine learning (AutoML), and the Many Models Solution Accelerator to train and score numerous machine learning models in parallel. This approach significantly shortened the company’s development cycle time.

Then, 3M integrated Microsoft Power BI into their solution to compile and visualize results more effectively. Executives now have an unbiased and unfiltered view of data for better decision-making. With its improved ability to forecast sales, 3M can focus on manufacturing the products it needs efficiently.

3M’s success demonstrates how combining various Azure services can enhance efficiency in the manufacturing industry. These apps improve operational processes while enabling employees to focus on higher-level tasks. Strategic decisions are more targeted, and business conversations are more meaningful.

Driving Innovation: Intelligent Apps in the Technology Industry

As expected, the tech industry is at the forefront of leveraging Intelligent Apps to drive innovation. Elastic saw an opportunity to use intelligent apps to optimize its search services and meet customer needs more effectively. The company built a managed, secure, and scalable solution for its customers, Elastic Cloud, by leveraging Microsoft Azure.

Elastic Cloud uses Azure Machine Learning and other Azure AI Services to automate many basic management tasks, such as backing up data, upgrading, and scaling deployments. This approach enables Elastic’s customers to focus less on managing the software and more on serving their own clients. Customers can quickly deploy and manage Elastic Cloud on Kubernetes (ECK) using Azure Kubernetes Service.

As a result, Elastic’s cloud business is growing twice as fast as its on-premises solutions. Enterprise search users have reported significant improvements in system availability and employee satisfaction.

Additionally, Elastic incorporated Azure OpenAI Service into its Elasticsearch Relevance Engine (ESRE), providing more relevant answers to search queries. ESRE enables text, vector, and hybrid search, offering a more effective and accurate response to queries.

Elastic’s success story demonstrates how combining various Azure services can create intelligent Apps to drive innovation in the tech industry. These apps help improve operational processes and deliver better results quickly.

Revolutionizing Healthcare: Intelligent Apps in Medicine

Intelligent apps also drive transformative changes in the healthcare sector. Cambridgeshire and Peterborough NHS Foundation Trust, a healthcare provider in the United Kingdom, gradually shifted their on-premises systems to the cloud using Azure, improving flexibility, and lowering costs. They needed a solution for their electronic patient record (EPR) system that integrates 23 years of data across multiple systems and formats.

The Trust used Microsoft Azure and Azure Cognitive Search, including semantic search for understanding intent and contexts and cognitive capabilities for extracting insights from data, to standardize and automate data management. This approach made legacy, archive, and live data more discoverable, saving clinicians’ time. The Trust’s solution searches through millions of clinical documents in seconds, even recognizing handwritten notes without needing a separate optical character recognition (OCR) solution to extract text from images (Azure Cognitive Search automatically does it). Clinicians can now uncover previously inaccessible insights, enabling them to offer more personalized care.

The Trust also used Integration Services tools such as Azure Logic Apps to create workflows without writing code and Event Grid to connect Azure and third-party services through a publisher-subscriber model. They switched on a live feed from their new EPR system to Azure to feed real-time information into other applications. Researchers can now access anonymized data to fuel medical innovations beyond the NHS.

The healthcare provider also invested heavily in Power BI’s data visualization and analytical functions to sift through massive amounts of information. This level of data accessibility was previously impossible with their on-premises system. The new insights have significantly improved clinicians’ efficiency for better patient care.

Data accessibility and security are critical in the healthcare industry. Intelligent Apps empower organizations like the Trust to revolutionize medical care. AI capabilities helped the Trust improve its operational efficiency and user experiences, setting a new standard for healthcare in the digital age.

Summary

AI is driving transformation and real-world successes across industries. Intelligent apps are reshaping our world by streamlining operations in manufacturing and logistics, enhancing customer experience in the retail sector, improving decision-making in finance, driving innovation in tech, and revolutionizing healthcare.

As organizations adopt AI, they’re innovating in ways that were once too difficult, expensive, or outright impossible. Companies that wait to embrace intelligent apps risk the chance of falling behind. By embracing intelligent apps and leveraging the capabilities of Azure, you can harness the transformative potential of AI to remain on the cutting edge of technology.

Exercise

- - +
Skip to main content

11 posts tagged with "azure-container-apps"

View All Tags

· 10 min read
It's 30DaysOfIA

Explore how digital natives and Independent Software Vendors (ISVs) can transform their business operations and generate higher value for customers with generative AI and intelligent apps.

What We'll Cover:

  • Potential of Generative AI in application development
  • Challenges with Generative AI
  • Integrating Generative AI with business operations

image of generative AI and intelligent apps used in business

Unleashing Business Transformation: How Digital Natives Can Leverage Generative AI

Generative AI is no longer a technological flight of fancy. It’s here, and it’s rewriting the rules of business innovation. 

This type of AI leverages machine learning algorithms to create new data with similar characteristics to a given dataset. It goes beyond text generation using generative pretrained transformer (GPT) models, covering an array of applications that span from image synthesis to translations and more. It also powers a new class of intelligent applications, which combine AI capabilities with traditional software functions for enhanced user experiences and advanced business solutions. 

Generative AI is transforming how digital natives and independent software vendors (ISVs) operate. Generative AI offers new possibilities for creating unique business value, enhancing customer experiences, and driving growth. 

It plays a critical role in applications, improving functionality, aiding decision-making, and automating complex tasks to deliver dynamic, uniquely personalized user experiences. Generative AI also enables and automates data-driven outcomes, functions, and features.

Integrating AI capabilities into customer-facing apps and turning them into intelligent apps benefits both the customer and the business, as we discussed in Demystifying Intelligent Applications: Leveraging AI in App Development. You get faster product development workflows and better business intelligence for sales and marketing, while your users get more natural and context-aware features and interactions. 

That said, to realize generative AI’s potential, you need some strategy and knowledge of how and where it fits into your current business and operations. We’ll examine this technology’s potential to transform business operations, its risks, and rewards, and how to integrate it into your existing operations. 

Unpacking the Potential of Generative AI

Generative AI is creating new opportunities in the digital playing field. It empowers businesses to generate insights, design products, and automate processes in ways and at speeds that were previously unimaginable. You can now predict customer behavior more accurately, offer highly personalized user experiences, and prototype innovative ideas rapidly. 

For example, Microsoft used generative AI to reinvent web search in the new AI-powered Bing and Edge. The search experience has evolved from a solitary user flow of typing in keywords and scrolling through results. It’s now a conversational experience where Bing uses context to offer better quality and more personalized search queries and results. 

Similarly, Microsoft harnessed generative AI to transform its productivity suite into intelligent apps. Through Copilot for Microsoft 365, PowerPoint now empowers users to tell their stories by using DALL-E to generate images effortlessly. It also helps rephrase text-heavy slides to be more straightforward. Outlook now assists users to compose more effective emails, enabling better communication.

These are just some examples of how AI powers day-to-day work and operations. 

Businesses that ignore generative AI miss out on untapped market opportunities, letting competitors gain an edge in the digital race. By seeking ways to rethink assumptions and reinvent existing approaches, you can effectively use generative AI to create new opportunities, open products to new use cases and markets, unlock new growth and customer satisfaction levels, and increase your revenue.

info

Register for the intelligent apps webinar on Driving Business Value by Modernizing with Cloud-Native & AI with Microsoft and Forrester on September 26.

Explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries. 

The Crucial Role of Generative AI in Business Operations

Generative AI can be a powerful catalyst for business transformation. Let’s quickly tour some ways it can redefine business operations. 

Product development is traditionally a long and painstaking process. Now, tools like GitHub Copilot and TestPilot use generative AI to make coding and testing software easier, faster, and more robust. 

Similarly, machine learning as a service technology, such as Azure Machine Learning, democratizes the availability of machine learning. It helps collect, clean, and train models across datasets, making engineering and analyzing data more efficient and accessible.  

Generative AI also simplifies the process of creating intelligent apps. Previously, simulating thousands of prototypes, predicting their performance, and narrowing down to the most viable options could take weeks, months, or even years of iteration and data analysis. With generative AI, this process can be as quick as days, hours, or perhaps minutes. 

Customer experience is also ripe for transformation. Personalized recommendations drive engagement and sales, and generative AI can scale that personalization to every customer interaction using comprehensive and continually updated data. For instance, Microsoft leverages AI to enable personalization in Dynamics 365 Customer Insights. Using this tool, you can ask questions about your customers in natural language and gain valuable and actionable insights into your customer base. 

Lastly, marketing and sales can benefit from generative AI. For example, identifying current and future key markets and emerging trends helps companies stay ahead of the curve. Tools like Copilot in Power BI help you quickly understand and uncover insights about your business, enabling your team to craft targeted campaigns, create engaging content, and optimize your business strategy. 

Leveraging this full generative AI potential requires the right tools. GPT-4, for instance, can be instrumental in realizing effective business transformation. It excels in understanding context, generating human-like text, and learning from minimal data input, making it a formidable tool in any digital native's arsenal. Platforms like Azure OpenAI Service bring GPT-4’s power to your fingertips.

However, as with any powerful technology, it’s unwise to treat generative AI as a perfect solution. You must know the challenges and risks of not leveraging it effectively, including technical and human-factor considerations. 

Without generative AI, your company risks missing the opportunity to please customers via personalization, as discussed previously. You’ll miss sales opportunities via business intelligence insights, limiting your ability to grow revenue and keep up with the competition. 

One challenge of leveraging generative AI’s full potential is finding the right tools. Fortunately, you can implement generative AI in your organization without investing in infrastructure, computational power, and expertise. Platforms like Azure OpenAI Service and Azure AI Services make AI easily accessible. They also work with Azure compute and data services like Azure Functions and Azure Kubernetes Service (AKS), enabling you to build intelligent apps easily and quickly. 

However, generative AI can’t do everything. It may need human expertise and intervention to use it fully. 

For example, generative AI can create unique data. However, the data may have limited diversity and complexity. The AI may need help recognizing additional considerations or requirements specific to a problem. It may also lack context due to various factors, such as the information’s freshness. Also, since it’s an emerging field that’s potentially worth $4.4 trillion, we’re still learning about the nuances and potential regulatory impact of generative AI. 

Perhaps the most crucial and challenging aspect of adopting generative AI is an organization’s required cultural shift. Your teams must learn how to use AI as your company fosters an understanding of AI’s potential and limitations. 

Begin integrating AI literacy into your company’s DNA. Knowledge of its potential and the readily available tools can inspire your teams to incorporate generative AI into their daily workflows and business operations quickly, improve productivity, and deliver superior customer experiences. 

No company wants to be outpaced by its competitors. You can balance generative AI’s potential with your business operations by staying aware of and adapting to the dynamic AI landscape.

Achieving Harmony: Integrating Generative AI with Existing Business Operations

Integrating generative AI into existing business operations doesn’t have to be disruptive. With the correct strategy, you can do so seamlessly. Your goal should be to augment and enhance your operations, not disrupt and replace. 

For example, Microsoft Cloud technologies leveraged AI to relieve healthcare organizations’ challenges, such as talent shortage and workforce burnout. Providers automated their workflows, invested in specific applications and connectors, and enhanced support for data and operations. 

Azure AI Services, such as text analytics for health, enable healthcare organizations to understand their largely unstructured and untapped medical data. Project Health Insights leans on AI to help healthcare professionals gain actionable insights and inferences from patient data. 

Azure Health Bot, an Intelligent App, empowers organizations to build and deploy legally compliant, powerful conversational healthcare experiences to patients. 

A successful AI integration like this begins with understanding. Knowing what generative AI can do and setting realistic expectations can help you identify where and how AI can best serve your operations. 

Next, you can foster a culture of AI literacy across teams, speeding up adoption by helping them understand how it can aid their work. The more familiar your team is with AI and its possibilities, the smoother the transition. Training and education programs play a pivotal role here. 

Finally, integration requires a systematic approach. It’s not about overhauling everything at once — it's about identifying areas where AI can add the most value, then gradually experimenting and scaling its use. Successful integration stories often feature a phased approach, starting small and growing steadily. 

As we move toward a future where generative AI is a staple in business operations, remember that every journey is unique. Although there’s no one-size-fits-all solution, a strategic approach and a keen understanding of your business needs will help you navigate this new landscape successfully.

Conclusion

Generative AI offers plenty of opportunities for businesses that embrace it. It’s fueling the next generation of intelligent applications, transforming everything from product development to customer engagement to marketing. This technology is the key to unlocking unprecedented growth and innovation. 

While the journey may have challenges, the potential rewards make generative AI worth exploring. As generative AI advances, Azure AI’s broad range of tools and services will help you navigate this fascinating field.

Exercise

  • Watch Episode 02 of the serverless edition Learn Live session to learn how to build, test and deploy an end-to-end intelligent app solution.
  • Complete the Cloud Skills Challenge to build on your AI skills.
  • Register for Ask the Expert: Azure Functions on September 26 for a live Q&A with the Product Team on how to build AI apps using serverless computing.
+ + \ No newline at end of file diff --git a/30daysofIA/tags/azure-container-apps/page/6/index.html b/30daysofIA/tags/azure-container-apps/page/6/index.html index b421303d91..d687d9058b 100644 --- a/30daysofIA/tags/azure-container-apps/page/6/index.html +++ b/30daysofIA/tags/azure-container-apps/page/6/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "azure-container-apps" | Build Intelligent Apps On Azure +11 posts tagged with "azure-container-apps" | Build Intelligent Apps On Azure @@ -14,13 +14,13 @@ - - + +
-

9 posts tagged with "azure-container-apps"

View All Tags

· 8 min read
It's 30DaysOfIA

Let’s start with understanding the power of intelligent applications.

What We'll Cover:

  • What are intelligent applications?
  • Categories of Intelligent Apps
  • Breaking down the role of AI in intelligent apps

A decorative header image for the blog

Demystifying Intelligent Applications: Leveraging AI in App Development

Until recent years, when you heard the term “artificial intelligence” (AI), you might have pictured autonomous robots from sci-fi films. Now, perhaps your mind jumps to big data and machine learning (ML). Maybe you’re even thinking about the latest ChatGPT plugins and transformer architectures. Today, AI is more than these imaginations: it’s a reality increasingly woven into our day-to-day routines through intelligent applications.

Intelligent apps combine cloud-scale compute, data, and predictive or generative AI. These applications help you automate tasks and make data-driven decisions, aiding in leveraging business intelligence to gain meaningful, practical outcomes. 

For developers, the AI landscape isn’t an abstract complexity. It’s a real, tangible opportunity for evolving traditional applications, making them “intelligent,” and delivering more value to users. And with tools like Azure Kubernetes Service, Azure Cosmos DB, and Azure AI Services, stepping into the world of intelligent apps isn’t a steep climb but a steady trek across a manageable learning curve.

The Three Categories of Intelligent Applications

Before we start building, it’s critical to understand what makes an app “intelligent.” It’s not a universal definition so much as an identifier for apps that fall into one or more of the three primary categories: 

  • Outcome-based — This designation represents the broadest category of intelligent apps, whose principal value for users is the intelligent outcomes they provide. These apps process data to provide insightful results that enhance decision-making or help with tasks like trend prediction, resource management, and customer relationship management.

  • Functionality-based — This type of intelligent app takes AI integration a degree further, integrating AI as a function of the app to create or process its outputs, enhancing the app’s overall interactivity and usefulness.

  • Feature-based — This is the narrowest category, in which the app’s AI/ML component is one of its core features and primary selling points

Let’s dive more deeply into each category.

Outcome-Based Apps: Intelligent Outcomes Drive Success

Outcome-based applications are all about the results provided to the user. They function beyond merely processing data to offer insightful outcomes that drive success.

Think of a personal fitness tracker app that uses AI to analyze the user’s activity and provides actionable recommendations for achieving a goal. This type of app not only tracks your steps, sleep patterns, and heart rate but uses that data to provide you with personalized health advice. It helps you make informed lifestyle choices — the “intelligent” outcome.

Another example is a social media marketing app that uses AI for time-saving data analysis, task automation, and trend prediction. AI-based sentiment analysis can gauge customer perception while natural language processing (NLP) accurately interprets customer inquiries. Task automation can schedule posts and automate responses to common customer queries, and trend forecasting helps model and predict customer behavior to further personalize targeted promotional efforts. Building this type of intelligence into your apps provides cost-effective, actionable insights that guide sales and marketing strategies.

Functionality-Based Apps: AI-Driven Features Enhance User Experiences

As the name suggests, functionality-based applications integrate AI into how the app functions, but the end user drives the action and outcome. In this category of apps, AI takes a more active role in the user’s interaction by allowing them to use AI-created or processed output. These apps leverage AI technologies like Azure AI Services for NLP, image recognition, and pattern recognition to improve user experience.

Consider a music app that generates personalized playlists based on your listening habits or your smartphone’s camera app that uses AI to recognize faces and optimize photo settings. Even more advanced are the language translation apps that translate text from one language to another and can even understand and maintain the context and subtleties of the conversation. They use NLP to accomplish these feats.

Similarly, consider the latest e-commerce apps, whose AI-driven functionalities enable customers to try on clothes virtually, significantly enhancing the online shopping experience.

Feature-Based Apps: Integrating Advanced AI/ML Components

Finally, there are feature-based intelligent apps. These apps incorporate advanced AI/ML components like neural networks, internal large language models (LLMs), and sophisticated algorithms as core features for the user. In this type of application, AI is the primary feature for users to engage with and the primary selling point for the app.

Some examples of feature-based applications are chatbots and virtual agents built using Azure Bot Service. These robo-representatives use AI to provide more natural and human-like conversation, balancing superior efficiency with a friendlier, more personalized user experience. The new Bing is a prime example of this implementation, integrating AI to give users a knowledgeable copilot for web searches.

Similarly, OpenAI’s ChatGPT allows users access to its state-of-the-art language models available as Microsoft’s Azure OpenAI Service. It leverages the power of the highly sophisticated GPT-3 LLM to perform tasks like drafting emails, writing code, answering general knowledge questions, translating between languages, and tutoring in numerous subjects.

These apps don’t just use AI to provide outputs or improve user experiences: They push the boundaries of what’s possible in app development by integrating AI/ML components.

info

Register for the live Q&A Ask The Expert session on September 26 with the Azure Functions product engineering team.

Breaking Down the Role of AI in Intelligent Applications

Intelligent apps reflect the merging of traditional software design with the dynamic capabilities of AI. Let’s explore how AI integrates and amplifies these applications.

At its core, AI augments applications with the capacity to self-learn and self-improve. It allows you to create applications that predict, react, and adapt to a constantly changing environment. In the power grid management system for a major city, an AI-infused application can forecast power usage trends, react to anomalies, and adapt to sudden demand surges, ensuring optimal efficiency and preventing blackouts.

But AI’s role doesn’t end at prediction or decision-making: It also automates tasks. In enterprise solutions, it has moved beyond simple automation to encompass intelligent process automation (IPA). In this arena, AI algorithms understand, learn, and then automate entire digital workflows. For developers, this is a fertile ground for creating applications that can dramatically improve the operational efficiency of businesses.

Even more, AI substantially aids in development tasks. For example, there’s GitHub Copilot, an AI-powered code assistant that offers suggestions based not only on syntax but on development best practices and the intent of the code. AI has evolved from a tool you use to build apps to a partner that helps in creating them. 

Using AI to build intelligent apps isn’t about throwing out what’s worked before. It’s about enhancing the components that have the most impact and achieve the best results. Incorporating AI in apps elevates their predictive abilities, streamlines complex tasks, and even aids in the development and creation process. When you build intelligent apps, you don’t just address current problems. You anticipate future challenges and craft solutions that adapt over time.

Summary

As developers the power of AI is in our hands, and it’s our responsibility to harness its full potential. Each category tells the story of AI's transformative potential, from applications that deliver intelligent outcomes to those with AI capabilities to enrich the user experience and those pushing boundaries with advanced AI/ML technology.

Accessible and robust platforms like Azure AI can simplify the process of developing Intelligent Applications. Whether you’re refining an existing app or building a new one, just entering the world of AI, or already familiar with the landscape, take a moment to appreciate the potential that AI integration offers. You’re embracing this exciting frontier and shaping the future of app development. 

Exercise

  • Complete the Cloud Skills Challenge to build on your apps and AI skills.
  • Watch Episode 01 of the Serverless Edition Learn Live session to learn how to build an end-to-end intelligent app solution.
- - +
Skip to main content

11 posts tagged with "azure-container-apps"

View All Tags

· 9 min read
It's 30DaysOfIA

In the part of our series on intelligent apps, we’ll explore how AI is transforming application development, from design and architecture to building.

What We'll Cover:

  • Revolutionizing application development using AI
  • Infusing AI in application design architecture
  • AI Assisted Pair Programming
  • Future of App Development

A decorative image of application development with AI

Reimagining Application Development With AI: A New Paradigm

Artificial intelligence (AI) is impacting every industry. AI-powered development tools offer new functionalities and assist software developers throughout the development process, revolutionizing app development. From initial design and architecture to coding and debugging, AI reshapes how we approach application conception, creation, and iteration.

In part one of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” we explored the new breed of intelligent apps and how they use AI. In this article, we’ll see how AI technologies enhance developer toolkits and shape the future of app development, particularly through AI-enabled coding assistance and debugging.

The Dawn of Intelligent Apps: Revolutionizing Application Development

AI is more than just another addition to the development toolbox—it represents a revolution bringing unprecedented efficiency, innovation, and user engagement to our applications. 

What sets intelligent apps apart is how they can adapt and learn, helping us deliver unique, real-time user experiences.  

One example is an AI-driven health application that can track biometric data, predict potential health issues, provide personalized health advice, and even guide emergency services during a medical crisis. An AI-enabled language learning app that adapts to our learning speeds, recognizes our problem areas, and customizes its content to help us improve our language proficiencies is another example.  

Intelligent apps like these don’t just use data for recommendations—they can make critical decisions and provide sophisticated services that were previously impossible. 

For developers, intelligent apps mean we’re no longer just coding—we’re teaching applications how to think, adapt, and make decisions. AI is reshaping every step of the development journey, helping us to build more intuitive and user-focused products. 

Next, we’ll examine the impact of AI on application design and architecture and how AI-assisted “pair programming” changes software development.

info

Register for the FREE webinar on Intelligent Apps with Microsoft and Forrester on September 26 to explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries.

There will be a showcase of real-world use cases that demonstrate how AI can be seamlessly integrated into cloud-native environments driving tangible business value.

AI in Design and Architecture: Fueling Creativity and Efficiency

AI technologies help developers automate and streamline their processes and provide new ways to think about design.  

In traditional application development, the design and architecture phase can take significant time, effort, and expertise. For instance, adopting a monolithic architecture or a microservices-based design can significantly increase coding, testing, and debugging complexity. The app may even behave differently when hosted locally than in a deployed environment. It may take hours to track down an issue manually—time developers could spend elsewhere. 

Conversely, we can generate numerous design iterations in seconds with AI, each optimized to fulfill specific user needs and business goals. AI tools can quickly model various scenarios' impacts and software design patterns in the architectural design phase. It provides insights into performance and potential issues and suggests improvements. AI can also generate skeleton code, allowing developers to prototype different solutions or test specific components and technologies rapidly. By reviewing thousands of lines of code in seconds, AI allows us to focus on creating apps that serve users through feature development instead of searching for bugs.  

Integrating AI into the design and architecture phase can even extend our creative capabilities. By leveraging the power of generative AI—like large language models (LLMs)—and their broad knowledge and context going beyond software, we can better brainstorm apps and feature ideas. We can go beyond code and software architecture and get ideas about user experience, logo and branding, or marketing concepts. And as AI tools deliver insights and ideas we would have never considered; it encourages more innovation.

info

Watch Episode 01 of the Learn Live session to learn how to build an end-to-end intelligent app solution architecture.

The Power of AI-Assisted Pair Programming in Building Applications

AI-assisted pair programming is a collaborative coding approach where an AI system—like GitHub Copilot or TestPilot—assists developers during coding. It’s an increasingly common approach that significantly impacts developer productivity. In fact, GitHub Copilot is now behind an average of 46 percent of developers’ code and users are seeing 55 percent faster task completion on average. 

For new software developers, or those interested in learning new skills, AI-assisted pair programming are training wheels for coding. With the benefits of code snippet suggestions, developers can avoid struggling with beginner pitfalls like language syntax. Tools like ChatGPT can act as a personal, on-demand tutor—answering questions, generating code samples, and explaining complex code syntax and logic. These tools dramatically speed the learning process and help developers gain confidence in their coding abilities. 

Building applications with AI tools hastens development and provides more robust code. For example, GitHub Copilot helps us write new code accurately. It also quickly makes sense of existing code, suggests whole new lines or blocks of code, and even detects errors and proposes fixes. It’s like having a second coder working alongside us—one who never tires and is trained on billions of lines of code. 

However, it’s also important to acknowledge the limitations of AI. While generative AI-assistance technologies like GitHub Copilot are impressive, they’re still imperfect. They can sometimes produce erroneous, non-functional, or vulnerable code. They can also lack nuanced, domain-specific knowledge.  

So, although these tools are helpful, we can’t rely on them fully—these are programming aids rather than definitive solutions. AI-assisted pair programming is about augmenting the human developer’s skills, not replacing them.

Debugging and Improving Code With AI: Enhancing Application Quality

Identifying and correcting code errors is often time-consuming and challenging for developers. AI streamlines the debugging process by automatically detecting anomalies, suggesting fixes, and learning from previous bugs to prevent them from recurring. 

We can use generative AI to create unit tests from code snippets, from simple parameter checking and input validation to complex mocks of database and network services. Automated AI testing improves code coverage and robustness. It saves significant time compared to manually crafting unit tests. 

AI also revolutionizes code reviews by scanning and highlighting potential issues—like code patterns leading to a bug or a coding standards violation—and suggesting ways to simplify or refactor the code for readability and performance. This reduces human error and makes the code review process more efficient. Its benefits extend to small teams, where the availability of developers is crucial to deadlines, and large teams, where effective collaboration depends on readability and code consistency. AI further saves time by triaging issues so developers can prioritize the most mission-critical ones. 

Aside from improving code quality, AI is critical to understanding system behavior. Intelligent AI-assisted software development tools can monitor and audit system behavior, giving developers a better insight into how their code performs in different scenarios. AI can track patterns, identify inefficiencies, and provide alerts when they detect unusual behavior—enabling developers to enhance their code further. 

By predicting and identifying potential vulnerabilities, intelligent AI-assisted coding apps like Microsoft Security Copilot are pivotal in ensuring application functionality, reliability, and security. These AI tools are continually trained and upgraded with large codebases and user feedback that refines their responses. This ability to learn from past mistakes makes AI an incredibly powerful tool that will continue to expand its capabilities in the future.

info

Complete the AI Cloud Skills Challenge to build on you AI application development skills with paired programming using generative AI and GitHub Copilot.

AI and the Future of Application Development

The rapid advancements in AI and machine learning technologies point to an exciting future for application development. 

In “The Future of Applications,” software developer Mike Cann illustrates how LLMs might revolutionize application development. Cann that suggests fully-autonomous coding—AI-generated applications created entirely from prompts—may supersede traditional hand-crafted applications. Dynamically generated user interfaces could offer personalized and customizable experiences while we can design APIs for machine consumption and efficiency rather than human readability. Furthermore, we can store data in less structured, more adaptable, and flexible forms. 

AI is already making application development more accessible. With AI handling the complex, technical aspects of development, less-technical individuals can take a greater role in building applications. Consequently, AI can potentially democratize software engineering. 

Intelligent apps will continue to evolve, becoming more efficient, adaptable, and capable of learning from user interactions. Similarly, AI will inevitably become integral in every stage of app development.

Summary

AI has already created a new development paradigm—one in which machine intelligence and human creativity come together to produce innovative, efficient, and robust applications. AI already assists us at every stage of app development, from design and architecture to coding and debugging. 

Developers who embrace AI tools and integrate them into their processes can enhance their productivity, improve the quality of their applications, and expand the possibilities of their application development.  

The future of development is automated and AI-driven, and it’s here today. Getting started is easy—try an AI-powered development tool like GitHub Copilot to see how AI-assisted pair programming can enhance your productivity and code quality.

Exercise

+ + \ No newline at end of file diff --git a/30daysofIA/tags/azure-container-apps/page/7/index.html b/30daysofIA/tags/azure-container-apps/page/7/index.html index 5462115c9a..bc5a1af74d 100644 --- a/30daysofIA/tags/azure-container-apps/page/7/index.html +++ b/30daysofIA/tags/azure-container-apps/page/7/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "azure-container-apps" | Build Intelligent Apps On Azure +11 posts tagged with "azure-container-apps" | Build Intelligent Apps On Azure @@ -14,14 +14,13 @@ - - + +
-

9 posts tagged with "azure-container-apps"

View All Tags

· 4 min read
It's 30DaysOfIA

This Fall focus on building intelligent apps using AI and cloud-native technologies. #FallForIntelligentApps brings to you a learning journey to build your skills on creating differentiated experiences while modernizing your applications. It’s time to learn it all.

What We'll Cover

image

Get Ready To #FallForIntelligentApps starting September 18!

Today, we kick off the Fall season with content and activities to skill you up on all things Intelligent Apps or AI Apps on Azure with content, events, and community interactions! Read on to learn about what is coming!

Explore Our Initiatives

We have a number of initiatives planned for the month to help you learn and skill up on relevant technologies. Click on the links to visit the relevant pages for each

image

Register for the events!

What are 4 things you can do today, to jumpstart your learning journey?

#30Days Of Intelligent Apps

#30DaysOfIA is a series of daily blog posts grouped into themed weeks - taking you from core concepts to end-to-end solution examples in 30 days. Each article will provide conceptual lessons paired with exercises and resources to help you reinforce learnings and take next steps.

This series takes you through learning journey in six stages, each building on the previous week to help you skill up in a beginner-friendly way:

We will start with defining intelligent apps and then expand on how to build with cloud-native technologies like Azure Kubernetes Service, Azure Container Apps and Azure Functions, and take you through end-to-end scenarios for real world application development. Before we dive deep on intelligent apps, here is a high-level overview of the Intelligent Apps landscape on Azure:

image

Containers on Azure services offer you a wide range of capabilities, from simplicity to control to suit your different needs.

image -image

To start with the basics for developing Kubernetes applications, explore #30DaysOfCloudNative.

Cloud-native development when paired with serverless computing enhances your solution architecture for building cost optimized, resilient applications.

image

To start with the basics for serverless computing, explore #30DaysOfServerless.

Let’s Get Started

Now you know everything! We hope you are as excited as we are to dive into a full month of active learning and doing! Don't forget to subscribe for updates in your favorite feed reader! And look out for our first Intelligent Apps post Monday!

- - +
Skip to main content

11 posts tagged with "azure-container-apps"

View All Tags

· 10 min read
It's 30DaysOfIA

Explore real-world examples of how Intelligent Apps revolutionize logistics, retail, finance, manufacturing, tech, and medicine.

What We'll Cover:

  • Real-world businesses revolutionizing operations with Intelligent Apps
  • High level architecture for industry scenarios
  • Implementation architecture overview across industries

A decorative header image for the blog

Real-world Success Stories

According to IBM’s 2022 Global AI Adoption Index, 35% of surveyed companies already use AI in their business, and 42% are exploring the idea. Organizations lagging behind the AI curve will quickly fall behind early adopters, who are already enjoying its benefits and boosting their investment in this technology.

In the first part of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” we explored how organizations can use AI-powered apps, or intelligent apps, to transform their operations, decision-making, and customer experiences, leaping ahead of the competition.

Intelligent apps significantly improve operational efficiency and customer engagement across industries like logistics, retail, finance, manufacturing, tech, and healthcare. In this article, we’ll spotlight real-world success stories of businesses revolutionizing operations with intelligent apps using Microsoft Azure’s app, data, and AI services.

info

Register for the Learn Live on September 21 for a live guided experience with SMEs on how to build end-to-end solution architecture for real world implementation of Intelligent Apps.

Streamlining Operations: Intelligent Apps in the Airline Industry

Aerospace pioneer Airbus leveraged intelligent apps to streamline its operations and innovate its services with two ground-breaking modernizations that use Azure apps, data, and AI services to reimagine pilot training and predict aircraft maintenance issues. 

Aviation technology’s rapid evolution compelled Airbus to reconceive their approach to training pilots. The aircraft manufacturer created a pilot training chatbot by combining Azure AI Services with Speech to Text and Text to Speech. The bot helps trainee pilots navigate over 6,000 pages of technical information. Pilots can ask questions verbally for quick answers, aiding their understanding of dense information and speeding up training. And when Airbus needed to accommodate its military consumers, deploying its Kubernetes cluster to a disconnected, “air-gapped” environment met the strict security requirements of military aircraft training.

A chatbot could look like the following diagram as Azure services work together to create a conversational experience. Its components could include security and governance, bot logic and user experience (UX), bot cognition and intelligence, data extract, transfer, and load (ETL) operations, quality assurance and enhancements, logging, and monitoring and reporting.

A diagram of a conversational experience enabled by Azure services

Airbus also used AI to improve its aircraft maintenance and safety. They deployed AI Anomaly Detector to gather and analyze telemetry data from multiple flights, enabling more profound insights into wear and tear on military aircraft operating in harsh conditions. This AI application helps Airbus better understand each airplane’s health. Airbus can predict and fix potential problems before they occur, improving the aircraft’s safety and operational readiness.

AI and Azure are changing how organizations like Airbus operate, even in strict, highly regulated markets with stringent security requirements. And by streamlining pilot training and staying ahead of aircraft maintenance, Airbus helps its military and commercial clients reliably deliver their goods across the globe, maintaining their critical supply chains.

Boosting Customer Service: Intelligent Apps in the Retail Industry

The retail industry has been among the first to adopt and benefit from intelligent apps. For example, the leading American used car retailer, CarMax, uses intelligent apps to transform the car shopping experience.

Before embracing AI, CarMax faced a significant challenge: Manually creating text summaries was time-consuming and labor-intensive. CarMax decided to leverage Azure OpenAI Service to automatically generate car research pages, offering customers valuable insights while enhancing the website’s search engine rankings.

This AI-driven approach has significantly streamlined the process, achieving in a few months what would have taken years manually. Also, the AI system condenses thousands of customer reviews into a few readable sentences, offering potential buyers a quick overview of others’ experiences.

CarMax has improved their customer experience while boosting operational efficiency through AI adoption. CarMax’s editorial staff focuses on creating strategic, longer-form content with the time saved, enhancing productivity, and driving more website traffic.

CarMax’s success story serves as a testament to the potential of intelligent apps in the retail industry.

Enhancing Cybersecurity: Intelligent Apps in Finance

Intelligent apps also impact the highly regulated financial services sector. Swift, a leading infrastructure provider for financial messaging services, uses intelligent apps to enhance decision-making and combat financial crime.

The massive growth of cross-border transactions and instant payment networks led to a surge in financial fraud that can cost the industry hundreds of billions of dollars every year. So, Swift and Microsoft partnered to build an anomaly detection model using federated learning techniques with Azure Machine Learning and Azure confidential computing.

Microsoft Purview helped govern data, as the federated learning method partially trained each model in a silo before aggregating them. This approach enabled Swift and Microsoft to develop a highly accurate model without copying or moving data from Swift members’ secure locations, ensuring the highest level of security and privacy.

Financial organizations can now deploy this trained ML model to run a real-time fraud detection system, lowering the cost of financial fraud.

Organizations can deploy a fraud detection system like the following example on Microsoft Azure. The system ingests data from streams or storage, runs analytics, and uses ML to detect fraud and store results. It then alerts users and other systems to the security issue.

A diagram of an example fraud detection system

Swift provides the innovative model to its banking partners, who can share data to further train the model. Azure confidential computing securely runs new models on current data to accumulate insights, generating an ongoing learning cycle. This iterative approach strengthens the global detection of fraudulent financial transactions through an increasingly faster and more accurate model. 

The collaboration between Swift and Microsoft demonstrates how combining various Azure services creates Intelligent Apps to enhance decision-making in finance. These apps solve real problems at an unprecedented global scale. 

Enhancing Efficiency: Intelligent Apps in Manufacturing

3M, known for Post-it Notes and other innovative products, wielded the power of Intelligent Apps to standardize, automate, and accelerate its sales forecasting. The company needed a unified, automated approach to replace its multiple manual methods.

It adopted Azure Machine Learning and other Azure AI Services to develop, train, and implement 1,500 custom models for each region and division, replacing manual methods. This solution delivers timely finance, sales, and marketing insights while freeing teams from time-consuming manual data manipulation tasks.

3M used Azure Machine Learning, automated machine learning (AutoML), and the Many Models Solution Accelerator to train and score numerous machine learning models in parallel. This approach significantly shortened the company’s development cycle time.

Then, 3M integrated Microsoft Power BI into their solution to compile and visualize results more effectively. Executives now have an unbiased and unfiltered view of data for better decision-making. With its improved ability to forecast sales, 3M can focus on manufacturing the products it needs efficiently.

3M’s success demonstrates how combining various Azure services can enhance efficiency in the manufacturing industry. These apps improve operational processes while enabling employees to focus on higher-level tasks. Strategic decisions are more targeted, and business conversations are more meaningful.

Driving Innovation: Intelligent Apps in the Technology Industry

As expected, the tech industry is at the forefront of leveraging Intelligent Apps to drive innovation. Elastic saw an opportunity to use intelligent apps to optimize its search services and meet customer needs more effectively. The company built a managed, secure, and scalable solution for its customers, Elastic Cloud, by leveraging Microsoft Azure.

Elastic Cloud uses Azure Machine Learning and other Azure AI Services to automate many basic management tasks, such as backing up data, upgrading, and scaling deployments. This approach enables Elastic’s customers to focus less on managing the software and more on serving their own clients. Customers can quickly deploy and manage Elastic Cloud on Kubernetes (ECK) using Azure Kubernetes Service.

As a result, Elastic’s cloud business is growing twice as fast as its on-premises solutions. Enterprise search users have reported significant improvements in system availability and employee satisfaction.

Additionally, Elastic incorporated Azure OpenAI Service into its Elasticsearch Relevance Engine (ESRE), providing more relevant answers to search queries. ESRE enables text, vector, and hybrid search, offering a more effective and accurate response to queries.

Elastic’s success story demonstrates how combining various Azure services can create intelligent Apps to drive innovation in the tech industry. These apps help improve operational processes and deliver better results quickly.

Revolutionizing Healthcare: Intelligent Apps in Medicine

Intelligent apps also drive transformative changes in the healthcare sector. Cambridgeshire and Peterborough NHS Foundation Trust, a healthcare provider in the United Kingdom, gradually shifted their on-premises systems to the cloud using Azure, improving flexibility, and lowering costs. They needed a solution for their electronic patient record (EPR) system that integrates 23 years of data across multiple systems and formats.

The Trust used Microsoft Azure and Azure Cognitive Search, including semantic search for understanding intent and contexts and cognitive capabilities for extracting insights from data, to standardize and automate data management. This approach made legacy, archive, and live data more discoverable, saving clinicians’ time. The Trust’s solution searches through millions of clinical documents in seconds, even recognizing handwritten notes without needing a separate optical character recognition (OCR) solution to extract text from images (Azure Cognitive Search automatically does it). Clinicians can now uncover previously inaccessible insights, enabling them to offer more personalized care.

The Trust also used Integration Services tools such as Azure Logic Apps to create workflows without writing code and Event Grid to connect Azure and third-party services through a publisher-subscriber model. They switched on a live feed from their new EPR system to Azure to feed real-time information into other applications. Researchers can now access anonymized data to fuel medical innovations beyond the NHS.

The healthcare provider also invested heavily in Power BI’s data visualization and analytical functions to sift through massive amounts of information. This level of data accessibility was previously impossible with their on-premises system. The new insights have significantly improved clinicians’ efficiency for better patient care.

Data accessibility and security are critical in the healthcare industry. Intelligent Apps empower organizations like the Trust to revolutionize medical care. AI capabilities helped the Trust improve its operational efficiency and user experiences, setting a new standard for healthcare in the digital age.

Summary

AI is driving transformation and real-world successes across industries. Intelligent apps are reshaping our world by streamlining operations in manufacturing and logistics, enhancing customer experience in the retail sector, improving decision-making in finance, driving innovation in tech, and revolutionizing healthcare.

As organizations adopt AI, they’re innovating in ways that were once too difficult, expensive, or outright impossible. Companies that wait to embrace intelligent apps risk the chance of falling behind. By embracing intelligent apps and leveraging the capabilities of Azure, you can harness the transformative potential of AI to remain on the cutting edge of technology.

Exercise

+ + \ No newline at end of file diff --git a/30daysofIA/tags/azure-container-apps/page/8/index.html b/30daysofIA/tags/azure-container-apps/page/8/index.html index abbf869e70..eb083f3ded 100644 --- a/30daysofIA/tags/azure-container-apps/page/8/index.html +++ b/30daysofIA/tags/azure-container-apps/page/8/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "azure-container-apps" | Build Intelligent Apps On Azure +11 posts tagged with "azure-container-apps" | Build Intelligent Apps On Azure @@ -14,14 +14,13 @@ - - + +
-

9 posts tagged with "azure-container-apps"

View All Tags

· 4 min read
It's 30DaysOfIA

Continue The Learning Journey through Fall For Intelligent Apps! 🍂

What We'll Cover

Thank you! ♥️

image

It's hard to believe that JavaScript on Azure hack-together is ending! It seems like just yesterday that we launched this initiative, and yet here we are, 15 days later, with an incredible amount of learning and growth behind us. So... it's time for a wrap!

From the bottom of our hearts, we want to thank each and every one of you for your participation, engagement, and enthusiasm. It's been truly inspiring to see the passion and dedication from this strong community, and we're honored to be a part of it. ✨

Recap of The JavaScript on Azure Global Hack-Together

As we wrap up this exciting event, we wanted to take a moment to reflect on all that we've accomplished together. Over the last 15 days, we've covered a lot of ground, from the basics of contributing to Open source to the exploration of the Contoso Real Estate project from its Frontend to its Backend and future AI implementation.

Now that the hack-together is ending, we want to make sure that you have all the resources you need to continue honing your skills in the future. Whether you're looking to make your fist contribution to open source, become an open source maintainers, collaborate with others, or simply keep learning, there are plenty of resources out there to help you achieve your goals. So, let's dive in and explore all the ways you can continue to grow your JavaScript skills on Azure!

JSonAzure Hack-together Roadmap 📍:

hack-together-roadmap (2)

Recap on past Livestreams🌟:

Day 1️⃣: Opening Keynote (Hack-together Launch): Introduction to the Contoso Real Estate Open-source project!, managing complex and complex enterprise architecture, new announcements for JavaScript developers on Azure

Day 2️⃣: GitHub Copilot & Codespaces: Introduction to your AI pair programmer (GitHub Copilot) and your virtual developer environment on the cloud (GitHub Codespaces)

Day 6️⃣: Build your Frontend using Static Web Apps as part of a complex, modern composable frontends (or micro-frontends) and cloud-native applications.

Day 9️⃣: Build a Serverless Backend using Azure Functions

Day 1️⃣3️⃣: Easily connect to an Azure Cosmos DB, exploring its benefits and how to get started

Day 1️⃣5️⃣: Being in the AI Era, we dive into the Azure OpenAI Service and how you can start to build intelligent JavaScript applications

📖 Self-Learning Resources

  1. JavaScript on Azure Global Hack Together Module collection
  2. Lets #HackTogether: Javascript On Azure Keynote
  3. Step by Step Guide: Migrating v3 to v4 programming model for Azure Functions for Node.Js Application

Continue your journey with #FallForIntelligentApps

Join us this Fall on a learning journey to explore building intelligent apps. Combine the power of AI, cloud-native app development, and cloud-scale data to build highly differentiated digital experiences. Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure. Engage in a self-paced learning adventure by following along the #30Days of Intelligent Apps series, completing the Intelligent Apps Skills Challenge, joining the Product Group for a live Ask The Expert series or building an end to end solution architecture with a live guided experience through the Learn Live series. Discover more here.

Hands-on practice: Make your first contribution to open source!

Join our GitHUb Discussion Forum to connect with developers from every part of world, see contributions from other, find collaborators and make your first contribution to a real-world project! -Don't forget to give the repo a star ⭐

Resources

All resources are accessible on our landing page

- - +
Skip to main content

11 posts tagged with "azure-container-apps"

View All Tags

· 8 min read
It's 30DaysOfIA

Let’s start with understanding the power of intelligent applications.

What We'll Cover:

  • What are intelligent applications?
  • Categories of Intelligent Apps
  • Breaking down the role of AI in intelligent apps

A decorative header image for the blog

Demystifying Intelligent Applications: Leveraging AI in App Development

Until recent years, when you heard the term “artificial intelligence” (AI), you might have pictured autonomous robots from sci-fi films. Now, perhaps your mind jumps to big data and machine learning (ML). Maybe you’re even thinking about the latest ChatGPT plugins and transformer architectures. Today, AI is more than these imaginations: it’s a reality increasingly woven into our day-to-day routines through intelligent applications.

Intelligent apps combine cloud-scale compute, data, and predictive or generative AI. These applications help you automate tasks and make data-driven decisions, aiding in leveraging business intelligence to gain meaningful, practical outcomes. 

For developers, the AI landscape isn’t an abstract complexity. It’s a real, tangible opportunity for evolving traditional applications, making them “intelligent,” and delivering more value to users. And with tools like Azure Kubernetes Service, Azure Cosmos DB, and Azure AI Services, stepping into the world of intelligent apps isn’t a steep climb but a steady trek across a manageable learning curve.

The Three Categories of Intelligent Applications

Before we start building, it’s critical to understand what makes an app “intelligent.” It’s not a universal definition so much as an identifier for apps that fall into one or more of the three primary categories: 

  • Outcome-based — This designation represents the broadest category of intelligent apps, whose principal value for users is the intelligent outcomes they provide. These apps process data to provide insightful results that enhance decision-making or help with tasks like trend prediction, resource management, and customer relationship management.

  • Functionality-based — This type of intelligent app takes AI integration a degree further, integrating AI as a function of the app to create or process its outputs, enhancing the app’s overall interactivity and usefulness.

  • Feature-based — This is the narrowest category, in which the app’s AI/ML component is one of its core features and primary selling points

Let’s dive more deeply into each category.

Outcome-Based Apps: Intelligent Outcomes Drive Success

Outcome-based applications are all about the results provided to the user. They function beyond merely processing data to offer insightful outcomes that drive success.

Think of a personal fitness tracker app that uses AI to analyze the user’s activity and provides actionable recommendations for achieving a goal. This type of app not only tracks your steps, sleep patterns, and heart rate but uses that data to provide you with personalized health advice. It helps you make informed lifestyle choices — the “intelligent” outcome.

Another example is a social media marketing app that uses AI for time-saving data analysis, task automation, and trend prediction. AI-based sentiment analysis can gauge customer perception while natural language processing (NLP) accurately interprets customer inquiries. Task automation can schedule posts and automate responses to common customer queries, and trend forecasting helps model and predict customer behavior to further personalize targeted promotional efforts. Building this type of intelligence into your apps provides cost-effective, actionable insights that guide sales and marketing strategies.

Functionality-Based Apps: AI-Driven Features Enhance User Experiences

As the name suggests, functionality-based applications integrate AI into how the app functions, but the end user drives the action and outcome. In this category of apps, AI takes a more active role in the user’s interaction by allowing them to use AI-created or processed output. These apps leverage AI technologies like Azure AI Services for NLP, image recognition, and pattern recognition to improve user experience.

Consider a music app that generates personalized playlists based on your listening habits or your smartphone’s camera app that uses AI to recognize faces and optimize photo settings. Even more advanced are the language translation apps that translate text from one language to another and can even understand and maintain the context and subtleties of the conversation. They use NLP to accomplish these feats.

Similarly, consider the latest e-commerce apps, whose AI-driven functionalities enable customers to try on clothes virtually, significantly enhancing the online shopping experience.

Feature-Based Apps: Integrating Advanced AI/ML Components

Finally, there are feature-based intelligent apps. These apps incorporate advanced AI/ML components like neural networks, internal large language models (LLMs), and sophisticated algorithms as core features for the user. In this type of application, AI is the primary feature for users to engage with and the primary selling point for the app.

Some examples of feature-based applications are chatbots and virtual agents built using Azure Bot Service. These robo-representatives use AI to provide more natural and human-like conversation, balancing superior efficiency with a friendlier, more personalized user experience. The new Bing is a prime example of this implementation, integrating AI to give users a knowledgeable copilot for web searches.

Similarly, OpenAI’s ChatGPT allows users access to its state-of-the-art language models available as Microsoft’s Azure OpenAI Service. It leverages the power of the highly sophisticated GPT-3 LLM to perform tasks like drafting emails, writing code, answering general knowledge questions, translating between languages, and tutoring in numerous subjects.

These apps don’t just use AI to provide outputs or improve user experiences: They push the boundaries of what’s possible in app development by integrating AI/ML components.

info

Register for the live Q&A Ask The Expert session on September 26 with the Azure Functions product engineering team.

Breaking Down the Role of AI in Intelligent Applications

Intelligent apps reflect the merging of traditional software design with the dynamic capabilities of AI. Let’s explore how AI integrates and amplifies these applications.

At its core, AI augments applications with the capacity to self-learn and self-improve. It allows you to create applications that predict, react, and adapt to a constantly changing environment. In the power grid management system for a major city, an AI-infused application can forecast power usage trends, react to anomalies, and adapt to sudden demand surges, ensuring optimal efficiency and preventing blackouts.

But AI’s role doesn’t end at prediction or decision-making: It also automates tasks. In enterprise solutions, it has moved beyond simple automation to encompass intelligent process automation (IPA). In this arena, AI algorithms understand, learn, and then automate entire digital workflows. For developers, this is a fertile ground for creating applications that can dramatically improve the operational efficiency of businesses.

Even more, AI substantially aids in development tasks. For example, there’s GitHub Copilot, an AI-powered code assistant that offers suggestions based not only on syntax but on development best practices and the intent of the code. AI has evolved from a tool you use to build apps to a partner that helps in creating them. 

Using AI to build intelligent apps isn’t about throwing out what’s worked before. It’s about enhancing the components that have the most impact and achieve the best results. Incorporating AI in apps elevates their predictive abilities, streamlines complex tasks, and even aids in the development and creation process. When you build intelligent apps, you don’t just address current problems. You anticipate future challenges and craft solutions that adapt over time.

Summary

As developers the power of AI is in our hands, and it’s our responsibility to harness its full potential. Each category tells the story of AI's transformative potential, from applications that deliver intelligent outcomes to those with AI capabilities to enrich the user experience and those pushing boundaries with advanced AI/ML technology.

Accessible and robust platforms like Azure AI can simplify the process of developing Intelligent Applications. Whether you’re refining an existing app or building a new one, just entering the world of AI, or already familiar with the landscape, take a moment to appreciate the potential that AI integration offers. You’re embracing this exciting frontier and shaping the future of app development. 

Exercise

  • Complete the Cloud Skills Challenge to build on your apps and AI skills.
  • Watch Episode 01 of the Serverless Edition Learn Live session to learn how to build an end-to-end intelligent app solution.
+ + \ No newline at end of file diff --git a/30daysofIA/tags/azure-container-apps/page/9/index.html b/30daysofIA/tags/azure-container-apps/page/9/index.html index f81fec6a02..cbaaa05c4b 100644 --- a/30daysofIA/tags/azure-container-apps/page/9/index.html +++ b/30daysofIA/tags/azure-container-apps/page/9/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "azure-container-apps" | Build Intelligent Apps On Azure +11 posts tagged with "azure-container-apps" | Build Intelligent Apps On Azure @@ -14,13 +14,14 @@ - - + +
-

9 posts tagged with "azure-container-apps"

View All Tags

· 2 min read
It's 30DaysOfIA

September is almost here - and that can only mean one thing!! It's time to 🍂 Fall for something new and exciting and spend a few weeks skilling up on relevant tools, techologies and solutions!!

Last year, we focused on #ServerlessSeptember. This year, we're building on that theme with the addition of cloud-scale Data, cloud-native Technologies and cloud-based AI integrations to help you modernize and build intelligent apps for the enterprise!

Watch this space - and join us in September to learn more!

- - +
Skip to main content

11 posts tagged with "azure-container-apps"

View All Tags

· 4 min read
It's 30DaysOfIA

This Fall focus on building intelligent apps using AI and cloud-native technologies. #FallForIntelligentApps brings to you a learning journey to build your skills on creating differentiated experiences while modernizing your applications. It’s time to learn it all.

What We'll Cover

image

Get Ready To #FallForIntelligentApps starting September 18!

Today, we kick off the Fall season with content and activities to skill you up on all things Intelligent Apps or AI Apps on Azure with content, events, and community interactions! Read on to learn about what is coming!

Explore Our Initiatives

We have a number of initiatives planned for the month to help you learn and skill up on relevant technologies. Click on the links to visit the relevant pages for each

image

Register for the events!

What are 4 things you can do today, to jumpstart your learning journey?

#30Days Of Intelligent Apps

#30DaysOfIA is a series of daily blog posts grouped into themed weeks - taking you from core concepts to end-to-end solution examples in 30 days. Each article will provide conceptual lessons paired with exercises and resources to help you reinforce learnings and take next steps.

This series takes you through learning journey in six stages, each building on the previous week to help you skill up in a beginner-friendly way:

We will start with defining intelligent apps and then expand on how to build with cloud-native technologies like Azure Kubernetes Service, Azure Container Apps and Azure Functions, and take you through end-to-end scenarios for real world application development. Before we dive deep on intelligent apps, here is a high-level overview of the Intelligent Apps landscape on Azure:

image

Containers on Azure services offer you a wide range of capabilities, from simplicity to control to suit your different needs.

image +image

To start with the basics for developing Kubernetes applications, explore #30DaysOfCloudNative.

Cloud-native development when paired with serverless computing enhances your solution architecture for building cost optimized, resilient applications.

image

To start with the basics for serverless computing, explore #30DaysOfServerless.

Let’s Get Started

Now you know everything! We hope you are as excited as we are to dive into a full month of active learning and doing! Don't forget to subscribe for updates in your favorite feed reader! And look out for our first Intelligent Apps post Monday!

+ + \ No newline at end of file diff --git a/30daysofIA/tags/azure-cosmos-db/index.html b/30daysofIA/tags/azure-cosmos-db/index.html index 18296f75b3..7a97ec5a2d 100644 --- a/30daysofIA/tags/azure-cosmos-db/index.html +++ b/30daysofIA/tags/azure-cosmos-db/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "azure-cosmos-db" | Build Intelligent Apps On Azure +11 posts tagged with "azure-cosmos-db" | Build Intelligent Apps On Azure @@ -14,13 +14,13 @@ - - + +
-

9 posts tagged with "azure-cosmos-db"

View All Tags

· 10 min read
It's 30DaysOfIA

Prepare your organization to adopt intelligent apps by taking a change management approach. Assess your readiness, upskill teams, create a culture of change and overcome resistance, and prepare for future evolution to pave the way for a smooth transition into an AI-centric company.

What We'll Cover:

  • Role of change management in modernizing to intelligent apps
  • Skills needed for building intelligent apps in your organization
  • How to build a culture of change to embrace AI
  • How to prepare for the future of intelligent apps and continuous evolution

image of modernizing AI solutions for intelligent apps

Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management

As we’ve discussed throughout this series, intelligent apps and generative AI are rewriting the rules of business and application development. The first article of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” explored how intelligent apps can enhance user experiences and drive businesses growth. These advancements are opening new opportunities and markets for companies ready to embrace them.

However, embracing this digital transformation is more complex than flipping a switch. It requires adjustments to the technical side, which we explored in the series’ fifth article, “Preparing the Path for Intelligent Apps: Transitioning from On-Premises/IaaS to Cloud Native Applications.” It also demands a shift in how organizations adapt to change and approach app design and development. Let’s explore that mindset transition with tips to help you make it happen.

The Role of Change Management in Transitioning to Intelligent Apps

Transitioning to intelligent apps isn’t merely a technological change. It’s a profound shift in how businesses perceive and operate. This move toward artificial intelligence (AI)-centric approach isn’t meant to replace humans with machines or radically overhaul all business operations.

Instead, intelligent apps should augment human capabilities, streamline processes, and foster an environment where innovation flourishes. They expand toolkits and techniques to include AI-assisted development tools like GitHub Copilot and AI-enabled workflows, helping us design and deliver AI-enhanced experiences.

Effective change management strategies are crucial in helping employees adapt to these new ways of working with intelligent apps, ensuring that the transition doesn’t disrupt productivity or morale.

Team and organization strategies should: 

  • Assess the impact of any proposed changes.  
  • Communicate the benefits and reasons behind the change to all affected team members.  
  • Provide the necessary training and support.  
  • Integrate the changes into the culture and daily operations. 

By incorporating this robust change management approach, you can identify and mitigate potential transition risks early. Moreover, this strategy ensures that employees are engaged throughout the process, fostering a sense of involvement and ownership. This, in turn, cultivates a culture suited for innovation through intelligent apps. 

These organizational changes focus on the following aspects: organizational readiness, skillset, culture of change and overcoming resistance, and adaptability. Let’s explore them below.

info

Register for the intelligent apps webinar on Driving Business Value by Modernizing with Cloud-Native & AI with Microsoft and Forrester on September 26. 

Explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries. 

Identifying Organizational Readiness for Intelligent Apps

The first step in preparing your organization for intelligent apps is to assess your current readiness for change. Analyze various elements of your organization, from infrastructure and workforce skills to strategic vision and overall culture.

Can your apps and systems integrate with AI platforms like Microsoft Azure AI? Is your workforce comfortable working alongside AI, or are they fearful and resistant? Does your strategic vision incorporate AI’s transformative potential, or does it fail to acknowledge AI as a critical driver of future growth?

These questions identify the challenges of integrating AI into an organization and determine the company’s readiness for intelligent apps.

Embracing intelligent apps involves more than being ready with your technological infrastructure or having the right technology or tools. You need flexible organizational structures and processes driving your business to accommodate new ideas and capabilities. Your teams must feel empowered to own and drive them. Much like Microsoft’s shift to AI-powered services—like AI-powered Bing and Edge and Microsoft 365 Copilot—embracing AI organization-wide requires a holistic vision and willingness to innovate, with teams capable of delivering on the plan.

After accessing these critical areas for organizational readiness, you can start planning a strategy for this transition to an AI-ready organization. Identify skill gaps and work to bridge them while nurturing an environment that appreciates and harnesses the potential of AI and intelligent apps.

Nurturing the Indispensable Skills for Intelligent Apps

Tasking your AI specialists with preparing your organization to use and build intelligent apps isn’t enough. Accomplishing this goal requires a cross-functional effort and a diverse skill set.

Developing intelligent apps involves data scientists managing and interpreting vast amounts of data, machine learning engineers building and training the models, and traditional developers integrating these models into usable applications. However, the impact of generative AI spans beyond creating apps. AI fluency across the entire organization—from sales and marketing to accounting and legal departments—offers substantial benefits, sparking new ideas and solutions from sometimes unexpected sources.

Teams' AI skills might roughly fall into three levels: 

  • Fundamental skills — Being familiar and efficient with using AI tools, for example, knowing how to write effective prompts for large language models (LLMs).
  • Visionary skills — Recognizing AI’s strengths and weaknesses, for example, its creativity and hallucinations, that is, unexpected output.
  • Expert skills — Managing data and training models and integrating AI technology into apps, such as incorporating the OpenAI application programming interface (API) into an Intelligent App.

Based on these levels, you can gauge your team's skill readiness to pinpoint the most significant gaps, whether in general AI knowledge across your organization or within technical development teams.

As intelligent apps become increasingly prevalent, upskilling your workforce is crucial. This transition opens a new realm of skills and capabilities for your team to master. You can offer machine learning, data analysis, and cloud computing training programs—such as Microsoft’s Azure AI Fundamentals certification—to empower your team to confidently navigate the intelligent apps landscape.

Nurturing these skills within your organization may require hiring new talent, reskilling current employees, and creating an environment that promotes learning and experimentation. Specifically, if your team understands tools like the Microsoft Azure AI platform, they could contribute significantly to developing and maintaining intelligent apps.

An upskilled team is more than a functional unit. It’s a think tank to drive innovation and improve your organization’s competitive standing in the digital marketplace.

Building a Culture of Change to Embrace AI

Even with the right people and skills in place, change remains an uphill battle for organizations committed to traditional processes. 

Transitioning to intelligent apps is as much about mindset as technology and skills. It requires fostering a culture open to change and innovation that understands AI’s transformative potential and embraces it to enhance rather than replace human capabilities. 

Leaders play a pivotal role in cultivating this mindset. As they model a positive attitude toward AI, emphasize its benefits, and address any concerns transparently, leaders significantly contribute to the organization’s readiness to adopt intelligent apps. 

The most critical factor in this transition is enabling an AI-friendly culture. It’s about encouraging curiosity, rewarding innovative thinking, and creating an environment where people thrive on learning and experimentation. 

Empowering teams to leverage practical AI tools and AI-driven data insights to aid decision-making helps motivate employees to use and explore AI’s potential, improve their skills, and contribute to the successful integration of intelligent apps. Such an environment is fertile ground to grow breakthrough ideas and innovative solutions. 

Overcoming Resistance to Change and Adopting Intelligent Apps

Despite AI’s potential, change isn’t easy. It takes effort and is especially challenging for organizations used to existing processes. This resistance to change is a significant roadblock when transitioning to intelligent apps. 

Overcoming this resistance involves:  

  • Understanding employees’ concerns. 
  • Challenging underlying assumptions about traditional systems and outdated processes that hinder change. 
  • Providing comprehensive training for workers to use AI effectively.
  • Demonstrating the benefits and opportunities of intelligent apps. 
  • Clear and continuous communication to dispel fears and misconceptions about AI.

These steps will foster greater acceptance and enthusiasm for intelligent apps in your teams. 

Preparing for the Future of Intelligent Apps and Continuous Evolution

Finally, it’s essential to understand that generative AI is just beginning to gain momentum. Adjusting to a world of intelligent apps isn’t a one-time change—it’s a continuous evolution. 

As AI technologies mature and evolve and more businesses realize their benefits, we can expect the landscape of intelligent apps to expand further. Organizations can cultivate a mindset of continuous learning, flexibility, and adaptation to prepare for this future. Staying ahead in this dynamic field requires organizations to keep up with the latest AI advancements, continually reassess their strategies, and pivot when necessary. 

The domain of intelligent apps is rapidly advancing. Organizations must prepare to navigate this dynamic landscape to stay ahead of the curve.

Summary

Preparing for AI and intelligent apps is a strategic decision. It requires thoughtful preparation and a culture that embraces change.

With the proper preparation and mindset, adopting AI can become a smoother, collaborative path to innovation for the whole organization. Integrating AI into your organization does not discard what has worked in the past. It simply enhances and augments those processes for a better future.

Consider the points above to assess your organization’s readiness and prepare to welcome intelligent apps.

In the first week of this series, you learned about intelligent apps, how organizations today find success through intelligent apps, what AI-assisted application development looks like, ways to leverage the power of generative AI, the technical changes needed to prepare for intelligent apps, and, in this article, the organizational changes required to harness the power of AI.

The future is intelligent. Use this series as a guideline to navigate challenges while preparing for and embracing AI's opportunities for an intelligent tomorrow.

Exercise

- - +
Skip to main content

11 posts tagged with "azure-cosmos-db"

View All Tags

· 10 min read
It's 30DaysOfIA

Prepare your organization to adopt intelligent apps by taking a change management approach. Assess your readiness, upskill teams, create a culture of change and overcome resistance, and prepare for future evolution to pave the way for a smooth transition into an AI-centric company.

What We'll Cover:

  • Role of change management in modernizing to intelligent apps
  • Skills needed for building intelligent apps in your organization
  • How to build a culture of change to embrace AI
  • How to prepare for the future of intelligent apps and continuous evolution

image of modernizing AI solutions for intelligent apps

Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management

As we’ve discussed throughout this series, intelligent apps and generative AI are rewriting the rules of business and application development. The first article of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” explored how intelligent apps can enhance user experiences and drive businesses growth. These advancements are opening new opportunities and markets for companies ready to embrace them.

However, embracing this digital transformation is more complex than flipping a switch. It requires adjustments to the technical side, which we explored in the series’ fifth article, “Preparing the Path for Intelligent Apps: Transitioning from On-Premises/IaaS to Cloud Native Applications.” It also demands a shift in how organizations adapt to change and approach app design and development. Let’s explore that mindset transition with tips to help you make it happen.

The Role of Change Management in Transitioning to Intelligent Apps

Transitioning to intelligent apps isn’t merely a technological change. It’s a profound shift in how businesses perceive and operate. This move toward artificial intelligence (AI)-centric approach isn’t meant to replace humans with machines or radically overhaul all business operations.

Instead, intelligent apps should augment human capabilities, streamline processes, and foster an environment where innovation flourishes. They expand toolkits and techniques to include AI-assisted development tools like GitHub Copilot and AI-enabled workflows, helping us design and deliver AI-enhanced experiences.

Effective change management strategies are crucial in helping employees adapt to these new ways of working with intelligent apps, ensuring that the transition doesn’t disrupt productivity or morale.

Team and organization strategies should: 

  • Assess the impact of any proposed changes.  
  • Communicate the benefits and reasons behind the change to all affected team members.  
  • Provide the necessary training and support.  
  • Integrate the changes into the culture and daily operations. 

By incorporating this robust change management approach, you can identify and mitigate potential transition risks early. Moreover, this strategy ensures that employees are engaged throughout the process, fostering a sense of involvement and ownership. This, in turn, cultivates a culture suited for innovation through intelligent apps. 

These organizational changes focus on the following aspects: organizational readiness, skillset, culture of change and overcoming resistance, and adaptability. Let’s explore them below.

info

Register for the intelligent apps webinar on Driving Business Value by Modernizing with Cloud-Native & AI with Microsoft and Forrester on September 26. 

Explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries. 

Identifying Organizational Readiness for Intelligent Apps

The first step in preparing your organization for intelligent apps is to assess your current readiness for change. Analyze various elements of your organization, from infrastructure and workforce skills to strategic vision and overall culture.

Can your apps and systems integrate with AI platforms like Microsoft Azure AI? Is your workforce comfortable working alongside AI, or are they fearful and resistant? Does your strategic vision incorporate AI’s transformative potential, or does it fail to acknowledge AI as a critical driver of future growth?

These questions identify the challenges of integrating AI into an organization and determine the company’s readiness for intelligent apps.

Embracing intelligent apps involves more than being ready with your technological infrastructure or having the right technology or tools. You need flexible organizational structures and processes driving your business to accommodate new ideas and capabilities. Your teams must feel empowered to own and drive them. Much like Microsoft’s shift to AI-powered services—like AI-powered Bing and Edge and Microsoft 365 Copilot—embracing AI organization-wide requires a holistic vision and willingness to innovate, with teams capable of delivering on the plan.

After accessing these critical areas for organizational readiness, you can start planning a strategy for this transition to an AI-ready organization. Identify skill gaps and work to bridge them while nurturing an environment that appreciates and harnesses the potential of AI and intelligent apps.

Nurturing the Indispensable Skills for Intelligent Apps

Tasking your AI specialists with preparing your organization to use and build intelligent apps isn’t enough. Accomplishing this goal requires a cross-functional effort and a diverse skill set.

Developing intelligent apps involves data scientists managing and interpreting vast amounts of data, machine learning engineers building and training the models, and traditional developers integrating these models into usable applications. However, the impact of generative AI spans beyond creating apps. AI fluency across the entire organization—from sales and marketing to accounting and legal departments—offers substantial benefits, sparking new ideas and solutions from sometimes unexpected sources.

Teams' AI skills might roughly fall into three levels: 

  • Fundamental skills — Being familiar and efficient with using AI tools, for example, knowing how to write effective prompts for large language models (LLMs).
  • Visionary skills — Recognizing AI’s strengths and weaknesses, for example, its creativity and hallucinations, that is, unexpected output.
  • Expert skills — Managing data and training models and integrating AI technology into apps, such as incorporating the OpenAI application programming interface (API) into an Intelligent App.

Based on these levels, you can gauge your team's skill readiness to pinpoint the most significant gaps, whether in general AI knowledge across your organization or within technical development teams.

As intelligent apps become increasingly prevalent, upskilling your workforce is crucial. This transition opens a new realm of skills and capabilities for your team to master. You can offer machine learning, data analysis, and cloud computing training programs—such as Microsoft’s Azure AI Fundamentals certification—to empower your team to confidently navigate the intelligent apps landscape.

Nurturing these skills within your organization may require hiring new talent, reskilling current employees, and creating an environment that promotes learning and experimentation. Specifically, if your team understands tools like the Microsoft Azure AI platform, they could contribute significantly to developing and maintaining intelligent apps.

An upskilled team is more than a functional unit. It’s a think tank to drive innovation and improve your organization’s competitive standing in the digital marketplace.

Building a Culture of Change to Embrace AI

Even with the right people and skills in place, change remains an uphill battle for organizations committed to traditional processes. 

Transitioning to intelligent apps is as much about mindset as technology and skills. It requires fostering a culture open to change and innovation that understands AI’s transformative potential and embraces it to enhance rather than replace human capabilities. 

Leaders play a pivotal role in cultivating this mindset. As they model a positive attitude toward AI, emphasize its benefits, and address any concerns transparently, leaders significantly contribute to the organization’s readiness to adopt intelligent apps. 

The most critical factor in this transition is enabling an AI-friendly culture. It’s about encouraging curiosity, rewarding innovative thinking, and creating an environment where people thrive on learning and experimentation. 

Empowering teams to leverage practical AI tools and AI-driven data insights to aid decision-making helps motivate employees to use and explore AI’s potential, improve their skills, and contribute to the successful integration of intelligent apps. Such an environment is fertile ground to grow breakthrough ideas and innovative solutions. 

Overcoming Resistance to Change and Adopting Intelligent Apps

Despite AI’s potential, change isn’t easy. It takes effort and is especially challenging for organizations used to existing processes. This resistance to change is a significant roadblock when transitioning to intelligent apps. 

Overcoming this resistance involves:  

  • Understanding employees’ concerns. 
  • Challenging underlying assumptions about traditional systems and outdated processes that hinder change. 
  • Providing comprehensive training for workers to use AI effectively.
  • Demonstrating the benefits and opportunities of intelligent apps. 
  • Clear and continuous communication to dispel fears and misconceptions about AI.

These steps will foster greater acceptance and enthusiasm for intelligent apps in your teams. 

Preparing for the Future of Intelligent Apps and Continuous Evolution

Finally, it’s essential to understand that generative AI is just beginning to gain momentum. Adjusting to a world of intelligent apps isn’t a one-time change—it’s a continuous evolution. 

As AI technologies mature and evolve and more businesses realize their benefits, we can expect the landscape of intelligent apps to expand further. Organizations can cultivate a mindset of continuous learning, flexibility, and adaptation to prepare for this future. Staying ahead in this dynamic field requires organizations to keep up with the latest AI advancements, continually reassess their strategies, and pivot when necessary. 

The domain of intelligent apps is rapidly advancing. Organizations must prepare to navigate this dynamic landscape to stay ahead of the curve.

Summary

Preparing for AI and intelligent apps is a strategic decision. It requires thoughtful preparation and a culture that embraces change.

With the proper preparation and mindset, adopting AI can become a smoother, collaborative path to innovation for the whole organization. Integrating AI into your organization does not discard what has worked in the past. It simply enhances and augments those processes for a better future.

Consider the points above to assess your organization’s readiness and prepare to welcome intelligent apps.

In the first week of this series, you learned about intelligent apps, how organizations today find success through intelligent apps, what AI-assisted application development looks like, ways to leverage the power of generative AI, the technical changes needed to prepare for intelligent apps, and, in this article, the organizational changes required to harness the power of AI.

The future is intelligent. Use this series as a guideline to navigate challenges while preparing for and embracing AI's opportunities for an intelligent tomorrow.

Exercise

+ + \ No newline at end of file diff --git a/30daysofIA/tags/azure-cosmos-db/page/10/index.html b/30daysofIA/tags/azure-cosmos-db/page/10/index.html new file mode 100644 index 0000000000..1cd859a2e3 --- /dev/null +++ b/30daysofIA/tags/azure-cosmos-db/page/10/index.html @@ -0,0 +1,27 @@ + + + + + +11 posts tagged with "azure-cosmos-db" | Build Intelligent Apps On Azure + + + + + + + + + + + + + + +
+

11 posts tagged with "azure-cosmos-db"

View All Tags

· 4 min read
It's 30DaysOfIA

Continue The Learning Journey through Fall For Intelligent Apps! 🍂

What We'll Cover

Thank you! ♥️

image

It's hard to believe that JavaScript on Azure hack-together is ending! It seems like just yesterday that we launched this initiative, and yet here we are, 15 days later, with an incredible amount of learning and growth behind us. So... it's time for a wrap!

From the bottom of our hearts, we want to thank each and every one of you for your participation, engagement, and enthusiasm. It's been truly inspiring to see the passion and dedication from this strong community, and we're honored to be a part of it. ✨

Recap of The JavaScript on Azure Global Hack-Together

As we wrap up this exciting event, we wanted to take a moment to reflect on all that we've accomplished together. Over the last 15 days, we've covered a lot of ground, from the basics of contributing to Open source to the exploration of the Contoso Real Estate project from its Frontend to its Backend and future AI implementation.

Now that the hack-together is ending, we want to make sure that you have all the resources you need to continue honing your skills in the future. Whether you're looking to make your fist contribution to open source, become an open source maintainers, collaborate with others, or simply keep learning, there are plenty of resources out there to help you achieve your goals. So, let's dive in and explore all the ways you can continue to grow your JavaScript skills on Azure!

JSonAzure Hack-together Roadmap 📍:

hack-together-roadmap (2)

Recap on past Livestreams🌟:

Day 1️⃣: Opening Keynote (Hack-together Launch): Introduction to the Contoso Real Estate Open-source project!, managing complex and complex enterprise architecture, new announcements for JavaScript developers on Azure

Day 2️⃣: GitHub Copilot & Codespaces: Introduction to your AI pair programmer (GitHub Copilot) and your virtual developer environment on the cloud (GitHub Codespaces)

Day 6️⃣: Build your Frontend using Static Web Apps as part of a complex, modern composable frontends (or micro-frontends) and cloud-native applications.

Day 9️⃣: Build a Serverless Backend using Azure Functions

Day 1️⃣3️⃣: Easily connect to an Azure Cosmos DB, exploring its benefits and how to get started

Day 1️⃣5️⃣: Being in the AI Era, we dive into the Azure OpenAI Service and how you can start to build intelligent JavaScript applications

📖 Self-Learning Resources

  1. JavaScript on Azure Global Hack Together Module collection
  2. Lets #HackTogether: Javascript On Azure Keynote
  3. Step by Step Guide: Migrating v3 to v4 programming model for Azure Functions for Node.Js Application

Continue your journey with #FallForIntelligentApps

Join us this Fall on a learning journey to explore building intelligent apps. Combine the power of AI, cloud-native app development, and cloud-scale data to build highly differentiated digital experiences. Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure. Engage in a self-paced learning adventure by following along the #30Days of Intelligent Apps series, completing the Intelligent Apps Skills Challenge, joining the Product Group for a live Ask The Expert series or building an end to end solution architecture with a live guided experience through the Learn Live series. Discover more here.

Hands-on practice: Make your first contribution to open source!

Join our GitHUb Discussion Forum to connect with developers from every part of world, see contributions from other, find collaborators and make your first contribution to a real-world project! +Don't forget to give the repo a star ⭐

Resources

All resources are accessible on our landing page

+ + + + \ No newline at end of file diff --git a/30daysofIA/tags/azure-cosmos-db/page/11/index.html b/30daysofIA/tags/azure-cosmos-db/page/11/index.html new file mode 100644 index 0000000000..515a3c43b6 --- /dev/null +++ b/30daysofIA/tags/azure-cosmos-db/page/11/index.html @@ -0,0 +1,26 @@ + + + + + +11 posts tagged with "azure-cosmos-db" | Build Intelligent Apps On Azure + + + + + + + + + + + + + + +
+

11 posts tagged with "azure-cosmos-db"

View All Tags

· 2 min read
It's 30DaysOfIA

September is almost here - and that can only mean one thing!! It's time to 🍂 Fall for something new and exciting and spend a few weeks skilling up on relevant tools, techologies and solutions!!

Last year, we focused on #ServerlessSeptember. This year, we're building on that theme with the addition of cloud-scale Data, cloud-native Technologies and cloud-based AI integrations to help you modernize and build intelligent apps for the enterprise!

Watch this space - and join us in September to learn more!

+ + + + \ No newline at end of file diff --git a/30daysofIA/tags/azure-cosmos-db/page/2/index.html b/30daysofIA/tags/azure-cosmos-db/page/2/index.html index 3d5519503b..9904554850 100644 --- a/30daysofIA/tags/azure-cosmos-db/page/2/index.html +++ b/30daysofIA/tags/azure-cosmos-db/page/2/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "azure-cosmos-db" | Build Intelligent Apps On Azure +11 posts tagged with "azure-cosmos-db" | Build Intelligent Apps On Azure @@ -14,13 +14,13 @@ - - + +
-

9 posts tagged with "azure-cosmos-db"

View All Tags

· 9 min read
It's 30DaysOfIA

Learn best practices for modernizing your on-premises or IaaS solutions to intelligent apps in the cloud.

What We'll Cover:

  • Traditional vs Intelligent Apps
  • On-Premises/IaaS vs. Cloud-Native Platforms
  • Modernization strategy for building Intelligent Apps

image of modernizing AI solutions for intelligent apps

Preparing the Path for Intelligent Apps: Transitioning from On-Premises/IaaS to Cloud-Native Applications

The proliferation of generative AI is paving the way for intelligent applications, software that leverages AI capabilities to deliver unparalleled functionality and user experience. As we covered in a previous article, “Demystifying Intelligent Applications: Leveraging AI in App Development,” intelligent Apps aren’t just products of advanced coding. They interact, learn, and evolve thanks to modern AI and machine learning (ML) breakthroughs. 

The paradigm shift from traditional to intelligent apps demands that we change our approach to the technical challenges of software and architecture design. It also necessitates adopting new ways of thinking and operating within an organization, which we explore in greater depth in “Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management.” 

This article explores the technical infrastructure powering intelligent apps, providing a roadmap to transition traditional on-premises or Infrastructure as a Service (IaaS) solutions to intelligent apps deployed to cloud native platforms and services. We’ll discover the differences between conventional and intelligent apps, options for re-architecting existing applications, and critical strategic considerations integral to modernizing the application-building approach. 

Understanding the Shift: Traditional vs. Intelligent Applications

Intelligent apps are more than just an incremental evolution from traditional apps. They involve a fundamental shift in software solution design, development, and deployment. A clear understanding of how each paradigm differs is crucial for comprehending the impact of this transition. 

Functionality and User Experience

Traditional applications, often rule-based and rigid, rely on preprogrammed operations. A traditional weather app, for example, typically presents structured, location-based forecasts using a predetermined data source. Meanwhile, an intelligent app leverages AI for data-driven decision-making and personalization. Rather than merely displaying the forecast, it integrates ML with additional data sources—like a user’s calendar and fitness data—to determine their favorite outdoor activities, provide relevant clothing suggestions, and even automatically design ideal vacations and times based on the user’s optimal weather preferences. 

Similarly, while traditional applications tend to offer a one-size-fits-all UI, intelligent apps use AI to create tailored interactions. For instance, an intelligent banking app might use generative AI to provide a voice-enabled UI that lets customers use natural language to ask about transactions and receive personalized financial advice.

Infrastructure

Intelligent apps integrating these sophisticated features require highly scalable, flexible infrastructure optimized for AI functionality and able to manage large volumes of real-time data. Unlike traditional applications deployed on-premises or in infrastructure as a service (IaaS) environments, intelligent apps demand the flexibility and scalability of cloud native architectures.

An on-premises or IaaS-based e-commerce application may struggle with connecting its data to AI services or scaling to accommodate voluminous real-time data and requests. But an intelligent app built with a cloud native approach and scalability at its core can handle variable demand much more efficiently. It also maintains its infrastructure components on demand, whether for collecting data for AI/ML consumption or user-facing features like AI-enabled customer support and personalization.

Consider an intelligent app built on Azure that creates ChatGPT-like experiences with custom data. It might connect Azure Open AI and Azure Cognitive Search to an app running on Azure Container Apps or Azure Kubernetes Service. 

The architecture might look like the following diagram based on this demo app:

diagram of a demo app architecture

While the cloud is well-equipped to handle the challenges associated with shifting to AI-centric infrastructure—handling real-time data processing and storage, integrating and managing AI services, configuring event-driven and microservices architectures, and ensuring security across the distributed environment—re-architecting a traditional app to an intelligent app requires careful planning and execution to ensure it benefits fully from the capabilities of AI and cloud computing.

On-Premises/IaaS vs. Cloud-Native Platforms

While on-premises and IaaS solutions provide substantial control over data and processes, this fine-grained control comes with setup and maintenance costs and may lack the scalability necessary for AI-driven Intelligent Apps. On-premises hosting involves purchasing and maintaining hardware, including servers and storage systems. IaaS involves leasing virtual hardware and then hiring and training IT personnel for the infrastructure’s operation, maintenance, and security.

Conversely, infrastructure built on cloud native app services with Microsoft Azure offers virtually unlimited scalability without ongoing hardware and maintenance costs. This feature is ideal for intelligent apps that need to adapt and scale to accommodate fluctuating workloads and data streams. And because these cloud infrastructures typically operate on a pay-as-you-go model, traditionally substantial upfront costs turn into predictable operating expenses, lowering both the cost and risk of launching applications.

Moreover, cloud service providers continually update their offerings, so building on sophisticated AI tools and services through the cloud, like the Azure AI Platform and orchestration capabilities with Azure Kubernetes Service, ensures that you have access to the latest AI and most advanced AI models and technologies.

Strategic Considerations for Transitioning to Intelligent Apps

When developing a strategy for transitioning to intelligent apps, you must consider costs, personnel skill development, and data management.

Monolithic architectures are unlikely to support the scale and ease of iteration required by intelligent apps, so you’ll likely need to implement code changes to support the transition. Your chosen hosting solution and the amount of data your app will handle affect cost and performance, in addition to determining privacy and compliance requirements.

An app that manages or stores terabytes or petabytes of data will require storage-optimized virtual machines or a managed database service hosting. Meanwhile, an app that processes video content or performs resource-intensive calculations should prioritize high-performance computing. Maximizing storage and performance can become extraordinarily costly.

Finally, existing applications shifting towards intelligent apps may encounter compatibility conflicts, depending on database type or the existing system’s underlying programming language. As you might imagine, a traditional app running on-premises and using a shared local database may need significant restructuring to support a cloud native architecture. You need plans for data migration, potential downtime, and training.

Next, let’s look at this transformation process.

Re-Architecting Monolithic Apps into Microservices

Shifting from monolithic applications to microservices is pivotal for transitioning to Intelligent Apps, and you can use this shift as an opportunity to transform your legacy software into intelligent apps that leverage the flexibility and power of microservices-enabled AI solutions.

Traditionally, a monolithic application (let’s use an online marketplace as an example) might handle all activities—from serving webpages to processing payments—within an on-premises server or a cloud server. However, this approach is challenging to scale and remains vulnerable to network failures or cyberattacks.

Breaking these applications into multiple services with specific responsibilities, such as authentication or notifications, can overcome these limitations. These services are typically deployed as microservices within container environments like Azure Kubernetes Service or serverless platforms like Azure Container Apps or Azure Functions using automated processes. This approach offers scalability, robustness, and improved performance, as each microservice can run independently and use its own database.

Shifting towards smaller, independent microservices in app architectures allows us to reap the benefits of AI-powered intelligent apps, for example, by using microservices to handle AI tasks such as processing natural language input, analyzing behavioral data, or personalizing content.

info

Watch Episode 1 and Episode 2 of the Learn Live series on how to build, test and deploy an end-to-end intelligent app solution using the Contoso Real Estate Sample.

Event-Driven Architectures: Knowing When to Use Them

Event-Driven Architectures (EDAs) can significantly enhance the performance and cost-effectiveness of intelligent apps. EDAs, typically implemented with microservices, respond in real time to events or state changes ranging from user interactions to real-time analytics.

Consider an intelligent app designed for personalized customer engagement. Instead of designing the application to check continuously for changes in user data, an EDA can react to such changes immediately, triggering appropriate microservices. For example, if users modify their preferences, an event could trigger an AI-driven recommendation microservice to update its suggestions immediately. This real-time responsiveness, enabled by services like Azure Event Grid, can improve user experiences and make intelligent apps more adaptive and proactive.

However, it’s worth noting that while EDAs provide significant benefits, they can introduce unique complexities, such as maintaining event consistency and debugging issues in event chains across multiple microservices. Therefore, it’s important to assess your application’s needs so you can understand where it can benefit from an EDA.

Conclusion

Transitioning to intelligent apps represents a shift in strategy, architecture, and infrastructure. By designing and re-architecting for cloud-based infrastructure, your apps gain scalability, cost-effectiveness, robust data management, access to sophisticated AI tools, enhanced security, and reduced operational overhead.  

But this transition extends well beyond its technical components. Organizations and the people working within them need equal attention. You can explore these facets in greater depth in “Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management.” 

The power of generative AI is here, and preparing for the move toward cloud-based intelligent applications will ensure that your apps remain performative and scalable and—most importantly—that your business is ready for the newest era of app innovation.

Exercise

- - +
Skip to main content

11 posts tagged with "azure-cosmos-db"

View All Tags

· 9 min read
It's 30DaysOfIA

Learn best practices for modernizing your on-premises or IaaS solutions to intelligent apps in the cloud.

What We'll Cover:

  • Traditional vs Intelligent Apps
  • On-Premises/IaaS vs. Cloud-Native Platforms
  • Modernization strategy for building Intelligent Apps

image of modernizing AI solutions for intelligent apps

Preparing the Path for Intelligent Apps: Transitioning from On-Premises/IaaS to Cloud-Native Applications

The proliferation of generative AI is paving the way for intelligent applications, software that leverages AI capabilities to deliver unparalleled functionality and user experience. As we covered in a previous article, “Demystifying Intelligent Applications: Leveraging AI in App Development,” intelligent Apps aren’t just products of advanced coding. They interact, learn, and evolve thanks to modern AI and machine learning (ML) breakthroughs. 

The paradigm shift from traditional to intelligent apps demands that we change our approach to the technical challenges of software and architecture design. It also necessitates adopting new ways of thinking and operating within an organization, which we explore in greater depth in “Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management.” 

This article explores the technical infrastructure powering intelligent apps, providing a roadmap to transition traditional on-premises or Infrastructure as a Service (IaaS) solutions to intelligent apps deployed to cloud native platforms and services. We’ll discover the differences between conventional and intelligent apps, options for re-architecting existing applications, and critical strategic considerations integral to modernizing the application-building approach. 

Understanding the Shift: Traditional vs. Intelligent Applications

Intelligent apps are more than just an incremental evolution from traditional apps. They involve a fundamental shift in software solution design, development, and deployment. A clear understanding of how each paradigm differs is crucial for comprehending the impact of this transition. 

Functionality and User Experience

Traditional applications, often rule-based and rigid, rely on preprogrammed operations. A traditional weather app, for example, typically presents structured, location-based forecasts using a predetermined data source. Meanwhile, an intelligent app leverages AI for data-driven decision-making and personalization. Rather than merely displaying the forecast, it integrates ML with additional data sources—like a user’s calendar and fitness data—to determine their favorite outdoor activities, provide relevant clothing suggestions, and even automatically design ideal vacations and times based on the user’s optimal weather preferences. 

Similarly, while traditional applications tend to offer a one-size-fits-all UI, intelligent apps use AI to create tailored interactions. For instance, an intelligent banking app might use generative AI to provide a voice-enabled UI that lets customers use natural language to ask about transactions and receive personalized financial advice.

Infrastructure

Intelligent apps integrating these sophisticated features require highly scalable, flexible infrastructure optimized for AI functionality and able to manage large volumes of real-time data. Unlike traditional applications deployed on-premises or in infrastructure as a service (IaaS) environments, intelligent apps demand the flexibility and scalability of cloud native architectures.

An on-premises or IaaS-based e-commerce application may struggle with connecting its data to AI services or scaling to accommodate voluminous real-time data and requests. But an intelligent app built with a cloud native approach and scalability at its core can handle variable demand much more efficiently. It also maintains its infrastructure components on demand, whether for collecting data for AI/ML consumption or user-facing features like AI-enabled customer support and personalization.

Consider an intelligent app built on Azure that creates ChatGPT-like experiences with custom data. It might connect Azure Open AI and Azure Cognitive Search to an app running on Azure Container Apps or Azure Kubernetes Service. 

The architecture might look like the following diagram based on this demo app:

diagram of a demo app architecture

While the cloud is well-equipped to handle the challenges associated with shifting to AI-centric infrastructure—handling real-time data processing and storage, integrating and managing AI services, configuring event-driven and microservices architectures, and ensuring security across the distributed environment—re-architecting a traditional app to an intelligent app requires careful planning and execution to ensure it benefits fully from the capabilities of AI and cloud computing.

On-Premises/IaaS vs. Cloud-Native Platforms

While on-premises and IaaS solutions provide substantial control over data and processes, this fine-grained control comes with setup and maintenance costs and may lack the scalability necessary for AI-driven Intelligent Apps. On-premises hosting involves purchasing and maintaining hardware, including servers and storage systems. IaaS involves leasing virtual hardware and then hiring and training IT personnel for the infrastructure’s operation, maintenance, and security.

Conversely, infrastructure built on cloud native app services with Microsoft Azure offers virtually unlimited scalability without ongoing hardware and maintenance costs. This feature is ideal for intelligent apps that need to adapt and scale to accommodate fluctuating workloads and data streams. And because these cloud infrastructures typically operate on a pay-as-you-go model, traditionally substantial upfront costs turn into predictable operating expenses, lowering both the cost and risk of launching applications.

Moreover, cloud service providers continually update their offerings, so building on sophisticated AI tools and services through the cloud, like the Azure AI Platform and orchestration capabilities with Azure Kubernetes Service, ensures that you have access to the latest AI and most advanced AI models and technologies.

Strategic Considerations for Transitioning to Intelligent Apps

When developing a strategy for transitioning to intelligent apps, you must consider costs, personnel skill development, and data management.

Monolithic architectures are unlikely to support the scale and ease of iteration required by intelligent apps, so you’ll likely need to implement code changes to support the transition. Your chosen hosting solution and the amount of data your app will handle affect cost and performance, in addition to determining privacy and compliance requirements.

An app that manages or stores terabytes or petabytes of data will require storage-optimized virtual machines or a managed database service hosting. Meanwhile, an app that processes video content or performs resource-intensive calculations should prioritize high-performance computing. Maximizing storage and performance can become extraordinarily costly.

Finally, existing applications shifting towards intelligent apps may encounter compatibility conflicts, depending on database type or the existing system’s underlying programming language. As you might imagine, a traditional app running on-premises and using a shared local database may need significant restructuring to support a cloud native architecture. You need plans for data migration, potential downtime, and training.

Next, let’s look at this transformation process.

Re-Architecting Monolithic Apps into Microservices

Shifting from monolithic applications to microservices is pivotal for transitioning to Intelligent Apps, and you can use this shift as an opportunity to transform your legacy software into intelligent apps that leverage the flexibility and power of microservices-enabled AI solutions.

Traditionally, a monolithic application (let’s use an online marketplace as an example) might handle all activities—from serving webpages to processing payments—within an on-premises server or a cloud server. However, this approach is challenging to scale and remains vulnerable to network failures or cyberattacks.

Breaking these applications into multiple services with specific responsibilities, such as authentication or notifications, can overcome these limitations. These services are typically deployed as microservices within container environments like Azure Kubernetes Service or serverless platforms like Azure Container Apps or Azure Functions using automated processes. This approach offers scalability, robustness, and improved performance, as each microservice can run independently and use its own database.

Shifting towards smaller, independent microservices in app architectures allows us to reap the benefits of AI-powered intelligent apps, for example, by using microservices to handle AI tasks such as processing natural language input, analyzing behavioral data, or personalizing content.

info

Watch Episode 1 and Episode 2 of the Learn Live series on how to build, test and deploy an end-to-end intelligent app solution using the Contoso Real Estate Sample.

Event-Driven Architectures: Knowing When to Use Them

Event-Driven Architectures (EDAs) can significantly enhance the performance and cost-effectiveness of intelligent apps. EDAs, typically implemented with microservices, respond in real time to events or state changes ranging from user interactions to real-time analytics.

Consider an intelligent app designed for personalized customer engagement. Instead of designing the application to check continuously for changes in user data, an EDA can react to such changes immediately, triggering appropriate microservices. For example, if users modify their preferences, an event could trigger an AI-driven recommendation microservice to update its suggestions immediately. This real-time responsiveness, enabled by services like Azure Event Grid, can improve user experiences and make intelligent apps more adaptive and proactive.

However, it’s worth noting that while EDAs provide significant benefits, they can introduce unique complexities, such as maintaining event consistency and debugging issues in event chains across multiple microservices. Therefore, it’s important to assess your application’s needs so you can understand where it can benefit from an EDA.

Conclusion

Transitioning to intelligent apps represents a shift in strategy, architecture, and infrastructure. By designing and re-architecting for cloud-based infrastructure, your apps gain scalability, cost-effectiveness, robust data management, access to sophisticated AI tools, enhanced security, and reduced operational overhead.  

But this transition extends well beyond its technical components. Organizations and the people working within them need equal attention. You can explore these facets in greater depth in “Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management.” 

The power of generative AI is here, and preparing for the move toward cloud-based intelligent applications will ensure that your apps remain performative and scalable and—most importantly—that your business is ready for the newest era of app innovation.

Exercise

+ + \ No newline at end of file diff --git a/30daysofIA/tags/azure-cosmos-db/page/3/index.html b/30daysofIA/tags/azure-cosmos-db/page/3/index.html index 14ee0475df..cae7727dee 100644 --- a/30daysofIA/tags/azure-cosmos-db/page/3/index.html +++ b/30daysofIA/tags/azure-cosmos-db/page/3/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "azure-cosmos-db" | Build Intelligent Apps On Azure +11 posts tagged with "azure-cosmos-db" | Build Intelligent Apps On Azure @@ -14,13 +14,13 @@ - - + +
-

9 posts tagged with "azure-cosmos-db"

View All Tags

· 10 min read
It's 30DaysOfIA

Explore how digital natives and Independent Software Vendors (ISVs) can transform their business operations and generate higher value for customers with generative AI and intelligent apps.

What We'll Cover:

  • Potential of Generative AI in application development
  • Challenges with Generative AI
  • Integrating Generative AI with business operations

image of generative AI and intelligent apps used in business

Unleashing Business Transformation: How Digital Natives Can Leverage Generative AI

Generative AI is no longer a technological flight of fancy. It’s here, and it’s rewriting the rules of business innovation. 

This type of AI leverages machine learning algorithms to create new data with similar characteristics to a given dataset. It goes beyond text generation using generative pretrained transformer (GPT) models, covering an array of applications that span from image synthesis to translations and more. It also powers a new class of intelligent applications, which combine AI capabilities with traditional software functions for enhanced user experiences and advanced business solutions. 

Generative AI is transforming how digital natives and independent software vendors (ISVs) operate. Generative AI offers new possibilities for creating unique business value, enhancing customer experiences, and driving growth. 

It plays a critical role in applications, improving functionality, aiding decision-making, and automating complex tasks to deliver dynamic, uniquely personalized user experiences. Generative AI also enables and automates data-driven outcomes, functions, and features.

Integrating AI capabilities into customer-facing apps and turning them into intelligent apps benefits both the customer and the business, as we discussed in Demystifying Intelligent Applications: Leveraging AI in App Development. You get faster product development workflows and better business intelligence for sales and marketing, while your users get more natural and context-aware features and interactions. 

That said, to realize generative AI’s potential, you need some strategy and knowledge of how and where it fits into your current business and operations. We’ll examine this technology’s potential to transform business operations, its risks, and rewards, and how to integrate it into your existing operations. 

Unpacking the Potential of Generative AI

Generative AI is creating new opportunities in the digital playing field. It empowers businesses to generate insights, design products, and automate processes in ways and at speeds that were previously unimaginable. You can now predict customer behavior more accurately, offer highly personalized user experiences, and prototype innovative ideas rapidly. 

For example, Microsoft used generative AI to reinvent web search in the new AI-powered Bing and Edge. The search experience has evolved from a solitary user flow of typing in keywords and scrolling through results. It’s now a conversational experience where Bing uses context to offer better quality and more personalized search queries and results. 

Similarly, Microsoft harnessed generative AI to transform its productivity suite into intelligent apps. Through Copilot for Microsoft 365, PowerPoint now empowers users to tell their stories by using DALL-E to generate images effortlessly. It also helps rephrase text-heavy slides to be more straightforward. Outlook now assists users to compose more effective emails, enabling better communication.

These are just some examples of how AI powers day-to-day work and operations. 

Businesses that ignore generative AI miss out on untapped market opportunities, letting competitors gain an edge in the digital race. By seeking ways to rethink assumptions and reinvent existing approaches, you can effectively use generative AI to create new opportunities, open products to new use cases and markets, unlock new growth and customer satisfaction levels, and increase your revenue.

info

Register for the intelligent apps webinar on Driving Business Value by Modernizing with Cloud-Native & AI with Microsoft and Forrester on September 26.

Explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries. 

The Crucial Role of Generative AI in Business Operations

Generative AI can be a powerful catalyst for business transformation. Let’s quickly tour some ways it can redefine business operations. 

Product development is traditionally a long and painstaking process. Now, tools like GitHub Copilot and TestPilot use generative AI to make coding and testing software easier, faster, and more robust. 

Similarly, machine learning as a service technology, such as Azure Machine Learning, democratizes the availability of machine learning. It helps collect, clean, and train models across datasets, making engineering and analyzing data more efficient and accessible.  

Generative AI also simplifies the process of creating intelligent apps. Previously, simulating thousands of prototypes, predicting their performance, and narrowing down to the most viable options could take weeks, months, or even years of iteration and data analysis. With generative AI, this process can be as quick as days, hours, or perhaps minutes. 

Customer experience is also ripe for transformation. Personalized recommendations drive engagement and sales, and generative AI can scale that personalization to every customer interaction using comprehensive and continually updated data. For instance, Microsoft leverages AI to enable personalization in Dynamics 365 Customer Insights. Using this tool, you can ask questions about your customers in natural language and gain valuable and actionable insights into your customer base. 

Lastly, marketing and sales can benefit from generative AI. For example, identifying current and future key markets and emerging trends helps companies stay ahead of the curve. Tools like Copilot in Power BI help you quickly understand and uncover insights about your business, enabling your team to craft targeted campaigns, create engaging content, and optimize your business strategy. 

Leveraging this full generative AI potential requires the right tools. GPT-4, for instance, can be instrumental in realizing effective business transformation. It excels in understanding context, generating human-like text, and learning from minimal data input, making it a formidable tool in any digital native's arsenal. Platforms like Azure OpenAI Service bring GPT-4’s power to your fingertips.

However, as with any powerful technology, it’s unwise to treat generative AI as a perfect solution. You must know the challenges and risks of not leveraging it effectively, including technical and human-factor considerations. 

Without generative AI, your company risks missing the opportunity to please customers via personalization, as discussed previously. You’ll miss sales opportunities via business intelligence insights, limiting your ability to grow revenue and keep up with the competition. 

One challenge of leveraging generative AI’s full potential is finding the right tools. Fortunately, you can implement generative AI in your organization without investing in infrastructure, computational power, and expertise. Platforms like Azure OpenAI Service and Azure AI Services make AI easily accessible. They also work with Azure compute and data services like Azure Functions and Azure Kubernetes Service (AKS), enabling you to build intelligent apps easily and quickly. 

However, generative AI can’t do everything. It may need human expertise and intervention to use it fully. 

For example, generative AI can create unique data. However, the data may have limited diversity and complexity. The AI may need help recognizing additional considerations or requirements specific to a problem. It may also lack context due to various factors, such as the information’s freshness. Also, since it’s an emerging field that’s potentially worth $4.4 trillion, we’re still learning about the nuances and potential regulatory impact of generative AI. 

Perhaps the most crucial and challenging aspect of adopting generative AI is an organization’s required cultural shift. Your teams must learn how to use AI as your company fosters an understanding of AI’s potential and limitations. 

Begin integrating AI literacy into your company’s DNA. Knowledge of its potential and the readily available tools can inspire your teams to incorporate generative AI into their daily workflows and business operations quickly, improve productivity, and deliver superior customer experiences. 

No company wants to be outpaced by its competitors. You can balance generative AI’s potential with your business operations by staying aware of and adapting to the dynamic AI landscape.

Achieving Harmony: Integrating Generative AI with Existing Business Operations

Integrating generative AI into existing business operations doesn’t have to be disruptive. With the correct strategy, you can do so seamlessly. Your goal should be to augment and enhance your operations, not disrupt and replace. 

For example, Microsoft Cloud technologies leveraged AI to relieve healthcare organizations’ challenges, such as talent shortage and workforce burnout. Providers automated their workflows, invested in specific applications and connectors, and enhanced support for data and operations. 

Azure AI Services, such as text analytics for health, enable healthcare organizations to understand their largely unstructured and untapped medical data. Project Health Insights leans on AI to help healthcare professionals gain actionable insights and inferences from patient data. 

Azure Health Bot, an Intelligent App, empowers organizations to build and deploy legally compliant, powerful conversational healthcare experiences to patients. 

A successful AI integration like this begins with understanding. Knowing what generative AI can do and setting realistic expectations can help you identify where and how AI can best serve your operations. 

Next, you can foster a culture of AI literacy across teams, speeding up adoption by helping them understand how it can aid their work. The more familiar your team is with AI and its possibilities, the smoother the transition. Training and education programs play a pivotal role here. 

Finally, integration requires a systematic approach. It’s not about overhauling everything at once — it's about identifying areas where AI can add the most value, then gradually experimenting and scaling its use. Successful integration stories often feature a phased approach, starting small and growing steadily. 

As we move toward a future where generative AI is a staple in business operations, remember that every journey is unique. Although there’s no one-size-fits-all solution, a strategic approach and a keen understanding of your business needs will help you navigate this new landscape successfully.

Conclusion

Generative AI offers plenty of opportunities for businesses that embrace it. It’s fueling the next generation of intelligent applications, transforming everything from product development to customer engagement to marketing. This technology is the key to unlocking unprecedented growth and innovation. 

While the journey may have challenges, the potential rewards make generative AI worth exploring. As generative AI advances, Azure AI’s broad range of tools and services will help you navigate this fascinating field.

Exercise

  • Watch Episode 02 of the serverless edition Learn Live session to learn how to build, test and deploy an end-to-end intelligent app solution.
  • Complete the Cloud Skills Challenge to build on your AI skills.
  • Register for Ask the Expert: Azure Functions on September 26 for a live Q&A with the Product Team on how to build AI apps using serverless computing.
- - +
Skip to main content

11 posts tagged with "azure-cosmos-db"

View All Tags

· 12 min read
It's 30DaysOfIA

Delve into creating an Intelligent App that leverages Azure AI Vision to analyze images and extract data. Develop an API to perform optical character recognition (OCR) on uploaded images and testing this API locally.

What We'll Cover:

  • Understanding Azure AI Vision and Azure Kubernetes Service
  • Build a Python Web API to perform OCR
  • Test the API locally

image depicting an Intelligent App using AI

Jumpstart Your AI Journey: Building Your First Intelligent App with Azure AI and AKS (1)

An Intelligent App is a software application that uses artificial intelligence (AI), such as machine learning (ML), to extract meaningful insights, enabling users to make informed decisions based on the extracted text, data, sound, images, and more.

Intelligent Apps can employ advanced algorithms and models to recognize objects, patterns, and features within images, allowing them to perform tasks like object identification, image classification, facial recognition, and image segmentation.

To explore the power of Intelligent Apps, let’s build a Python app that performs optical character recognition (OCR) on images containing numbers and performs aggregate functions on the extracted numbers, like sum and average. We’ll leverage Azure AI Services for the OCR functionality and Azure Kubernetes Service (AKS) to develop, publish, and maintain a cloud-based, scalable app in Azure.

Let’s get started!

Understanding Azure AI Vision and Azure Kubernetes Service

Azure AI Vision is a cloud-based service that offers a range of advanced computer vision capabilities enabling applications to interpret content, such as images and videos, using ML and other AI algorithms. Azure computer vision lets users integrate powerful vision features into their projects without ML experience.

OCR is one of the critical functionalities within Azure vision service, allowing applications to extract text from images. Within Azure AI Vision service, OCR offers several features, including text extraction, multilingual support, layout analysis of documents, and integration with Azure services, such as Azure Functions and Azure Machine Learning.

info

Watch the intelligent apps webinar on Driving Business Value by Modernizing with Cloud-Native & AI with Microsoft and Forrester on September 26.

AKS is Microsoft Azure’s container orchestration platform that enables us to quickly and efficiently deploy, manage, and scale containerized applications, such as our Intelligent App.

Let’s explore how to leverage these tools to build an Intelligent App that uses Azure AI Vision to analyze images and extract data.

Prerequisites

To follow this tutorial, ensure you have the following:

For a preview of this final project, take a look at the complete project code.

Building the API with Azure AI Vision Service

First, log in to your Azure account and navigate to the Azure Portal.

Click Create a resource and search for “resource group.” Create a new resource group named computer-vision.

image in Azure Portal of creating a new resource group

Return to the Azure Portal home page and click Create a resource.

image in Azure Portal of creating a new resource

Search for “computer vision” and select it from the results. Click Create.

image of searching for resource group in Azure Portal

Clicking Create displays the Create Computer Vision wizard. Select the resource group you just created as the Resource group from the dropdown. Enter a resource name similar to what is seen in the screenshot below. Note that this must be a unique name so choose one that is not currently in use. Select Standard S1 as the pricing tier and check the Responsible use of AI box in the field at the bottom.

Image of selecting the Responsible use of AI box

Image of project details

Click Review + Create, then click Create.

Next, get the API key and endpoint from the Azure AI services multi-service account you created. Once the resource is created, navigate to the resource details page. Under Resource Management, select Keys and Endpoint. Once on the Keys and Endpoint page, you’ll find the Key 1 and Endpoint values. These credentials are necessary to access the Azure AI APIs.

image of selecting Keys and Endpoint

second image of selecting Keys and Endpoint

Configuring the Local Environment Variables

Next, we need to define a local environment variable file. This convenient way to store our API keys in a simple text file enables us to manage sensitive information and separate configuration from our API code.

If you haven’t done so already, clone the starter project template from GitHub into your local computer. Open the starter project template in a code editor and create a new .env file in the root folder.

Note: Root folder in this case is the “/Microsoft_Series17-18_Code/intelligent-app-before" folder.

Add the following key-value pairs to the file, remembering to replace the placeholder values with those you obtained from the previous step.

VISION_KEY=<THE-KEY-1-VALUE-FROM-YOUR-AZURE-AI-SERVICE>
VISION_ENDPOINT=<THE-ENDPOINT-VALUE-FROM-YOUR-AZURE-AI-SERVICE>

Reviewing the Quickstart Code

Let’s review the app.py file in the starter project template.

The app.py file works as the entry point for the Flask application. It is responsible for initializing the web server, listening for incoming requests, dispatching them to the appropriate route handlers, and generating responses.

Below is the code contained in the app.py file:

import os
import json

from flask import Flask, request
from flask_restful import Resource, Api
from werkzeug.utils import secure_filename

app = Flask(__name__,
static_url_path='',
static_folder='static/files')

api = Api(app)

app = Flask(__name__)

app.config['UPLOAD_FOLDER'] = 'files'

api = Api(app)

class UploadHandler(Resource):

def allowed_file(self, filename):
return '.' in filename and \
filename.rsplit('.', 1)[1].lower() in {'png'}

def post(self):
form = request.form.to_dict()

if 'file' not in request.files:
return json.dumps({ "success": False, "error": "No file part"})

file = request.files.get("file")
if file and self.allowed_file(file.filename):
filename = secure_filename(file.filename)
upload_folder = "static/files"
if not os.path.exists(upload_folder):
os.makedirs(upload_folder)
local_file_path = os.path.join(upload_folder, filename)
file.save(local_file_path)

return f"File {filename} uploaded successfully to folder: {upload_folder}"

api.add_resource(UploadHandler, "/")

if __name__ == '__main__':
app.run(debug=True)

The UploadHandler class above is defined as a Flask RESTful resource to handle file uploads. The class contains two methods:

  • The allowed_file method checks whether the file extension is allowed for upload. In this case, only .png files are allowed.

  • The post method handles HTTP POST requests for file uploads. It saves the uploaded .png file to the static/files folder.

Finally, the application returns a text response informing us that the file has been uploaded successfully.

Implementing the REST API in Python

To implement image analysis in your REST API, open the starter project template in your terminal, create a virtual environment, and activate it.

Next, install the Azure AI Vision SDK:

pip install azure-ai-vision

Then, add the following line to the requirements.txt file to include the azure-ai-vision package:

azure-ai-vision==0.13.0b1

Now, create an ocr_helper.py file in the project’s root folder. This file is a Python module that provides functions for processing images using OCR. Add the following code to the file:

import os
from statistics import median
from decimal import Decimal
import azure.ai.vision as sdk

def process_ocr(source_image):
service_options = sdk.VisionServiceOptions(os.environ["VISION_ENDPOINT"],
os.environ["VISION_KEY"])

vision_source = sdk.VisionSource(filename=source_image)

analysis_options = sdk.ImageAnalysisOptions()

analysis_options.features = (
sdk.ImageAnalysisFeature.CAPTION |
sdk.ImageAnalysisFeature.TEXT
)

analysis_options.language = "en"

analysis_options.gender_neutral_caption = True

image_analyzer = sdk.ImageAnalyzer(service_options, vision_source, analysis_options)

result = image_analyzer.analyze()

ocr_result = get_ocr_result(result)

return ocr_result

def get_ocr_result(result):
string_list = []

if result.reason != sdk.ImageAnalysisResultReason.ANALYZED:
return sdk.ImageAnalysisErrorDetails.from_result(result)
else:
if result.text is not None:
for line in result.text.lines:
for word in line.words:
string_list.append(word.content)

number_list = convert_to_decimal_list(string_list)

aggregate_result = aggregate_operations(number_list)

return {
"aggregate_result": aggregate_result,
"numbers_read": string_list
}

def convert_to_decimal_list(string_list):
return list(map(Decimal, string_list))

def aggregate_operations(numbers):
result = {
'sum': sum(numbers),
'average': sum(numbers) / len(numbers),
'median': median(numbers),
'min': min(numbers),
'max': max(numbers)
`}`
`return result`

This module uses the azure-ai-vision package to analyze images, including capturing captions and extracting text from the image. The process_ocr function inputs an image file and performs the OCR analysis. The module is a convenient tool for analyzing text-based data in pictures and performing numerical computations based on that data.

Let’s review the different components of the ocr_helper.py module listed above and explore how its functions help our Intelligent App process images and perform aggregate operations:

  • The process_ocr function takes the parameter source_image, which is the path of the image to be processed. The function then initializes the VisionServiceOptions using environment variables VISION_ENDPOINT and VISION_KEY to connect to the Azure AI Vision API.
  • The process_ocr function creates a VisionSource object with the specified source_image file name. ImageAnalysisOptions specify the features to be analyzed, including CAPTION and TEXT. The language is set to English ("en"), and gender-neutral captions are enabled.
  • Finally, an ImageAnalyzer object is created with the service options, vision source, and analysis options. The image is then analyzed using the image_analyzer.analyze method to retrieve OCR results. The recognized text from the OCR result is extracted, with numerical values stored in the string_list variable.
  • The convert_to_decimal_list function converts the list of strings to a list of decimal numbers, which helps process numeric values extracted from the text.
  • The aggregate_operations function calculates various aggregate statistics (sum, average, median, minimum, maximum) from a list of numbers and returns the results in a dictionary.

Note that you must have the appropriate credentials (VISION_KEY) and endpoint (VISION_ENDPOINT) configured for the Azure AI Vision API to use this module.

Finally, we must modify the app.py file so our code can use the process_ocr function of the ocr_helper.py file.

Add the following import statement to the app.py file:

from ocr_helper import process_ocr

Then, replace this line:

return f"File {filename} uploaded successfully to folder: {upload_folder}"

With these two lines:

aggregates = process_ocr(local_file_path)
return json.dumps(aggregates, default=str)

Doing so allows our application to perform aggregate operations (sum, average, median, minimum, maximum) of any numeric values found in the text and return the operation’s result as a JSON response.

Running the Intelligent App Locally

Our final goal is to deploy the sample app to AKS, which will run the app in a container. However, as a good practice, let’s run the app in a container locally before moving it to AKS. Doing so ensures that our application and its dependencies are encapsulated and can be run consistently across different environments.

Deploying to Docker Desktop

For our application to run on Docker, it needs two additional files: a Dockerfile and docker-compose.yml. A Dockerfile creates a single container image by specifying the steps to build it, and a docker-compose.yml file manages multiple containers as a cohesive application, defining their configurations and relationships. Dockerfile builds the image, while docker-compose.yml orchestrates multi-container applications.

In the project root folder, add a file named Dockerfile with the content below:

# syntax=docker/dockerfile:1

FROM python:3.8-slim-buster

WORKDIR /intelligentapp

COPY requirements.txt requirements.txt
RUN pip3 install -r requirements.txt

RUN pip install debugpy

COPY . .

CMD [ "python3", "-m" , "flask", "run", "--host=0.0.0.0"]

Now create a file named docker-compose.yml file in the project’s root folder with the following code, replacing the VISION_KEY and the VISION_ENDPOINT according to the environment variables you configured earlier:

version: '3.8'
services:
intelligentapp:
build:
context: .
dockerfile: Dockerfile
image: intelligent-app
ports:
- 5000:5000
container_name: intelligent-app
environment:
- VISION_KEY=<THE-KEY-1-VALUE-FROM-YOUR-AZURE-AI-SERVICE>
- VISION_ENDPOINT=<THE-ENDPOINT-VALUE-FROM-YOUR-AZURE-AI-SERVICE>

Then, run the following command in the terminal to build the image and start a container for the Intelligent App services defined in the docker-compose.yml file:

docker-compose up --build --force-recreate

Next, open a new terminal and run the command below to list the image deployed to your local Docker:

docker images

docker images

Testing the Intelligent App Locally

Now, we’ll test the Intelligent App’s functionality using the following test image:

test image to test functionality

This image is provided as sample1.png in the sample app source files, so you can easily use it in the next step.

The Intelligent App will read the image above using OCR to extract numbers in the image and perform five aggregate functions:

  • Sum
  • Average
  • Median
  • Min
  • Max

To test the API, open Postman and fill out the fields as follows:

  • URL: http://localhost:5000/
  • Method: POST
  • Body:
    • Form-data
      • Key: File — Click the right end of the Key field and select File from the dropdown list.
      • Value: Click Select Files, then select the sample1.png file provided in the sample code.

image of selecting the sample file

Now click the Send button and review the result body:

"{\"aggregate_result\": {\"sum\": \"25821\", \"average\": \"5164.2\", \"median\": \"5622\", \"min\": \"1447\", \"max\": \"9802\"}, \"numbers_read\": [\"3049\", \"5901\", \"5622\", \"1447\", \"9802\"]}"

As we can see, the app running on a local container returned the correct results based on the sample image. So, we’re ready to prepare our Azure resources to run the Intelligent App on AKS.

Exercise

In this article, we explored the creation of an Intelligent App that leverages Azure AI Vision to analyze images and extract data. We learned how to build a Python Web API to perform OCR on uploaded images and subsequently test this API locally.

Head to the next part, to deploy this API via Azure Kubernetes Service.

+ + \ No newline at end of file diff --git a/30daysofIA/tags/azure-cosmos-db/page/4/index.html b/30daysofIA/tags/azure-cosmos-db/page/4/index.html index 8a1029373a..e986b911e0 100644 --- a/30daysofIA/tags/azure-cosmos-db/page/4/index.html +++ b/30daysofIA/tags/azure-cosmos-db/page/4/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "azure-cosmos-db" | Build Intelligent Apps On Azure +11 posts tagged with "azure-cosmos-db" | Build Intelligent Apps On Azure @@ -14,13 +14,13 @@ - - + +
-

9 posts tagged with "azure-cosmos-db"

View All Tags

· 9 min read
It's 30DaysOfIA

In the part of our series on intelligent apps, we’ll explore how AI is transforming application development, from design and architecture to building.

What We'll Cover:

  • Revolutionizing application development using AI
  • Infusing AI in application design architecture
  • AI Assisted Pair Programming
  • Future of App Development

A decorative image of application development with AI

Reimagining Application Development With AI: A New Paradigm

Artificial intelligence (AI) is impacting every industry. AI-powered development tools offer new functionalities and assist software developers throughout the development process, revolutionizing app development. From initial design and architecture to coding and debugging, AI reshapes how we approach application conception, creation, and iteration.

In part one of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” we explored the new breed of intelligent apps and how they use AI. In this article, we’ll see how AI technologies enhance developer toolkits and shape the future of app development, particularly through AI-enabled coding assistance and debugging.

The Dawn of Intelligent Apps: Revolutionizing Application Development

AI is more than just another addition to the development toolbox—it represents a revolution bringing unprecedented efficiency, innovation, and user engagement to our applications. 

What sets intelligent apps apart is how they can adapt and learn, helping us deliver unique, real-time user experiences.  

One example is an AI-driven health application that can track biometric data, predict potential health issues, provide personalized health advice, and even guide emergency services during a medical crisis. An AI-enabled language learning app that adapts to our learning speeds, recognizes our problem areas, and customizes its content to help us improve our language proficiencies is another example.  

Intelligent apps like these don’t just use data for recommendations—they can make critical decisions and provide sophisticated services that were previously impossible. 

For developers, intelligent apps mean we’re no longer just coding—we’re teaching applications how to think, adapt, and make decisions. AI is reshaping every step of the development journey, helping us to build more intuitive and user-focused products. 

Next, we’ll examine the impact of AI on application design and architecture and how AI-assisted “pair programming” changes software development.

info

Register for the FREE webinar on Intelligent Apps with Microsoft and Forrester on September 26 to explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries.

There will be a showcase of real-world use cases that demonstrate how AI can be seamlessly integrated into cloud-native environments driving tangible business value.

AI in Design and Architecture: Fueling Creativity and Efficiency

AI technologies help developers automate and streamline their processes and provide new ways to think about design.  

In traditional application development, the design and architecture phase can take significant time, effort, and expertise. For instance, adopting a monolithic architecture or a microservices-based design can significantly increase coding, testing, and debugging complexity. The app may even behave differently when hosted locally than in a deployed environment. It may take hours to track down an issue manually—time developers could spend elsewhere. 

Conversely, we can generate numerous design iterations in seconds with AI, each optimized to fulfill specific user needs and business goals. AI tools can quickly model various scenarios' impacts and software design patterns in the architectural design phase. It provides insights into performance and potential issues and suggests improvements. AI can also generate skeleton code, allowing developers to prototype different solutions or test specific components and technologies rapidly. By reviewing thousands of lines of code in seconds, AI allows us to focus on creating apps that serve users through feature development instead of searching for bugs.  

Integrating AI into the design and architecture phase can even extend our creative capabilities. By leveraging the power of generative AI—like large language models (LLMs)—and their broad knowledge and context going beyond software, we can better brainstorm apps and feature ideas. We can go beyond code and software architecture and get ideas about user experience, logo and branding, or marketing concepts. And as AI tools deliver insights and ideas we would have never considered; it encourages more innovation.

info

Watch Episode 01 of the Learn Live session to learn how to build an end-to-end intelligent app solution architecture.

The Power of AI-Assisted Pair Programming in Building Applications

AI-assisted pair programming is a collaborative coding approach where an AI system—like GitHub Copilot or TestPilot—assists developers during coding. It’s an increasingly common approach that significantly impacts developer productivity. In fact, GitHub Copilot is now behind an average of 46 percent of developers’ code and users are seeing 55 percent faster task completion on average. 

For new software developers, or those interested in learning new skills, AI-assisted pair programming are training wheels for coding. With the benefits of code snippet suggestions, developers can avoid struggling with beginner pitfalls like language syntax. Tools like ChatGPT can act as a personal, on-demand tutor—answering questions, generating code samples, and explaining complex code syntax and logic. These tools dramatically speed the learning process and help developers gain confidence in their coding abilities. 

Building applications with AI tools hastens development and provides more robust code. For example, GitHub Copilot helps us write new code accurately. It also quickly makes sense of existing code, suggests whole new lines or blocks of code, and even detects errors and proposes fixes. It’s like having a second coder working alongside us—one who never tires and is trained on billions of lines of code. 

However, it’s also important to acknowledge the limitations of AI. While generative AI-assistance technologies like GitHub Copilot are impressive, they’re still imperfect. They can sometimes produce erroneous, non-functional, or vulnerable code. They can also lack nuanced, domain-specific knowledge.  

So, although these tools are helpful, we can’t rely on them fully—these are programming aids rather than definitive solutions. AI-assisted pair programming is about augmenting the human developer’s skills, not replacing them.

Debugging and Improving Code With AI: Enhancing Application Quality

Identifying and correcting code errors is often time-consuming and challenging for developers. AI streamlines the debugging process by automatically detecting anomalies, suggesting fixes, and learning from previous bugs to prevent them from recurring. 

We can use generative AI to create unit tests from code snippets, from simple parameter checking and input validation to complex mocks of database and network services. Automated AI testing improves code coverage and robustness. It saves significant time compared to manually crafting unit tests. 

AI also revolutionizes code reviews by scanning and highlighting potential issues—like code patterns leading to a bug or a coding standards violation—and suggesting ways to simplify or refactor the code for readability and performance. This reduces human error and makes the code review process more efficient. Its benefits extend to small teams, where the availability of developers is crucial to deadlines, and large teams, where effective collaboration depends on readability and code consistency. AI further saves time by triaging issues so developers can prioritize the most mission-critical ones. 

Aside from improving code quality, AI is critical to understanding system behavior. Intelligent AI-assisted software development tools can monitor and audit system behavior, giving developers a better insight into how their code performs in different scenarios. AI can track patterns, identify inefficiencies, and provide alerts when they detect unusual behavior—enabling developers to enhance their code further. 

By predicting and identifying potential vulnerabilities, intelligent AI-assisted coding apps like Microsoft Security Copilot are pivotal in ensuring application functionality, reliability, and security. These AI tools are continually trained and upgraded with large codebases and user feedback that refines their responses. This ability to learn from past mistakes makes AI an incredibly powerful tool that will continue to expand its capabilities in the future.

info

Complete the AI Cloud Skills Challenge to build on you AI application development skills with paired programming using generative AI and GitHub Copilot.

AI and the Future of Application Development

The rapid advancements in AI and machine learning technologies point to an exciting future for application development. 

In “The Future of Applications,” software developer Mike Cann illustrates how LLMs might revolutionize application development. Cann that suggests fully-autonomous coding—AI-generated applications created entirely from prompts—may supersede traditional hand-crafted applications. Dynamically generated user interfaces could offer personalized and customizable experiences while we can design APIs for machine consumption and efficiency rather than human readability. Furthermore, we can store data in less structured, more adaptable, and flexible forms. 

AI is already making application development more accessible. With AI handling the complex, technical aspects of development, less-technical individuals can take a greater role in building applications. Consequently, AI can potentially democratize software engineering. 

Intelligent apps will continue to evolve, becoming more efficient, adaptable, and capable of learning from user interactions. Similarly, AI will inevitably become integral in every stage of app development.

Summary

AI has already created a new development paradigm—one in which machine intelligence and human creativity come together to produce innovative, efficient, and robust applications. AI already assists us at every stage of app development, from design and architecture to coding and debugging. 

Developers who embrace AI tools and integrate them into their processes can enhance their productivity, improve the quality of their applications, and expand the possibilities of their application development.  

The future of development is automated and AI-driven, and it’s here today. Getting started is easy—try an AI-powered development tool like GitHub Copilot to see how AI-assisted pair programming can enhance your productivity and code quality.

Exercise

- - +
Skip to main content

11 posts tagged with "azure-cosmos-db"

View All Tags

· 9 min read
It's 30DaysOfIA

In the last article, we created an Intelligent App that leverages Azure AI Vision to analyze images and extract data. Develop an API to perform optical character recognition (OCR) on uploaded images and testing this API locally.

In this article, we will deploy the web API to the cloud platform using Azure Kubernetes Service.

What We'll Cover:

  • Understanding Azure AI Vision and Azure Kubernetes Service
  • Build a Python Web API to perform OCR
  • Test the API locally

image depicting an Intelligent App using AI

Jumpstart Your AI Journey: Building Your First Intelligent App with Azure AI and AKS (2)

In the previous article we explored the creation of an Intelligent App that leverages Azure AI Vision to analyze images and extract data. We learned how to build a Python Web API to perform OCR on uploaded images and subsequently test this API locally.

In this article we will use Azure Kubernetes Service (AKS) to develop, publish, and maintain our app in the cloud on Azure.

Let’s get started!

Prerequisites

To follow this tutorial, ensure you have completed Jumpstart Your AI Journey: Building Your First Intelligent App with Azure AI and AKS (1).

Pushing a Container Image to Azure Container Registry (ACR)

To start, open your CLI or terminal and type the following command:

az login

Follow the instructions displayed on your browser to enter your Azure credentials.

Once authenticated, you’ll initiate a secure connection between your local environment and Azure. This process grants you access to cloud services and resources.

Next, type the command below in your terminal to set up a new Azure Container Registry (ACR) to store your container images:

az acr create --resource-group computer-vision --name <name-of-azure-container-registry> --sku Basic

Remember to replace <name-of-azure-container-registry> with your container registry name. The name must be unique within Azure and comply with these rules.

The command above creates an Azure Container Registry (ACR) in the computer-vision resource group under the Basic SKU. This ACR is your secure and private repository for storing container images within Azure.

Next, log in to the registry with the following command:

az acr login -n <name-of-azure-container-registry>

The az acr login command above lets you securely authenticate and access the specified ACR without providing their Azure credentials each time.

Now, run the following command in your terminal. It will display the endpoint URL to log in to and interact with the ACR for pushing and pulling container images.

az acr show --name <name-of-azure-container-registry> --query loginServer --output table

This returns an endpoint URL as follows:

Result
----------------------------------
<name-of-azure-container-registry>.azurecr.io

Now, run the following command to show all container images, their repository, tags, and size:

docker images
REPOSITORYTAGIMAGE IDCREATEDSIZE
intelligent-applatesta7bf9f75361716 hours ago197MB

Tags are necessary to push Docker images to a remote registry like Azure Container Registry. They also let you differentiate between different versions of the same image and upload or download the one you want.

Run the following command to tag your Docker image:

docker tag intelligent-app <name-of-azure-container-registry>.azurecr.io/intelligent-app:v1

Then, run the docker images command again to check your tagged image:

docker images
REPOSITORYTAGIMAGE IDCREATEDSIZE
intelligent-applatestc52168039265About a minute ago197MB
<name-of-azure-container-registry>.azurecr.io/intelligent-appv1c52168039265About a minute ago197MB

Now run the following command so Docker can securely upload the image to your Azure Container Registry:

docker push <name-of-azure-container-registry>.azurecr.io/intelligent-app:v1

Once we've deployed the image to the container registry, AKS can access it during deployment.

info

Watch Episode 03 for the Learn Live session to experience a guided session on how to build, test and deploy an end-to-end intelligent app solution.

Deploying the Intelligent App on Azure Kubernetes Service (AKS)

Before we deploy our Intelligent App to AKS, we need to provision an AKS cluster and define Kubernetes manifests.

To provision an AKS cluster to host our application, we specify the desired configurations for the cluster, such as the number of nodes, node size, and networking options. But first, download and install the Kubernetes command-line tools (kubectl), a client credential plugin implementing Azure authentication:

az aks install-cli

If you’re using Linux, review this tutorial. Then run the following:

sudo az aks install-cli

Next, run the following command in your terminal to enable access to the networking-related resources and services provided by the `Microsoft.Network`` namespace in Azure:

az provider register --namespace Microsoft.Network

Now, we must create an AKS cluster. Run the following command to create an AKS cluster named aks-intelligent-app in the computer-vision resource group.

az aks create --resource-group computer-vision --name aks-intelligent-app --node-count 1 --generate-ssh-keys

The command above specifies the target resource group: computer-vision. The node pool is configured with one virtual machine (VM), and the secure shell (SSH) keys for secure node access are generated automatically.

Next, run the following command to update the AKS cluster you created by attaching it to your ACR. Doing so allows the AKS cluster to pull container images from the specified ACR when deploying workloads to the cluster.

az aks update -n aks-intelligent-app -g computer-vision --attach-acr <name-of-azure-container-registry>

Then, run the command below to configure kubectl to work with our AKS cluster in the computer-vision resource group.

az aks get-credentials --resource-group computer-vision --name aks-intelligent-app

The command above retrieves the necessary credentials and context information required for kubectl to communicate with the AKS cluster.

We still have to define the Kubernetes manifests written in YAML, describing the desired state of our application deployment, including containers, networking, and scaling rules. We’ll prepare these manifests, including Deployment and Service configurations, to define how our application should be deployed and exposed.

Create a folder named Deployment in the project root directory. Next, create two files in the deployment folder: deployment.yml and service.yml.

Add the following configuration to the deployment.yml file, replacing the <name-of-azure-container-registry> placeholder with your registry’s name:

apiVersion: apps/v1
kind: Deployment
metadata:
name: intelligent-app
spec:
replicas: 1
selector:
matchLabels:
app: intelligent-app
template:
metadata:
labels:
app: intelligent-app
spec:
nodeSelector:
kubernetes.io/os: linux
containers:
- name: intelligent-app
image: <name-of-azure-container-registry>.azurecr.io/intelligent-app:v1
resources:
limits:
memory: 512Mi
cpu: "1"
requests:
memory: 256Mi
cpu: "0.2"
ports:
- containerPort: 5000
env:
- name: FLASK_DEBUG
value: "1"
- name: VISION_KEY
value: <THE-KEY-1-VALUE-FROM-YOUR-AZURE-AI-SERVICE>
- name: VISION_ENDPOINT
value: <THE-ENDPOINT-VALUE-FROM-YOUR-AZURE-AI>

Additionally, edit the VISION_KEY and VISION_ENDPOINT environment variables above according to the API key and endpoint of your Azure AI Services instance.

Then, add the following configuration to the service.yml file:

apiVersion: v1
kind: Service
metadata:
name: intelligent-app-service
spec:
type: LoadBalancer
ports:
- protocol: TCP
port: 80
targetPort: 5000
name: port5000
selector:
app: intelligent-app

Now, we’ll deploy our application using kubectl. Applying the Kubernetes manifests creates the necessary resources and deploys our containerized application to the AKS cluster.

First, change the terminal to the deployment folder:

cd Deployment 

Then, run the following command to create or update Kubernetes resources defined in the deployment.yml file:

kubectl apply -f deployment.yml

Create a Kubernetes Service resource in a Kubernetes cluster based on the configuration defined in the service.yml file using the code below:

kubectl apply -f service.yml

Once you’ve applied the resource definition and the service configuration contained in the deployment.yml and the service.yml files, open the aks-intelligent-app Kubernetes Service in the Azure Portal, select Workloads under Kubernetes resources on the sidebar, and find the deployment named intelligent-app. It must have the status “Ready 1/1”. If you encounter an issue with this status, check out these troubleshooting resources.

image of selecting the deployment in Azure Portal

Testing the Intelligent App on AKS

To test the app on AKS, first, run the command below:

kubectl get services

This command lists the Services and their corresponding details, including the Service name, cluster IP address, external IP, and ports.

NAMETYPECLUSTER-IPEXTERNAL-IPPORT(S)AGE
intelligent-app-serviceLoadBalancer10.0.77.6020.121.76.15380:30936/TCP47s
kubernetesClusterIP10.0.0.1<none>443/TCP14m

The output above shows a Kubernetes Service named intelligent-app-service with a type set to LoadBalancer. It’s reachable from within the cluster using the cluster IP 10.0.77.60 and accessible externally via the external IP 20.121.76.153 on port 80 (mapped to port 30936).

Note: Your set of IP addresses will differ. Remember to use your unique external IP address when testing with Postman.

To test the deployed app, go to Postman, replace the URL with the external IP of the Kubernetes Service you just deployed, and click Send:

mage of sending the app in Postman

As we can see, our Intelligent App has successfully deployed to AKS and is functioning on the cloud as expected.

Exercise

Next Steps

In this two-part article, we explored the creation of an Intelligent App that leverages Azure AI Vision to analyze images and extract data. We learned how to build a Python Web API to perform OCR on uploaded images and subsequently deploy this API via Azure Kubernetes Service.

Besides OCR and Image Analysis, you can continue exploring Azure’s vast array of services and experiment further with Azure AI and AKS by applying various practical uses to your Intelligent Apps, including natural language processing, speech recognition and synthesis, sentiment analysis for customer feedback, and automated content moderation.

+ + \ No newline at end of file diff --git a/30daysofIA/tags/azure-cosmos-db/page/5/index.html b/30daysofIA/tags/azure-cosmos-db/page/5/index.html index f1093c3894..f2453364f9 100644 --- a/30daysofIA/tags/azure-cosmos-db/page/5/index.html +++ b/30daysofIA/tags/azure-cosmos-db/page/5/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "azure-cosmos-db" | Build Intelligent Apps On Azure +11 posts tagged with "azure-cosmos-db" | Build Intelligent Apps On Azure @@ -14,13 +14,13 @@ - - + +
-

9 posts tagged with "azure-cosmos-db"

View All Tags

· 10 min read
It's 30DaysOfIA

Explore real-world examples of how Intelligent Apps revolutionize logistics, retail, finance, manufacturing, tech, and medicine.

What We'll Cover:

  • Real-world businesses revolutionizing operations with Intelligent Apps
  • High level architecture for industry scenarios
  • Implementation architecture overview across industries

A decorative header image for the blog

Real-world Success Stories

According to IBM’s 2022 Global AI Adoption Index, 35% of surveyed companies already use AI in their business, and 42% are exploring the idea. Organizations lagging behind the AI curve will quickly fall behind early adopters, who are already enjoying its benefits and boosting their investment in this technology.

In the first part of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” we explored how organizations can use AI-powered apps, or intelligent apps, to transform their operations, decision-making, and customer experiences, leaping ahead of the competition.

Intelligent apps significantly improve operational efficiency and customer engagement across industries like logistics, retail, finance, manufacturing, tech, and healthcare. In this article, we’ll spotlight real-world success stories of businesses revolutionizing operations with intelligent apps using Microsoft Azure’s app, data, and AI services.

info

Register for the Learn Live on September 21 for a live guided experience with SMEs on how to build end-to-end solution architecture for real world implementation of Intelligent Apps.

Streamlining Operations: Intelligent Apps in the Airline Industry

Aerospace pioneer Airbus leveraged intelligent apps to streamline its operations and innovate its services with two ground-breaking modernizations that use Azure apps, data, and AI services to reimagine pilot training and predict aircraft maintenance issues. 

Aviation technology’s rapid evolution compelled Airbus to reconceive their approach to training pilots. The aircraft manufacturer created a pilot training chatbot by combining Azure AI Services with Speech to Text and Text to Speech. The bot helps trainee pilots navigate over 6,000 pages of technical information. Pilots can ask questions verbally for quick answers, aiding their understanding of dense information and speeding up training. And when Airbus needed to accommodate its military consumers, deploying its Kubernetes cluster to a disconnected, “air-gapped” environment met the strict security requirements of military aircraft training.

A chatbot could look like the following diagram as Azure services work together to create a conversational experience. Its components could include security and governance, bot logic and user experience (UX), bot cognition and intelligence, data extract, transfer, and load (ETL) operations, quality assurance and enhancements, logging, and monitoring and reporting.

A diagram of a conversational experience enabled by Azure services

Airbus also used AI to improve its aircraft maintenance and safety. They deployed AI Anomaly Detector to gather and analyze telemetry data from multiple flights, enabling more profound insights into wear and tear on military aircraft operating in harsh conditions. This AI application helps Airbus better understand each airplane’s health. Airbus can predict and fix potential problems before they occur, improving the aircraft’s safety and operational readiness.

AI and Azure are changing how organizations like Airbus operate, even in strict, highly regulated markets with stringent security requirements. And by streamlining pilot training and staying ahead of aircraft maintenance, Airbus helps its military and commercial clients reliably deliver their goods across the globe, maintaining their critical supply chains.

Boosting Customer Service: Intelligent Apps in the Retail Industry

The retail industry has been among the first to adopt and benefit from intelligent apps. For example, the leading American used car retailer, CarMax, uses intelligent apps to transform the car shopping experience.

Before embracing AI, CarMax faced a significant challenge: Manually creating text summaries was time-consuming and labor-intensive. CarMax decided to leverage Azure OpenAI Service to automatically generate car research pages, offering customers valuable insights while enhancing the website’s search engine rankings.

This AI-driven approach has significantly streamlined the process, achieving in a few months what would have taken years manually. Also, the AI system condenses thousands of customer reviews into a few readable sentences, offering potential buyers a quick overview of others’ experiences.

CarMax has improved their customer experience while boosting operational efficiency through AI adoption. CarMax’s editorial staff focuses on creating strategic, longer-form content with the time saved, enhancing productivity, and driving more website traffic.

CarMax’s success story serves as a testament to the potential of intelligent apps in the retail industry.

Enhancing Cybersecurity: Intelligent Apps in Finance

Intelligent apps also impact the highly regulated financial services sector. Swift, a leading infrastructure provider for financial messaging services, uses intelligent apps to enhance decision-making and combat financial crime.

The massive growth of cross-border transactions and instant payment networks led to a surge in financial fraud that can cost the industry hundreds of billions of dollars every year. So, Swift and Microsoft partnered to build an anomaly detection model using federated learning techniques with Azure Machine Learning and Azure confidential computing.

Microsoft Purview helped govern data, as the federated learning method partially trained each model in a silo before aggregating them. This approach enabled Swift and Microsoft to develop a highly accurate model without copying or moving data from Swift members’ secure locations, ensuring the highest level of security and privacy.

Financial organizations can now deploy this trained ML model to run a real-time fraud detection system, lowering the cost of financial fraud.

Organizations can deploy a fraud detection system like the following example on Microsoft Azure. The system ingests data from streams or storage, runs analytics, and uses ML to detect fraud and store results. It then alerts users and other systems to the security issue.

A diagram of an example fraud detection system

Swift provides the innovative model to its banking partners, who can share data to further train the model. Azure confidential computing securely runs new models on current data to accumulate insights, generating an ongoing learning cycle. This iterative approach strengthens the global detection of fraudulent financial transactions through an increasingly faster and more accurate model. 

The collaboration between Swift and Microsoft demonstrates how combining various Azure services creates Intelligent Apps to enhance decision-making in finance. These apps solve real problems at an unprecedented global scale. 

Enhancing Efficiency: Intelligent Apps in Manufacturing

3M, known for Post-it Notes and other innovative products, wielded the power of Intelligent Apps to standardize, automate, and accelerate its sales forecasting. The company needed a unified, automated approach to replace its multiple manual methods.

It adopted Azure Machine Learning and other Azure AI Services to develop, train, and implement 1,500 custom models for each region and division, replacing manual methods. This solution delivers timely finance, sales, and marketing insights while freeing teams from time-consuming manual data manipulation tasks.

3M used Azure Machine Learning, automated machine learning (AutoML), and the Many Models Solution Accelerator to train and score numerous machine learning models in parallel. This approach significantly shortened the company’s development cycle time.

Then, 3M integrated Microsoft Power BI into their solution to compile and visualize results more effectively. Executives now have an unbiased and unfiltered view of data for better decision-making. With its improved ability to forecast sales, 3M can focus on manufacturing the products it needs efficiently.

3M’s success demonstrates how combining various Azure services can enhance efficiency in the manufacturing industry. These apps improve operational processes while enabling employees to focus on higher-level tasks. Strategic decisions are more targeted, and business conversations are more meaningful.

Driving Innovation: Intelligent Apps in the Technology Industry

As expected, the tech industry is at the forefront of leveraging Intelligent Apps to drive innovation. Elastic saw an opportunity to use intelligent apps to optimize its search services and meet customer needs more effectively. The company built a managed, secure, and scalable solution for its customers, Elastic Cloud, by leveraging Microsoft Azure.

Elastic Cloud uses Azure Machine Learning and other Azure AI Services to automate many basic management tasks, such as backing up data, upgrading, and scaling deployments. This approach enables Elastic’s customers to focus less on managing the software and more on serving their own clients. Customers can quickly deploy and manage Elastic Cloud on Kubernetes (ECK) using Azure Kubernetes Service.

As a result, Elastic’s cloud business is growing twice as fast as its on-premises solutions. Enterprise search users have reported significant improvements in system availability and employee satisfaction.

Additionally, Elastic incorporated Azure OpenAI Service into its Elasticsearch Relevance Engine (ESRE), providing more relevant answers to search queries. ESRE enables text, vector, and hybrid search, offering a more effective and accurate response to queries.

Elastic’s success story demonstrates how combining various Azure services can create intelligent Apps to drive innovation in the tech industry. These apps help improve operational processes and deliver better results quickly.

Revolutionizing Healthcare: Intelligent Apps in Medicine

Intelligent apps also drive transformative changes in the healthcare sector. Cambridgeshire and Peterborough NHS Foundation Trust, a healthcare provider in the United Kingdom, gradually shifted their on-premises systems to the cloud using Azure, improving flexibility, and lowering costs. They needed a solution for their electronic patient record (EPR) system that integrates 23 years of data across multiple systems and formats.

The Trust used Microsoft Azure and Azure Cognitive Search, including semantic search for understanding intent and contexts and cognitive capabilities for extracting insights from data, to standardize and automate data management. This approach made legacy, archive, and live data more discoverable, saving clinicians’ time. The Trust’s solution searches through millions of clinical documents in seconds, even recognizing handwritten notes without needing a separate optical character recognition (OCR) solution to extract text from images (Azure Cognitive Search automatically does it). Clinicians can now uncover previously inaccessible insights, enabling them to offer more personalized care.

The Trust also used Integration Services tools such as Azure Logic Apps to create workflows without writing code and Event Grid to connect Azure and third-party services through a publisher-subscriber model. They switched on a live feed from their new EPR system to Azure to feed real-time information into other applications. Researchers can now access anonymized data to fuel medical innovations beyond the NHS.

The healthcare provider also invested heavily in Power BI’s data visualization and analytical functions to sift through massive amounts of information. This level of data accessibility was previously impossible with their on-premises system. The new insights have significantly improved clinicians’ efficiency for better patient care.

Data accessibility and security are critical in the healthcare industry. Intelligent Apps empower organizations like the Trust to revolutionize medical care. AI capabilities helped the Trust improve its operational efficiency and user experiences, setting a new standard for healthcare in the digital age.

Summary

AI is driving transformation and real-world successes across industries. Intelligent apps are reshaping our world by streamlining operations in manufacturing and logistics, enhancing customer experience in the retail sector, improving decision-making in finance, driving innovation in tech, and revolutionizing healthcare.

As organizations adopt AI, they’re innovating in ways that were once too difficult, expensive, or outright impossible. Companies that wait to embrace intelligent apps risk the chance of falling behind. By embracing intelligent apps and leveraging the capabilities of Azure, you can harness the transformative potential of AI to remain on the cutting edge of technology.

Exercise

- - +
Skip to main content

11 posts tagged with "azure-cosmos-db"

View All Tags

· 10 min read
It's 30DaysOfIA

Explore how digital natives and Independent Software Vendors (ISVs) can transform their business operations and generate higher value for customers with generative AI and intelligent apps.

What We'll Cover:

  • Potential of Generative AI in application development
  • Challenges with Generative AI
  • Integrating Generative AI with business operations

image of generative AI and intelligent apps used in business

Unleashing Business Transformation: How Digital Natives Can Leverage Generative AI

Generative AI is no longer a technological flight of fancy. It’s here, and it’s rewriting the rules of business innovation. 

This type of AI leverages machine learning algorithms to create new data with similar characteristics to a given dataset. It goes beyond text generation using generative pretrained transformer (GPT) models, covering an array of applications that span from image synthesis to translations and more. It also powers a new class of intelligent applications, which combine AI capabilities with traditional software functions for enhanced user experiences and advanced business solutions. 

Generative AI is transforming how digital natives and independent software vendors (ISVs) operate. Generative AI offers new possibilities for creating unique business value, enhancing customer experiences, and driving growth. 

It plays a critical role in applications, improving functionality, aiding decision-making, and automating complex tasks to deliver dynamic, uniquely personalized user experiences. Generative AI also enables and automates data-driven outcomes, functions, and features.

Integrating AI capabilities into customer-facing apps and turning them into intelligent apps benefits both the customer and the business, as we discussed in Demystifying Intelligent Applications: Leveraging AI in App Development. You get faster product development workflows and better business intelligence for sales and marketing, while your users get more natural and context-aware features and interactions. 

That said, to realize generative AI’s potential, you need some strategy and knowledge of how and where it fits into your current business and operations. We’ll examine this technology’s potential to transform business operations, its risks, and rewards, and how to integrate it into your existing operations. 

Unpacking the Potential of Generative AI

Generative AI is creating new opportunities in the digital playing field. It empowers businesses to generate insights, design products, and automate processes in ways and at speeds that were previously unimaginable. You can now predict customer behavior more accurately, offer highly personalized user experiences, and prototype innovative ideas rapidly. 

For example, Microsoft used generative AI to reinvent web search in the new AI-powered Bing and Edge. The search experience has evolved from a solitary user flow of typing in keywords and scrolling through results. It’s now a conversational experience where Bing uses context to offer better quality and more personalized search queries and results. 

Similarly, Microsoft harnessed generative AI to transform its productivity suite into intelligent apps. Through Copilot for Microsoft 365, PowerPoint now empowers users to tell their stories by using DALL-E to generate images effortlessly. It also helps rephrase text-heavy slides to be more straightforward. Outlook now assists users to compose more effective emails, enabling better communication.

These are just some examples of how AI powers day-to-day work and operations. 

Businesses that ignore generative AI miss out on untapped market opportunities, letting competitors gain an edge in the digital race. By seeking ways to rethink assumptions and reinvent existing approaches, you can effectively use generative AI to create new opportunities, open products to new use cases and markets, unlock new growth and customer satisfaction levels, and increase your revenue.

info

Register for the intelligent apps webinar on Driving Business Value by Modernizing with Cloud-Native & AI with Microsoft and Forrester on September 26.

Explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries. 

The Crucial Role of Generative AI in Business Operations

Generative AI can be a powerful catalyst for business transformation. Let’s quickly tour some ways it can redefine business operations. 

Product development is traditionally a long and painstaking process. Now, tools like GitHub Copilot and TestPilot use generative AI to make coding and testing software easier, faster, and more robust. 

Similarly, machine learning as a service technology, such as Azure Machine Learning, democratizes the availability of machine learning. It helps collect, clean, and train models across datasets, making engineering and analyzing data more efficient and accessible.  

Generative AI also simplifies the process of creating intelligent apps. Previously, simulating thousands of prototypes, predicting their performance, and narrowing down to the most viable options could take weeks, months, or even years of iteration and data analysis. With generative AI, this process can be as quick as days, hours, or perhaps minutes. 

Customer experience is also ripe for transformation. Personalized recommendations drive engagement and sales, and generative AI can scale that personalization to every customer interaction using comprehensive and continually updated data. For instance, Microsoft leverages AI to enable personalization in Dynamics 365 Customer Insights. Using this tool, you can ask questions about your customers in natural language and gain valuable and actionable insights into your customer base. 

Lastly, marketing and sales can benefit from generative AI. For example, identifying current and future key markets and emerging trends helps companies stay ahead of the curve. Tools like Copilot in Power BI help you quickly understand and uncover insights about your business, enabling your team to craft targeted campaigns, create engaging content, and optimize your business strategy. 

Leveraging this full generative AI potential requires the right tools. GPT-4, for instance, can be instrumental in realizing effective business transformation. It excels in understanding context, generating human-like text, and learning from minimal data input, making it a formidable tool in any digital native's arsenal. Platforms like Azure OpenAI Service bring GPT-4’s power to your fingertips.

However, as with any powerful technology, it’s unwise to treat generative AI as a perfect solution. You must know the challenges and risks of not leveraging it effectively, including technical and human-factor considerations. 

Without generative AI, your company risks missing the opportunity to please customers via personalization, as discussed previously. You’ll miss sales opportunities via business intelligence insights, limiting your ability to grow revenue and keep up with the competition. 

One challenge of leveraging generative AI’s full potential is finding the right tools. Fortunately, you can implement generative AI in your organization without investing in infrastructure, computational power, and expertise. Platforms like Azure OpenAI Service and Azure AI Services make AI easily accessible. They also work with Azure compute and data services like Azure Functions and Azure Kubernetes Service (AKS), enabling you to build intelligent apps easily and quickly. 

However, generative AI can’t do everything. It may need human expertise and intervention to use it fully. 

For example, generative AI can create unique data. However, the data may have limited diversity and complexity. The AI may need help recognizing additional considerations or requirements specific to a problem. It may also lack context due to various factors, such as the information’s freshness. Also, since it’s an emerging field that’s potentially worth $4.4 trillion, we’re still learning about the nuances and potential regulatory impact of generative AI. 

Perhaps the most crucial and challenging aspect of adopting generative AI is an organization’s required cultural shift. Your teams must learn how to use AI as your company fosters an understanding of AI’s potential and limitations. 

Begin integrating AI literacy into your company’s DNA. Knowledge of its potential and the readily available tools can inspire your teams to incorporate generative AI into their daily workflows and business operations quickly, improve productivity, and deliver superior customer experiences. 

No company wants to be outpaced by its competitors. You can balance generative AI’s potential with your business operations by staying aware of and adapting to the dynamic AI landscape.

Achieving Harmony: Integrating Generative AI with Existing Business Operations

Integrating generative AI into existing business operations doesn’t have to be disruptive. With the correct strategy, you can do so seamlessly. Your goal should be to augment and enhance your operations, not disrupt and replace. 

For example, Microsoft Cloud technologies leveraged AI to relieve healthcare organizations’ challenges, such as talent shortage and workforce burnout. Providers automated their workflows, invested in specific applications and connectors, and enhanced support for data and operations. 

Azure AI Services, such as text analytics for health, enable healthcare organizations to understand their largely unstructured and untapped medical data. Project Health Insights leans on AI to help healthcare professionals gain actionable insights and inferences from patient data. 

Azure Health Bot, an Intelligent App, empowers organizations to build and deploy legally compliant, powerful conversational healthcare experiences to patients. 

A successful AI integration like this begins with understanding. Knowing what generative AI can do and setting realistic expectations can help you identify where and how AI can best serve your operations. 

Next, you can foster a culture of AI literacy across teams, speeding up adoption by helping them understand how it can aid their work. The more familiar your team is with AI and its possibilities, the smoother the transition. Training and education programs play a pivotal role here. 

Finally, integration requires a systematic approach. It’s not about overhauling everything at once — it's about identifying areas where AI can add the most value, then gradually experimenting and scaling its use. Successful integration stories often feature a phased approach, starting small and growing steadily. 

As we move toward a future where generative AI is a staple in business operations, remember that every journey is unique. Although there’s no one-size-fits-all solution, a strategic approach and a keen understanding of your business needs will help you navigate this new landscape successfully.

Conclusion

Generative AI offers plenty of opportunities for businesses that embrace it. It’s fueling the next generation of intelligent applications, transforming everything from product development to customer engagement to marketing. This technology is the key to unlocking unprecedented growth and innovation. 

While the journey may have challenges, the potential rewards make generative AI worth exploring. As generative AI advances, Azure AI’s broad range of tools and services will help you navigate this fascinating field.

Exercise

  • Watch Episode 02 of the serverless edition Learn Live session to learn how to build, test and deploy an end-to-end intelligent app solution.
  • Complete the Cloud Skills Challenge to build on your AI skills.
  • Register for Ask the Expert: Azure Functions on September 26 for a live Q&A with the Product Team on how to build AI apps using serverless computing.
+ + \ No newline at end of file diff --git a/30daysofIA/tags/azure-cosmos-db/page/6/index.html b/30daysofIA/tags/azure-cosmos-db/page/6/index.html index 7107f32138..f3233f21d4 100644 --- a/30daysofIA/tags/azure-cosmos-db/page/6/index.html +++ b/30daysofIA/tags/azure-cosmos-db/page/6/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "azure-cosmos-db" | Build Intelligent Apps On Azure +11 posts tagged with "azure-cosmos-db" | Build Intelligent Apps On Azure @@ -14,13 +14,13 @@ - - + +
-

9 posts tagged with "azure-cosmos-db"

View All Tags

· 8 min read
It's 30DaysOfIA

Let’s start with understanding the power of intelligent applications.

What We'll Cover:

  • What are intelligent applications?
  • Categories of Intelligent Apps
  • Breaking down the role of AI in intelligent apps

A decorative header image for the blog

Demystifying Intelligent Applications: Leveraging AI in App Development

Until recent years, when you heard the term “artificial intelligence” (AI), you might have pictured autonomous robots from sci-fi films. Now, perhaps your mind jumps to big data and machine learning (ML). Maybe you’re even thinking about the latest ChatGPT plugins and transformer architectures. Today, AI is more than these imaginations: it’s a reality increasingly woven into our day-to-day routines through intelligent applications.

Intelligent apps combine cloud-scale compute, data, and predictive or generative AI. These applications help you automate tasks and make data-driven decisions, aiding in leveraging business intelligence to gain meaningful, practical outcomes. 

For developers, the AI landscape isn’t an abstract complexity. It’s a real, tangible opportunity for evolving traditional applications, making them “intelligent,” and delivering more value to users. And with tools like Azure Kubernetes Service, Azure Cosmos DB, and Azure AI Services, stepping into the world of intelligent apps isn’t a steep climb but a steady trek across a manageable learning curve.

The Three Categories of Intelligent Applications

Before we start building, it’s critical to understand what makes an app “intelligent.” It’s not a universal definition so much as an identifier for apps that fall into one or more of the three primary categories: 

  • Outcome-based — This designation represents the broadest category of intelligent apps, whose principal value for users is the intelligent outcomes they provide. These apps process data to provide insightful results that enhance decision-making or help with tasks like trend prediction, resource management, and customer relationship management.

  • Functionality-based — This type of intelligent app takes AI integration a degree further, integrating AI as a function of the app to create or process its outputs, enhancing the app’s overall interactivity and usefulness.

  • Feature-based — This is the narrowest category, in which the app’s AI/ML component is one of its core features and primary selling points

Let’s dive more deeply into each category.

Outcome-Based Apps: Intelligent Outcomes Drive Success

Outcome-based applications are all about the results provided to the user. They function beyond merely processing data to offer insightful outcomes that drive success.

Think of a personal fitness tracker app that uses AI to analyze the user’s activity and provides actionable recommendations for achieving a goal. This type of app not only tracks your steps, sleep patterns, and heart rate but uses that data to provide you with personalized health advice. It helps you make informed lifestyle choices — the “intelligent” outcome.

Another example is a social media marketing app that uses AI for time-saving data analysis, task automation, and trend prediction. AI-based sentiment analysis can gauge customer perception while natural language processing (NLP) accurately interprets customer inquiries. Task automation can schedule posts and automate responses to common customer queries, and trend forecasting helps model and predict customer behavior to further personalize targeted promotional efforts. Building this type of intelligence into your apps provides cost-effective, actionable insights that guide sales and marketing strategies.

Functionality-Based Apps: AI-Driven Features Enhance User Experiences

As the name suggests, functionality-based applications integrate AI into how the app functions, but the end user drives the action and outcome. In this category of apps, AI takes a more active role in the user’s interaction by allowing them to use AI-created or processed output. These apps leverage AI technologies like Azure AI Services for NLP, image recognition, and pattern recognition to improve user experience.

Consider a music app that generates personalized playlists based on your listening habits or your smartphone’s camera app that uses AI to recognize faces and optimize photo settings. Even more advanced are the language translation apps that translate text from one language to another and can even understand and maintain the context and subtleties of the conversation. They use NLP to accomplish these feats.

Similarly, consider the latest e-commerce apps, whose AI-driven functionalities enable customers to try on clothes virtually, significantly enhancing the online shopping experience.

Feature-Based Apps: Integrating Advanced AI/ML Components

Finally, there are feature-based intelligent apps. These apps incorporate advanced AI/ML components like neural networks, internal large language models (LLMs), and sophisticated algorithms as core features for the user. In this type of application, AI is the primary feature for users to engage with and the primary selling point for the app.

Some examples of feature-based applications are chatbots and virtual agents built using Azure Bot Service. These robo-representatives use AI to provide more natural and human-like conversation, balancing superior efficiency with a friendlier, more personalized user experience. The new Bing is a prime example of this implementation, integrating AI to give users a knowledgeable copilot for web searches.

Similarly, OpenAI’s ChatGPT allows users access to its state-of-the-art language models available as Microsoft’s Azure OpenAI Service. It leverages the power of the highly sophisticated GPT-3 LLM to perform tasks like drafting emails, writing code, answering general knowledge questions, translating between languages, and tutoring in numerous subjects.

These apps don’t just use AI to provide outputs or improve user experiences: They push the boundaries of what’s possible in app development by integrating AI/ML components.

info

Register for the live Q&A Ask The Expert session on September 26 with the Azure Functions product engineering team.

Breaking Down the Role of AI in Intelligent Applications

Intelligent apps reflect the merging of traditional software design with the dynamic capabilities of AI. Let’s explore how AI integrates and amplifies these applications.

At its core, AI augments applications with the capacity to self-learn and self-improve. It allows you to create applications that predict, react, and adapt to a constantly changing environment. In the power grid management system for a major city, an AI-infused application can forecast power usage trends, react to anomalies, and adapt to sudden demand surges, ensuring optimal efficiency and preventing blackouts.

But AI’s role doesn’t end at prediction or decision-making: It also automates tasks. In enterprise solutions, it has moved beyond simple automation to encompass intelligent process automation (IPA). In this arena, AI algorithms understand, learn, and then automate entire digital workflows. For developers, this is a fertile ground for creating applications that can dramatically improve the operational efficiency of businesses.

Even more, AI substantially aids in development tasks. For example, there’s GitHub Copilot, an AI-powered code assistant that offers suggestions based not only on syntax but on development best practices and the intent of the code. AI has evolved from a tool you use to build apps to a partner that helps in creating them. 

Using AI to build intelligent apps isn’t about throwing out what’s worked before. It’s about enhancing the components that have the most impact and achieve the best results. Incorporating AI in apps elevates their predictive abilities, streamlines complex tasks, and even aids in the development and creation process. When you build intelligent apps, you don’t just address current problems. You anticipate future challenges and craft solutions that adapt over time.

Summary

As developers the power of AI is in our hands, and it’s our responsibility to harness its full potential. Each category tells the story of AI's transformative potential, from applications that deliver intelligent outcomes to those with AI capabilities to enrich the user experience and those pushing boundaries with advanced AI/ML technology.

Accessible and robust platforms like Azure AI can simplify the process of developing Intelligent Applications. Whether you’re refining an existing app or building a new one, just entering the world of AI, or already familiar with the landscape, take a moment to appreciate the potential that AI integration offers. You’re embracing this exciting frontier and shaping the future of app development. 

Exercise

  • Complete the Cloud Skills Challenge to build on your apps and AI skills.
  • Watch Episode 01 of the Serverless Edition Learn Live session to learn how to build an end-to-end intelligent app solution.
- - +
Skip to main content

11 posts tagged with "azure-cosmos-db"

View All Tags

· 9 min read
It's 30DaysOfIA

In the part of our series on intelligent apps, we’ll explore how AI is transforming application development, from design and architecture to building.

What We'll Cover:

  • Revolutionizing application development using AI
  • Infusing AI in application design architecture
  • AI Assisted Pair Programming
  • Future of App Development

A decorative image of application development with AI

Reimagining Application Development With AI: A New Paradigm

Artificial intelligence (AI) is impacting every industry. AI-powered development tools offer new functionalities and assist software developers throughout the development process, revolutionizing app development. From initial design and architecture to coding and debugging, AI reshapes how we approach application conception, creation, and iteration.

In part one of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” we explored the new breed of intelligent apps and how they use AI. In this article, we’ll see how AI technologies enhance developer toolkits and shape the future of app development, particularly through AI-enabled coding assistance and debugging.

The Dawn of Intelligent Apps: Revolutionizing Application Development

AI is more than just another addition to the development toolbox—it represents a revolution bringing unprecedented efficiency, innovation, and user engagement to our applications. 

What sets intelligent apps apart is how they can adapt and learn, helping us deliver unique, real-time user experiences.  

One example is an AI-driven health application that can track biometric data, predict potential health issues, provide personalized health advice, and even guide emergency services during a medical crisis. An AI-enabled language learning app that adapts to our learning speeds, recognizes our problem areas, and customizes its content to help us improve our language proficiencies is another example.  

Intelligent apps like these don’t just use data for recommendations—they can make critical decisions and provide sophisticated services that were previously impossible. 

For developers, intelligent apps mean we’re no longer just coding—we’re teaching applications how to think, adapt, and make decisions. AI is reshaping every step of the development journey, helping us to build more intuitive and user-focused products. 

Next, we’ll examine the impact of AI on application design and architecture and how AI-assisted “pair programming” changes software development.

info

Register for the FREE webinar on Intelligent Apps with Microsoft and Forrester on September 26 to explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries.

There will be a showcase of real-world use cases that demonstrate how AI can be seamlessly integrated into cloud-native environments driving tangible business value.

AI in Design and Architecture: Fueling Creativity and Efficiency

AI technologies help developers automate and streamline their processes and provide new ways to think about design.  

In traditional application development, the design and architecture phase can take significant time, effort, and expertise. For instance, adopting a monolithic architecture or a microservices-based design can significantly increase coding, testing, and debugging complexity. The app may even behave differently when hosted locally than in a deployed environment. It may take hours to track down an issue manually—time developers could spend elsewhere. 

Conversely, we can generate numerous design iterations in seconds with AI, each optimized to fulfill specific user needs and business goals. AI tools can quickly model various scenarios' impacts and software design patterns in the architectural design phase. It provides insights into performance and potential issues and suggests improvements. AI can also generate skeleton code, allowing developers to prototype different solutions or test specific components and technologies rapidly. By reviewing thousands of lines of code in seconds, AI allows us to focus on creating apps that serve users through feature development instead of searching for bugs.  

Integrating AI into the design and architecture phase can even extend our creative capabilities. By leveraging the power of generative AI—like large language models (LLMs)—and their broad knowledge and context going beyond software, we can better brainstorm apps and feature ideas. We can go beyond code and software architecture and get ideas about user experience, logo and branding, or marketing concepts. And as AI tools deliver insights and ideas we would have never considered; it encourages more innovation.

info

Watch Episode 01 of the Learn Live session to learn how to build an end-to-end intelligent app solution architecture.

The Power of AI-Assisted Pair Programming in Building Applications

AI-assisted pair programming is a collaborative coding approach where an AI system—like GitHub Copilot or TestPilot—assists developers during coding. It’s an increasingly common approach that significantly impacts developer productivity. In fact, GitHub Copilot is now behind an average of 46 percent of developers’ code and users are seeing 55 percent faster task completion on average. 

For new software developers, or those interested in learning new skills, AI-assisted pair programming are training wheels for coding. With the benefits of code snippet suggestions, developers can avoid struggling with beginner pitfalls like language syntax. Tools like ChatGPT can act as a personal, on-demand tutor—answering questions, generating code samples, and explaining complex code syntax and logic. These tools dramatically speed the learning process and help developers gain confidence in their coding abilities. 

Building applications with AI tools hastens development and provides more robust code. For example, GitHub Copilot helps us write new code accurately. It also quickly makes sense of existing code, suggests whole new lines or blocks of code, and even detects errors and proposes fixes. It’s like having a second coder working alongside us—one who never tires and is trained on billions of lines of code. 

However, it’s also important to acknowledge the limitations of AI. While generative AI-assistance technologies like GitHub Copilot are impressive, they’re still imperfect. They can sometimes produce erroneous, non-functional, or vulnerable code. They can also lack nuanced, domain-specific knowledge.  

So, although these tools are helpful, we can’t rely on them fully—these are programming aids rather than definitive solutions. AI-assisted pair programming is about augmenting the human developer’s skills, not replacing them.

Debugging and Improving Code With AI: Enhancing Application Quality

Identifying and correcting code errors is often time-consuming and challenging for developers. AI streamlines the debugging process by automatically detecting anomalies, suggesting fixes, and learning from previous bugs to prevent them from recurring. 

We can use generative AI to create unit tests from code snippets, from simple parameter checking and input validation to complex mocks of database and network services. Automated AI testing improves code coverage and robustness. It saves significant time compared to manually crafting unit tests. 

AI also revolutionizes code reviews by scanning and highlighting potential issues—like code patterns leading to a bug or a coding standards violation—and suggesting ways to simplify or refactor the code for readability and performance. This reduces human error and makes the code review process more efficient. Its benefits extend to small teams, where the availability of developers is crucial to deadlines, and large teams, where effective collaboration depends on readability and code consistency. AI further saves time by triaging issues so developers can prioritize the most mission-critical ones. 

Aside from improving code quality, AI is critical to understanding system behavior. Intelligent AI-assisted software development tools can monitor and audit system behavior, giving developers a better insight into how their code performs in different scenarios. AI can track patterns, identify inefficiencies, and provide alerts when they detect unusual behavior—enabling developers to enhance their code further. 

By predicting and identifying potential vulnerabilities, intelligent AI-assisted coding apps like Microsoft Security Copilot are pivotal in ensuring application functionality, reliability, and security. These AI tools are continually trained and upgraded with large codebases and user feedback that refines their responses. This ability to learn from past mistakes makes AI an incredibly powerful tool that will continue to expand its capabilities in the future.

info

Complete the AI Cloud Skills Challenge to build on you AI application development skills with paired programming using generative AI and GitHub Copilot.

AI and the Future of Application Development

The rapid advancements in AI and machine learning technologies point to an exciting future for application development. 

In “The Future of Applications,” software developer Mike Cann illustrates how LLMs might revolutionize application development. Cann that suggests fully-autonomous coding—AI-generated applications created entirely from prompts—may supersede traditional hand-crafted applications. Dynamically generated user interfaces could offer personalized and customizable experiences while we can design APIs for machine consumption and efficiency rather than human readability. Furthermore, we can store data in less structured, more adaptable, and flexible forms. 

AI is already making application development more accessible. With AI handling the complex, technical aspects of development, less-technical individuals can take a greater role in building applications. Consequently, AI can potentially democratize software engineering. 

Intelligent apps will continue to evolve, becoming more efficient, adaptable, and capable of learning from user interactions. Similarly, AI will inevitably become integral in every stage of app development.

Summary

AI has already created a new development paradigm—one in which machine intelligence and human creativity come together to produce innovative, efficient, and robust applications. AI already assists us at every stage of app development, from design and architecture to coding and debugging. 

Developers who embrace AI tools and integrate them into their processes can enhance their productivity, improve the quality of their applications, and expand the possibilities of their application development.  

The future of development is automated and AI-driven, and it’s here today. Getting started is easy—try an AI-powered development tool like GitHub Copilot to see how AI-assisted pair programming can enhance your productivity and code quality.

Exercise

+ + \ No newline at end of file diff --git a/30daysofIA/tags/azure-cosmos-db/page/7/index.html b/30daysofIA/tags/azure-cosmos-db/page/7/index.html index 6ce3da0e85..527c659f69 100644 --- a/30daysofIA/tags/azure-cosmos-db/page/7/index.html +++ b/30daysofIA/tags/azure-cosmos-db/page/7/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "azure-cosmos-db" | Build Intelligent Apps On Azure +11 posts tagged with "azure-cosmos-db" | Build Intelligent Apps On Azure @@ -14,14 +14,13 @@ - - + +
-

9 posts tagged with "azure-cosmos-db"

View All Tags

· 4 min read
It's 30DaysOfIA

This Fall focus on building intelligent apps using AI and cloud-native technologies. #FallForIntelligentApps brings to you a learning journey to build your skills on creating differentiated experiences while modernizing your applications. It’s time to learn it all.

What We'll Cover

image

Get Ready To #FallForIntelligentApps starting September 18!

Today, we kick off the Fall season with content and activities to skill you up on all things Intelligent Apps or AI Apps on Azure with content, events, and community interactions! Read on to learn about what is coming!

Explore Our Initiatives

We have a number of initiatives planned for the month to help you learn and skill up on relevant technologies. Click on the links to visit the relevant pages for each

image

Register for the events!

What are 4 things you can do today, to jumpstart your learning journey?

#30Days Of Intelligent Apps

#30DaysOfIA is a series of daily blog posts grouped into themed weeks - taking you from core concepts to end-to-end solution examples in 30 days. Each article will provide conceptual lessons paired with exercises and resources to help you reinforce learnings and take next steps.

This series takes you through learning journey in six stages, each building on the previous week to help you skill up in a beginner-friendly way:

We will start with defining intelligent apps and then expand on how to build with cloud-native technologies like Azure Kubernetes Service, Azure Container Apps and Azure Functions, and take you through end-to-end scenarios for real world application development. Before we dive deep on intelligent apps, here is a high-level overview of the Intelligent Apps landscape on Azure:

image

Containers on Azure services offer you a wide range of capabilities, from simplicity to control to suit your different needs.

image -image

To start with the basics for developing Kubernetes applications, explore #30DaysOfCloudNative.

Cloud-native development when paired with serverless computing enhances your solution architecture for building cost optimized, resilient applications.

image

To start with the basics for serverless computing, explore #30DaysOfServerless.

Let’s Get Started

Now you know everything! We hope you are as excited as we are to dive into a full month of active learning and doing! Don't forget to subscribe for updates in your favorite feed reader! And look out for our first Intelligent Apps post Monday!

- - +
Skip to main content

11 posts tagged with "azure-cosmos-db"

View All Tags

· 10 min read
It's 30DaysOfIA

Explore real-world examples of how Intelligent Apps revolutionize logistics, retail, finance, manufacturing, tech, and medicine.

What We'll Cover:

  • Real-world businesses revolutionizing operations with Intelligent Apps
  • High level architecture for industry scenarios
  • Implementation architecture overview across industries

A decorative header image for the blog

Real-world Success Stories

According to IBM’s 2022 Global AI Adoption Index, 35% of surveyed companies already use AI in their business, and 42% are exploring the idea. Organizations lagging behind the AI curve will quickly fall behind early adopters, who are already enjoying its benefits and boosting their investment in this technology.

In the first part of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” we explored how organizations can use AI-powered apps, or intelligent apps, to transform their operations, decision-making, and customer experiences, leaping ahead of the competition.

Intelligent apps significantly improve operational efficiency and customer engagement across industries like logistics, retail, finance, manufacturing, tech, and healthcare. In this article, we’ll spotlight real-world success stories of businesses revolutionizing operations with intelligent apps using Microsoft Azure’s app, data, and AI services.

info

Register for the Learn Live on September 21 for a live guided experience with SMEs on how to build end-to-end solution architecture for real world implementation of Intelligent Apps.

Streamlining Operations: Intelligent Apps in the Airline Industry

Aerospace pioneer Airbus leveraged intelligent apps to streamline its operations and innovate its services with two ground-breaking modernizations that use Azure apps, data, and AI services to reimagine pilot training and predict aircraft maintenance issues. 

Aviation technology’s rapid evolution compelled Airbus to reconceive their approach to training pilots. The aircraft manufacturer created a pilot training chatbot by combining Azure AI Services with Speech to Text and Text to Speech. The bot helps trainee pilots navigate over 6,000 pages of technical information. Pilots can ask questions verbally for quick answers, aiding their understanding of dense information and speeding up training. And when Airbus needed to accommodate its military consumers, deploying its Kubernetes cluster to a disconnected, “air-gapped” environment met the strict security requirements of military aircraft training.

A chatbot could look like the following diagram as Azure services work together to create a conversational experience. Its components could include security and governance, bot logic and user experience (UX), bot cognition and intelligence, data extract, transfer, and load (ETL) operations, quality assurance and enhancements, logging, and monitoring and reporting.

A diagram of a conversational experience enabled by Azure services

Airbus also used AI to improve its aircraft maintenance and safety. They deployed AI Anomaly Detector to gather and analyze telemetry data from multiple flights, enabling more profound insights into wear and tear on military aircraft operating in harsh conditions. This AI application helps Airbus better understand each airplane’s health. Airbus can predict and fix potential problems before they occur, improving the aircraft’s safety and operational readiness.

AI and Azure are changing how organizations like Airbus operate, even in strict, highly regulated markets with stringent security requirements. And by streamlining pilot training and staying ahead of aircraft maintenance, Airbus helps its military and commercial clients reliably deliver their goods across the globe, maintaining their critical supply chains.

Boosting Customer Service: Intelligent Apps in the Retail Industry

The retail industry has been among the first to adopt and benefit from intelligent apps. For example, the leading American used car retailer, CarMax, uses intelligent apps to transform the car shopping experience.

Before embracing AI, CarMax faced a significant challenge: Manually creating text summaries was time-consuming and labor-intensive. CarMax decided to leverage Azure OpenAI Service to automatically generate car research pages, offering customers valuable insights while enhancing the website’s search engine rankings.

This AI-driven approach has significantly streamlined the process, achieving in a few months what would have taken years manually. Also, the AI system condenses thousands of customer reviews into a few readable sentences, offering potential buyers a quick overview of others’ experiences.

CarMax has improved their customer experience while boosting operational efficiency through AI adoption. CarMax’s editorial staff focuses on creating strategic, longer-form content with the time saved, enhancing productivity, and driving more website traffic.

CarMax’s success story serves as a testament to the potential of intelligent apps in the retail industry.

Enhancing Cybersecurity: Intelligent Apps in Finance

Intelligent apps also impact the highly regulated financial services sector. Swift, a leading infrastructure provider for financial messaging services, uses intelligent apps to enhance decision-making and combat financial crime.

The massive growth of cross-border transactions and instant payment networks led to a surge in financial fraud that can cost the industry hundreds of billions of dollars every year. So, Swift and Microsoft partnered to build an anomaly detection model using federated learning techniques with Azure Machine Learning and Azure confidential computing.

Microsoft Purview helped govern data, as the federated learning method partially trained each model in a silo before aggregating them. This approach enabled Swift and Microsoft to develop a highly accurate model without copying or moving data from Swift members’ secure locations, ensuring the highest level of security and privacy.

Financial organizations can now deploy this trained ML model to run a real-time fraud detection system, lowering the cost of financial fraud.

Organizations can deploy a fraud detection system like the following example on Microsoft Azure. The system ingests data from streams or storage, runs analytics, and uses ML to detect fraud and store results. It then alerts users and other systems to the security issue.

A diagram of an example fraud detection system

Swift provides the innovative model to its banking partners, who can share data to further train the model. Azure confidential computing securely runs new models on current data to accumulate insights, generating an ongoing learning cycle. This iterative approach strengthens the global detection of fraudulent financial transactions through an increasingly faster and more accurate model. 

The collaboration between Swift and Microsoft demonstrates how combining various Azure services creates Intelligent Apps to enhance decision-making in finance. These apps solve real problems at an unprecedented global scale. 

Enhancing Efficiency: Intelligent Apps in Manufacturing

3M, known for Post-it Notes and other innovative products, wielded the power of Intelligent Apps to standardize, automate, and accelerate its sales forecasting. The company needed a unified, automated approach to replace its multiple manual methods.

It adopted Azure Machine Learning and other Azure AI Services to develop, train, and implement 1,500 custom models for each region and division, replacing manual methods. This solution delivers timely finance, sales, and marketing insights while freeing teams from time-consuming manual data manipulation tasks.

3M used Azure Machine Learning, automated machine learning (AutoML), and the Many Models Solution Accelerator to train and score numerous machine learning models in parallel. This approach significantly shortened the company’s development cycle time.

Then, 3M integrated Microsoft Power BI into their solution to compile and visualize results more effectively. Executives now have an unbiased and unfiltered view of data for better decision-making. With its improved ability to forecast sales, 3M can focus on manufacturing the products it needs efficiently.

3M’s success demonstrates how combining various Azure services can enhance efficiency in the manufacturing industry. These apps improve operational processes while enabling employees to focus on higher-level tasks. Strategic decisions are more targeted, and business conversations are more meaningful.

Driving Innovation: Intelligent Apps in the Technology Industry

As expected, the tech industry is at the forefront of leveraging Intelligent Apps to drive innovation. Elastic saw an opportunity to use intelligent apps to optimize its search services and meet customer needs more effectively. The company built a managed, secure, and scalable solution for its customers, Elastic Cloud, by leveraging Microsoft Azure.

Elastic Cloud uses Azure Machine Learning and other Azure AI Services to automate many basic management tasks, such as backing up data, upgrading, and scaling deployments. This approach enables Elastic’s customers to focus less on managing the software and more on serving their own clients. Customers can quickly deploy and manage Elastic Cloud on Kubernetes (ECK) using Azure Kubernetes Service.

As a result, Elastic’s cloud business is growing twice as fast as its on-premises solutions. Enterprise search users have reported significant improvements in system availability and employee satisfaction.

Additionally, Elastic incorporated Azure OpenAI Service into its Elasticsearch Relevance Engine (ESRE), providing more relevant answers to search queries. ESRE enables text, vector, and hybrid search, offering a more effective and accurate response to queries.

Elastic’s success story demonstrates how combining various Azure services can create intelligent Apps to drive innovation in the tech industry. These apps help improve operational processes and deliver better results quickly.

Revolutionizing Healthcare: Intelligent Apps in Medicine

Intelligent apps also drive transformative changes in the healthcare sector. Cambridgeshire and Peterborough NHS Foundation Trust, a healthcare provider in the United Kingdom, gradually shifted their on-premises systems to the cloud using Azure, improving flexibility, and lowering costs. They needed a solution for their electronic patient record (EPR) system that integrates 23 years of data across multiple systems and formats.

The Trust used Microsoft Azure and Azure Cognitive Search, including semantic search for understanding intent and contexts and cognitive capabilities for extracting insights from data, to standardize and automate data management. This approach made legacy, archive, and live data more discoverable, saving clinicians’ time. The Trust’s solution searches through millions of clinical documents in seconds, even recognizing handwritten notes without needing a separate optical character recognition (OCR) solution to extract text from images (Azure Cognitive Search automatically does it). Clinicians can now uncover previously inaccessible insights, enabling them to offer more personalized care.

The Trust also used Integration Services tools such as Azure Logic Apps to create workflows without writing code and Event Grid to connect Azure and third-party services through a publisher-subscriber model. They switched on a live feed from their new EPR system to Azure to feed real-time information into other applications. Researchers can now access anonymized data to fuel medical innovations beyond the NHS.

The healthcare provider also invested heavily in Power BI’s data visualization and analytical functions to sift through massive amounts of information. This level of data accessibility was previously impossible with their on-premises system. The new insights have significantly improved clinicians’ efficiency for better patient care.

Data accessibility and security are critical in the healthcare industry. Intelligent Apps empower organizations like the Trust to revolutionize medical care. AI capabilities helped the Trust improve its operational efficiency and user experiences, setting a new standard for healthcare in the digital age.

Summary

AI is driving transformation and real-world successes across industries. Intelligent apps are reshaping our world by streamlining operations in manufacturing and logistics, enhancing customer experience in the retail sector, improving decision-making in finance, driving innovation in tech, and revolutionizing healthcare.

As organizations adopt AI, they’re innovating in ways that were once too difficult, expensive, or outright impossible. Companies that wait to embrace intelligent apps risk the chance of falling behind. By embracing intelligent apps and leveraging the capabilities of Azure, you can harness the transformative potential of AI to remain on the cutting edge of technology.

Exercise

+ + \ No newline at end of file diff --git a/30daysofIA/tags/azure-cosmos-db/page/8/index.html b/30daysofIA/tags/azure-cosmos-db/page/8/index.html index eb73e9082f..901536dfaf 100644 --- a/30daysofIA/tags/azure-cosmos-db/page/8/index.html +++ b/30daysofIA/tags/azure-cosmos-db/page/8/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "azure-cosmos-db" | Build Intelligent Apps On Azure +11 posts tagged with "azure-cosmos-db" | Build Intelligent Apps On Azure @@ -14,14 +14,13 @@ - - + +
-

9 posts tagged with "azure-cosmos-db"

View All Tags

· 4 min read
It's 30DaysOfIA

Continue The Learning Journey through Fall For Intelligent Apps! 🍂

What We'll Cover

Thank you! ♥️

image

It's hard to believe that JavaScript on Azure hack-together is ending! It seems like just yesterday that we launched this initiative, and yet here we are, 15 days later, with an incredible amount of learning and growth behind us. So... it's time for a wrap!

From the bottom of our hearts, we want to thank each and every one of you for your participation, engagement, and enthusiasm. It's been truly inspiring to see the passion and dedication from this strong community, and we're honored to be a part of it. ✨

Recap of The JavaScript on Azure Global Hack-Together

As we wrap up this exciting event, we wanted to take a moment to reflect on all that we've accomplished together. Over the last 15 days, we've covered a lot of ground, from the basics of contributing to Open source to the exploration of the Contoso Real Estate project from its Frontend to its Backend and future AI implementation.

Now that the hack-together is ending, we want to make sure that you have all the resources you need to continue honing your skills in the future. Whether you're looking to make your fist contribution to open source, become an open source maintainers, collaborate with others, or simply keep learning, there are plenty of resources out there to help you achieve your goals. So, let's dive in and explore all the ways you can continue to grow your JavaScript skills on Azure!

JSonAzure Hack-together Roadmap 📍:

hack-together-roadmap (2)

Recap on past Livestreams🌟:

Day 1️⃣: Opening Keynote (Hack-together Launch): Introduction to the Contoso Real Estate Open-source project!, managing complex and complex enterprise architecture, new announcements for JavaScript developers on Azure

Day 2️⃣: GitHub Copilot & Codespaces: Introduction to your AI pair programmer (GitHub Copilot) and your virtual developer environment on the cloud (GitHub Codespaces)

Day 6️⃣: Build your Frontend using Static Web Apps as part of a complex, modern composable frontends (or micro-frontends) and cloud-native applications.

Day 9️⃣: Build a Serverless Backend using Azure Functions

Day 1️⃣3️⃣: Easily connect to an Azure Cosmos DB, exploring its benefits and how to get started

Day 1️⃣5️⃣: Being in the AI Era, we dive into the Azure OpenAI Service and how you can start to build intelligent JavaScript applications

📖 Self-Learning Resources

  1. JavaScript on Azure Global Hack Together Module collection
  2. Lets #HackTogether: Javascript On Azure Keynote
  3. Step by Step Guide: Migrating v3 to v4 programming model for Azure Functions for Node.Js Application

Continue your journey with #FallForIntelligentApps

Join us this Fall on a learning journey to explore building intelligent apps. Combine the power of AI, cloud-native app development, and cloud-scale data to build highly differentiated digital experiences. Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure. Engage in a self-paced learning adventure by following along the #30Days of Intelligent Apps series, completing the Intelligent Apps Skills Challenge, joining the Product Group for a live Ask The Expert series or building an end to end solution architecture with a live guided experience through the Learn Live series. Discover more here.

Hands-on practice: Make your first contribution to open source!

Join our GitHUb Discussion Forum to connect with developers from every part of world, see contributions from other, find collaborators and make your first contribution to a real-world project! -Don't forget to give the repo a star ⭐

Resources

All resources are accessible on our landing page

- - +
Skip to main content

11 posts tagged with "azure-cosmos-db"

View All Tags

· 8 min read
It's 30DaysOfIA

Let’s start with understanding the power of intelligent applications.

What We'll Cover:

  • What are intelligent applications?
  • Categories of Intelligent Apps
  • Breaking down the role of AI in intelligent apps

A decorative header image for the blog

Demystifying Intelligent Applications: Leveraging AI in App Development

Until recent years, when you heard the term “artificial intelligence” (AI), you might have pictured autonomous robots from sci-fi films. Now, perhaps your mind jumps to big data and machine learning (ML). Maybe you’re even thinking about the latest ChatGPT plugins and transformer architectures. Today, AI is more than these imaginations: it’s a reality increasingly woven into our day-to-day routines through intelligent applications.

Intelligent apps combine cloud-scale compute, data, and predictive or generative AI. These applications help you automate tasks and make data-driven decisions, aiding in leveraging business intelligence to gain meaningful, practical outcomes. 

For developers, the AI landscape isn’t an abstract complexity. It’s a real, tangible opportunity for evolving traditional applications, making them “intelligent,” and delivering more value to users. And with tools like Azure Kubernetes Service, Azure Cosmos DB, and Azure AI Services, stepping into the world of intelligent apps isn’t a steep climb but a steady trek across a manageable learning curve.

The Three Categories of Intelligent Applications

Before we start building, it’s critical to understand what makes an app “intelligent.” It’s not a universal definition so much as an identifier for apps that fall into one or more of the three primary categories: 

  • Outcome-based — This designation represents the broadest category of intelligent apps, whose principal value for users is the intelligent outcomes they provide. These apps process data to provide insightful results that enhance decision-making or help with tasks like trend prediction, resource management, and customer relationship management.

  • Functionality-based — This type of intelligent app takes AI integration a degree further, integrating AI as a function of the app to create or process its outputs, enhancing the app’s overall interactivity and usefulness.

  • Feature-based — This is the narrowest category, in which the app’s AI/ML component is one of its core features and primary selling points

Let’s dive more deeply into each category.

Outcome-Based Apps: Intelligent Outcomes Drive Success

Outcome-based applications are all about the results provided to the user. They function beyond merely processing data to offer insightful outcomes that drive success.

Think of a personal fitness tracker app that uses AI to analyze the user’s activity and provides actionable recommendations for achieving a goal. This type of app not only tracks your steps, sleep patterns, and heart rate but uses that data to provide you with personalized health advice. It helps you make informed lifestyle choices — the “intelligent” outcome.

Another example is a social media marketing app that uses AI for time-saving data analysis, task automation, and trend prediction. AI-based sentiment analysis can gauge customer perception while natural language processing (NLP) accurately interprets customer inquiries. Task automation can schedule posts and automate responses to common customer queries, and trend forecasting helps model and predict customer behavior to further personalize targeted promotional efforts. Building this type of intelligence into your apps provides cost-effective, actionable insights that guide sales and marketing strategies.

Functionality-Based Apps: AI-Driven Features Enhance User Experiences

As the name suggests, functionality-based applications integrate AI into how the app functions, but the end user drives the action and outcome. In this category of apps, AI takes a more active role in the user’s interaction by allowing them to use AI-created or processed output. These apps leverage AI technologies like Azure AI Services for NLP, image recognition, and pattern recognition to improve user experience.

Consider a music app that generates personalized playlists based on your listening habits or your smartphone’s camera app that uses AI to recognize faces and optimize photo settings. Even more advanced are the language translation apps that translate text from one language to another and can even understand and maintain the context and subtleties of the conversation. They use NLP to accomplish these feats.

Similarly, consider the latest e-commerce apps, whose AI-driven functionalities enable customers to try on clothes virtually, significantly enhancing the online shopping experience.

Feature-Based Apps: Integrating Advanced AI/ML Components

Finally, there are feature-based intelligent apps. These apps incorporate advanced AI/ML components like neural networks, internal large language models (LLMs), and sophisticated algorithms as core features for the user. In this type of application, AI is the primary feature for users to engage with and the primary selling point for the app.

Some examples of feature-based applications are chatbots and virtual agents built using Azure Bot Service. These robo-representatives use AI to provide more natural and human-like conversation, balancing superior efficiency with a friendlier, more personalized user experience. The new Bing is a prime example of this implementation, integrating AI to give users a knowledgeable copilot for web searches.

Similarly, OpenAI’s ChatGPT allows users access to its state-of-the-art language models available as Microsoft’s Azure OpenAI Service. It leverages the power of the highly sophisticated GPT-3 LLM to perform tasks like drafting emails, writing code, answering general knowledge questions, translating between languages, and tutoring in numerous subjects.

These apps don’t just use AI to provide outputs or improve user experiences: They push the boundaries of what’s possible in app development by integrating AI/ML components.

info

Register for the live Q&A Ask The Expert session on September 26 with the Azure Functions product engineering team.

Breaking Down the Role of AI in Intelligent Applications

Intelligent apps reflect the merging of traditional software design with the dynamic capabilities of AI. Let’s explore how AI integrates and amplifies these applications.

At its core, AI augments applications with the capacity to self-learn and self-improve. It allows you to create applications that predict, react, and adapt to a constantly changing environment. In the power grid management system for a major city, an AI-infused application can forecast power usage trends, react to anomalies, and adapt to sudden demand surges, ensuring optimal efficiency and preventing blackouts.

But AI’s role doesn’t end at prediction or decision-making: It also automates tasks. In enterprise solutions, it has moved beyond simple automation to encompass intelligent process automation (IPA). In this arena, AI algorithms understand, learn, and then automate entire digital workflows. For developers, this is a fertile ground for creating applications that can dramatically improve the operational efficiency of businesses.

Even more, AI substantially aids in development tasks. For example, there’s GitHub Copilot, an AI-powered code assistant that offers suggestions based not only on syntax but on development best practices and the intent of the code. AI has evolved from a tool you use to build apps to a partner that helps in creating them. 

Using AI to build intelligent apps isn’t about throwing out what’s worked before. It’s about enhancing the components that have the most impact and achieve the best results. Incorporating AI in apps elevates their predictive abilities, streamlines complex tasks, and even aids in the development and creation process. When you build intelligent apps, you don’t just address current problems. You anticipate future challenges and craft solutions that adapt over time.

Summary

As developers the power of AI is in our hands, and it’s our responsibility to harness its full potential. Each category tells the story of AI's transformative potential, from applications that deliver intelligent outcomes to those with AI capabilities to enrich the user experience and those pushing boundaries with advanced AI/ML technology.

Accessible and robust platforms like Azure AI can simplify the process of developing Intelligent Applications. Whether you’re refining an existing app or building a new one, just entering the world of AI, or already familiar with the landscape, take a moment to appreciate the potential that AI integration offers. You’re embracing this exciting frontier and shaping the future of app development. 

Exercise

  • Complete the Cloud Skills Challenge to build on your apps and AI skills.
  • Watch Episode 01 of the Serverless Edition Learn Live session to learn how to build an end-to-end intelligent app solution.
+ + \ No newline at end of file diff --git a/30daysofIA/tags/azure-cosmos-db/page/9/index.html b/30daysofIA/tags/azure-cosmos-db/page/9/index.html index fd2b74e207..c9967ec42d 100644 --- a/30daysofIA/tags/azure-cosmos-db/page/9/index.html +++ b/30daysofIA/tags/azure-cosmos-db/page/9/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "azure-cosmos-db" | Build Intelligent Apps On Azure +11 posts tagged with "azure-cosmos-db" | Build Intelligent Apps On Azure @@ -14,13 +14,14 @@ - - + +
-

9 posts tagged with "azure-cosmos-db"

View All Tags

· 2 min read
It's 30DaysOfIA

September is almost here - and that can only mean one thing!! It's time to 🍂 Fall for something new and exciting and spend a few weeks skilling up on relevant tools, techologies and solutions!!

Last year, we focused on #ServerlessSeptember. This year, we're building on that theme with the addition of cloud-scale Data, cloud-native Technologies and cloud-based AI integrations to help you modernize and build intelligent apps for the enterprise!

Watch this space - and join us in September to learn more!

- - +
Skip to main content

11 posts tagged with "azure-cosmos-db"

View All Tags

· 4 min read
It's 30DaysOfIA

This Fall focus on building intelligent apps using AI and cloud-native technologies. #FallForIntelligentApps brings to you a learning journey to build your skills on creating differentiated experiences while modernizing your applications. It’s time to learn it all.

What We'll Cover

image

Get Ready To #FallForIntelligentApps starting September 18!

Today, we kick off the Fall season with content and activities to skill you up on all things Intelligent Apps or AI Apps on Azure with content, events, and community interactions! Read on to learn about what is coming!

Explore Our Initiatives

We have a number of initiatives planned for the month to help you learn and skill up on relevant technologies. Click on the links to visit the relevant pages for each

image

Register for the events!

What are 4 things you can do today, to jumpstart your learning journey?

#30Days Of Intelligent Apps

#30DaysOfIA is a series of daily blog posts grouped into themed weeks - taking you from core concepts to end-to-end solution examples in 30 days. Each article will provide conceptual lessons paired with exercises and resources to help you reinforce learnings and take next steps.

This series takes you through learning journey in six stages, each building on the previous week to help you skill up in a beginner-friendly way:

We will start with defining intelligent apps and then expand on how to build with cloud-native technologies like Azure Kubernetes Service, Azure Container Apps and Azure Functions, and take you through end-to-end scenarios for real world application development. Before we dive deep on intelligent apps, here is a high-level overview of the Intelligent Apps landscape on Azure:

image

Containers on Azure services offer you a wide range of capabilities, from simplicity to control to suit your different needs.

image +image

To start with the basics for developing Kubernetes applications, explore #30DaysOfCloudNative.

Cloud-native development when paired with serverless computing enhances your solution architecture for building cost optimized, resilient applications.

image

To start with the basics for serverless computing, explore #30DaysOfServerless.

Let’s Get Started

Now you know everything! We hope you are as excited as we are to dive into a full month of active learning and doing! Don't forget to subscribe for updates in your favorite feed reader! And look out for our first Intelligent Apps post Monday!

+ + \ No newline at end of file diff --git a/30daysofIA/tags/azure-functions/index.html b/30daysofIA/tags/azure-functions/index.html index 17847025c3..1ad240139e 100644 --- a/30daysofIA/tags/azure-functions/index.html +++ b/30daysofIA/tags/azure-functions/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "azure-functions" | Build Intelligent Apps On Azure +11 posts tagged with "azure-functions" | Build Intelligent Apps On Azure @@ -14,13 +14,13 @@ - - + +
-

9 posts tagged with "azure-functions"

View All Tags

· 10 min read
It's 30DaysOfIA

Prepare your organization to adopt intelligent apps by taking a change management approach. Assess your readiness, upskill teams, create a culture of change and overcome resistance, and prepare for future evolution to pave the way for a smooth transition into an AI-centric company.

What We'll Cover:

  • Role of change management in modernizing to intelligent apps
  • Skills needed for building intelligent apps in your organization
  • How to build a culture of change to embrace AI
  • How to prepare for the future of intelligent apps and continuous evolution

image of modernizing AI solutions for intelligent apps

Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management

As we’ve discussed throughout this series, intelligent apps and generative AI are rewriting the rules of business and application development. The first article of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” explored how intelligent apps can enhance user experiences and drive businesses growth. These advancements are opening new opportunities and markets for companies ready to embrace them.

However, embracing this digital transformation is more complex than flipping a switch. It requires adjustments to the technical side, which we explored in the series’ fifth article, “Preparing the Path for Intelligent Apps: Transitioning from On-Premises/IaaS to Cloud Native Applications.” It also demands a shift in how organizations adapt to change and approach app design and development. Let’s explore that mindset transition with tips to help you make it happen.

The Role of Change Management in Transitioning to Intelligent Apps

Transitioning to intelligent apps isn’t merely a technological change. It’s a profound shift in how businesses perceive and operate. This move toward artificial intelligence (AI)-centric approach isn’t meant to replace humans with machines or radically overhaul all business operations.

Instead, intelligent apps should augment human capabilities, streamline processes, and foster an environment where innovation flourishes. They expand toolkits and techniques to include AI-assisted development tools like GitHub Copilot and AI-enabled workflows, helping us design and deliver AI-enhanced experiences.

Effective change management strategies are crucial in helping employees adapt to these new ways of working with intelligent apps, ensuring that the transition doesn’t disrupt productivity or morale.

Team and organization strategies should: 

  • Assess the impact of any proposed changes.  
  • Communicate the benefits and reasons behind the change to all affected team members.  
  • Provide the necessary training and support.  
  • Integrate the changes into the culture and daily operations. 

By incorporating this robust change management approach, you can identify and mitigate potential transition risks early. Moreover, this strategy ensures that employees are engaged throughout the process, fostering a sense of involvement and ownership. This, in turn, cultivates a culture suited for innovation through intelligent apps. 

These organizational changes focus on the following aspects: organizational readiness, skillset, culture of change and overcoming resistance, and adaptability. Let’s explore them below.

info

Register for the intelligent apps webinar on Driving Business Value by Modernizing with Cloud-Native & AI with Microsoft and Forrester on September 26. 

Explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries. 

Identifying Organizational Readiness for Intelligent Apps

The first step in preparing your organization for intelligent apps is to assess your current readiness for change. Analyze various elements of your organization, from infrastructure and workforce skills to strategic vision and overall culture.

Can your apps and systems integrate with AI platforms like Microsoft Azure AI? Is your workforce comfortable working alongside AI, or are they fearful and resistant? Does your strategic vision incorporate AI’s transformative potential, or does it fail to acknowledge AI as a critical driver of future growth?

These questions identify the challenges of integrating AI into an organization and determine the company’s readiness for intelligent apps.

Embracing intelligent apps involves more than being ready with your technological infrastructure or having the right technology or tools. You need flexible organizational structures and processes driving your business to accommodate new ideas and capabilities. Your teams must feel empowered to own and drive them. Much like Microsoft’s shift to AI-powered services—like AI-powered Bing and Edge and Microsoft 365 Copilot—embracing AI organization-wide requires a holistic vision and willingness to innovate, with teams capable of delivering on the plan.

After accessing these critical areas for organizational readiness, you can start planning a strategy for this transition to an AI-ready organization. Identify skill gaps and work to bridge them while nurturing an environment that appreciates and harnesses the potential of AI and intelligent apps.

Nurturing the Indispensable Skills for Intelligent Apps

Tasking your AI specialists with preparing your organization to use and build intelligent apps isn’t enough. Accomplishing this goal requires a cross-functional effort and a diverse skill set.

Developing intelligent apps involves data scientists managing and interpreting vast amounts of data, machine learning engineers building and training the models, and traditional developers integrating these models into usable applications. However, the impact of generative AI spans beyond creating apps. AI fluency across the entire organization—from sales and marketing to accounting and legal departments—offers substantial benefits, sparking new ideas and solutions from sometimes unexpected sources.

Teams' AI skills might roughly fall into three levels: 

  • Fundamental skills — Being familiar and efficient with using AI tools, for example, knowing how to write effective prompts for large language models (LLMs).
  • Visionary skills — Recognizing AI’s strengths and weaknesses, for example, its creativity and hallucinations, that is, unexpected output.
  • Expert skills — Managing data and training models and integrating AI technology into apps, such as incorporating the OpenAI application programming interface (API) into an Intelligent App.

Based on these levels, you can gauge your team's skill readiness to pinpoint the most significant gaps, whether in general AI knowledge across your organization or within technical development teams.

As intelligent apps become increasingly prevalent, upskilling your workforce is crucial. This transition opens a new realm of skills and capabilities for your team to master. You can offer machine learning, data analysis, and cloud computing training programs—such as Microsoft’s Azure AI Fundamentals certification—to empower your team to confidently navigate the intelligent apps landscape.

Nurturing these skills within your organization may require hiring new talent, reskilling current employees, and creating an environment that promotes learning and experimentation. Specifically, if your team understands tools like the Microsoft Azure AI platform, they could contribute significantly to developing and maintaining intelligent apps.

An upskilled team is more than a functional unit. It’s a think tank to drive innovation and improve your organization’s competitive standing in the digital marketplace.

Building a Culture of Change to Embrace AI

Even with the right people and skills in place, change remains an uphill battle for organizations committed to traditional processes. 

Transitioning to intelligent apps is as much about mindset as technology and skills. It requires fostering a culture open to change and innovation that understands AI’s transformative potential and embraces it to enhance rather than replace human capabilities. 

Leaders play a pivotal role in cultivating this mindset. As they model a positive attitude toward AI, emphasize its benefits, and address any concerns transparently, leaders significantly contribute to the organization’s readiness to adopt intelligent apps. 

The most critical factor in this transition is enabling an AI-friendly culture. It’s about encouraging curiosity, rewarding innovative thinking, and creating an environment where people thrive on learning and experimentation. 

Empowering teams to leverage practical AI tools and AI-driven data insights to aid decision-making helps motivate employees to use and explore AI’s potential, improve their skills, and contribute to the successful integration of intelligent apps. Such an environment is fertile ground to grow breakthrough ideas and innovative solutions. 

Overcoming Resistance to Change and Adopting Intelligent Apps

Despite AI’s potential, change isn’t easy. It takes effort and is especially challenging for organizations used to existing processes. This resistance to change is a significant roadblock when transitioning to intelligent apps. 

Overcoming this resistance involves:  

  • Understanding employees’ concerns. 
  • Challenging underlying assumptions about traditional systems and outdated processes that hinder change. 
  • Providing comprehensive training for workers to use AI effectively.
  • Demonstrating the benefits and opportunities of intelligent apps. 
  • Clear and continuous communication to dispel fears and misconceptions about AI.

These steps will foster greater acceptance and enthusiasm for intelligent apps in your teams. 

Preparing for the Future of Intelligent Apps and Continuous Evolution

Finally, it’s essential to understand that generative AI is just beginning to gain momentum. Adjusting to a world of intelligent apps isn’t a one-time change—it’s a continuous evolution. 

As AI technologies mature and evolve and more businesses realize their benefits, we can expect the landscape of intelligent apps to expand further. Organizations can cultivate a mindset of continuous learning, flexibility, and adaptation to prepare for this future. Staying ahead in this dynamic field requires organizations to keep up with the latest AI advancements, continually reassess their strategies, and pivot when necessary. 

The domain of intelligent apps is rapidly advancing. Organizations must prepare to navigate this dynamic landscape to stay ahead of the curve.

Summary

Preparing for AI and intelligent apps is a strategic decision. It requires thoughtful preparation and a culture that embraces change.

With the proper preparation and mindset, adopting AI can become a smoother, collaborative path to innovation for the whole organization. Integrating AI into your organization does not discard what has worked in the past. It simply enhances and augments those processes for a better future.

Consider the points above to assess your organization’s readiness and prepare to welcome intelligent apps.

In the first week of this series, you learned about intelligent apps, how organizations today find success through intelligent apps, what AI-assisted application development looks like, ways to leverage the power of generative AI, the technical changes needed to prepare for intelligent apps, and, in this article, the organizational changes required to harness the power of AI.

The future is intelligent. Use this series as a guideline to navigate challenges while preparing for and embracing AI's opportunities for an intelligent tomorrow.

Exercise

- - +
Skip to main content

11 posts tagged with "azure-functions"

View All Tags

· 10 min read
It's 30DaysOfIA

Prepare your organization to adopt intelligent apps by taking a change management approach. Assess your readiness, upskill teams, create a culture of change and overcome resistance, and prepare for future evolution to pave the way for a smooth transition into an AI-centric company.

What We'll Cover:

  • Role of change management in modernizing to intelligent apps
  • Skills needed for building intelligent apps in your organization
  • How to build a culture of change to embrace AI
  • How to prepare for the future of intelligent apps and continuous evolution

image of modernizing AI solutions for intelligent apps

Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management

As we’ve discussed throughout this series, intelligent apps and generative AI are rewriting the rules of business and application development. The first article of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” explored how intelligent apps can enhance user experiences and drive businesses growth. These advancements are opening new opportunities and markets for companies ready to embrace them.

However, embracing this digital transformation is more complex than flipping a switch. It requires adjustments to the technical side, which we explored in the series’ fifth article, “Preparing the Path for Intelligent Apps: Transitioning from On-Premises/IaaS to Cloud Native Applications.” It also demands a shift in how organizations adapt to change and approach app design and development. Let’s explore that mindset transition with tips to help you make it happen.

The Role of Change Management in Transitioning to Intelligent Apps

Transitioning to intelligent apps isn’t merely a technological change. It’s a profound shift in how businesses perceive and operate. This move toward artificial intelligence (AI)-centric approach isn’t meant to replace humans with machines or radically overhaul all business operations.

Instead, intelligent apps should augment human capabilities, streamline processes, and foster an environment where innovation flourishes. They expand toolkits and techniques to include AI-assisted development tools like GitHub Copilot and AI-enabled workflows, helping us design and deliver AI-enhanced experiences.

Effective change management strategies are crucial in helping employees adapt to these new ways of working with intelligent apps, ensuring that the transition doesn’t disrupt productivity or morale.

Team and organization strategies should: 

  • Assess the impact of any proposed changes.  
  • Communicate the benefits and reasons behind the change to all affected team members.  
  • Provide the necessary training and support.  
  • Integrate the changes into the culture and daily operations. 

By incorporating this robust change management approach, you can identify and mitigate potential transition risks early. Moreover, this strategy ensures that employees are engaged throughout the process, fostering a sense of involvement and ownership. This, in turn, cultivates a culture suited for innovation through intelligent apps. 

These organizational changes focus on the following aspects: organizational readiness, skillset, culture of change and overcoming resistance, and adaptability. Let’s explore them below.

info

Register for the intelligent apps webinar on Driving Business Value by Modernizing with Cloud-Native & AI with Microsoft and Forrester on September 26. 

Explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries. 

Identifying Organizational Readiness for Intelligent Apps

The first step in preparing your organization for intelligent apps is to assess your current readiness for change. Analyze various elements of your organization, from infrastructure and workforce skills to strategic vision and overall culture.

Can your apps and systems integrate with AI platforms like Microsoft Azure AI? Is your workforce comfortable working alongside AI, or are they fearful and resistant? Does your strategic vision incorporate AI’s transformative potential, or does it fail to acknowledge AI as a critical driver of future growth?

These questions identify the challenges of integrating AI into an organization and determine the company’s readiness for intelligent apps.

Embracing intelligent apps involves more than being ready with your technological infrastructure or having the right technology or tools. You need flexible organizational structures and processes driving your business to accommodate new ideas and capabilities. Your teams must feel empowered to own and drive them. Much like Microsoft’s shift to AI-powered services—like AI-powered Bing and Edge and Microsoft 365 Copilot—embracing AI organization-wide requires a holistic vision and willingness to innovate, with teams capable of delivering on the plan.

After accessing these critical areas for organizational readiness, you can start planning a strategy for this transition to an AI-ready organization. Identify skill gaps and work to bridge them while nurturing an environment that appreciates and harnesses the potential of AI and intelligent apps.

Nurturing the Indispensable Skills for Intelligent Apps

Tasking your AI specialists with preparing your organization to use and build intelligent apps isn’t enough. Accomplishing this goal requires a cross-functional effort and a diverse skill set.

Developing intelligent apps involves data scientists managing and interpreting vast amounts of data, machine learning engineers building and training the models, and traditional developers integrating these models into usable applications. However, the impact of generative AI spans beyond creating apps. AI fluency across the entire organization—from sales and marketing to accounting and legal departments—offers substantial benefits, sparking new ideas and solutions from sometimes unexpected sources.

Teams' AI skills might roughly fall into three levels: 

  • Fundamental skills — Being familiar and efficient with using AI tools, for example, knowing how to write effective prompts for large language models (LLMs).
  • Visionary skills — Recognizing AI’s strengths and weaknesses, for example, its creativity and hallucinations, that is, unexpected output.
  • Expert skills — Managing data and training models and integrating AI technology into apps, such as incorporating the OpenAI application programming interface (API) into an Intelligent App.

Based on these levels, you can gauge your team's skill readiness to pinpoint the most significant gaps, whether in general AI knowledge across your organization or within technical development teams.

As intelligent apps become increasingly prevalent, upskilling your workforce is crucial. This transition opens a new realm of skills and capabilities for your team to master. You can offer machine learning, data analysis, and cloud computing training programs—such as Microsoft’s Azure AI Fundamentals certification—to empower your team to confidently navigate the intelligent apps landscape.

Nurturing these skills within your organization may require hiring new talent, reskilling current employees, and creating an environment that promotes learning and experimentation. Specifically, if your team understands tools like the Microsoft Azure AI platform, they could contribute significantly to developing and maintaining intelligent apps.

An upskilled team is more than a functional unit. It’s a think tank to drive innovation and improve your organization’s competitive standing in the digital marketplace.

Building a Culture of Change to Embrace AI

Even with the right people and skills in place, change remains an uphill battle for organizations committed to traditional processes. 

Transitioning to intelligent apps is as much about mindset as technology and skills. It requires fostering a culture open to change and innovation that understands AI’s transformative potential and embraces it to enhance rather than replace human capabilities. 

Leaders play a pivotal role in cultivating this mindset. As they model a positive attitude toward AI, emphasize its benefits, and address any concerns transparently, leaders significantly contribute to the organization’s readiness to adopt intelligent apps. 

The most critical factor in this transition is enabling an AI-friendly culture. It’s about encouraging curiosity, rewarding innovative thinking, and creating an environment where people thrive on learning and experimentation. 

Empowering teams to leverage practical AI tools and AI-driven data insights to aid decision-making helps motivate employees to use and explore AI’s potential, improve their skills, and contribute to the successful integration of intelligent apps. Such an environment is fertile ground to grow breakthrough ideas and innovative solutions. 

Overcoming Resistance to Change and Adopting Intelligent Apps

Despite AI’s potential, change isn’t easy. It takes effort and is especially challenging for organizations used to existing processes. This resistance to change is a significant roadblock when transitioning to intelligent apps. 

Overcoming this resistance involves:  

  • Understanding employees’ concerns. 
  • Challenging underlying assumptions about traditional systems and outdated processes that hinder change. 
  • Providing comprehensive training for workers to use AI effectively.
  • Demonstrating the benefits and opportunities of intelligent apps. 
  • Clear and continuous communication to dispel fears and misconceptions about AI.

These steps will foster greater acceptance and enthusiasm for intelligent apps in your teams. 

Preparing for the Future of Intelligent Apps and Continuous Evolution

Finally, it’s essential to understand that generative AI is just beginning to gain momentum. Adjusting to a world of intelligent apps isn’t a one-time change—it’s a continuous evolution. 

As AI technologies mature and evolve and more businesses realize their benefits, we can expect the landscape of intelligent apps to expand further. Organizations can cultivate a mindset of continuous learning, flexibility, and adaptation to prepare for this future. Staying ahead in this dynamic field requires organizations to keep up with the latest AI advancements, continually reassess their strategies, and pivot when necessary. 

The domain of intelligent apps is rapidly advancing. Organizations must prepare to navigate this dynamic landscape to stay ahead of the curve.

Summary

Preparing for AI and intelligent apps is a strategic decision. It requires thoughtful preparation and a culture that embraces change.

With the proper preparation and mindset, adopting AI can become a smoother, collaborative path to innovation for the whole organization. Integrating AI into your organization does not discard what has worked in the past. It simply enhances and augments those processes for a better future.

Consider the points above to assess your organization’s readiness and prepare to welcome intelligent apps.

In the first week of this series, you learned about intelligent apps, how organizations today find success through intelligent apps, what AI-assisted application development looks like, ways to leverage the power of generative AI, the technical changes needed to prepare for intelligent apps, and, in this article, the organizational changes required to harness the power of AI.

The future is intelligent. Use this series as a guideline to navigate challenges while preparing for and embracing AI's opportunities for an intelligent tomorrow.

Exercise

+ + \ No newline at end of file diff --git a/30daysofIA/tags/azure-functions/page/10/index.html b/30daysofIA/tags/azure-functions/page/10/index.html new file mode 100644 index 0000000000..cc8c1225c9 --- /dev/null +++ b/30daysofIA/tags/azure-functions/page/10/index.html @@ -0,0 +1,27 @@ + + + + + +11 posts tagged with "azure-functions" | Build Intelligent Apps On Azure + + + + + + + + + + + + + + +
+

11 posts tagged with "azure-functions"

View All Tags

· 4 min read
It's 30DaysOfIA

Continue The Learning Journey through Fall For Intelligent Apps! 🍂

What We'll Cover

Thank you! ♥️

image

It's hard to believe that JavaScript on Azure hack-together is ending! It seems like just yesterday that we launched this initiative, and yet here we are, 15 days later, with an incredible amount of learning and growth behind us. So... it's time for a wrap!

From the bottom of our hearts, we want to thank each and every one of you for your participation, engagement, and enthusiasm. It's been truly inspiring to see the passion and dedication from this strong community, and we're honored to be a part of it. ✨

Recap of The JavaScript on Azure Global Hack-Together

As we wrap up this exciting event, we wanted to take a moment to reflect on all that we've accomplished together. Over the last 15 days, we've covered a lot of ground, from the basics of contributing to Open source to the exploration of the Contoso Real Estate project from its Frontend to its Backend and future AI implementation.

Now that the hack-together is ending, we want to make sure that you have all the resources you need to continue honing your skills in the future. Whether you're looking to make your fist contribution to open source, become an open source maintainers, collaborate with others, or simply keep learning, there are plenty of resources out there to help you achieve your goals. So, let's dive in and explore all the ways you can continue to grow your JavaScript skills on Azure!

JSonAzure Hack-together Roadmap 📍:

hack-together-roadmap (2)

Recap on past Livestreams🌟:

Day 1️⃣: Opening Keynote (Hack-together Launch): Introduction to the Contoso Real Estate Open-source project!, managing complex and complex enterprise architecture, new announcements for JavaScript developers on Azure

Day 2️⃣: GitHub Copilot & Codespaces: Introduction to your AI pair programmer (GitHub Copilot) and your virtual developer environment on the cloud (GitHub Codespaces)

Day 6️⃣: Build your Frontend using Static Web Apps as part of a complex, modern composable frontends (or micro-frontends) and cloud-native applications.

Day 9️⃣: Build a Serverless Backend using Azure Functions

Day 1️⃣3️⃣: Easily connect to an Azure Cosmos DB, exploring its benefits and how to get started

Day 1️⃣5️⃣: Being in the AI Era, we dive into the Azure OpenAI Service and how you can start to build intelligent JavaScript applications

📖 Self-Learning Resources

  1. JavaScript on Azure Global Hack Together Module collection
  2. Lets #HackTogether: Javascript On Azure Keynote
  3. Step by Step Guide: Migrating v3 to v4 programming model for Azure Functions for Node.Js Application

Continue your journey with #FallForIntelligentApps

Join us this Fall on a learning journey to explore building intelligent apps. Combine the power of AI, cloud-native app development, and cloud-scale data to build highly differentiated digital experiences. Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure. Engage in a self-paced learning adventure by following along the #30Days of Intelligent Apps series, completing the Intelligent Apps Skills Challenge, joining the Product Group for a live Ask The Expert series or building an end to end solution architecture with a live guided experience through the Learn Live series. Discover more here.

Hands-on practice: Make your first contribution to open source!

Join our GitHUb Discussion Forum to connect with developers from every part of world, see contributions from other, find collaborators and make your first contribution to a real-world project! +Don't forget to give the repo a star ⭐

Resources

All resources are accessible on our landing page

+ + + + \ No newline at end of file diff --git a/30daysofIA/tags/azure-functions/page/11/index.html b/30daysofIA/tags/azure-functions/page/11/index.html new file mode 100644 index 0000000000..4794b709a4 --- /dev/null +++ b/30daysofIA/tags/azure-functions/page/11/index.html @@ -0,0 +1,26 @@ + + + + + +11 posts tagged with "azure-functions" | Build Intelligent Apps On Azure + + + + + + + + + + + + + + +
+

11 posts tagged with "azure-functions"

View All Tags

· 2 min read
It's 30DaysOfIA

September is almost here - and that can only mean one thing!! It's time to 🍂 Fall for something new and exciting and spend a few weeks skilling up on relevant tools, techologies and solutions!!

Last year, we focused on #ServerlessSeptember. This year, we're building on that theme with the addition of cloud-scale Data, cloud-native Technologies and cloud-based AI integrations to help you modernize and build intelligent apps for the enterprise!

Watch this space - and join us in September to learn more!

+ + + + \ No newline at end of file diff --git a/30daysofIA/tags/azure-functions/page/2/index.html b/30daysofIA/tags/azure-functions/page/2/index.html index 77b064d522..ee8e5722dd 100644 --- a/30daysofIA/tags/azure-functions/page/2/index.html +++ b/30daysofIA/tags/azure-functions/page/2/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "azure-functions" | Build Intelligent Apps On Azure +11 posts tagged with "azure-functions" | Build Intelligent Apps On Azure @@ -14,13 +14,13 @@ - - + +
-

9 posts tagged with "azure-functions"

View All Tags

· 9 min read
It's 30DaysOfIA

Learn best practices for modernizing your on-premises or IaaS solutions to intelligent apps in the cloud.

What We'll Cover:

  • Traditional vs Intelligent Apps
  • On-Premises/IaaS vs. Cloud-Native Platforms
  • Modernization strategy for building Intelligent Apps

image of modernizing AI solutions for intelligent apps

Preparing the Path for Intelligent Apps: Transitioning from On-Premises/IaaS to Cloud-Native Applications

The proliferation of generative AI is paving the way for intelligent applications, software that leverages AI capabilities to deliver unparalleled functionality and user experience. As we covered in a previous article, “Demystifying Intelligent Applications: Leveraging AI in App Development,” intelligent Apps aren’t just products of advanced coding. They interact, learn, and evolve thanks to modern AI and machine learning (ML) breakthroughs. 

The paradigm shift from traditional to intelligent apps demands that we change our approach to the technical challenges of software and architecture design. It also necessitates adopting new ways of thinking and operating within an organization, which we explore in greater depth in “Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management.” 

This article explores the technical infrastructure powering intelligent apps, providing a roadmap to transition traditional on-premises or Infrastructure as a Service (IaaS) solutions to intelligent apps deployed to cloud native platforms and services. We’ll discover the differences between conventional and intelligent apps, options for re-architecting existing applications, and critical strategic considerations integral to modernizing the application-building approach. 

Understanding the Shift: Traditional vs. Intelligent Applications

Intelligent apps are more than just an incremental evolution from traditional apps. They involve a fundamental shift in software solution design, development, and deployment. A clear understanding of how each paradigm differs is crucial for comprehending the impact of this transition. 

Functionality and User Experience

Traditional applications, often rule-based and rigid, rely on preprogrammed operations. A traditional weather app, for example, typically presents structured, location-based forecasts using a predetermined data source. Meanwhile, an intelligent app leverages AI for data-driven decision-making and personalization. Rather than merely displaying the forecast, it integrates ML with additional data sources—like a user’s calendar and fitness data—to determine their favorite outdoor activities, provide relevant clothing suggestions, and even automatically design ideal vacations and times based on the user’s optimal weather preferences. 

Similarly, while traditional applications tend to offer a one-size-fits-all UI, intelligent apps use AI to create tailored interactions. For instance, an intelligent banking app might use generative AI to provide a voice-enabled UI that lets customers use natural language to ask about transactions and receive personalized financial advice.

Infrastructure

Intelligent apps integrating these sophisticated features require highly scalable, flexible infrastructure optimized for AI functionality and able to manage large volumes of real-time data. Unlike traditional applications deployed on-premises or in infrastructure as a service (IaaS) environments, intelligent apps demand the flexibility and scalability of cloud native architectures.

An on-premises or IaaS-based e-commerce application may struggle with connecting its data to AI services or scaling to accommodate voluminous real-time data and requests. But an intelligent app built with a cloud native approach and scalability at its core can handle variable demand much more efficiently. It also maintains its infrastructure components on demand, whether for collecting data for AI/ML consumption or user-facing features like AI-enabled customer support and personalization.

Consider an intelligent app built on Azure that creates ChatGPT-like experiences with custom data. It might connect Azure Open AI and Azure Cognitive Search to an app running on Azure Container Apps or Azure Kubernetes Service. 

The architecture might look like the following diagram based on this demo app:

diagram of a demo app architecture

While the cloud is well-equipped to handle the challenges associated with shifting to AI-centric infrastructure—handling real-time data processing and storage, integrating and managing AI services, configuring event-driven and microservices architectures, and ensuring security across the distributed environment—re-architecting a traditional app to an intelligent app requires careful planning and execution to ensure it benefits fully from the capabilities of AI and cloud computing.

On-Premises/IaaS vs. Cloud-Native Platforms

While on-premises and IaaS solutions provide substantial control over data and processes, this fine-grained control comes with setup and maintenance costs and may lack the scalability necessary for AI-driven Intelligent Apps. On-premises hosting involves purchasing and maintaining hardware, including servers and storage systems. IaaS involves leasing virtual hardware and then hiring and training IT personnel for the infrastructure’s operation, maintenance, and security.

Conversely, infrastructure built on cloud native app services with Microsoft Azure offers virtually unlimited scalability without ongoing hardware and maintenance costs. This feature is ideal for intelligent apps that need to adapt and scale to accommodate fluctuating workloads and data streams. And because these cloud infrastructures typically operate on a pay-as-you-go model, traditionally substantial upfront costs turn into predictable operating expenses, lowering both the cost and risk of launching applications.

Moreover, cloud service providers continually update their offerings, so building on sophisticated AI tools and services through the cloud, like the Azure AI Platform and orchestration capabilities with Azure Kubernetes Service, ensures that you have access to the latest AI and most advanced AI models and technologies.

Strategic Considerations for Transitioning to Intelligent Apps

When developing a strategy for transitioning to intelligent apps, you must consider costs, personnel skill development, and data management.

Monolithic architectures are unlikely to support the scale and ease of iteration required by intelligent apps, so you’ll likely need to implement code changes to support the transition. Your chosen hosting solution and the amount of data your app will handle affect cost and performance, in addition to determining privacy and compliance requirements.

An app that manages or stores terabytes or petabytes of data will require storage-optimized virtual machines or a managed database service hosting. Meanwhile, an app that processes video content or performs resource-intensive calculations should prioritize high-performance computing. Maximizing storage and performance can become extraordinarily costly.

Finally, existing applications shifting towards intelligent apps may encounter compatibility conflicts, depending on database type or the existing system’s underlying programming language. As you might imagine, a traditional app running on-premises and using a shared local database may need significant restructuring to support a cloud native architecture. You need plans for data migration, potential downtime, and training.

Next, let’s look at this transformation process.

Re-Architecting Monolithic Apps into Microservices

Shifting from monolithic applications to microservices is pivotal for transitioning to Intelligent Apps, and you can use this shift as an opportunity to transform your legacy software into intelligent apps that leverage the flexibility and power of microservices-enabled AI solutions.

Traditionally, a monolithic application (let’s use an online marketplace as an example) might handle all activities—from serving webpages to processing payments—within an on-premises server or a cloud server. However, this approach is challenging to scale and remains vulnerable to network failures or cyberattacks.

Breaking these applications into multiple services with specific responsibilities, such as authentication or notifications, can overcome these limitations. These services are typically deployed as microservices within container environments like Azure Kubernetes Service or serverless platforms like Azure Container Apps or Azure Functions using automated processes. This approach offers scalability, robustness, and improved performance, as each microservice can run independently and use its own database.

Shifting towards smaller, independent microservices in app architectures allows us to reap the benefits of AI-powered intelligent apps, for example, by using microservices to handle AI tasks such as processing natural language input, analyzing behavioral data, or personalizing content.

info

Watch Episode 1 and Episode 2 of the Learn Live series on how to build, test and deploy an end-to-end intelligent app solution using the Contoso Real Estate Sample.

Event-Driven Architectures: Knowing When to Use Them

Event-Driven Architectures (EDAs) can significantly enhance the performance and cost-effectiveness of intelligent apps. EDAs, typically implemented with microservices, respond in real time to events or state changes ranging from user interactions to real-time analytics.

Consider an intelligent app designed for personalized customer engagement. Instead of designing the application to check continuously for changes in user data, an EDA can react to such changes immediately, triggering appropriate microservices. For example, if users modify their preferences, an event could trigger an AI-driven recommendation microservice to update its suggestions immediately. This real-time responsiveness, enabled by services like Azure Event Grid, can improve user experiences and make intelligent apps more adaptive and proactive.

However, it’s worth noting that while EDAs provide significant benefits, they can introduce unique complexities, such as maintaining event consistency and debugging issues in event chains across multiple microservices. Therefore, it’s important to assess your application’s needs so you can understand where it can benefit from an EDA.

Conclusion

Transitioning to intelligent apps represents a shift in strategy, architecture, and infrastructure. By designing and re-architecting for cloud-based infrastructure, your apps gain scalability, cost-effectiveness, robust data management, access to sophisticated AI tools, enhanced security, and reduced operational overhead.  

But this transition extends well beyond its technical components. Organizations and the people working within them need equal attention. You can explore these facets in greater depth in “Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management.” 

The power of generative AI is here, and preparing for the move toward cloud-based intelligent applications will ensure that your apps remain performative and scalable and—most importantly—that your business is ready for the newest era of app innovation.

Exercise

- - +
Skip to main content

11 posts tagged with "azure-functions"

View All Tags

· 9 min read
It's 30DaysOfIA

Learn best practices for modernizing your on-premises or IaaS solutions to intelligent apps in the cloud.

What We'll Cover:

  • Traditional vs Intelligent Apps
  • On-Premises/IaaS vs. Cloud-Native Platforms
  • Modernization strategy for building Intelligent Apps

image of modernizing AI solutions for intelligent apps

Preparing the Path for Intelligent Apps: Transitioning from On-Premises/IaaS to Cloud-Native Applications

The proliferation of generative AI is paving the way for intelligent applications, software that leverages AI capabilities to deliver unparalleled functionality and user experience. As we covered in a previous article, “Demystifying Intelligent Applications: Leveraging AI in App Development,” intelligent Apps aren’t just products of advanced coding. They interact, learn, and evolve thanks to modern AI and machine learning (ML) breakthroughs. 

The paradigm shift from traditional to intelligent apps demands that we change our approach to the technical challenges of software and architecture design. It also necessitates adopting new ways of thinking and operating within an organization, which we explore in greater depth in “Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management.” 

This article explores the technical infrastructure powering intelligent apps, providing a roadmap to transition traditional on-premises or Infrastructure as a Service (IaaS) solutions to intelligent apps deployed to cloud native platforms and services. We’ll discover the differences between conventional and intelligent apps, options for re-architecting existing applications, and critical strategic considerations integral to modernizing the application-building approach. 

Understanding the Shift: Traditional vs. Intelligent Applications

Intelligent apps are more than just an incremental evolution from traditional apps. They involve a fundamental shift in software solution design, development, and deployment. A clear understanding of how each paradigm differs is crucial for comprehending the impact of this transition. 

Functionality and User Experience

Traditional applications, often rule-based and rigid, rely on preprogrammed operations. A traditional weather app, for example, typically presents structured, location-based forecasts using a predetermined data source. Meanwhile, an intelligent app leverages AI for data-driven decision-making and personalization. Rather than merely displaying the forecast, it integrates ML with additional data sources—like a user’s calendar and fitness data—to determine their favorite outdoor activities, provide relevant clothing suggestions, and even automatically design ideal vacations and times based on the user’s optimal weather preferences. 

Similarly, while traditional applications tend to offer a one-size-fits-all UI, intelligent apps use AI to create tailored interactions. For instance, an intelligent banking app might use generative AI to provide a voice-enabled UI that lets customers use natural language to ask about transactions and receive personalized financial advice.

Infrastructure

Intelligent apps integrating these sophisticated features require highly scalable, flexible infrastructure optimized for AI functionality and able to manage large volumes of real-time data. Unlike traditional applications deployed on-premises or in infrastructure as a service (IaaS) environments, intelligent apps demand the flexibility and scalability of cloud native architectures.

An on-premises or IaaS-based e-commerce application may struggle with connecting its data to AI services or scaling to accommodate voluminous real-time data and requests. But an intelligent app built with a cloud native approach and scalability at its core can handle variable demand much more efficiently. It also maintains its infrastructure components on demand, whether for collecting data for AI/ML consumption or user-facing features like AI-enabled customer support and personalization.

Consider an intelligent app built on Azure that creates ChatGPT-like experiences with custom data. It might connect Azure Open AI and Azure Cognitive Search to an app running on Azure Container Apps or Azure Kubernetes Service. 

The architecture might look like the following diagram based on this demo app:

diagram of a demo app architecture

While the cloud is well-equipped to handle the challenges associated with shifting to AI-centric infrastructure—handling real-time data processing and storage, integrating and managing AI services, configuring event-driven and microservices architectures, and ensuring security across the distributed environment—re-architecting a traditional app to an intelligent app requires careful planning and execution to ensure it benefits fully from the capabilities of AI and cloud computing.

On-Premises/IaaS vs. Cloud-Native Platforms

While on-premises and IaaS solutions provide substantial control over data and processes, this fine-grained control comes with setup and maintenance costs and may lack the scalability necessary for AI-driven Intelligent Apps. On-premises hosting involves purchasing and maintaining hardware, including servers and storage systems. IaaS involves leasing virtual hardware and then hiring and training IT personnel for the infrastructure’s operation, maintenance, and security.

Conversely, infrastructure built on cloud native app services with Microsoft Azure offers virtually unlimited scalability without ongoing hardware and maintenance costs. This feature is ideal for intelligent apps that need to adapt and scale to accommodate fluctuating workloads and data streams. And because these cloud infrastructures typically operate on a pay-as-you-go model, traditionally substantial upfront costs turn into predictable operating expenses, lowering both the cost and risk of launching applications.

Moreover, cloud service providers continually update their offerings, so building on sophisticated AI tools and services through the cloud, like the Azure AI Platform and orchestration capabilities with Azure Kubernetes Service, ensures that you have access to the latest AI and most advanced AI models and technologies.

Strategic Considerations for Transitioning to Intelligent Apps

When developing a strategy for transitioning to intelligent apps, you must consider costs, personnel skill development, and data management.

Monolithic architectures are unlikely to support the scale and ease of iteration required by intelligent apps, so you’ll likely need to implement code changes to support the transition. Your chosen hosting solution and the amount of data your app will handle affect cost and performance, in addition to determining privacy and compliance requirements.

An app that manages or stores terabytes or petabytes of data will require storage-optimized virtual machines or a managed database service hosting. Meanwhile, an app that processes video content or performs resource-intensive calculations should prioritize high-performance computing. Maximizing storage and performance can become extraordinarily costly.

Finally, existing applications shifting towards intelligent apps may encounter compatibility conflicts, depending on database type or the existing system’s underlying programming language. As you might imagine, a traditional app running on-premises and using a shared local database may need significant restructuring to support a cloud native architecture. You need plans for data migration, potential downtime, and training.

Next, let’s look at this transformation process.

Re-Architecting Monolithic Apps into Microservices

Shifting from monolithic applications to microservices is pivotal for transitioning to Intelligent Apps, and you can use this shift as an opportunity to transform your legacy software into intelligent apps that leverage the flexibility and power of microservices-enabled AI solutions.

Traditionally, a monolithic application (let’s use an online marketplace as an example) might handle all activities—from serving webpages to processing payments—within an on-premises server or a cloud server. However, this approach is challenging to scale and remains vulnerable to network failures or cyberattacks.

Breaking these applications into multiple services with specific responsibilities, such as authentication or notifications, can overcome these limitations. These services are typically deployed as microservices within container environments like Azure Kubernetes Service or serverless platforms like Azure Container Apps or Azure Functions using automated processes. This approach offers scalability, robustness, and improved performance, as each microservice can run independently and use its own database.

Shifting towards smaller, independent microservices in app architectures allows us to reap the benefits of AI-powered intelligent apps, for example, by using microservices to handle AI tasks such as processing natural language input, analyzing behavioral data, or personalizing content.

info

Watch Episode 1 and Episode 2 of the Learn Live series on how to build, test and deploy an end-to-end intelligent app solution using the Contoso Real Estate Sample.

Event-Driven Architectures: Knowing When to Use Them

Event-Driven Architectures (EDAs) can significantly enhance the performance and cost-effectiveness of intelligent apps. EDAs, typically implemented with microservices, respond in real time to events or state changes ranging from user interactions to real-time analytics.

Consider an intelligent app designed for personalized customer engagement. Instead of designing the application to check continuously for changes in user data, an EDA can react to such changes immediately, triggering appropriate microservices. For example, if users modify their preferences, an event could trigger an AI-driven recommendation microservice to update its suggestions immediately. This real-time responsiveness, enabled by services like Azure Event Grid, can improve user experiences and make intelligent apps more adaptive and proactive.

However, it’s worth noting that while EDAs provide significant benefits, they can introduce unique complexities, such as maintaining event consistency and debugging issues in event chains across multiple microservices. Therefore, it’s important to assess your application’s needs so you can understand where it can benefit from an EDA.

Conclusion

Transitioning to intelligent apps represents a shift in strategy, architecture, and infrastructure. By designing and re-architecting for cloud-based infrastructure, your apps gain scalability, cost-effectiveness, robust data management, access to sophisticated AI tools, enhanced security, and reduced operational overhead.  

But this transition extends well beyond its technical components. Organizations and the people working within them need equal attention. You can explore these facets in greater depth in “Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management.” 

The power of generative AI is here, and preparing for the move toward cloud-based intelligent applications will ensure that your apps remain performative and scalable and—most importantly—that your business is ready for the newest era of app innovation.

Exercise

+ + \ No newline at end of file diff --git a/30daysofIA/tags/azure-functions/page/3/index.html b/30daysofIA/tags/azure-functions/page/3/index.html index a416baed41..d6fbadf14b 100644 --- a/30daysofIA/tags/azure-functions/page/3/index.html +++ b/30daysofIA/tags/azure-functions/page/3/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "azure-functions" | Build Intelligent Apps On Azure +11 posts tagged with "azure-functions" | Build Intelligent Apps On Azure @@ -14,13 +14,13 @@ - - + +
-

9 posts tagged with "azure-functions"

View All Tags

· 10 min read
It's 30DaysOfIA

Explore how digital natives and Independent Software Vendors (ISVs) can transform their business operations and generate higher value for customers with generative AI and intelligent apps.

What We'll Cover:

  • Potential of Generative AI in application development
  • Challenges with Generative AI
  • Integrating Generative AI with business operations

image of generative AI and intelligent apps used in business

Unleashing Business Transformation: How Digital Natives Can Leverage Generative AI

Generative AI is no longer a technological flight of fancy. It’s here, and it’s rewriting the rules of business innovation. 

This type of AI leverages machine learning algorithms to create new data with similar characteristics to a given dataset. It goes beyond text generation using generative pretrained transformer (GPT) models, covering an array of applications that span from image synthesis to translations and more. It also powers a new class of intelligent applications, which combine AI capabilities with traditional software functions for enhanced user experiences and advanced business solutions. 

Generative AI is transforming how digital natives and independent software vendors (ISVs) operate. Generative AI offers new possibilities for creating unique business value, enhancing customer experiences, and driving growth. 

It plays a critical role in applications, improving functionality, aiding decision-making, and automating complex tasks to deliver dynamic, uniquely personalized user experiences. Generative AI also enables and automates data-driven outcomes, functions, and features.

Integrating AI capabilities into customer-facing apps and turning them into intelligent apps benefits both the customer and the business, as we discussed in Demystifying Intelligent Applications: Leveraging AI in App Development. You get faster product development workflows and better business intelligence for sales and marketing, while your users get more natural and context-aware features and interactions. 

That said, to realize generative AI’s potential, you need some strategy and knowledge of how and where it fits into your current business and operations. We’ll examine this technology’s potential to transform business operations, its risks, and rewards, and how to integrate it into your existing operations. 

Unpacking the Potential of Generative AI

Generative AI is creating new opportunities in the digital playing field. It empowers businesses to generate insights, design products, and automate processes in ways and at speeds that were previously unimaginable. You can now predict customer behavior more accurately, offer highly personalized user experiences, and prototype innovative ideas rapidly. 

For example, Microsoft used generative AI to reinvent web search in the new AI-powered Bing and Edge. The search experience has evolved from a solitary user flow of typing in keywords and scrolling through results. It’s now a conversational experience where Bing uses context to offer better quality and more personalized search queries and results. 

Similarly, Microsoft harnessed generative AI to transform its productivity suite into intelligent apps. Through Copilot for Microsoft 365, PowerPoint now empowers users to tell their stories by using DALL-E to generate images effortlessly. It also helps rephrase text-heavy slides to be more straightforward. Outlook now assists users to compose more effective emails, enabling better communication.

These are just some examples of how AI powers day-to-day work and operations. 

Businesses that ignore generative AI miss out on untapped market opportunities, letting competitors gain an edge in the digital race. By seeking ways to rethink assumptions and reinvent existing approaches, you can effectively use generative AI to create new opportunities, open products to new use cases and markets, unlock new growth and customer satisfaction levels, and increase your revenue.

info

Register for the intelligent apps webinar on Driving Business Value by Modernizing with Cloud-Native & AI with Microsoft and Forrester on September 26.

Explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries. 

The Crucial Role of Generative AI in Business Operations

Generative AI can be a powerful catalyst for business transformation. Let’s quickly tour some ways it can redefine business operations. 

Product development is traditionally a long and painstaking process. Now, tools like GitHub Copilot and TestPilot use generative AI to make coding and testing software easier, faster, and more robust. 

Similarly, machine learning as a service technology, such as Azure Machine Learning, democratizes the availability of machine learning. It helps collect, clean, and train models across datasets, making engineering and analyzing data more efficient and accessible.  

Generative AI also simplifies the process of creating intelligent apps. Previously, simulating thousands of prototypes, predicting their performance, and narrowing down to the most viable options could take weeks, months, or even years of iteration and data analysis. With generative AI, this process can be as quick as days, hours, or perhaps minutes. 

Customer experience is also ripe for transformation. Personalized recommendations drive engagement and sales, and generative AI can scale that personalization to every customer interaction using comprehensive and continually updated data. For instance, Microsoft leverages AI to enable personalization in Dynamics 365 Customer Insights. Using this tool, you can ask questions about your customers in natural language and gain valuable and actionable insights into your customer base. 

Lastly, marketing and sales can benefit from generative AI. For example, identifying current and future key markets and emerging trends helps companies stay ahead of the curve. Tools like Copilot in Power BI help you quickly understand and uncover insights about your business, enabling your team to craft targeted campaigns, create engaging content, and optimize your business strategy. 

Leveraging this full generative AI potential requires the right tools. GPT-4, for instance, can be instrumental in realizing effective business transformation. It excels in understanding context, generating human-like text, and learning from minimal data input, making it a formidable tool in any digital native's arsenal. Platforms like Azure OpenAI Service bring GPT-4’s power to your fingertips.

However, as with any powerful technology, it’s unwise to treat generative AI as a perfect solution. You must know the challenges and risks of not leveraging it effectively, including technical and human-factor considerations. 

Without generative AI, your company risks missing the opportunity to please customers via personalization, as discussed previously. You’ll miss sales opportunities via business intelligence insights, limiting your ability to grow revenue and keep up with the competition. 

One challenge of leveraging generative AI’s full potential is finding the right tools. Fortunately, you can implement generative AI in your organization without investing in infrastructure, computational power, and expertise. Platforms like Azure OpenAI Service and Azure AI Services make AI easily accessible. They also work with Azure compute and data services like Azure Functions and Azure Kubernetes Service (AKS), enabling you to build intelligent apps easily and quickly. 

However, generative AI can’t do everything. It may need human expertise and intervention to use it fully. 

For example, generative AI can create unique data. However, the data may have limited diversity and complexity. The AI may need help recognizing additional considerations or requirements specific to a problem. It may also lack context due to various factors, such as the information’s freshness. Also, since it’s an emerging field that’s potentially worth $4.4 trillion, we’re still learning about the nuances and potential regulatory impact of generative AI. 

Perhaps the most crucial and challenging aspect of adopting generative AI is an organization’s required cultural shift. Your teams must learn how to use AI as your company fosters an understanding of AI’s potential and limitations. 

Begin integrating AI literacy into your company’s DNA. Knowledge of its potential and the readily available tools can inspire your teams to incorporate generative AI into their daily workflows and business operations quickly, improve productivity, and deliver superior customer experiences. 

No company wants to be outpaced by its competitors. You can balance generative AI’s potential with your business operations by staying aware of and adapting to the dynamic AI landscape.

Achieving Harmony: Integrating Generative AI with Existing Business Operations

Integrating generative AI into existing business operations doesn’t have to be disruptive. With the correct strategy, you can do so seamlessly. Your goal should be to augment and enhance your operations, not disrupt and replace. 

For example, Microsoft Cloud technologies leveraged AI to relieve healthcare organizations’ challenges, such as talent shortage and workforce burnout. Providers automated their workflows, invested in specific applications and connectors, and enhanced support for data and operations. 

Azure AI Services, such as text analytics for health, enable healthcare organizations to understand their largely unstructured and untapped medical data. Project Health Insights leans on AI to help healthcare professionals gain actionable insights and inferences from patient data. 

Azure Health Bot, an Intelligent App, empowers organizations to build and deploy legally compliant, powerful conversational healthcare experiences to patients. 

A successful AI integration like this begins with understanding. Knowing what generative AI can do and setting realistic expectations can help you identify where and how AI can best serve your operations. 

Next, you can foster a culture of AI literacy across teams, speeding up adoption by helping them understand how it can aid their work. The more familiar your team is with AI and its possibilities, the smoother the transition. Training and education programs play a pivotal role here. 

Finally, integration requires a systematic approach. It’s not about overhauling everything at once — it's about identifying areas where AI can add the most value, then gradually experimenting and scaling its use. Successful integration stories often feature a phased approach, starting small and growing steadily. 

As we move toward a future where generative AI is a staple in business operations, remember that every journey is unique. Although there’s no one-size-fits-all solution, a strategic approach and a keen understanding of your business needs will help you navigate this new landscape successfully.

Conclusion

Generative AI offers plenty of opportunities for businesses that embrace it. It’s fueling the next generation of intelligent applications, transforming everything from product development to customer engagement to marketing. This technology is the key to unlocking unprecedented growth and innovation. 

While the journey may have challenges, the potential rewards make generative AI worth exploring. As generative AI advances, Azure AI’s broad range of tools and services will help you navigate this fascinating field.

Exercise

  • Watch Episode 02 of the serverless edition Learn Live session to learn how to build, test and deploy an end-to-end intelligent app solution.
  • Complete the Cloud Skills Challenge to build on your AI skills.
  • Register for Ask the Expert: Azure Functions on September 26 for a live Q&A with the Product Team on how to build AI apps using serverless computing.
- - +
Skip to main content

11 posts tagged with "azure-functions"

View All Tags

· 12 min read
It's 30DaysOfIA

Delve into creating an Intelligent App that leverages Azure AI Vision to analyze images and extract data. Develop an API to perform optical character recognition (OCR) on uploaded images and testing this API locally.

What We'll Cover:

  • Understanding Azure AI Vision and Azure Kubernetes Service
  • Build a Python Web API to perform OCR
  • Test the API locally

image depicting an Intelligent App using AI

Jumpstart Your AI Journey: Building Your First Intelligent App with Azure AI and AKS (1)

An Intelligent App is a software application that uses artificial intelligence (AI), such as machine learning (ML), to extract meaningful insights, enabling users to make informed decisions based on the extracted text, data, sound, images, and more.

Intelligent Apps can employ advanced algorithms and models to recognize objects, patterns, and features within images, allowing them to perform tasks like object identification, image classification, facial recognition, and image segmentation.

To explore the power of Intelligent Apps, let’s build a Python app that performs optical character recognition (OCR) on images containing numbers and performs aggregate functions on the extracted numbers, like sum and average. We’ll leverage Azure AI Services for the OCR functionality and Azure Kubernetes Service (AKS) to develop, publish, and maintain a cloud-based, scalable app in Azure.

Let’s get started!

Understanding Azure AI Vision and Azure Kubernetes Service

Azure AI Vision is a cloud-based service that offers a range of advanced computer vision capabilities enabling applications to interpret content, such as images and videos, using ML and other AI algorithms. Azure computer vision lets users integrate powerful vision features into their projects without ML experience.

OCR is one of the critical functionalities within Azure vision service, allowing applications to extract text from images. Within Azure AI Vision service, OCR offers several features, including text extraction, multilingual support, layout analysis of documents, and integration with Azure services, such as Azure Functions and Azure Machine Learning.

info

Watch the intelligent apps webinar on Driving Business Value by Modernizing with Cloud-Native & AI with Microsoft and Forrester on September 26.

AKS is Microsoft Azure’s container orchestration platform that enables us to quickly and efficiently deploy, manage, and scale containerized applications, such as our Intelligent App.

Let’s explore how to leverage these tools to build an Intelligent App that uses Azure AI Vision to analyze images and extract data.

Prerequisites

To follow this tutorial, ensure you have the following:

For a preview of this final project, take a look at the complete project code.

Building the API with Azure AI Vision Service

First, log in to your Azure account and navigate to the Azure Portal.

Click Create a resource and search for “resource group.” Create a new resource group named computer-vision.

image in Azure Portal of creating a new resource group

Return to the Azure Portal home page and click Create a resource.

image in Azure Portal of creating a new resource

Search for “computer vision” and select it from the results. Click Create.

image of searching for resource group in Azure Portal

Clicking Create displays the Create Computer Vision wizard. Select the resource group you just created as the Resource group from the dropdown. Enter a resource name similar to what is seen in the screenshot below. Note that this must be a unique name so choose one that is not currently in use. Select Standard S1 as the pricing tier and check the Responsible use of AI box in the field at the bottom.

Image of selecting the Responsible use of AI box

Image of project details

Click Review + Create, then click Create.

Next, get the API key and endpoint from the Azure AI services multi-service account you created. Once the resource is created, navigate to the resource details page. Under Resource Management, select Keys and Endpoint. Once on the Keys and Endpoint page, you’ll find the Key 1 and Endpoint values. These credentials are necessary to access the Azure AI APIs.

image of selecting Keys and Endpoint

second image of selecting Keys and Endpoint

Configuring the Local Environment Variables

Next, we need to define a local environment variable file. This convenient way to store our API keys in a simple text file enables us to manage sensitive information and separate configuration from our API code.

If you haven’t done so already, clone the starter project template from GitHub into your local computer. Open the starter project template in a code editor and create a new .env file in the root folder.

Note: Root folder in this case is the “/Microsoft_Series17-18_Code/intelligent-app-before" folder.

Add the following key-value pairs to the file, remembering to replace the placeholder values with those you obtained from the previous step.

VISION_KEY=<THE-KEY-1-VALUE-FROM-YOUR-AZURE-AI-SERVICE>
VISION_ENDPOINT=<THE-ENDPOINT-VALUE-FROM-YOUR-AZURE-AI-SERVICE>

Reviewing the Quickstart Code

Let’s review the app.py file in the starter project template.

The app.py file works as the entry point for the Flask application. It is responsible for initializing the web server, listening for incoming requests, dispatching them to the appropriate route handlers, and generating responses.

Below is the code contained in the app.py file:

import os
import json

from flask import Flask, request
from flask_restful import Resource, Api
from werkzeug.utils import secure_filename

app = Flask(__name__,
static_url_path='',
static_folder='static/files')

api = Api(app)

app = Flask(__name__)

app.config['UPLOAD_FOLDER'] = 'files'

api = Api(app)

class UploadHandler(Resource):

def allowed_file(self, filename):
return '.' in filename and \
filename.rsplit('.', 1)[1].lower() in {'png'}

def post(self):
form = request.form.to_dict()

if 'file' not in request.files:
return json.dumps({ "success": False, "error": "No file part"})

file = request.files.get("file")
if file and self.allowed_file(file.filename):
filename = secure_filename(file.filename)
upload_folder = "static/files"
if not os.path.exists(upload_folder):
os.makedirs(upload_folder)
local_file_path = os.path.join(upload_folder, filename)
file.save(local_file_path)

return f"File {filename} uploaded successfully to folder: {upload_folder}"

api.add_resource(UploadHandler, "/")

if __name__ == '__main__':
app.run(debug=True)

The UploadHandler class above is defined as a Flask RESTful resource to handle file uploads. The class contains two methods:

  • The allowed_file method checks whether the file extension is allowed for upload. In this case, only .png files are allowed.

  • The post method handles HTTP POST requests for file uploads. It saves the uploaded .png file to the static/files folder.

Finally, the application returns a text response informing us that the file has been uploaded successfully.

Implementing the REST API in Python

To implement image analysis in your REST API, open the starter project template in your terminal, create a virtual environment, and activate it.

Next, install the Azure AI Vision SDK:

pip install azure-ai-vision

Then, add the following line to the requirements.txt file to include the azure-ai-vision package:

azure-ai-vision==0.13.0b1

Now, create an ocr_helper.py file in the project’s root folder. This file is a Python module that provides functions for processing images using OCR. Add the following code to the file:

import os
from statistics import median
from decimal import Decimal
import azure.ai.vision as sdk

def process_ocr(source_image):
service_options = sdk.VisionServiceOptions(os.environ["VISION_ENDPOINT"],
os.environ["VISION_KEY"])

vision_source = sdk.VisionSource(filename=source_image)

analysis_options = sdk.ImageAnalysisOptions()

analysis_options.features = (
sdk.ImageAnalysisFeature.CAPTION |
sdk.ImageAnalysisFeature.TEXT
)

analysis_options.language = "en"

analysis_options.gender_neutral_caption = True

image_analyzer = sdk.ImageAnalyzer(service_options, vision_source, analysis_options)

result = image_analyzer.analyze()

ocr_result = get_ocr_result(result)

return ocr_result

def get_ocr_result(result):
string_list = []

if result.reason != sdk.ImageAnalysisResultReason.ANALYZED:
return sdk.ImageAnalysisErrorDetails.from_result(result)
else:
if result.text is not None:
for line in result.text.lines:
for word in line.words:
string_list.append(word.content)

number_list = convert_to_decimal_list(string_list)

aggregate_result = aggregate_operations(number_list)

return {
"aggregate_result": aggregate_result,
"numbers_read": string_list
}

def convert_to_decimal_list(string_list):
return list(map(Decimal, string_list))

def aggregate_operations(numbers):
result = {
'sum': sum(numbers),
'average': sum(numbers) / len(numbers),
'median': median(numbers),
'min': min(numbers),
'max': max(numbers)
`}`
`return result`

This module uses the azure-ai-vision package to analyze images, including capturing captions and extracting text from the image. The process_ocr function inputs an image file and performs the OCR analysis. The module is a convenient tool for analyzing text-based data in pictures and performing numerical computations based on that data.

Let’s review the different components of the ocr_helper.py module listed above and explore how its functions help our Intelligent App process images and perform aggregate operations:

  • The process_ocr function takes the parameter source_image, which is the path of the image to be processed. The function then initializes the VisionServiceOptions using environment variables VISION_ENDPOINT and VISION_KEY to connect to the Azure AI Vision API.
  • The process_ocr function creates a VisionSource object with the specified source_image file name. ImageAnalysisOptions specify the features to be analyzed, including CAPTION and TEXT. The language is set to English ("en"), and gender-neutral captions are enabled.
  • Finally, an ImageAnalyzer object is created with the service options, vision source, and analysis options. The image is then analyzed using the image_analyzer.analyze method to retrieve OCR results. The recognized text from the OCR result is extracted, with numerical values stored in the string_list variable.
  • The convert_to_decimal_list function converts the list of strings to a list of decimal numbers, which helps process numeric values extracted from the text.
  • The aggregate_operations function calculates various aggregate statistics (sum, average, median, minimum, maximum) from a list of numbers and returns the results in a dictionary.

Note that you must have the appropriate credentials (VISION_KEY) and endpoint (VISION_ENDPOINT) configured for the Azure AI Vision API to use this module.

Finally, we must modify the app.py file so our code can use the process_ocr function of the ocr_helper.py file.

Add the following import statement to the app.py file:

from ocr_helper import process_ocr

Then, replace this line:

return f"File {filename} uploaded successfully to folder: {upload_folder}"

With these two lines:

aggregates = process_ocr(local_file_path)
return json.dumps(aggregates, default=str)

Doing so allows our application to perform aggregate operations (sum, average, median, minimum, maximum) of any numeric values found in the text and return the operation’s result as a JSON response.

Running the Intelligent App Locally

Our final goal is to deploy the sample app to AKS, which will run the app in a container. However, as a good practice, let’s run the app in a container locally before moving it to AKS. Doing so ensures that our application and its dependencies are encapsulated and can be run consistently across different environments.

Deploying to Docker Desktop

For our application to run on Docker, it needs two additional files: a Dockerfile and docker-compose.yml. A Dockerfile creates a single container image by specifying the steps to build it, and a docker-compose.yml file manages multiple containers as a cohesive application, defining their configurations and relationships. Dockerfile builds the image, while docker-compose.yml orchestrates multi-container applications.

In the project root folder, add a file named Dockerfile with the content below:

# syntax=docker/dockerfile:1

FROM python:3.8-slim-buster

WORKDIR /intelligentapp

COPY requirements.txt requirements.txt
RUN pip3 install -r requirements.txt

RUN pip install debugpy

COPY . .

CMD [ "python3", "-m" , "flask", "run", "--host=0.0.0.0"]

Now create a file named docker-compose.yml file in the project’s root folder with the following code, replacing the VISION_KEY and the VISION_ENDPOINT according to the environment variables you configured earlier:

version: '3.8'
services:
intelligentapp:
build:
context: .
dockerfile: Dockerfile
image: intelligent-app
ports:
- 5000:5000
container_name: intelligent-app
environment:
- VISION_KEY=<THE-KEY-1-VALUE-FROM-YOUR-AZURE-AI-SERVICE>
- VISION_ENDPOINT=<THE-ENDPOINT-VALUE-FROM-YOUR-AZURE-AI-SERVICE>

Then, run the following command in the terminal to build the image and start a container for the Intelligent App services defined in the docker-compose.yml file:

docker-compose up --build --force-recreate

Next, open a new terminal and run the command below to list the image deployed to your local Docker:

docker images

docker images

Testing the Intelligent App Locally

Now, we’ll test the Intelligent App’s functionality using the following test image:

test image to test functionality

This image is provided as sample1.png in the sample app source files, so you can easily use it in the next step.

The Intelligent App will read the image above using OCR to extract numbers in the image and perform five aggregate functions:

  • Sum
  • Average
  • Median
  • Min
  • Max

To test the API, open Postman and fill out the fields as follows:

  • URL: http://localhost:5000/
  • Method: POST
  • Body:
    • Form-data
      • Key: File — Click the right end of the Key field and select File from the dropdown list.
      • Value: Click Select Files, then select the sample1.png file provided in the sample code.

image of selecting the sample file

Now click the Send button and review the result body:

"{\"aggregate_result\": {\"sum\": \"25821\", \"average\": \"5164.2\", \"median\": \"5622\", \"min\": \"1447\", \"max\": \"9802\"}, \"numbers_read\": [\"3049\", \"5901\", \"5622\", \"1447\", \"9802\"]}"

As we can see, the app running on a local container returned the correct results based on the sample image. So, we’re ready to prepare our Azure resources to run the Intelligent App on AKS.

Exercise

In this article, we explored the creation of an Intelligent App that leverages Azure AI Vision to analyze images and extract data. We learned how to build a Python Web API to perform OCR on uploaded images and subsequently test this API locally.

Head to the next part, to deploy this API via Azure Kubernetes Service.

+ + \ No newline at end of file diff --git a/30daysofIA/tags/azure-functions/page/4/index.html b/30daysofIA/tags/azure-functions/page/4/index.html index 26a25e0729..233501e4bf 100644 --- a/30daysofIA/tags/azure-functions/page/4/index.html +++ b/30daysofIA/tags/azure-functions/page/4/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "azure-functions" | Build Intelligent Apps On Azure +11 posts tagged with "azure-functions" | Build Intelligent Apps On Azure @@ -14,13 +14,13 @@ - - + +
-

9 posts tagged with "azure-functions"

View All Tags

· 9 min read
It's 30DaysOfIA

In the part of our series on intelligent apps, we’ll explore how AI is transforming application development, from design and architecture to building.

What We'll Cover:

  • Revolutionizing application development using AI
  • Infusing AI in application design architecture
  • AI Assisted Pair Programming
  • Future of App Development

A decorative image of application development with AI

Reimagining Application Development With AI: A New Paradigm

Artificial intelligence (AI) is impacting every industry. AI-powered development tools offer new functionalities and assist software developers throughout the development process, revolutionizing app development. From initial design and architecture to coding and debugging, AI reshapes how we approach application conception, creation, and iteration.

In part one of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” we explored the new breed of intelligent apps and how they use AI. In this article, we’ll see how AI technologies enhance developer toolkits and shape the future of app development, particularly through AI-enabled coding assistance and debugging.

The Dawn of Intelligent Apps: Revolutionizing Application Development

AI is more than just another addition to the development toolbox—it represents a revolution bringing unprecedented efficiency, innovation, and user engagement to our applications. 

What sets intelligent apps apart is how they can adapt and learn, helping us deliver unique, real-time user experiences.  

One example is an AI-driven health application that can track biometric data, predict potential health issues, provide personalized health advice, and even guide emergency services during a medical crisis. An AI-enabled language learning app that adapts to our learning speeds, recognizes our problem areas, and customizes its content to help us improve our language proficiencies is another example.  

Intelligent apps like these don’t just use data for recommendations—they can make critical decisions and provide sophisticated services that were previously impossible. 

For developers, intelligent apps mean we’re no longer just coding—we’re teaching applications how to think, adapt, and make decisions. AI is reshaping every step of the development journey, helping us to build more intuitive and user-focused products. 

Next, we’ll examine the impact of AI on application design and architecture and how AI-assisted “pair programming” changes software development.

info

Register for the FREE webinar on Intelligent Apps with Microsoft and Forrester on September 26 to explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries.

There will be a showcase of real-world use cases that demonstrate how AI can be seamlessly integrated into cloud-native environments driving tangible business value.

AI in Design and Architecture: Fueling Creativity and Efficiency

AI technologies help developers automate and streamline their processes and provide new ways to think about design.  

In traditional application development, the design and architecture phase can take significant time, effort, and expertise. For instance, adopting a monolithic architecture or a microservices-based design can significantly increase coding, testing, and debugging complexity. The app may even behave differently when hosted locally than in a deployed environment. It may take hours to track down an issue manually—time developers could spend elsewhere. 

Conversely, we can generate numerous design iterations in seconds with AI, each optimized to fulfill specific user needs and business goals. AI tools can quickly model various scenarios' impacts and software design patterns in the architectural design phase. It provides insights into performance and potential issues and suggests improvements. AI can also generate skeleton code, allowing developers to prototype different solutions or test specific components and technologies rapidly. By reviewing thousands of lines of code in seconds, AI allows us to focus on creating apps that serve users through feature development instead of searching for bugs.  

Integrating AI into the design and architecture phase can even extend our creative capabilities. By leveraging the power of generative AI—like large language models (LLMs)—and their broad knowledge and context going beyond software, we can better brainstorm apps and feature ideas. We can go beyond code and software architecture and get ideas about user experience, logo and branding, or marketing concepts. And as AI tools deliver insights and ideas we would have never considered; it encourages more innovation.

info

Watch Episode 01 of the Learn Live session to learn how to build an end-to-end intelligent app solution architecture.

The Power of AI-Assisted Pair Programming in Building Applications

AI-assisted pair programming is a collaborative coding approach where an AI system—like GitHub Copilot or TestPilot—assists developers during coding. It’s an increasingly common approach that significantly impacts developer productivity. In fact, GitHub Copilot is now behind an average of 46 percent of developers’ code and users are seeing 55 percent faster task completion on average. 

For new software developers, or those interested in learning new skills, AI-assisted pair programming are training wheels for coding. With the benefits of code snippet suggestions, developers can avoid struggling with beginner pitfalls like language syntax. Tools like ChatGPT can act as a personal, on-demand tutor—answering questions, generating code samples, and explaining complex code syntax and logic. These tools dramatically speed the learning process and help developers gain confidence in their coding abilities. 

Building applications with AI tools hastens development and provides more robust code. For example, GitHub Copilot helps us write new code accurately. It also quickly makes sense of existing code, suggests whole new lines or blocks of code, and even detects errors and proposes fixes. It’s like having a second coder working alongside us—one who never tires and is trained on billions of lines of code. 

However, it’s also important to acknowledge the limitations of AI. While generative AI-assistance technologies like GitHub Copilot are impressive, they’re still imperfect. They can sometimes produce erroneous, non-functional, or vulnerable code. They can also lack nuanced, domain-specific knowledge.  

So, although these tools are helpful, we can’t rely on them fully—these are programming aids rather than definitive solutions. AI-assisted pair programming is about augmenting the human developer’s skills, not replacing them.

Debugging and Improving Code With AI: Enhancing Application Quality

Identifying and correcting code errors is often time-consuming and challenging for developers. AI streamlines the debugging process by automatically detecting anomalies, suggesting fixes, and learning from previous bugs to prevent them from recurring. 

We can use generative AI to create unit tests from code snippets, from simple parameter checking and input validation to complex mocks of database and network services. Automated AI testing improves code coverage and robustness. It saves significant time compared to manually crafting unit tests. 

AI also revolutionizes code reviews by scanning and highlighting potential issues—like code patterns leading to a bug or a coding standards violation—and suggesting ways to simplify or refactor the code for readability and performance. This reduces human error and makes the code review process more efficient. Its benefits extend to small teams, where the availability of developers is crucial to deadlines, and large teams, where effective collaboration depends on readability and code consistency. AI further saves time by triaging issues so developers can prioritize the most mission-critical ones. 

Aside from improving code quality, AI is critical to understanding system behavior. Intelligent AI-assisted software development tools can monitor and audit system behavior, giving developers a better insight into how their code performs in different scenarios. AI can track patterns, identify inefficiencies, and provide alerts when they detect unusual behavior—enabling developers to enhance their code further. 

By predicting and identifying potential vulnerabilities, intelligent AI-assisted coding apps like Microsoft Security Copilot are pivotal in ensuring application functionality, reliability, and security. These AI tools are continually trained and upgraded with large codebases and user feedback that refines their responses. This ability to learn from past mistakes makes AI an incredibly powerful tool that will continue to expand its capabilities in the future.

info

Complete the AI Cloud Skills Challenge to build on you AI application development skills with paired programming using generative AI and GitHub Copilot.

AI and the Future of Application Development

The rapid advancements in AI and machine learning technologies point to an exciting future for application development. 

In “The Future of Applications,” software developer Mike Cann illustrates how LLMs might revolutionize application development. Cann that suggests fully-autonomous coding—AI-generated applications created entirely from prompts—may supersede traditional hand-crafted applications. Dynamically generated user interfaces could offer personalized and customizable experiences while we can design APIs for machine consumption and efficiency rather than human readability. Furthermore, we can store data in less structured, more adaptable, and flexible forms. 

AI is already making application development more accessible. With AI handling the complex, technical aspects of development, less-technical individuals can take a greater role in building applications. Consequently, AI can potentially democratize software engineering. 

Intelligent apps will continue to evolve, becoming more efficient, adaptable, and capable of learning from user interactions. Similarly, AI will inevitably become integral in every stage of app development.

Summary

AI has already created a new development paradigm—one in which machine intelligence and human creativity come together to produce innovative, efficient, and robust applications. AI already assists us at every stage of app development, from design and architecture to coding and debugging. 

Developers who embrace AI tools and integrate them into their processes can enhance their productivity, improve the quality of their applications, and expand the possibilities of their application development.  

The future of development is automated and AI-driven, and it’s here today. Getting started is easy—try an AI-powered development tool like GitHub Copilot to see how AI-assisted pair programming can enhance your productivity and code quality.

Exercise

- - +
Skip to main content

11 posts tagged with "azure-functions"

View All Tags

· 9 min read
It's 30DaysOfIA

In the last article, we created an Intelligent App that leverages Azure AI Vision to analyze images and extract data. Develop an API to perform optical character recognition (OCR) on uploaded images and testing this API locally.

In this article, we will deploy the web API to the cloud platform using Azure Kubernetes Service.

What We'll Cover:

  • Understanding Azure AI Vision and Azure Kubernetes Service
  • Build a Python Web API to perform OCR
  • Test the API locally

image depicting an Intelligent App using AI

Jumpstart Your AI Journey: Building Your First Intelligent App with Azure AI and AKS (2)

In the previous article we explored the creation of an Intelligent App that leverages Azure AI Vision to analyze images and extract data. We learned how to build a Python Web API to perform OCR on uploaded images and subsequently test this API locally.

In this article we will use Azure Kubernetes Service (AKS) to develop, publish, and maintain our app in the cloud on Azure.

Let’s get started!

Prerequisites

To follow this tutorial, ensure you have completed Jumpstart Your AI Journey: Building Your First Intelligent App with Azure AI and AKS (1).

Pushing a Container Image to Azure Container Registry (ACR)

To start, open your CLI or terminal and type the following command:

az login

Follow the instructions displayed on your browser to enter your Azure credentials.

Once authenticated, you’ll initiate a secure connection between your local environment and Azure. This process grants you access to cloud services and resources.

Next, type the command below in your terminal to set up a new Azure Container Registry (ACR) to store your container images:

az acr create --resource-group computer-vision --name <name-of-azure-container-registry> --sku Basic

Remember to replace <name-of-azure-container-registry> with your container registry name. The name must be unique within Azure and comply with these rules.

The command above creates an Azure Container Registry (ACR) in the computer-vision resource group under the Basic SKU. This ACR is your secure and private repository for storing container images within Azure.

Next, log in to the registry with the following command:

az acr login -n <name-of-azure-container-registry>

The az acr login command above lets you securely authenticate and access the specified ACR without providing their Azure credentials each time.

Now, run the following command in your terminal. It will display the endpoint URL to log in to and interact with the ACR for pushing and pulling container images.

az acr show --name <name-of-azure-container-registry> --query loginServer --output table

This returns an endpoint URL as follows:

Result
----------------------------------
<name-of-azure-container-registry>.azurecr.io

Now, run the following command to show all container images, their repository, tags, and size:

docker images
REPOSITORYTAGIMAGE IDCREATEDSIZE
intelligent-applatesta7bf9f75361716 hours ago197MB

Tags are necessary to push Docker images to a remote registry like Azure Container Registry. They also let you differentiate between different versions of the same image and upload or download the one you want.

Run the following command to tag your Docker image:

docker tag intelligent-app <name-of-azure-container-registry>.azurecr.io/intelligent-app:v1

Then, run the docker images command again to check your tagged image:

docker images
REPOSITORYTAGIMAGE IDCREATEDSIZE
intelligent-applatestc52168039265About a minute ago197MB
<name-of-azure-container-registry>.azurecr.io/intelligent-appv1c52168039265About a minute ago197MB

Now run the following command so Docker can securely upload the image to your Azure Container Registry:

docker push <name-of-azure-container-registry>.azurecr.io/intelligent-app:v1

Once we've deployed the image to the container registry, AKS can access it during deployment.

info

Watch Episode 03 for the Learn Live session to experience a guided session on how to build, test and deploy an end-to-end intelligent app solution.

Deploying the Intelligent App on Azure Kubernetes Service (AKS)

Before we deploy our Intelligent App to AKS, we need to provision an AKS cluster and define Kubernetes manifests.

To provision an AKS cluster to host our application, we specify the desired configurations for the cluster, such as the number of nodes, node size, and networking options. But first, download and install the Kubernetes command-line tools (kubectl), a client credential plugin implementing Azure authentication:

az aks install-cli

If you’re using Linux, review this tutorial. Then run the following:

sudo az aks install-cli

Next, run the following command in your terminal to enable access to the networking-related resources and services provided by the `Microsoft.Network`` namespace in Azure:

az provider register --namespace Microsoft.Network

Now, we must create an AKS cluster. Run the following command to create an AKS cluster named aks-intelligent-app in the computer-vision resource group.

az aks create --resource-group computer-vision --name aks-intelligent-app --node-count 1 --generate-ssh-keys

The command above specifies the target resource group: computer-vision. The node pool is configured with one virtual machine (VM), and the secure shell (SSH) keys for secure node access are generated automatically.

Next, run the following command to update the AKS cluster you created by attaching it to your ACR. Doing so allows the AKS cluster to pull container images from the specified ACR when deploying workloads to the cluster.

az aks update -n aks-intelligent-app -g computer-vision --attach-acr <name-of-azure-container-registry>

Then, run the command below to configure kubectl to work with our AKS cluster in the computer-vision resource group.

az aks get-credentials --resource-group computer-vision --name aks-intelligent-app

The command above retrieves the necessary credentials and context information required for kubectl to communicate with the AKS cluster.

We still have to define the Kubernetes manifests written in YAML, describing the desired state of our application deployment, including containers, networking, and scaling rules. We’ll prepare these manifests, including Deployment and Service configurations, to define how our application should be deployed and exposed.

Create a folder named Deployment in the project root directory. Next, create two files in the deployment folder: deployment.yml and service.yml.

Add the following configuration to the deployment.yml file, replacing the <name-of-azure-container-registry> placeholder with your registry’s name:

apiVersion: apps/v1
kind: Deployment
metadata:
name: intelligent-app
spec:
replicas: 1
selector:
matchLabels:
app: intelligent-app
template:
metadata:
labels:
app: intelligent-app
spec:
nodeSelector:
kubernetes.io/os: linux
containers:
- name: intelligent-app
image: <name-of-azure-container-registry>.azurecr.io/intelligent-app:v1
resources:
limits:
memory: 512Mi
cpu: "1"
requests:
memory: 256Mi
cpu: "0.2"
ports:
- containerPort: 5000
env:
- name: FLASK_DEBUG
value: "1"
- name: VISION_KEY
value: <THE-KEY-1-VALUE-FROM-YOUR-AZURE-AI-SERVICE>
- name: VISION_ENDPOINT
value: <THE-ENDPOINT-VALUE-FROM-YOUR-AZURE-AI>

Additionally, edit the VISION_KEY and VISION_ENDPOINT environment variables above according to the API key and endpoint of your Azure AI Services instance.

Then, add the following configuration to the service.yml file:

apiVersion: v1
kind: Service
metadata:
name: intelligent-app-service
spec:
type: LoadBalancer
ports:
- protocol: TCP
port: 80
targetPort: 5000
name: port5000
selector:
app: intelligent-app

Now, we’ll deploy our application using kubectl. Applying the Kubernetes manifests creates the necessary resources and deploys our containerized application to the AKS cluster.

First, change the terminal to the deployment folder:

cd Deployment 

Then, run the following command to create or update Kubernetes resources defined in the deployment.yml file:

kubectl apply -f deployment.yml

Create a Kubernetes Service resource in a Kubernetes cluster based on the configuration defined in the service.yml file using the code below:

kubectl apply -f service.yml

Once you’ve applied the resource definition and the service configuration contained in the deployment.yml and the service.yml files, open the aks-intelligent-app Kubernetes Service in the Azure Portal, select Workloads under Kubernetes resources on the sidebar, and find the deployment named intelligent-app. It must have the status “Ready 1/1”. If you encounter an issue with this status, check out these troubleshooting resources.

image of selecting the deployment in Azure Portal

Testing the Intelligent App on AKS

To test the app on AKS, first, run the command below:

kubectl get services

This command lists the Services and their corresponding details, including the Service name, cluster IP address, external IP, and ports.

NAMETYPECLUSTER-IPEXTERNAL-IPPORT(S)AGE
intelligent-app-serviceLoadBalancer10.0.77.6020.121.76.15380:30936/TCP47s
kubernetesClusterIP10.0.0.1<none>443/TCP14m

The output above shows a Kubernetes Service named intelligent-app-service with a type set to LoadBalancer. It’s reachable from within the cluster using the cluster IP 10.0.77.60 and accessible externally via the external IP 20.121.76.153 on port 80 (mapped to port 30936).

Note: Your set of IP addresses will differ. Remember to use your unique external IP address when testing with Postman.

To test the deployed app, go to Postman, replace the URL with the external IP of the Kubernetes Service you just deployed, and click Send:

mage of sending the app in Postman

As we can see, our Intelligent App has successfully deployed to AKS and is functioning on the cloud as expected.

Exercise

Next Steps

In this two-part article, we explored the creation of an Intelligent App that leverages Azure AI Vision to analyze images and extract data. We learned how to build a Python Web API to perform OCR on uploaded images and subsequently deploy this API via Azure Kubernetes Service.

Besides OCR and Image Analysis, you can continue exploring Azure’s vast array of services and experiment further with Azure AI and AKS by applying various practical uses to your Intelligent Apps, including natural language processing, speech recognition and synthesis, sentiment analysis for customer feedback, and automated content moderation.

+ + \ No newline at end of file diff --git a/30daysofIA/tags/azure-functions/page/5/index.html b/30daysofIA/tags/azure-functions/page/5/index.html index 08575e069e..a38561f49c 100644 --- a/30daysofIA/tags/azure-functions/page/5/index.html +++ b/30daysofIA/tags/azure-functions/page/5/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "azure-functions" | Build Intelligent Apps On Azure +11 posts tagged with "azure-functions" | Build Intelligent Apps On Azure @@ -14,13 +14,13 @@ - - + +
-

9 posts tagged with "azure-functions"

View All Tags

· 10 min read
It's 30DaysOfIA

Explore real-world examples of how Intelligent Apps revolutionize logistics, retail, finance, manufacturing, tech, and medicine.

What We'll Cover:

  • Real-world businesses revolutionizing operations with Intelligent Apps
  • High level architecture for industry scenarios
  • Implementation architecture overview across industries

A decorative header image for the blog

Real-world Success Stories

According to IBM’s 2022 Global AI Adoption Index, 35% of surveyed companies already use AI in their business, and 42% are exploring the idea. Organizations lagging behind the AI curve will quickly fall behind early adopters, who are already enjoying its benefits and boosting their investment in this technology.

In the first part of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” we explored how organizations can use AI-powered apps, or intelligent apps, to transform their operations, decision-making, and customer experiences, leaping ahead of the competition.

Intelligent apps significantly improve operational efficiency and customer engagement across industries like logistics, retail, finance, manufacturing, tech, and healthcare. In this article, we’ll spotlight real-world success stories of businesses revolutionizing operations with intelligent apps using Microsoft Azure’s app, data, and AI services.

info

Register for the Learn Live on September 21 for a live guided experience with SMEs on how to build end-to-end solution architecture for real world implementation of Intelligent Apps.

Streamlining Operations: Intelligent Apps in the Airline Industry

Aerospace pioneer Airbus leveraged intelligent apps to streamline its operations and innovate its services with two ground-breaking modernizations that use Azure apps, data, and AI services to reimagine pilot training and predict aircraft maintenance issues. 

Aviation technology’s rapid evolution compelled Airbus to reconceive their approach to training pilots. The aircraft manufacturer created a pilot training chatbot by combining Azure AI Services with Speech to Text and Text to Speech. The bot helps trainee pilots navigate over 6,000 pages of technical information. Pilots can ask questions verbally for quick answers, aiding their understanding of dense information and speeding up training. And when Airbus needed to accommodate its military consumers, deploying its Kubernetes cluster to a disconnected, “air-gapped” environment met the strict security requirements of military aircraft training.

A chatbot could look like the following diagram as Azure services work together to create a conversational experience. Its components could include security and governance, bot logic and user experience (UX), bot cognition and intelligence, data extract, transfer, and load (ETL) operations, quality assurance and enhancements, logging, and monitoring and reporting.

A diagram of a conversational experience enabled by Azure services

Airbus also used AI to improve its aircraft maintenance and safety. They deployed AI Anomaly Detector to gather and analyze telemetry data from multiple flights, enabling more profound insights into wear and tear on military aircraft operating in harsh conditions. This AI application helps Airbus better understand each airplane’s health. Airbus can predict and fix potential problems before they occur, improving the aircraft’s safety and operational readiness.

AI and Azure are changing how organizations like Airbus operate, even in strict, highly regulated markets with stringent security requirements. And by streamlining pilot training and staying ahead of aircraft maintenance, Airbus helps its military and commercial clients reliably deliver their goods across the globe, maintaining their critical supply chains.

Boosting Customer Service: Intelligent Apps in the Retail Industry

The retail industry has been among the first to adopt and benefit from intelligent apps. For example, the leading American used car retailer, CarMax, uses intelligent apps to transform the car shopping experience.

Before embracing AI, CarMax faced a significant challenge: Manually creating text summaries was time-consuming and labor-intensive. CarMax decided to leverage Azure OpenAI Service to automatically generate car research pages, offering customers valuable insights while enhancing the website’s search engine rankings.

This AI-driven approach has significantly streamlined the process, achieving in a few months what would have taken years manually. Also, the AI system condenses thousands of customer reviews into a few readable sentences, offering potential buyers a quick overview of others’ experiences.

CarMax has improved their customer experience while boosting operational efficiency through AI adoption. CarMax’s editorial staff focuses on creating strategic, longer-form content with the time saved, enhancing productivity, and driving more website traffic.

CarMax’s success story serves as a testament to the potential of intelligent apps in the retail industry.

Enhancing Cybersecurity: Intelligent Apps in Finance

Intelligent apps also impact the highly regulated financial services sector. Swift, a leading infrastructure provider for financial messaging services, uses intelligent apps to enhance decision-making and combat financial crime.

The massive growth of cross-border transactions and instant payment networks led to a surge in financial fraud that can cost the industry hundreds of billions of dollars every year. So, Swift and Microsoft partnered to build an anomaly detection model using federated learning techniques with Azure Machine Learning and Azure confidential computing.

Microsoft Purview helped govern data, as the federated learning method partially trained each model in a silo before aggregating them. This approach enabled Swift and Microsoft to develop a highly accurate model without copying or moving data from Swift members’ secure locations, ensuring the highest level of security and privacy.

Financial organizations can now deploy this trained ML model to run a real-time fraud detection system, lowering the cost of financial fraud.

Organizations can deploy a fraud detection system like the following example on Microsoft Azure. The system ingests data from streams or storage, runs analytics, and uses ML to detect fraud and store results. It then alerts users and other systems to the security issue.

A diagram of an example fraud detection system

Swift provides the innovative model to its banking partners, who can share data to further train the model. Azure confidential computing securely runs new models on current data to accumulate insights, generating an ongoing learning cycle. This iterative approach strengthens the global detection of fraudulent financial transactions through an increasingly faster and more accurate model. 

The collaboration between Swift and Microsoft demonstrates how combining various Azure services creates Intelligent Apps to enhance decision-making in finance. These apps solve real problems at an unprecedented global scale. 

Enhancing Efficiency: Intelligent Apps in Manufacturing

3M, known for Post-it Notes and other innovative products, wielded the power of Intelligent Apps to standardize, automate, and accelerate its sales forecasting. The company needed a unified, automated approach to replace its multiple manual methods.

It adopted Azure Machine Learning and other Azure AI Services to develop, train, and implement 1,500 custom models for each region and division, replacing manual methods. This solution delivers timely finance, sales, and marketing insights while freeing teams from time-consuming manual data manipulation tasks.

3M used Azure Machine Learning, automated machine learning (AutoML), and the Many Models Solution Accelerator to train and score numerous machine learning models in parallel. This approach significantly shortened the company’s development cycle time.

Then, 3M integrated Microsoft Power BI into their solution to compile and visualize results more effectively. Executives now have an unbiased and unfiltered view of data for better decision-making. With its improved ability to forecast sales, 3M can focus on manufacturing the products it needs efficiently.

3M’s success demonstrates how combining various Azure services can enhance efficiency in the manufacturing industry. These apps improve operational processes while enabling employees to focus on higher-level tasks. Strategic decisions are more targeted, and business conversations are more meaningful.

Driving Innovation: Intelligent Apps in the Technology Industry

As expected, the tech industry is at the forefront of leveraging Intelligent Apps to drive innovation. Elastic saw an opportunity to use intelligent apps to optimize its search services and meet customer needs more effectively. The company built a managed, secure, and scalable solution for its customers, Elastic Cloud, by leveraging Microsoft Azure.

Elastic Cloud uses Azure Machine Learning and other Azure AI Services to automate many basic management tasks, such as backing up data, upgrading, and scaling deployments. This approach enables Elastic’s customers to focus less on managing the software and more on serving their own clients. Customers can quickly deploy and manage Elastic Cloud on Kubernetes (ECK) using Azure Kubernetes Service.

As a result, Elastic’s cloud business is growing twice as fast as its on-premises solutions. Enterprise search users have reported significant improvements in system availability and employee satisfaction.

Additionally, Elastic incorporated Azure OpenAI Service into its Elasticsearch Relevance Engine (ESRE), providing more relevant answers to search queries. ESRE enables text, vector, and hybrid search, offering a more effective and accurate response to queries.

Elastic’s success story demonstrates how combining various Azure services can create intelligent Apps to drive innovation in the tech industry. These apps help improve operational processes and deliver better results quickly.

Revolutionizing Healthcare: Intelligent Apps in Medicine

Intelligent apps also drive transformative changes in the healthcare sector. Cambridgeshire and Peterborough NHS Foundation Trust, a healthcare provider in the United Kingdom, gradually shifted their on-premises systems to the cloud using Azure, improving flexibility, and lowering costs. They needed a solution for their electronic patient record (EPR) system that integrates 23 years of data across multiple systems and formats.

The Trust used Microsoft Azure and Azure Cognitive Search, including semantic search for understanding intent and contexts and cognitive capabilities for extracting insights from data, to standardize and automate data management. This approach made legacy, archive, and live data more discoverable, saving clinicians’ time. The Trust’s solution searches through millions of clinical documents in seconds, even recognizing handwritten notes without needing a separate optical character recognition (OCR) solution to extract text from images (Azure Cognitive Search automatically does it). Clinicians can now uncover previously inaccessible insights, enabling them to offer more personalized care.

The Trust also used Integration Services tools such as Azure Logic Apps to create workflows without writing code and Event Grid to connect Azure and third-party services through a publisher-subscriber model. They switched on a live feed from their new EPR system to Azure to feed real-time information into other applications. Researchers can now access anonymized data to fuel medical innovations beyond the NHS.

The healthcare provider also invested heavily in Power BI’s data visualization and analytical functions to sift through massive amounts of information. This level of data accessibility was previously impossible with their on-premises system. The new insights have significantly improved clinicians’ efficiency for better patient care.

Data accessibility and security are critical in the healthcare industry. Intelligent Apps empower organizations like the Trust to revolutionize medical care. AI capabilities helped the Trust improve its operational efficiency and user experiences, setting a new standard for healthcare in the digital age.

Summary

AI is driving transformation and real-world successes across industries. Intelligent apps are reshaping our world by streamlining operations in manufacturing and logistics, enhancing customer experience in the retail sector, improving decision-making in finance, driving innovation in tech, and revolutionizing healthcare.

As organizations adopt AI, they’re innovating in ways that were once too difficult, expensive, or outright impossible. Companies that wait to embrace intelligent apps risk the chance of falling behind. By embracing intelligent apps and leveraging the capabilities of Azure, you can harness the transformative potential of AI to remain on the cutting edge of technology.

Exercise

- - +
Skip to main content

11 posts tagged with "azure-functions"

View All Tags

· 10 min read
It's 30DaysOfIA

Explore how digital natives and Independent Software Vendors (ISVs) can transform their business operations and generate higher value for customers with generative AI and intelligent apps.

What We'll Cover:

  • Potential of Generative AI in application development
  • Challenges with Generative AI
  • Integrating Generative AI with business operations

image of generative AI and intelligent apps used in business

Unleashing Business Transformation: How Digital Natives Can Leverage Generative AI

Generative AI is no longer a technological flight of fancy. It’s here, and it’s rewriting the rules of business innovation. 

This type of AI leverages machine learning algorithms to create new data with similar characteristics to a given dataset. It goes beyond text generation using generative pretrained transformer (GPT) models, covering an array of applications that span from image synthesis to translations and more. It also powers a new class of intelligent applications, which combine AI capabilities with traditional software functions for enhanced user experiences and advanced business solutions. 

Generative AI is transforming how digital natives and independent software vendors (ISVs) operate. Generative AI offers new possibilities for creating unique business value, enhancing customer experiences, and driving growth. 

It plays a critical role in applications, improving functionality, aiding decision-making, and automating complex tasks to deliver dynamic, uniquely personalized user experiences. Generative AI also enables and automates data-driven outcomes, functions, and features.

Integrating AI capabilities into customer-facing apps and turning them into intelligent apps benefits both the customer and the business, as we discussed in Demystifying Intelligent Applications: Leveraging AI in App Development. You get faster product development workflows and better business intelligence for sales and marketing, while your users get more natural and context-aware features and interactions. 

That said, to realize generative AI’s potential, you need some strategy and knowledge of how and where it fits into your current business and operations. We’ll examine this technology’s potential to transform business operations, its risks, and rewards, and how to integrate it into your existing operations. 

Unpacking the Potential of Generative AI

Generative AI is creating new opportunities in the digital playing field. It empowers businesses to generate insights, design products, and automate processes in ways and at speeds that were previously unimaginable. You can now predict customer behavior more accurately, offer highly personalized user experiences, and prototype innovative ideas rapidly. 

For example, Microsoft used generative AI to reinvent web search in the new AI-powered Bing and Edge. The search experience has evolved from a solitary user flow of typing in keywords and scrolling through results. It’s now a conversational experience where Bing uses context to offer better quality and more personalized search queries and results. 

Similarly, Microsoft harnessed generative AI to transform its productivity suite into intelligent apps. Through Copilot for Microsoft 365, PowerPoint now empowers users to tell their stories by using DALL-E to generate images effortlessly. It also helps rephrase text-heavy slides to be more straightforward. Outlook now assists users to compose more effective emails, enabling better communication.

These are just some examples of how AI powers day-to-day work and operations. 

Businesses that ignore generative AI miss out on untapped market opportunities, letting competitors gain an edge in the digital race. By seeking ways to rethink assumptions and reinvent existing approaches, you can effectively use generative AI to create new opportunities, open products to new use cases and markets, unlock new growth and customer satisfaction levels, and increase your revenue.

info

Register for the intelligent apps webinar on Driving Business Value by Modernizing with Cloud-Native & AI with Microsoft and Forrester on September 26.

Explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries. 

The Crucial Role of Generative AI in Business Operations

Generative AI can be a powerful catalyst for business transformation. Let’s quickly tour some ways it can redefine business operations. 

Product development is traditionally a long and painstaking process. Now, tools like GitHub Copilot and TestPilot use generative AI to make coding and testing software easier, faster, and more robust. 

Similarly, machine learning as a service technology, such as Azure Machine Learning, democratizes the availability of machine learning. It helps collect, clean, and train models across datasets, making engineering and analyzing data more efficient and accessible.  

Generative AI also simplifies the process of creating intelligent apps. Previously, simulating thousands of prototypes, predicting their performance, and narrowing down to the most viable options could take weeks, months, or even years of iteration and data analysis. With generative AI, this process can be as quick as days, hours, or perhaps minutes. 

Customer experience is also ripe for transformation. Personalized recommendations drive engagement and sales, and generative AI can scale that personalization to every customer interaction using comprehensive and continually updated data. For instance, Microsoft leverages AI to enable personalization in Dynamics 365 Customer Insights. Using this tool, you can ask questions about your customers in natural language and gain valuable and actionable insights into your customer base. 

Lastly, marketing and sales can benefit from generative AI. For example, identifying current and future key markets and emerging trends helps companies stay ahead of the curve. Tools like Copilot in Power BI help you quickly understand and uncover insights about your business, enabling your team to craft targeted campaigns, create engaging content, and optimize your business strategy. 

Leveraging this full generative AI potential requires the right tools. GPT-4, for instance, can be instrumental in realizing effective business transformation. It excels in understanding context, generating human-like text, and learning from minimal data input, making it a formidable tool in any digital native's arsenal. Platforms like Azure OpenAI Service bring GPT-4’s power to your fingertips.

However, as with any powerful technology, it’s unwise to treat generative AI as a perfect solution. You must know the challenges and risks of not leveraging it effectively, including technical and human-factor considerations. 

Without generative AI, your company risks missing the opportunity to please customers via personalization, as discussed previously. You’ll miss sales opportunities via business intelligence insights, limiting your ability to grow revenue and keep up with the competition. 

One challenge of leveraging generative AI’s full potential is finding the right tools. Fortunately, you can implement generative AI in your organization without investing in infrastructure, computational power, and expertise. Platforms like Azure OpenAI Service and Azure AI Services make AI easily accessible. They also work with Azure compute and data services like Azure Functions and Azure Kubernetes Service (AKS), enabling you to build intelligent apps easily and quickly. 

However, generative AI can’t do everything. It may need human expertise and intervention to use it fully. 

For example, generative AI can create unique data. However, the data may have limited diversity and complexity. The AI may need help recognizing additional considerations or requirements specific to a problem. It may also lack context due to various factors, such as the information’s freshness. Also, since it’s an emerging field that’s potentially worth $4.4 trillion, we’re still learning about the nuances and potential regulatory impact of generative AI. 

Perhaps the most crucial and challenging aspect of adopting generative AI is an organization’s required cultural shift. Your teams must learn how to use AI as your company fosters an understanding of AI’s potential and limitations. 

Begin integrating AI literacy into your company’s DNA. Knowledge of its potential and the readily available tools can inspire your teams to incorporate generative AI into their daily workflows and business operations quickly, improve productivity, and deliver superior customer experiences. 

No company wants to be outpaced by its competitors. You can balance generative AI’s potential with your business operations by staying aware of and adapting to the dynamic AI landscape.

Achieving Harmony: Integrating Generative AI with Existing Business Operations

Integrating generative AI into existing business operations doesn’t have to be disruptive. With the correct strategy, you can do so seamlessly. Your goal should be to augment and enhance your operations, not disrupt and replace. 

For example, Microsoft Cloud technologies leveraged AI to relieve healthcare organizations’ challenges, such as talent shortage and workforce burnout. Providers automated their workflows, invested in specific applications and connectors, and enhanced support for data and operations. 

Azure AI Services, such as text analytics for health, enable healthcare organizations to understand their largely unstructured and untapped medical data. Project Health Insights leans on AI to help healthcare professionals gain actionable insights and inferences from patient data. 

Azure Health Bot, an Intelligent App, empowers organizations to build and deploy legally compliant, powerful conversational healthcare experiences to patients. 

A successful AI integration like this begins with understanding. Knowing what generative AI can do and setting realistic expectations can help you identify where and how AI can best serve your operations. 

Next, you can foster a culture of AI literacy across teams, speeding up adoption by helping them understand how it can aid their work. The more familiar your team is with AI and its possibilities, the smoother the transition. Training and education programs play a pivotal role here. 

Finally, integration requires a systematic approach. It’s not about overhauling everything at once — it's about identifying areas where AI can add the most value, then gradually experimenting and scaling its use. Successful integration stories often feature a phased approach, starting small and growing steadily. 

As we move toward a future where generative AI is a staple in business operations, remember that every journey is unique. Although there’s no one-size-fits-all solution, a strategic approach and a keen understanding of your business needs will help you navigate this new landscape successfully.

Conclusion

Generative AI offers plenty of opportunities for businesses that embrace it. It’s fueling the next generation of intelligent applications, transforming everything from product development to customer engagement to marketing. This technology is the key to unlocking unprecedented growth and innovation. 

While the journey may have challenges, the potential rewards make generative AI worth exploring. As generative AI advances, Azure AI’s broad range of tools and services will help you navigate this fascinating field.

Exercise

  • Watch Episode 02 of the serverless edition Learn Live session to learn how to build, test and deploy an end-to-end intelligent app solution.
  • Complete the Cloud Skills Challenge to build on your AI skills.
  • Register for Ask the Expert: Azure Functions on September 26 for a live Q&A with the Product Team on how to build AI apps using serverless computing.
+ + \ No newline at end of file diff --git a/30daysofIA/tags/azure-functions/page/6/index.html b/30daysofIA/tags/azure-functions/page/6/index.html index d110aa09ca..a65d7d651f 100644 --- a/30daysofIA/tags/azure-functions/page/6/index.html +++ b/30daysofIA/tags/azure-functions/page/6/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "azure-functions" | Build Intelligent Apps On Azure +11 posts tagged with "azure-functions" | Build Intelligent Apps On Azure @@ -14,13 +14,13 @@ - - + +
-

9 posts tagged with "azure-functions"

View All Tags

· 8 min read
It's 30DaysOfIA

Let’s start with understanding the power of intelligent applications.

What We'll Cover:

  • What are intelligent applications?
  • Categories of Intelligent Apps
  • Breaking down the role of AI in intelligent apps

A decorative header image for the blog

Demystifying Intelligent Applications: Leveraging AI in App Development

Until recent years, when you heard the term “artificial intelligence” (AI), you might have pictured autonomous robots from sci-fi films. Now, perhaps your mind jumps to big data and machine learning (ML). Maybe you’re even thinking about the latest ChatGPT plugins and transformer architectures. Today, AI is more than these imaginations: it’s a reality increasingly woven into our day-to-day routines through intelligent applications.

Intelligent apps combine cloud-scale compute, data, and predictive or generative AI. These applications help you automate tasks and make data-driven decisions, aiding in leveraging business intelligence to gain meaningful, practical outcomes. 

For developers, the AI landscape isn’t an abstract complexity. It’s a real, tangible opportunity for evolving traditional applications, making them “intelligent,” and delivering more value to users. And with tools like Azure Kubernetes Service, Azure Cosmos DB, and Azure AI Services, stepping into the world of intelligent apps isn’t a steep climb but a steady trek across a manageable learning curve.

The Three Categories of Intelligent Applications

Before we start building, it’s critical to understand what makes an app “intelligent.” It’s not a universal definition so much as an identifier for apps that fall into one or more of the three primary categories: 

  • Outcome-based — This designation represents the broadest category of intelligent apps, whose principal value for users is the intelligent outcomes they provide. These apps process data to provide insightful results that enhance decision-making or help with tasks like trend prediction, resource management, and customer relationship management.

  • Functionality-based — This type of intelligent app takes AI integration a degree further, integrating AI as a function of the app to create or process its outputs, enhancing the app’s overall interactivity and usefulness.

  • Feature-based — This is the narrowest category, in which the app’s AI/ML component is one of its core features and primary selling points

Let’s dive more deeply into each category.

Outcome-Based Apps: Intelligent Outcomes Drive Success

Outcome-based applications are all about the results provided to the user. They function beyond merely processing data to offer insightful outcomes that drive success.

Think of a personal fitness tracker app that uses AI to analyze the user’s activity and provides actionable recommendations for achieving a goal. This type of app not only tracks your steps, sleep patterns, and heart rate but uses that data to provide you with personalized health advice. It helps you make informed lifestyle choices — the “intelligent” outcome.

Another example is a social media marketing app that uses AI for time-saving data analysis, task automation, and trend prediction. AI-based sentiment analysis can gauge customer perception while natural language processing (NLP) accurately interprets customer inquiries. Task automation can schedule posts and automate responses to common customer queries, and trend forecasting helps model and predict customer behavior to further personalize targeted promotional efforts. Building this type of intelligence into your apps provides cost-effective, actionable insights that guide sales and marketing strategies.

Functionality-Based Apps: AI-Driven Features Enhance User Experiences

As the name suggests, functionality-based applications integrate AI into how the app functions, but the end user drives the action and outcome. In this category of apps, AI takes a more active role in the user’s interaction by allowing them to use AI-created or processed output. These apps leverage AI technologies like Azure AI Services for NLP, image recognition, and pattern recognition to improve user experience.

Consider a music app that generates personalized playlists based on your listening habits or your smartphone’s camera app that uses AI to recognize faces and optimize photo settings. Even more advanced are the language translation apps that translate text from one language to another and can even understand and maintain the context and subtleties of the conversation. They use NLP to accomplish these feats.

Similarly, consider the latest e-commerce apps, whose AI-driven functionalities enable customers to try on clothes virtually, significantly enhancing the online shopping experience.

Feature-Based Apps: Integrating Advanced AI/ML Components

Finally, there are feature-based intelligent apps. These apps incorporate advanced AI/ML components like neural networks, internal large language models (LLMs), and sophisticated algorithms as core features for the user. In this type of application, AI is the primary feature for users to engage with and the primary selling point for the app.

Some examples of feature-based applications are chatbots and virtual agents built using Azure Bot Service. These robo-representatives use AI to provide more natural and human-like conversation, balancing superior efficiency with a friendlier, more personalized user experience. The new Bing is a prime example of this implementation, integrating AI to give users a knowledgeable copilot for web searches.

Similarly, OpenAI’s ChatGPT allows users access to its state-of-the-art language models available as Microsoft’s Azure OpenAI Service. It leverages the power of the highly sophisticated GPT-3 LLM to perform tasks like drafting emails, writing code, answering general knowledge questions, translating between languages, and tutoring in numerous subjects.

These apps don’t just use AI to provide outputs or improve user experiences: They push the boundaries of what’s possible in app development by integrating AI/ML components.

info

Register for the live Q&A Ask The Expert session on September 26 with the Azure Functions product engineering team.

Breaking Down the Role of AI in Intelligent Applications

Intelligent apps reflect the merging of traditional software design with the dynamic capabilities of AI. Let’s explore how AI integrates and amplifies these applications.

At its core, AI augments applications with the capacity to self-learn and self-improve. It allows you to create applications that predict, react, and adapt to a constantly changing environment. In the power grid management system for a major city, an AI-infused application can forecast power usage trends, react to anomalies, and adapt to sudden demand surges, ensuring optimal efficiency and preventing blackouts.

But AI’s role doesn’t end at prediction or decision-making: It also automates tasks. In enterprise solutions, it has moved beyond simple automation to encompass intelligent process automation (IPA). In this arena, AI algorithms understand, learn, and then automate entire digital workflows. For developers, this is a fertile ground for creating applications that can dramatically improve the operational efficiency of businesses.

Even more, AI substantially aids in development tasks. For example, there’s GitHub Copilot, an AI-powered code assistant that offers suggestions based not only on syntax but on development best practices and the intent of the code. AI has evolved from a tool you use to build apps to a partner that helps in creating them. 

Using AI to build intelligent apps isn’t about throwing out what’s worked before. It’s about enhancing the components that have the most impact and achieve the best results. Incorporating AI in apps elevates their predictive abilities, streamlines complex tasks, and even aids in the development and creation process. When you build intelligent apps, you don’t just address current problems. You anticipate future challenges and craft solutions that adapt over time.

Summary

As developers the power of AI is in our hands, and it’s our responsibility to harness its full potential. Each category tells the story of AI's transformative potential, from applications that deliver intelligent outcomes to those with AI capabilities to enrich the user experience and those pushing boundaries with advanced AI/ML technology.

Accessible and robust platforms like Azure AI can simplify the process of developing Intelligent Applications. Whether you’re refining an existing app or building a new one, just entering the world of AI, or already familiar with the landscape, take a moment to appreciate the potential that AI integration offers. You’re embracing this exciting frontier and shaping the future of app development. 

Exercise

  • Complete the Cloud Skills Challenge to build on your apps and AI skills.
  • Watch Episode 01 of the Serverless Edition Learn Live session to learn how to build an end-to-end intelligent app solution.
- - +
Skip to main content

11 posts tagged with "azure-functions"

View All Tags

· 9 min read
It's 30DaysOfIA

In the part of our series on intelligent apps, we’ll explore how AI is transforming application development, from design and architecture to building.

What We'll Cover:

  • Revolutionizing application development using AI
  • Infusing AI in application design architecture
  • AI Assisted Pair Programming
  • Future of App Development

A decorative image of application development with AI

Reimagining Application Development With AI: A New Paradigm

Artificial intelligence (AI) is impacting every industry. AI-powered development tools offer new functionalities and assist software developers throughout the development process, revolutionizing app development. From initial design and architecture to coding and debugging, AI reshapes how we approach application conception, creation, and iteration.

In part one of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” we explored the new breed of intelligent apps and how they use AI. In this article, we’ll see how AI technologies enhance developer toolkits and shape the future of app development, particularly through AI-enabled coding assistance and debugging.

The Dawn of Intelligent Apps: Revolutionizing Application Development

AI is more than just another addition to the development toolbox—it represents a revolution bringing unprecedented efficiency, innovation, and user engagement to our applications. 

What sets intelligent apps apart is how they can adapt and learn, helping us deliver unique, real-time user experiences.  

One example is an AI-driven health application that can track biometric data, predict potential health issues, provide personalized health advice, and even guide emergency services during a medical crisis. An AI-enabled language learning app that adapts to our learning speeds, recognizes our problem areas, and customizes its content to help us improve our language proficiencies is another example.  

Intelligent apps like these don’t just use data for recommendations—they can make critical decisions and provide sophisticated services that were previously impossible. 

For developers, intelligent apps mean we’re no longer just coding—we’re teaching applications how to think, adapt, and make decisions. AI is reshaping every step of the development journey, helping us to build more intuitive and user-focused products. 

Next, we’ll examine the impact of AI on application design and architecture and how AI-assisted “pair programming” changes software development.

info

Register for the FREE webinar on Intelligent Apps with Microsoft and Forrester on September 26 to explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries.

There will be a showcase of real-world use cases that demonstrate how AI can be seamlessly integrated into cloud-native environments driving tangible business value.

AI in Design and Architecture: Fueling Creativity and Efficiency

AI technologies help developers automate and streamline their processes and provide new ways to think about design.  

In traditional application development, the design and architecture phase can take significant time, effort, and expertise. For instance, adopting a monolithic architecture or a microservices-based design can significantly increase coding, testing, and debugging complexity. The app may even behave differently when hosted locally than in a deployed environment. It may take hours to track down an issue manually—time developers could spend elsewhere. 

Conversely, we can generate numerous design iterations in seconds with AI, each optimized to fulfill specific user needs and business goals. AI tools can quickly model various scenarios' impacts and software design patterns in the architectural design phase. It provides insights into performance and potential issues and suggests improvements. AI can also generate skeleton code, allowing developers to prototype different solutions or test specific components and technologies rapidly. By reviewing thousands of lines of code in seconds, AI allows us to focus on creating apps that serve users through feature development instead of searching for bugs.  

Integrating AI into the design and architecture phase can even extend our creative capabilities. By leveraging the power of generative AI—like large language models (LLMs)—and their broad knowledge and context going beyond software, we can better brainstorm apps and feature ideas. We can go beyond code and software architecture and get ideas about user experience, logo and branding, or marketing concepts. And as AI tools deliver insights and ideas we would have never considered; it encourages more innovation.

info

Watch Episode 01 of the Learn Live session to learn how to build an end-to-end intelligent app solution architecture.

The Power of AI-Assisted Pair Programming in Building Applications

AI-assisted pair programming is a collaborative coding approach where an AI system—like GitHub Copilot or TestPilot—assists developers during coding. It’s an increasingly common approach that significantly impacts developer productivity. In fact, GitHub Copilot is now behind an average of 46 percent of developers’ code and users are seeing 55 percent faster task completion on average. 

For new software developers, or those interested in learning new skills, AI-assisted pair programming are training wheels for coding. With the benefits of code snippet suggestions, developers can avoid struggling with beginner pitfalls like language syntax. Tools like ChatGPT can act as a personal, on-demand tutor—answering questions, generating code samples, and explaining complex code syntax and logic. These tools dramatically speed the learning process and help developers gain confidence in their coding abilities. 

Building applications with AI tools hastens development and provides more robust code. For example, GitHub Copilot helps us write new code accurately. It also quickly makes sense of existing code, suggests whole new lines or blocks of code, and even detects errors and proposes fixes. It’s like having a second coder working alongside us—one who never tires and is trained on billions of lines of code. 

However, it’s also important to acknowledge the limitations of AI. While generative AI-assistance technologies like GitHub Copilot are impressive, they’re still imperfect. They can sometimes produce erroneous, non-functional, or vulnerable code. They can also lack nuanced, domain-specific knowledge.  

So, although these tools are helpful, we can’t rely on them fully—these are programming aids rather than definitive solutions. AI-assisted pair programming is about augmenting the human developer’s skills, not replacing them.

Debugging and Improving Code With AI: Enhancing Application Quality

Identifying and correcting code errors is often time-consuming and challenging for developers. AI streamlines the debugging process by automatically detecting anomalies, suggesting fixes, and learning from previous bugs to prevent them from recurring. 

We can use generative AI to create unit tests from code snippets, from simple parameter checking and input validation to complex mocks of database and network services. Automated AI testing improves code coverage and robustness. It saves significant time compared to manually crafting unit tests. 

AI also revolutionizes code reviews by scanning and highlighting potential issues—like code patterns leading to a bug or a coding standards violation—and suggesting ways to simplify or refactor the code for readability and performance. This reduces human error and makes the code review process more efficient. Its benefits extend to small teams, where the availability of developers is crucial to deadlines, and large teams, where effective collaboration depends on readability and code consistency. AI further saves time by triaging issues so developers can prioritize the most mission-critical ones. 

Aside from improving code quality, AI is critical to understanding system behavior. Intelligent AI-assisted software development tools can monitor and audit system behavior, giving developers a better insight into how their code performs in different scenarios. AI can track patterns, identify inefficiencies, and provide alerts when they detect unusual behavior—enabling developers to enhance their code further. 

By predicting and identifying potential vulnerabilities, intelligent AI-assisted coding apps like Microsoft Security Copilot are pivotal in ensuring application functionality, reliability, and security. These AI tools are continually trained and upgraded with large codebases and user feedback that refines their responses. This ability to learn from past mistakes makes AI an incredibly powerful tool that will continue to expand its capabilities in the future.

info

Complete the AI Cloud Skills Challenge to build on you AI application development skills with paired programming using generative AI and GitHub Copilot.

AI and the Future of Application Development

The rapid advancements in AI and machine learning technologies point to an exciting future for application development. 

In “The Future of Applications,” software developer Mike Cann illustrates how LLMs might revolutionize application development. Cann that suggests fully-autonomous coding—AI-generated applications created entirely from prompts—may supersede traditional hand-crafted applications. Dynamically generated user interfaces could offer personalized and customizable experiences while we can design APIs for machine consumption and efficiency rather than human readability. Furthermore, we can store data in less structured, more adaptable, and flexible forms. 

AI is already making application development more accessible. With AI handling the complex, technical aspects of development, less-technical individuals can take a greater role in building applications. Consequently, AI can potentially democratize software engineering. 

Intelligent apps will continue to evolve, becoming more efficient, adaptable, and capable of learning from user interactions. Similarly, AI will inevitably become integral in every stage of app development.

Summary

AI has already created a new development paradigm—one in which machine intelligence and human creativity come together to produce innovative, efficient, and robust applications. AI already assists us at every stage of app development, from design and architecture to coding and debugging. 

Developers who embrace AI tools and integrate them into their processes can enhance their productivity, improve the quality of their applications, and expand the possibilities of their application development.  

The future of development is automated and AI-driven, and it’s here today. Getting started is easy—try an AI-powered development tool like GitHub Copilot to see how AI-assisted pair programming can enhance your productivity and code quality.

Exercise

+ + \ No newline at end of file diff --git a/30daysofIA/tags/azure-functions/page/7/index.html b/30daysofIA/tags/azure-functions/page/7/index.html index f5dce28bb4..f95084849f 100644 --- a/30daysofIA/tags/azure-functions/page/7/index.html +++ b/30daysofIA/tags/azure-functions/page/7/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "azure-functions" | Build Intelligent Apps On Azure +11 posts tagged with "azure-functions" | Build Intelligent Apps On Azure @@ -14,14 +14,13 @@ - - + +
-

9 posts tagged with "azure-functions"

View All Tags

· 4 min read
It's 30DaysOfIA

This Fall focus on building intelligent apps using AI and cloud-native technologies. #FallForIntelligentApps brings to you a learning journey to build your skills on creating differentiated experiences while modernizing your applications. It’s time to learn it all.

What We'll Cover

image

Get Ready To #FallForIntelligentApps starting September 18!

Today, we kick off the Fall season with content and activities to skill you up on all things Intelligent Apps or AI Apps on Azure with content, events, and community interactions! Read on to learn about what is coming!

Explore Our Initiatives

We have a number of initiatives planned for the month to help you learn and skill up on relevant technologies. Click on the links to visit the relevant pages for each

image

Register for the events!

What are 4 things you can do today, to jumpstart your learning journey?

#30Days Of Intelligent Apps

#30DaysOfIA is a series of daily blog posts grouped into themed weeks - taking you from core concepts to end-to-end solution examples in 30 days. Each article will provide conceptual lessons paired with exercises and resources to help you reinforce learnings and take next steps.

This series takes you through learning journey in six stages, each building on the previous week to help you skill up in a beginner-friendly way:

We will start with defining intelligent apps and then expand on how to build with cloud-native technologies like Azure Kubernetes Service, Azure Container Apps and Azure Functions, and take you through end-to-end scenarios for real world application development. Before we dive deep on intelligent apps, here is a high-level overview of the Intelligent Apps landscape on Azure:

image

Containers on Azure services offer you a wide range of capabilities, from simplicity to control to suit your different needs.

image -image

To start with the basics for developing Kubernetes applications, explore #30DaysOfCloudNative.

Cloud-native development when paired with serverless computing enhances your solution architecture for building cost optimized, resilient applications.

image

To start with the basics for serverless computing, explore #30DaysOfServerless.

Let’s Get Started

Now you know everything! We hope you are as excited as we are to dive into a full month of active learning and doing! Don't forget to subscribe for updates in your favorite feed reader! And look out for our first Intelligent Apps post Monday!

- - +
Skip to main content

11 posts tagged with "azure-functions"

View All Tags

· 10 min read
It's 30DaysOfIA

Explore real-world examples of how Intelligent Apps revolutionize logistics, retail, finance, manufacturing, tech, and medicine.

What We'll Cover:

  • Real-world businesses revolutionizing operations with Intelligent Apps
  • High level architecture for industry scenarios
  • Implementation architecture overview across industries

A decorative header image for the blog

Real-world Success Stories

According to IBM’s 2022 Global AI Adoption Index, 35% of surveyed companies already use AI in their business, and 42% are exploring the idea. Organizations lagging behind the AI curve will quickly fall behind early adopters, who are already enjoying its benefits and boosting their investment in this technology.

In the first part of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” we explored how organizations can use AI-powered apps, or intelligent apps, to transform their operations, decision-making, and customer experiences, leaping ahead of the competition.

Intelligent apps significantly improve operational efficiency and customer engagement across industries like logistics, retail, finance, manufacturing, tech, and healthcare. In this article, we’ll spotlight real-world success stories of businesses revolutionizing operations with intelligent apps using Microsoft Azure’s app, data, and AI services.

info

Register for the Learn Live on September 21 for a live guided experience with SMEs on how to build end-to-end solution architecture for real world implementation of Intelligent Apps.

Streamlining Operations: Intelligent Apps in the Airline Industry

Aerospace pioneer Airbus leveraged intelligent apps to streamline its operations and innovate its services with two ground-breaking modernizations that use Azure apps, data, and AI services to reimagine pilot training and predict aircraft maintenance issues. 

Aviation technology’s rapid evolution compelled Airbus to reconceive their approach to training pilots. The aircraft manufacturer created a pilot training chatbot by combining Azure AI Services with Speech to Text and Text to Speech. The bot helps trainee pilots navigate over 6,000 pages of technical information. Pilots can ask questions verbally for quick answers, aiding their understanding of dense information and speeding up training. And when Airbus needed to accommodate its military consumers, deploying its Kubernetes cluster to a disconnected, “air-gapped” environment met the strict security requirements of military aircraft training.

A chatbot could look like the following diagram as Azure services work together to create a conversational experience. Its components could include security and governance, bot logic and user experience (UX), bot cognition and intelligence, data extract, transfer, and load (ETL) operations, quality assurance and enhancements, logging, and monitoring and reporting.

A diagram of a conversational experience enabled by Azure services

Airbus also used AI to improve its aircraft maintenance and safety. They deployed AI Anomaly Detector to gather and analyze telemetry data from multiple flights, enabling more profound insights into wear and tear on military aircraft operating in harsh conditions. This AI application helps Airbus better understand each airplane’s health. Airbus can predict and fix potential problems before they occur, improving the aircraft’s safety and operational readiness.

AI and Azure are changing how organizations like Airbus operate, even in strict, highly regulated markets with stringent security requirements. And by streamlining pilot training and staying ahead of aircraft maintenance, Airbus helps its military and commercial clients reliably deliver their goods across the globe, maintaining their critical supply chains.

Boosting Customer Service: Intelligent Apps in the Retail Industry

The retail industry has been among the first to adopt and benefit from intelligent apps. For example, the leading American used car retailer, CarMax, uses intelligent apps to transform the car shopping experience.

Before embracing AI, CarMax faced a significant challenge: Manually creating text summaries was time-consuming and labor-intensive. CarMax decided to leverage Azure OpenAI Service to automatically generate car research pages, offering customers valuable insights while enhancing the website’s search engine rankings.

This AI-driven approach has significantly streamlined the process, achieving in a few months what would have taken years manually. Also, the AI system condenses thousands of customer reviews into a few readable sentences, offering potential buyers a quick overview of others’ experiences.

CarMax has improved their customer experience while boosting operational efficiency through AI adoption. CarMax’s editorial staff focuses on creating strategic, longer-form content with the time saved, enhancing productivity, and driving more website traffic.

CarMax’s success story serves as a testament to the potential of intelligent apps in the retail industry.

Enhancing Cybersecurity: Intelligent Apps in Finance

Intelligent apps also impact the highly regulated financial services sector. Swift, a leading infrastructure provider for financial messaging services, uses intelligent apps to enhance decision-making and combat financial crime.

The massive growth of cross-border transactions and instant payment networks led to a surge in financial fraud that can cost the industry hundreds of billions of dollars every year. So, Swift and Microsoft partnered to build an anomaly detection model using federated learning techniques with Azure Machine Learning and Azure confidential computing.

Microsoft Purview helped govern data, as the federated learning method partially trained each model in a silo before aggregating them. This approach enabled Swift and Microsoft to develop a highly accurate model without copying or moving data from Swift members’ secure locations, ensuring the highest level of security and privacy.

Financial organizations can now deploy this trained ML model to run a real-time fraud detection system, lowering the cost of financial fraud.

Organizations can deploy a fraud detection system like the following example on Microsoft Azure. The system ingests data from streams or storage, runs analytics, and uses ML to detect fraud and store results. It then alerts users and other systems to the security issue.

A diagram of an example fraud detection system

Swift provides the innovative model to its banking partners, who can share data to further train the model. Azure confidential computing securely runs new models on current data to accumulate insights, generating an ongoing learning cycle. This iterative approach strengthens the global detection of fraudulent financial transactions through an increasingly faster and more accurate model. 

The collaboration between Swift and Microsoft demonstrates how combining various Azure services creates Intelligent Apps to enhance decision-making in finance. These apps solve real problems at an unprecedented global scale. 

Enhancing Efficiency: Intelligent Apps in Manufacturing

3M, known for Post-it Notes and other innovative products, wielded the power of Intelligent Apps to standardize, automate, and accelerate its sales forecasting. The company needed a unified, automated approach to replace its multiple manual methods.

It adopted Azure Machine Learning and other Azure AI Services to develop, train, and implement 1,500 custom models for each region and division, replacing manual methods. This solution delivers timely finance, sales, and marketing insights while freeing teams from time-consuming manual data manipulation tasks.

3M used Azure Machine Learning, automated machine learning (AutoML), and the Many Models Solution Accelerator to train and score numerous machine learning models in parallel. This approach significantly shortened the company’s development cycle time.

Then, 3M integrated Microsoft Power BI into their solution to compile and visualize results more effectively. Executives now have an unbiased and unfiltered view of data for better decision-making. With its improved ability to forecast sales, 3M can focus on manufacturing the products it needs efficiently.

3M’s success demonstrates how combining various Azure services can enhance efficiency in the manufacturing industry. These apps improve operational processes while enabling employees to focus on higher-level tasks. Strategic decisions are more targeted, and business conversations are more meaningful.

Driving Innovation: Intelligent Apps in the Technology Industry

As expected, the tech industry is at the forefront of leveraging Intelligent Apps to drive innovation. Elastic saw an opportunity to use intelligent apps to optimize its search services and meet customer needs more effectively. The company built a managed, secure, and scalable solution for its customers, Elastic Cloud, by leveraging Microsoft Azure.

Elastic Cloud uses Azure Machine Learning and other Azure AI Services to automate many basic management tasks, such as backing up data, upgrading, and scaling deployments. This approach enables Elastic’s customers to focus less on managing the software and more on serving their own clients. Customers can quickly deploy and manage Elastic Cloud on Kubernetes (ECK) using Azure Kubernetes Service.

As a result, Elastic’s cloud business is growing twice as fast as its on-premises solutions. Enterprise search users have reported significant improvements in system availability and employee satisfaction.

Additionally, Elastic incorporated Azure OpenAI Service into its Elasticsearch Relevance Engine (ESRE), providing more relevant answers to search queries. ESRE enables text, vector, and hybrid search, offering a more effective and accurate response to queries.

Elastic’s success story demonstrates how combining various Azure services can create intelligent Apps to drive innovation in the tech industry. These apps help improve operational processes and deliver better results quickly.

Revolutionizing Healthcare: Intelligent Apps in Medicine

Intelligent apps also drive transformative changes in the healthcare sector. Cambridgeshire and Peterborough NHS Foundation Trust, a healthcare provider in the United Kingdom, gradually shifted their on-premises systems to the cloud using Azure, improving flexibility, and lowering costs. They needed a solution for their electronic patient record (EPR) system that integrates 23 years of data across multiple systems and formats.

The Trust used Microsoft Azure and Azure Cognitive Search, including semantic search for understanding intent and contexts and cognitive capabilities for extracting insights from data, to standardize and automate data management. This approach made legacy, archive, and live data more discoverable, saving clinicians’ time. The Trust’s solution searches through millions of clinical documents in seconds, even recognizing handwritten notes without needing a separate optical character recognition (OCR) solution to extract text from images (Azure Cognitive Search automatically does it). Clinicians can now uncover previously inaccessible insights, enabling them to offer more personalized care.

The Trust also used Integration Services tools such as Azure Logic Apps to create workflows without writing code and Event Grid to connect Azure and third-party services through a publisher-subscriber model. They switched on a live feed from their new EPR system to Azure to feed real-time information into other applications. Researchers can now access anonymized data to fuel medical innovations beyond the NHS.

The healthcare provider also invested heavily in Power BI’s data visualization and analytical functions to sift through massive amounts of information. This level of data accessibility was previously impossible with their on-premises system. The new insights have significantly improved clinicians’ efficiency for better patient care.

Data accessibility and security are critical in the healthcare industry. Intelligent Apps empower organizations like the Trust to revolutionize medical care. AI capabilities helped the Trust improve its operational efficiency and user experiences, setting a new standard for healthcare in the digital age.

Summary

AI is driving transformation and real-world successes across industries. Intelligent apps are reshaping our world by streamlining operations in manufacturing and logistics, enhancing customer experience in the retail sector, improving decision-making in finance, driving innovation in tech, and revolutionizing healthcare.

As organizations adopt AI, they’re innovating in ways that were once too difficult, expensive, or outright impossible. Companies that wait to embrace intelligent apps risk the chance of falling behind. By embracing intelligent apps and leveraging the capabilities of Azure, you can harness the transformative potential of AI to remain on the cutting edge of technology.

Exercise

+ + \ No newline at end of file diff --git a/30daysofIA/tags/azure-functions/page/8/index.html b/30daysofIA/tags/azure-functions/page/8/index.html index 0ef92595bc..a515b41fd5 100644 --- a/30daysofIA/tags/azure-functions/page/8/index.html +++ b/30daysofIA/tags/azure-functions/page/8/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "azure-functions" | Build Intelligent Apps On Azure +11 posts tagged with "azure-functions" | Build Intelligent Apps On Azure @@ -14,14 +14,13 @@ - - + +
-

9 posts tagged with "azure-functions"

View All Tags

· 4 min read
It's 30DaysOfIA

Continue The Learning Journey through Fall For Intelligent Apps! 🍂

What We'll Cover

Thank you! ♥️

image

It's hard to believe that JavaScript on Azure hack-together is ending! It seems like just yesterday that we launched this initiative, and yet here we are, 15 days later, with an incredible amount of learning and growth behind us. So... it's time for a wrap!

From the bottom of our hearts, we want to thank each and every one of you for your participation, engagement, and enthusiasm. It's been truly inspiring to see the passion and dedication from this strong community, and we're honored to be a part of it. ✨

Recap of The JavaScript on Azure Global Hack-Together

As we wrap up this exciting event, we wanted to take a moment to reflect on all that we've accomplished together. Over the last 15 days, we've covered a lot of ground, from the basics of contributing to Open source to the exploration of the Contoso Real Estate project from its Frontend to its Backend and future AI implementation.

Now that the hack-together is ending, we want to make sure that you have all the resources you need to continue honing your skills in the future. Whether you're looking to make your fist contribution to open source, become an open source maintainers, collaborate with others, or simply keep learning, there are plenty of resources out there to help you achieve your goals. So, let's dive in and explore all the ways you can continue to grow your JavaScript skills on Azure!

JSonAzure Hack-together Roadmap 📍:

hack-together-roadmap (2)

Recap on past Livestreams🌟:

Day 1️⃣: Opening Keynote (Hack-together Launch): Introduction to the Contoso Real Estate Open-source project!, managing complex and complex enterprise architecture, new announcements for JavaScript developers on Azure

Day 2️⃣: GitHub Copilot & Codespaces: Introduction to your AI pair programmer (GitHub Copilot) and your virtual developer environment on the cloud (GitHub Codespaces)

Day 6️⃣: Build your Frontend using Static Web Apps as part of a complex, modern composable frontends (or micro-frontends) and cloud-native applications.

Day 9️⃣: Build a Serverless Backend using Azure Functions

Day 1️⃣3️⃣: Easily connect to an Azure Cosmos DB, exploring its benefits and how to get started

Day 1️⃣5️⃣: Being in the AI Era, we dive into the Azure OpenAI Service and how you can start to build intelligent JavaScript applications

📖 Self-Learning Resources

  1. JavaScript on Azure Global Hack Together Module collection
  2. Lets #HackTogether: Javascript On Azure Keynote
  3. Step by Step Guide: Migrating v3 to v4 programming model for Azure Functions for Node.Js Application

Continue your journey with #FallForIntelligentApps

Join us this Fall on a learning journey to explore building intelligent apps. Combine the power of AI, cloud-native app development, and cloud-scale data to build highly differentiated digital experiences. Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure. Engage in a self-paced learning adventure by following along the #30Days of Intelligent Apps series, completing the Intelligent Apps Skills Challenge, joining the Product Group for a live Ask The Expert series or building an end to end solution architecture with a live guided experience through the Learn Live series. Discover more here.

Hands-on practice: Make your first contribution to open source!

Join our GitHUb Discussion Forum to connect with developers from every part of world, see contributions from other, find collaborators and make your first contribution to a real-world project! -Don't forget to give the repo a star ⭐

Resources

All resources are accessible on our landing page

- - +
Skip to main content

11 posts tagged with "azure-functions"

View All Tags

· 8 min read
It's 30DaysOfIA

Let’s start with understanding the power of intelligent applications.

What We'll Cover:

  • What are intelligent applications?
  • Categories of Intelligent Apps
  • Breaking down the role of AI in intelligent apps

A decorative header image for the blog

Demystifying Intelligent Applications: Leveraging AI in App Development

Until recent years, when you heard the term “artificial intelligence” (AI), you might have pictured autonomous robots from sci-fi films. Now, perhaps your mind jumps to big data and machine learning (ML). Maybe you’re even thinking about the latest ChatGPT plugins and transformer architectures. Today, AI is more than these imaginations: it’s a reality increasingly woven into our day-to-day routines through intelligent applications.

Intelligent apps combine cloud-scale compute, data, and predictive or generative AI. These applications help you automate tasks and make data-driven decisions, aiding in leveraging business intelligence to gain meaningful, practical outcomes. 

For developers, the AI landscape isn’t an abstract complexity. It’s a real, tangible opportunity for evolving traditional applications, making them “intelligent,” and delivering more value to users. And with tools like Azure Kubernetes Service, Azure Cosmos DB, and Azure AI Services, stepping into the world of intelligent apps isn’t a steep climb but a steady trek across a manageable learning curve.

The Three Categories of Intelligent Applications

Before we start building, it’s critical to understand what makes an app “intelligent.” It’s not a universal definition so much as an identifier for apps that fall into one or more of the three primary categories: 

  • Outcome-based — This designation represents the broadest category of intelligent apps, whose principal value for users is the intelligent outcomes they provide. These apps process data to provide insightful results that enhance decision-making or help with tasks like trend prediction, resource management, and customer relationship management.

  • Functionality-based — This type of intelligent app takes AI integration a degree further, integrating AI as a function of the app to create or process its outputs, enhancing the app’s overall interactivity and usefulness.

  • Feature-based — This is the narrowest category, in which the app’s AI/ML component is one of its core features and primary selling points

Let’s dive more deeply into each category.

Outcome-Based Apps: Intelligent Outcomes Drive Success

Outcome-based applications are all about the results provided to the user. They function beyond merely processing data to offer insightful outcomes that drive success.

Think of a personal fitness tracker app that uses AI to analyze the user’s activity and provides actionable recommendations for achieving a goal. This type of app not only tracks your steps, sleep patterns, and heart rate but uses that data to provide you with personalized health advice. It helps you make informed lifestyle choices — the “intelligent” outcome.

Another example is a social media marketing app that uses AI for time-saving data analysis, task automation, and trend prediction. AI-based sentiment analysis can gauge customer perception while natural language processing (NLP) accurately interprets customer inquiries. Task automation can schedule posts and automate responses to common customer queries, and trend forecasting helps model and predict customer behavior to further personalize targeted promotional efforts. Building this type of intelligence into your apps provides cost-effective, actionable insights that guide sales and marketing strategies.

Functionality-Based Apps: AI-Driven Features Enhance User Experiences

As the name suggests, functionality-based applications integrate AI into how the app functions, but the end user drives the action and outcome. In this category of apps, AI takes a more active role in the user’s interaction by allowing them to use AI-created or processed output. These apps leverage AI technologies like Azure AI Services for NLP, image recognition, and pattern recognition to improve user experience.

Consider a music app that generates personalized playlists based on your listening habits or your smartphone’s camera app that uses AI to recognize faces and optimize photo settings. Even more advanced are the language translation apps that translate text from one language to another and can even understand and maintain the context and subtleties of the conversation. They use NLP to accomplish these feats.

Similarly, consider the latest e-commerce apps, whose AI-driven functionalities enable customers to try on clothes virtually, significantly enhancing the online shopping experience.

Feature-Based Apps: Integrating Advanced AI/ML Components

Finally, there are feature-based intelligent apps. These apps incorporate advanced AI/ML components like neural networks, internal large language models (LLMs), and sophisticated algorithms as core features for the user. In this type of application, AI is the primary feature for users to engage with and the primary selling point for the app.

Some examples of feature-based applications are chatbots and virtual agents built using Azure Bot Service. These robo-representatives use AI to provide more natural and human-like conversation, balancing superior efficiency with a friendlier, more personalized user experience. The new Bing is a prime example of this implementation, integrating AI to give users a knowledgeable copilot for web searches.

Similarly, OpenAI’s ChatGPT allows users access to its state-of-the-art language models available as Microsoft’s Azure OpenAI Service. It leverages the power of the highly sophisticated GPT-3 LLM to perform tasks like drafting emails, writing code, answering general knowledge questions, translating between languages, and tutoring in numerous subjects.

These apps don’t just use AI to provide outputs or improve user experiences: They push the boundaries of what’s possible in app development by integrating AI/ML components.

info

Register for the live Q&A Ask The Expert session on September 26 with the Azure Functions product engineering team.

Breaking Down the Role of AI in Intelligent Applications

Intelligent apps reflect the merging of traditional software design with the dynamic capabilities of AI. Let’s explore how AI integrates and amplifies these applications.

At its core, AI augments applications with the capacity to self-learn and self-improve. It allows you to create applications that predict, react, and adapt to a constantly changing environment. In the power grid management system for a major city, an AI-infused application can forecast power usage trends, react to anomalies, and adapt to sudden demand surges, ensuring optimal efficiency and preventing blackouts.

But AI’s role doesn’t end at prediction or decision-making: It also automates tasks. In enterprise solutions, it has moved beyond simple automation to encompass intelligent process automation (IPA). In this arena, AI algorithms understand, learn, and then automate entire digital workflows. For developers, this is a fertile ground for creating applications that can dramatically improve the operational efficiency of businesses.

Even more, AI substantially aids in development tasks. For example, there’s GitHub Copilot, an AI-powered code assistant that offers suggestions based not only on syntax but on development best practices and the intent of the code. AI has evolved from a tool you use to build apps to a partner that helps in creating them. 

Using AI to build intelligent apps isn’t about throwing out what’s worked before. It’s about enhancing the components that have the most impact and achieve the best results. Incorporating AI in apps elevates their predictive abilities, streamlines complex tasks, and even aids in the development and creation process. When you build intelligent apps, you don’t just address current problems. You anticipate future challenges and craft solutions that adapt over time.

Summary

As developers the power of AI is in our hands, and it’s our responsibility to harness its full potential. Each category tells the story of AI's transformative potential, from applications that deliver intelligent outcomes to those with AI capabilities to enrich the user experience and those pushing boundaries with advanced AI/ML technology.

Accessible and robust platforms like Azure AI can simplify the process of developing Intelligent Applications. Whether you’re refining an existing app or building a new one, just entering the world of AI, or already familiar with the landscape, take a moment to appreciate the potential that AI integration offers. You’re embracing this exciting frontier and shaping the future of app development. 

Exercise

  • Complete the Cloud Skills Challenge to build on your apps and AI skills.
  • Watch Episode 01 of the Serverless Edition Learn Live session to learn how to build an end-to-end intelligent app solution.
+ + \ No newline at end of file diff --git a/30daysofIA/tags/azure-functions/page/9/index.html b/30daysofIA/tags/azure-functions/page/9/index.html index 8d00fdfe87..6be715e0bf 100644 --- a/30daysofIA/tags/azure-functions/page/9/index.html +++ b/30daysofIA/tags/azure-functions/page/9/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "azure-functions" | Build Intelligent Apps On Azure +11 posts tagged with "azure-functions" | Build Intelligent Apps On Azure @@ -14,13 +14,14 @@ - - + +
-

9 posts tagged with "azure-functions"

View All Tags

· 2 min read
It's 30DaysOfIA

September is almost here - and that can only mean one thing!! It's time to 🍂 Fall for something new and exciting and spend a few weeks skilling up on relevant tools, techologies and solutions!!

Last year, we focused on #ServerlessSeptember. This year, we're building on that theme with the addition of cloud-scale Data, cloud-native Technologies and cloud-based AI integrations to help you modernize and build intelligent apps for the enterprise!

Watch this space - and join us in September to learn more!

- - +
Skip to main content

11 posts tagged with "azure-functions"

View All Tags

· 4 min read
It's 30DaysOfIA

This Fall focus on building intelligent apps using AI and cloud-native technologies. #FallForIntelligentApps brings to you a learning journey to build your skills on creating differentiated experiences while modernizing your applications. It’s time to learn it all.

What We'll Cover

image

Get Ready To #FallForIntelligentApps starting September 18!

Today, we kick off the Fall season with content and activities to skill you up on all things Intelligent Apps or AI Apps on Azure with content, events, and community interactions! Read on to learn about what is coming!

Explore Our Initiatives

We have a number of initiatives planned for the month to help you learn and skill up on relevant technologies. Click on the links to visit the relevant pages for each

image

Register for the events!

What are 4 things you can do today, to jumpstart your learning journey?

#30Days Of Intelligent Apps

#30DaysOfIA is a series of daily blog posts grouped into themed weeks - taking you from core concepts to end-to-end solution examples in 30 days. Each article will provide conceptual lessons paired with exercises and resources to help you reinforce learnings and take next steps.

This series takes you through learning journey in six stages, each building on the previous week to help you skill up in a beginner-friendly way:

We will start with defining intelligent apps and then expand on how to build with cloud-native technologies like Azure Kubernetes Service, Azure Container Apps and Azure Functions, and take you through end-to-end scenarios for real world application development. Before we dive deep on intelligent apps, here is a high-level overview of the Intelligent Apps landscape on Azure:

image

Containers on Azure services offer you a wide range of capabilities, from simplicity to control to suit your different needs.

image +image

To start with the basics for developing Kubernetes applications, explore #30DaysOfCloudNative.

Cloud-native development when paired with serverless computing enhances your solution architecture for building cost optimized, resilient applications.

image

To start with the basics for serverless computing, explore #30DaysOfServerless.

Let’s Get Started

Now you know everything! We hope you are as excited as we are to dive into a full month of active learning and doing! Don't forget to subscribe for updates in your favorite feed reader! And look out for our first Intelligent Apps post Monday!

+ + \ No newline at end of file diff --git a/30daysofIA/tags/azure-kubernetes-service/index.html b/30daysofIA/tags/azure-kubernetes-service/index.html index 74e2899186..8a809bb888 100644 --- a/30daysofIA/tags/azure-kubernetes-service/index.html +++ b/30daysofIA/tags/azure-kubernetes-service/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "azure-kubernetes-service" | Build Intelligent Apps On Azure +11 posts tagged with "azure-kubernetes-service" | Build Intelligent Apps On Azure @@ -14,13 +14,13 @@ - - + +
-

9 posts tagged with "azure-kubernetes-service"

View All Tags

· 10 min read
It's 30DaysOfIA

Prepare your organization to adopt intelligent apps by taking a change management approach. Assess your readiness, upskill teams, create a culture of change and overcome resistance, and prepare for future evolution to pave the way for a smooth transition into an AI-centric company.

What We'll Cover:

  • Role of change management in modernizing to intelligent apps
  • Skills needed for building intelligent apps in your organization
  • How to build a culture of change to embrace AI
  • How to prepare for the future of intelligent apps and continuous evolution

image of modernizing AI solutions for intelligent apps

Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management

As we’ve discussed throughout this series, intelligent apps and generative AI are rewriting the rules of business and application development. The first article of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” explored how intelligent apps can enhance user experiences and drive businesses growth. These advancements are opening new opportunities and markets for companies ready to embrace them.

However, embracing this digital transformation is more complex than flipping a switch. It requires adjustments to the technical side, which we explored in the series’ fifth article, “Preparing the Path for Intelligent Apps: Transitioning from On-Premises/IaaS to Cloud Native Applications.” It also demands a shift in how organizations adapt to change and approach app design and development. Let’s explore that mindset transition with tips to help you make it happen.

The Role of Change Management in Transitioning to Intelligent Apps

Transitioning to intelligent apps isn’t merely a technological change. It’s a profound shift in how businesses perceive and operate. This move toward artificial intelligence (AI)-centric approach isn’t meant to replace humans with machines or radically overhaul all business operations.

Instead, intelligent apps should augment human capabilities, streamline processes, and foster an environment where innovation flourishes. They expand toolkits and techniques to include AI-assisted development tools like GitHub Copilot and AI-enabled workflows, helping us design and deliver AI-enhanced experiences.

Effective change management strategies are crucial in helping employees adapt to these new ways of working with intelligent apps, ensuring that the transition doesn’t disrupt productivity or morale.

Team and organization strategies should: 

  • Assess the impact of any proposed changes.  
  • Communicate the benefits and reasons behind the change to all affected team members.  
  • Provide the necessary training and support.  
  • Integrate the changes into the culture and daily operations. 

By incorporating this robust change management approach, you can identify and mitigate potential transition risks early. Moreover, this strategy ensures that employees are engaged throughout the process, fostering a sense of involvement and ownership. This, in turn, cultivates a culture suited for innovation through intelligent apps. 

These organizational changes focus on the following aspects: organizational readiness, skillset, culture of change and overcoming resistance, and adaptability. Let’s explore them below.

info

Register for the intelligent apps webinar on Driving Business Value by Modernizing with Cloud-Native & AI with Microsoft and Forrester on September 26. 

Explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries. 

Identifying Organizational Readiness for Intelligent Apps

The first step in preparing your organization for intelligent apps is to assess your current readiness for change. Analyze various elements of your organization, from infrastructure and workforce skills to strategic vision and overall culture.

Can your apps and systems integrate with AI platforms like Microsoft Azure AI? Is your workforce comfortable working alongside AI, or are they fearful and resistant? Does your strategic vision incorporate AI’s transformative potential, or does it fail to acknowledge AI as a critical driver of future growth?

These questions identify the challenges of integrating AI into an organization and determine the company’s readiness for intelligent apps.

Embracing intelligent apps involves more than being ready with your technological infrastructure or having the right technology or tools. You need flexible organizational structures and processes driving your business to accommodate new ideas and capabilities. Your teams must feel empowered to own and drive them. Much like Microsoft’s shift to AI-powered services—like AI-powered Bing and Edge and Microsoft 365 Copilot—embracing AI organization-wide requires a holistic vision and willingness to innovate, with teams capable of delivering on the plan.

After accessing these critical areas for organizational readiness, you can start planning a strategy for this transition to an AI-ready organization. Identify skill gaps and work to bridge them while nurturing an environment that appreciates and harnesses the potential of AI and intelligent apps.

Nurturing the Indispensable Skills for Intelligent Apps

Tasking your AI specialists with preparing your organization to use and build intelligent apps isn’t enough. Accomplishing this goal requires a cross-functional effort and a diverse skill set.

Developing intelligent apps involves data scientists managing and interpreting vast amounts of data, machine learning engineers building and training the models, and traditional developers integrating these models into usable applications. However, the impact of generative AI spans beyond creating apps. AI fluency across the entire organization—from sales and marketing to accounting and legal departments—offers substantial benefits, sparking new ideas and solutions from sometimes unexpected sources.

Teams' AI skills might roughly fall into three levels: 

  • Fundamental skills — Being familiar and efficient with using AI tools, for example, knowing how to write effective prompts for large language models (LLMs).
  • Visionary skills — Recognizing AI’s strengths and weaknesses, for example, its creativity and hallucinations, that is, unexpected output.
  • Expert skills — Managing data and training models and integrating AI technology into apps, such as incorporating the OpenAI application programming interface (API) into an Intelligent App.

Based on these levels, you can gauge your team's skill readiness to pinpoint the most significant gaps, whether in general AI knowledge across your organization or within technical development teams.

As intelligent apps become increasingly prevalent, upskilling your workforce is crucial. This transition opens a new realm of skills and capabilities for your team to master. You can offer machine learning, data analysis, and cloud computing training programs—such as Microsoft’s Azure AI Fundamentals certification—to empower your team to confidently navigate the intelligent apps landscape.

Nurturing these skills within your organization may require hiring new talent, reskilling current employees, and creating an environment that promotes learning and experimentation. Specifically, if your team understands tools like the Microsoft Azure AI platform, they could contribute significantly to developing and maintaining intelligent apps.

An upskilled team is more than a functional unit. It’s a think tank to drive innovation and improve your organization’s competitive standing in the digital marketplace.

Building a Culture of Change to Embrace AI

Even with the right people and skills in place, change remains an uphill battle for organizations committed to traditional processes. 

Transitioning to intelligent apps is as much about mindset as technology and skills. It requires fostering a culture open to change and innovation that understands AI’s transformative potential and embraces it to enhance rather than replace human capabilities. 

Leaders play a pivotal role in cultivating this mindset. As they model a positive attitude toward AI, emphasize its benefits, and address any concerns transparently, leaders significantly contribute to the organization’s readiness to adopt intelligent apps. 

The most critical factor in this transition is enabling an AI-friendly culture. It’s about encouraging curiosity, rewarding innovative thinking, and creating an environment where people thrive on learning and experimentation. 

Empowering teams to leverage practical AI tools and AI-driven data insights to aid decision-making helps motivate employees to use and explore AI’s potential, improve their skills, and contribute to the successful integration of intelligent apps. Such an environment is fertile ground to grow breakthrough ideas and innovative solutions. 

Overcoming Resistance to Change and Adopting Intelligent Apps

Despite AI’s potential, change isn’t easy. It takes effort and is especially challenging for organizations used to existing processes. This resistance to change is a significant roadblock when transitioning to intelligent apps. 

Overcoming this resistance involves:  

  • Understanding employees’ concerns. 
  • Challenging underlying assumptions about traditional systems and outdated processes that hinder change. 
  • Providing comprehensive training for workers to use AI effectively.
  • Demonstrating the benefits and opportunities of intelligent apps. 
  • Clear and continuous communication to dispel fears and misconceptions about AI.

These steps will foster greater acceptance and enthusiasm for intelligent apps in your teams. 

Preparing for the Future of Intelligent Apps and Continuous Evolution

Finally, it’s essential to understand that generative AI is just beginning to gain momentum. Adjusting to a world of intelligent apps isn’t a one-time change—it’s a continuous evolution. 

As AI technologies mature and evolve and more businesses realize their benefits, we can expect the landscape of intelligent apps to expand further. Organizations can cultivate a mindset of continuous learning, flexibility, and adaptation to prepare for this future. Staying ahead in this dynamic field requires organizations to keep up with the latest AI advancements, continually reassess their strategies, and pivot when necessary. 

The domain of intelligent apps is rapidly advancing. Organizations must prepare to navigate this dynamic landscape to stay ahead of the curve.

Summary

Preparing for AI and intelligent apps is a strategic decision. It requires thoughtful preparation and a culture that embraces change.

With the proper preparation and mindset, adopting AI can become a smoother, collaborative path to innovation for the whole organization. Integrating AI into your organization does not discard what has worked in the past. It simply enhances and augments those processes for a better future.

Consider the points above to assess your organization’s readiness and prepare to welcome intelligent apps.

In the first week of this series, you learned about intelligent apps, how organizations today find success through intelligent apps, what AI-assisted application development looks like, ways to leverage the power of generative AI, the technical changes needed to prepare for intelligent apps, and, in this article, the organizational changes required to harness the power of AI.

The future is intelligent. Use this series as a guideline to navigate challenges while preparing for and embracing AI's opportunities for an intelligent tomorrow.

Exercise

- - +
Skip to main content

11 posts tagged with "azure-kubernetes-service"

View All Tags

· 10 min read
It's 30DaysOfIA

Prepare your organization to adopt intelligent apps by taking a change management approach. Assess your readiness, upskill teams, create a culture of change and overcome resistance, and prepare for future evolution to pave the way for a smooth transition into an AI-centric company.

What We'll Cover:

  • Role of change management in modernizing to intelligent apps
  • Skills needed for building intelligent apps in your organization
  • How to build a culture of change to embrace AI
  • How to prepare for the future of intelligent apps and continuous evolution

image of modernizing AI solutions for intelligent apps

Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management

As we’ve discussed throughout this series, intelligent apps and generative AI are rewriting the rules of business and application development. The first article of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” explored how intelligent apps can enhance user experiences and drive businesses growth. These advancements are opening new opportunities and markets for companies ready to embrace them.

However, embracing this digital transformation is more complex than flipping a switch. It requires adjustments to the technical side, which we explored in the series’ fifth article, “Preparing the Path for Intelligent Apps: Transitioning from On-Premises/IaaS to Cloud Native Applications.” It also demands a shift in how organizations adapt to change and approach app design and development. Let’s explore that mindset transition with tips to help you make it happen.

The Role of Change Management in Transitioning to Intelligent Apps

Transitioning to intelligent apps isn’t merely a technological change. It’s a profound shift in how businesses perceive and operate. This move toward artificial intelligence (AI)-centric approach isn’t meant to replace humans with machines or radically overhaul all business operations.

Instead, intelligent apps should augment human capabilities, streamline processes, and foster an environment where innovation flourishes. They expand toolkits and techniques to include AI-assisted development tools like GitHub Copilot and AI-enabled workflows, helping us design and deliver AI-enhanced experiences.

Effective change management strategies are crucial in helping employees adapt to these new ways of working with intelligent apps, ensuring that the transition doesn’t disrupt productivity or morale.

Team and organization strategies should: 

  • Assess the impact of any proposed changes.  
  • Communicate the benefits and reasons behind the change to all affected team members.  
  • Provide the necessary training and support.  
  • Integrate the changes into the culture and daily operations. 

By incorporating this robust change management approach, you can identify and mitigate potential transition risks early. Moreover, this strategy ensures that employees are engaged throughout the process, fostering a sense of involvement and ownership. This, in turn, cultivates a culture suited for innovation through intelligent apps. 

These organizational changes focus on the following aspects: organizational readiness, skillset, culture of change and overcoming resistance, and adaptability. Let’s explore them below.

info

Register for the intelligent apps webinar on Driving Business Value by Modernizing with Cloud-Native & AI with Microsoft and Forrester on September 26. 

Explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries. 

Identifying Organizational Readiness for Intelligent Apps

The first step in preparing your organization for intelligent apps is to assess your current readiness for change. Analyze various elements of your organization, from infrastructure and workforce skills to strategic vision and overall culture.

Can your apps and systems integrate with AI platforms like Microsoft Azure AI? Is your workforce comfortable working alongside AI, or are they fearful and resistant? Does your strategic vision incorporate AI’s transformative potential, or does it fail to acknowledge AI as a critical driver of future growth?

These questions identify the challenges of integrating AI into an organization and determine the company’s readiness for intelligent apps.

Embracing intelligent apps involves more than being ready with your technological infrastructure or having the right technology or tools. You need flexible organizational structures and processes driving your business to accommodate new ideas and capabilities. Your teams must feel empowered to own and drive them. Much like Microsoft’s shift to AI-powered services—like AI-powered Bing and Edge and Microsoft 365 Copilot—embracing AI organization-wide requires a holistic vision and willingness to innovate, with teams capable of delivering on the plan.

After accessing these critical areas for organizational readiness, you can start planning a strategy for this transition to an AI-ready organization. Identify skill gaps and work to bridge them while nurturing an environment that appreciates and harnesses the potential of AI and intelligent apps.

Nurturing the Indispensable Skills for Intelligent Apps

Tasking your AI specialists with preparing your organization to use and build intelligent apps isn’t enough. Accomplishing this goal requires a cross-functional effort and a diverse skill set.

Developing intelligent apps involves data scientists managing and interpreting vast amounts of data, machine learning engineers building and training the models, and traditional developers integrating these models into usable applications. However, the impact of generative AI spans beyond creating apps. AI fluency across the entire organization—from sales and marketing to accounting and legal departments—offers substantial benefits, sparking new ideas and solutions from sometimes unexpected sources.

Teams' AI skills might roughly fall into three levels: 

  • Fundamental skills — Being familiar and efficient with using AI tools, for example, knowing how to write effective prompts for large language models (LLMs).
  • Visionary skills — Recognizing AI’s strengths and weaknesses, for example, its creativity and hallucinations, that is, unexpected output.
  • Expert skills — Managing data and training models and integrating AI technology into apps, such as incorporating the OpenAI application programming interface (API) into an Intelligent App.

Based on these levels, you can gauge your team's skill readiness to pinpoint the most significant gaps, whether in general AI knowledge across your organization or within technical development teams.

As intelligent apps become increasingly prevalent, upskilling your workforce is crucial. This transition opens a new realm of skills and capabilities for your team to master. You can offer machine learning, data analysis, and cloud computing training programs—such as Microsoft’s Azure AI Fundamentals certification—to empower your team to confidently navigate the intelligent apps landscape.

Nurturing these skills within your organization may require hiring new talent, reskilling current employees, and creating an environment that promotes learning and experimentation. Specifically, if your team understands tools like the Microsoft Azure AI platform, they could contribute significantly to developing and maintaining intelligent apps.

An upskilled team is more than a functional unit. It’s a think tank to drive innovation and improve your organization’s competitive standing in the digital marketplace.

Building a Culture of Change to Embrace AI

Even with the right people and skills in place, change remains an uphill battle for organizations committed to traditional processes. 

Transitioning to intelligent apps is as much about mindset as technology and skills. It requires fostering a culture open to change and innovation that understands AI’s transformative potential and embraces it to enhance rather than replace human capabilities. 

Leaders play a pivotal role in cultivating this mindset. As they model a positive attitude toward AI, emphasize its benefits, and address any concerns transparently, leaders significantly contribute to the organization’s readiness to adopt intelligent apps. 

The most critical factor in this transition is enabling an AI-friendly culture. It’s about encouraging curiosity, rewarding innovative thinking, and creating an environment where people thrive on learning and experimentation. 

Empowering teams to leverage practical AI tools and AI-driven data insights to aid decision-making helps motivate employees to use and explore AI’s potential, improve their skills, and contribute to the successful integration of intelligent apps. Such an environment is fertile ground to grow breakthrough ideas and innovative solutions. 

Overcoming Resistance to Change and Adopting Intelligent Apps

Despite AI’s potential, change isn’t easy. It takes effort and is especially challenging for organizations used to existing processes. This resistance to change is a significant roadblock when transitioning to intelligent apps. 

Overcoming this resistance involves:  

  • Understanding employees’ concerns. 
  • Challenging underlying assumptions about traditional systems and outdated processes that hinder change. 
  • Providing comprehensive training for workers to use AI effectively.
  • Demonstrating the benefits and opportunities of intelligent apps. 
  • Clear and continuous communication to dispel fears and misconceptions about AI.

These steps will foster greater acceptance and enthusiasm for intelligent apps in your teams. 

Preparing for the Future of Intelligent Apps and Continuous Evolution

Finally, it’s essential to understand that generative AI is just beginning to gain momentum. Adjusting to a world of intelligent apps isn’t a one-time change—it’s a continuous evolution. 

As AI technologies mature and evolve and more businesses realize their benefits, we can expect the landscape of intelligent apps to expand further. Organizations can cultivate a mindset of continuous learning, flexibility, and adaptation to prepare for this future. Staying ahead in this dynamic field requires organizations to keep up with the latest AI advancements, continually reassess their strategies, and pivot when necessary. 

The domain of intelligent apps is rapidly advancing. Organizations must prepare to navigate this dynamic landscape to stay ahead of the curve.

Summary

Preparing for AI and intelligent apps is a strategic decision. It requires thoughtful preparation and a culture that embraces change.

With the proper preparation and mindset, adopting AI can become a smoother, collaborative path to innovation for the whole organization. Integrating AI into your organization does not discard what has worked in the past. It simply enhances and augments those processes for a better future.

Consider the points above to assess your organization’s readiness and prepare to welcome intelligent apps.

In the first week of this series, you learned about intelligent apps, how organizations today find success through intelligent apps, what AI-assisted application development looks like, ways to leverage the power of generative AI, the technical changes needed to prepare for intelligent apps, and, in this article, the organizational changes required to harness the power of AI.

The future is intelligent. Use this series as a guideline to navigate challenges while preparing for and embracing AI's opportunities for an intelligent tomorrow.

Exercise

+ + \ No newline at end of file diff --git a/30daysofIA/tags/azure-kubernetes-service/page/10/index.html b/30daysofIA/tags/azure-kubernetes-service/page/10/index.html new file mode 100644 index 0000000000..fb1c83e3ee --- /dev/null +++ b/30daysofIA/tags/azure-kubernetes-service/page/10/index.html @@ -0,0 +1,27 @@ + + + + + +11 posts tagged with "azure-kubernetes-service" | Build Intelligent Apps On Azure + + + + + + + + + + + + + + +
+

11 posts tagged with "azure-kubernetes-service"

View All Tags

· 4 min read
It's 30DaysOfIA

Continue The Learning Journey through Fall For Intelligent Apps! 🍂

What We'll Cover

Thank you! ♥️

image

It's hard to believe that JavaScript on Azure hack-together is ending! It seems like just yesterday that we launched this initiative, and yet here we are, 15 days later, with an incredible amount of learning and growth behind us. So... it's time for a wrap!

From the bottom of our hearts, we want to thank each and every one of you for your participation, engagement, and enthusiasm. It's been truly inspiring to see the passion and dedication from this strong community, and we're honored to be a part of it. ✨

Recap of The JavaScript on Azure Global Hack-Together

As we wrap up this exciting event, we wanted to take a moment to reflect on all that we've accomplished together. Over the last 15 days, we've covered a lot of ground, from the basics of contributing to Open source to the exploration of the Contoso Real Estate project from its Frontend to its Backend and future AI implementation.

Now that the hack-together is ending, we want to make sure that you have all the resources you need to continue honing your skills in the future. Whether you're looking to make your fist contribution to open source, become an open source maintainers, collaborate with others, or simply keep learning, there are plenty of resources out there to help you achieve your goals. So, let's dive in and explore all the ways you can continue to grow your JavaScript skills on Azure!

JSonAzure Hack-together Roadmap 📍:

hack-together-roadmap (2)

Recap on past Livestreams🌟:

Day 1️⃣: Opening Keynote (Hack-together Launch): Introduction to the Contoso Real Estate Open-source project!, managing complex and complex enterprise architecture, new announcements for JavaScript developers on Azure

Day 2️⃣: GitHub Copilot & Codespaces: Introduction to your AI pair programmer (GitHub Copilot) and your virtual developer environment on the cloud (GitHub Codespaces)

Day 6️⃣: Build your Frontend using Static Web Apps as part of a complex, modern composable frontends (or micro-frontends) and cloud-native applications.

Day 9️⃣: Build a Serverless Backend using Azure Functions

Day 1️⃣3️⃣: Easily connect to an Azure Cosmos DB, exploring its benefits and how to get started

Day 1️⃣5️⃣: Being in the AI Era, we dive into the Azure OpenAI Service and how you can start to build intelligent JavaScript applications

📖 Self-Learning Resources

  1. JavaScript on Azure Global Hack Together Module collection
  2. Lets #HackTogether: Javascript On Azure Keynote
  3. Step by Step Guide: Migrating v3 to v4 programming model for Azure Functions for Node.Js Application

Continue your journey with #FallForIntelligentApps

Join us this Fall on a learning journey to explore building intelligent apps. Combine the power of AI, cloud-native app development, and cloud-scale data to build highly differentiated digital experiences. Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure. Engage in a self-paced learning adventure by following along the #30Days of Intelligent Apps series, completing the Intelligent Apps Skills Challenge, joining the Product Group for a live Ask The Expert series or building an end to end solution architecture with a live guided experience through the Learn Live series. Discover more here.

Hands-on practice: Make your first contribution to open source!

Join our GitHUb Discussion Forum to connect with developers from every part of world, see contributions from other, find collaborators and make your first contribution to a real-world project! +Don't forget to give the repo a star ⭐

Resources

All resources are accessible on our landing page

+ + + + \ No newline at end of file diff --git a/30daysofIA/tags/azure-kubernetes-service/page/11/index.html b/30daysofIA/tags/azure-kubernetes-service/page/11/index.html new file mode 100644 index 0000000000..909dd45cc0 --- /dev/null +++ b/30daysofIA/tags/azure-kubernetes-service/page/11/index.html @@ -0,0 +1,26 @@ + + + + + +11 posts tagged with "azure-kubernetes-service" | Build Intelligent Apps On Azure + + + + + + + + + + + + + + +
+

11 posts tagged with "azure-kubernetes-service"

View All Tags

· 2 min read
It's 30DaysOfIA

September is almost here - and that can only mean one thing!! It's time to 🍂 Fall for something new and exciting and spend a few weeks skilling up on relevant tools, techologies and solutions!!

Last year, we focused on #ServerlessSeptember. This year, we're building on that theme with the addition of cloud-scale Data, cloud-native Technologies and cloud-based AI integrations to help you modernize and build intelligent apps for the enterprise!

Watch this space - and join us in September to learn more!

+ + + + \ No newline at end of file diff --git a/30daysofIA/tags/azure-kubernetes-service/page/2/index.html b/30daysofIA/tags/azure-kubernetes-service/page/2/index.html index d2efe4a8c7..ee18b57413 100644 --- a/30daysofIA/tags/azure-kubernetes-service/page/2/index.html +++ b/30daysofIA/tags/azure-kubernetes-service/page/2/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "azure-kubernetes-service" | Build Intelligent Apps On Azure +11 posts tagged with "azure-kubernetes-service" | Build Intelligent Apps On Azure @@ -14,13 +14,13 @@ - - + +
-

9 posts tagged with "azure-kubernetes-service"

View All Tags

· 9 min read
It's 30DaysOfIA

Learn best practices for modernizing your on-premises or IaaS solutions to intelligent apps in the cloud.

What We'll Cover:

  • Traditional vs Intelligent Apps
  • On-Premises/IaaS vs. Cloud-Native Platforms
  • Modernization strategy for building Intelligent Apps

image of modernizing AI solutions for intelligent apps

Preparing the Path for Intelligent Apps: Transitioning from On-Premises/IaaS to Cloud-Native Applications

The proliferation of generative AI is paving the way for intelligent applications, software that leverages AI capabilities to deliver unparalleled functionality and user experience. As we covered in a previous article, “Demystifying Intelligent Applications: Leveraging AI in App Development,” intelligent Apps aren’t just products of advanced coding. They interact, learn, and evolve thanks to modern AI and machine learning (ML) breakthroughs. 

The paradigm shift from traditional to intelligent apps demands that we change our approach to the technical challenges of software and architecture design. It also necessitates adopting new ways of thinking and operating within an organization, which we explore in greater depth in “Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management.” 

This article explores the technical infrastructure powering intelligent apps, providing a roadmap to transition traditional on-premises or Infrastructure as a Service (IaaS) solutions to intelligent apps deployed to cloud native platforms and services. We’ll discover the differences between conventional and intelligent apps, options for re-architecting existing applications, and critical strategic considerations integral to modernizing the application-building approach. 

Understanding the Shift: Traditional vs. Intelligent Applications

Intelligent apps are more than just an incremental evolution from traditional apps. They involve a fundamental shift in software solution design, development, and deployment. A clear understanding of how each paradigm differs is crucial for comprehending the impact of this transition. 

Functionality and User Experience

Traditional applications, often rule-based and rigid, rely on preprogrammed operations. A traditional weather app, for example, typically presents structured, location-based forecasts using a predetermined data source. Meanwhile, an intelligent app leverages AI for data-driven decision-making and personalization. Rather than merely displaying the forecast, it integrates ML with additional data sources—like a user’s calendar and fitness data—to determine their favorite outdoor activities, provide relevant clothing suggestions, and even automatically design ideal vacations and times based on the user’s optimal weather preferences. 

Similarly, while traditional applications tend to offer a one-size-fits-all UI, intelligent apps use AI to create tailored interactions. For instance, an intelligent banking app might use generative AI to provide a voice-enabled UI that lets customers use natural language to ask about transactions and receive personalized financial advice.

Infrastructure

Intelligent apps integrating these sophisticated features require highly scalable, flexible infrastructure optimized for AI functionality and able to manage large volumes of real-time data. Unlike traditional applications deployed on-premises or in infrastructure as a service (IaaS) environments, intelligent apps demand the flexibility and scalability of cloud native architectures.

An on-premises or IaaS-based e-commerce application may struggle with connecting its data to AI services or scaling to accommodate voluminous real-time data and requests. But an intelligent app built with a cloud native approach and scalability at its core can handle variable demand much more efficiently. It also maintains its infrastructure components on demand, whether for collecting data for AI/ML consumption or user-facing features like AI-enabled customer support and personalization.

Consider an intelligent app built on Azure that creates ChatGPT-like experiences with custom data. It might connect Azure Open AI and Azure Cognitive Search to an app running on Azure Container Apps or Azure Kubernetes Service. 

The architecture might look like the following diagram based on this demo app:

diagram of a demo app architecture

While the cloud is well-equipped to handle the challenges associated with shifting to AI-centric infrastructure—handling real-time data processing and storage, integrating and managing AI services, configuring event-driven and microservices architectures, and ensuring security across the distributed environment—re-architecting a traditional app to an intelligent app requires careful planning and execution to ensure it benefits fully from the capabilities of AI and cloud computing.

On-Premises/IaaS vs. Cloud-Native Platforms

While on-premises and IaaS solutions provide substantial control over data and processes, this fine-grained control comes with setup and maintenance costs and may lack the scalability necessary for AI-driven Intelligent Apps. On-premises hosting involves purchasing and maintaining hardware, including servers and storage systems. IaaS involves leasing virtual hardware and then hiring and training IT personnel for the infrastructure’s operation, maintenance, and security.

Conversely, infrastructure built on cloud native app services with Microsoft Azure offers virtually unlimited scalability without ongoing hardware and maintenance costs. This feature is ideal for intelligent apps that need to adapt and scale to accommodate fluctuating workloads and data streams. And because these cloud infrastructures typically operate on a pay-as-you-go model, traditionally substantial upfront costs turn into predictable operating expenses, lowering both the cost and risk of launching applications.

Moreover, cloud service providers continually update their offerings, so building on sophisticated AI tools and services through the cloud, like the Azure AI Platform and orchestration capabilities with Azure Kubernetes Service, ensures that you have access to the latest AI and most advanced AI models and technologies.

Strategic Considerations for Transitioning to Intelligent Apps

When developing a strategy for transitioning to intelligent apps, you must consider costs, personnel skill development, and data management.

Monolithic architectures are unlikely to support the scale and ease of iteration required by intelligent apps, so you’ll likely need to implement code changes to support the transition. Your chosen hosting solution and the amount of data your app will handle affect cost and performance, in addition to determining privacy and compliance requirements.

An app that manages or stores terabytes or petabytes of data will require storage-optimized virtual machines or a managed database service hosting. Meanwhile, an app that processes video content or performs resource-intensive calculations should prioritize high-performance computing. Maximizing storage and performance can become extraordinarily costly.

Finally, existing applications shifting towards intelligent apps may encounter compatibility conflicts, depending on database type or the existing system’s underlying programming language. As you might imagine, a traditional app running on-premises and using a shared local database may need significant restructuring to support a cloud native architecture. You need plans for data migration, potential downtime, and training.

Next, let’s look at this transformation process.

Re-Architecting Monolithic Apps into Microservices

Shifting from monolithic applications to microservices is pivotal for transitioning to Intelligent Apps, and you can use this shift as an opportunity to transform your legacy software into intelligent apps that leverage the flexibility and power of microservices-enabled AI solutions.

Traditionally, a monolithic application (let’s use an online marketplace as an example) might handle all activities—from serving webpages to processing payments—within an on-premises server or a cloud server. However, this approach is challenging to scale and remains vulnerable to network failures or cyberattacks.

Breaking these applications into multiple services with specific responsibilities, such as authentication or notifications, can overcome these limitations. These services are typically deployed as microservices within container environments like Azure Kubernetes Service or serverless platforms like Azure Container Apps or Azure Functions using automated processes. This approach offers scalability, robustness, and improved performance, as each microservice can run independently and use its own database.

Shifting towards smaller, independent microservices in app architectures allows us to reap the benefits of AI-powered intelligent apps, for example, by using microservices to handle AI tasks such as processing natural language input, analyzing behavioral data, or personalizing content.

info

Watch Episode 1 and Episode 2 of the Learn Live series on how to build, test and deploy an end-to-end intelligent app solution using the Contoso Real Estate Sample.

Event-Driven Architectures: Knowing When to Use Them

Event-Driven Architectures (EDAs) can significantly enhance the performance and cost-effectiveness of intelligent apps. EDAs, typically implemented with microservices, respond in real time to events or state changes ranging from user interactions to real-time analytics.

Consider an intelligent app designed for personalized customer engagement. Instead of designing the application to check continuously for changes in user data, an EDA can react to such changes immediately, triggering appropriate microservices. For example, if users modify their preferences, an event could trigger an AI-driven recommendation microservice to update its suggestions immediately. This real-time responsiveness, enabled by services like Azure Event Grid, can improve user experiences and make intelligent apps more adaptive and proactive.

However, it’s worth noting that while EDAs provide significant benefits, they can introduce unique complexities, such as maintaining event consistency and debugging issues in event chains across multiple microservices. Therefore, it’s important to assess your application’s needs so you can understand where it can benefit from an EDA.

Conclusion

Transitioning to intelligent apps represents a shift in strategy, architecture, and infrastructure. By designing and re-architecting for cloud-based infrastructure, your apps gain scalability, cost-effectiveness, robust data management, access to sophisticated AI tools, enhanced security, and reduced operational overhead.  

But this transition extends well beyond its technical components. Organizations and the people working within them need equal attention. You can explore these facets in greater depth in “Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management.” 

The power of generative AI is here, and preparing for the move toward cloud-based intelligent applications will ensure that your apps remain performative and scalable and—most importantly—that your business is ready for the newest era of app innovation.

Exercise

- - +
Skip to main content

11 posts tagged with "azure-kubernetes-service"

View All Tags

· 9 min read
It's 30DaysOfIA

Learn best practices for modernizing your on-premises or IaaS solutions to intelligent apps in the cloud.

What We'll Cover:

  • Traditional vs Intelligent Apps
  • On-Premises/IaaS vs. Cloud-Native Platforms
  • Modernization strategy for building Intelligent Apps

image of modernizing AI solutions for intelligent apps

Preparing the Path for Intelligent Apps: Transitioning from On-Premises/IaaS to Cloud-Native Applications

The proliferation of generative AI is paving the way for intelligent applications, software that leverages AI capabilities to deliver unparalleled functionality and user experience. As we covered in a previous article, “Demystifying Intelligent Applications: Leveraging AI in App Development,” intelligent Apps aren’t just products of advanced coding. They interact, learn, and evolve thanks to modern AI and machine learning (ML) breakthroughs. 

The paradigm shift from traditional to intelligent apps demands that we change our approach to the technical challenges of software and architecture design. It also necessitates adopting new ways of thinking and operating within an organization, which we explore in greater depth in “Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management.” 

This article explores the technical infrastructure powering intelligent apps, providing a roadmap to transition traditional on-premises or Infrastructure as a Service (IaaS) solutions to intelligent apps deployed to cloud native platforms and services. We’ll discover the differences between conventional and intelligent apps, options for re-architecting existing applications, and critical strategic considerations integral to modernizing the application-building approach. 

Understanding the Shift: Traditional vs. Intelligent Applications

Intelligent apps are more than just an incremental evolution from traditional apps. They involve a fundamental shift in software solution design, development, and deployment. A clear understanding of how each paradigm differs is crucial for comprehending the impact of this transition. 

Functionality and User Experience

Traditional applications, often rule-based and rigid, rely on preprogrammed operations. A traditional weather app, for example, typically presents structured, location-based forecasts using a predetermined data source. Meanwhile, an intelligent app leverages AI for data-driven decision-making and personalization. Rather than merely displaying the forecast, it integrates ML with additional data sources—like a user’s calendar and fitness data—to determine their favorite outdoor activities, provide relevant clothing suggestions, and even automatically design ideal vacations and times based on the user’s optimal weather preferences. 

Similarly, while traditional applications tend to offer a one-size-fits-all UI, intelligent apps use AI to create tailored interactions. For instance, an intelligent banking app might use generative AI to provide a voice-enabled UI that lets customers use natural language to ask about transactions and receive personalized financial advice.

Infrastructure

Intelligent apps integrating these sophisticated features require highly scalable, flexible infrastructure optimized for AI functionality and able to manage large volumes of real-time data. Unlike traditional applications deployed on-premises or in infrastructure as a service (IaaS) environments, intelligent apps demand the flexibility and scalability of cloud native architectures.

An on-premises or IaaS-based e-commerce application may struggle with connecting its data to AI services or scaling to accommodate voluminous real-time data and requests. But an intelligent app built with a cloud native approach and scalability at its core can handle variable demand much more efficiently. It also maintains its infrastructure components on demand, whether for collecting data for AI/ML consumption or user-facing features like AI-enabled customer support and personalization.

Consider an intelligent app built on Azure that creates ChatGPT-like experiences with custom data. It might connect Azure Open AI and Azure Cognitive Search to an app running on Azure Container Apps or Azure Kubernetes Service. 

The architecture might look like the following diagram based on this demo app:

diagram of a demo app architecture

While the cloud is well-equipped to handle the challenges associated with shifting to AI-centric infrastructure—handling real-time data processing and storage, integrating and managing AI services, configuring event-driven and microservices architectures, and ensuring security across the distributed environment—re-architecting a traditional app to an intelligent app requires careful planning and execution to ensure it benefits fully from the capabilities of AI and cloud computing.

On-Premises/IaaS vs. Cloud-Native Platforms

While on-premises and IaaS solutions provide substantial control over data and processes, this fine-grained control comes with setup and maintenance costs and may lack the scalability necessary for AI-driven Intelligent Apps. On-premises hosting involves purchasing and maintaining hardware, including servers and storage systems. IaaS involves leasing virtual hardware and then hiring and training IT personnel for the infrastructure’s operation, maintenance, and security.

Conversely, infrastructure built on cloud native app services with Microsoft Azure offers virtually unlimited scalability without ongoing hardware and maintenance costs. This feature is ideal for intelligent apps that need to adapt and scale to accommodate fluctuating workloads and data streams. And because these cloud infrastructures typically operate on a pay-as-you-go model, traditionally substantial upfront costs turn into predictable operating expenses, lowering both the cost and risk of launching applications.

Moreover, cloud service providers continually update their offerings, so building on sophisticated AI tools and services through the cloud, like the Azure AI Platform and orchestration capabilities with Azure Kubernetes Service, ensures that you have access to the latest AI and most advanced AI models and technologies.

Strategic Considerations for Transitioning to Intelligent Apps

When developing a strategy for transitioning to intelligent apps, you must consider costs, personnel skill development, and data management.

Monolithic architectures are unlikely to support the scale and ease of iteration required by intelligent apps, so you’ll likely need to implement code changes to support the transition. Your chosen hosting solution and the amount of data your app will handle affect cost and performance, in addition to determining privacy and compliance requirements.

An app that manages or stores terabytes or petabytes of data will require storage-optimized virtual machines or a managed database service hosting. Meanwhile, an app that processes video content or performs resource-intensive calculations should prioritize high-performance computing. Maximizing storage and performance can become extraordinarily costly.

Finally, existing applications shifting towards intelligent apps may encounter compatibility conflicts, depending on database type or the existing system’s underlying programming language. As you might imagine, a traditional app running on-premises and using a shared local database may need significant restructuring to support a cloud native architecture. You need plans for data migration, potential downtime, and training.

Next, let’s look at this transformation process.

Re-Architecting Monolithic Apps into Microservices

Shifting from monolithic applications to microservices is pivotal for transitioning to Intelligent Apps, and you can use this shift as an opportunity to transform your legacy software into intelligent apps that leverage the flexibility and power of microservices-enabled AI solutions.

Traditionally, a monolithic application (let’s use an online marketplace as an example) might handle all activities—from serving webpages to processing payments—within an on-premises server or a cloud server. However, this approach is challenging to scale and remains vulnerable to network failures or cyberattacks.

Breaking these applications into multiple services with specific responsibilities, such as authentication or notifications, can overcome these limitations. These services are typically deployed as microservices within container environments like Azure Kubernetes Service or serverless platforms like Azure Container Apps or Azure Functions using automated processes. This approach offers scalability, robustness, and improved performance, as each microservice can run independently and use its own database.

Shifting towards smaller, independent microservices in app architectures allows us to reap the benefits of AI-powered intelligent apps, for example, by using microservices to handle AI tasks such as processing natural language input, analyzing behavioral data, or personalizing content.

info

Watch Episode 1 and Episode 2 of the Learn Live series on how to build, test and deploy an end-to-end intelligent app solution using the Contoso Real Estate Sample.

Event-Driven Architectures: Knowing When to Use Them

Event-Driven Architectures (EDAs) can significantly enhance the performance and cost-effectiveness of intelligent apps. EDAs, typically implemented with microservices, respond in real time to events or state changes ranging from user interactions to real-time analytics.

Consider an intelligent app designed for personalized customer engagement. Instead of designing the application to check continuously for changes in user data, an EDA can react to such changes immediately, triggering appropriate microservices. For example, if users modify their preferences, an event could trigger an AI-driven recommendation microservice to update its suggestions immediately. This real-time responsiveness, enabled by services like Azure Event Grid, can improve user experiences and make intelligent apps more adaptive and proactive.

However, it’s worth noting that while EDAs provide significant benefits, they can introduce unique complexities, such as maintaining event consistency and debugging issues in event chains across multiple microservices. Therefore, it’s important to assess your application’s needs so you can understand where it can benefit from an EDA.

Conclusion

Transitioning to intelligent apps represents a shift in strategy, architecture, and infrastructure. By designing and re-architecting for cloud-based infrastructure, your apps gain scalability, cost-effectiveness, robust data management, access to sophisticated AI tools, enhanced security, and reduced operational overhead.  

But this transition extends well beyond its technical components. Organizations and the people working within them need equal attention. You can explore these facets in greater depth in “Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management.” 

The power of generative AI is here, and preparing for the move toward cloud-based intelligent applications will ensure that your apps remain performative and scalable and—most importantly—that your business is ready for the newest era of app innovation.

Exercise

+ + \ No newline at end of file diff --git a/30daysofIA/tags/azure-kubernetes-service/page/3/index.html b/30daysofIA/tags/azure-kubernetes-service/page/3/index.html index 2735c16f0a..aa50902558 100644 --- a/30daysofIA/tags/azure-kubernetes-service/page/3/index.html +++ b/30daysofIA/tags/azure-kubernetes-service/page/3/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "azure-kubernetes-service" | Build Intelligent Apps On Azure +11 posts tagged with "azure-kubernetes-service" | Build Intelligent Apps On Azure @@ -14,13 +14,13 @@ - - + +
-

9 posts tagged with "azure-kubernetes-service"

View All Tags

· 10 min read
It's 30DaysOfIA

Explore how digital natives and Independent Software Vendors (ISVs) can transform their business operations and generate higher value for customers with generative AI and intelligent apps.

What We'll Cover:

  • Potential of Generative AI in application development
  • Challenges with Generative AI
  • Integrating Generative AI with business operations

image of generative AI and intelligent apps used in business

Unleashing Business Transformation: How Digital Natives Can Leverage Generative AI

Generative AI is no longer a technological flight of fancy. It’s here, and it’s rewriting the rules of business innovation. 

This type of AI leverages machine learning algorithms to create new data with similar characteristics to a given dataset. It goes beyond text generation using generative pretrained transformer (GPT) models, covering an array of applications that span from image synthesis to translations and more. It also powers a new class of intelligent applications, which combine AI capabilities with traditional software functions for enhanced user experiences and advanced business solutions. 

Generative AI is transforming how digital natives and independent software vendors (ISVs) operate. Generative AI offers new possibilities for creating unique business value, enhancing customer experiences, and driving growth. 

It plays a critical role in applications, improving functionality, aiding decision-making, and automating complex tasks to deliver dynamic, uniquely personalized user experiences. Generative AI also enables and automates data-driven outcomes, functions, and features.

Integrating AI capabilities into customer-facing apps and turning them into intelligent apps benefits both the customer and the business, as we discussed in Demystifying Intelligent Applications: Leveraging AI in App Development. You get faster product development workflows and better business intelligence for sales and marketing, while your users get more natural and context-aware features and interactions. 

That said, to realize generative AI’s potential, you need some strategy and knowledge of how and where it fits into your current business and operations. We’ll examine this technology’s potential to transform business operations, its risks, and rewards, and how to integrate it into your existing operations. 

Unpacking the Potential of Generative AI

Generative AI is creating new opportunities in the digital playing field. It empowers businesses to generate insights, design products, and automate processes in ways and at speeds that were previously unimaginable. You can now predict customer behavior more accurately, offer highly personalized user experiences, and prototype innovative ideas rapidly. 

For example, Microsoft used generative AI to reinvent web search in the new AI-powered Bing and Edge. The search experience has evolved from a solitary user flow of typing in keywords and scrolling through results. It’s now a conversational experience where Bing uses context to offer better quality and more personalized search queries and results. 

Similarly, Microsoft harnessed generative AI to transform its productivity suite into intelligent apps. Through Copilot for Microsoft 365, PowerPoint now empowers users to tell their stories by using DALL-E to generate images effortlessly. It also helps rephrase text-heavy slides to be more straightforward. Outlook now assists users to compose more effective emails, enabling better communication.

These are just some examples of how AI powers day-to-day work and operations. 

Businesses that ignore generative AI miss out on untapped market opportunities, letting competitors gain an edge in the digital race. By seeking ways to rethink assumptions and reinvent existing approaches, you can effectively use generative AI to create new opportunities, open products to new use cases and markets, unlock new growth and customer satisfaction levels, and increase your revenue.

info

Register for the intelligent apps webinar on Driving Business Value by Modernizing with Cloud-Native & AI with Microsoft and Forrester on September 26.

Explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries. 

The Crucial Role of Generative AI in Business Operations

Generative AI can be a powerful catalyst for business transformation. Let’s quickly tour some ways it can redefine business operations. 

Product development is traditionally a long and painstaking process. Now, tools like GitHub Copilot and TestPilot use generative AI to make coding and testing software easier, faster, and more robust. 

Similarly, machine learning as a service technology, such as Azure Machine Learning, democratizes the availability of machine learning. It helps collect, clean, and train models across datasets, making engineering and analyzing data more efficient and accessible.  

Generative AI also simplifies the process of creating intelligent apps. Previously, simulating thousands of prototypes, predicting their performance, and narrowing down to the most viable options could take weeks, months, or even years of iteration and data analysis. With generative AI, this process can be as quick as days, hours, or perhaps minutes. 

Customer experience is also ripe for transformation. Personalized recommendations drive engagement and sales, and generative AI can scale that personalization to every customer interaction using comprehensive and continually updated data. For instance, Microsoft leverages AI to enable personalization in Dynamics 365 Customer Insights. Using this tool, you can ask questions about your customers in natural language and gain valuable and actionable insights into your customer base. 

Lastly, marketing and sales can benefit from generative AI. For example, identifying current and future key markets and emerging trends helps companies stay ahead of the curve. Tools like Copilot in Power BI help you quickly understand and uncover insights about your business, enabling your team to craft targeted campaigns, create engaging content, and optimize your business strategy. 

Leveraging this full generative AI potential requires the right tools. GPT-4, for instance, can be instrumental in realizing effective business transformation. It excels in understanding context, generating human-like text, and learning from minimal data input, making it a formidable tool in any digital native's arsenal. Platforms like Azure OpenAI Service bring GPT-4’s power to your fingertips.

However, as with any powerful technology, it’s unwise to treat generative AI as a perfect solution. You must know the challenges and risks of not leveraging it effectively, including technical and human-factor considerations. 

Without generative AI, your company risks missing the opportunity to please customers via personalization, as discussed previously. You’ll miss sales opportunities via business intelligence insights, limiting your ability to grow revenue and keep up with the competition. 

One challenge of leveraging generative AI’s full potential is finding the right tools. Fortunately, you can implement generative AI in your organization without investing in infrastructure, computational power, and expertise. Platforms like Azure OpenAI Service and Azure AI Services make AI easily accessible. They also work with Azure compute and data services like Azure Functions and Azure Kubernetes Service (AKS), enabling you to build intelligent apps easily and quickly. 

However, generative AI can’t do everything. It may need human expertise and intervention to use it fully. 

For example, generative AI can create unique data. However, the data may have limited diversity and complexity. The AI may need help recognizing additional considerations or requirements specific to a problem. It may also lack context due to various factors, such as the information’s freshness. Also, since it’s an emerging field that’s potentially worth $4.4 trillion, we’re still learning about the nuances and potential regulatory impact of generative AI. 

Perhaps the most crucial and challenging aspect of adopting generative AI is an organization’s required cultural shift. Your teams must learn how to use AI as your company fosters an understanding of AI’s potential and limitations. 

Begin integrating AI literacy into your company’s DNA. Knowledge of its potential and the readily available tools can inspire your teams to incorporate generative AI into their daily workflows and business operations quickly, improve productivity, and deliver superior customer experiences. 

No company wants to be outpaced by its competitors. You can balance generative AI’s potential with your business operations by staying aware of and adapting to the dynamic AI landscape.

Achieving Harmony: Integrating Generative AI with Existing Business Operations

Integrating generative AI into existing business operations doesn’t have to be disruptive. With the correct strategy, you can do so seamlessly. Your goal should be to augment and enhance your operations, not disrupt and replace. 

For example, Microsoft Cloud technologies leveraged AI to relieve healthcare organizations’ challenges, such as talent shortage and workforce burnout. Providers automated their workflows, invested in specific applications and connectors, and enhanced support for data and operations. 

Azure AI Services, such as text analytics for health, enable healthcare organizations to understand their largely unstructured and untapped medical data. Project Health Insights leans on AI to help healthcare professionals gain actionable insights and inferences from patient data. 

Azure Health Bot, an Intelligent App, empowers organizations to build and deploy legally compliant, powerful conversational healthcare experiences to patients. 

A successful AI integration like this begins with understanding. Knowing what generative AI can do and setting realistic expectations can help you identify where and how AI can best serve your operations. 

Next, you can foster a culture of AI literacy across teams, speeding up adoption by helping them understand how it can aid their work. The more familiar your team is with AI and its possibilities, the smoother the transition. Training and education programs play a pivotal role here. 

Finally, integration requires a systematic approach. It’s not about overhauling everything at once — it's about identifying areas where AI can add the most value, then gradually experimenting and scaling its use. Successful integration stories often feature a phased approach, starting small and growing steadily. 

As we move toward a future where generative AI is a staple in business operations, remember that every journey is unique. Although there’s no one-size-fits-all solution, a strategic approach and a keen understanding of your business needs will help you navigate this new landscape successfully.

Conclusion

Generative AI offers plenty of opportunities for businesses that embrace it. It’s fueling the next generation of intelligent applications, transforming everything from product development to customer engagement to marketing. This technology is the key to unlocking unprecedented growth and innovation. 

While the journey may have challenges, the potential rewards make generative AI worth exploring. As generative AI advances, Azure AI’s broad range of tools and services will help you navigate this fascinating field.

Exercise

  • Watch Episode 02 of the serverless edition Learn Live session to learn how to build, test and deploy an end-to-end intelligent app solution.
  • Complete the Cloud Skills Challenge to build on your AI skills.
  • Register for Ask the Expert: Azure Functions on September 26 for a live Q&A with the Product Team on how to build AI apps using serverless computing.
- - +
Skip to main content

11 posts tagged with "azure-kubernetes-service"

View All Tags

· 12 min read
It's 30DaysOfIA

Delve into creating an Intelligent App that leverages Azure AI Vision to analyze images and extract data. Develop an API to perform optical character recognition (OCR) on uploaded images and testing this API locally.

What We'll Cover:

  • Understanding Azure AI Vision and Azure Kubernetes Service
  • Build a Python Web API to perform OCR
  • Test the API locally

image depicting an Intelligent App using AI

Jumpstart Your AI Journey: Building Your First Intelligent App with Azure AI and AKS (1)

An Intelligent App is a software application that uses artificial intelligence (AI), such as machine learning (ML), to extract meaningful insights, enabling users to make informed decisions based on the extracted text, data, sound, images, and more.

Intelligent Apps can employ advanced algorithms and models to recognize objects, patterns, and features within images, allowing them to perform tasks like object identification, image classification, facial recognition, and image segmentation.

To explore the power of Intelligent Apps, let’s build a Python app that performs optical character recognition (OCR) on images containing numbers and performs aggregate functions on the extracted numbers, like sum and average. We’ll leverage Azure AI Services for the OCR functionality and Azure Kubernetes Service (AKS) to develop, publish, and maintain a cloud-based, scalable app in Azure.

Let’s get started!

Understanding Azure AI Vision and Azure Kubernetes Service

Azure AI Vision is a cloud-based service that offers a range of advanced computer vision capabilities enabling applications to interpret content, such as images and videos, using ML and other AI algorithms. Azure computer vision lets users integrate powerful vision features into their projects without ML experience.

OCR is one of the critical functionalities within Azure vision service, allowing applications to extract text from images. Within Azure AI Vision service, OCR offers several features, including text extraction, multilingual support, layout analysis of documents, and integration with Azure services, such as Azure Functions and Azure Machine Learning.

info

Watch the intelligent apps webinar on Driving Business Value by Modernizing with Cloud-Native & AI with Microsoft and Forrester on September 26.

AKS is Microsoft Azure’s container orchestration platform that enables us to quickly and efficiently deploy, manage, and scale containerized applications, such as our Intelligent App.

Let’s explore how to leverage these tools to build an Intelligent App that uses Azure AI Vision to analyze images and extract data.

Prerequisites

To follow this tutorial, ensure you have the following:

For a preview of this final project, take a look at the complete project code.

Building the API with Azure AI Vision Service

First, log in to your Azure account and navigate to the Azure Portal.

Click Create a resource and search for “resource group.” Create a new resource group named computer-vision.

image in Azure Portal of creating a new resource group

Return to the Azure Portal home page and click Create a resource.

image in Azure Portal of creating a new resource

Search for “computer vision” and select it from the results. Click Create.

image of searching for resource group in Azure Portal

Clicking Create displays the Create Computer Vision wizard. Select the resource group you just created as the Resource group from the dropdown. Enter a resource name similar to what is seen in the screenshot below. Note that this must be a unique name so choose one that is not currently in use. Select Standard S1 as the pricing tier and check the Responsible use of AI box in the field at the bottom.

Image of selecting the Responsible use of AI box

Image of project details

Click Review + Create, then click Create.

Next, get the API key and endpoint from the Azure AI services multi-service account you created. Once the resource is created, navigate to the resource details page. Under Resource Management, select Keys and Endpoint. Once on the Keys and Endpoint page, you’ll find the Key 1 and Endpoint values. These credentials are necessary to access the Azure AI APIs.

image of selecting Keys and Endpoint

second image of selecting Keys and Endpoint

Configuring the Local Environment Variables

Next, we need to define a local environment variable file. This convenient way to store our API keys in a simple text file enables us to manage sensitive information and separate configuration from our API code.

If you haven’t done so already, clone the starter project template from GitHub into your local computer. Open the starter project template in a code editor and create a new .env file in the root folder.

Note: Root folder in this case is the “/Microsoft_Series17-18_Code/intelligent-app-before" folder.

Add the following key-value pairs to the file, remembering to replace the placeholder values with those you obtained from the previous step.

VISION_KEY=<THE-KEY-1-VALUE-FROM-YOUR-AZURE-AI-SERVICE>
VISION_ENDPOINT=<THE-ENDPOINT-VALUE-FROM-YOUR-AZURE-AI-SERVICE>

Reviewing the Quickstart Code

Let’s review the app.py file in the starter project template.

The app.py file works as the entry point for the Flask application. It is responsible for initializing the web server, listening for incoming requests, dispatching them to the appropriate route handlers, and generating responses.

Below is the code contained in the app.py file:

import os
import json

from flask import Flask, request
from flask_restful import Resource, Api
from werkzeug.utils import secure_filename

app = Flask(__name__,
static_url_path='',
static_folder='static/files')

api = Api(app)

app = Flask(__name__)

app.config['UPLOAD_FOLDER'] = 'files'

api = Api(app)

class UploadHandler(Resource):

def allowed_file(self, filename):
return '.' in filename and \
filename.rsplit('.', 1)[1].lower() in {'png'}

def post(self):
form = request.form.to_dict()

if 'file' not in request.files:
return json.dumps({ "success": False, "error": "No file part"})

file = request.files.get("file")
if file and self.allowed_file(file.filename):
filename = secure_filename(file.filename)
upload_folder = "static/files"
if not os.path.exists(upload_folder):
os.makedirs(upload_folder)
local_file_path = os.path.join(upload_folder, filename)
file.save(local_file_path)

return f"File {filename} uploaded successfully to folder: {upload_folder}"

api.add_resource(UploadHandler, "/")

if __name__ == '__main__':
app.run(debug=True)

The UploadHandler class above is defined as a Flask RESTful resource to handle file uploads. The class contains two methods:

  • The allowed_file method checks whether the file extension is allowed for upload. In this case, only .png files are allowed.

  • The post method handles HTTP POST requests for file uploads. It saves the uploaded .png file to the static/files folder.

Finally, the application returns a text response informing us that the file has been uploaded successfully.

Implementing the REST API in Python

To implement image analysis in your REST API, open the starter project template in your terminal, create a virtual environment, and activate it.

Next, install the Azure AI Vision SDK:

pip install azure-ai-vision

Then, add the following line to the requirements.txt file to include the azure-ai-vision package:

azure-ai-vision==0.13.0b1

Now, create an ocr_helper.py file in the project’s root folder. This file is a Python module that provides functions for processing images using OCR. Add the following code to the file:

import os
from statistics import median
from decimal import Decimal
import azure.ai.vision as sdk

def process_ocr(source_image):
service_options = sdk.VisionServiceOptions(os.environ["VISION_ENDPOINT"],
os.environ["VISION_KEY"])

vision_source = sdk.VisionSource(filename=source_image)

analysis_options = sdk.ImageAnalysisOptions()

analysis_options.features = (
sdk.ImageAnalysisFeature.CAPTION |
sdk.ImageAnalysisFeature.TEXT
)

analysis_options.language = "en"

analysis_options.gender_neutral_caption = True

image_analyzer = sdk.ImageAnalyzer(service_options, vision_source, analysis_options)

result = image_analyzer.analyze()

ocr_result = get_ocr_result(result)

return ocr_result

def get_ocr_result(result):
string_list = []

if result.reason != sdk.ImageAnalysisResultReason.ANALYZED:
return sdk.ImageAnalysisErrorDetails.from_result(result)
else:
if result.text is not None:
for line in result.text.lines:
for word in line.words:
string_list.append(word.content)

number_list = convert_to_decimal_list(string_list)

aggregate_result = aggregate_operations(number_list)

return {
"aggregate_result": aggregate_result,
"numbers_read": string_list
}

def convert_to_decimal_list(string_list):
return list(map(Decimal, string_list))

def aggregate_operations(numbers):
result = {
'sum': sum(numbers),
'average': sum(numbers) / len(numbers),
'median': median(numbers),
'min': min(numbers),
'max': max(numbers)
`}`
`return result`

This module uses the azure-ai-vision package to analyze images, including capturing captions and extracting text from the image. The process_ocr function inputs an image file and performs the OCR analysis. The module is a convenient tool for analyzing text-based data in pictures and performing numerical computations based on that data.

Let’s review the different components of the ocr_helper.py module listed above and explore how its functions help our Intelligent App process images and perform aggregate operations:

  • The process_ocr function takes the parameter source_image, which is the path of the image to be processed. The function then initializes the VisionServiceOptions using environment variables VISION_ENDPOINT and VISION_KEY to connect to the Azure AI Vision API.
  • The process_ocr function creates a VisionSource object with the specified source_image file name. ImageAnalysisOptions specify the features to be analyzed, including CAPTION and TEXT. The language is set to English ("en"), and gender-neutral captions are enabled.
  • Finally, an ImageAnalyzer object is created with the service options, vision source, and analysis options. The image is then analyzed using the image_analyzer.analyze method to retrieve OCR results. The recognized text from the OCR result is extracted, with numerical values stored in the string_list variable.
  • The convert_to_decimal_list function converts the list of strings to a list of decimal numbers, which helps process numeric values extracted from the text.
  • The aggregate_operations function calculates various aggregate statistics (sum, average, median, minimum, maximum) from a list of numbers and returns the results in a dictionary.

Note that you must have the appropriate credentials (VISION_KEY) and endpoint (VISION_ENDPOINT) configured for the Azure AI Vision API to use this module.

Finally, we must modify the app.py file so our code can use the process_ocr function of the ocr_helper.py file.

Add the following import statement to the app.py file:

from ocr_helper import process_ocr

Then, replace this line:

return f"File {filename} uploaded successfully to folder: {upload_folder}"

With these two lines:

aggregates = process_ocr(local_file_path)
return json.dumps(aggregates, default=str)

Doing so allows our application to perform aggregate operations (sum, average, median, minimum, maximum) of any numeric values found in the text and return the operation’s result as a JSON response.

Running the Intelligent App Locally

Our final goal is to deploy the sample app to AKS, which will run the app in a container. However, as a good practice, let’s run the app in a container locally before moving it to AKS. Doing so ensures that our application and its dependencies are encapsulated and can be run consistently across different environments.

Deploying to Docker Desktop

For our application to run on Docker, it needs two additional files: a Dockerfile and docker-compose.yml. A Dockerfile creates a single container image by specifying the steps to build it, and a docker-compose.yml file manages multiple containers as a cohesive application, defining their configurations and relationships. Dockerfile builds the image, while docker-compose.yml orchestrates multi-container applications.

In the project root folder, add a file named Dockerfile with the content below:

# syntax=docker/dockerfile:1

FROM python:3.8-slim-buster

WORKDIR /intelligentapp

COPY requirements.txt requirements.txt
RUN pip3 install -r requirements.txt

RUN pip install debugpy

COPY . .

CMD [ "python3", "-m" , "flask", "run", "--host=0.0.0.0"]

Now create a file named docker-compose.yml file in the project’s root folder with the following code, replacing the VISION_KEY and the VISION_ENDPOINT according to the environment variables you configured earlier:

version: '3.8'
services:
intelligentapp:
build:
context: .
dockerfile: Dockerfile
image: intelligent-app
ports:
- 5000:5000
container_name: intelligent-app
environment:
- VISION_KEY=<THE-KEY-1-VALUE-FROM-YOUR-AZURE-AI-SERVICE>
- VISION_ENDPOINT=<THE-ENDPOINT-VALUE-FROM-YOUR-AZURE-AI-SERVICE>

Then, run the following command in the terminal to build the image and start a container for the Intelligent App services defined in the docker-compose.yml file:

docker-compose up --build --force-recreate

Next, open a new terminal and run the command below to list the image deployed to your local Docker:

docker images

docker images

Testing the Intelligent App Locally

Now, we’ll test the Intelligent App’s functionality using the following test image:

test image to test functionality

This image is provided as sample1.png in the sample app source files, so you can easily use it in the next step.

The Intelligent App will read the image above using OCR to extract numbers in the image and perform five aggregate functions:

  • Sum
  • Average
  • Median
  • Min
  • Max

To test the API, open Postman and fill out the fields as follows:

  • URL: http://localhost:5000/
  • Method: POST
  • Body:
    • Form-data
      • Key: File — Click the right end of the Key field and select File from the dropdown list.
      • Value: Click Select Files, then select the sample1.png file provided in the sample code.

image of selecting the sample file

Now click the Send button and review the result body:

"{\"aggregate_result\": {\"sum\": \"25821\", \"average\": \"5164.2\", \"median\": \"5622\", \"min\": \"1447\", \"max\": \"9802\"}, \"numbers_read\": [\"3049\", \"5901\", \"5622\", \"1447\", \"9802\"]}"

As we can see, the app running on a local container returned the correct results based on the sample image. So, we’re ready to prepare our Azure resources to run the Intelligent App on AKS.

Exercise

In this article, we explored the creation of an Intelligent App that leverages Azure AI Vision to analyze images and extract data. We learned how to build a Python Web API to perform OCR on uploaded images and subsequently test this API locally.

Head to the next part, to deploy this API via Azure Kubernetes Service.

+ + \ No newline at end of file diff --git a/30daysofIA/tags/azure-kubernetes-service/page/4/index.html b/30daysofIA/tags/azure-kubernetes-service/page/4/index.html index de72f226ae..65ea8120e9 100644 --- a/30daysofIA/tags/azure-kubernetes-service/page/4/index.html +++ b/30daysofIA/tags/azure-kubernetes-service/page/4/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "azure-kubernetes-service" | Build Intelligent Apps On Azure +11 posts tagged with "azure-kubernetes-service" | Build Intelligent Apps On Azure @@ -14,13 +14,13 @@ - - + +
-

9 posts tagged with "azure-kubernetes-service"

View All Tags

· 9 min read
It's 30DaysOfIA

In the part of our series on intelligent apps, we’ll explore how AI is transforming application development, from design and architecture to building.

What We'll Cover:

  • Revolutionizing application development using AI
  • Infusing AI in application design architecture
  • AI Assisted Pair Programming
  • Future of App Development

A decorative image of application development with AI

Reimagining Application Development With AI: A New Paradigm

Artificial intelligence (AI) is impacting every industry. AI-powered development tools offer new functionalities and assist software developers throughout the development process, revolutionizing app development. From initial design and architecture to coding and debugging, AI reshapes how we approach application conception, creation, and iteration.

In part one of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” we explored the new breed of intelligent apps and how they use AI. In this article, we’ll see how AI technologies enhance developer toolkits and shape the future of app development, particularly through AI-enabled coding assistance and debugging.

The Dawn of Intelligent Apps: Revolutionizing Application Development

AI is more than just another addition to the development toolbox—it represents a revolution bringing unprecedented efficiency, innovation, and user engagement to our applications. 

What sets intelligent apps apart is how they can adapt and learn, helping us deliver unique, real-time user experiences.  

One example is an AI-driven health application that can track biometric data, predict potential health issues, provide personalized health advice, and even guide emergency services during a medical crisis. An AI-enabled language learning app that adapts to our learning speeds, recognizes our problem areas, and customizes its content to help us improve our language proficiencies is another example.  

Intelligent apps like these don’t just use data for recommendations—they can make critical decisions and provide sophisticated services that were previously impossible. 

For developers, intelligent apps mean we’re no longer just coding—we’re teaching applications how to think, adapt, and make decisions. AI is reshaping every step of the development journey, helping us to build more intuitive and user-focused products. 

Next, we’ll examine the impact of AI on application design and architecture and how AI-assisted “pair programming” changes software development.

info

Register for the FREE webinar on Intelligent Apps with Microsoft and Forrester on September 26 to explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries.

There will be a showcase of real-world use cases that demonstrate how AI can be seamlessly integrated into cloud-native environments driving tangible business value.

AI in Design and Architecture: Fueling Creativity and Efficiency

AI technologies help developers automate and streamline their processes and provide new ways to think about design.  

In traditional application development, the design and architecture phase can take significant time, effort, and expertise. For instance, adopting a monolithic architecture or a microservices-based design can significantly increase coding, testing, and debugging complexity. The app may even behave differently when hosted locally than in a deployed environment. It may take hours to track down an issue manually—time developers could spend elsewhere. 

Conversely, we can generate numerous design iterations in seconds with AI, each optimized to fulfill specific user needs and business goals. AI tools can quickly model various scenarios' impacts and software design patterns in the architectural design phase. It provides insights into performance and potential issues and suggests improvements. AI can also generate skeleton code, allowing developers to prototype different solutions or test specific components and technologies rapidly. By reviewing thousands of lines of code in seconds, AI allows us to focus on creating apps that serve users through feature development instead of searching for bugs.  

Integrating AI into the design and architecture phase can even extend our creative capabilities. By leveraging the power of generative AI—like large language models (LLMs)—and their broad knowledge and context going beyond software, we can better brainstorm apps and feature ideas. We can go beyond code and software architecture and get ideas about user experience, logo and branding, or marketing concepts. And as AI tools deliver insights and ideas we would have never considered; it encourages more innovation.

info

Watch Episode 01 of the Learn Live session to learn how to build an end-to-end intelligent app solution architecture.

The Power of AI-Assisted Pair Programming in Building Applications

AI-assisted pair programming is a collaborative coding approach where an AI system—like GitHub Copilot or TestPilot—assists developers during coding. It’s an increasingly common approach that significantly impacts developer productivity. In fact, GitHub Copilot is now behind an average of 46 percent of developers’ code and users are seeing 55 percent faster task completion on average. 

For new software developers, or those interested in learning new skills, AI-assisted pair programming are training wheels for coding. With the benefits of code snippet suggestions, developers can avoid struggling with beginner pitfalls like language syntax. Tools like ChatGPT can act as a personal, on-demand tutor—answering questions, generating code samples, and explaining complex code syntax and logic. These tools dramatically speed the learning process and help developers gain confidence in their coding abilities. 

Building applications with AI tools hastens development and provides more robust code. For example, GitHub Copilot helps us write new code accurately. It also quickly makes sense of existing code, suggests whole new lines or blocks of code, and even detects errors and proposes fixes. It’s like having a second coder working alongside us—one who never tires and is trained on billions of lines of code. 

However, it’s also important to acknowledge the limitations of AI. While generative AI-assistance technologies like GitHub Copilot are impressive, they’re still imperfect. They can sometimes produce erroneous, non-functional, or vulnerable code. They can also lack nuanced, domain-specific knowledge.  

So, although these tools are helpful, we can’t rely on them fully—these are programming aids rather than definitive solutions. AI-assisted pair programming is about augmenting the human developer’s skills, not replacing them.

Debugging and Improving Code With AI: Enhancing Application Quality

Identifying and correcting code errors is often time-consuming and challenging for developers. AI streamlines the debugging process by automatically detecting anomalies, suggesting fixes, and learning from previous bugs to prevent them from recurring. 

We can use generative AI to create unit tests from code snippets, from simple parameter checking and input validation to complex mocks of database and network services. Automated AI testing improves code coverage and robustness. It saves significant time compared to manually crafting unit tests. 

AI also revolutionizes code reviews by scanning and highlighting potential issues—like code patterns leading to a bug or a coding standards violation—and suggesting ways to simplify or refactor the code for readability and performance. This reduces human error and makes the code review process more efficient. Its benefits extend to small teams, where the availability of developers is crucial to deadlines, and large teams, where effective collaboration depends on readability and code consistency. AI further saves time by triaging issues so developers can prioritize the most mission-critical ones. 

Aside from improving code quality, AI is critical to understanding system behavior. Intelligent AI-assisted software development tools can monitor and audit system behavior, giving developers a better insight into how their code performs in different scenarios. AI can track patterns, identify inefficiencies, and provide alerts when they detect unusual behavior—enabling developers to enhance their code further. 

By predicting and identifying potential vulnerabilities, intelligent AI-assisted coding apps like Microsoft Security Copilot are pivotal in ensuring application functionality, reliability, and security. These AI tools are continually trained and upgraded with large codebases and user feedback that refines their responses. This ability to learn from past mistakes makes AI an incredibly powerful tool that will continue to expand its capabilities in the future.

info

Complete the AI Cloud Skills Challenge to build on you AI application development skills with paired programming using generative AI and GitHub Copilot.

AI and the Future of Application Development

The rapid advancements in AI and machine learning technologies point to an exciting future for application development. 

In “The Future of Applications,” software developer Mike Cann illustrates how LLMs might revolutionize application development. Cann that suggests fully-autonomous coding—AI-generated applications created entirely from prompts—may supersede traditional hand-crafted applications. Dynamically generated user interfaces could offer personalized and customizable experiences while we can design APIs for machine consumption and efficiency rather than human readability. Furthermore, we can store data in less structured, more adaptable, and flexible forms. 

AI is already making application development more accessible. With AI handling the complex, technical aspects of development, less-technical individuals can take a greater role in building applications. Consequently, AI can potentially democratize software engineering. 

Intelligent apps will continue to evolve, becoming more efficient, adaptable, and capable of learning from user interactions. Similarly, AI will inevitably become integral in every stage of app development.

Summary

AI has already created a new development paradigm—one in which machine intelligence and human creativity come together to produce innovative, efficient, and robust applications. AI already assists us at every stage of app development, from design and architecture to coding and debugging. 

Developers who embrace AI tools and integrate them into their processes can enhance their productivity, improve the quality of their applications, and expand the possibilities of their application development.  

The future of development is automated and AI-driven, and it’s here today. Getting started is easy—try an AI-powered development tool like GitHub Copilot to see how AI-assisted pair programming can enhance your productivity and code quality.

Exercise

- - +
Skip to main content

11 posts tagged with "azure-kubernetes-service"

View All Tags

· 9 min read
It's 30DaysOfIA

In the last article, we created an Intelligent App that leverages Azure AI Vision to analyze images and extract data. Develop an API to perform optical character recognition (OCR) on uploaded images and testing this API locally.

In this article, we will deploy the web API to the cloud platform using Azure Kubernetes Service.

What We'll Cover:

  • Understanding Azure AI Vision and Azure Kubernetes Service
  • Build a Python Web API to perform OCR
  • Test the API locally

image depicting an Intelligent App using AI

Jumpstart Your AI Journey: Building Your First Intelligent App with Azure AI and AKS (2)

In the previous article we explored the creation of an Intelligent App that leverages Azure AI Vision to analyze images and extract data. We learned how to build a Python Web API to perform OCR on uploaded images and subsequently test this API locally.

In this article we will use Azure Kubernetes Service (AKS) to develop, publish, and maintain our app in the cloud on Azure.

Let’s get started!

Prerequisites

To follow this tutorial, ensure you have completed Jumpstart Your AI Journey: Building Your First Intelligent App with Azure AI and AKS (1).

Pushing a Container Image to Azure Container Registry (ACR)

To start, open your CLI or terminal and type the following command:

az login

Follow the instructions displayed on your browser to enter your Azure credentials.

Once authenticated, you’ll initiate a secure connection between your local environment and Azure. This process grants you access to cloud services and resources.

Next, type the command below in your terminal to set up a new Azure Container Registry (ACR) to store your container images:

az acr create --resource-group computer-vision --name <name-of-azure-container-registry> --sku Basic

Remember to replace <name-of-azure-container-registry> with your container registry name. The name must be unique within Azure and comply with these rules.

The command above creates an Azure Container Registry (ACR) in the computer-vision resource group under the Basic SKU. This ACR is your secure and private repository for storing container images within Azure.

Next, log in to the registry with the following command:

az acr login -n <name-of-azure-container-registry>

The az acr login command above lets you securely authenticate and access the specified ACR without providing their Azure credentials each time.

Now, run the following command in your terminal. It will display the endpoint URL to log in to and interact with the ACR for pushing and pulling container images.

az acr show --name <name-of-azure-container-registry> --query loginServer --output table

This returns an endpoint URL as follows:

Result
----------------------------------
<name-of-azure-container-registry>.azurecr.io

Now, run the following command to show all container images, their repository, tags, and size:

docker images
REPOSITORYTAGIMAGE IDCREATEDSIZE
intelligent-applatesta7bf9f75361716 hours ago197MB

Tags are necessary to push Docker images to a remote registry like Azure Container Registry. They also let you differentiate between different versions of the same image and upload or download the one you want.

Run the following command to tag your Docker image:

docker tag intelligent-app <name-of-azure-container-registry>.azurecr.io/intelligent-app:v1

Then, run the docker images command again to check your tagged image:

docker images
REPOSITORYTAGIMAGE IDCREATEDSIZE
intelligent-applatestc52168039265About a minute ago197MB
<name-of-azure-container-registry>.azurecr.io/intelligent-appv1c52168039265About a minute ago197MB

Now run the following command so Docker can securely upload the image to your Azure Container Registry:

docker push <name-of-azure-container-registry>.azurecr.io/intelligent-app:v1

Once we've deployed the image to the container registry, AKS can access it during deployment.

info

Watch Episode 03 for the Learn Live session to experience a guided session on how to build, test and deploy an end-to-end intelligent app solution.

Deploying the Intelligent App on Azure Kubernetes Service (AKS)

Before we deploy our Intelligent App to AKS, we need to provision an AKS cluster and define Kubernetes manifests.

To provision an AKS cluster to host our application, we specify the desired configurations for the cluster, such as the number of nodes, node size, and networking options. But first, download and install the Kubernetes command-line tools (kubectl), a client credential plugin implementing Azure authentication:

az aks install-cli

If you’re using Linux, review this tutorial. Then run the following:

sudo az aks install-cli

Next, run the following command in your terminal to enable access to the networking-related resources and services provided by the `Microsoft.Network`` namespace in Azure:

az provider register --namespace Microsoft.Network

Now, we must create an AKS cluster. Run the following command to create an AKS cluster named aks-intelligent-app in the computer-vision resource group.

az aks create --resource-group computer-vision --name aks-intelligent-app --node-count 1 --generate-ssh-keys

The command above specifies the target resource group: computer-vision. The node pool is configured with one virtual machine (VM), and the secure shell (SSH) keys for secure node access are generated automatically.

Next, run the following command to update the AKS cluster you created by attaching it to your ACR. Doing so allows the AKS cluster to pull container images from the specified ACR when deploying workloads to the cluster.

az aks update -n aks-intelligent-app -g computer-vision --attach-acr <name-of-azure-container-registry>

Then, run the command below to configure kubectl to work with our AKS cluster in the computer-vision resource group.

az aks get-credentials --resource-group computer-vision --name aks-intelligent-app

The command above retrieves the necessary credentials and context information required for kubectl to communicate with the AKS cluster.

We still have to define the Kubernetes manifests written in YAML, describing the desired state of our application deployment, including containers, networking, and scaling rules. We’ll prepare these manifests, including Deployment and Service configurations, to define how our application should be deployed and exposed.

Create a folder named Deployment in the project root directory. Next, create two files in the deployment folder: deployment.yml and service.yml.

Add the following configuration to the deployment.yml file, replacing the <name-of-azure-container-registry> placeholder with your registry’s name:

apiVersion: apps/v1
kind: Deployment
metadata:
name: intelligent-app
spec:
replicas: 1
selector:
matchLabels:
app: intelligent-app
template:
metadata:
labels:
app: intelligent-app
spec:
nodeSelector:
kubernetes.io/os: linux
containers:
- name: intelligent-app
image: <name-of-azure-container-registry>.azurecr.io/intelligent-app:v1
resources:
limits:
memory: 512Mi
cpu: "1"
requests:
memory: 256Mi
cpu: "0.2"
ports:
- containerPort: 5000
env:
- name: FLASK_DEBUG
value: "1"
- name: VISION_KEY
value: <THE-KEY-1-VALUE-FROM-YOUR-AZURE-AI-SERVICE>
- name: VISION_ENDPOINT
value: <THE-ENDPOINT-VALUE-FROM-YOUR-AZURE-AI>

Additionally, edit the VISION_KEY and VISION_ENDPOINT environment variables above according to the API key and endpoint of your Azure AI Services instance.

Then, add the following configuration to the service.yml file:

apiVersion: v1
kind: Service
metadata:
name: intelligent-app-service
spec:
type: LoadBalancer
ports:
- protocol: TCP
port: 80
targetPort: 5000
name: port5000
selector:
app: intelligent-app

Now, we’ll deploy our application using kubectl. Applying the Kubernetes manifests creates the necessary resources and deploys our containerized application to the AKS cluster.

First, change the terminal to the deployment folder:

cd Deployment 

Then, run the following command to create or update Kubernetes resources defined in the deployment.yml file:

kubectl apply -f deployment.yml

Create a Kubernetes Service resource in a Kubernetes cluster based on the configuration defined in the service.yml file using the code below:

kubectl apply -f service.yml

Once you’ve applied the resource definition and the service configuration contained in the deployment.yml and the service.yml files, open the aks-intelligent-app Kubernetes Service in the Azure Portal, select Workloads under Kubernetes resources on the sidebar, and find the deployment named intelligent-app. It must have the status “Ready 1/1”. If you encounter an issue with this status, check out these troubleshooting resources.

image of selecting the deployment in Azure Portal

Testing the Intelligent App on AKS

To test the app on AKS, first, run the command below:

kubectl get services

This command lists the Services and their corresponding details, including the Service name, cluster IP address, external IP, and ports.

NAMETYPECLUSTER-IPEXTERNAL-IPPORT(S)AGE
intelligent-app-serviceLoadBalancer10.0.77.6020.121.76.15380:30936/TCP47s
kubernetesClusterIP10.0.0.1<none>443/TCP14m

The output above shows a Kubernetes Service named intelligent-app-service with a type set to LoadBalancer. It’s reachable from within the cluster using the cluster IP 10.0.77.60 and accessible externally via the external IP 20.121.76.153 on port 80 (mapped to port 30936).

Note: Your set of IP addresses will differ. Remember to use your unique external IP address when testing with Postman.

To test the deployed app, go to Postman, replace the URL with the external IP of the Kubernetes Service you just deployed, and click Send:

mage of sending the app in Postman

As we can see, our Intelligent App has successfully deployed to AKS and is functioning on the cloud as expected.

Exercise

Next Steps

In this two-part article, we explored the creation of an Intelligent App that leverages Azure AI Vision to analyze images and extract data. We learned how to build a Python Web API to perform OCR on uploaded images and subsequently deploy this API via Azure Kubernetes Service.

Besides OCR and Image Analysis, you can continue exploring Azure’s vast array of services and experiment further with Azure AI and AKS by applying various practical uses to your Intelligent Apps, including natural language processing, speech recognition and synthesis, sentiment analysis for customer feedback, and automated content moderation.

+ + \ No newline at end of file diff --git a/30daysofIA/tags/azure-kubernetes-service/page/5/index.html b/30daysofIA/tags/azure-kubernetes-service/page/5/index.html index 373f11a0c8..fd993ddab4 100644 --- a/30daysofIA/tags/azure-kubernetes-service/page/5/index.html +++ b/30daysofIA/tags/azure-kubernetes-service/page/5/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "azure-kubernetes-service" | Build Intelligent Apps On Azure +11 posts tagged with "azure-kubernetes-service" | Build Intelligent Apps On Azure @@ -14,13 +14,13 @@ - - + +
-

9 posts tagged with "azure-kubernetes-service"

View All Tags

· 10 min read
It's 30DaysOfIA

Explore real-world examples of how Intelligent Apps revolutionize logistics, retail, finance, manufacturing, tech, and medicine.

What We'll Cover:

  • Real-world businesses revolutionizing operations with Intelligent Apps
  • High level architecture for industry scenarios
  • Implementation architecture overview across industries

A decorative header image for the blog

Real-world Success Stories

According to IBM’s 2022 Global AI Adoption Index, 35% of surveyed companies already use AI in their business, and 42% are exploring the idea. Organizations lagging behind the AI curve will quickly fall behind early adopters, who are already enjoying its benefits and boosting their investment in this technology.

In the first part of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” we explored how organizations can use AI-powered apps, or intelligent apps, to transform their operations, decision-making, and customer experiences, leaping ahead of the competition.

Intelligent apps significantly improve operational efficiency and customer engagement across industries like logistics, retail, finance, manufacturing, tech, and healthcare. In this article, we’ll spotlight real-world success stories of businesses revolutionizing operations with intelligent apps using Microsoft Azure’s app, data, and AI services.

info

Register for the Learn Live on September 21 for a live guided experience with SMEs on how to build end-to-end solution architecture for real world implementation of Intelligent Apps.

Streamlining Operations: Intelligent Apps in the Airline Industry

Aerospace pioneer Airbus leveraged intelligent apps to streamline its operations and innovate its services with two ground-breaking modernizations that use Azure apps, data, and AI services to reimagine pilot training and predict aircraft maintenance issues. 

Aviation technology’s rapid evolution compelled Airbus to reconceive their approach to training pilots. The aircraft manufacturer created a pilot training chatbot by combining Azure AI Services with Speech to Text and Text to Speech. The bot helps trainee pilots navigate over 6,000 pages of technical information. Pilots can ask questions verbally for quick answers, aiding their understanding of dense information and speeding up training. And when Airbus needed to accommodate its military consumers, deploying its Kubernetes cluster to a disconnected, “air-gapped” environment met the strict security requirements of military aircraft training.

A chatbot could look like the following diagram as Azure services work together to create a conversational experience. Its components could include security and governance, bot logic and user experience (UX), bot cognition and intelligence, data extract, transfer, and load (ETL) operations, quality assurance and enhancements, logging, and monitoring and reporting.

A diagram of a conversational experience enabled by Azure services

Airbus also used AI to improve its aircraft maintenance and safety. They deployed AI Anomaly Detector to gather and analyze telemetry data from multiple flights, enabling more profound insights into wear and tear on military aircraft operating in harsh conditions. This AI application helps Airbus better understand each airplane’s health. Airbus can predict and fix potential problems before they occur, improving the aircraft’s safety and operational readiness.

AI and Azure are changing how organizations like Airbus operate, even in strict, highly regulated markets with stringent security requirements. And by streamlining pilot training and staying ahead of aircraft maintenance, Airbus helps its military and commercial clients reliably deliver their goods across the globe, maintaining their critical supply chains.

Boosting Customer Service: Intelligent Apps in the Retail Industry

The retail industry has been among the first to adopt and benefit from intelligent apps. For example, the leading American used car retailer, CarMax, uses intelligent apps to transform the car shopping experience.

Before embracing AI, CarMax faced a significant challenge: Manually creating text summaries was time-consuming and labor-intensive. CarMax decided to leverage Azure OpenAI Service to automatically generate car research pages, offering customers valuable insights while enhancing the website’s search engine rankings.

This AI-driven approach has significantly streamlined the process, achieving in a few months what would have taken years manually. Also, the AI system condenses thousands of customer reviews into a few readable sentences, offering potential buyers a quick overview of others’ experiences.

CarMax has improved their customer experience while boosting operational efficiency through AI adoption. CarMax’s editorial staff focuses on creating strategic, longer-form content with the time saved, enhancing productivity, and driving more website traffic.

CarMax’s success story serves as a testament to the potential of intelligent apps in the retail industry.

Enhancing Cybersecurity: Intelligent Apps in Finance

Intelligent apps also impact the highly regulated financial services sector. Swift, a leading infrastructure provider for financial messaging services, uses intelligent apps to enhance decision-making and combat financial crime.

The massive growth of cross-border transactions and instant payment networks led to a surge in financial fraud that can cost the industry hundreds of billions of dollars every year. So, Swift and Microsoft partnered to build an anomaly detection model using federated learning techniques with Azure Machine Learning and Azure confidential computing.

Microsoft Purview helped govern data, as the federated learning method partially trained each model in a silo before aggregating them. This approach enabled Swift and Microsoft to develop a highly accurate model without copying or moving data from Swift members’ secure locations, ensuring the highest level of security and privacy.

Financial organizations can now deploy this trained ML model to run a real-time fraud detection system, lowering the cost of financial fraud.

Organizations can deploy a fraud detection system like the following example on Microsoft Azure. The system ingests data from streams or storage, runs analytics, and uses ML to detect fraud and store results. It then alerts users and other systems to the security issue.

A diagram of an example fraud detection system

Swift provides the innovative model to its banking partners, who can share data to further train the model. Azure confidential computing securely runs new models on current data to accumulate insights, generating an ongoing learning cycle. This iterative approach strengthens the global detection of fraudulent financial transactions through an increasingly faster and more accurate model. 

The collaboration between Swift and Microsoft demonstrates how combining various Azure services creates Intelligent Apps to enhance decision-making in finance. These apps solve real problems at an unprecedented global scale. 

Enhancing Efficiency: Intelligent Apps in Manufacturing

3M, known for Post-it Notes and other innovative products, wielded the power of Intelligent Apps to standardize, automate, and accelerate its sales forecasting. The company needed a unified, automated approach to replace its multiple manual methods.

It adopted Azure Machine Learning and other Azure AI Services to develop, train, and implement 1,500 custom models for each region and division, replacing manual methods. This solution delivers timely finance, sales, and marketing insights while freeing teams from time-consuming manual data manipulation tasks.

3M used Azure Machine Learning, automated machine learning (AutoML), and the Many Models Solution Accelerator to train and score numerous machine learning models in parallel. This approach significantly shortened the company’s development cycle time.

Then, 3M integrated Microsoft Power BI into their solution to compile and visualize results more effectively. Executives now have an unbiased and unfiltered view of data for better decision-making. With its improved ability to forecast sales, 3M can focus on manufacturing the products it needs efficiently.

3M’s success demonstrates how combining various Azure services can enhance efficiency in the manufacturing industry. These apps improve operational processes while enabling employees to focus on higher-level tasks. Strategic decisions are more targeted, and business conversations are more meaningful.

Driving Innovation: Intelligent Apps in the Technology Industry

As expected, the tech industry is at the forefront of leveraging Intelligent Apps to drive innovation. Elastic saw an opportunity to use intelligent apps to optimize its search services and meet customer needs more effectively. The company built a managed, secure, and scalable solution for its customers, Elastic Cloud, by leveraging Microsoft Azure.

Elastic Cloud uses Azure Machine Learning and other Azure AI Services to automate many basic management tasks, such as backing up data, upgrading, and scaling deployments. This approach enables Elastic’s customers to focus less on managing the software and more on serving their own clients. Customers can quickly deploy and manage Elastic Cloud on Kubernetes (ECK) using Azure Kubernetes Service.

As a result, Elastic’s cloud business is growing twice as fast as its on-premises solutions. Enterprise search users have reported significant improvements in system availability and employee satisfaction.

Additionally, Elastic incorporated Azure OpenAI Service into its Elasticsearch Relevance Engine (ESRE), providing more relevant answers to search queries. ESRE enables text, vector, and hybrid search, offering a more effective and accurate response to queries.

Elastic’s success story demonstrates how combining various Azure services can create intelligent Apps to drive innovation in the tech industry. These apps help improve operational processes and deliver better results quickly.

Revolutionizing Healthcare: Intelligent Apps in Medicine

Intelligent apps also drive transformative changes in the healthcare sector. Cambridgeshire and Peterborough NHS Foundation Trust, a healthcare provider in the United Kingdom, gradually shifted their on-premises systems to the cloud using Azure, improving flexibility, and lowering costs. They needed a solution for their electronic patient record (EPR) system that integrates 23 years of data across multiple systems and formats.

The Trust used Microsoft Azure and Azure Cognitive Search, including semantic search for understanding intent and contexts and cognitive capabilities for extracting insights from data, to standardize and automate data management. This approach made legacy, archive, and live data more discoverable, saving clinicians’ time. The Trust’s solution searches through millions of clinical documents in seconds, even recognizing handwritten notes without needing a separate optical character recognition (OCR) solution to extract text from images (Azure Cognitive Search automatically does it). Clinicians can now uncover previously inaccessible insights, enabling them to offer more personalized care.

The Trust also used Integration Services tools such as Azure Logic Apps to create workflows without writing code and Event Grid to connect Azure and third-party services through a publisher-subscriber model. They switched on a live feed from their new EPR system to Azure to feed real-time information into other applications. Researchers can now access anonymized data to fuel medical innovations beyond the NHS.

The healthcare provider also invested heavily in Power BI’s data visualization and analytical functions to sift through massive amounts of information. This level of data accessibility was previously impossible with their on-premises system. The new insights have significantly improved clinicians’ efficiency for better patient care.

Data accessibility and security are critical in the healthcare industry. Intelligent Apps empower organizations like the Trust to revolutionize medical care. AI capabilities helped the Trust improve its operational efficiency and user experiences, setting a new standard for healthcare in the digital age.

Summary

AI is driving transformation and real-world successes across industries. Intelligent apps are reshaping our world by streamlining operations in manufacturing and logistics, enhancing customer experience in the retail sector, improving decision-making in finance, driving innovation in tech, and revolutionizing healthcare.

As organizations adopt AI, they’re innovating in ways that were once too difficult, expensive, or outright impossible. Companies that wait to embrace intelligent apps risk the chance of falling behind. By embracing intelligent apps and leveraging the capabilities of Azure, you can harness the transformative potential of AI to remain on the cutting edge of technology.

Exercise

- - +
Skip to main content

11 posts tagged with "azure-kubernetes-service"

View All Tags

· 10 min read
It's 30DaysOfIA

Explore how digital natives and Independent Software Vendors (ISVs) can transform their business operations and generate higher value for customers with generative AI and intelligent apps.

What We'll Cover:

  • Potential of Generative AI in application development
  • Challenges with Generative AI
  • Integrating Generative AI with business operations

image of generative AI and intelligent apps used in business

Unleashing Business Transformation: How Digital Natives Can Leverage Generative AI

Generative AI is no longer a technological flight of fancy. It’s here, and it’s rewriting the rules of business innovation. 

This type of AI leverages machine learning algorithms to create new data with similar characteristics to a given dataset. It goes beyond text generation using generative pretrained transformer (GPT) models, covering an array of applications that span from image synthesis to translations and more. It also powers a new class of intelligent applications, which combine AI capabilities with traditional software functions for enhanced user experiences and advanced business solutions. 

Generative AI is transforming how digital natives and independent software vendors (ISVs) operate. Generative AI offers new possibilities for creating unique business value, enhancing customer experiences, and driving growth. 

It plays a critical role in applications, improving functionality, aiding decision-making, and automating complex tasks to deliver dynamic, uniquely personalized user experiences. Generative AI also enables and automates data-driven outcomes, functions, and features.

Integrating AI capabilities into customer-facing apps and turning them into intelligent apps benefits both the customer and the business, as we discussed in Demystifying Intelligent Applications: Leveraging AI in App Development. You get faster product development workflows and better business intelligence for sales and marketing, while your users get more natural and context-aware features and interactions. 

That said, to realize generative AI’s potential, you need some strategy and knowledge of how and where it fits into your current business and operations. We’ll examine this technology’s potential to transform business operations, its risks, and rewards, and how to integrate it into your existing operations. 

Unpacking the Potential of Generative AI

Generative AI is creating new opportunities in the digital playing field. It empowers businesses to generate insights, design products, and automate processes in ways and at speeds that were previously unimaginable. You can now predict customer behavior more accurately, offer highly personalized user experiences, and prototype innovative ideas rapidly. 

For example, Microsoft used generative AI to reinvent web search in the new AI-powered Bing and Edge. The search experience has evolved from a solitary user flow of typing in keywords and scrolling through results. It’s now a conversational experience where Bing uses context to offer better quality and more personalized search queries and results. 

Similarly, Microsoft harnessed generative AI to transform its productivity suite into intelligent apps. Through Copilot for Microsoft 365, PowerPoint now empowers users to tell their stories by using DALL-E to generate images effortlessly. It also helps rephrase text-heavy slides to be more straightforward. Outlook now assists users to compose more effective emails, enabling better communication.

These are just some examples of how AI powers day-to-day work and operations. 

Businesses that ignore generative AI miss out on untapped market opportunities, letting competitors gain an edge in the digital race. By seeking ways to rethink assumptions and reinvent existing approaches, you can effectively use generative AI to create new opportunities, open products to new use cases and markets, unlock new growth and customer satisfaction levels, and increase your revenue.

info

Register for the intelligent apps webinar on Driving Business Value by Modernizing with Cloud-Native & AI with Microsoft and Forrester on September 26.

Explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries. 

The Crucial Role of Generative AI in Business Operations

Generative AI can be a powerful catalyst for business transformation. Let’s quickly tour some ways it can redefine business operations. 

Product development is traditionally a long and painstaking process. Now, tools like GitHub Copilot and TestPilot use generative AI to make coding and testing software easier, faster, and more robust. 

Similarly, machine learning as a service technology, such as Azure Machine Learning, democratizes the availability of machine learning. It helps collect, clean, and train models across datasets, making engineering and analyzing data more efficient and accessible.  

Generative AI also simplifies the process of creating intelligent apps. Previously, simulating thousands of prototypes, predicting their performance, and narrowing down to the most viable options could take weeks, months, or even years of iteration and data analysis. With generative AI, this process can be as quick as days, hours, or perhaps minutes. 

Customer experience is also ripe for transformation. Personalized recommendations drive engagement and sales, and generative AI can scale that personalization to every customer interaction using comprehensive and continually updated data. For instance, Microsoft leverages AI to enable personalization in Dynamics 365 Customer Insights. Using this tool, you can ask questions about your customers in natural language and gain valuable and actionable insights into your customer base. 

Lastly, marketing and sales can benefit from generative AI. For example, identifying current and future key markets and emerging trends helps companies stay ahead of the curve. Tools like Copilot in Power BI help you quickly understand and uncover insights about your business, enabling your team to craft targeted campaigns, create engaging content, and optimize your business strategy. 

Leveraging this full generative AI potential requires the right tools. GPT-4, for instance, can be instrumental in realizing effective business transformation. It excels in understanding context, generating human-like text, and learning from minimal data input, making it a formidable tool in any digital native's arsenal. Platforms like Azure OpenAI Service bring GPT-4’s power to your fingertips.

However, as with any powerful technology, it’s unwise to treat generative AI as a perfect solution. You must know the challenges and risks of not leveraging it effectively, including technical and human-factor considerations. 

Without generative AI, your company risks missing the opportunity to please customers via personalization, as discussed previously. You’ll miss sales opportunities via business intelligence insights, limiting your ability to grow revenue and keep up with the competition. 

One challenge of leveraging generative AI’s full potential is finding the right tools. Fortunately, you can implement generative AI in your organization without investing in infrastructure, computational power, and expertise. Platforms like Azure OpenAI Service and Azure AI Services make AI easily accessible. They also work with Azure compute and data services like Azure Functions and Azure Kubernetes Service (AKS), enabling you to build intelligent apps easily and quickly. 

However, generative AI can’t do everything. It may need human expertise and intervention to use it fully. 

For example, generative AI can create unique data. However, the data may have limited diversity and complexity. The AI may need help recognizing additional considerations or requirements specific to a problem. It may also lack context due to various factors, such as the information’s freshness. Also, since it’s an emerging field that’s potentially worth $4.4 trillion, we’re still learning about the nuances and potential regulatory impact of generative AI. 

Perhaps the most crucial and challenging aspect of adopting generative AI is an organization’s required cultural shift. Your teams must learn how to use AI as your company fosters an understanding of AI’s potential and limitations. 

Begin integrating AI literacy into your company’s DNA. Knowledge of its potential and the readily available tools can inspire your teams to incorporate generative AI into their daily workflows and business operations quickly, improve productivity, and deliver superior customer experiences. 

No company wants to be outpaced by its competitors. You can balance generative AI’s potential with your business operations by staying aware of and adapting to the dynamic AI landscape.

Achieving Harmony: Integrating Generative AI with Existing Business Operations

Integrating generative AI into existing business operations doesn’t have to be disruptive. With the correct strategy, you can do so seamlessly. Your goal should be to augment and enhance your operations, not disrupt and replace. 

For example, Microsoft Cloud technologies leveraged AI to relieve healthcare organizations’ challenges, such as talent shortage and workforce burnout. Providers automated their workflows, invested in specific applications and connectors, and enhanced support for data and operations. 

Azure AI Services, such as text analytics for health, enable healthcare organizations to understand their largely unstructured and untapped medical data. Project Health Insights leans on AI to help healthcare professionals gain actionable insights and inferences from patient data. 

Azure Health Bot, an Intelligent App, empowers organizations to build and deploy legally compliant, powerful conversational healthcare experiences to patients. 

A successful AI integration like this begins with understanding. Knowing what generative AI can do and setting realistic expectations can help you identify where and how AI can best serve your operations. 

Next, you can foster a culture of AI literacy across teams, speeding up adoption by helping them understand how it can aid their work. The more familiar your team is with AI and its possibilities, the smoother the transition. Training and education programs play a pivotal role here. 

Finally, integration requires a systematic approach. It’s not about overhauling everything at once — it's about identifying areas where AI can add the most value, then gradually experimenting and scaling its use. Successful integration stories often feature a phased approach, starting small and growing steadily. 

As we move toward a future where generative AI is a staple in business operations, remember that every journey is unique. Although there’s no one-size-fits-all solution, a strategic approach and a keen understanding of your business needs will help you navigate this new landscape successfully.

Conclusion

Generative AI offers plenty of opportunities for businesses that embrace it. It’s fueling the next generation of intelligent applications, transforming everything from product development to customer engagement to marketing. This technology is the key to unlocking unprecedented growth and innovation. 

While the journey may have challenges, the potential rewards make generative AI worth exploring. As generative AI advances, Azure AI’s broad range of tools and services will help you navigate this fascinating field.

Exercise

  • Watch Episode 02 of the serverless edition Learn Live session to learn how to build, test and deploy an end-to-end intelligent app solution.
  • Complete the Cloud Skills Challenge to build on your AI skills.
  • Register for Ask the Expert: Azure Functions on September 26 for a live Q&A with the Product Team on how to build AI apps using serverless computing.
+ + \ No newline at end of file diff --git a/30daysofIA/tags/azure-kubernetes-service/page/6/index.html b/30daysofIA/tags/azure-kubernetes-service/page/6/index.html index 82d77e5ef8..539dfd9971 100644 --- a/30daysofIA/tags/azure-kubernetes-service/page/6/index.html +++ b/30daysofIA/tags/azure-kubernetes-service/page/6/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "azure-kubernetes-service" | Build Intelligent Apps On Azure +11 posts tagged with "azure-kubernetes-service" | Build Intelligent Apps On Azure @@ -14,13 +14,13 @@ - - + +
-

9 posts tagged with "azure-kubernetes-service"

View All Tags

· 8 min read
It's 30DaysOfIA

Let’s start with understanding the power of intelligent applications.

What We'll Cover:

  • What are intelligent applications?
  • Categories of Intelligent Apps
  • Breaking down the role of AI in intelligent apps

A decorative header image for the blog

Demystifying Intelligent Applications: Leveraging AI in App Development

Until recent years, when you heard the term “artificial intelligence” (AI), you might have pictured autonomous robots from sci-fi films. Now, perhaps your mind jumps to big data and machine learning (ML). Maybe you’re even thinking about the latest ChatGPT plugins and transformer architectures. Today, AI is more than these imaginations: it’s a reality increasingly woven into our day-to-day routines through intelligent applications.

Intelligent apps combine cloud-scale compute, data, and predictive or generative AI. These applications help you automate tasks and make data-driven decisions, aiding in leveraging business intelligence to gain meaningful, practical outcomes. 

For developers, the AI landscape isn’t an abstract complexity. It’s a real, tangible opportunity for evolving traditional applications, making them “intelligent,” and delivering more value to users. And with tools like Azure Kubernetes Service, Azure Cosmos DB, and Azure AI Services, stepping into the world of intelligent apps isn’t a steep climb but a steady trek across a manageable learning curve.

The Three Categories of Intelligent Applications

Before we start building, it’s critical to understand what makes an app “intelligent.” It’s not a universal definition so much as an identifier for apps that fall into one or more of the three primary categories: 

  • Outcome-based — This designation represents the broadest category of intelligent apps, whose principal value for users is the intelligent outcomes they provide. These apps process data to provide insightful results that enhance decision-making or help with tasks like trend prediction, resource management, and customer relationship management.

  • Functionality-based — This type of intelligent app takes AI integration a degree further, integrating AI as a function of the app to create or process its outputs, enhancing the app’s overall interactivity and usefulness.

  • Feature-based — This is the narrowest category, in which the app’s AI/ML component is one of its core features and primary selling points

Let’s dive more deeply into each category.

Outcome-Based Apps: Intelligent Outcomes Drive Success

Outcome-based applications are all about the results provided to the user. They function beyond merely processing data to offer insightful outcomes that drive success.

Think of a personal fitness tracker app that uses AI to analyze the user’s activity and provides actionable recommendations for achieving a goal. This type of app not only tracks your steps, sleep patterns, and heart rate but uses that data to provide you with personalized health advice. It helps you make informed lifestyle choices — the “intelligent” outcome.

Another example is a social media marketing app that uses AI for time-saving data analysis, task automation, and trend prediction. AI-based sentiment analysis can gauge customer perception while natural language processing (NLP) accurately interprets customer inquiries. Task automation can schedule posts and automate responses to common customer queries, and trend forecasting helps model and predict customer behavior to further personalize targeted promotional efforts. Building this type of intelligence into your apps provides cost-effective, actionable insights that guide sales and marketing strategies.

Functionality-Based Apps: AI-Driven Features Enhance User Experiences

As the name suggests, functionality-based applications integrate AI into how the app functions, but the end user drives the action and outcome. In this category of apps, AI takes a more active role in the user’s interaction by allowing them to use AI-created or processed output. These apps leverage AI technologies like Azure AI Services for NLP, image recognition, and pattern recognition to improve user experience.

Consider a music app that generates personalized playlists based on your listening habits or your smartphone’s camera app that uses AI to recognize faces and optimize photo settings. Even more advanced are the language translation apps that translate text from one language to another and can even understand and maintain the context and subtleties of the conversation. They use NLP to accomplish these feats.

Similarly, consider the latest e-commerce apps, whose AI-driven functionalities enable customers to try on clothes virtually, significantly enhancing the online shopping experience.

Feature-Based Apps: Integrating Advanced AI/ML Components

Finally, there are feature-based intelligent apps. These apps incorporate advanced AI/ML components like neural networks, internal large language models (LLMs), and sophisticated algorithms as core features for the user. In this type of application, AI is the primary feature for users to engage with and the primary selling point for the app.

Some examples of feature-based applications are chatbots and virtual agents built using Azure Bot Service. These robo-representatives use AI to provide more natural and human-like conversation, balancing superior efficiency with a friendlier, more personalized user experience. The new Bing is a prime example of this implementation, integrating AI to give users a knowledgeable copilot for web searches.

Similarly, OpenAI’s ChatGPT allows users access to its state-of-the-art language models available as Microsoft’s Azure OpenAI Service. It leverages the power of the highly sophisticated GPT-3 LLM to perform tasks like drafting emails, writing code, answering general knowledge questions, translating between languages, and tutoring in numerous subjects.

These apps don’t just use AI to provide outputs or improve user experiences: They push the boundaries of what’s possible in app development by integrating AI/ML components.

info

Register for the live Q&A Ask The Expert session on September 26 with the Azure Functions product engineering team.

Breaking Down the Role of AI in Intelligent Applications

Intelligent apps reflect the merging of traditional software design with the dynamic capabilities of AI. Let’s explore how AI integrates and amplifies these applications.

At its core, AI augments applications with the capacity to self-learn and self-improve. It allows you to create applications that predict, react, and adapt to a constantly changing environment. In the power grid management system for a major city, an AI-infused application can forecast power usage trends, react to anomalies, and adapt to sudden demand surges, ensuring optimal efficiency and preventing blackouts.

But AI’s role doesn’t end at prediction or decision-making: It also automates tasks. In enterprise solutions, it has moved beyond simple automation to encompass intelligent process automation (IPA). In this arena, AI algorithms understand, learn, and then automate entire digital workflows. For developers, this is a fertile ground for creating applications that can dramatically improve the operational efficiency of businesses.

Even more, AI substantially aids in development tasks. For example, there’s GitHub Copilot, an AI-powered code assistant that offers suggestions based not only on syntax but on development best practices and the intent of the code. AI has evolved from a tool you use to build apps to a partner that helps in creating them. 

Using AI to build intelligent apps isn’t about throwing out what’s worked before. It’s about enhancing the components that have the most impact and achieve the best results. Incorporating AI in apps elevates their predictive abilities, streamlines complex tasks, and even aids in the development and creation process. When you build intelligent apps, you don’t just address current problems. You anticipate future challenges and craft solutions that adapt over time.

Summary

As developers the power of AI is in our hands, and it’s our responsibility to harness its full potential. Each category tells the story of AI's transformative potential, from applications that deliver intelligent outcomes to those with AI capabilities to enrich the user experience and those pushing boundaries with advanced AI/ML technology.

Accessible and robust platforms like Azure AI can simplify the process of developing Intelligent Applications. Whether you’re refining an existing app or building a new one, just entering the world of AI, or already familiar with the landscape, take a moment to appreciate the potential that AI integration offers. You’re embracing this exciting frontier and shaping the future of app development. 

Exercise

  • Complete the Cloud Skills Challenge to build on your apps and AI skills.
  • Watch Episode 01 of the Serverless Edition Learn Live session to learn how to build an end-to-end intelligent app solution.
- - +
Skip to main content

11 posts tagged with "azure-kubernetes-service"

View All Tags

· 9 min read
It's 30DaysOfIA

In the part of our series on intelligent apps, we’ll explore how AI is transforming application development, from design and architecture to building.

What We'll Cover:

  • Revolutionizing application development using AI
  • Infusing AI in application design architecture
  • AI Assisted Pair Programming
  • Future of App Development

A decorative image of application development with AI

Reimagining Application Development With AI: A New Paradigm

Artificial intelligence (AI) is impacting every industry. AI-powered development tools offer new functionalities and assist software developers throughout the development process, revolutionizing app development. From initial design and architecture to coding and debugging, AI reshapes how we approach application conception, creation, and iteration.

In part one of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” we explored the new breed of intelligent apps and how they use AI. In this article, we’ll see how AI technologies enhance developer toolkits and shape the future of app development, particularly through AI-enabled coding assistance and debugging.

The Dawn of Intelligent Apps: Revolutionizing Application Development

AI is more than just another addition to the development toolbox—it represents a revolution bringing unprecedented efficiency, innovation, and user engagement to our applications. 

What sets intelligent apps apart is how they can adapt and learn, helping us deliver unique, real-time user experiences.  

One example is an AI-driven health application that can track biometric data, predict potential health issues, provide personalized health advice, and even guide emergency services during a medical crisis. An AI-enabled language learning app that adapts to our learning speeds, recognizes our problem areas, and customizes its content to help us improve our language proficiencies is another example.  

Intelligent apps like these don’t just use data for recommendations—they can make critical decisions and provide sophisticated services that were previously impossible. 

For developers, intelligent apps mean we’re no longer just coding—we’re teaching applications how to think, adapt, and make decisions. AI is reshaping every step of the development journey, helping us to build more intuitive and user-focused products. 

Next, we’ll examine the impact of AI on application design and architecture and how AI-assisted “pair programming” changes software development.

info

Register for the FREE webinar on Intelligent Apps with Microsoft and Forrester on September 26 to explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries.

There will be a showcase of real-world use cases that demonstrate how AI can be seamlessly integrated into cloud-native environments driving tangible business value.

AI in Design and Architecture: Fueling Creativity and Efficiency

AI technologies help developers automate and streamline their processes and provide new ways to think about design.  

In traditional application development, the design and architecture phase can take significant time, effort, and expertise. For instance, adopting a monolithic architecture or a microservices-based design can significantly increase coding, testing, and debugging complexity. The app may even behave differently when hosted locally than in a deployed environment. It may take hours to track down an issue manually—time developers could spend elsewhere. 

Conversely, we can generate numerous design iterations in seconds with AI, each optimized to fulfill specific user needs and business goals. AI tools can quickly model various scenarios' impacts and software design patterns in the architectural design phase. It provides insights into performance and potential issues and suggests improvements. AI can also generate skeleton code, allowing developers to prototype different solutions or test specific components and technologies rapidly. By reviewing thousands of lines of code in seconds, AI allows us to focus on creating apps that serve users through feature development instead of searching for bugs.  

Integrating AI into the design and architecture phase can even extend our creative capabilities. By leveraging the power of generative AI—like large language models (LLMs)—and their broad knowledge and context going beyond software, we can better brainstorm apps and feature ideas. We can go beyond code and software architecture and get ideas about user experience, logo and branding, or marketing concepts. And as AI tools deliver insights and ideas we would have never considered; it encourages more innovation.

info

Watch Episode 01 of the Learn Live session to learn how to build an end-to-end intelligent app solution architecture.

The Power of AI-Assisted Pair Programming in Building Applications

AI-assisted pair programming is a collaborative coding approach where an AI system—like GitHub Copilot or TestPilot—assists developers during coding. It’s an increasingly common approach that significantly impacts developer productivity. In fact, GitHub Copilot is now behind an average of 46 percent of developers’ code and users are seeing 55 percent faster task completion on average. 

For new software developers, or those interested in learning new skills, AI-assisted pair programming are training wheels for coding. With the benefits of code snippet suggestions, developers can avoid struggling with beginner pitfalls like language syntax. Tools like ChatGPT can act as a personal, on-demand tutor—answering questions, generating code samples, and explaining complex code syntax and logic. These tools dramatically speed the learning process and help developers gain confidence in their coding abilities. 

Building applications with AI tools hastens development and provides more robust code. For example, GitHub Copilot helps us write new code accurately. It also quickly makes sense of existing code, suggests whole new lines or blocks of code, and even detects errors and proposes fixes. It’s like having a second coder working alongside us—one who never tires and is trained on billions of lines of code. 

However, it’s also important to acknowledge the limitations of AI. While generative AI-assistance technologies like GitHub Copilot are impressive, they’re still imperfect. They can sometimes produce erroneous, non-functional, or vulnerable code. They can also lack nuanced, domain-specific knowledge.  

So, although these tools are helpful, we can’t rely on them fully—these are programming aids rather than definitive solutions. AI-assisted pair programming is about augmenting the human developer’s skills, not replacing them.

Debugging and Improving Code With AI: Enhancing Application Quality

Identifying and correcting code errors is often time-consuming and challenging for developers. AI streamlines the debugging process by automatically detecting anomalies, suggesting fixes, and learning from previous bugs to prevent them from recurring. 

We can use generative AI to create unit tests from code snippets, from simple parameter checking and input validation to complex mocks of database and network services. Automated AI testing improves code coverage and robustness. It saves significant time compared to manually crafting unit tests. 

AI also revolutionizes code reviews by scanning and highlighting potential issues—like code patterns leading to a bug or a coding standards violation—and suggesting ways to simplify or refactor the code for readability and performance. This reduces human error and makes the code review process more efficient. Its benefits extend to small teams, where the availability of developers is crucial to deadlines, and large teams, where effective collaboration depends on readability and code consistency. AI further saves time by triaging issues so developers can prioritize the most mission-critical ones. 

Aside from improving code quality, AI is critical to understanding system behavior. Intelligent AI-assisted software development tools can monitor and audit system behavior, giving developers a better insight into how their code performs in different scenarios. AI can track patterns, identify inefficiencies, and provide alerts when they detect unusual behavior—enabling developers to enhance their code further. 

By predicting and identifying potential vulnerabilities, intelligent AI-assisted coding apps like Microsoft Security Copilot are pivotal in ensuring application functionality, reliability, and security. These AI tools are continually trained and upgraded with large codebases and user feedback that refines their responses. This ability to learn from past mistakes makes AI an incredibly powerful tool that will continue to expand its capabilities in the future.

info

Complete the AI Cloud Skills Challenge to build on you AI application development skills with paired programming using generative AI and GitHub Copilot.

AI and the Future of Application Development

The rapid advancements in AI and machine learning technologies point to an exciting future for application development. 

In “The Future of Applications,” software developer Mike Cann illustrates how LLMs might revolutionize application development. Cann that suggests fully-autonomous coding—AI-generated applications created entirely from prompts—may supersede traditional hand-crafted applications. Dynamically generated user interfaces could offer personalized and customizable experiences while we can design APIs for machine consumption and efficiency rather than human readability. Furthermore, we can store data in less structured, more adaptable, and flexible forms. 

AI is already making application development more accessible. With AI handling the complex, technical aspects of development, less-technical individuals can take a greater role in building applications. Consequently, AI can potentially democratize software engineering. 

Intelligent apps will continue to evolve, becoming more efficient, adaptable, and capable of learning from user interactions. Similarly, AI will inevitably become integral in every stage of app development.

Summary

AI has already created a new development paradigm—one in which machine intelligence and human creativity come together to produce innovative, efficient, and robust applications. AI already assists us at every stage of app development, from design and architecture to coding and debugging. 

Developers who embrace AI tools and integrate them into their processes can enhance their productivity, improve the quality of their applications, and expand the possibilities of their application development.  

The future of development is automated and AI-driven, and it’s here today. Getting started is easy—try an AI-powered development tool like GitHub Copilot to see how AI-assisted pair programming can enhance your productivity and code quality.

Exercise

+ + \ No newline at end of file diff --git a/30daysofIA/tags/azure-kubernetes-service/page/7/index.html b/30daysofIA/tags/azure-kubernetes-service/page/7/index.html index 2d7910bf97..e7eda6c941 100644 --- a/30daysofIA/tags/azure-kubernetes-service/page/7/index.html +++ b/30daysofIA/tags/azure-kubernetes-service/page/7/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "azure-kubernetes-service" | Build Intelligent Apps On Azure +11 posts tagged with "azure-kubernetes-service" | Build Intelligent Apps On Azure @@ -14,14 +14,13 @@ - - + +
-

9 posts tagged with "azure-kubernetes-service"

View All Tags

· 4 min read
It's 30DaysOfIA

This Fall focus on building intelligent apps using AI and cloud-native technologies. #FallForIntelligentApps brings to you a learning journey to build your skills on creating differentiated experiences while modernizing your applications. It’s time to learn it all.

What We'll Cover

image

Get Ready To #FallForIntelligentApps starting September 18!

Today, we kick off the Fall season with content and activities to skill you up on all things Intelligent Apps or AI Apps on Azure with content, events, and community interactions! Read on to learn about what is coming!

Explore Our Initiatives

We have a number of initiatives planned for the month to help you learn and skill up on relevant technologies. Click on the links to visit the relevant pages for each

image

Register for the events!

What are 4 things you can do today, to jumpstart your learning journey?

#30Days Of Intelligent Apps

#30DaysOfIA is a series of daily blog posts grouped into themed weeks - taking you from core concepts to end-to-end solution examples in 30 days. Each article will provide conceptual lessons paired with exercises and resources to help you reinforce learnings and take next steps.

This series takes you through learning journey in six stages, each building on the previous week to help you skill up in a beginner-friendly way:

We will start with defining intelligent apps and then expand on how to build with cloud-native technologies like Azure Kubernetes Service, Azure Container Apps and Azure Functions, and take you through end-to-end scenarios for real world application development. Before we dive deep on intelligent apps, here is a high-level overview of the Intelligent Apps landscape on Azure:

image

Containers on Azure services offer you a wide range of capabilities, from simplicity to control to suit your different needs.

image -image

To start with the basics for developing Kubernetes applications, explore #30DaysOfCloudNative.

Cloud-native development when paired with serverless computing enhances your solution architecture for building cost optimized, resilient applications.

image

To start with the basics for serverless computing, explore #30DaysOfServerless.

Let’s Get Started

Now you know everything! We hope you are as excited as we are to dive into a full month of active learning and doing! Don't forget to subscribe for updates in your favorite feed reader! And look out for our first Intelligent Apps post Monday!

- - +
Skip to main content

11 posts tagged with "azure-kubernetes-service"

View All Tags

· 10 min read
It's 30DaysOfIA

Explore real-world examples of how Intelligent Apps revolutionize logistics, retail, finance, manufacturing, tech, and medicine.

What We'll Cover:

  • Real-world businesses revolutionizing operations with Intelligent Apps
  • High level architecture for industry scenarios
  • Implementation architecture overview across industries

A decorative header image for the blog

Real-world Success Stories

According to IBM’s 2022 Global AI Adoption Index, 35% of surveyed companies already use AI in their business, and 42% are exploring the idea. Organizations lagging behind the AI curve will quickly fall behind early adopters, who are already enjoying its benefits and boosting their investment in this technology.

In the first part of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” we explored how organizations can use AI-powered apps, or intelligent apps, to transform their operations, decision-making, and customer experiences, leaping ahead of the competition.

Intelligent apps significantly improve operational efficiency and customer engagement across industries like logistics, retail, finance, manufacturing, tech, and healthcare. In this article, we’ll spotlight real-world success stories of businesses revolutionizing operations with intelligent apps using Microsoft Azure’s app, data, and AI services.

info

Register for the Learn Live on September 21 for a live guided experience with SMEs on how to build end-to-end solution architecture for real world implementation of Intelligent Apps.

Streamlining Operations: Intelligent Apps in the Airline Industry

Aerospace pioneer Airbus leveraged intelligent apps to streamline its operations and innovate its services with two ground-breaking modernizations that use Azure apps, data, and AI services to reimagine pilot training and predict aircraft maintenance issues. 

Aviation technology’s rapid evolution compelled Airbus to reconceive their approach to training pilots. The aircraft manufacturer created a pilot training chatbot by combining Azure AI Services with Speech to Text and Text to Speech. The bot helps trainee pilots navigate over 6,000 pages of technical information. Pilots can ask questions verbally for quick answers, aiding their understanding of dense information and speeding up training. And when Airbus needed to accommodate its military consumers, deploying its Kubernetes cluster to a disconnected, “air-gapped” environment met the strict security requirements of military aircraft training.

A chatbot could look like the following diagram as Azure services work together to create a conversational experience. Its components could include security and governance, bot logic and user experience (UX), bot cognition and intelligence, data extract, transfer, and load (ETL) operations, quality assurance and enhancements, logging, and monitoring and reporting.

A diagram of a conversational experience enabled by Azure services

Airbus also used AI to improve its aircraft maintenance and safety. They deployed AI Anomaly Detector to gather and analyze telemetry data from multiple flights, enabling more profound insights into wear and tear on military aircraft operating in harsh conditions. This AI application helps Airbus better understand each airplane’s health. Airbus can predict and fix potential problems before they occur, improving the aircraft’s safety and operational readiness.

AI and Azure are changing how organizations like Airbus operate, even in strict, highly regulated markets with stringent security requirements. And by streamlining pilot training and staying ahead of aircraft maintenance, Airbus helps its military and commercial clients reliably deliver their goods across the globe, maintaining their critical supply chains.

Boosting Customer Service: Intelligent Apps in the Retail Industry

The retail industry has been among the first to adopt and benefit from intelligent apps. For example, the leading American used car retailer, CarMax, uses intelligent apps to transform the car shopping experience.

Before embracing AI, CarMax faced a significant challenge: Manually creating text summaries was time-consuming and labor-intensive. CarMax decided to leverage Azure OpenAI Service to automatically generate car research pages, offering customers valuable insights while enhancing the website’s search engine rankings.

This AI-driven approach has significantly streamlined the process, achieving in a few months what would have taken years manually. Also, the AI system condenses thousands of customer reviews into a few readable sentences, offering potential buyers a quick overview of others’ experiences.

CarMax has improved their customer experience while boosting operational efficiency through AI adoption. CarMax’s editorial staff focuses on creating strategic, longer-form content with the time saved, enhancing productivity, and driving more website traffic.

CarMax’s success story serves as a testament to the potential of intelligent apps in the retail industry.

Enhancing Cybersecurity: Intelligent Apps in Finance

Intelligent apps also impact the highly regulated financial services sector. Swift, a leading infrastructure provider for financial messaging services, uses intelligent apps to enhance decision-making and combat financial crime.

The massive growth of cross-border transactions and instant payment networks led to a surge in financial fraud that can cost the industry hundreds of billions of dollars every year. So, Swift and Microsoft partnered to build an anomaly detection model using federated learning techniques with Azure Machine Learning and Azure confidential computing.

Microsoft Purview helped govern data, as the federated learning method partially trained each model in a silo before aggregating them. This approach enabled Swift and Microsoft to develop a highly accurate model without copying or moving data from Swift members’ secure locations, ensuring the highest level of security and privacy.

Financial organizations can now deploy this trained ML model to run a real-time fraud detection system, lowering the cost of financial fraud.

Organizations can deploy a fraud detection system like the following example on Microsoft Azure. The system ingests data from streams or storage, runs analytics, and uses ML to detect fraud and store results. It then alerts users and other systems to the security issue.

A diagram of an example fraud detection system

Swift provides the innovative model to its banking partners, who can share data to further train the model. Azure confidential computing securely runs new models on current data to accumulate insights, generating an ongoing learning cycle. This iterative approach strengthens the global detection of fraudulent financial transactions through an increasingly faster and more accurate model. 

The collaboration between Swift and Microsoft demonstrates how combining various Azure services creates Intelligent Apps to enhance decision-making in finance. These apps solve real problems at an unprecedented global scale. 

Enhancing Efficiency: Intelligent Apps in Manufacturing

3M, known for Post-it Notes and other innovative products, wielded the power of Intelligent Apps to standardize, automate, and accelerate its sales forecasting. The company needed a unified, automated approach to replace its multiple manual methods.

It adopted Azure Machine Learning and other Azure AI Services to develop, train, and implement 1,500 custom models for each region and division, replacing manual methods. This solution delivers timely finance, sales, and marketing insights while freeing teams from time-consuming manual data manipulation tasks.

3M used Azure Machine Learning, automated machine learning (AutoML), and the Many Models Solution Accelerator to train and score numerous machine learning models in parallel. This approach significantly shortened the company’s development cycle time.

Then, 3M integrated Microsoft Power BI into their solution to compile and visualize results more effectively. Executives now have an unbiased and unfiltered view of data for better decision-making. With its improved ability to forecast sales, 3M can focus on manufacturing the products it needs efficiently.

3M’s success demonstrates how combining various Azure services can enhance efficiency in the manufacturing industry. These apps improve operational processes while enabling employees to focus on higher-level tasks. Strategic decisions are more targeted, and business conversations are more meaningful.

Driving Innovation: Intelligent Apps in the Technology Industry

As expected, the tech industry is at the forefront of leveraging Intelligent Apps to drive innovation. Elastic saw an opportunity to use intelligent apps to optimize its search services and meet customer needs more effectively. The company built a managed, secure, and scalable solution for its customers, Elastic Cloud, by leveraging Microsoft Azure.

Elastic Cloud uses Azure Machine Learning and other Azure AI Services to automate many basic management tasks, such as backing up data, upgrading, and scaling deployments. This approach enables Elastic’s customers to focus less on managing the software and more on serving their own clients. Customers can quickly deploy and manage Elastic Cloud on Kubernetes (ECK) using Azure Kubernetes Service.

As a result, Elastic’s cloud business is growing twice as fast as its on-premises solutions. Enterprise search users have reported significant improvements in system availability and employee satisfaction.

Additionally, Elastic incorporated Azure OpenAI Service into its Elasticsearch Relevance Engine (ESRE), providing more relevant answers to search queries. ESRE enables text, vector, and hybrid search, offering a more effective and accurate response to queries.

Elastic’s success story demonstrates how combining various Azure services can create intelligent Apps to drive innovation in the tech industry. These apps help improve operational processes and deliver better results quickly.

Revolutionizing Healthcare: Intelligent Apps in Medicine

Intelligent apps also drive transformative changes in the healthcare sector. Cambridgeshire and Peterborough NHS Foundation Trust, a healthcare provider in the United Kingdom, gradually shifted their on-premises systems to the cloud using Azure, improving flexibility, and lowering costs. They needed a solution for their electronic patient record (EPR) system that integrates 23 years of data across multiple systems and formats.

The Trust used Microsoft Azure and Azure Cognitive Search, including semantic search for understanding intent and contexts and cognitive capabilities for extracting insights from data, to standardize and automate data management. This approach made legacy, archive, and live data more discoverable, saving clinicians’ time. The Trust’s solution searches through millions of clinical documents in seconds, even recognizing handwritten notes without needing a separate optical character recognition (OCR) solution to extract text from images (Azure Cognitive Search automatically does it). Clinicians can now uncover previously inaccessible insights, enabling them to offer more personalized care.

The Trust also used Integration Services tools such as Azure Logic Apps to create workflows without writing code and Event Grid to connect Azure and third-party services through a publisher-subscriber model. They switched on a live feed from their new EPR system to Azure to feed real-time information into other applications. Researchers can now access anonymized data to fuel medical innovations beyond the NHS.

The healthcare provider also invested heavily in Power BI’s data visualization and analytical functions to sift through massive amounts of information. This level of data accessibility was previously impossible with their on-premises system. The new insights have significantly improved clinicians’ efficiency for better patient care.

Data accessibility and security are critical in the healthcare industry. Intelligent Apps empower organizations like the Trust to revolutionize medical care. AI capabilities helped the Trust improve its operational efficiency and user experiences, setting a new standard for healthcare in the digital age.

Summary

AI is driving transformation and real-world successes across industries. Intelligent apps are reshaping our world by streamlining operations in manufacturing and logistics, enhancing customer experience in the retail sector, improving decision-making in finance, driving innovation in tech, and revolutionizing healthcare.

As organizations adopt AI, they’re innovating in ways that were once too difficult, expensive, or outright impossible. Companies that wait to embrace intelligent apps risk the chance of falling behind. By embracing intelligent apps and leveraging the capabilities of Azure, you can harness the transformative potential of AI to remain on the cutting edge of technology.

Exercise

+ + \ No newline at end of file diff --git a/30daysofIA/tags/azure-kubernetes-service/page/8/index.html b/30daysofIA/tags/azure-kubernetes-service/page/8/index.html index de45d3775f..316ebd22ae 100644 --- a/30daysofIA/tags/azure-kubernetes-service/page/8/index.html +++ b/30daysofIA/tags/azure-kubernetes-service/page/8/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "azure-kubernetes-service" | Build Intelligent Apps On Azure +11 posts tagged with "azure-kubernetes-service" | Build Intelligent Apps On Azure @@ -14,14 +14,13 @@ - - + +
-

9 posts tagged with "azure-kubernetes-service"

View All Tags

· 4 min read
It's 30DaysOfIA

Continue The Learning Journey through Fall For Intelligent Apps! 🍂

What We'll Cover

Thank you! ♥️

image

It's hard to believe that JavaScript on Azure hack-together is ending! It seems like just yesterday that we launched this initiative, and yet here we are, 15 days later, with an incredible amount of learning and growth behind us. So... it's time for a wrap!

From the bottom of our hearts, we want to thank each and every one of you for your participation, engagement, and enthusiasm. It's been truly inspiring to see the passion and dedication from this strong community, and we're honored to be a part of it. ✨

Recap of The JavaScript on Azure Global Hack-Together

As we wrap up this exciting event, we wanted to take a moment to reflect on all that we've accomplished together. Over the last 15 days, we've covered a lot of ground, from the basics of contributing to Open source to the exploration of the Contoso Real Estate project from its Frontend to its Backend and future AI implementation.

Now that the hack-together is ending, we want to make sure that you have all the resources you need to continue honing your skills in the future. Whether you're looking to make your fist contribution to open source, become an open source maintainers, collaborate with others, or simply keep learning, there are plenty of resources out there to help you achieve your goals. So, let's dive in and explore all the ways you can continue to grow your JavaScript skills on Azure!

JSonAzure Hack-together Roadmap 📍:

hack-together-roadmap (2)

Recap on past Livestreams🌟:

Day 1️⃣: Opening Keynote (Hack-together Launch): Introduction to the Contoso Real Estate Open-source project!, managing complex and complex enterprise architecture, new announcements for JavaScript developers on Azure

Day 2️⃣: GitHub Copilot & Codespaces: Introduction to your AI pair programmer (GitHub Copilot) and your virtual developer environment on the cloud (GitHub Codespaces)

Day 6️⃣: Build your Frontend using Static Web Apps as part of a complex, modern composable frontends (or micro-frontends) and cloud-native applications.

Day 9️⃣: Build a Serverless Backend using Azure Functions

Day 1️⃣3️⃣: Easily connect to an Azure Cosmos DB, exploring its benefits and how to get started

Day 1️⃣5️⃣: Being in the AI Era, we dive into the Azure OpenAI Service and how you can start to build intelligent JavaScript applications

📖 Self-Learning Resources

  1. JavaScript on Azure Global Hack Together Module collection
  2. Lets #HackTogether: Javascript On Azure Keynote
  3. Step by Step Guide: Migrating v3 to v4 programming model for Azure Functions for Node.Js Application

Continue your journey with #FallForIntelligentApps

Join us this Fall on a learning journey to explore building intelligent apps. Combine the power of AI, cloud-native app development, and cloud-scale data to build highly differentiated digital experiences. Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure. Engage in a self-paced learning adventure by following along the #30Days of Intelligent Apps series, completing the Intelligent Apps Skills Challenge, joining the Product Group for a live Ask The Expert series or building an end to end solution architecture with a live guided experience through the Learn Live series. Discover more here.

Hands-on practice: Make your first contribution to open source!

Join our GitHUb Discussion Forum to connect with developers from every part of world, see contributions from other, find collaborators and make your first contribution to a real-world project! -Don't forget to give the repo a star ⭐

Resources

All resources are accessible on our landing page

- - +
Skip to main content

11 posts tagged with "azure-kubernetes-service"

View All Tags

· 8 min read
It's 30DaysOfIA

Let’s start with understanding the power of intelligent applications.

What We'll Cover:

  • What are intelligent applications?
  • Categories of Intelligent Apps
  • Breaking down the role of AI in intelligent apps

A decorative header image for the blog

Demystifying Intelligent Applications: Leveraging AI in App Development

Until recent years, when you heard the term “artificial intelligence” (AI), you might have pictured autonomous robots from sci-fi films. Now, perhaps your mind jumps to big data and machine learning (ML). Maybe you’re even thinking about the latest ChatGPT plugins and transformer architectures. Today, AI is more than these imaginations: it’s a reality increasingly woven into our day-to-day routines through intelligent applications.

Intelligent apps combine cloud-scale compute, data, and predictive or generative AI. These applications help you automate tasks and make data-driven decisions, aiding in leveraging business intelligence to gain meaningful, practical outcomes. 

For developers, the AI landscape isn’t an abstract complexity. It’s a real, tangible opportunity for evolving traditional applications, making them “intelligent,” and delivering more value to users. And with tools like Azure Kubernetes Service, Azure Cosmos DB, and Azure AI Services, stepping into the world of intelligent apps isn’t a steep climb but a steady trek across a manageable learning curve.

The Three Categories of Intelligent Applications

Before we start building, it’s critical to understand what makes an app “intelligent.” It’s not a universal definition so much as an identifier for apps that fall into one or more of the three primary categories: 

  • Outcome-based — This designation represents the broadest category of intelligent apps, whose principal value for users is the intelligent outcomes they provide. These apps process data to provide insightful results that enhance decision-making or help with tasks like trend prediction, resource management, and customer relationship management.

  • Functionality-based — This type of intelligent app takes AI integration a degree further, integrating AI as a function of the app to create or process its outputs, enhancing the app’s overall interactivity and usefulness.

  • Feature-based — This is the narrowest category, in which the app’s AI/ML component is one of its core features and primary selling points

Let’s dive more deeply into each category.

Outcome-Based Apps: Intelligent Outcomes Drive Success

Outcome-based applications are all about the results provided to the user. They function beyond merely processing data to offer insightful outcomes that drive success.

Think of a personal fitness tracker app that uses AI to analyze the user’s activity and provides actionable recommendations for achieving a goal. This type of app not only tracks your steps, sleep patterns, and heart rate but uses that data to provide you with personalized health advice. It helps you make informed lifestyle choices — the “intelligent” outcome.

Another example is a social media marketing app that uses AI for time-saving data analysis, task automation, and trend prediction. AI-based sentiment analysis can gauge customer perception while natural language processing (NLP) accurately interprets customer inquiries. Task automation can schedule posts and automate responses to common customer queries, and trend forecasting helps model and predict customer behavior to further personalize targeted promotional efforts. Building this type of intelligence into your apps provides cost-effective, actionable insights that guide sales and marketing strategies.

Functionality-Based Apps: AI-Driven Features Enhance User Experiences

As the name suggests, functionality-based applications integrate AI into how the app functions, but the end user drives the action and outcome. In this category of apps, AI takes a more active role in the user’s interaction by allowing them to use AI-created or processed output. These apps leverage AI technologies like Azure AI Services for NLP, image recognition, and pattern recognition to improve user experience.

Consider a music app that generates personalized playlists based on your listening habits or your smartphone’s camera app that uses AI to recognize faces and optimize photo settings. Even more advanced are the language translation apps that translate text from one language to another and can even understand and maintain the context and subtleties of the conversation. They use NLP to accomplish these feats.

Similarly, consider the latest e-commerce apps, whose AI-driven functionalities enable customers to try on clothes virtually, significantly enhancing the online shopping experience.

Feature-Based Apps: Integrating Advanced AI/ML Components

Finally, there are feature-based intelligent apps. These apps incorporate advanced AI/ML components like neural networks, internal large language models (LLMs), and sophisticated algorithms as core features for the user. In this type of application, AI is the primary feature for users to engage with and the primary selling point for the app.

Some examples of feature-based applications are chatbots and virtual agents built using Azure Bot Service. These robo-representatives use AI to provide more natural and human-like conversation, balancing superior efficiency with a friendlier, more personalized user experience. The new Bing is a prime example of this implementation, integrating AI to give users a knowledgeable copilot for web searches.

Similarly, OpenAI’s ChatGPT allows users access to its state-of-the-art language models available as Microsoft’s Azure OpenAI Service. It leverages the power of the highly sophisticated GPT-3 LLM to perform tasks like drafting emails, writing code, answering general knowledge questions, translating between languages, and tutoring in numerous subjects.

These apps don’t just use AI to provide outputs or improve user experiences: They push the boundaries of what’s possible in app development by integrating AI/ML components.

info

Register for the live Q&A Ask The Expert session on September 26 with the Azure Functions product engineering team.

Breaking Down the Role of AI in Intelligent Applications

Intelligent apps reflect the merging of traditional software design with the dynamic capabilities of AI. Let’s explore how AI integrates and amplifies these applications.

At its core, AI augments applications with the capacity to self-learn and self-improve. It allows you to create applications that predict, react, and adapt to a constantly changing environment. In the power grid management system for a major city, an AI-infused application can forecast power usage trends, react to anomalies, and adapt to sudden demand surges, ensuring optimal efficiency and preventing blackouts.

But AI’s role doesn’t end at prediction or decision-making: It also automates tasks. In enterprise solutions, it has moved beyond simple automation to encompass intelligent process automation (IPA). In this arena, AI algorithms understand, learn, and then automate entire digital workflows. For developers, this is a fertile ground for creating applications that can dramatically improve the operational efficiency of businesses.

Even more, AI substantially aids in development tasks. For example, there’s GitHub Copilot, an AI-powered code assistant that offers suggestions based not only on syntax but on development best practices and the intent of the code. AI has evolved from a tool you use to build apps to a partner that helps in creating them. 

Using AI to build intelligent apps isn’t about throwing out what’s worked before. It’s about enhancing the components that have the most impact and achieve the best results. Incorporating AI in apps elevates their predictive abilities, streamlines complex tasks, and even aids in the development and creation process. When you build intelligent apps, you don’t just address current problems. You anticipate future challenges and craft solutions that adapt over time.

Summary

As developers the power of AI is in our hands, and it’s our responsibility to harness its full potential. Each category tells the story of AI's transformative potential, from applications that deliver intelligent outcomes to those with AI capabilities to enrich the user experience and those pushing boundaries with advanced AI/ML technology.

Accessible and robust platforms like Azure AI can simplify the process of developing Intelligent Applications. Whether you’re refining an existing app or building a new one, just entering the world of AI, or already familiar with the landscape, take a moment to appreciate the potential that AI integration offers. You’re embracing this exciting frontier and shaping the future of app development. 

Exercise

  • Complete the Cloud Skills Challenge to build on your apps and AI skills.
  • Watch Episode 01 of the Serverless Edition Learn Live session to learn how to build an end-to-end intelligent app solution.
+ + \ No newline at end of file diff --git a/30daysofIA/tags/azure-kubernetes-service/page/9/index.html b/30daysofIA/tags/azure-kubernetes-service/page/9/index.html index 16474ec78a..04efbcc223 100644 --- a/30daysofIA/tags/azure-kubernetes-service/page/9/index.html +++ b/30daysofIA/tags/azure-kubernetes-service/page/9/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "azure-kubernetes-service" | Build Intelligent Apps On Azure +11 posts tagged with "azure-kubernetes-service" | Build Intelligent Apps On Azure @@ -14,13 +14,14 @@ - - + +
-

9 posts tagged with "azure-kubernetes-service"

View All Tags

· 2 min read
It's 30DaysOfIA

September is almost here - and that can only mean one thing!! It's time to 🍂 Fall for something new and exciting and spend a few weeks skilling up on relevant tools, techologies and solutions!!

Last year, we focused on #ServerlessSeptember. This year, we're building on that theme with the addition of cloud-scale Data, cloud-native Technologies and cloud-based AI integrations to help you modernize and build intelligent apps for the enterprise!

Watch this space - and join us in September to learn more!

- - +
Skip to main content

11 posts tagged with "azure-kubernetes-service"

View All Tags

· 4 min read
It's 30DaysOfIA

This Fall focus on building intelligent apps using AI and cloud-native technologies. #FallForIntelligentApps brings to you a learning journey to build your skills on creating differentiated experiences while modernizing your applications. It’s time to learn it all.

What We'll Cover

image

Get Ready To #FallForIntelligentApps starting September 18!

Today, we kick off the Fall season with content and activities to skill you up on all things Intelligent Apps or AI Apps on Azure with content, events, and community interactions! Read on to learn about what is coming!

Explore Our Initiatives

We have a number of initiatives planned for the month to help you learn and skill up on relevant technologies. Click on the links to visit the relevant pages for each

image

Register for the events!

What are 4 things you can do today, to jumpstart your learning journey?

#30Days Of Intelligent Apps

#30DaysOfIA is a series of daily blog posts grouped into themed weeks - taking you from core concepts to end-to-end solution examples in 30 days. Each article will provide conceptual lessons paired with exercises and resources to help you reinforce learnings and take next steps.

This series takes you through learning journey in six stages, each building on the previous week to help you skill up in a beginner-friendly way:

We will start with defining intelligent apps and then expand on how to build with cloud-native technologies like Azure Kubernetes Service, Azure Container Apps and Azure Functions, and take you through end-to-end scenarios for real world application development. Before we dive deep on intelligent apps, here is a high-level overview of the Intelligent Apps landscape on Azure:

image

Containers on Azure services offer you a wide range of capabilities, from simplicity to control to suit your different needs.

image +image

To start with the basics for developing Kubernetes applications, explore #30DaysOfCloudNative.

Cloud-native development when paired with serverless computing enhances your solution architecture for building cost optimized, resilient applications.

image

To start with the basics for serverless computing, explore #30DaysOfServerless.

Let’s Get Started

Now you know everything! We hope you are as excited as we are to dive into a full month of active learning and doing! Don't forget to subscribe for updates in your favorite feed reader! And look out for our first Intelligent Apps post Monday!

+ + \ No newline at end of file diff --git a/30daysofIA/tags/azure-openai/index.html b/30daysofIA/tags/azure-openai/index.html index c756f4a346..7fbab1c31b 100644 --- a/30daysofIA/tags/azure-openai/index.html +++ b/30daysofIA/tags/azure-openai/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "azure-openai" | Build Intelligent Apps On Azure +11 posts tagged with "azure-openai" | Build Intelligent Apps On Azure @@ -14,13 +14,13 @@ - - + +
-

9 posts tagged with "azure-openai"

View All Tags

· 10 min read
It's 30DaysOfIA

Prepare your organization to adopt intelligent apps by taking a change management approach. Assess your readiness, upskill teams, create a culture of change and overcome resistance, and prepare for future evolution to pave the way for a smooth transition into an AI-centric company.

What We'll Cover:

  • Role of change management in modernizing to intelligent apps
  • Skills needed for building intelligent apps in your organization
  • How to build a culture of change to embrace AI
  • How to prepare for the future of intelligent apps and continuous evolution

image of modernizing AI solutions for intelligent apps

Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management

As we’ve discussed throughout this series, intelligent apps and generative AI are rewriting the rules of business and application development. The first article of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” explored how intelligent apps can enhance user experiences and drive businesses growth. These advancements are opening new opportunities and markets for companies ready to embrace them.

However, embracing this digital transformation is more complex than flipping a switch. It requires adjustments to the technical side, which we explored in the series’ fifth article, “Preparing the Path for Intelligent Apps: Transitioning from On-Premises/IaaS to Cloud Native Applications.” It also demands a shift in how organizations adapt to change and approach app design and development. Let’s explore that mindset transition with tips to help you make it happen.

The Role of Change Management in Transitioning to Intelligent Apps

Transitioning to intelligent apps isn’t merely a technological change. It’s a profound shift in how businesses perceive and operate. This move toward artificial intelligence (AI)-centric approach isn’t meant to replace humans with machines or radically overhaul all business operations.

Instead, intelligent apps should augment human capabilities, streamline processes, and foster an environment where innovation flourishes. They expand toolkits and techniques to include AI-assisted development tools like GitHub Copilot and AI-enabled workflows, helping us design and deliver AI-enhanced experiences.

Effective change management strategies are crucial in helping employees adapt to these new ways of working with intelligent apps, ensuring that the transition doesn’t disrupt productivity or morale.

Team and organization strategies should: 

  • Assess the impact of any proposed changes.  
  • Communicate the benefits and reasons behind the change to all affected team members.  
  • Provide the necessary training and support.  
  • Integrate the changes into the culture and daily operations. 

By incorporating this robust change management approach, you can identify and mitigate potential transition risks early. Moreover, this strategy ensures that employees are engaged throughout the process, fostering a sense of involvement and ownership. This, in turn, cultivates a culture suited for innovation through intelligent apps. 

These organizational changes focus on the following aspects: organizational readiness, skillset, culture of change and overcoming resistance, and adaptability. Let’s explore them below.

info

Register for the intelligent apps webinar on Driving Business Value by Modernizing with Cloud-Native & AI with Microsoft and Forrester on September 26. 

Explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries. 

Identifying Organizational Readiness for Intelligent Apps

The first step in preparing your organization for intelligent apps is to assess your current readiness for change. Analyze various elements of your organization, from infrastructure and workforce skills to strategic vision and overall culture.

Can your apps and systems integrate with AI platforms like Microsoft Azure AI? Is your workforce comfortable working alongside AI, or are they fearful and resistant? Does your strategic vision incorporate AI’s transformative potential, or does it fail to acknowledge AI as a critical driver of future growth?

These questions identify the challenges of integrating AI into an organization and determine the company’s readiness for intelligent apps.

Embracing intelligent apps involves more than being ready with your technological infrastructure or having the right technology or tools. You need flexible organizational structures and processes driving your business to accommodate new ideas and capabilities. Your teams must feel empowered to own and drive them. Much like Microsoft’s shift to AI-powered services—like AI-powered Bing and Edge and Microsoft 365 Copilot—embracing AI organization-wide requires a holistic vision and willingness to innovate, with teams capable of delivering on the plan.

After accessing these critical areas for organizational readiness, you can start planning a strategy for this transition to an AI-ready organization. Identify skill gaps and work to bridge them while nurturing an environment that appreciates and harnesses the potential of AI and intelligent apps.

Nurturing the Indispensable Skills for Intelligent Apps

Tasking your AI specialists with preparing your organization to use and build intelligent apps isn’t enough. Accomplishing this goal requires a cross-functional effort and a diverse skill set.

Developing intelligent apps involves data scientists managing and interpreting vast amounts of data, machine learning engineers building and training the models, and traditional developers integrating these models into usable applications. However, the impact of generative AI spans beyond creating apps. AI fluency across the entire organization—from sales and marketing to accounting and legal departments—offers substantial benefits, sparking new ideas and solutions from sometimes unexpected sources.

Teams' AI skills might roughly fall into three levels: 

  • Fundamental skills — Being familiar and efficient with using AI tools, for example, knowing how to write effective prompts for large language models (LLMs).
  • Visionary skills — Recognizing AI’s strengths and weaknesses, for example, its creativity and hallucinations, that is, unexpected output.
  • Expert skills — Managing data and training models and integrating AI technology into apps, such as incorporating the OpenAI application programming interface (API) into an Intelligent App.

Based on these levels, you can gauge your team's skill readiness to pinpoint the most significant gaps, whether in general AI knowledge across your organization or within technical development teams.

As intelligent apps become increasingly prevalent, upskilling your workforce is crucial. This transition opens a new realm of skills and capabilities for your team to master. You can offer machine learning, data analysis, and cloud computing training programs—such as Microsoft’s Azure AI Fundamentals certification—to empower your team to confidently navigate the intelligent apps landscape.

Nurturing these skills within your organization may require hiring new talent, reskilling current employees, and creating an environment that promotes learning and experimentation. Specifically, if your team understands tools like the Microsoft Azure AI platform, they could contribute significantly to developing and maintaining intelligent apps.

An upskilled team is more than a functional unit. It’s a think tank to drive innovation and improve your organization’s competitive standing in the digital marketplace.

Building a Culture of Change to Embrace AI

Even with the right people and skills in place, change remains an uphill battle for organizations committed to traditional processes. 

Transitioning to intelligent apps is as much about mindset as technology and skills. It requires fostering a culture open to change and innovation that understands AI’s transformative potential and embraces it to enhance rather than replace human capabilities. 

Leaders play a pivotal role in cultivating this mindset. As they model a positive attitude toward AI, emphasize its benefits, and address any concerns transparently, leaders significantly contribute to the organization’s readiness to adopt intelligent apps. 

The most critical factor in this transition is enabling an AI-friendly culture. It’s about encouraging curiosity, rewarding innovative thinking, and creating an environment where people thrive on learning and experimentation. 

Empowering teams to leverage practical AI tools and AI-driven data insights to aid decision-making helps motivate employees to use and explore AI’s potential, improve their skills, and contribute to the successful integration of intelligent apps. Such an environment is fertile ground to grow breakthrough ideas and innovative solutions. 

Overcoming Resistance to Change and Adopting Intelligent Apps

Despite AI’s potential, change isn’t easy. It takes effort and is especially challenging for organizations used to existing processes. This resistance to change is a significant roadblock when transitioning to intelligent apps. 

Overcoming this resistance involves:  

  • Understanding employees’ concerns. 
  • Challenging underlying assumptions about traditional systems and outdated processes that hinder change. 
  • Providing comprehensive training for workers to use AI effectively.
  • Demonstrating the benefits and opportunities of intelligent apps. 
  • Clear and continuous communication to dispel fears and misconceptions about AI.

These steps will foster greater acceptance and enthusiasm for intelligent apps in your teams. 

Preparing for the Future of Intelligent Apps and Continuous Evolution

Finally, it’s essential to understand that generative AI is just beginning to gain momentum. Adjusting to a world of intelligent apps isn’t a one-time change—it’s a continuous evolution. 

As AI technologies mature and evolve and more businesses realize their benefits, we can expect the landscape of intelligent apps to expand further. Organizations can cultivate a mindset of continuous learning, flexibility, and adaptation to prepare for this future. Staying ahead in this dynamic field requires organizations to keep up with the latest AI advancements, continually reassess their strategies, and pivot when necessary. 

The domain of intelligent apps is rapidly advancing. Organizations must prepare to navigate this dynamic landscape to stay ahead of the curve.

Summary

Preparing for AI and intelligent apps is a strategic decision. It requires thoughtful preparation and a culture that embraces change.

With the proper preparation and mindset, adopting AI can become a smoother, collaborative path to innovation for the whole organization. Integrating AI into your organization does not discard what has worked in the past. It simply enhances and augments those processes for a better future.

Consider the points above to assess your organization’s readiness and prepare to welcome intelligent apps.

In the first week of this series, you learned about intelligent apps, how organizations today find success through intelligent apps, what AI-assisted application development looks like, ways to leverage the power of generative AI, the technical changes needed to prepare for intelligent apps, and, in this article, the organizational changes required to harness the power of AI.

The future is intelligent. Use this series as a guideline to navigate challenges while preparing for and embracing AI's opportunities for an intelligent tomorrow.

Exercise

- - +
Skip to main content

11 posts tagged with "azure-openai"

View All Tags

· 10 min read
It's 30DaysOfIA

Prepare your organization to adopt intelligent apps by taking a change management approach. Assess your readiness, upskill teams, create a culture of change and overcome resistance, and prepare for future evolution to pave the way for a smooth transition into an AI-centric company.

What We'll Cover:

  • Role of change management in modernizing to intelligent apps
  • Skills needed for building intelligent apps in your organization
  • How to build a culture of change to embrace AI
  • How to prepare for the future of intelligent apps and continuous evolution

image of modernizing AI solutions for intelligent apps

Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management

As we’ve discussed throughout this series, intelligent apps and generative AI are rewriting the rules of business and application development. The first article of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” explored how intelligent apps can enhance user experiences and drive businesses growth. These advancements are opening new opportunities and markets for companies ready to embrace them.

However, embracing this digital transformation is more complex than flipping a switch. It requires adjustments to the technical side, which we explored in the series’ fifth article, “Preparing the Path for Intelligent Apps: Transitioning from On-Premises/IaaS to Cloud Native Applications.” It also demands a shift in how organizations adapt to change and approach app design and development. Let’s explore that mindset transition with tips to help you make it happen.

The Role of Change Management in Transitioning to Intelligent Apps

Transitioning to intelligent apps isn’t merely a technological change. It’s a profound shift in how businesses perceive and operate. This move toward artificial intelligence (AI)-centric approach isn’t meant to replace humans with machines or radically overhaul all business operations.

Instead, intelligent apps should augment human capabilities, streamline processes, and foster an environment where innovation flourishes. They expand toolkits and techniques to include AI-assisted development tools like GitHub Copilot and AI-enabled workflows, helping us design and deliver AI-enhanced experiences.

Effective change management strategies are crucial in helping employees adapt to these new ways of working with intelligent apps, ensuring that the transition doesn’t disrupt productivity or morale.

Team and organization strategies should: 

  • Assess the impact of any proposed changes.  
  • Communicate the benefits and reasons behind the change to all affected team members.  
  • Provide the necessary training and support.  
  • Integrate the changes into the culture and daily operations. 

By incorporating this robust change management approach, you can identify and mitigate potential transition risks early. Moreover, this strategy ensures that employees are engaged throughout the process, fostering a sense of involvement and ownership. This, in turn, cultivates a culture suited for innovation through intelligent apps. 

These organizational changes focus on the following aspects: organizational readiness, skillset, culture of change and overcoming resistance, and adaptability. Let’s explore them below.

info

Register for the intelligent apps webinar on Driving Business Value by Modernizing with Cloud-Native & AI with Microsoft and Forrester on September 26. 

Explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries. 

Identifying Organizational Readiness for Intelligent Apps

The first step in preparing your organization for intelligent apps is to assess your current readiness for change. Analyze various elements of your organization, from infrastructure and workforce skills to strategic vision and overall culture.

Can your apps and systems integrate with AI platforms like Microsoft Azure AI? Is your workforce comfortable working alongside AI, or are they fearful and resistant? Does your strategic vision incorporate AI’s transformative potential, or does it fail to acknowledge AI as a critical driver of future growth?

These questions identify the challenges of integrating AI into an organization and determine the company’s readiness for intelligent apps.

Embracing intelligent apps involves more than being ready with your technological infrastructure or having the right technology or tools. You need flexible organizational structures and processes driving your business to accommodate new ideas and capabilities. Your teams must feel empowered to own and drive them. Much like Microsoft’s shift to AI-powered services—like AI-powered Bing and Edge and Microsoft 365 Copilot—embracing AI organization-wide requires a holistic vision and willingness to innovate, with teams capable of delivering on the plan.

After accessing these critical areas for organizational readiness, you can start planning a strategy for this transition to an AI-ready organization. Identify skill gaps and work to bridge them while nurturing an environment that appreciates and harnesses the potential of AI and intelligent apps.

Nurturing the Indispensable Skills for Intelligent Apps

Tasking your AI specialists with preparing your organization to use and build intelligent apps isn’t enough. Accomplishing this goal requires a cross-functional effort and a diverse skill set.

Developing intelligent apps involves data scientists managing and interpreting vast amounts of data, machine learning engineers building and training the models, and traditional developers integrating these models into usable applications. However, the impact of generative AI spans beyond creating apps. AI fluency across the entire organization—from sales and marketing to accounting and legal departments—offers substantial benefits, sparking new ideas and solutions from sometimes unexpected sources.

Teams' AI skills might roughly fall into three levels: 

  • Fundamental skills — Being familiar and efficient with using AI tools, for example, knowing how to write effective prompts for large language models (LLMs).
  • Visionary skills — Recognizing AI’s strengths and weaknesses, for example, its creativity and hallucinations, that is, unexpected output.
  • Expert skills — Managing data and training models and integrating AI technology into apps, such as incorporating the OpenAI application programming interface (API) into an Intelligent App.

Based on these levels, you can gauge your team's skill readiness to pinpoint the most significant gaps, whether in general AI knowledge across your organization or within technical development teams.

As intelligent apps become increasingly prevalent, upskilling your workforce is crucial. This transition opens a new realm of skills and capabilities for your team to master. You can offer machine learning, data analysis, and cloud computing training programs—such as Microsoft’s Azure AI Fundamentals certification—to empower your team to confidently navigate the intelligent apps landscape.

Nurturing these skills within your organization may require hiring new talent, reskilling current employees, and creating an environment that promotes learning and experimentation. Specifically, if your team understands tools like the Microsoft Azure AI platform, they could contribute significantly to developing and maintaining intelligent apps.

An upskilled team is more than a functional unit. It’s a think tank to drive innovation and improve your organization’s competitive standing in the digital marketplace.

Building a Culture of Change to Embrace AI

Even with the right people and skills in place, change remains an uphill battle for organizations committed to traditional processes. 

Transitioning to intelligent apps is as much about mindset as technology and skills. It requires fostering a culture open to change and innovation that understands AI’s transformative potential and embraces it to enhance rather than replace human capabilities. 

Leaders play a pivotal role in cultivating this mindset. As they model a positive attitude toward AI, emphasize its benefits, and address any concerns transparently, leaders significantly contribute to the organization’s readiness to adopt intelligent apps. 

The most critical factor in this transition is enabling an AI-friendly culture. It’s about encouraging curiosity, rewarding innovative thinking, and creating an environment where people thrive on learning and experimentation. 

Empowering teams to leverage practical AI tools and AI-driven data insights to aid decision-making helps motivate employees to use and explore AI’s potential, improve their skills, and contribute to the successful integration of intelligent apps. Such an environment is fertile ground to grow breakthrough ideas and innovative solutions. 

Overcoming Resistance to Change and Adopting Intelligent Apps

Despite AI’s potential, change isn’t easy. It takes effort and is especially challenging for organizations used to existing processes. This resistance to change is a significant roadblock when transitioning to intelligent apps. 

Overcoming this resistance involves:  

  • Understanding employees’ concerns. 
  • Challenging underlying assumptions about traditional systems and outdated processes that hinder change. 
  • Providing comprehensive training for workers to use AI effectively.
  • Demonstrating the benefits and opportunities of intelligent apps. 
  • Clear and continuous communication to dispel fears and misconceptions about AI.

These steps will foster greater acceptance and enthusiasm for intelligent apps in your teams. 

Preparing for the Future of Intelligent Apps and Continuous Evolution

Finally, it’s essential to understand that generative AI is just beginning to gain momentum. Adjusting to a world of intelligent apps isn’t a one-time change—it’s a continuous evolution. 

As AI technologies mature and evolve and more businesses realize their benefits, we can expect the landscape of intelligent apps to expand further. Organizations can cultivate a mindset of continuous learning, flexibility, and adaptation to prepare for this future. Staying ahead in this dynamic field requires organizations to keep up with the latest AI advancements, continually reassess their strategies, and pivot when necessary. 

The domain of intelligent apps is rapidly advancing. Organizations must prepare to navigate this dynamic landscape to stay ahead of the curve.

Summary

Preparing for AI and intelligent apps is a strategic decision. It requires thoughtful preparation and a culture that embraces change.

With the proper preparation and mindset, adopting AI can become a smoother, collaborative path to innovation for the whole organization. Integrating AI into your organization does not discard what has worked in the past. It simply enhances and augments those processes for a better future.

Consider the points above to assess your organization’s readiness and prepare to welcome intelligent apps.

In the first week of this series, you learned about intelligent apps, how organizations today find success through intelligent apps, what AI-assisted application development looks like, ways to leverage the power of generative AI, the technical changes needed to prepare for intelligent apps, and, in this article, the organizational changes required to harness the power of AI.

The future is intelligent. Use this series as a guideline to navigate challenges while preparing for and embracing AI's opportunities for an intelligent tomorrow.

Exercise

+ + \ No newline at end of file diff --git a/30daysofIA/tags/azure-openai/page/10/index.html b/30daysofIA/tags/azure-openai/page/10/index.html new file mode 100644 index 0000000000..2338029064 --- /dev/null +++ b/30daysofIA/tags/azure-openai/page/10/index.html @@ -0,0 +1,27 @@ + + + + + +11 posts tagged with "azure-openai" | Build Intelligent Apps On Azure + + + + + + + + + + + + + + +
+

11 posts tagged with "azure-openai"

View All Tags

· 4 min read
It's 30DaysOfIA

Continue The Learning Journey through Fall For Intelligent Apps! 🍂

What We'll Cover

Thank you! ♥️

image

It's hard to believe that JavaScript on Azure hack-together is ending! It seems like just yesterday that we launched this initiative, and yet here we are, 15 days later, with an incredible amount of learning and growth behind us. So... it's time for a wrap!

From the bottom of our hearts, we want to thank each and every one of you for your participation, engagement, and enthusiasm. It's been truly inspiring to see the passion and dedication from this strong community, and we're honored to be a part of it. ✨

Recap of The JavaScript on Azure Global Hack-Together

As we wrap up this exciting event, we wanted to take a moment to reflect on all that we've accomplished together. Over the last 15 days, we've covered a lot of ground, from the basics of contributing to Open source to the exploration of the Contoso Real Estate project from its Frontend to its Backend and future AI implementation.

Now that the hack-together is ending, we want to make sure that you have all the resources you need to continue honing your skills in the future. Whether you're looking to make your fist contribution to open source, become an open source maintainers, collaborate with others, or simply keep learning, there are plenty of resources out there to help you achieve your goals. So, let's dive in and explore all the ways you can continue to grow your JavaScript skills on Azure!

JSonAzure Hack-together Roadmap 📍:

hack-together-roadmap (2)

Recap on past Livestreams🌟:

Day 1️⃣: Opening Keynote (Hack-together Launch): Introduction to the Contoso Real Estate Open-source project!, managing complex and complex enterprise architecture, new announcements for JavaScript developers on Azure

Day 2️⃣: GitHub Copilot & Codespaces: Introduction to your AI pair programmer (GitHub Copilot) and your virtual developer environment on the cloud (GitHub Codespaces)

Day 6️⃣: Build your Frontend using Static Web Apps as part of a complex, modern composable frontends (or micro-frontends) and cloud-native applications.

Day 9️⃣: Build a Serverless Backend using Azure Functions

Day 1️⃣3️⃣: Easily connect to an Azure Cosmos DB, exploring its benefits and how to get started

Day 1️⃣5️⃣: Being in the AI Era, we dive into the Azure OpenAI Service and how you can start to build intelligent JavaScript applications

📖 Self-Learning Resources

  1. JavaScript on Azure Global Hack Together Module collection
  2. Lets #HackTogether: Javascript On Azure Keynote
  3. Step by Step Guide: Migrating v3 to v4 programming model for Azure Functions for Node.Js Application

Continue your journey with #FallForIntelligentApps

Join us this Fall on a learning journey to explore building intelligent apps. Combine the power of AI, cloud-native app development, and cloud-scale data to build highly differentiated digital experiences. Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure. Engage in a self-paced learning adventure by following along the #30Days of Intelligent Apps series, completing the Intelligent Apps Skills Challenge, joining the Product Group for a live Ask The Expert series or building an end to end solution architecture with a live guided experience through the Learn Live series. Discover more here.

Hands-on practice: Make your first contribution to open source!

Join our GitHUb Discussion Forum to connect with developers from every part of world, see contributions from other, find collaborators and make your first contribution to a real-world project! +Don't forget to give the repo a star ⭐

Resources

All resources are accessible on our landing page

+ + + + \ No newline at end of file diff --git a/30daysofIA/tags/azure-openai/page/11/index.html b/30daysofIA/tags/azure-openai/page/11/index.html new file mode 100644 index 0000000000..5f10290a93 --- /dev/null +++ b/30daysofIA/tags/azure-openai/page/11/index.html @@ -0,0 +1,26 @@ + + + + + +11 posts tagged with "azure-openai" | Build Intelligent Apps On Azure + + + + + + + + + + + + + + +
+

11 posts tagged with "azure-openai"

View All Tags

· 2 min read
It's 30DaysOfIA

September is almost here - and that can only mean one thing!! It's time to 🍂 Fall for something new and exciting and spend a few weeks skilling up on relevant tools, techologies and solutions!!

Last year, we focused on #ServerlessSeptember. This year, we're building on that theme with the addition of cloud-scale Data, cloud-native Technologies and cloud-based AI integrations to help you modernize and build intelligent apps for the enterprise!

Watch this space - and join us in September to learn more!

+ + + + \ No newline at end of file diff --git a/30daysofIA/tags/azure-openai/page/2/index.html b/30daysofIA/tags/azure-openai/page/2/index.html index 66429c338a..e12b46599b 100644 --- a/30daysofIA/tags/azure-openai/page/2/index.html +++ b/30daysofIA/tags/azure-openai/page/2/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "azure-openai" | Build Intelligent Apps On Azure +11 posts tagged with "azure-openai" | Build Intelligent Apps On Azure @@ -14,13 +14,13 @@ - - + +
-

9 posts tagged with "azure-openai"

View All Tags

· 9 min read
It's 30DaysOfIA

Learn best practices for modernizing your on-premises or IaaS solutions to intelligent apps in the cloud.

What We'll Cover:

  • Traditional vs Intelligent Apps
  • On-Premises/IaaS vs. Cloud-Native Platforms
  • Modernization strategy for building Intelligent Apps

image of modernizing AI solutions for intelligent apps

Preparing the Path for Intelligent Apps: Transitioning from On-Premises/IaaS to Cloud-Native Applications

The proliferation of generative AI is paving the way for intelligent applications, software that leverages AI capabilities to deliver unparalleled functionality and user experience. As we covered in a previous article, “Demystifying Intelligent Applications: Leveraging AI in App Development,” intelligent Apps aren’t just products of advanced coding. They interact, learn, and evolve thanks to modern AI and machine learning (ML) breakthroughs. 

The paradigm shift from traditional to intelligent apps demands that we change our approach to the technical challenges of software and architecture design. It also necessitates adopting new ways of thinking and operating within an organization, which we explore in greater depth in “Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management.” 

This article explores the technical infrastructure powering intelligent apps, providing a roadmap to transition traditional on-premises or Infrastructure as a Service (IaaS) solutions to intelligent apps deployed to cloud native platforms and services. We’ll discover the differences between conventional and intelligent apps, options for re-architecting existing applications, and critical strategic considerations integral to modernizing the application-building approach. 

Understanding the Shift: Traditional vs. Intelligent Applications

Intelligent apps are more than just an incremental evolution from traditional apps. They involve a fundamental shift in software solution design, development, and deployment. A clear understanding of how each paradigm differs is crucial for comprehending the impact of this transition. 

Functionality and User Experience

Traditional applications, often rule-based and rigid, rely on preprogrammed operations. A traditional weather app, for example, typically presents structured, location-based forecasts using a predetermined data source. Meanwhile, an intelligent app leverages AI for data-driven decision-making and personalization. Rather than merely displaying the forecast, it integrates ML with additional data sources—like a user’s calendar and fitness data—to determine their favorite outdoor activities, provide relevant clothing suggestions, and even automatically design ideal vacations and times based on the user’s optimal weather preferences. 

Similarly, while traditional applications tend to offer a one-size-fits-all UI, intelligent apps use AI to create tailored interactions. For instance, an intelligent banking app might use generative AI to provide a voice-enabled UI that lets customers use natural language to ask about transactions and receive personalized financial advice.

Infrastructure

Intelligent apps integrating these sophisticated features require highly scalable, flexible infrastructure optimized for AI functionality and able to manage large volumes of real-time data. Unlike traditional applications deployed on-premises or in infrastructure as a service (IaaS) environments, intelligent apps demand the flexibility and scalability of cloud native architectures.

An on-premises or IaaS-based e-commerce application may struggle with connecting its data to AI services or scaling to accommodate voluminous real-time data and requests. But an intelligent app built with a cloud native approach and scalability at its core can handle variable demand much more efficiently. It also maintains its infrastructure components on demand, whether for collecting data for AI/ML consumption or user-facing features like AI-enabled customer support and personalization.

Consider an intelligent app built on Azure that creates ChatGPT-like experiences with custom data. It might connect Azure Open AI and Azure Cognitive Search to an app running on Azure Container Apps or Azure Kubernetes Service. 

The architecture might look like the following diagram based on this demo app:

diagram of a demo app architecture

While the cloud is well-equipped to handle the challenges associated with shifting to AI-centric infrastructure—handling real-time data processing and storage, integrating and managing AI services, configuring event-driven and microservices architectures, and ensuring security across the distributed environment—re-architecting a traditional app to an intelligent app requires careful planning and execution to ensure it benefits fully from the capabilities of AI and cloud computing.

On-Premises/IaaS vs. Cloud-Native Platforms

While on-premises and IaaS solutions provide substantial control over data and processes, this fine-grained control comes with setup and maintenance costs and may lack the scalability necessary for AI-driven Intelligent Apps. On-premises hosting involves purchasing and maintaining hardware, including servers and storage systems. IaaS involves leasing virtual hardware and then hiring and training IT personnel for the infrastructure’s operation, maintenance, and security.

Conversely, infrastructure built on cloud native app services with Microsoft Azure offers virtually unlimited scalability without ongoing hardware and maintenance costs. This feature is ideal for intelligent apps that need to adapt and scale to accommodate fluctuating workloads and data streams. And because these cloud infrastructures typically operate on a pay-as-you-go model, traditionally substantial upfront costs turn into predictable operating expenses, lowering both the cost and risk of launching applications.

Moreover, cloud service providers continually update their offerings, so building on sophisticated AI tools and services through the cloud, like the Azure AI Platform and orchestration capabilities with Azure Kubernetes Service, ensures that you have access to the latest AI and most advanced AI models and technologies.

Strategic Considerations for Transitioning to Intelligent Apps

When developing a strategy for transitioning to intelligent apps, you must consider costs, personnel skill development, and data management.

Monolithic architectures are unlikely to support the scale and ease of iteration required by intelligent apps, so you’ll likely need to implement code changes to support the transition. Your chosen hosting solution and the amount of data your app will handle affect cost and performance, in addition to determining privacy and compliance requirements.

An app that manages or stores terabytes or petabytes of data will require storage-optimized virtual machines or a managed database service hosting. Meanwhile, an app that processes video content or performs resource-intensive calculations should prioritize high-performance computing. Maximizing storage and performance can become extraordinarily costly.

Finally, existing applications shifting towards intelligent apps may encounter compatibility conflicts, depending on database type or the existing system’s underlying programming language. As you might imagine, a traditional app running on-premises and using a shared local database may need significant restructuring to support a cloud native architecture. You need plans for data migration, potential downtime, and training.

Next, let’s look at this transformation process.

Re-Architecting Monolithic Apps into Microservices

Shifting from monolithic applications to microservices is pivotal for transitioning to Intelligent Apps, and you can use this shift as an opportunity to transform your legacy software into intelligent apps that leverage the flexibility and power of microservices-enabled AI solutions.

Traditionally, a monolithic application (let’s use an online marketplace as an example) might handle all activities—from serving webpages to processing payments—within an on-premises server or a cloud server. However, this approach is challenging to scale and remains vulnerable to network failures or cyberattacks.

Breaking these applications into multiple services with specific responsibilities, such as authentication or notifications, can overcome these limitations. These services are typically deployed as microservices within container environments like Azure Kubernetes Service or serverless platforms like Azure Container Apps or Azure Functions using automated processes. This approach offers scalability, robustness, and improved performance, as each microservice can run independently and use its own database.

Shifting towards smaller, independent microservices in app architectures allows us to reap the benefits of AI-powered intelligent apps, for example, by using microservices to handle AI tasks such as processing natural language input, analyzing behavioral data, or personalizing content.

info

Watch Episode 1 and Episode 2 of the Learn Live series on how to build, test and deploy an end-to-end intelligent app solution using the Contoso Real Estate Sample.

Event-Driven Architectures: Knowing When to Use Them

Event-Driven Architectures (EDAs) can significantly enhance the performance and cost-effectiveness of intelligent apps. EDAs, typically implemented with microservices, respond in real time to events or state changes ranging from user interactions to real-time analytics.

Consider an intelligent app designed for personalized customer engagement. Instead of designing the application to check continuously for changes in user data, an EDA can react to such changes immediately, triggering appropriate microservices. For example, if users modify their preferences, an event could trigger an AI-driven recommendation microservice to update its suggestions immediately. This real-time responsiveness, enabled by services like Azure Event Grid, can improve user experiences and make intelligent apps more adaptive and proactive.

However, it’s worth noting that while EDAs provide significant benefits, they can introduce unique complexities, such as maintaining event consistency and debugging issues in event chains across multiple microservices. Therefore, it’s important to assess your application’s needs so you can understand where it can benefit from an EDA.

Conclusion

Transitioning to intelligent apps represents a shift in strategy, architecture, and infrastructure. By designing and re-architecting for cloud-based infrastructure, your apps gain scalability, cost-effectiveness, robust data management, access to sophisticated AI tools, enhanced security, and reduced operational overhead.  

But this transition extends well beyond its technical components. Organizations and the people working within them need equal attention. You can explore these facets in greater depth in “Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management.” 

The power of generative AI is here, and preparing for the move toward cloud-based intelligent applications will ensure that your apps remain performative and scalable and—most importantly—that your business is ready for the newest era of app innovation.

Exercise

- - +
Skip to main content

11 posts tagged with "azure-openai"

View All Tags

· 9 min read
It's 30DaysOfIA

Learn best practices for modernizing your on-premises or IaaS solutions to intelligent apps in the cloud.

What We'll Cover:

  • Traditional vs Intelligent Apps
  • On-Premises/IaaS vs. Cloud-Native Platforms
  • Modernization strategy for building Intelligent Apps

image of modernizing AI solutions for intelligent apps

Preparing the Path for Intelligent Apps: Transitioning from On-Premises/IaaS to Cloud-Native Applications

The proliferation of generative AI is paving the way for intelligent applications, software that leverages AI capabilities to deliver unparalleled functionality and user experience. As we covered in a previous article, “Demystifying Intelligent Applications: Leveraging AI in App Development,” intelligent Apps aren’t just products of advanced coding. They interact, learn, and evolve thanks to modern AI and machine learning (ML) breakthroughs. 

The paradigm shift from traditional to intelligent apps demands that we change our approach to the technical challenges of software and architecture design. It also necessitates adopting new ways of thinking and operating within an organization, which we explore in greater depth in “Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management.” 

This article explores the technical infrastructure powering intelligent apps, providing a roadmap to transition traditional on-premises or Infrastructure as a Service (IaaS) solutions to intelligent apps deployed to cloud native platforms and services. We’ll discover the differences between conventional and intelligent apps, options for re-architecting existing applications, and critical strategic considerations integral to modernizing the application-building approach. 

Understanding the Shift: Traditional vs. Intelligent Applications

Intelligent apps are more than just an incremental evolution from traditional apps. They involve a fundamental shift in software solution design, development, and deployment. A clear understanding of how each paradigm differs is crucial for comprehending the impact of this transition. 

Functionality and User Experience

Traditional applications, often rule-based and rigid, rely on preprogrammed operations. A traditional weather app, for example, typically presents structured, location-based forecasts using a predetermined data source. Meanwhile, an intelligent app leverages AI for data-driven decision-making and personalization. Rather than merely displaying the forecast, it integrates ML with additional data sources—like a user’s calendar and fitness data—to determine their favorite outdoor activities, provide relevant clothing suggestions, and even automatically design ideal vacations and times based on the user’s optimal weather preferences. 

Similarly, while traditional applications tend to offer a one-size-fits-all UI, intelligent apps use AI to create tailored interactions. For instance, an intelligent banking app might use generative AI to provide a voice-enabled UI that lets customers use natural language to ask about transactions and receive personalized financial advice.

Infrastructure

Intelligent apps integrating these sophisticated features require highly scalable, flexible infrastructure optimized for AI functionality and able to manage large volumes of real-time data. Unlike traditional applications deployed on-premises or in infrastructure as a service (IaaS) environments, intelligent apps demand the flexibility and scalability of cloud native architectures.

An on-premises or IaaS-based e-commerce application may struggle with connecting its data to AI services or scaling to accommodate voluminous real-time data and requests. But an intelligent app built with a cloud native approach and scalability at its core can handle variable demand much more efficiently. It also maintains its infrastructure components on demand, whether for collecting data for AI/ML consumption or user-facing features like AI-enabled customer support and personalization.

Consider an intelligent app built on Azure that creates ChatGPT-like experiences with custom data. It might connect Azure Open AI and Azure Cognitive Search to an app running on Azure Container Apps or Azure Kubernetes Service. 

The architecture might look like the following diagram based on this demo app:

diagram of a demo app architecture

While the cloud is well-equipped to handle the challenges associated with shifting to AI-centric infrastructure—handling real-time data processing and storage, integrating and managing AI services, configuring event-driven and microservices architectures, and ensuring security across the distributed environment—re-architecting a traditional app to an intelligent app requires careful planning and execution to ensure it benefits fully from the capabilities of AI and cloud computing.

On-Premises/IaaS vs. Cloud-Native Platforms

While on-premises and IaaS solutions provide substantial control over data and processes, this fine-grained control comes with setup and maintenance costs and may lack the scalability necessary for AI-driven Intelligent Apps. On-premises hosting involves purchasing and maintaining hardware, including servers and storage systems. IaaS involves leasing virtual hardware and then hiring and training IT personnel for the infrastructure’s operation, maintenance, and security.

Conversely, infrastructure built on cloud native app services with Microsoft Azure offers virtually unlimited scalability without ongoing hardware and maintenance costs. This feature is ideal for intelligent apps that need to adapt and scale to accommodate fluctuating workloads and data streams. And because these cloud infrastructures typically operate on a pay-as-you-go model, traditionally substantial upfront costs turn into predictable operating expenses, lowering both the cost and risk of launching applications.

Moreover, cloud service providers continually update their offerings, so building on sophisticated AI tools and services through the cloud, like the Azure AI Platform and orchestration capabilities with Azure Kubernetes Service, ensures that you have access to the latest AI and most advanced AI models and technologies.

Strategic Considerations for Transitioning to Intelligent Apps

When developing a strategy for transitioning to intelligent apps, you must consider costs, personnel skill development, and data management.

Monolithic architectures are unlikely to support the scale and ease of iteration required by intelligent apps, so you’ll likely need to implement code changes to support the transition. Your chosen hosting solution and the amount of data your app will handle affect cost and performance, in addition to determining privacy and compliance requirements.

An app that manages or stores terabytes or petabytes of data will require storage-optimized virtual machines or a managed database service hosting. Meanwhile, an app that processes video content or performs resource-intensive calculations should prioritize high-performance computing. Maximizing storage and performance can become extraordinarily costly.

Finally, existing applications shifting towards intelligent apps may encounter compatibility conflicts, depending on database type or the existing system’s underlying programming language. As you might imagine, a traditional app running on-premises and using a shared local database may need significant restructuring to support a cloud native architecture. You need plans for data migration, potential downtime, and training.

Next, let’s look at this transformation process.

Re-Architecting Monolithic Apps into Microservices

Shifting from monolithic applications to microservices is pivotal for transitioning to Intelligent Apps, and you can use this shift as an opportunity to transform your legacy software into intelligent apps that leverage the flexibility and power of microservices-enabled AI solutions.

Traditionally, a monolithic application (let’s use an online marketplace as an example) might handle all activities—from serving webpages to processing payments—within an on-premises server or a cloud server. However, this approach is challenging to scale and remains vulnerable to network failures or cyberattacks.

Breaking these applications into multiple services with specific responsibilities, such as authentication or notifications, can overcome these limitations. These services are typically deployed as microservices within container environments like Azure Kubernetes Service or serverless platforms like Azure Container Apps or Azure Functions using automated processes. This approach offers scalability, robustness, and improved performance, as each microservice can run independently and use its own database.

Shifting towards smaller, independent microservices in app architectures allows us to reap the benefits of AI-powered intelligent apps, for example, by using microservices to handle AI tasks such as processing natural language input, analyzing behavioral data, or personalizing content.

info

Watch Episode 1 and Episode 2 of the Learn Live series on how to build, test and deploy an end-to-end intelligent app solution using the Contoso Real Estate Sample.

Event-Driven Architectures: Knowing When to Use Them

Event-Driven Architectures (EDAs) can significantly enhance the performance and cost-effectiveness of intelligent apps. EDAs, typically implemented with microservices, respond in real time to events or state changes ranging from user interactions to real-time analytics.

Consider an intelligent app designed for personalized customer engagement. Instead of designing the application to check continuously for changes in user data, an EDA can react to such changes immediately, triggering appropriate microservices. For example, if users modify their preferences, an event could trigger an AI-driven recommendation microservice to update its suggestions immediately. This real-time responsiveness, enabled by services like Azure Event Grid, can improve user experiences and make intelligent apps more adaptive and proactive.

However, it’s worth noting that while EDAs provide significant benefits, they can introduce unique complexities, such as maintaining event consistency and debugging issues in event chains across multiple microservices. Therefore, it’s important to assess your application’s needs so you can understand where it can benefit from an EDA.

Conclusion

Transitioning to intelligent apps represents a shift in strategy, architecture, and infrastructure. By designing and re-architecting for cloud-based infrastructure, your apps gain scalability, cost-effectiveness, robust data management, access to sophisticated AI tools, enhanced security, and reduced operational overhead.  

But this transition extends well beyond its technical components. Organizations and the people working within them need equal attention. You can explore these facets in greater depth in “Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management.” 

The power of generative AI is here, and preparing for the move toward cloud-based intelligent applications will ensure that your apps remain performative and scalable and—most importantly—that your business is ready for the newest era of app innovation.

Exercise

+ + \ No newline at end of file diff --git a/30daysofIA/tags/azure-openai/page/3/index.html b/30daysofIA/tags/azure-openai/page/3/index.html index 4fbb06c09e..56f7cb909f 100644 --- a/30daysofIA/tags/azure-openai/page/3/index.html +++ b/30daysofIA/tags/azure-openai/page/3/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "azure-openai" | Build Intelligent Apps On Azure +11 posts tagged with "azure-openai" | Build Intelligent Apps On Azure @@ -14,13 +14,13 @@ - - + +
-

9 posts tagged with "azure-openai"

View All Tags

· 10 min read
It's 30DaysOfIA

Explore how digital natives and Independent Software Vendors (ISVs) can transform their business operations and generate higher value for customers with generative AI and intelligent apps.

What We'll Cover:

  • Potential of Generative AI in application development
  • Challenges with Generative AI
  • Integrating Generative AI with business operations

image of generative AI and intelligent apps used in business

Unleashing Business Transformation: How Digital Natives Can Leverage Generative AI

Generative AI is no longer a technological flight of fancy. It’s here, and it’s rewriting the rules of business innovation. 

This type of AI leverages machine learning algorithms to create new data with similar characteristics to a given dataset. It goes beyond text generation using generative pretrained transformer (GPT) models, covering an array of applications that span from image synthesis to translations and more. It also powers a new class of intelligent applications, which combine AI capabilities with traditional software functions for enhanced user experiences and advanced business solutions. 

Generative AI is transforming how digital natives and independent software vendors (ISVs) operate. Generative AI offers new possibilities for creating unique business value, enhancing customer experiences, and driving growth. 

It plays a critical role in applications, improving functionality, aiding decision-making, and automating complex tasks to deliver dynamic, uniquely personalized user experiences. Generative AI also enables and automates data-driven outcomes, functions, and features.

Integrating AI capabilities into customer-facing apps and turning them into intelligent apps benefits both the customer and the business, as we discussed in Demystifying Intelligent Applications: Leveraging AI in App Development. You get faster product development workflows and better business intelligence for sales and marketing, while your users get more natural and context-aware features and interactions. 

That said, to realize generative AI’s potential, you need some strategy and knowledge of how and where it fits into your current business and operations. We’ll examine this technology’s potential to transform business operations, its risks, and rewards, and how to integrate it into your existing operations. 

Unpacking the Potential of Generative AI

Generative AI is creating new opportunities in the digital playing field. It empowers businesses to generate insights, design products, and automate processes in ways and at speeds that were previously unimaginable. You can now predict customer behavior more accurately, offer highly personalized user experiences, and prototype innovative ideas rapidly. 

For example, Microsoft used generative AI to reinvent web search in the new AI-powered Bing and Edge. The search experience has evolved from a solitary user flow of typing in keywords and scrolling through results. It’s now a conversational experience where Bing uses context to offer better quality and more personalized search queries and results. 

Similarly, Microsoft harnessed generative AI to transform its productivity suite into intelligent apps. Through Copilot for Microsoft 365, PowerPoint now empowers users to tell their stories by using DALL-E to generate images effortlessly. It also helps rephrase text-heavy slides to be more straightforward. Outlook now assists users to compose more effective emails, enabling better communication.

These are just some examples of how AI powers day-to-day work and operations. 

Businesses that ignore generative AI miss out on untapped market opportunities, letting competitors gain an edge in the digital race. By seeking ways to rethink assumptions and reinvent existing approaches, you can effectively use generative AI to create new opportunities, open products to new use cases and markets, unlock new growth and customer satisfaction levels, and increase your revenue.

info

Register for the intelligent apps webinar on Driving Business Value by Modernizing with Cloud-Native & AI with Microsoft and Forrester on September 26.

Explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries. 

The Crucial Role of Generative AI in Business Operations

Generative AI can be a powerful catalyst for business transformation. Let’s quickly tour some ways it can redefine business operations. 

Product development is traditionally a long and painstaking process. Now, tools like GitHub Copilot and TestPilot use generative AI to make coding and testing software easier, faster, and more robust. 

Similarly, machine learning as a service technology, such as Azure Machine Learning, democratizes the availability of machine learning. It helps collect, clean, and train models across datasets, making engineering and analyzing data more efficient and accessible.  

Generative AI also simplifies the process of creating intelligent apps. Previously, simulating thousands of prototypes, predicting their performance, and narrowing down to the most viable options could take weeks, months, or even years of iteration and data analysis. With generative AI, this process can be as quick as days, hours, or perhaps minutes. 

Customer experience is also ripe for transformation. Personalized recommendations drive engagement and sales, and generative AI can scale that personalization to every customer interaction using comprehensive and continually updated data. For instance, Microsoft leverages AI to enable personalization in Dynamics 365 Customer Insights. Using this tool, you can ask questions about your customers in natural language and gain valuable and actionable insights into your customer base. 

Lastly, marketing and sales can benefit from generative AI. For example, identifying current and future key markets and emerging trends helps companies stay ahead of the curve. Tools like Copilot in Power BI help you quickly understand and uncover insights about your business, enabling your team to craft targeted campaigns, create engaging content, and optimize your business strategy. 

Leveraging this full generative AI potential requires the right tools. GPT-4, for instance, can be instrumental in realizing effective business transformation. It excels in understanding context, generating human-like text, and learning from minimal data input, making it a formidable tool in any digital native's arsenal. Platforms like Azure OpenAI Service bring GPT-4’s power to your fingertips.

However, as with any powerful technology, it’s unwise to treat generative AI as a perfect solution. You must know the challenges and risks of not leveraging it effectively, including technical and human-factor considerations. 

Without generative AI, your company risks missing the opportunity to please customers via personalization, as discussed previously. You’ll miss sales opportunities via business intelligence insights, limiting your ability to grow revenue and keep up with the competition. 

One challenge of leveraging generative AI’s full potential is finding the right tools. Fortunately, you can implement generative AI in your organization without investing in infrastructure, computational power, and expertise. Platforms like Azure OpenAI Service and Azure AI Services make AI easily accessible. They also work with Azure compute and data services like Azure Functions and Azure Kubernetes Service (AKS), enabling you to build intelligent apps easily and quickly. 

However, generative AI can’t do everything. It may need human expertise and intervention to use it fully. 

For example, generative AI can create unique data. However, the data may have limited diversity and complexity. The AI may need help recognizing additional considerations or requirements specific to a problem. It may also lack context due to various factors, such as the information’s freshness. Also, since it’s an emerging field that’s potentially worth $4.4 trillion, we’re still learning about the nuances and potential regulatory impact of generative AI. 

Perhaps the most crucial and challenging aspect of adopting generative AI is an organization’s required cultural shift. Your teams must learn how to use AI as your company fosters an understanding of AI’s potential and limitations. 

Begin integrating AI literacy into your company’s DNA. Knowledge of its potential and the readily available tools can inspire your teams to incorporate generative AI into their daily workflows and business operations quickly, improve productivity, and deliver superior customer experiences. 

No company wants to be outpaced by its competitors. You can balance generative AI’s potential with your business operations by staying aware of and adapting to the dynamic AI landscape.

Achieving Harmony: Integrating Generative AI with Existing Business Operations

Integrating generative AI into existing business operations doesn’t have to be disruptive. With the correct strategy, you can do so seamlessly. Your goal should be to augment and enhance your operations, not disrupt and replace. 

For example, Microsoft Cloud technologies leveraged AI to relieve healthcare organizations’ challenges, such as talent shortage and workforce burnout. Providers automated their workflows, invested in specific applications and connectors, and enhanced support for data and operations. 

Azure AI Services, such as text analytics for health, enable healthcare organizations to understand their largely unstructured and untapped medical data. Project Health Insights leans on AI to help healthcare professionals gain actionable insights and inferences from patient data. 

Azure Health Bot, an Intelligent App, empowers organizations to build and deploy legally compliant, powerful conversational healthcare experiences to patients. 

A successful AI integration like this begins with understanding. Knowing what generative AI can do and setting realistic expectations can help you identify where and how AI can best serve your operations. 

Next, you can foster a culture of AI literacy across teams, speeding up adoption by helping them understand how it can aid their work. The more familiar your team is with AI and its possibilities, the smoother the transition. Training and education programs play a pivotal role here. 

Finally, integration requires a systematic approach. It’s not about overhauling everything at once — it's about identifying areas where AI can add the most value, then gradually experimenting and scaling its use. Successful integration stories often feature a phased approach, starting small and growing steadily. 

As we move toward a future where generative AI is a staple in business operations, remember that every journey is unique. Although there’s no one-size-fits-all solution, a strategic approach and a keen understanding of your business needs will help you navigate this new landscape successfully.

Conclusion

Generative AI offers plenty of opportunities for businesses that embrace it. It’s fueling the next generation of intelligent applications, transforming everything from product development to customer engagement to marketing. This technology is the key to unlocking unprecedented growth and innovation. 

While the journey may have challenges, the potential rewards make generative AI worth exploring. As generative AI advances, Azure AI’s broad range of tools and services will help you navigate this fascinating field.

Exercise

  • Watch Episode 02 of the serverless edition Learn Live session to learn how to build, test and deploy an end-to-end intelligent app solution.
  • Complete the Cloud Skills Challenge to build on your AI skills.
  • Register for Ask the Expert: Azure Functions on September 26 for a live Q&A with the Product Team on how to build AI apps using serverless computing.
- - +
Skip to main content

11 posts tagged with "azure-openai"

View All Tags

· 12 min read
It's 30DaysOfIA

Delve into creating an Intelligent App that leverages Azure AI Vision to analyze images and extract data. Develop an API to perform optical character recognition (OCR) on uploaded images and testing this API locally.

What We'll Cover:

  • Understanding Azure AI Vision and Azure Kubernetes Service
  • Build a Python Web API to perform OCR
  • Test the API locally

image depicting an Intelligent App using AI

Jumpstart Your AI Journey: Building Your First Intelligent App with Azure AI and AKS (1)

An Intelligent App is a software application that uses artificial intelligence (AI), such as machine learning (ML), to extract meaningful insights, enabling users to make informed decisions based on the extracted text, data, sound, images, and more.

Intelligent Apps can employ advanced algorithms and models to recognize objects, patterns, and features within images, allowing them to perform tasks like object identification, image classification, facial recognition, and image segmentation.

To explore the power of Intelligent Apps, let’s build a Python app that performs optical character recognition (OCR) on images containing numbers and performs aggregate functions on the extracted numbers, like sum and average. We’ll leverage Azure AI Services for the OCR functionality and Azure Kubernetes Service (AKS) to develop, publish, and maintain a cloud-based, scalable app in Azure.

Let’s get started!

Understanding Azure AI Vision and Azure Kubernetes Service

Azure AI Vision is a cloud-based service that offers a range of advanced computer vision capabilities enabling applications to interpret content, such as images and videos, using ML and other AI algorithms. Azure computer vision lets users integrate powerful vision features into their projects without ML experience.

OCR is one of the critical functionalities within Azure vision service, allowing applications to extract text from images. Within Azure AI Vision service, OCR offers several features, including text extraction, multilingual support, layout analysis of documents, and integration with Azure services, such as Azure Functions and Azure Machine Learning.

info

Watch the intelligent apps webinar on Driving Business Value by Modernizing with Cloud-Native & AI with Microsoft and Forrester on September 26.

AKS is Microsoft Azure’s container orchestration platform that enables us to quickly and efficiently deploy, manage, and scale containerized applications, such as our Intelligent App.

Let’s explore how to leverage these tools to build an Intelligent App that uses Azure AI Vision to analyze images and extract data.

Prerequisites

To follow this tutorial, ensure you have the following:

For a preview of this final project, take a look at the complete project code.

Building the API with Azure AI Vision Service

First, log in to your Azure account and navigate to the Azure Portal.

Click Create a resource and search for “resource group.” Create a new resource group named computer-vision.

image in Azure Portal of creating a new resource group

Return to the Azure Portal home page and click Create a resource.

image in Azure Portal of creating a new resource

Search for “computer vision” and select it from the results. Click Create.

image of searching for resource group in Azure Portal

Clicking Create displays the Create Computer Vision wizard. Select the resource group you just created as the Resource group from the dropdown. Enter a resource name similar to what is seen in the screenshot below. Note that this must be a unique name so choose one that is not currently in use. Select Standard S1 as the pricing tier and check the Responsible use of AI box in the field at the bottom.

Image of selecting the Responsible use of AI box

Image of project details

Click Review + Create, then click Create.

Next, get the API key and endpoint from the Azure AI services multi-service account you created. Once the resource is created, navigate to the resource details page. Under Resource Management, select Keys and Endpoint. Once on the Keys and Endpoint page, you’ll find the Key 1 and Endpoint values. These credentials are necessary to access the Azure AI APIs.

image of selecting Keys and Endpoint

second image of selecting Keys and Endpoint

Configuring the Local Environment Variables

Next, we need to define a local environment variable file. This convenient way to store our API keys in a simple text file enables us to manage sensitive information and separate configuration from our API code.

If you haven’t done so already, clone the starter project template from GitHub into your local computer. Open the starter project template in a code editor and create a new .env file in the root folder.

Note: Root folder in this case is the “/Microsoft_Series17-18_Code/intelligent-app-before" folder.

Add the following key-value pairs to the file, remembering to replace the placeholder values with those you obtained from the previous step.

VISION_KEY=<THE-KEY-1-VALUE-FROM-YOUR-AZURE-AI-SERVICE>
VISION_ENDPOINT=<THE-ENDPOINT-VALUE-FROM-YOUR-AZURE-AI-SERVICE>

Reviewing the Quickstart Code

Let’s review the app.py file in the starter project template.

The app.py file works as the entry point for the Flask application. It is responsible for initializing the web server, listening for incoming requests, dispatching them to the appropriate route handlers, and generating responses.

Below is the code contained in the app.py file:

import os
import json

from flask import Flask, request
from flask_restful import Resource, Api
from werkzeug.utils import secure_filename

app = Flask(__name__,
static_url_path='',
static_folder='static/files')

api = Api(app)

app = Flask(__name__)

app.config['UPLOAD_FOLDER'] = 'files'

api = Api(app)

class UploadHandler(Resource):

def allowed_file(self, filename):
return '.' in filename and \
filename.rsplit('.', 1)[1].lower() in {'png'}

def post(self):
form = request.form.to_dict()

if 'file' not in request.files:
return json.dumps({ "success": False, "error": "No file part"})

file = request.files.get("file")
if file and self.allowed_file(file.filename):
filename = secure_filename(file.filename)
upload_folder = "static/files"
if not os.path.exists(upload_folder):
os.makedirs(upload_folder)
local_file_path = os.path.join(upload_folder, filename)
file.save(local_file_path)

return f"File {filename} uploaded successfully to folder: {upload_folder}"

api.add_resource(UploadHandler, "/")

if __name__ == '__main__':
app.run(debug=True)

The UploadHandler class above is defined as a Flask RESTful resource to handle file uploads. The class contains two methods:

  • The allowed_file method checks whether the file extension is allowed for upload. In this case, only .png files are allowed.

  • The post method handles HTTP POST requests for file uploads. It saves the uploaded .png file to the static/files folder.

Finally, the application returns a text response informing us that the file has been uploaded successfully.

Implementing the REST API in Python

To implement image analysis in your REST API, open the starter project template in your terminal, create a virtual environment, and activate it.

Next, install the Azure AI Vision SDK:

pip install azure-ai-vision

Then, add the following line to the requirements.txt file to include the azure-ai-vision package:

azure-ai-vision==0.13.0b1

Now, create an ocr_helper.py file in the project’s root folder. This file is a Python module that provides functions for processing images using OCR. Add the following code to the file:

import os
from statistics import median
from decimal import Decimal
import azure.ai.vision as sdk

def process_ocr(source_image):
service_options = sdk.VisionServiceOptions(os.environ["VISION_ENDPOINT"],
os.environ["VISION_KEY"])

vision_source = sdk.VisionSource(filename=source_image)

analysis_options = sdk.ImageAnalysisOptions()

analysis_options.features = (
sdk.ImageAnalysisFeature.CAPTION |
sdk.ImageAnalysisFeature.TEXT
)

analysis_options.language = "en"

analysis_options.gender_neutral_caption = True

image_analyzer = sdk.ImageAnalyzer(service_options, vision_source, analysis_options)

result = image_analyzer.analyze()

ocr_result = get_ocr_result(result)

return ocr_result

def get_ocr_result(result):
string_list = []

if result.reason != sdk.ImageAnalysisResultReason.ANALYZED:
return sdk.ImageAnalysisErrorDetails.from_result(result)
else:
if result.text is not None:
for line in result.text.lines:
for word in line.words:
string_list.append(word.content)

number_list = convert_to_decimal_list(string_list)

aggregate_result = aggregate_operations(number_list)

return {
"aggregate_result": aggregate_result,
"numbers_read": string_list
}

def convert_to_decimal_list(string_list):
return list(map(Decimal, string_list))

def aggregate_operations(numbers):
result = {
'sum': sum(numbers),
'average': sum(numbers) / len(numbers),
'median': median(numbers),
'min': min(numbers),
'max': max(numbers)
`}`
`return result`

This module uses the azure-ai-vision package to analyze images, including capturing captions and extracting text from the image. The process_ocr function inputs an image file and performs the OCR analysis. The module is a convenient tool for analyzing text-based data in pictures and performing numerical computations based on that data.

Let’s review the different components of the ocr_helper.py module listed above and explore how its functions help our Intelligent App process images and perform aggregate operations:

  • The process_ocr function takes the parameter source_image, which is the path of the image to be processed. The function then initializes the VisionServiceOptions using environment variables VISION_ENDPOINT and VISION_KEY to connect to the Azure AI Vision API.
  • The process_ocr function creates a VisionSource object with the specified source_image file name. ImageAnalysisOptions specify the features to be analyzed, including CAPTION and TEXT. The language is set to English ("en"), and gender-neutral captions are enabled.
  • Finally, an ImageAnalyzer object is created with the service options, vision source, and analysis options. The image is then analyzed using the image_analyzer.analyze method to retrieve OCR results. The recognized text from the OCR result is extracted, with numerical values stored in the string_list variable.
  • The convert_to_decimal_list function converts the list of strings to a list of decimal numbers, which helps process numeric values extracted from the text.
  • The aggregate_operations function calculates various aggregate statistics (sum, average, median, minimum, maximum) from a list of numbers and returns the results in a dictionary.

Note that you must have the appropriate credentials (VISION_KEY) and endpoint (VISION_ENDPOINT) configured for the Azure AI Vision API to use this module.

Finally, we must modify the app.py file so our code can use the process_ocr function of the ocr_helper.py file.

Add the following import statement to the app.py file:

from ocr_helper import process_ocr

Then, replace this line:

return f"File {filename} uploaded successfully to folder: {upload_folder}"

With these two lines:

aggregates = process_ocr(local_file_path)
return json.dumps(aggregates, default=str)

Doing so allows our application to perform aggregate operations (sum, average, median, minimum, maximum) of any numeric values found in the text and return the operation’s result as a JSON response.

Running the Intelligent App Locally

Our final goal is to deploy the sample app to AKS, which will run the app in a container. However, as a good practice, let’s run the app in a container locally before moving it to AKS. Doing so ensures that our application and its dependencies are encapsulated and can be run consistently across different environments.

Deploying to Docker Desktop

For our application to run on Docker, it needs two additional files: a Dockerfile and docker-compose.yml. A Dockerfile creates a single container image by specifying the steps to build it, and a docker-compose.yml file manages multiple containers as a cohesive application, defining their configurations and relationships. Dockerfile builds the image, while docker-compose.yml orchestrates multi-container applications.

In the project root folder, add a file named Dockerfile with the content below:

# syntax=docker/dockerfile:1

FROM python:3.8-slim-buster

WORKDIR /intelligentapp

COPY requirements.txt requirements.txt
RUN pip3 install -r requirements.txt

RUN pip install debugpy

COPY . .

CMD [ "python3", "-m" , "flask", "run", "--host=0.0.0.0"]

Now create a file named docker-compose.yml file in the project’s root folder with the following code, replacing the VISION_KEY and the VISION_ENDPOINT according to the environment variables you configured earlier:

version: '3.8'
services:
intelligentapp:
build:
context: .
dockerfile: Dockerfile
image: intelligent-app
ports:
- 5000:5000
container_name: intelligent-app
environment:
- VISION_KEY=<THE-KEY-1-VALUE-FROM-YOUR-AZURE-AI-SERVICE>
- VISION_ENDPOINT=<THE-ENDPOINT-VALUE-FROM-YOUR-AZURE-AI-SERVICE>

Then, run the following command in the terminal to build the image and start a container for the Intelligent App services defined in the docker-compose.yml file:

docker-compose up --build --force-recreate

Next, open a new terminal and run the command below to list the image deployed to your local Docker:

docker images

docker images

Testing the Intelligent App Locally

Now, we’ll test the Intelligent App’s functionality using the following test image:

test image to test functionality

This image is provided as sample1.png in the sample app source files, so you can easily use it in the next step.

The Intelligent App will read the image above using OCR to extract numbers in the image and perform five aggregate functions:

  • Sum
  • Average
  • Median
  • Min
  • Max

To test the API, open Postman and fill out the fields as follows:

  • URL: http://localhost:5000/
  • Method: POST
  • Body:
    • Form-data
      • Key: File — Click the right end of the Key field and select File from the dropdown list.
      • Value: Click Select Files, then select the sample1.png file provided in the sample code.

image of selecting the sample file

Now click the Send button and review the result body:

"{\"aggregate_result\": {\"sum\": \"25821\", \"average\": \"5164.2\", \"median\": \"5622\", \"min\": \"1447\", \"max\": \"9802\"}, \"numbers_read\": [\"3049\", \"5901\", \"5622\", \"1447\", \"9802\"]}"

As we can see, the app running on a local container returned the correct results based on the sample image. So, we’re ready to prepare our Azure resources to run the Intelligent App on AKS.

Exercise

In this article, we explored the creation of an Intelligent App that leverages Azure AI Vision to analyze images and extract data. We learned how to build a Python Web API to perform OCR on uploaded images and subsequently test this API locally.

Head to the next part, to deploy this API via Azure Kubernetes Service.

+ + \ No newline at end of file diff --git a/30daysofIA/tags/azure-openai/page/4/index.html b/30daysofIA/tags/azure-openai/page/4/index.html index cfeb3d6d02..f21767f030 100644 --- a/30daysofIA/tags/azure-openai/page/4/index.html +++ b/30daysofIA/tags/azure-openai/page/4/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "azure-openai" | Build Intelligent Apps On Azure +11 posts tagged with "azure-openai" | Build Intelligent Apps On Azure @@ -14,13 +14,13 @@ - - + +
-

9 posts tagged with "azure-openai"

View All Tags

· 9 min read
It's 30DaysOfIA

In the part of our series on intelligent apps, we’ll explore how AI is transforming application development, from design and architecture to building.

What We'll Cover:

  • Revolutionizing application development using AI
  • Infusing AI in application design architecture
  • AI Assisted Pair Programming
  • Future of App Development

A decorative image of application development with AI

Reimagining Application Development With AI: A New Paradigm

Artificial intelligence (AI) is impacting every industry. AI-powered development tools offer new functionalities and assist software developers throughout the development process, revolutionizing app development. From initial design and architecture to coding and debugging, AI reshapes how we approach application conception, creation, and iteration.

In part one of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” we explored the new breed of intelligent apps and how they use AI. In this article, we’ll see how AI technologies enhance developer toolkits and shape the future of app development, particularly through AI-enabled coding assistance and debugging.

The Dawn of Intelligent Apps: Revolutionizing Application Development

AI is more than just another addition to the development toolbox—it represents a revolution bringing unprecedented efficiency, innovation, and user engagement to our applications. 

What sets intelligent apps apart is how they can adapt and learn, helping us deliver unique, real-time user experiences.  

One example is an AI-driven health application that can track biometric data, predict potential health issues, provide personalized health advice, and even guide emergency services during a medical crisis. An AI-enabled language learning app that adapts to our learning speeds, recognizes our problem areas, and customizes its content to help us improve our language proficiencies is another example.  

Intelligent apps like these don’t just use data for recommendations—they can make critical decisions and provide sophisticated services that were previously impossible. 

For developers, intelligent apps mean we’re no longer just coding—we’re teaching applications how to think, adapt, and make decisions. AI is reshaping every step of the development journey, helping us to build more intuitive and user-focused products. 

Next, we’ll examine the impact of AI on application design and architecture and how AI-assisted “pair programming” changes software development.

info

Register for the FREE webinar on Intelligent Apps with Microsoft and Forrester on September 26 to explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries.

There will be a showcase of real-world use cases that demonstrate how AI can be seamlessly integrated into cloud-native environments driving tangible business value.

AI in Design and Architecture: Fueling Creativity and Efficiency

AI technologies help developers automate and streamline their processes and provide new ways to think about design.  

In traditional application development, the design and architecture phase can take significant time, effort, and expertise. For instance, adopting a monolithic architecture or a microservices-based design can significantly increase coding, testing, and debugging complexity. The app may even behave differently when hosted locally than in a deployed environment. It may take hours to track down an issue manually—time developers could spend elsewhere. 

Conversely, we can generate numerous design iterations in seconds with AI, each optimized to fulfill specific user needs and business goals. AI tools can quickly model various scenarios' impacts and software design patterns in the architectural design phase. It provides insights into performance and potential issues and suggests improvements. AI can also generate skeleton code, allowing developers to prototype different solutions or test specific components and technologies rapidly. By reviewing thousands of lines of code in seconds, AI allows us to focus on creating apps that serve users through feature development instead of searching for bugs.  

Integrating AI into the design and architecture phase can even extend our creative capabilities. By leveraging the power of generative AI—like large language models (LLMs)—and their broad knowledge and context going beyond software, we can better brainstorm apps and feature ideas. We can go beyond code and software architecture and get ideas about user experience, logo and branding, or marketing concepts. And as AI tools deliver insights and ideas we would have never considered; it encourages more innovation.

info

Watch Episode 01 of the Learn Live session to learn how to build an end-to-end intelligent app solution architecture.

The Power of AI-Assisted Pair Programming in Building Applications

AI-assisted pair programming is a collaborative coding approach where an AI system—like GitHub Copilot or TestPilot—assists developers during coding. It’s an increasingly common approach that significantly impacts developer productivity. In fact, GitHub Copilot is now behind an average of 46 percent of developers’ code and users are seeing 55 percent faster task completion on average. 

For new software developers, or those interested in learning new skills, AI-assisted pair programming are training wheels for coding. With the benefits of code snippet suggestions, developers can avoid struggling with beginner pitfalls like language syntax. Tools like ChatGPT can act as a personal, on-demand tutor—answering questions, generating code samples, and explaining complex code syntax and logic. These tools dramatically speed the learning process and help developers gain confidence in their coding abilities. 

Building applications with AI tools hastens development and provides more robust code. For example, GitHub Copilot helps us write new code accurately. It also quickly makes sense of existing code, suggests whole new lines or blocks of code, and even detects errors and proposes fixes. It’s like having a second coder working alongside us—one who never tires and is trained on billions of lines of code. 

However, it’s also important to acknowledge the limitations of AI. While generative AI-assistance technologies like GitHub Copilot are impressive, they’re still imperfect. They can sometimes produce erroneous, non-functional, or vulnerable code. They can also lack nuanced, domain-specific knowledge.  

So, although these tools are helpful, we can’t rely on them fully—these are programming aids rather than definitive solutions. AI-assisted pair programming is about augmenting the human developer’s skills, not replacing them.

Debugging and Improving Code With AI: Enhancing Application Quality

Identifying and correcting code errors is often time-consuming and challenging for developers. AI streamlines the debugging process by automatically detecting anomalies, suggesting fixes, and learning from previous bugs to prevent them from recurring. 

We can use generative AI to create unit tests from code snippets, from simple parameter checking and input validation to complex mocks of database and network services. Automated AI testing improves code coverage and robustness. It saves significant time compared to manually crafting unit tests. 

AI also revolutionizes code reviews by scanning and highlighting potential issues—like code patterns leading to a bug or a coding standards violation—and suggesting ways to simplify or refactor the code for readability and performance. This reduces human error and makes the code review process more efficient. Its benefits extend to small teams, where the availability of developers is crucial to deadlines, and large teams, where effective collaboration depends on readability and code consistency. AI further saves time by triaging issues so developers can prioritize the most mission-critical ones. 

Aside from improving code quality, AI is critical to understanding system behavior. Intelligent AI-assisted software development tools can monitor and audit system behavior, giving developers a better insight into how their code performs in different scenarios. AI can track patterns, identify inefficiencies, and provide alerts when they detect unusual behavior—enabling developers to enhance their code further. 

By predicting and identifying potential vulnerabilities, intelligent AI-assisted coding apps like Microsoft Security Copilot are pivotal in ensuring application functionality, reliability, and security. These AI tools are continually trained and upgraded with large codebases and user feedback that refines their responses. This ability to learn from past mistakes makes AI an incredibly powerful tool that will continue to expand its capabilities in the future.

info

Complete the AI Cloud Skills Challenge to build on you AI application development skills with paired programming using generative AI and GitHub Copilot.

AI and the Future of Application Development

The rapid advancements in AI and machine learning technologies point to an exciting future for application development. 

In “The Future of Applications,” software developer Mike Cann illustrates how LLMs might revolutionize application development. Cann that suggests fully-autonomous coding—AI-generated applications created entirely from prompts—may supersede traditional hand-crafted applications. Dynamically generated user interfaces could offer personalized and customizable experiences while we can design APIs for machine consumption and efficiency rather than human readability. Furthermore, we can store data in less structured, more adaptable, and flexible forms. 

AI is already making application development more accessible. With AI handling the complex, technical aspects of development, less-technical individuals can take a greater role in building applications. Consequently, AI can potentially democratize software engineering. 

Intelligent apps will continue to evolve, becoming more efficient, adaptable, and capable of learning from user interactions. Similarly, AI will inevitably become integral in every stage of app development.

Summary

AI has already created a new development paradigm—one in which machine intelligence and human creativity come together to produce innovative, efficient, and robust applications. AI already assists us at every stage of app development, from design and architecture to coding and debugging. 

Developers who embrace AI tools and integrate them into their processes can enhance their productivity, improve the quality of their applications, and expand the possibilities of their application development.  

The future of development is automated and AI-driven, and it’s here today. Getting started is easy—try an AI-powered development tool like GitHub Copilot to see how AI-assisted pair programming can enhance your productivity and code quality.

Exercise

- - +
Skip to main content

11 posts tagged with "azure-openai"

View All Tags

· 9 min read
It's 30DaysOfIA

In the last article, we created an Intelligent App that leverages Azure AI Vision to analyze images and extract data. Develop an API to perform optical character recognition (OCR) on uploaded images and testing this API locally.

In this article, we will deploy the web API to the cloud platform using Azure Kubernetes Service.

What We'll Cover:

  • Understanding Azure AI Vision and Azure Kubernetes Service
  • Build a Python Web API to perform OCR
  • Test the API locally

image depicting an Intelligent App using AI

Jumpstart Your AI Journey: Building Your First Intelligent App with Azure AI and AKS (2)

In the previous article we explored the creation of an Intelligent App that leverages Azure AI Vision to analyze images and extract data. We learned how to build a Python Web API to perform OCR on uploaded images and subsequently test this API locally.

In this article we will use Azure Kubernetes Service (AKS) to develop, publish, and maintain our app in the cloud on Azure.

Let’s get started!

Prerequisites

To follow this tutorial, ensure you have completed Jumpstart Your AI Journey: Building Your First Intelligent App with Azure AI and AKS (1).

Pushing a Container Image to Azure Container Registry (ACR)

To start, open your CLI or terminal and type the following command:

az login

Follow the instructions displayed on your browser to enter your Azure credentials.

Once authenticated, you’ll initiate a secure connection between your local environment and Azure. This process grants you access to cloud services and resources.

Next, type the command below in your terminal to set up a new Azure Container Registry (ACR) to store your container images:

az acr create --resource-group computer-vision --name <name-of-azure-container-registry> --sku Basic

Remember to replace <name-of-azure-container-registry> with your container registry name. The name must be unique within Azure and comply with these rules.

The command above creates an Azure Container Registry (ACR) in the computer-vision resource group under the Basic SKU. This ACR is your secure and private repository for storing container images within Azure.

Next, log in to the registry with the following command:

az acr login -n <name-of-azure-container-registry>

The az acr login command above lets you securely authenticate and access the specified ACR without providing their Azure credentials each time.

Now, run the following command in your terminal. It will display the endpoint URL to log in to and interact with the ACR for pushing and pulling container images.

az acr show --name <name-of-azure-container-registry> --query loginServer --output table

This returns an endpoint URL as follows:

Result
----------------------------------
<name-of-azure-container-registry>.azurecr.io

Now, run the following command to show all container images, their repository, tags, and size:

docker images
REPOSITORYTAGIMAGE IDCREATEDSIZE
intelligent-applatesta7bf9f75361716 hours ago197MB

Tags are necessary to push Docker images to a remote registry like Azure Container Registry. They also let you differentiate between different versions of the same image and upload or download the one you want.

Run the following command to tag your Docker image:

docker tag intelligent-app <name-of-azure-container-registry>.azurecr.io/intelligent-app:v1

Then, run the docker images command again to check your tagged image:

docker images
REPOSITORYTAGIMAGE IDCREATEDSIZE
intelligent-applatestc52168039265About a minute ago197MB
<name-of-azure-container-registry>.azurecr.io/intelligent-appv1c52168039265About a minute ago197MB

Now run the following command so Docker can securely upload the image to your Azure Container Registry:

docker push <name-of-azure-container-registry>.azurecr.io/intelligent-app:v1

Once we've deployed the image to the container registry, AKS can access it during deployment.

info

Watch Episode 03 for the Learn Live session to experience a guided session on how to build, test and deploy an end-to-end intelligent app solution.

Deploying the Intelligent App on Azure Kubernetes Service (AKS)

Before we deploy our Intelligent App to AKS, we need to provision an AKS cluster and define Kubernetes manifests.

To provision an AKS cluster to host our application, we specify the desired configurations for the cluster, such as the number of nodes, node size, and networking options. But first, download and install the Kubernetes command-line tools (kubectl), a client credential plugin implementing Azure authentication:

az aks install-cli

If you’re using Linux, review this tutorial. Then run the following:

sudo az aks install-cli

Next, run the following command in your terminal to enable access to the networking-related resources and services provided by the `Microsoft.Network`` namespace in Azure:

az provider register --namespace Microsoft.Network

Now, we must create an AKS cluster. Run the following command to create an AKS cluster named aks-intelligent-app in the computer-vision resource group.

az aks create --resource-group computer-vision --name aks-intelligent-app --node-count 1 --generate-ssh-keys

The command above specifies the target resource group: computer-vision. The node pool is configured with one virtual machine (VM), and the secure shell (SSH) keys for secure node access are generated automatically.

Next, run the following command to update the AKS cluster you created by attaching it to your ACR. Doing so allows the AKS cluster to pull container images from the specified ACR when deploying workloads to the cluster.

az aks update -n aks-intelligent-app -g computer-vision --attach-acr <name-of-azure-container-registry>

Then, run the command below to configure kubectl to work with our AKS cluster in the computer-vision resource group.

az aks get-credentials --resource-group computer-vision --name aks-intelligent-app

The command above retrieves the necessary credentials and context information required for kubectl to communicate with the AKS cluster.

We still have to define the Kubernetes manifests written in YAML, describing the desired state of our application deployment, including containers, networking, and scaling rules. We’ll prepare these manifests, including Deployment and Service configurations, to define how our application should be deployed and exposed.

Create a folder named Deployment in the project root directory. Next, create two files in the deployment folder: deployment.yml and service.yml.

Add the following configuration to the deployment.yml file, replacing the <name-of-azure-container-registry> placeholder with your registry’s name:

apiVersion: apps/v1
kind: Deployment
metadata:
name: intelligent-app
spec:
replicas: 1
selector:
matchLabels:
app: intelligent-app
template:
metadata:
labels:
app: intelligent-app
spec:
nodeSelector:
kubernetes.io/os: linux
containers:
- name: intelligent-app
image: <name-of-azure-container-registry>.azurecr.io/intelligent-app:v1
resources:
limits:
memory: 512Mi
cpu: "1"
requests:
memory: 256Mi
cpu: "0.2"
ports:
- containerPort: 5000
env:
- name: FLASK_DEBUG
value: "1"
- name: VISION_KEY
value: <THE-KEY-1-VALUE-FROM-YOUR-AZURE-AI-SERVICE>
- name: VISION_ENDPOINT
value: <THE-ENDPOINT-VALUE-FROM-YOUR-AZURE-AI>

Additionally, edit the VISION_KEY and VISION_ENDPOINT environment variables above according to the API key and endpoint of your Azure AI Services instance.

Then, add the following configuration to the service.yml file:

apiVersion: v1
kind: Service
metadata:
name: intelligent-app-service
spec:
type: LoadBalancer
ports:
- protocol: TCP
port: 80
targetPort: 5000
name: port5000
selector:
app: intelligent-app

Now, we’ll deploy our application using kubectl. Applying the Kubernetes manifests creates the necessary resources and deploys our containerized application to the AKS cluster.

First, change the terminal to the deployment folder:

cd Deployment 

Then, run the following command to create or update Kubernetes resources defined in the deployment.yml file:

kubectl apply -f deployment.yml

Create a Kubernetes Service resource in a Kubernetes cluster based on the configuration defined in the service.yml file using the code below:

kubectl apply -f service.yml

Once you’ve applied the resource definition and the service configuration contained in the deployment.yml and the service.yml files, open the aks-intelligent-app Kubernetes Service in the Azure Portal, select Workloads under Kubernetes resources on the sidebar, and find the deployment named intelligent-app. It must have the status “Ready 1/1”. If you encounter an issue with this status, check out these troubleshooting resources.

image of selecting the deployment in Azure Portal

Testing the Intelligent App on AKS

To test the app on AKS, first, run the command below:

kubectl get services

This command lists the Services and their corresponding details, including the Service name, cluster IP address, external IP, and ports.

NAMETYPECLUSTER-IPEXTERNAL-IPPORT(S)AGE
intelligent-app-serviceLoadBalancer10.0.77.6020.121.76.15380:30936/TCP47s
kubernetesClusterIP10.0.0.1<none>443/TCP14m

The output above shows a Kubernetes Service named intelligent-app-service with a type set to LoadBalancer. It’s reachable from within the cluster using the cluster IP 10.0.77.60 and accessible externally via the external IP 20.121.76.153 on port 80 (mapped to port 30936).

Note: Your set of IP addresses will differ. Remember to use your unique external IP address when testing with Postman.

To test the deployed app, go to Postman, replace the URL with the external IP of the Kubernetes Service you just deployed, and click Send:

mage of sending the app in Postman

As we can see, our Intelligent App has successfully deployed to AKS and is functioning on the cloud as expected.

Exercise

Next Steps

In this two-part article, we explored the creation of an Intelligent App that leverages Azure AI Vision to analyze images and extract data. We learned how to build a Python Web API to perform OCR on uploaded images and subsequently deploy this API via Azure Kubernetes Service.

Besides OCR and Image Analysis, you can continue exploring Azure’s vast array of services and experiment further with Azure AI and AKS by applying various practical uses to your Intelligent Apps, including natural language processing, speech recognition and synthesis, sentiment analysis for customer feedback, and automated content moderation.

+ + \ No newline at end of file diff --git a/30daysofIA/tags/azure-openai/page/5/index.html b/30daysofIA/tags/azure-openai/page/5/index.html index 663ee0f2f8..455bf10e79 100644 --- a/30daysofIA/tags/azure-openai/page/5/index.html +++ b/30daysofIA/tags/azure-openai/page/5/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "azure-openai" | Build Intelligent Apps On Azure +11 posts tagged with "azure-openai" | Build Intelligent Apps On Azure @@ -14,13 +14,13 @@ - - + +
-

9 posts tagged with "azure-openai"

View All Tags

· 10 min read
It's 30DaysOfIA

Explore real-world examples of how Intelligent Apps revolutionize logistics, retail, finance, manufacturing, tech, and medicine.

What We'll Cover:

  • Real-world businesses revolutionizing operations with Intelligent Apps
  • High level architecture for industry scenarios
  • Implementation architecture overview across industries

A decorative header image for the blog

Real-world Success Stories

According to IBM’s 2022 Global AI Adoption Index, 35% of surveyed companies already use AI in their business, and 42% are exploring the idea. Organizations lagging behind the AI curve will quickly fall behind early adopters, who are already enjoying its benefits and boosting their investment in this technology.

In the first part of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” we explored how organizations can use AI-powered apps, or intelligent apps, to transform their operations, decision-making, and customer experiences, leaping ahead of the competition.

Intelligent apps significantly improve operational efficiency and customer engagement across industries like logistics, retail, finance, manufacturing, tech, and healthcare. In this article, we’ll spotlight real-world success stories of businesses revolutionizing operations with intelligent apps using Microsoft Azure’s app, data, and AI services.

info

Register for the Learn Live on September 21 for a live guided experience with SMEs on how to build end-to-end solution architecture for real world implementation of Intelligent Apps.

Streamlining Operations: Intelligent Apps in the Airline Industry

Aerospace pioneer Airbus leveraged intelligent apps to streamline its operations and innovate its services with two ground-breaking modernizations that use Azure apps, data, and AI services to reimagine pilot training and predict aircraft maintenance issues. 

Aviation technology’s rapid evolution compelled Airbus to reconceive their approach to training pilots. The aircraft manufacturer created a pilot training chatbot by combining Azure AI Services with Speech to Text and Text to Speech. The bot helps trainee pilots navigate over 6,000 pages of technical information. Pilots can ask questions verbally for quick answers, aiding their understanding of dense information and speeding up training. And when Airbus needed to accommodate its military consumers, deploying its Kubernetes cluster to a disconnected, “air-gapped” environment met the strict security requirements of military aircraft training.

A chatbot could look like the following diagram as Azure services work together to create a conversational experience. Its components could include security and governance, bot logic and user experience (UX), bot cognition and intelligence, data extract, transfer, and load (ETL) operations, quality assurance and enhancements, logging, and monitoring and reporting.

A diagram of a conversational experience enabled by Azure services

Airbus also used AI to improve its aircraft maintenance and safety. They deployed AI Anomaly Detector to gather and analyze telemetry data from multiple flights, enabling more profound insights into wear and tear on military aircraft operating in harsh conditions. This AI application helps Airbus better understand each airplane’s health. Airbus can predict and fix potential problems before they occur, improving the aircraft’s safety and operational readiness.

AI and Azure are changing how organizations like Airbus operate, even in strict, highly regulated markets with stringent security requirements. And by streamlining pilot training and staying ahead of aircraft maintenance, Airbus helps its military and commercial clients reliably deliver their goods across the globe, maintaining their critical supply chains.

Boosting Customer Service: Intelligent Apps in the Retail Industry

The retail industry has been among the first to adopt and benefit from intelligent apps. For example, the leading American used car retailer, CarMax, uses intelligent apps to transform the car shopping experience.

Before embracing AI, CarMax faced a significant challenge: Manually creating text summaries was time-consuming and labor-intensive. CarMax decided to leverage Azure OpenAI Service to automatically generate car research pages, offering customers valuable insights while enhancing the website’s search engine rankings.

This AI-driven approach has significantly streamlined the process, achieving in a few months what would have taken years manually. Also, the AI system condenses thousands of customer reviews into a few readable sentences, offering potential buyers a quick overview of others’ experiences.

CarMax has improved their customer experience while boosting operational efficiency through AI adoption. CarMax’s editorial staff focuses on creating strategic, longer-form content with the time saved, enhancing productivity, and driving more website traffic.

CarMax’s success story serves as a testament to the potential of intelligent apps in the retail industry.

Enhancing Cybersecurity: Intelligent Apps in Finance

Intelligent apps also impact the highly regulated financial services sector. Swift, a leading infrastructure provider for financial messaging services, uses intelligent apps to enhance decision-making and combat financial crime.

The massive growth of cross-border transactions and instant payment networks led to a surge in financial fraud that can cost the industry hundreds of billions of dollars every year. So, Swift and Microsoft partnered to build an anomaly detection model using federated learning techniques with Azure Machine Learning and Azure confidential computing.

Microsoft Purview helped govern data, as the federated learning method partially trained each model in a silo before aggregating them. This approach enabled Swift and Microsoft to develop a highly accurate model without copying or moving data from Swift members’ secure locations, ensuring the highest level of security and privacy.

Financial organizations can now deploy this trained ML model to run a real-time fraud detection system, lowering the cost of financial fraud.

Organizations can deploy a fraud detection system like the following example on Microsoft Azure. The system ingests data from streams or storage, runs analytics, and uses ML to detect fraud and store results. It then alerts users and other systems to the security issue.

A diagram of an example fraud detection system

Swift provides the innovative model to its banking partners, who can share data to further train the model. Azure confidential computing securely runs new models on current data to accumulate insights, generating an ongoing learning cycle. This iterative approach strengthens the global detection of fraudulent financial transactions through an increasingly faster and more accurate model. 

The collaboration between Swift and Microsoft demonstrates how combining various Azure services creates Intelligent Apps to enhance decision-making in finance. These apps solve real problems at an unprecedented global scale. 

Enhancing Efficiency: Intelligent Apps in Manufacturing

3M, known for Post-it Notes and other innovative products, wielded the power of Intelligent Apps to standardize, automate, and accelerate its sales forecasting. The company needed a unified, automated approach to replace its multiple manual methods.

It adopted Azure Machine Learning and other Azure AI Services to develop, train, and implement 1,500 custom models for each region and division, replacing manual methods. This solution delivers timely finance, sales, and marketing insights while freeing teams from time-consuming manual data manipulation tasks.

3M used Azure Machine Learning, automated machine learning (AutoML), and the Many Models Solution Accelerator to train and score numerous machine learning models in parallel. This approach significantly shortened the company’s development cycle time.

Then, 3M integrated Microsoft Power BI into their solution to compile and visualize results more effectively. Executives now have an unbiased and unfiltered view of data for better decision-making. With its improved ability to forecast sales, 3M can focus on manufacturing the products it needs efficiently.

3M’s success demonstrates how combining various Azure services can enhance efficiency in the manufacturing industry. These apps improve operational processes while enabling employees to focus on higher-level tasks. Strategic decisions are more targeted, and business conversations are more meaningful.

Driving Innovation: Intelligent Apps in the Technology Industry

As expected, the tech industry is at the forefront of leveraging Intelligent Apps to drive innovation. Elastic saw an opportunity to use intelligent apps to optimize its search services and meet customer needs more effectively. The company built a managed, secure, and scalable solution for its customers, Elastic Cloud, by leveraging Microsoft Azure.

Elastic Cloud uses Azure Machine Learning and other Azure AI Services to automate many basic management tasks, such as backing up data, upgrading, and scaling deployments. This approach enables Elastic’s customers to focus less on managing the software and more on serving their own clients. Customers can quickly deploy and manage Elastic Cloud on Kubernetes (ECK) using Azure Kubernetes Service.

As a result, Elastic’s cloud business is growing twice as fast as its on-premises solutions. Enterprise search users have reported significant improvements in system availability and employee satisfaction.

Additionally, Elastic incorporated Azure OpenAI Service into its Elasticsearch Relevance Engine (ESRE), providing more relevant answers to search queries. ESRE enables text, vector, and hybrid search, offering a more effective and accurate response to queries.

Elastic’s success story demonstrates how combining various Azure services can create intelligent Apps to drive innovation in the tech industry. These apps help improve operational processes and deliver better results quickly.

Revolutionizing Healthcare: Intelligent Apps in Medicine

Intelligent apps also drive transformative changes in the healthcare sector. Cambridgeshire and Peterborough NHS Foundation Trust, a healthcare provider in the United Kingdom, gradually shifted their on-premises systems to the cloud using Azure, improving flexibility, and lowering costs. They needed a solution for their electronic patient record (EPR) system that integrates 23 years of data across multiple systems and formats.

The Trust used Microsoft Azure and Azure Cognitive Search, including semantic search for understanding intent and contexts and cognitive capabilities for extracting insights from data, to standardize and automate data management. This approach made legacy, archive, and live data more discoverable, saving clinicians’ time. The Trust’s solution searches through millions of clinical documents in seconds, even recognizing handwritten notes without needing a separate optical character recognition (OCR) solution to extract text from images (Azure Cognitive Search automatically does it). Clinicians can now uncover previously inaccessible insights, enabling them to offer more personalized care.

The Trust also used Integration Services tools such as Azure Logic Apps to create workflows without writing code and Event Grid to connect Azure and third-party services through a publisher-subscriber model. They switched on a live feed from their new EPR system to Azure to feed real-time information into other applications. Researchers can now access anonymized data to fuel medical innovations beyond the NHS.

The healthcare provider also invested heavily in Power BI’s data visualization and analytical functions to sift through massive amounts of information. This level of data accessibility was previously impossible with their on-premises system. The new insights have significantly improved clinicians’ efficiency for better patient care.

Data accessibility and security are critical in the healthcare industry. Intelligent Apps empower organizations like the Trust to revolutionize medical care. AI capabilities helped the Trust improve its operational efficiency and user experiences, setting a new standard for healthcare in the digital age.

Summary

AI is driving transformation and real-world successes across industries. Intelligent apps are reshaping our world by streamlining operations in manufacturing and logistics, enhancing customer experience in the retail sector, improving decision-making in finance, driving innovation in tech, and revolutionizing healthcare.

As organizations adopt AI, they’re innovating in ways that were once too difficult, expensive, or outright impossible. Companies that wait to embrace intelligent apps risk the chance of falling behind. By embracing intelligent apps and leveraging the capabilities of Azure, you can harness the transformative potential of AI to remain on the cutting edge of technology.

Exercise

- - +
Skip to main content

11 posts tagged with "azure-openai"

View All Tags

· 10 min read
It's 30DaysOfIA

Explore how digital natives and Independent Software Vendors (ISVs) can transform their business operations and generate higher value for customers with generative AI and intelligent apps.

What We'll Cover:

  • Potential of Generative AI in application development
  • Challenges with Generative AI
  • Integrating Generative AI with business operations

image of generative AI and intelligent apps used in business

Unleashing Business Transformation: How Digital Natives Can Leverage Generative AI

Generative AI is no longer a technological flight of fancy. It’s here, and it’s rewriting the rules of business innovation. 

This type of AI leverages machine learning algorithms to create new data with similar characteristics to a given dataset. It goes beyond text generation using generative pretrained transformer (GPT) models, covering an array of applications that span from image synthesis to translations and more. It also powers a new class of intelligent applications, which combine AI capabilities with traditional software functions for enhanced user experiences and advanced business solutions. 

Generative AI is transforming how digital natives and independent software vendors (ISVs) operate. Generative AI offers new possibilities for creating unique business value, enhancing customer experiences, and driving growth. 

It plays a critical role in applications, improving functionality, aiding decision-making, and automating complex tasks to deliver dynamic, uniquely personalized user experiences. Generative AI also enables and automates data-driven outcomes, functions, and features.

Integrating AI capabilities into customer-facing apps and turning them into intelligent apps benefits both the customer and the business, as we discussed in Demystifying Intelligent Applications: Leveraging AI in App Development. You get faster product development workflows and better business intelligence for sales and marketing, while your users get more natural and context-aware features and interactions. 

That said, to realize generative AI’s potential, you need some strategy and knowledge of how and where it fits into your current business and operations. We’ll examine this technology’s potential to transform business operations, its risks, and rewards, and how to integrate it into your existing operations. 

Unpacking the Potential of Generative AI

Generative AI is creating new opportunities in the digital playing field. It empowers businesses to generate insights, design products, and automate processes in ways and at speeds that were previously unimaginable. You can now predict customer behavior more accurately, offer highly personalized user experiences, and prototype innovative ideas rapidly. 

For example, Microsoft used generative AI to reinvent web search in the new AI-powered Bing and Edge. The search experience has evolved from a solitary user flow of typing in keywords and scrolling through results. It’s now a conversational experience where Bing uses context to offer better quality and more personalized search queries and results. 

Similarly, Microsoft harnessed generative AI to transform its productivity suite into intelligent apps. Through Copilot for Microsoft 365, PowerPoint now empowers users to tell their stories by using DALL-E to generate images effortlessly. It also helps rephrase text-heavy slides to be more straightforward. Outlook now assists users to compose more effective emails, enabling better communication.

These are just some examples of how AI powers day-to-day work and operations. 

Businesses that ignore generative AI miss out on untapped market opportunities, letting competitors gain an edge in the digital race. By seeking ways to rethink assumptions and reinvent existing approaches, you can effectively use generative AI to create new opportunities, open products to new use cases and markets, unlock new growth and customer satisfaction levels, and increase your revenue.

info

Register for the intelligent apps webinar on Driving Business Value by Modernizing with Cloud-Native & AI with Microsoft and Forrester on September 26.

Explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries. 

The Crucial Role of Generative AI in Business Operations

Generative AI can be a powerful catalyst for business transformation. Let’s quickly tour some ways it can redefine business operations. 

Product development is traditionally a long and painstaking process. Now, tools like GitHub Copilot and TestPilot use generative AI to make coding and testing software easier, faster, and more robust. 

Similarly, machine learning as a service technology, such as Azure Machine Learning, democratizes the availability of machine learning. It helps collect, clean, and train models across datasets, making engineering and analyzing data more efficient and accessible.  

Generative AI also simplifies the process of creating intelligent apps. Previously, simulating thousands of prototypes, predicting their performance, and narrowing down to the most viable options could take weeks, months, or even years of iteration and data analysis. With generative AI, this process can be as quick as days, hours, or perhaps minutes. 

Customer experience is also ripe for transformation. Personalized recommendations drive engagement and sales, and generative AI can scale that personalization to every customer interaction using comprehensive and continually updated data. For instance, Microsoft leverages AI to enable personalization in Dynamics 365 Customer Insights. Using this tool, you can ask questions about your customers in natural language and gain valuable and actionable insights into your customer base. 

Lastly, marketing and sales can benefit from generative AI. For example, identifying current and future key markets and emerging trends helps companies stay ahead of the curve. Tools like Copilot in Power BI help you quickly understand and uncover insights about your business, enabling your team to craft targeted campaigns, create engaging content, and optimize your business strategy. 

Leveraging this full generative AI potential requires the right tools. GPT-4, for instance, can be instrumental in realizing effective business transformation. It excels in understanding context, generating human-like text, and learning from minimal data input, making it a formidable tool in any digital native's arsenal. Platforms like Azure OpenAI Service bring GPT-4’s power to your fingertips.

However, as with any powerful technology, it’s unwise to treat generative AI as a perfect solution. You must know the challenges and risks of not leveraging it effectively, including technical and human-factor considerations. 

Without generative AI, your company risks missing the opportunity to please customers via personalization, as discussed previously. You’ll miss sales opportunities via business intelligence insights, limiting your ability to grow revenue and keep up with the competition. 

One challenge of leveraging generative AI’s full potential is finding the right tools. Fortunately, you can implement generative AI in your organization without investing in infrastructure, computational power, and expertise. Platforms like Azure OpenAI Service and Azure AI Services make AI easily accessible. They also work with Azure compute and data services like Azure Functions and Azure Kubernetes Service (AKS), enabling you to build intelligent apps easily and quickly. 

However, generative AI can’t do everything. It may need human expertise and intervention to use it fully. 

For example, generative AI can create unique data. However, the data may have limited diversity and complexity. The AI may need help recognizing additional considerations or requirements specific to a problem. It may also lack context due to various factors, such as the information’s freshness. Also, since it’s an emerging field that’s potentially worth $4.4 trillion, we’re still learning about the nuances and potential regulatory impact of generative AI. 

Perhaps the most crucial and challenging aspect of adopting generative AI is an organization’s required cultural shift. Your teams must learn how to use AI as your company fosters an understanding of AI’s potential and limitations. 

Begin integrating AI literacy into your company’s DNA. Knowledge of its potential and the readily available tools can inspire your teams to incorporate generative AI into their daily workflows and business operations quickly, improve productivity, and deliver superior customer experiences. 

No company wants to be outpaced by its competitors. You can balance generative AI’s potential with your business operations by staying aware of and adapting to the dynamic AI landscape.

Achieving Harmony: Integrating Generative AI with Existing Business Operations

Integrating generative AI into existing business operations doesn’t have to be disruptive. With the correct strategy, you can do so seamlessly. Your goal should be to augment and enhance your operations, not disrupt and replace. 

For example, Microsoft Cloud technologies leveraged AI to relieve healthcare organizations’ challenges, such as talent shortage and workforce burnout. Providers automated their workflows, invested in specific applications and connectors, and enhanced support for data and operations. 

Azure AI Services, such as text analytics for health, enable healthcare organizations to understand their largely unstructured and untapped medical data. Project Health Insights leans on AI to help healthcare professionals gain actionable insights and inferences from patient data. 

Azure Health Bot, an Intelligent App, empowers organizations to build and deploy legally compliant, powerful conversational healthcare experiences to patients. 

A successful AI integration like this begins with understanding. Knowing what generative AI can do and setting realistic expectations can help you identify where and how AI can best serve your operations. 

Next, you can foster a culture of AI literacy across teams, speeding up adoption by helping them understand how it can aid their work. The more familiar your team is with AI and its possibilities, the smoother the transition. Training and education programs play a pivotal role here. 

Finally, integration requires a systematic approach. It’s not about overhauling everything at once — it's about identifying areas where AI can add the most value, then gradually experimenting and scaling its use. Successful integration stories often feature a phased approach, starting small and growing steadily. 

As we move toward a future where generative AI is a staple in business operations, remember that every journey is unique. Although there’s no one-size-fits-all solution, a strategic approach and a keen understanding of your business needs will help you navigate this new landscape successfully.

Conclusion

Generative AI offers plenty of opportunities for businesses that embrace it. It’s fueling the next generation of intelligent applications, transforming everything from product development to customer engagement to marketing. This technology is the key to unlocking unprecedented growth and innovation. 

While the journey may have challenges, the potential rewards make generative AI worth exploring. As generative AI advances, Azure AI’s broad range of tools and services will help you navigate this fascinating field.

Exercise

  • Watch Episode 02 of the serverless edition Learn Live session to learn how to build, test and deploy an end-to-end intelligent app solution.
  • Complete the Cloud Skills Challenge to build on your AI skills.
  • Register for Ask the Expert: Azure Functions on September 26 for a live Q&A with the Product Team on how to build AI apps using serverless computing.
+ + \ No newline at end of file diff --git a/30daysofIA/tags/azure-openai/page/6/index.html b/30daysofIA/tags/azure-openai/page/6/index.html index 8b66651fb2..793f831cca 100644 --- a/30daysofIA/tags/azure-openai/page/6/index.html +++ b/30daysofIA/tags/azure-openai/page/6/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "azure-openai" | Build Intelligent Apps On Azure +11 posts tagged with "azure-openai" | Build Intelligent Apps On Azure @@ -14,13 +14,13 @@ - - + +
-

9 posts tagged with "azure-openai"

View All Tags

· 8 min read
It's 30DaysOfIA

Let’s start with understanding the power of intelligent applications.

What We'll Cover:

  • What are intelligent applications?
  • Categories of Intelligent Apps
  • Breaking down the role of AI in intelligent apps

A decorative header image for the blog

Demystifying Intelligent Applications: Leveraging AI in App Development

Until recent years, when you heard the term “artificial intelligence” (AI), you might have pictured autonomous robots from sci-fi films. Now, perhaps your mind jumps to big data and machine learning (ML). Maybe you’re even thinking about the latest ChatGPT plugins and transformer architectures. Today, AI is more than these imaginations: it’s a reality increasingly woven into our day-to-day routines through intelligent applications.

Intelligent apps combine cloud-scale compute, data, and predictive or generative AI. These applications help you automate tasks and make data-driven decisions, aiding in leveraging business intelligence to gain meaningful, practical outcomes. 

For developers, the AI landscape isn’t an abstract complexity. It’s a real, tangible opportunity for evolving traditional applications, making them “intelligent,” and delivering more value to users. And with tools like Azure Kubernetes Service, Azure Cosmos DB, and Azure AI Services, stepping into the world of intelligent apps isn’t a steep climb but a steady trek across a manageable learning curve.

The Three Categories of Intelligent Applications

Before we start building, it’s critical to understand what makes an app “intelligent.” It’s not a universal definition so much as an identifier for apps that fall into one or more of the three primary categories: 

  • Outcome-based — This designation represents the broadest category of intelligent apps, whose principal value for users is the intelligent outcomes they provide. These apps process data to provide insightful results that enhance decision-making or help with tasks like trend prediction, resource management, and customer relationship management.

  • Functionality-based — This type of intelligent app takes AI integration a degree further, integrating AI as a function of the app to create or process its outputs, enhancing the app’s overall interactivity and usefulness.

  • Feature-based — This is the narrowest category, in which the app’s AI/ML component is one of its core features and primary selling points

Let’s dive more deeply into each category.

Outcome-Based Apps: Intelligent Outcomes Drive Success

Outcome-based applications are all about the results provided to the user. They function beyond merely processing data to offer insightful outcomes that drive success.

Think of a personal fitness tracker app that uses AI to analyze the user’s activity and provides actionable recommendations for achieving a goal. This type of app not only tracks your steps, sleep patterns, and heart rate but uses that data to provide you with personalized health advice. It helps you make informed lifestyle choices — the “intelligent” outcome.

Another example is a social media marketing app that uses AI for time-saving data analysis, task automation, and trend prediction. AI-based sentiment analysis can gauge customer perception while natural language processing (NLP) accurately interprets customer inquiries. Task automation can schedule posts and automate responses to common customer queries, and trend forecasting helps model and predict customer behavior to further personalize targeted promotional efforts. Building this type of intelligence into your apps provides cost-effective, actionable insights that guide sales and marketing strategies.

Functionality-Based Apps: AI-Driven Features Enhance User Experiences

As the name suggests, functionality-based applications integrate AI into how the app functions, but the end user drives the action and outcome. In this category of apps, AI takes a more active role in the user’s interaction by allowing them to use AI-created or processed output. These apps leverage AI technologies like Azure AI Services for NLP, image recognition, and pattern recognition to improve user experience.

Consider a music app that generates personalized playlists based on your listening habits or your smartphone’s camera app that uses AI to recognize faces and optimize photo settings. Even more advanced are the language translation apps that translate text from one language to another and can even understand and maintain the context and subtleties of the conversation. They use NLP to accomplish these feats.

Similarly, consider the latest e-commerce apps, whose AI-driven functionalities enable customers to try on clothes virtually, significantly enhancing the online shopping experience.

Feature-Based Apps: Integrating Advanced AI/ML Components

Finally, there are feature-based intelligent apps. These apps incorporate advanced AI/ML components like neural networks, internal large language models (LLMs), and sophisticated algorithms as core features for the user. In this type of application, AI is the primary feature for users to engage with and the primary selling point for the app.

Some examples of feature-based applications are chatbots and virtual agents built using Azure Bot Service. These robo-representatives use AI to provide more natural and human-like conversation, balancing superior efficiency with a friendlier, more personalized user experience. The new Bing is a prime example of this implementation, integrating AI to give users a knowledgeable copilot for web searches.

Similarly, OpenAI’s ChatGPT allows users access to its state-of-the-art language models available as Microsoft’s Azure OpenAI Service. It leverages the power of the highly sophisticated GPT-3 LLM to perform tasks like drafting emails, writing code, answering general knowledge questions, translating between languages, and tutoring in numerous subjects.

These apps don’t just use AI to provide outputs or improve user experiences: They push the boundaries of what’s possible in app development by integrating AI/ML components.

info

Register for the live Q&A Ask The Expert session on September 26 with the Azure Functions product engineering team.

Breaking Down the Role of AI in Intelligent Applications

Intelligent apps reflect the merging of traditional software design with the dynamic capabilities of AI. Let’s explore how AI integrates and amplifies these applications.

At its core, AI augments applications with the capacity to self-learn and self-improve. It allows you to create applications that predict, react, and adapt to a constantly changing environment. In the power grid management system for a major city, an AI-infused application can forecast power usage trends, react to anomalies, and adapt to sudden demand surges, ensuring optimal efficiency and preventing blackouts.

But AI’s role doesn’t end at prediction or decision-making: It also automates tasks. In enterprise solutions, it has moved beyond simple automation to encompass intelligent process automation (IPA). In this arena, AI algorithms understand, learn, and then automate entire digital workflows. For developers, this is a fertile ground for creating applications that can dramatically improve the operational efficiency of businesses.

Even more, AI substantially aids in development tasks. For example, there’s GitHub Copilot, an AI-powered code assistant that offers suggestions based not only on syntax but on development best practices and the intent of the code. AI has evolved from a tool you use to build apps to a partner that helps in creating them. 

Using AI to build intelligent apps isn’t about throwing out what’s worked before. It’s about enhancing the components that have the most impact and achieve the best results. Incorporating AI in apps elevates their predictive abilities, streamlines complex tasks, and even aids in the development and creation process. When you build intelligent apps, you don’t just address current problems. You anticipate future challenges and craft solutions that adapt over time.

Summary

As developers the power of AI is in our hands, and it’s our responsibility to harness its full potential. Each category tells the story of AI's transformative potential, from applications that deliver intelligent outcomes to those with AI capabilities to enrich the user experience and those pushing boundaries with advanced AI/ML technology.

Accessible and robust platforms like Azure AI can simplify the process of developing Intelligent Applications. Whether you’re refining an existing app or building a new one, just entering the world of AI, or already familiar with the landscape, take a moment to appreciate the potential that AI integration offers. You’re embracing this exciting frontier and shaping the future of app development. 

Exercise

  • Complete the Cloud Skills Challenge to build on your apps and AI skills.
  • Watch Episode 01 of the Serverless Edition Learn Live session to learn how to build an end-to-end intelligent app solution.
- - +
Skip to main content

11 posts tagged with "azure-openai"

View All Tags

· 9 min read
It's 30DaysOfIA

In the part of our series on intelligent apps, we’ll explore how AI is transforming application development, from design and architecture to building.

What We'll Cover:

  • Revolutionizing application development using AI
  • Infusing AI in application design architecture
  • AI Assisted Pair Programming
  • Future of App Development

A decorative image of application development with AI

Reimagining Application Development With AI: A New Paradigm

Artificial intelligence (AI) is impacting every industry. AI-powered development tools offer new functionalities and assist software developers throughout the development process, revolutionizing app development. From initial design and architecture to coding and debugging, AI reshapes how we approach application conception, creation, and iteration.

In part one of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” we explored the new breed of intelligent apps and how they use AI. In this article, we’ll see how AI technologies enhance developer toolkits and shape the future of app development, particularly through AI-enabled coding assistance and debugging.

The Dawn of Intelligent Apps: Revolutionizing Application Development

AI is more than just another addition to the development toolbox—it represents a revolution bringing unprecedented efficiency, innovation, and user engagement to our applications. 

What sets intelligent apps apart is how they can adapt and learn, helping us deliver unique, real-time user experiences.  

One example is an AI-driven health application that can track biometric data, predict potential health issues, provide personalized health advice, and even guide emergency services during a medical crisis. An AI-enabled language learning app that adapts to our learning speeds, recognizes our problem areas, and customizes its content to help us improve our language proficiencies is another example.  

Intelligent apps like these don’t just use data for recommendations—they can make critical decisions and provide sophisticated services that were previously impossible. 

For developers, intelligent apps mean we’re no longer just coding—we’re teaching applications how to think, adapt, and make decisions. AI is reshaping every step of the development journey, helping us to build more intuitive and user-focused products. 

Next, we’ll examine the impact of AI on application design and architecture and how AI-assisted “pair programming” changes software development.

info

Register for the FREE webinar on Intelligent Apps with Microsoft and Forrester on September 26 to explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries.

There will be a showcase of real-world use cases that demonstrate how AI can be seamlessly integrated into cloud-native environments driving tangible business value.

AI in Design and Architecture: Fueling Creativity and Efficiency

AI technologies help developers automate and streamline their processes and provide new ways to think about design.  

In traditional application development, the design and architecture phase can take significant time, effort, and expertise. For instance, adopting a monolithic architecture or a microservices-based design can significantly increase coding, testing, and debugging complexity. The app may even behave differently when hosted locally than in a deployed environment. It may take hours to track down an issue manually—time developers could spend elsewhere. 

Conversely, we can generate numerous design iterations in seconds with AI, each optimized to fulfill specific user needs and business goals. AI tools can quickly model various scenarios' impacts and software design patterns in the architectural design phase. It provides insights into performance and potential issues and suggests improvements. AI can also generate skeleton code, allowing developers to prototype different solutions or test specific components and technologies rapidly. By reviewing thousands of lines of code in seconds, AI allows us to focus on creating apps that serve users through feature development instead of searching for bugs.  

Integrating AI into the design and architecture phase can even extend our creative capabilities. By leveraging the power of generative AI—like large language models (LLMs)—and their broad knowledge and context going beyond software, we can better brainstorm apps and feature ideas. We can go beyond code and software architecture and get ideas about user experience, logo and branding, or marketing concepts. And as AI tools deliver insights and ideas we would have never considered; it encourages more innovation.

info

Watch Episode 01 of the Learn Live session to learn how to build an end-to-end intelligent app solution architecture.

The Power of AI-Assisted Pair Programming in Building Applications

AI-assisted pair programming is a collaborative coding approach where an AI system—like GitHub Copilot or TestPilot—assists developers during coding. It’s an increasingly common approach that significantly impacts developer productivity. In fact, GitHub Copilot is now behind an average of 46 percent of developers’ code and users are seeing 55 percent faster task completion on average. 

For new software developers, or those interested in learning new skills, AI-assisted pair programming are training wheels for coding. With the benefits of code snippet suggestions, developers can avoid struggling with beginner pitfalls like language syntax. Tools like ChatGPT can act as a personal, on-demand tutor—answering questions, generating code samples, and explaining complex code syntax and logic. These tools dramatically speed the learning process and help developers gain confidence in their coding abilities. 

Building applications with AI tools hastens development and provides more robust code. For example, GitHub Copilot helps us write new code accurately. It also quickly makes sense of existing code, suggests whole new lines or blocks of code, and even detects errors and proposes fixes. It’s like having a second coder working alongside us—one who never tires and is trained on billions of lines of code. 

However, it’s also important to acknowledge the limitations of AI. While generative AI-assistance technologies like GitHub Copilot are impressive, they’re still imperfect. They can sometimes produce erroneous, non-functional, or vulnerable code. They can also lack nuanced, domain-specific knowledge.  

So, although these tools are helpful, we can’t rely on them fully—these are programming aids rather than definitive solutions. AI-assisted pair programming is about augmenting the human developer’s skills, not replacing them.

Debugging and Improving Code With AI: Enhancing Application Quality

Identifying and correcting code errors is often time-consuming and challenging for developers. AI streamlines the debugging process by automatically detecting anomalies, suggesting fixes, and learning from previous bugs to prevent them from recurring. 

We can use generative AI to create unit tests from code snippets, from simple parameter checking and input validation to complex mocks of database and network services. Automated AI testing improves code coverage and robustness. It saves significant time compared to manually crafting unit tests. 

AI also revolutionizes code reviews by scanning and highlighting potential issues—like code patterns leading to a bug or a coding standards violation—and suggesting ways to simplify or refactor the code for readability and performance. This reduces human error and makes the code review process more efficient. Its benefits extend to small teams, where the availability of developers is crucial to deadlines, and large teams, where effective collaboration depends on readability and code consistency. AI further saves time by triaging issues so developers can prioritize the most mission-critical ones. 

Aside from improving code quality, AI is critical to understanding system behavior. Intelligent AI-assisted software development tools can monitor and audit system behavior, giving developers a better insight into how their code performs in different scenarios. AI can track patterns, identify inefficiencies, and provide alerts when they detect unusual behavior—enabling developers to enhance their code further. 

By predicting and identifying potential vulnerabilities, intelligent AI-assisted coding apps like Microsoft Security Copilot are pivotal in ensuring application functionality, reliability, and security. These AI tools are continually trained and upgraded with large codebases and user feedback that refines their responses. This ability to learn from past mistakes makes AI an incredibly powerful tool that will continue to expand its capabilities in the future.

info

Complete the AI Cloud Skills Challenge to build on you AI application development skills with paired programming using generative AI and GitHub Copilot.

AI and the Future of Application Development

The rapid advancements in AI and machine learning technologies point to an exciting future for application development. 

In “The Future of Applications,” software developer Mike Cann illustrates how LLMs might revolutionize application development. Cann that suggests fully-autonomous coding—AI-generated applications created entirely from prompts—may supersede traditional hand-crafted applications. Dynamically generated user interfaces could offer personalized and customizable experiences while we can design APIs for machine consumption and efficiency rather than human readability. Furthermore, we can store data in less structured, more adaptable, and flexible forms. 

AI is already making application development more accessible. With AI handling the complex, technical aspects of development, less-technical individuals can take a greater role in building applications. Consequently, AI can potentially democratize software engineering. 

Intelligent apps will continue to evolve, becoming more efficient, adaptable, and capable of learning from user interactions. Similarly, AI will inevitably become integral in every stage of app development.

Summary

AI has already created a new development paradigm—one in which machine intelligence and human creativity come together to produce innovative, efficient, and robust applications. AI already assists us at every stage of app development, from design and architecture to coding and debugging. 

Developers who embrace AI tools and integrate them into their processes can enhance their productivity, improve the quality of their applications, and expand the possibilities of their application development.  

The future of development is automated and AI-driven, and it’s here today. Getting started is easy—try an AI-powered development tool like GitHub Copilot to see how AI-assisted pair programming can enhance your productivity and code quality.

Exercise

+ + \ No newline at end of file diff --git a/30daysofIA/tags/azure-openai/page/7/index.html b/30daysofIA/tags/azure-openai/page/7/index.html index 61a86ed1a0..c920214ee2 100644 --- a/30daysofIA/tags/azure-openai/page/7/index.html +++ b/30daysofIA/tags/azure-openai/page/7/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "azure-openai" | Build Intelligent Apps On Azure +11 posts tagged with "azure-openai" | Build Intelligent Apps On Azure @@ -14,14 +14,13 @@ - - + +
-

9 posts tagged with "azure-openai"

View All Tags

· 4 min read
It's 30DaysOfIA

This Fall focus on building intelligent apps using AI and cloud-native technologies. #FallForIntelligentApps brings to you a learning journey to build your skills on creating differentiated experiences while modernizing your applications. It’s time to learn it all.

What We'll Cover

image

Get Ready To #FallForIntelligentApps starting September 18!

Today, we kick off the Fall season with content and activities to skill you up on all things Intelligent Apps or AI Apps on Azure with content, events, and community interactions! Read on to learn about what is coming!

Explore Our Initiatives

We have a number of initiatives planned for the month to help you learn and skill up on relevant technologies. Click on the links to visit the relevant pages for each

image

Register for the events!

What are 4 things you can do today, to jumpstart your learning journey?

#30Days Of Intelligent Apps

#30DaysOfIA is a series of daily blog posts grouped into themed weeks - taking you from core concepts to end-to-end solution examples in 30 days. Each article will provide conceptual lessons paired with exercises and resources to help you reinforce learnings and take next steps.

This series takes you through learning journey in six stages, each building on the previous week to help you skill up in a beginner-friendly way:

We will start with defining intelligent apps and then expand on how to build with cloud-native technologies like Azure Kubernetes Service, Azure Container Apps and Azure Functions, and take you through end-to-end scenarios for real world application development. Before we dive deep on intelligent apps, here is a high-level overview of the Intelligent Apps landscape on Azure:

image

Containers on Azure services offer you a wide range of capabilities, from simplicity to control to suit your different needs.

image -image

To start with the basics for developing Kubernetes applications, explore #30DaysOfCloudNative.

Cloud-native development when paired with serverless computing enhances your solution architecture for building cost optimized, resilient applications.

image

To start with the basics for serverless computing, explore #30DaysOfServerless.

Let’s Get Started

Now you know everything! We hope you are as excited as we are to dive into a full month of active learning and doing! Don't forget to subscribe for updates in your favorite feed reader! And look out for our first Intelligent Apps post Monday!

- - +
Skip to main content

11 posts tagged with "azure-openai"

View All Tags

· 10 min read
It's 30DaysOfIA

Explore real-world examples of how Intelligent Apps revolutionize logistics, retail, finance, manufacturing, tech, and medicine.

What We'll Cover:

  • Real-world businesses revolutionizing operations with Intelligent Apps
  • High level architecture for industry scenarios
  • Implementation architecture overview across industries

A decorative header image for the blog

Real-world Success Stories

According to IBM’s 2022 Global AI Adoption Index, 35% of surveyed companies already use AI in their business, and 42% are exploring the idea. Organizations lagging behind the AI curve will quickly fall behind early adopters, who are already enjoying its benefits and boosting their investment in this technology.

In the first part of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” we explored how organizations can use AI-powered apps, or intelligent apps, to transform their operations, decision-making, and customer experiences, leaping ahead of the competition.

Intelligent apps significantly improve operational efficiency and customer engagement across industries like logistics, retail, finance, manufacturing, tech, and healthcare. In this article, we’ll spotlight real-world success stories of businesses revolutionizing operations with intelligent apps using Microsoft Azure’s app, data, and AI services.

info

Register for the Learn Live on September 21 for a live guided experience with SMEs on how to build end-to-end solution architecture for real world implementation of Intelligent Apps.

Streamlining Operations: Intelligent Apps in the Airline Industry

Aerospace pioneer Airbus leveraged intelligent apps to streamline its operations and innovate its services with two ground-breaking modernizations that use Azure apps, data, and AI services to reimagine pilot training and predict aircraft maintenance issues. 

Aviation technology’s rapid evolution compelled Airbus to reconceive their approach to training pilots. The aircraft manufacturer created a pilot training chatbot by combining Azure AI Services with Speech to Text and Text to Speech. The bot helps trainee pilots navigate over 6,000 pages of technical information. Pilots can ask questions verbally for quick answers, aiding their understanding of dense information and speeding up training. And when Airbus needed to accommodate its military consumers, deploying its Kubernetes cluster to a disconnected, “air-gapped” environment met the strict security requirements of military aircraft training.

A chatbot could look like the following diagram as Azure services work together to create a conversational experience. Its components could include security and governance, bot logic and user experience (UX), bot cognition and intelligence, data extract, transfer, and load (ETL) operations, quality assurance and enhancements, logging, and monitoring and reporting.

A diagram of a conversational experience enabled by Azure services

Airbus also used AI to improve its aircraft maintenance and safety. They deployed AI Anomaly Detector to gather and analyze telemetry data from multiple flights, enabling more profound insights into wear and tear on military aircraft operating in harsh conditions. This AI application helps Airbus better understand each airplane’s health. Airbus can predict and fix potential problems before they occur, improving the aircraft’s safety and operational readiness.

AI and Azure are changing how organizations like Airbus operate, even in strict, highly regulated markets with stringent security requirements. And by streamlining pilot training and staying ahead of aircraft maintenance, Airbus helps its military and commercial clients reliably deliver their goods across the globe, maintaining their critical supply chains.

Boosting Customer Service: Intelligent Apps in the Retail Industry

The retail industry has been among the first to adopt and benefit from intelligent apps. For example, the leading American used car retailer, CarMax, uses intelligent apps to transform the car shopping experience.

Before embracing AI, CarMax faced a significant challenge: Manually creating text summaries was time-consuming and labor-intensive. CarMax decided to leverage Azure OpenAI Service to automatically generate car research pages, offering customers valuable insights while enhancing the website’s search engine rankings.

This AI-driven approach has significantly streamlined the process, achieving in a few months what would have taken years manually. Also, the AI system condenses thousands of customer reviews into a few readable sentences, offering potential buyers a quick overview of others’ experiences.

CarMax has improved their customer experience while boosting operational efficiency through AI adoption. CarMax’s editorial staff focuses on creating strategic, longer-form content with the time saved, enhancing productivity, and driving more website traffic.

CarMax’s success story serves as a testament to the potential of intelligent apps in the retail industry.

Enhancing Cybersecurity: Intelligent Apps in Finance

Intelligent apps also impact the highly regulated financial services sector. Swift, a leading infrastructure provider for financial messaging services, uses intelligent apps to enhance decision-making and combat financial crime.

The massive growth of cross-border transactions and instant payment networks led to a surge in financial fraud that can cost the industry hundreds of billions of dollars every year. So, Swift and Microsoft partnered to build an anomaly detection model using federated learning techniques with Azure Machine Learning and Azure confidential computing.

Microsoft Purview helped govern data, as the federated learning method partially trained each model in a silo before aggregating them. This approach enabled Swift and Microsoft to develop a highly accurate model without copying or moving data from Swift members’ secure locations, ensuring the highest level of security and privacy.

Financial organizations can now deploy this trained ML model to run a real-time fraud detection system, lowering the cost of financial fraud.

Organizations can deploy a fraud detection system like the following example on Microsoft Azure. The system ingests data from streams or storage, runs analytics, and uses ML to detect fraud and store results. It then alerts users and other systems to the security issue.

A diagram of an example fraud detection system

Swift provides the innovative model to its banking partners, who can share data to further train the model. Azure confidential computing securely runs new models on current data to accumulate insights, generating an ongoing learning cycle. This iterative approach strengthens the global detection of fraudulent financial transactions through an increasingly faster and more accurate model. 

The collaboration between Swift and Microsoft demonstrates how combining various Azure services creates Intelligent Apps to enhance decision-making in finance. These apps solve real problems at an unprecedented global scale. 

Enhancing Efficiency: Intelligent Apps in Manufacturing

3M, known for Post-it Notes and other innovative products, wielded the power of Intelligent Apps to standardize, automate, and accelerate its sales forecasting. The company needed a unified, automated approach to replace its multiple manual methods.

It adopted Azure Machine Learning and other Azure AI Services to develop, train, and implement 1,500 custom models for each region and division, replacing manual methods. This solution delivers timely finance, sales, and marketing insights while freeing teams from time-consuming manual data manipulation tasks.

3M used Azure Machine Learning, automated machine learning (AutoML), and the Many Models Solution Accelerator to train and score numerous machine learning models in parallel. This approach significantly shortened the company’s development cycle time.

Then, 3M integrated Microsoft Power BI into their solution to compile and visualize results more effectively. Executives now have an unbiased and unfiltered view of data for better decision-making. With its improved ability to forecast sales, 3M can focus on manufacturing the products it needs efficiently.

3M’s success demonstrates how combining various Azure services can enhance efficiency in the manufacturing industry. These apps improve operational processes while enabling employees to focus on higher-level tasks. Strategic decisions are more targeted, and business conversations are more meaningful.

Driving Innovation: Intelligent Apps in the Technology Industry

As expected, the tech industry is at the forefront of leveraging Intelligent Apps to drive innovation. Elastic saw an opportunity to use intelligent apps to optimize its search services and meet customer needs more effectively. The company built a managed, secure, and scalable solution for its customers, Elastic Cloud, by leveraging Microsoft Azure.

Elastic Cloud uses Azure Machine Learning and other Azure AI Services to automate many basic management tasks, such as backing up data, upgrading, and scaling deployments. This approach enables Elastic’s customers to focus less on managing the software and more on serving their own clients. Customers can quickly deploy and manage Elastic Cloud on Kubernetes (ECK) using Azure Kubernetes Service.

As a result, Elastic’s cloud business is growing twice as fast as its on-premises solutions. Enterprise search users have reported significant improvements in system availability and employee satisfaction.

Additionally, Elastic incorporated Azure OpenAI Service into its Elasticsearch Relevance Engine (ESRE), providing more relevant answers to search queries. ESRE enables text, vector, and hybrid search, offering a more effective and accurate response to queries.

Elastic’s success story demonstrates how combining various Azure services can create intelligent Apps to drive innovation in the tech industry. These apps help improve operational processes and deliver better results quickly.

Revolutionizing Healthcare: Intelligent Apps in Medicine

Intelligent apps also drive transformative changes in the healthcare sector. Cambridgeshire and Peterborough NHS Foundation Trust, a healthcare provider in the United Kingdom, gradually shifted their on-premises systems to the cloud using Azure, improving flexibility, and lowering costs. They needed a solution for their electronic patient record (EPR) system that integrates 23 years of data across multiple systems and formats.

The Trust used Microsoft Azure and Azure Cognitive Search, including semantic search for understanding intent and contexts and cognitive capabilities for extracting insights from data, to standardize and automate data management. This approach made legacy, archive, and live data more discoverable, saving clinicians’ time. The Trust’s solution searches through millions of clinical documents in seconds, even recognizing handwritten notes without needing a separate optical character recognition (OCR) solution to extract text from images (Azure Cognitive Search automatically does it). Clinicians can now uncover previously inaccessible insights, enabling them to offer more personalized care.

The Trust also used Integration Services tools such as Azure Logic Apps to create workflows without writing code and Event Grid to connect Azure and third-party services through a publisher-subscriber model. They switched on a live feed from their new EPR system to Azure to feed real-time information into other applications. Researchers can now access anonymized data to fuel medical innovations beyond the NHS.

The healthcare provider also invested heavily in Power BI’s data visualization and analytical functions to sift through massive amounts of information. This level of data accessibility was previously impossible with their on-premises system. The new insights have significantly improved clinicians’ efficiency for better patient care.

Data accessibility and security are critical in the healthcare industry. Intelligent Apps empower organizations like the Trust to revolutionize medical care. AI capabilities helped the Trust improve its operational efficiency and user experiences, setting a new standard for healthcare in the digital age.

Summary

AI is driving transformation and real-world successes across industries. Intelligent apps are reshaping our world by streamlining operations in manufacturing and logistics, enhancing customer experience in the retail sector, improving decision-making in finance, driving innovation in tech, and revolutionizing healthcare.

As organizations adopt AI, they’re innovating in ways that were once too difficult, expensive, or outright impossible. Companies that wait to embrace intelligent apps risk the chance of falling behind. By embracing intelligent apps and leveraging the capabilities of Azure, you can harness the transformative potential of AI to remain on the cutting edge of technology.

Exercise

+ + \ No newline at end of file diff --git a/30daysofIA/tags/azure-openai/page/8/index.html b/30daysofIA/tags/azure-openai/page/8/index.html index 6358d3d577..8a9074dd6c 100644 --- a/30daysofIA/tags/azure-openai/page/8/index.html +++ b/30daysofIA/tags/azure-openai/page/8/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "azure-openai" | Build Intelligent Apps On Azure +11 posts tagged with "azure-openai" | Build Intelligent Apps On Azure @@ -14,14 +14,13 @@ - - + +
-

9 posts tagged with "azure-openai"

View All Tags

· 4 min read
It's 30DaysOfIA

Continue The Learning Journey through Fall For Intelligent Apps! 🍂

What We'll Cover

Thank you! ♥️

image

It's hard to believe that JavaScript on Azure hack-together is ending! It seems like just yesterday that we launched this initiative, and yet here we are, 15 days later, with an incredible amount of learning and growth behind us. So... it's time for a wrap!

From the bottom of our hearts, we want to thank each and every one of you for your participation, engagement, and enthusiasm. It's been truly inspiring to see the passion and dedication from this strong community, and we're honored to be a part of it. ✨

Recap of The JavaScript on Azure Global Hack-Together

As we wrap up this exciting event, we wanted to take a moment to reflect on all that we've accomplished together. Over the last 15 days, we've covered a lot of ground, from the basics of contributing to Open source to the exploration of the Contoso Real Estate project from its Frontend to its Backend and future AI implementation.

Now that the hack-together is ending, we want to make sure that you have all the resources you need to continue honing your skills in the future. Whether you're looking to make your fist contribution to open source, become an open source maintainers, collaborate with others, or simply keep learning, there are plenty of resources out there to help you achieve your goals. So, let's dive in and explore all the ways you can continue to grow your JavaScript skills on Azure!

JSonAzure Hack-together Roadmap 📍:

hack-together-roadmap (2)

Recap on past Livestreams🌟:

Day 1️⃣: Opening Keynote (Hack-together Launch): Introduction to the Contoso Real Estate Open-source project!, managing complex and complex enterprise architecture, new announcements for JavaScript developers on Azure

Day 2️⃣: GitHub Copilot & Codespaces: Introduction to your AI pair programmer (GitHub Copilot) and your virtual developer environment on the cloud (GitHub Codespaces)

Day 6️⃣: Build your Frontend using Static Web Apps as part of a complex, modern composable frontends (or micro-frontends) and cloud-native applications.

Day 9️⃣: Build a Serverless Backend using Azure Functions

Day 1️⃣3️⃣: Easily connect to an Azure Cosmos DB, exploring its benefits and how to get started

Day 1️⃣5️⃣: Being in the AI Era, we dive into the Azure OpenAI Service and how you can start to build intelligent JavaScript applications

📖 Self-Learning Resources

  1. JavaScript on Azure Global Hack Together Module collection
  2. Lets #HackTogether: Javascript On Azure Keynote
  3. Step by Step Guide: Migrating v3 to v4 programming model for Azure Functions for Node.Js Application

Continue your journey with #FallForIntelligentApps

Join us this Fall on a learning journey to explore building intelligent apps. Combine the power of AI, cloud-native app development, and cloud-scale data to build highly differentiated digital experiences. Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure. Engage in a self-paced learning adventure by following along the #30Days of Intelligent Apps series, completing the Intelligent Apps Skills Challenge, joining the Product Group for a live Ask The Expert series or building an end to end solution architecture with a live guided experience through the Learn Live series. Discover more here.

Hands-on practice: Make your first contribution to open source!

Join our GitHUb Discussion Forum to connect with developers from every part of world, see contributions from other, find collaborators and make your first contribution to a real-world project! -Don't forget to give the repo a star ⭐

Resources

All resources are accessible on our landing page

- - +
Skip to main content

11 posts tagged with "azure-openai"

View All Tags

· 8 min read
It's 30DaysOfIA

Let’s start with understanding the power of intelligent applications.

What We'll Cover:

  • What are intelligent applications?
  • Categories of Intelligent Apps
  • Breaking down the role of AI in intelligent apps

A decorative header image for the blog

Demystifying Intelligent Applications: Leveraging AI in App Development

Until recent years, when you heard the term “artificial intelligence” (AI), you might have pictured autonomous robots from sci-fi films. Now, perhaps your mind jumps to big data and machine learning (ML). Maybe you’re even thinking about the latest ChatGPT plugins and transformer architectures. Today, AI is more than these imaginations: it’s a reality increasingly woven into our day-to-day routines through intelligent applications.

Intelligent apps combine cloud-scale compute, data, and predictive or generative AI. These applications help you automate tasks and make data-driven decisions, aiding in leveraging business intelligence to gain meaningful, practical outcomes. 

For developers, the AI landscape isn’t an abstract complexity. It’s a real, tangible opportunity for evolving traditional applications, making them “intelligent,” and delivering more value to users. And with tools like Azure Kubernetes Service, Azure Cosmos DB, and Azure AI Services, stepping into the world of intelligent apps isn’t a steep climb but a steady trek across a manageable learning curve.

The Three Categories of Intelligent Applications

Before we start building, it’s critical to understand what makes an app “intelligent.” It’s not a universal definition so much as an identifier for apps that fall into one or more of the three primary categories: 

  • Outcome-based — This designation represents the broadest category of intelligent apps, whose principal value for users is the intelligent outcomes they provide. These apps process data to provide insightful results that enhance decision-making or help with tasks like trend prediction, resource management, and customer relationship management.

  • Functionality-based — This type of intelligent app takes AI integration a degree further, integrating AI as a function of the app to create or process its outputs, enhancing the app’s overall interactivity and usefulness.

  • Feature-based — This is the narrowest category, in which the app’s AI/ML component is one of its core features and primary selling points

Let’s dive more deeply into each category.

Outcome-Based Apps: Intelligent Outcomes Drive Success

Outcome-based applications are all about the results provided to the user. They function beyond merely processing data to offer insightful outcomes that drive success.

Think of a personal fitness tracker app that uses AI to analyze the user’s activity and provides actionable recommendations for achieving a goal. This type of app not only tracks your steps, sleep patterns, and heart rate but uses that data to provide you with personalized health advice. It helps you make informed lifestyle choices — the “intelligent” outcome.

Another example is a social media marketing app that uses AI for time-saving data analysis, task automation, and trend prediction. AI-based sentiment analysis can gauge customer perception while natural language processing (NLP) accurately interprets customer inquiries. Task automation can schedule posts and automate responses to common customer queries, and trend forecasting helps model and predict customer behavior to further personalize targeted promotional efforts. Building this type of intelligence into your apps provides cost-effective, actionable insights that guide sales and marketing strategies.

Functionality-Based Apps: AI-Driven Features Enhance User Experiences

As the name suggests, functionality-based applications integrate AI into how the app functions, but the end user drives the action and outcome. In this category of apps, AI takes a more active role in the user’s interaction by allowing them to use AI-created or processed output. These apps leverage AI technologies like Azure AI Services for NLP, image recognition, and pattern recognition to improve user experience.

Consider a music app that generates personalized playlists based on your listening habits or your smartphone’s camera app that uses AI to recognize faces and optimize photo settings. Even more advanced are the language translation apps that translate text from one language to another and can even understand and maintain the context and subtleties of the conversation. They use NLP to accomplish these feats.

Similarly, consider the latest e-commerce apps, whose AI-driven functionalities enable customers to try on clothes virtually, significantly enhancing the online shopping experience.

Feature-Based Apps: Integrating Advanced AI/ML Components

Finally, there are feature-based intelligent apps. These apps incorporate advanced AI/ML components like neural networks, internal large language models (LLMs), and sophisticated algorithms as core features for the user. In this type of application, AI is the primary feature for users to engage with and the primary selling point for the app.

Some examples of feature-based applications are chatbots and virtual agents built using Azure Bot Service. These robo-representatives use AI to provide more natural and human-like conversation, balancing superior efficiency with a friendlier, more personalized user experience. The new Bing is a prime example of this implementation, integrating AI to give users a knowledgeable copilot for web searches.

Similarly, OpenAI’s ChatGPT allows users access to its state-of-the-art language models available as Microsoft’s Azure OpenAI Service. It leverages the power of the highly sophisticated GPT-3 LLM to perform tasks like drafting emails, writing code, answering general knowledge questions, translating between languages, and tutoring in numerous subjects.

These apps don’t just use AI to provide outputs or improve user experiences: They push the boundaries of what’s possible in app development by integrating AI/ML components.

info

Register for the live Q&A Ask The Expert session on September 26 with the Azure Functions product engineering team.

Breaking Down the Role of AI in Intelligent Applications

Intelligent apps reflect the merging of traditional software design with the dynamic capabilities of AI. Let’s explore how AI integrates and amplifies these applications.

At its core, AI augments applications with the capacity to self-learn and self-improve. It allows you to create applications that predict, react, and adapt to a constantly changing environment. In the power grid management system for a major city, an AI-infused application can forecast power usage trends, react to anomalies, and adapt to sudden demand surges, ensuring optimal efficiency and preventing blackouts.

But AI’s role doesn’t end at prediction or decision-making: It also automates tasks. In enterprise solutions, it has moved beyond simple automation to encompass intelligent process automation (IPA). In this arena, AI algorithms understand, learn, and then automate entire digital workflows. For developers, this is a fertile ground for creating applications that can dramatically improve the operational efficiency of businesses.

Even more, AI substantially aids in development tasks. For example, there’s GitHub Copilot, an AI-powered code assistant that offers suggestions based not only on syntax but on development best practices and the intent of the code. AI has evolved from a tool you use to build apps to a partner that helps in creating them. 

Using AI to build intelligent apps isn’t about throwing out what’s worked before. It’s about enhancing the components that have the most impact and achieve the best results. Incorporating AI in apps elevates their predictive abilities, streamlines complex tasks, and even aids in the development and creation process. When you build intelligent apps, you don’t just address current problems. You anticipate future challenges and craft solutions that adapt over time.

Summary

As developers the power of AI is in our hands, and it’s our responsibility to harness its full potential. Each category tells the story of AI's transformative potential, from applications that deliver intelligent outcomes to those with AI capabilities to enrich the user experience and those pushing boundaries with advanced AI/ML technology.

Accessible and robust platforms like Azure AI can simplify the process of developing Intelligent Applications. Whether you’re refining an existing app or building a new one, just entering the world of AI, or already familiar with the landscape, take a moment to appreciate the potential that AI integration offers. You’re embracing this exciting frontier and shaping the future of app development. 

Exercise

  • Complete the Cloud Skills Challenge to build on your apps and AI skills.
  • Watch Episode 01 of the Serverless Edition Learn Live session to learn how to build an end-to-end intelligent app solution.
+ + \ No newline at end of file diff --git a/30daysofIA/tags/azure-openai/page/9/index.html b/30daysofIA/tags/azure-openai/page/9/index.html index e55e568e08..85ce51032e 100644 --- a/30daysofIA/tags/azure-openai/page/9/index.html +++ b/30daysofIA/tags/azure-openai/page/9/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "azure-openai" | Build Intelligent Apps On Azure +11 posts tagged with "azure-openai" | Build Intelligent Apps On Azure @@ -14,13 +14,14 @@ - - + +
-

9 posts tagged with "azure-openai"

View All Tags

· 2 min read
It's 30DaysOfIA

September is almost here - and that can only mean one thing!! It's time to 🍂 Fall for something new and exciting and spend a few weeks skilling up on relevant tools, techologies and solutions!!

Last year, we focused on #ServerlessSeptember. This year, we're building on that theme with the addition of cloud-scale Data, cloud-native Technologies and cloud-based AI integrations to help you modernize and build intelligent apps for the enterprise!

Watch this space - and join us in September to learn more!

- - +
Skip to main content

11 posts tagged with "azure-openai"

View All Tags

· 4 min read
It's 30DaysOfIA

This Fall focus on building intelligent apps using AI and cloud-native technologies. #FallForIntelligentApps brings to you a learning journey to build your skills on creating differentiated experiences while modernizing your applications. It’s time to learn it all.

What We'll Cover

image

Get Ready To #FallForIntelligentApps starting September 18!

Today, we kick off the Fall season with content and activities to skill you up on all things Intelligent Apps or AI Apps on Azure with content, events, and community interactions! Read on to learn about what is coming!

Explore Our Initiatives

We have a number of initiatives planned for the month to help you learn and skill up on relevant technologies. Click on the links to visit the relevant pages for each

image

Register for the events!

What are 4 things you can do today, to jumpstart your learning journey?

#30Days Of Intelligent Apps

#30DaysOfIA is a series of daily blog posts grouped into themed weeks - taking you from core concepts to end-to-end solution examples in 30 days. Each article will provide conceptual lessons paired with exercises and resources to help you reinforce learnings and take next steps.

This series takes you through learning journey in six stages, each building on the previous week to help you skill up in a beginner-friendly way:

We will start with defining intelligent apps and then expand on how to build with cloud-native technologies like Azure Kubernetes Service, Azure Container Apps and Azure Functions, and take you through end-to-end scenarios for real world application development. Before we dive deep on intelligent apps, here is a high-level overview of the Intelligent Apps landscape on Azure:

image

Containers on Azure services offer you a wide range of capabilities, from simplicity to control to suit your different needs.

image +image

To start with the basics for developing Kubernetes applications, explore #30DaysOfCloudNative.

Cloud-native development when paired with serverless computing enhances your solution architecture for building cost optimized, resilient applications.

image

To start with the basics for serverless computing, explore #30DaysOfServerless.

Let’s Get Started

Now you know everything! We hope you are as excited as we are to dive into a full month of active learning and doing! Don't forget to subscribe for updates in your favorite feed reader! And look out for our first Intelligent Apps post Monday!

+ + \ No newline at end of file diff --git a/30daysofIA/tags/community-buzz/index.html b/30daysofIA/tags/community-buzz/index.html index 93ec188f60..585e14f12a 100644 --- a/30daysofIA/tags/community-buzz/index.html +++ b/30daysofIA/tags/community-buzz/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "community-buzz" | Build Intelligent Apps On Azure +11 posts tagged with "community-buzz" | Build Intelligent Apps On Azure @@ -14,13 +14,13 @@ - - + +
-

9 posts tagged with "community-buzz"

View All Tags

· 10 min read
It's 30DaysOfIA

Prepare your organization to adopt intelligent apps by taking a change management approach. Assess your readiness, upskill teams, create a culture of change and overcome resistance, and prepare for future evolution to pave the way for a smooth transition into an AI-centric company.

What We'll Cover:

  • Role of change management in modernizing to intelligent apps
  • Skills needed for building intelligent apps in your organization
  • How to build a culture of change to embrace AI
  • How to prepare for the future of intelligent apps and continuous evolution

image of modernizing AI solutions for intelligent apps

Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management

As we’ve discussed throughout this series, intelligent apps and generative AI are rewriting the rules of business and application development. The first article of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” explored how intelligent apps can enhance user experiences and drive businesses growth. These advancements are opening new opportunities and markets for companies ready to embrace them.

However, embracing this digital transformation is more complex than flipping a switch. It requires adjustments to the technical side, which we explored in the series’ fifth article, “Preparing the Path for Intelligent Apps: Transitioning from On-Premises/IaaS to Cloud Native Applications.” It also demands a shift in how organizations adapt to change and approach app design and development. Let’s explore that mindset transition with tips to help you make it happen.

The Role of Change Management in Transitioning to Intelligent Apps

Transitioning to intelligent apps isn’t merely a technological change. It’s a profound shift in how businesses perceive and operate. This move toward artificial intelligence (AI)-centric approach isn’t meant to replace humans with machines or radically overhaul all business operations.

Instead, intelligent apps should augment human capabilities, streamline processes, and foster an environment where innovation flourishes. They expand toolkits and techniques to include AI-assisted development tools like GitHub Copilot and AI-enabled workflows, helping us design and deliver AI-enhanced experiences.

Effective change management strategies are crucial in helping employees adapt to these new ways of working with intelligent apps, ensuring that the transition doesn’t disrupt productivity or morale.

Team and organization strategies should: 

  • Assess the impact of any proposed changes.  
  • Communicate the benefits and reasons behind the change to all affected team members.  
  • Provide the necessary training and support.  
  • Integrate the changes into the culture and daily operations. 

By incorporating this robust change management approach, you can identify and mitigate potential transition risks early. Moreover, this strategy ensures that employees are engaged throughout the process, fostering a sense of involvement and ownership. This, in turn, cultivates a culture suited for innovation through intelligent apps. 

These organizational changes focus on the following aspects: organizational readiness, skillset, culture of change and overcoming resistance, and adaptability. Let’s explore them below.

info

Register for the intelligent apps webinar on Driving Business Value by Modernizing with Cloud-Native & AI with Microsoft and Forrester on September 26. 

Explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries. 

Identifying Organizational Readiness for Intelligent Apps

The first step in preparing your organization for intelligent apps is to assess your current readiness for change. Analyze various elements of your organization, from infrastructure and workforce skills to strategic vision and overall culture.

Can your apps and systems integrate with AI platforms like Microsoft Azure AI? Is your workforce comfortable working alongside AI, or are they fearful and resistant? Does your strategic vision incorporate AI’s transformative potential, or does it fail to acknowledge AI as a critical driver of future growth?

These questions identify the challenges of integrating AI into an organization and determine the company’s readiness for intelligent apps.

Embracing intelligent apps involves more than being ready with your technological infrastructure or having the right technology or tools. You need flexible organizational structures and processes driving your business to accommodate new ideas and capabilities. Your teams must feel empowered to own and drive them. Much like Microsoft’s shift to AI-powered services—like AI-powered Bing and Edge and Microsoft 365 Copilot—embracing AI organization-wide requires a holistic vision and willingness to innovate, with teams capable of delivering on the plan.

After accessing these critical areas for organizational readiness, you can start planning a strategy for this transition to an AI-ready organization. Identify skill gaps and work to bridge them while nurturing an environment that appreciates and harnesses the potential of AI and intelligent apps.

Nurturing the Indispensable Skills for Intelligent Apps

Tasking your AI specialists with preparing your organization to use and build intelligent apps isn’t enough. Accomplishing this goal requires a cross-functional effort and a diverse skill set.

Developing intelligent apps involves data scientists managing and interpreting vast amounts of data, machine learning engineers building and training the models, and traditional developers integrating these models into usable applications. However, the impact of generative AI spans beyond creating apps. AI fluency across the entire organization—from sales and marketing to accounting and legal departments—offers substantial benefits, sparking new ideas and solutions from sometimes unexpected sources.

Teams' AI skills might roughly fall into three levels: 

  • Fundamental skills — Being familiar and efficient with using AI tools, for example, knowing how to write effective prompts for large language models (LLMs).
  • Visionary skills — Recognizing AI’s strengths and weaknesses, for example, its creativity and hallucinations, that is, unexpected output.
  • Expert skills — Managing data and training models and integrating AI technology into apps, such as incorporating the OpenAI application programming interface (API) into an Intelligent App.

Based on these levels, you can gauge your team's skill readiness to pinpoint the most significant gaps, whether in general AI knowledge across your organization or within technical development teams.

As intelligent apps become increasingly prevalent, upskilling your workforce is crucial. This transition opens a new realm of skills and capabilities for your team to master. You can offer machine learning, data analysis, and cloud computing training programs—such as Microsoft’s Azure AI Fundamentals certification—to empower your team to confidently navigate the intelligent apps landscape.

Nurturing these skills within your organization may require hiring new talent, reskilling current employees, and creating an environment that promotes learning and experimentation. Specifically, if your team understands tools like the Microsoft Azure AI platform, they could contribute significantly to developing and maintaining intelligent apps.

An upskilled team is more than a functional unit. It’s a think tank to drive innovation and improve your organization’s competitive standing in the digital marketplace.

Building a Culture of Change to Embrace AI

Even with the right people and skills in place, change remains an uphill battle for organizations committed to traditional processes. 

Transitioning to intelligent apps is as much about mindset as technology and skills. It requires fostering a culture open to change and innovation that understands AI’s transformative potential and embraces it to enhance rather than replace human capabilities. 

Leaders play a pivotal role in cultivating this mindset. As they model a positive attitude toward AI, emphasize its benefits, and address any concerns transparently, leaders significantly contribute to the organization’s readiness to adopt intelligent apps. 

The most critical factor in this transition is enabling an AI-friendly culture. It’s about encouraging curiosity, rewarding innovative thinking, and creating an environment where people thrive on learning and experimentation. 

Empowering teams to leverage practical AI tools and AI-driven data insights to aid decision-making helps motivate employees to use and explore AI’s potential, improve their skills, and contribute to the successful integration of intelligent apps. Such an environment is fertile ground to grow breakthrough ideas and innovative solutions. 

Overcoming Resistance to Change and Adopting Intelligent Apps

Despite AI’s potential, change isn’t easy. It takes effort and is especially challenging for organizations used to existing processes. This resistance to change is a significant roadblock when transitioning to intelligent apps. 

Overcoming this resistance involves:  

  • Understanding employees’ concerns. 
  • Challenging underlying assumptions about traditional systems and outdated processes that hinder change. 
  • Providing comprehensive training for workers to use AI effectively.
  • Demonstrating the benefits and opportunities of intelligent apps. 
  • Clear and continuous communication to dispel fears and misconceptions about AI.

These steps will foster greater acceptance and enthusiasm for intelligent apps in your teams. 

Preparing for the Future of Intelligent Apps and Continuous Evolution

Finally, it’s essential to understand that generative AI is just beginning to gain momentum. Adjusting to a world of intelligent apps isn’t a one-time change—it’s a continuous evolution. 

As AI technologies mature and evolve and more businesses realize their benefits, we can expect the landscape of intelligent apps to expand further. Organizations can cultivate a mindset of continuous learning, flexibility, and adaptation to prepare for this future. Staying ahead in this dynamic field requires organizations to keep up with the latest AI advancements, continually reassess their strategies, and pivot when necessary. 

The domain of intelligent apps is rapidly advancing. Organizations must prepare to navigate this dynamic landscape to stay ahead of the curve.

Summary

Preparing for AI and intelligent apps is a strategic decision. It requires thoughtful preparation and a culture that embraces change.

With the proper preparation and mindset, adopting AI can become a smoother, collaborative path to innovation for the whole organization. Integrating AI into your organization does not discard what has worked in the past. It simply enhances and augments those processes for a better future.

Consider the points above to assess your organization’s readiness and prepare to welcome intelligent apps.

In the first week of this series, you learned about intelligent apps, how organizations today find success through intelligent apps, what AI-assisted application development looks like, ways to leverage the power of generative AI, the technical changes needed to prepare for intelligent apps, and, in this article, the organizational changes required to harness the power of AI.

The future is intelligent. Use this series as a guideline to navigate challenges while preparing for and embracing AI's opportunities for an intelligent tomorrow.

Exercise

- - +
Skip to main content

11 posts tagged with "community-buzz"

View All Tags

· 10 min read
It's 30DaysOfIA

Prepare your organization to adopt intelligent apps by taking a change management approach. Assess your readiness, upskill teams, create a culture of change and overcome resistance, and prepare for future evolution to pave the way for a smooth transition into an AI-centric company.

What We'll Cover:

  • Role of change management in modernizing to intelligent apps
  • Skills needed for building intelligent apps in your organization
  • How to build a culture of change to embrace AI
  • How to prepare for the future of intelligent apps and continuous evolution

image of modernizing AI solutions for intelligent apps

Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management

As we’ve discussed throughout this series, intelligent apps and generative AI are rewriting the rules of business and application development. The first article of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” explored how intelligent apps can enhance user experiences and drive businesses growth. These advancements are opening new opportunities and markets for companies ready to embrace them.

However, embracing this digital transformation is more complex than flipping a switch. It requires adjustments to the technical side, which we explored in the series’ fifth article, “Preparing the Path for Intelligent Apps: Transitioning from On-Premises/IaaS to Cloud Native Applications.” It also demands a shift in how organizations adapt to change and approach app design and development. Let’s explore that mindset transition with tips to help you make it happen.

The Role of Change Management in Transitioning to Intelligent Apps

Transitioning to intelligent apps isn’t merely a technological change. It’s a profound shift in how businesses perceive and operate. This move toward artificial intelligence (AI)-centric approach isn’t meant to replace humans with machines or radically overhaul all business operations.

Instead, intelligent apps should augment human capabilities, streamline processes, and foster an environment where innovation flourishes. They expand toolkits and techniques to include AI-assisted development tools like GitHub Copilot and AI-enabled workflows, helping us design and deliver AI-enhanced experiences.

Effective change management strategies are crucial in helping employees adapt to these new ways of working with intelligent apps, ensuring that the transition doesn’t disrupt productivity or morale.

Team and organization strategies should: 

  • Assess the impact of any proposed changes.  
  • Communicate the benefits and reasons behind the change to all affected team members.  
  • Provide the necessary training and support.  
  • Integrate the changes into the culture and daily operations. 

By incorporating this robust change management approach, you can identify and mitigate potential transition risks early. Moreover, this strategy ensures that employees are engaged throughout the process, fostering a sense of involvement and ownership. This, in turn, cultivates a culture suited for innovation through intelligent apps. 

These organizational changes focus on the following aspects: organizational readiness, skillset, culture of change and overcoming resistance, and adaptability. Let’s explore them below.

info

Register for the intelligent apps webinar on Driving Business Value by Modernizing with Cloud-Native & AI with Microsoft and Forrester on September 26. 

Explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries. 

Identifying Organizational Readiness for Intelligent Apps

The first step in preparing your organization for intelligent apps is to assess your current readiness for change. Analyze various elements of your organization, from infrastructure and workforce skills to strategic vision and overall culture.

Can your apps and systems integrate with AI platforms like Microsoft Azure AI? Is your workforce comfortable working alongside AI, or are they fearful and resistant? Does your strategic vision incorporate AI’s transformative potential, or does it fail to acknowledge AI as a critical driver of future growth?

These questions identify the challenges of integrating AI into an organization and determine the company’s readiness for intelligent apps.

Embracing intelligent apps involves more than being ready with your technological infrastructure or having the right technology or tools. You need flexible organizational structures and processes driving your business to accommodate new ideas and capabilities. Your teams must feel empowered to own and drive them. Much like Microsoft’s shift to AI-powered services—like AI-powered Bing and Edge and Microsoft 365 Copilot—embracing AI organization-wide requires a holistic vision and willingness to innovate, with teams capable of delivering on the plan.

After accessing these critical areas for organizational readiness, you can start planning a strategy for this transition to an AI-ready organization. Identify skill gaps and work to bridge them while nurturing an environment that appreciates and harnesses the potential of AI and intelligent apps.

Nurturing the Indispensable Skills for Intelligent Apps

Tasking your AI specialists with preparing your organization to use and build intelligent apps isn’t enough. Accomplishing this goal requires a cross-functional effort and a diverse skill set.

Developing intelligent apps involves data scientists managing and interpreting vast amounts of data, machine learning engineers building and training the models, and traditional developers integrating these models into usable applications. However, the impact of generative AI spans beyond creating apps. AI fluency across the entire organization—from sales and marketing to accounting and legal departments—offers substantial benefits, sparking new ideas and solutions from sometimes unexpected sources.

Teams' AI skills might roughly fall into three levels: 

  • Fundamental skills — Being familiar and efficient with using AI tools, for example, knowing how to write effective prompts for large language models (LLMs).
  • Visionary skills — Recognizing AI’s strengths and weaknesses, for example, its creativity and hallucinations, that is, unexpected output.
  • Expert skills — Managing data and training models and integrating AI technology into apps, such as incorporating the OpenAI application programming interface (API) into an Intelligent App.

Based on these levels, you can gauge your team's skill readiness to pinpoint the most significant gaps, whether in general AI knowledge across your organization or within technical development teams.

As intelligent apps become increasingly prevalent, upskilling your workforce is crucial. This transition opens a new realm of skills and capabilities for your team to master. You can offer machine learning, data analysis, and cloud computing training programs—such as Microsoft’s Azure AI Fundamentals certification—to empower your team to confidently navigate the intelligent apps landscape.

Nurturing these skills within your organization may require hiring new talent, reskilling current employees, and creating an environment that promotes learning and experimentation. Specifically, if your team understands tools like the Microsoft Azure AI platform, they could contribute significantly to developing and maintaining intelligent apps.

An upskilled team is more than a functional unit. It’s a think tank to drive innovation and improve your organization’s competitive standing in the digital marketplace.

Building a Culture of Change to Embrace AI

Even with the right people and skills in place, change remains an uphill battle for organizations committed to traditional processes. 

Transitioning to intelligent apps is as much about mindset as technology and skills. It requires fostering a culture open to change and innovation that understands AI’s transformative potential and embraces it to enhance rather than replace human capabilities. 

Leaders play a pivotal role in cultivating this mindset. As they model a positive attitude toward AI, emphasize its benefits, and address any concerns transparently, leaders significantly contribute to the organization’s readiness to adopt intelligent apps. 

The most critical factor in this transition is enabling an AI-friendly culture. It’s about encouraging curiosity, rewarding innovative thinking, and creating an environment where people thrive on learning and experimentation. 

Empowering teams to leverage practical AI tools and AI-driven data insights to aid decision-making helps motivate employees to use and explore AI’s potential, improve their skills, and contribute to the successful integration of intelligent apps. Such an environment is fertile ground to grow breakthrough ideas and innovative solutions. 

Overcoming Resistance to Change and Adopting Intelligent Apps

Despite AI’s potential, change isn’t easy. It takes effort and is especially challenging for organizations used to existing processes. This resistance to change is a significant roadblock when transitioning to intelligent apps. 

Overcoming this resistance involves:  

  • Understanding employees’ concerns. 
  • Challenging underlying assumptions about traditional systems and outdated processes that hinder change. 
  • Providing comprehensive training for workers to use AI effectively.
  • Demonstrating the benefits and opportunities of intelligent apps. 
  • Clear and continuous communication to dispel fears and misconceptions about AI.

These steps will foster greater acceptance and enthusiasm for intelligent apps in your teams. 

Preparing for the Future of Intelligent Apps and Continuous Evolution

Finally, it’s essential to understand that generative AI is just beginning to gain momentum. Adjusting to a world of intelligent apps isn’t a one-time change—it’s a continuous evolution. 

As AI technologies mature and evolve and more businesses realize their benefits, we can expect the landscape of intelligent apps to expand further. Organizations can cultivate a mindset of continuous learning, flexibility, and adaptation to prepare for this future. Staying ahead in this dynamic field requires organizations to keep up with the latest AI advancements, continually reassess their strategies, and pivot when necessary. 

The domain of intelligent apps is rapidly advancing. Organizations must prepare to navigate this dynamic landscape to stay ahead of the curve.

Summary

Preparing for AI and intelligent apps is a strategic decision. It requires thoughtful preparation and a culture that embraces change.

With the proper preparation and mindset, adopting AI can become a smoother, collaborative path to innovation for the whole organization. Integrating AI into your organization does not discard what has worked in the past. It simply enhances and augments those processes for a better future.

Consider the points above to assess your organization’s readiness and prepare to welcome intelligent apps.

In the first week of this series, you learned about intelligent apps, how organizations today find success through intelligent apps, what AI-assisted application development looks like, ways to leverage the power of generative AI, the technical changes needed to prepare for intelligent apps, and, in this article, the organizational changes required to harness the power of AI.

The future is intelligent. Use this series as a guideline to navigate challenges while preparing for and embracing AI's opportunities for an intelligent tomorrow.

Exercise

+ + \ No newline at end of file diff --git a/30daysofIA/tags/community-buzz/page/10/index.html b/30daysofIA/tags/community-buzz/page/10/index.html new file mode 100644 index 0000000000..6626652f0e --- /dev/null +++ b/30daysofIA/tags/community-buzz/page/10/index.html @@ -0,0 +1,27 @@ + + + + + +11 posts tagged with "community-buzz" | Build Intelligent Apps On Azure + + + + + + + + + + + + + + +
+

11 posts tagged with "community-buzz"

View All Tags

· 4 min read
It's 30DaysOfIA

Continue The Learning Journey through Fall For Intelligent Apps! 🍂

What We'll Cover

Thank you! ♥️

image

It's hard to believe that JavaScript on Azure hack-together is ending! It seems like just yesterday that we launched this initiative, and yet here we are, 15 days later, with an incredible amount of learning and growth behind us. So... it's time for a wrap!

From the bottom of our hearts, we want to thank each and every one of you for your participation, engagement, and enthusiasm. It's been truly inspiring to see the passion and dedication from this strong community, and we're honored to be a part of it. ✨

Recap of The JavaScript on Azure Global Hack-Together

As we wrap up this exciting event, we wanted to take a moment to reflect on all that we've accomplished together. Over the last 15 days, we've covered a lot of ground, from the basics of contributing to Open source to the exploration of the Contoso Real Estate project from its Frontend to its Backend and future AI implementation.

Now that the hack-together is ending, we want to make sure that you have all the resources you need to continue honing your skills in the future. Whether you're looking to make your fist contribution to open source, become an open source maintainers, collaborate with others, or simply keep learning, there are plenty of resources out there to help you achieve your goals. So, let's dive in and explore all the ways you can continue to grow your JavaScript skills on Azure!

JSonAzure Hack-together Roadmap 📍:

hack-together-roadmap (2)

Recap on past Livestreams🌟:

Day 1️⃣: Opening Keynote (Hack-together Launch): Introduction to the Contoso Real Estate Open-source project!, managing complex and complex enterprise architecture, new announcements for JavaScript developers on Azure

Day 2️⃣: GitHub Copilot & Codespaces: Introduction to your AI pair programmer (GitHub Copilot) and your virtual developer environment on the cloud (GitHub Codespaces)

Day 6️⃣: Build your Frontend using Static Web Apps as part of a complex, modern composable frontends (or micro-frontends) and cloud-native applications.

Day 9️⃣: Build a Serverless Backend using Azure Functions

Day 1️⃣3️⃣: Easily connect to an Azure Cosmos DB, exploring its benefits and how to get started

Day 1️⃣5️⃣: Being in the AI Era, we dive into the Azure OpenAI Service and how you can start to build intelligent JavaScript applications

📖 Self-Learning Resources

  1. JavaScript on Azure Global Hack Together Module collection
  2. Lets #HackTogether: Javascript On Azure Keynote
  3. Step by Step Guide: Migrating v3 to v4 programming model for Azure Functions for Node.Js Application

Continue your journey with #FallForIntelligentApps

Join us this Fall on a learning journey to explore building intelligent apps. Combine the power of AI, cloud-native app development, and cloud-scale data to build highly differentiated digital experiences. Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure. Engage in a self-paced learning adventure by following along the #30Days of Intelligent Apps series, completing the Intelligent Apps Skills Challenge, joining the Product Group for a live Ask The Expert series or building an end to end solution architecture with a live guided experience through the Learn Live series. Discover more here.

Hands-on practice: Make your first contribution to open source!

Join our GitHUb Discussion Forum to connect with developers from every part of world, see contributions from other, find collaborators and make your first contribution to a real-world project! +Don't forget to give the repo a star ⭐

Resources

All resources are accessible on our landing page

+ + + + \ No newline at end of file diff --git a/30daysofIA/tags/community-buzz/page/11/index.html b/30daysofIA/tags/community-buzz/page/11/index.html new file mode 100644 index 0000000000..2936ac972f --- /dev/null +++ b/30daysofIA/tags/community-buzz/page/11/index.html @@ -0,0 +1,26 @@ + + + + + +11 posts tagged with "community-buzz" | Build Intelligent Apps On Azure + + + + + + + + + + + + + + +
+

11 posts tagged with "community-buzz"

View All Tags

· 2 min read
It's 30DaysOfIA

September is almost here - and that can only mean one thing!! It's time to 🍂 Fall for something new and exciting and spend a few weeks skilling up on relevant tools, techologies and solutions!!

Last year, we focused on #ServerlessSeptember. This year, we're building on that theme with the addition of cloud-scale Data, cloud-native Technologies and cloud-based AI integrations to help you modernize and build intelligent apps for the enterprise!

Watch this space - and join us in September to learn more!

+ + + + \ No newline at end of file diff --git a/30daysofIA/tags/community-buzz/page/2/index.html b/30daysofIA/tags/community-buzz/page/2/index.html index 9c15f33b36..2c4a635f58 100644 --- a/30daysofIA/tags/community-buzz/page/2/index.html +++ b/30daysofIA/tags/community-buzz/page/2/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "community-buzz" | Build Intelligent Apps On Azure +11 posts tagged with "community-buzz" | Build Intelligent Apps On Azure @@ -14,13 +14,13 @@ - - + +
-

9 posts tagged with "community-buzz"

View All Tags

· 9 min read
It's 30DaysOfIA

Learn best practices for modernizing your on-premises or IaaS solutions to intelligent apps in the cloud.

What We'll Cover:

  • Traditional vs Intelligent Apps
  • On-Premises/IaaS vs. Cloud-Native Platforms
  • Modernization strategy for building Intelligent Apps

image of modernizing AI solutions for intelligent apps

Preparing the Path for Intelligent Apps: Transitioning from On-Premises/IaaS to Cloud-Native Applications

The proliferation of generative AI is paving the way for intelligent applications, software that leverages AI capabilities to deliver unparalleled functionality and user experience. As we covered in a previous article, “Demystifying Intelligent Applications: Leveraging AI in App Development,” intelligent Apps aren’t just products of advanced coding. They interact, learn, and evolve thanks to modern AI and machine learning (ML) breakthroughs. 

The paradigm shift from traditional to intelligent apps demands that we change our approach to the technical challenges of software and architecture design. It also necessitates adopting new ways of thinking and operating within an organization, which we explore in greater depth in “Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management.” 

This article explores the technical infrastructure powering intelligent apps, providing a roadmap to transition traditional on-premises or Infrastructure as a Service (IaaS) solutions to intelligent apps deployed to cloud native platforms and services. We’ll discover the differences between conventional and intelligent apps, options for re-architecting existing applications, and critical strategic considerations integral to modernizing the application-building approach. 

Understanding the Shift: Traditional vs. Intelligent Applications

Intelligent apps are more than just an incremental evolution from traditional apps. They involve a fundamental shift in software solution design, development, and deployment. A clear understanding of how each paradigm differs is crucial for comprehending the impact of this transition. 

Functionality and User Experience

Traditional applications, often rule-based and rigid, rely on preprogrammed operations. A traditional weather app, for example, typically presents structured, location-based forecasts using a predetermined data source. Meanwhile, an intelligent app leverages AI for data-driven decision-making and personalization. Rather than merely displaying the forecast, it integrates ML with additional data sources—like a user’s calendar and fitness data—to determine their favorite outdoor activities, provide relevant clothing suggestions, and even automatically design ideal vacations and times based on the user’s optimal weather preferences. 

Similarly, while traditional applications tend to offer a one-size-fits-all UI, intelligent apps use AI to create tailored interactions. For instance, an intelligent banking app might use generative AI to provide a voice-enabled UI that lets customers use natural language to ask about transactions and receive personalized financial advice.

Infrastructure

Intelligent apps integrating these sophisticated features require highly scalable, flexible infrastructure optimized for AI functionality and able to manage large volumes of real-time data. Unlike traditional applications deployed on-premises or in infrastructure as a service (IaaS) environments, intelligent apps demand the flexibility and scalability of cloud native architectures.

An on-premises or IaaS-based e-commerce application may struggle with connecting its data to AI services or scaling to accommodate voluminous real-time data and requests. But an intelligent app built with a cloud native approach and scalability at its core can handle variable demand much more efficiently. It also maintains its infrastructure components on demand, whether for collecting data for AI/ML consumption or user-facing features like AI-enabled customer support and personalization.

Consider an intelligent app built on Azure that creates ChatGPT-like experiences with custom data. It might connect Azure Open AI and Azure Cognitive Search to an app running on Azure Container Apps or Azure Kubernetes Service. 

The architecture might look like the following diagram based on this demo app:

diagram of a demo app architecture

While the cloud is well-equipped to handle the challenges associated with shifting to AI-centric infrastructure—handling real-time data processing and storage, integrating and managing AI services, configuring event-driven and microservices architectures, and ensuring security across the distributed environment—re-architecting a traditional app to an intelligent app requires careful planning and execution to ensure it benefits fully from the capabilities of AI and cloud computing.

On-Premises/IaaS vs. Cloud-Native Platforms

While on-premises and IaaS solutions provide substantial control over data and processes, this fine-grained control comes with setup and maintenance costs and may lack the scalability necessary for AI-driven Intelligent Apps. On-premises hosting involves purchasing and maintaining hardware, including servers and storage systems. IaaS involves leasing virtual hardware and then hiring and training IT personnel for the infrastructure’s operation, maintenance, and security.

Conversely, infrastructure built on cloud native app services with Microsoft Azure offers virtually unlimited scalability without ongoing hardware and maintenance costs. This feature is ideal for intelligent apps that need to adapt and scale to accommodate fluctuating workloads and data streams. And because these cloud infrastructures typically operate on a pay-as-you-go model, traditionally substantial upfront costs turn into predictable operating expenses, lowering both the cost and risk of launching applications.

Moreover, cloud service providers continually update their offerings, so building on sophisticated AI tools and services through the cloud, like the Azure AI Platform and orchestration capabilities with Azure Kubernetes Service, ensures that you have access to the latest AI and most advanced AI models and technologies.

Strategic Considerations for Transitioning to Intelligent Apps

When developing a strategy for transitioning to intelligent apps, you must consider costs, personnel skill development, and data management.

Monolithic architectures are unlikely to support the scale and ease of iteration required by intelligent apps, so you’ll likely need to implement code changes to support the transition. Your chosen hosting solution and the amount of data your app will handle affect cost and performance, in addition to determining privacy and compliance requirements.

An app that manages or stores terabytes or petabytes of data will require storage-optimized virtual machines or a managed database service hosting. Meanwhile, an app that processes video content or performs resource-intensive calculations should prioritize high-performance computing. Maximizing storage and performance can become extraordinarily costly.

Finally, existing applications shifting towards intelligent apps may encounter compatibility conflicts, depending on database type or the existing system’s underlying programming language. As you might imagine, a traditional app running on-premises and using a shared local database may need significant restructuring to support a cloud native architecture. You need plans for data migration, potential downtime, and training.

Next, let’s look at this transformation process.

Re-Architecting Monolithic Apps into Microservices

Shifting from monolithic applications to microservices is pivotal for transitioning to Intelligent Apps, and you can use this shift as an opportunity to transform your legacy software into intelligent apps that leverage the flexibility and power of microservices-enabled AI solutions.

Traditionally, a monolithic application (let’s use an online marketplace as an example) might handle all activities—from serving webpages to processing payments—within an on-premises server or a cloud server. However, this approach is challenging to scale and remains vulnerable to network failures or cyberattacks.

Breaking these applications into multiple services with specific responsibilities, such as authentication or notifications, can overcome these limitations. These services are typically deployed as microservices within container environments like Azure Kubernetes Service or serverless platforms like Azure Container Apps or Azure Functions using automated processes. This approach offers scalability, robustness, and improved performance, as each microservice can run independently and use its own database.

Shifting towards smaller, independent microservices in app architectures allows us to reap the benefits of AI-powered intelligent apps, for example, by using microservices to handle AI tasks such as processing natural language input, analyzing behavioral data, or personalizing content.

info

Watch Episode 1 and Episode 2 of the Learn Live series on how to build, test and deploy an end-to-end intelligent app solution using the Contoso Real Estate Sample.

Event-Driven Architectures: Knowing When to Use Them

Event-Driven Architectures (EDAs) can significantly enhance the performance and cost-effectiveness of intelligent apps. EDAs, typically implemented with microservices, respond in real time to events or state changes ranging from user interactions to real-time analytics.

Consider an intelligent app designed for personalized customer engagement. Instead of designing the application to check continuously for changes in user data, an EDA can react to such changes immediately, triggering appropriate microservices. For example, if users modify their preferences, an event could trigger an AI-driven recommendation microservice to update its suggestions immediately. This real-time responsiveness, enabled by services like Azure Event Grid, can improve user experiences and make intelligent apps more adaptive and proactive.

However, it’s worth noting that while EDAs provide significant benefits, they can introduce unique complexities, such as maintaining event consistency and debugging issues in event chains across multiple microservices. Therefore, it’s important to assess your application’s needs so you can understand where it can benefit from an EDA.

Conclusion

Transitioning to intelligent apps represents a shift in strategy, architecture, and infrastructure. By designing and re-architecting for cloud-based infrastructure, your apps gain scalability, cost-effectiveness, robust data management, access to sophisticated AI tools, enhanced security, and reduced operational overhead.  

But this transition extends well beyond its technical components. Organizations and the people working within them need equal attention. You can explore these facets in greater depth in “Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management.” 

The power of generative AI is here, and preparing for the move toward cloud-based intelligent applications will ensure that your apps remain performative and scalable and—most importantly—that your business is ready for the newest era of app innovation.

Exercise

- - +
Skip to main content

11 posts tagged with "community-buzz"

View All Tags

· 9 min read
It's 30DaysOfIA

Learn best practices for modernizing your on-premises or IaaS solutions to intelligent apps in the cloud.

What We'll Cover:

  • Traditional vs Intelligent Apps
  • On-Premises/IaaS vs. Cloud-Native Platforms
  • Modernization strategy for building Intelligent Apps

image of modernizing AI solutions for intelligent apps

Preparing the Path for Intelligent Apps: Transitioning from On-Premises/IaaS to Cloud-Native Applications

The proliferation of generative AI is paving the way for intelligent applications, software that leverages AI capabilities to deliver unparalleled functionality and user experience. As we covered in a previous article, “Demystifying Intelligent Applications: Leveraging AI in App Development,” intelligent Apps aren’t just products of advanced coding. They interact, learn, and evolve thanks to modern AI and machine learning (ML) breakthroughs. 

The paradigm shift from traditional to intelligent apps demands that we change our approach to the technical challenges of software and architecture design. It also necessitates adopting new ways of thinking and operating within an organization, which we explore in greater depth in “Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management.” 

This article explores the technical infrastructure powering intelligent apps, providing a roadmap to transition traditional on-premises or Infrastructure as a Service (IaaS) solutions to intelligent apps deployed to cloud native platforms and services. We’ll discover the differences between conventional and intelligent apps, options for re-architecting existing applications, and critical strategic considerations integral to modernizing the application-building approach. 

Understanding the Shift: Traditional vs. Intelligent Applications

Intelligent apps are more than just an incremental evolution from traditional apps. They involve a fundamental shift in software solution design, development, and deployment. A clear understanding of how each paradigm differs is crucial for comprehending the impact of this transition. 

Functionality and User Experience

Traditional applications, often rule-based and rigid, rely on preprogrammed operations. A traditional weather app, for example, typically presents structured, location-based forecasts using a predetermined data source. Meanwhile, an intelligent app leverages AI for data-driven decision-making and personalization. Rather than merely displaying the forecast, it integrates ML with additional data sources—like a user’s calendar and fitness data—to determine their favorite outdoor activities, provide relevant clothing suggestions, and even automatically design ideal vacations and times based on the user’s optimal weather preferences. 

Similarly, while traditional applications tend to offer a one-size-fits-all UI, intelligent apps use AI to create tailored interactions. For instance, an intelligent banking app might use generative AI to provide a voice-enabled UI that lets customers use natural language to ask about transactions and receive personalized financial advice.

Infrastructure

Intelligent apps integrating these sophisticated features require highly scalable, flexible infrastructure optimized for AI functionality and able to manage large volumes of real-time data. Unlike traditional applications deployed on-premises or in infrastructure as a service (IaaS) environments, intelligent apps demand the flexibility and scalability of cloud native architectures.

An on-premises or IaaS-based e-commerce application may struggle with connecting its data to AI services or scaling to accommodate voluminous real-time data and requests. But an intelligent app built with a cloud native approach and scalability at its core can handle variable demand much more efficiently. It also maintains its infrastructure components on demand, whether for collecting data for AI/ML consumption or user-facing features like AI-enabled customer support and personalization.

Consider an intelligent app built on Azure that creates ChatGPT-like experiences with custom data. It might connect Azure Open AI and Azure Cognitive Search to an app running on Azure Container Apps or Azure Kubernetes Service. 

The architecture might look like the following diagram based on this demo app:

diagram of a demo app architecture

While the cloud is well-equipped to handle the challenges associated with shifting to AI-centric infrastructure—handling real-time data processing and storage, integrating and managing AI services, configuring event-driven and microservices architectures, and ensuring security across the distributed environment—re-architecting a traditional app to an intelligent app requires careful planning and execution to ensure it benefits fully from the capabilities of AI and cloud computing.

On-Premises/IaaS vs. Cloud-Native Platforms

While on-premises and IaaS solutions provide substantial control over data and processes, this fine-grained control comes with setup and maintenance costs and may lack the scalability necessary for AI-driven Intelligent Apps. On-premises hosting involves purchasing and maintaining hardware, including servers and storage systems. IaaS involves leasing virtual hardware and then hiring and training IT personnel for the infrastructure’s operation, maintenance, and security.

Conversely, infrastructure built on cloud native app services with Microsoft Azure offers virtually unlimited scalability without ongoing hardware and maintenance costs. This feature is ideal for intelligent apps that need to adapt and scale to accommodate fluctuating workloads and data streams. And because these cloud infrastructures typically operate on a pay-as-you-go model, traditionally substantial upfront costs turn into predictable operating expenses, lowering both the cost and risk of launching applications.

Moreover, cloud service providers continually update their offerings, so building on sophisticated AI tools and services through the cloud, like the Azure AI Platform and orchestration capabilities with Azure Kubernetes Service, ensures that you have access to the latest AI and most advanced AI models and technologies.

Strategic Considerations for Transitioning to Intelligent Apps

When developing a strategy for transitioning to intelligent apps, you must consider costs, personnel skill development, and data management.

Monolithic architectures are unlikely to support the scale and ease of iteration required by intelligent apps, so you’ll likely need to implement code changes to support the transition. Your chosen hosting solution and the amount of data your app will handle affect cost and performance, in addition to determining privacy and compliance requirements.

An app that manages or stores terabytes or petabytes of data will require storage-optimized virtual machines or a managed database service hosting. Meanwhile, an app that processes video content or performs resource-intensive calculations should prioritize high-performance computing. Maximizing storage and performance can become extraordinarily costly.

Finally, existing applications shifting towards intelligent apps may encounter compatibility conflicts, depending on database type or the existing system’s underlying programming language. As you might imagine, a traditional app running on-premises and using a shared local database may need significant restructuring to support a cloud native architecture. You need plans for data migration, potential downtime, and training.

Next, let’s look at this transformation process.

Re-Architecting Monolithic Apps into Microservices

Shifting from monolithic applications to microservices is pivotal for transitioning to Intelligent Apps, and you can use this shift as an opportunity to transform your legacy software into intelligent apps that leverage the flexibility and power of microservices-enabled AI solutions.

Traditionally, a monolithic application (let’s use an online marketplace as an example) might handle all activities—from serving webpages to processing payments—within an on-premises server or a cloud server. However, this approach is challenging to scale and remains vulnerable to network failures or cyberattacks.

Breaking these applications into multiple services with specific responsibilities, such as authentication or notifications, can overcome these limitations. These services are typically deployed as microservices within container environments like Azure Kubernetes Service or serverless platforms like Azure Container Apps or Azure Functions using automated processes. This approach offers scalability, robustness, and improved performance, as each microservice can run independently and use its own database.

Shifting towards smaller, independent microservices in app architectures allows us to reap the benefits of AI-powered intelligent apps, for example, by using microservices to handle AI tasks such as processing natural language input, analyzing behavioral data, or personalizing content.

info

Watch Episode 1 and Episode 2 of the Learn Live series on how to build, test and deploy an end-to-end intelligent app solution using the Contoso Real Estate Sample.

Event-Driven Architectures: Knowing When to Use Them

Event-Driven Architectures (EDAs) can significantly enhance the performance and cost-effectiveness of intelligent apps. EDAs, typically implemented with microservices, respond in real time to events or state changes ranging from user interactions to real-time analytics.

Consider an intelligent app designed for personalized customer engagement. Instead of designing the application to check continuously for changes in user data, an EDA can react to such changes immediately, triggering appropriate microservices. For example, if users modify their preferences, an event could trigger an AI-driven recommendation microservice to update its suggestions immediately. This real-time responsiveness, enabled by services like Azure Event Grid, can improve user experiences and make intelligent apps more adaptive and proactive.

However, it’s worth noting that while EDAs provide significant benefits, they can introduce unique complexities, such as maintaining event consistency and debugging issues in event chains across multiple microservices. Therefore, it’s important to assess your application’s needs so you can understand where it can benefit from an EDA.

Conclusion

Transitioning to intelligent apps represents a shift in strategy, architecture, and infrastructure. By designing and re-architecting for cloud-based infrastructure, your apps gain scalability, cost-effectiveness, robust data management, access to sophisticated AI tools, enhanced security, and reduced operational overhead.  

But this transition extends well beyond its technical components. Organizations and the people working within them need equal attention. You can explore these facets in greater depth in “Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management.” 

The power of generative AI is here, and preparing for the move toward cloud-based intelligent applications will ensure that your apps remain performative and scalable and—most importantly—that your business is ready for the newest era of app innovation.

Exercise

+ + \ No newline at end of file diff --git a/30daysofIA/tags/community-buzz/page/3/index.html b/30daysofIA/tags/community-buzz/page/3/index.html index 0bef7713a2..4b882f0719 100644 --- a/30daysofIA/tags/community-buzz/page/3/index.html +++ b/30daysofIA/tags/community-buzz/page/3/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "community-buzz" | Build Intelligent Apps On Azure +11 posts tagged with "community-buzz" | Build Intelligent Apps On Azure @@ -14,13 +14,13 @@ - - + +
-

9 posts tagged with "community-buzz"

View All Tags

· 10 min read
It's 30DaysOfIA

Explore how digital natives and Independent Software Vendors (ISVs) can transform their business operations and generate higher value for customers with generative AI and intelligent apps.

What We'll Cover:

  • Potential of Generative AI in application development
  • Challenges with Generative AI
  • Integrating Generative AI with business operations

image of generative AI and intelligent apps used in business

Unleashing Business Transformation: How Digital Natives Can Leverage Generative AI

Generative AI is no longer a technological flight of fancy. It’s here, and it’s rewriting the rules of business innovation. 

This type of AI leverages machine learning algorithms to create new data with similar characteristics to a given dataset. It goes beyond text generation using generative pretrained transformer (GPT) models, covering an array of applications that span from image synthesis to translations and more. It also powers a new class of intelligent applications, which combine AI capabilities with traditional software functions for enhanced user experiences and advanced business solutions. 

Generative AI is transforming how digital natives and independent software vendors (ISVs) operate. Generative AI offers new possibilities for creating unique business value, enhancing customer experiences, and driving growth. 

It plays a critical role in applications, improving functionality, aiding decision-making, and automating complex tasks to deliver dynamic, uniquely personalized user experiences. Generative AI also enables and automates data-driven outcomes, functions, and features.

Integrating AI capabilities into customer-facing apps and turning them into intelligent apps benefits both the customer and the business, as we discussed in Demystifying Intelligent Applications: Leveraging AI in App Development. You get faster product development workflows and better business intelligence for sales and marketing, while your users get more natural and context-aware features and interactions. 

That said, to realize generative AI’s potential, you need some strategy and knowledge of how and where it fits into your current business and operations. We’ll examine this technology’s potential to transform business operations, its risks, and rewards, and how to integrate it into your existing operations. 

Unpacking the Potential of Generative AI

Generative AI is creating new opportunities in the digital playing field. It empowers businesses to generate insights, design products, and automate processes in ways and at speeds that were previously unimaginable. You can now predict customer behavior more accurately, offer highly personalized user experiences, and prototype innovative ideas rapidly. 

For example, Microsoft used generative AI to reinvent web search in the new AI-powered Bing and Edge. The search experience has evolved from a solitary user flow of typing in keywords and scrolling through results. It’s now a conversational experience where Bing uses context to offer better quality and more personalized search queries and results. 

Similarly, Microsoft harnessed generative AI to transform its productivity suite into intelligent apps. Through Copilot for Microsoft 365, PowerPoint now empowers users to tell their stories by using DALL-E to generate images effortlessly. It also helps rephrase text-heavy slides to be more straightforward. Outlook now assists users to compose more effective emails, enabling better communication.

These are just some examples of how AI powers day-to-day work and operations. 

Businesses that ignore generative AI miss out on untapped market opportunities, letting competitors gain an edge in the digital race. By seeking ways to rethink assumptions and reinvent existing approaches, you can effectively use generative AI to create new opportunities, open products to new use cases and markets, unlock new growth and customer satisfaction levels, and increase your revenue.

info

Register for the intelligent apps webinar on Driving Business Value by Modernizing with Cloud-Native & AI with Microsoft and Forrester on September 26.

Explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries. 

The Crucial Role of Generative AI in Business Operations

Generative AI can be a powerful catalyst for business transformation. Let’s quickly tour some ways it can redefine business operations. 

Product development is traditionally a long and painstaking process. Now, tools like GitHub Copilot and TestPilot use generative AI to make coding and testing software easier, faster, and more robust. 

Similarly, machine learning as a service technology, such as Azure Machine Learning, democratizes the availability of machine learning. It helps collect, clean, and train models across datasets, making engineering and analyzing data more efficient and accessible.  

Generative AI also simplifies the process of creating intelligent apps. Previously, simulating thousands of prototypes, predicting their performance, and narrowing down to the most viable options could take weeks, months, or even years of iteration and data analysis. With generative AI, this process can be as quick as days, hours, or perhaps minutes. 

Customer experience is also ripe for transformation. Personalized recommendations drive engagement and sales, and generative AI can scale that personalization to every customer interaction using comprehensive and continually updated data. For instance, Microsoft leverages AI to enable personalization in Dynamics 365 Customer Insights. Using this tool, you can ask questions about your customers in natural language and gain valuable and actionable insights into your customer base. 

Lastly, marketing and sales can benefit from generative AI. For example, identifying current and future key markets and emerging trends helps companies stay ahead of the curve. Tools like Copilot in Power BI help you quickly understand and uncover insights about your business, enabling your team to craft targeted campaigns, create engaging content, and optimize your business strategy. 

Leveraging this full generative AI potential requires the right tools. GPT-4, for instance, can be instrumental in realizing effective business transformation. It excels in understanding context, generating human-like text, and learning from minimal data input, making it a formidable tool in any digital native's arsenal. Platforms like Azure OpenAI Service bring GPT-4’s power to your fingertips.

However, as with any powerful technology, it’s unwise to treat generative AI as a perfect solution. You must know the challenges and risks of not leveraging it effectively, including technical and human-factor considerations. 

Without generative AI, your company risks missing the opportunity to please customers via personalization, as discussed previously. You’ll miss sales opportunities via business intelligence insights, limiting your ability to grow revenue and keep up with the competition. 

One challenge of leveraging generative AI’s full potential is finding the right tools. Fortunately, you can implement generative AI in your organization without investing in infrastructure, computational power, and expertise. Platforms like Azure OpenAI Service and Azure AI Services make AI easily accessible. They also work with Azure compute and data services like Azure Functions and Azure Kubernetes Service (AKS), enabling you to build intelligent apps easily and quickly. 

However, generative AI can’t do everything. It may need human expertise and intervention to use it fully. 

For example, generative AI can create unique data. However, the data may have limited diversity and complexity. The AI may need help recognizing additional considerations or requirements specific to a problem. It may also lack context due to various factors, such as the information’s freshness. Also, since it’s an emerging field that’s potentially worth $4.4 trillion, we’re still learning about the nuances and potential regulatory impact of generative AI. 

Perhaps the most crucial and challenging aspect of adopting generative AI is an organization’s required cultural shift. Your teams must learn how to use AI as your company fosters an understanding of AI’s potential and limitations. 

Begin integrating AI literacy into your company’s DNA. Knowledge of its potential and the readily available tools can inspire your teams to incorporate generative AI into their daily workflows and business operations quickly, improve productivity, and deliver superior customer experiences. 

No company wants to be outpaced by its competitors. You can balance generative AI’s potential with your business operations by staying aware of and adapting to the dynamic AI landscape.

Achieving Harmony: Integrating Generative AI with Existing Business Operations

Integrating generative AI into existing business operations doesn’t have to be disruptive. With the correct strategy, you can do so seamlessly. Your goal should be to augment and enhance your operations, not disrupt and replace. 

For example, Microsoft Cloud technologies leveraged AI to relieve healthcare organizations’ challenges, such as talent shortage and workforce burnout. Providers automated their workflows, invested in specific applications and connectors, and enhanced support for data and operations. 

Azure AI Services, such as text analytics for health, enable healthcare organizations to understand their largely unstructured and untapped medical data. Project Health Insights leans on AI to help healthcare professionals gain actionable insights and inferences from patient data. 

Azure Health Bot, an Intelligent App, empowers organizations to build and deploy legally compliant, powerful conversational healthcare experiences to patients. 

A successful AI integration like this begins with understanding. Knowing what generative AI can do and setting realistic expectations can help you identify where and how AI can best serve your operations. 

Next, you can foster a culture of AI literacy across teams, speeding up adoption by helping them understand how it can aid their work. The more familiar your team is with AI and its possibilities, the smoother the transition. Training and education programs play a pivotal role here. 

Finally, integration requires a systematic approach. It’s not about overhauling everything at once — it's about identifying areas where AI can add the most value, then gradually experimenting and scaling its use. Successful integration stories often feature a phased approach, starting small and growing steadily. 

As we move toward a future where generative AI is a staple in business operations, remember that every journey is unique. Although there’s no one-size-fits-all solution, a strategic approach and a keen understanding of your business needs will help you navigate this new landscape successfully.

Conclusion

Generative AI offers plenty of opportunities for businesses that embrace it. It’s fueling the next generation of intelligent applications, transforming everything from product development to customer engagement to marketing. This technology is the key to unlocking unprecedented growth and innovation. 

While the journey may have challenges, the potential rewards make generative AI worth exploring. As generative AI advances, Azure AI’s broad range of tools and services will help you navigate this fascinating field.

Exercise

  • Watch Episode 02 of the serverless edition Learn Live session to learn how to build, test and deploy an end-to-end intelligent app solution.
  • Complete the Cloud Skills Challenge to build on your AI skills.
  • Register for Ask the Expert: Azure Functions on September 26 for a live Q&A with the Product Team on how to build AI apps using serverless computing.
- - +
Skip to main content

11 posts tagged with "community-buzz"

View All Tags

· 12 min read
It's 30DaysOfIA

Delve into creating an Intelligent App that leverages Azure AI Vision to analyze images and extract data. Develop an API to perform optical character recognition (OCR) on uploaded images and testing this API locally.

What We'll Cover:

  • Understanding Azure AI Vision and Azure Kubernetes Service
  • Build a Python Web API to perform OCR
  • Test the API locally

image depicting an Intelligent App using AI

Jumpstart Your AI Journey: Building Your First Intelligent App with Azure AI and AKS (1)

An Intelligent App is a software application that uses artificial intelligence (AI), such as machine learning (ML), to extract meaningful insights, enabling users to make informed decisions based on the extracted text, data, sound, images, and more.

Intelligent Apps can employ advanced algorithms and models to recognize objects, patterns, and features within images, allowing them to perform tasks like object identification, image classification, facial recognition, and image segmentation.

To explore the power of Intelligent Apps, let’s build a Python app that performs optical character recognition (OCR) on images containing numbers and performs aggregate functions on the extracted numbers, like sum and average. We’ll leverage Azure AI Services for the OCR functionality and Azure Kubernetes Service (AKS) to develop, publish, and maintain a cloud-based, scalable app in Azure.

Let’s get started!

Understanding Azure AI Vision and Azure Kubernetes Service

Azure AI Vision is a cloud-based service that offers a range of advanced computer vision capabilities enabling applications to interpret content, such as images and videos, using ML and other AI algorithms. Azure computer vision lets users integrate powerful vision features into their projects without ML experience.

OCR is one of the critical functionalities within Azure vision service, allowing applications to extract text from images. Within Azure AI Vision service, OCR offers several features, including text extraction, multilingual support, layout analysis of documents, and integration with Azure services, such as Azure Functions and Azure Machine Learning.

info

Watch the intelligent apps webinar on Driving Business Value by Modernizing with Cloud-Native & AI with Microsoft and Forrester on September 26.

AKS is Microsoft Azure’s container orchestration platform that enables us to quickly and efficiently deploy, manage, and scale containerized applications, such as our Intelligent App.

Let’s explore how to leverage these tools to build an Intelligent App that uses Azure AI Vision to analyze images and extract data.

Prerequisites

To follow this tutorial, ensure you have the following:

For a preview of this final project, take a look at the complete project code.

Building the API with Azure AI Vision Service

First, log in to your Azure account and navigate to the Azure Portal.

Click Create a resource and search for “resource group.” Create a new resource group named computer-vision.

image in Azure Portal of creating a new resource group

Return to the Azure Portal home page and click Create a resource.

image in Azure Portal of creating a new resource

Search for “computer vision” and select it from the results. Click Create.

image of searching for resource group in Azure Portal

Clicking Create displays the Create Computer Vision wizard. Select the resource group you just created as the Resource group from the dropdown. Enter a resource name similar to what is seen in the screenshot below. Note that this must be a unique name so choose one that is not currently in use. Select Standard S1 as the pricing tier and check the Responsible use of AI box in the field at the bottom.

Image of selecting the Responsible use of AI box

Image of project details

Click Review + Create, then click Create.

Next, get the API key and endpoint from the Azure AI services multi-service account you created. Once the resource is created, navigate to the resource details page. Under Resource Management, select Keys and Endpoint. Once on the Keys and Endpoint page, you’ll find the Key 1 and Endpoint values. These credentials are necessary to access the Azure AI APIs.

image of selecting Keys and Endpoint

second image of selecting Keys and Endpoint

Configuring the Local Environment Variables

Next, we need to define a local environment variable file. This convenient way to store our API keys in a simple text file enables us to manage sensitive information and separate configuration from our API code.

If you haven’t done so already, clone the starter project template from GitHub into your local computer. Open the starter project template in a code editor and create a new .env file in the root folder.

Note: Root folder in this case is the “/Microsoft_Series17-18_Code/intelligent-app-before" folder.

Add the following key-value pairs to the file, remembering to replace the placeholder values with those you obtained from the previous step.

VISION_KEY=<THE-KEY-1-VALUE-FROM-YOUR-AZURE-AI-SERVICE>
VISION_ENDPOINT=<THE-ENDPOINT-VALUE-FROM-YOUR-AZURE-AI-SERVICE>

Reviewing the Quickstart Code

Let’s review the app.py file in the starter project template.

The app.py file works as the entry point for the Flask application. It is responsible for initializing the web server, listening for incoming requests, dispatching them to the appropriate route handlers, and generating responses.

Below is the code contained in the app.py file:

import os
import json

from flask import Flask, request
from flask_restful import Resource, Api
from werkzeug.utils import secure_filename

app = Flask(__name__,
static_url_path='',
static_folder='static/files')

api = Api(app)

app = Flask(__name__)

app.config['UPLOAD_FOLDER'] = 'files'

api = Api(app)

class UploadHandler(Resource):

def allowed_file(self, filename):
return '.' in filename and \
filename.rsplit('.', 1)[1].lower() in {'png'}

def post(self):
form = request.form.to_dict()

if 'file' not in request.files:
return json.dumps({ "success": False, "error": "No file part"})

file = request.files.get("file")
if file and self.allowed_file(file.filename):
filename = secure_filename(file.filename)
upload_folder = "static/files"
if not os.path.exists(upload_folder):
os.makedirs(upload_folder)
local_file_path = os.path.join(upload_folder, filename)
file.save(local_file_path)

return f"File {filename} uploaded successfully to folder: {upload_folder}"

api.add_resource(UploadHandler, "/")

if __name__ == '__main__':
app.run(debug=True)

The UploadHandler class above is defined as a Flask RESTful resource to handle file uploads. The class contains two methods:

  • The allowed_file method checks whether the file extension is allowed for upload. In this case, only .png files are allowed.

  • The post method handles HTTP POST requests for file uploads. It saves the uploaded .png file to the static/files folder.

Finally, the application returns a text response informing us that the file has been uploaded successfully.

Implementing the REST API in Python

To implement image analysis in your REST API, open the starter project template in your terminal, create a virtual environment, and activate it.

Next, install the Azure AI Vision SDK:

pip install azure-ai-vision

Then, add the following line to the requirements.txt file to include the azure-ai-vision package:

azure-ai-vision==0.13.0b1

Now, create an ocr_helper.py file in the project’s root folder. This file is a Python module that provides functions for processing images using OCR. Add the following code to the file:

import os
from statistics import median
from decimal import Decimal
import azure.ai.vision as sdk

def process_ocr(source_image):
service_options = sdk.VisionServiceOptions(os.environ["VISION_ENDPOINT"],
os.environ["VISION_KEY"])

vision_source = sdk.VisionSource(filename=source_image)

analysis_options = sdk.ImageAnalysisOptions()

analysis_options.features = (
sdk.ImageAnalysisFeature.CAPTION |
sdk.ImageAnalysisFeature.TEXT
)

analysis_options.language = "en"

analysis_options.gender_neutral_caption = True

image_analyzer = sdk.ImageAnalyzer(service_options, vision_source, analysis_options)

result = image_analyzer.analyze()

ocr_result = get_ocr_result(result)

return ocr_result

def get_ocr_result(result):
string_list = []

if result.reason != sdk.ImageAnalysisResultReason.ANALYZED:
return sdk.ImageAnalysisErrorDetails.from_result(result)
else:
if result.text is not None:
for line in result.text.lines:
for word in line.words:
string_list.append(word.content)

number_list = convert_to_decimal_list(string_list)

aggregate_result = aggregate_operations(number_list)

return {
"aggregate_result": aggregate_result,
"numbers_read": string_list
}

def convert_to_decimal_list(string_list):
return list(map(Decimal, string_list))

def aggregate_operations(numbers):
result = {
'sum': sum(numbers),
'average': sum(numbers) / len(numbers),
'median': median(numbers),
'min': min(numbers),
'max': max(numbers)
`}`
`return result`

This module uses the azure-ai-vision package to analyze images, including capturing captions and extracting text from the image. The process_ocr function inputs an image file and performs the OCR analysis. The module is a convenient tool for analyzing text-based data in pictures and performing numerical computations based on that data.

Let’s review the different components of the ocr_helper.py module listed above and explore how its functions help our Intelligent App process images and perform aggregate operations:

  • The process_ocr function takes the parameter source_image, which is the path of the image to be processed. The function then initializes the VisionServiceOptions using environment variables VISION_ENDPOINT and VISION_KEY to connect to the Azure AI Vision API.
  • The process_ocr function creates a VisionSource object with the specified source_image file name. ImageAnalysisOptions specify the features to be analyzed, including CAPTION and TEXT. The language is set to English ("en"), and gender-neutral captions are enabled.
  • Finally, an ImageAnalyzer object is created with the service options, vision source, and analysis options. The image is then analyzed using the image_analyzer.analyze method to retrieve OCR results. The recognized text from the OCR result is extracted, with numerical values stored in the string_list variable.
  • The convert_to_decimal_list function converts the list of strings to a list of decimal numbers, which helps process numeric values extracted from the text.
  • The aggregate_operations function calculates various aggregate statistics (sum, average, median, minimum, maximum) from a list of numbers and returns the results in a dictionary.

Note that you must have the appropriate credentials (VISION_KEY) and endpoint (VISION_ENDPOINT) configured for the Azure AI Vision API to use this module.

Finally, we must modify the app.py file so our code can use the process_ocr function of the ocr_helper.py file.

Add the following import statement to the app.py file:

from ocr_helper import process_ocr

Then, replace this line:

return f"File {filename} uploaded successfully to folder: {upload_folder}"

With these two lines:

aggregates = process_ocr(local_file_path)
return json.dumps(aggregates, default=str)

Doing so allows our application to perform aggregate operations (sum, average, median, minimum, maximum) of any numeric values found in the text and return the operation’s result as a JSON response.

Running the Intelligent App Locally

Our final goal is to deploy the sample app to AKS, which will run the app in a container. However, as a good practice, let’s run the app in a container locally before moving it to AKS. Doing so ensures that our application and its dependencies are encapsulated and can be run consistently across different environments.

Deploying to Docker Desktop

For our application to run on Docker, it needs two additional files: a Dockerfile and docker-compose.yml. A Dockerfile creates a single container image by specifying the steps to build it, and a docker-compose.yml file manages multiple containers as a cohesive application, defining their configurations and relationships. Dockerfile builds the image, while docker-compose.yml orchestrates multi-container applications.

In the project root folder, add a file named Dockerfile with the content below:

# syntax=docker/dockerfile:1

FROM python:3.8-slim-buster

WORKDIR /intelligentapp

COPY requirements.txt requirements.txt
RUN pip3 install -r requirements.txt

RUN pip install debugpy

COPY . .

CMD [ "python3", "-m" , "flask", "run", "--host=0.0.0.0"]

Now create a file named docker-compose.yml file in the project’s root folder with the following code, replacing the VISION_KEY and the VISION_ENDPOINT according to the environment variables you configured earlier:

version: '3.8'
services:
intelligentapp:
build:
context: .
dockerfile: Dockerfile
image: intelligent-app
ports:
- 5000:5000
container_name: intelligent-app
environment:
- VISION_KEY=<THE-KEY-1-VALUE-FROM-YOUR-AZURE-AI-SERVICE>
- VISION_ENDPOINT=<THE-ENDPOINT-VALUE-FROM-YOUR-AZURE-AI-SERVICE>

Then, run the following command in the terminal to build the image and start a container for the Intelligent App services defined in the docker-compose.yml file:

docker-compose up --build --force-recreate

Next, open a new terminal and run the command below to list the image deployed to your local Docker:

docker images

docker images

Testing the Intelligent App Locally

Now, we’ll test the Intelligent App’s functionality using the following test image:

test image to test functionality

This image is provided as sample1.png in the sample app source files, so you can easily use it in the next step.

The Intelligent App will read the image above using OCR to extract numbers in the image and perform five aggregate functions:

  • Sum
  • Average
  • Median
  • Min
  • Max

To test the API, open Postman and fill out the fields as follows:

  • URL: http://localhost:5000/
  • Method: POST
  • Body:
    • Form-data
      • Key: File — Click the right end of the Key field and select File from the dropdown list.
      • Value: Click Select Files, then select the sample1.png file provided in the sample code.

image of selecting the sample file

Now click the Send button and review the result body:

"{\"aggregate_result\": {\"sum\": \"25821\", \"average\": \"5164.2\", \"median\": \"5622\", \"min\": \"1447\", \"max\": \"9802\"}, \"numbers_read\": [\"3049\", \"5901\", \"5622\", \"1447\", \"9802\"]}"

As we can see, the app running on a local container returned the correct results based on the sample image. So, we’re ready to prepare our Azure resources to run the Intelligent App on AKS.

Exercise

In this article, we explored the creation of an Intelligent App that leverages Azure AI Vision to analyze images and extract data. We learned how to build a Python Web API to perform OCR on uploaded images and subsequently test this API locally.

Head to the next part, to deploy this API via Azure Kubernetes Service.

+ + \ No newline at end of file diff --git a/30daysofIA/tags/community-buzz/page/4/index.html b/30daysofIA/tags/community-buzz/page/4/index.html index 38eb577683..fff8b5b54b 100644 --- a/30daysofIA/tags/community-buzz/page/4/index.html +++ b/30daysofIA/tags/community-buzz/page/4/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "community-buzz" | Build Intelligent Apps On Azure +11 posts tagged with "community-buzz" | Build Intelligent Apps On Azure @@ -14,13 +14,13 @@ - - + +
-

9 posts tagged with "community-buzz"

View All Tags

· 9 min read
It's 30DaysOfIA

In the part of our series on intelligent apps, we’ll explore how AI is transforming application development, from design and architecture to building.

What We'll Cover:

  • Revolutionizing application development using AI
  • Infusing AI in application design architecture
  • AI Assisted Pair Programming
  • Future of App Development

A decorative image of application development with AI

Reimagining Application Development With AI: A New Paradigm

Artificial intelligence (AI) is impacting every industry. AI-powered development tools offer new functionalities and assist software developers throughout the development process, revolutionizing app development. From initial design and architecture to coding and debugging, AI reshapes how we approach application conception, creation, and iteration.

In part one of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” we explored the new breed of intelligent apps and how they use AI. In this article, we’ll see how AI technologies enhance developer toolkits and shape the future of app development, particularly through AI-enabled coding assistance and debugging.

The Dawn of Intelligent Apps: Revolutionizing Application Development

AI is more than just another addition to the development toolbox—it represents a revolution bringing unprecedented efficiency, innovation, and user engagement to our applications. 

What sets intelligent apps apart is how they can adapt and learn, helping us deliver unique, real-time user experiences.  

One example is an AI-driven health application that can track biometric data, predict potential health issues, provide personalized health advice, and even guide emergency services during a medical crisis. An AI-enabled language learning app that adapts to our learning speeds, recognizes our problem areas, and customizes its content to help us improve our language proficiencies is another example.  

Intelligent apps like these don’t just use data for recommendations—they can make critical decisions and provide sophisticated services that were previously impossible. 

For developers, intelligent apps mean we’re no longer just coding—we’re teaching applications how to think, adapt, and make decisions. AI is reshaping every step of the development journey, helping us to build more intuitive and user-focused products. 

Next, we’ll examine the impact of AI on application design and architecture and how AI-assisted “pair programming” changes software development.

info

Register for the FREE webinar on Intelligent Apps with Microsoft and Forrester on September 26 to explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries.

There will be a showcase of real-world use cases that demonstrate how AI can be seamlessly integrated into cloud-native environments driving tangible business value.

AI in Design and Architecture: Fueling Creativity and Efficiency

AI technologies help developers automate and streamline their processes and provide new ways to think about design.  

In traditional application development, the design and architecture phase can take significant time, effort, and expertise. For instance, adopting a monolithic architecture or a microservices-based design can significantly increase coding, testing, and debugging complexity. The app may even behave differently when hosted locally than in a deployed environment. It may take hours to track down an issue manually—time developers could spend elsewhere. 

Conversely, we can generate numerous design iterations in seconds with AI, each optimized to fulfill specific user needs and business goals. AI tools can quickly model various scenarios' impacts and software design patterns in the architectural design phase. It provides insights into performance and potential issues and suggests improvements. AI can also generate skeleton code, allowing developers to prototype different solutions or test specific components and technologies rapidly. By reviewing thousands of lines of code in seconds, AI allows us to focus on creating apps that serve users through feature development instead of searching for bugs.  

Integrating AI into the design and architecture phase can even extend our creative capabilities. By leveraging the power of generative AI—like large language models (LLMs)—and their broad knowledge and context going beyond software, we can better brainstorm apps and feature ideas. We can go beyond code and software architecture and get ideas about user experience, logo and branding, or marketing concepts. And as AI tools deliver insights and ideas we would have never considered; it encourages more innovation.

info

Watch Episode 01 of the Learn Live session to learn how to build an end-to-end intelligent app solution architecture.

The Power of AI-Assisted Pair Programming in Building Applications

AI-assisted pair programming is a collaborative coding approach where an AI system—like GitHub Copilot or TestPilot—assists developers during coding. It’s an increasingly common approach that significantly impacts developer productivity. In fact, GitHub Copilot is now behind an average of 46 percent of developers’ code and users are seeing 55 percent faster task completion on average. 

For new software developers, or those interested in learning new skills, AI-assisted pair programming are training wheels for coding. With the benefits of code snippet suggestions, developers can avoid struggling with beginner pitfalls like language syntax. Tools like ChatGPT can act as a personal, on-demand tutor—answering questions, generating code samples, and explaining complex code syntax and logic. These tools dramatically speed the learning process and help developers gain confidence in their coding abilities. 

Building applications with AI tools hastens development and provides more robust code. For example, GitHub Copilot helps us write new code accurately. It also quickly makes sense of existing code, suggests whole new lines or blocks of code, and even detects errors and proposes fixes. It’s like having a second coder working alongside us—one who never tires and is trained on billions of lines of code. 

However, it’s also important to acknowledge the limitations of AI. While generative AI-assistance technologies like GitHub Copilot are impressive, they’re still imperfect. They can sometimes produce erroneous, non-functional, or vulnerable code. They can also lack nuanced, domain-specific knowledge.  

So, although these tools are helpful, we can’t rely on them fully—these are programming aids rather than definitive solutions. AI-assisted pair programming is about augmenting the human developer’s skills, not replacing them.

Debugging and Improving Code With AI: Enhancing Application Quality

Identifying and correcting code errors is often time-consuming and challenging for developers. AI streamlines the debugging process by automatically detecting anomalies, suggesting fixes, and learning from previous bugs to prevent them from recurring. 

We can use generative AI to create unit tests from code snippets, from simple parameter checking and input validation to complex mocks of database and network services. Automated AI testing improves code coverage and robustness. It saves significant time compared to manually crafting unit tests. 

AI also revolutionizes code reviews by scanning and highlighting potential issues—like code patterns leading to a bug or a coding standards violation—and suggesting ways to simplify or refactor the code for readability and performance. This reduces human error and makes the code review process more efficient. Its benefits extend to small teams, where the availability of developers is crucial to deadlines, and large teams, where effective collaboration depends on readability and code consistency. AI further saves time by triaging issues so developers can prioritize the most mission-critical ones. 

Aside from improving code quality, AI is critical to understanding system behavior. Intelligent AI-assisted software development tools can monitor and audit system behavior, giving developers a better insight into how their code performs in different scenarios. AI can track patterns, identify inefficiencies, and provide alerts when they detect unusual behavior—enabling developers to enhance their code further. 

By predicting and identifying potential vulnerabilities, intelligent AI-assisted coding apps like Microsoft Security Copilot are pivotal in ensuring application functionality, reliability, and security. These AI tools are continually trained and upgraded with large codebases and user feedback that refines their responses. This ability to learn from past mistakes makes AI an incredibly powerful tool that will continue to expand its capabilities in the future.

info

Complete the AI Cloud Skills Challenge to build on you AI application development skills with paired programming using generative AI and GitHub Copilot.

AI and the Future of Application Development

The rapid advancements in AI and machine learning technologies point to an exciting future for application development. 

In “The Future of Applications,” software developer Mike Cann illustrates how LLMs might revolutionize application development. Cann that suggests fully-autonomous coding—AI-generated applications created entirely from prompts—may supersede traditional hand-crafted applications. Dynamically generated user interfaces could offer personalized and customizable experiences while we can design APIs for machine consumption and efficiency rather than human readability. Furthermore, we can store data in less structured, more adaptable, and flexible forms. 

AI is already making application development more accessible. With AI handling the complex, technical aspects of development, less-technical individuals can take a greater role in building applications. Consequently, AI can potentially democratize software engineering. 

Intelligent apps will continue to evolve, becoming more efficient, adaptable, and capable of learning from user interactions. Similarly, AI will inevitably become integral in every stage of app development.

Summary

AI has already created a new development paradigm—one in which machine intelligence and human creativity come together to produce innovative, efficient, and robust applications. AI already assists us at every stage of app development, from design and architecture to coding and debugging. 

Developers who embrace AI tools and integrate them into their processes can enhance their productivity, improve the quality of their applications, and expand the possibilities of their application development.  

The future of development is automated and AI-driven, and it’s here today. Getting started is easy—try an AI-powered development tool like GitHub Copilot to see how AI-assisted pair programming can enhance your productivity and code quality.

Exercise

- - +
Skip to main content

11 posts tagged with "community-buzz"

View All Tags

· 9 min read
It's 30DaysOfIA

In the last article, we created an Intelligent App that leverages Azure AI Vision to analyze images and extract data. Develop an API to perform optical character recognition (OCR) on uploaded images and testing this API locally.

In this article, we will deploy the web API to the cloud platform using Azure Kubernetes Service.

What We'll Cover:

  • Understanding Azure AI Vision and Azure Kubernetes Service
  • Build a Python Web API to perform OCR
  • Test the API locally

image depicting an Intelligent App using AI

Jumpstart Your AI Journey: Building Your First Intelligent App with Azure AI and AKS (2)

In the previous article we explored the creation of an Intelligent App that leverages Azure AI Vision to analyze images and extract data. We learned how to build a Python Web API to perform OCR on uploaded images and subsequently test this API locally.

In this article we will use Azure Kubernetes Service (AKS) to develop, publish, and maintain our app in the cloud on Azure.

Let’s get started!

Prerequisites

To follow this tutorial, ensure you have completed Jumpstart Your AI Journey: Building Your First Intelligent App with Azure AI and AKS (1).

Pushing a Container Image to Azure Container Registry (ACR)

To start, open your CLI or terminal and type the following command:

az login

Follow the instructions displayed on your browser to enter your Azure credentials.

Once authenticated, you’ll initiate a secure connection between your local environment and Azure. This process grants you access to cloud services and resources.

Next, type the command below in your terminal to set up a new Azure Container Registry (ACR) to store your container images:

az acr create --resource-group computer-vision --name <name-of-azure-container-registry> --sku Basic

Remember to replace <name-of-azure-container-registry> with your container registry name. The name must be unique within Azure and comply with these rules.

The command above creates an Azure Container Registry (ACR) in the computer-vision resource group under the Basic SKU. This ACR is your secure and private repository for storing container images within Azure.

Next, log in to the registry with the following command:

az acr login -n <name-of-azure-container-registry>

The az acr login command above lets you securely authenticate and access the specified ACR without providing their Azure credentials each time.

Now, run the following command in your terminal. It will display the endpoint URL to log in to and interact with the ACR for pushing and pulling container images.

az acr show --name <name-of-azure-container-registry> --query loginServer --output table

This returns an endpoint URL as follows:

Result
----------------------------------
<name-of-azure-container-registry>.azurecr.io

Now, run the following command to show all container images, their repository, tags, and size:

docker images
REPOSITORYTAGIMAGE IDCREATEDSIZE
intelligent-applatesta7bf9f75361716 hours ago197MB

Tags are necessary to push Docker images to a remote registry like Azure Container Registry. They also let you differentiate between different versions of the same image and upload or download the one you want.

Run the following command to tag your Docker image:

docker tag intelligent-app <name-of-azure-container-registry>.azurecr.io/intelligent-app:v1

Then, run the docker images command again to check your tagged image:

docker images
REPOSITORYTAGIMAGE IDCREATEDSIZE
intelligent-applatestc52168039265About a minute ago197MB
<name-of-azure-container-registry>.azurecr.io/intelligent-appv1c52168039265About a minute ago197MB

Now run the following command so Docker can securely upload the image to your Azure Container Registry:

docker push <name-of-azure-container-registry>.azurecr.io/intelligent-app:v1

Once we've deployed the image to the container registry, AKS can access it during deployment.

info

Watch Episode 03 for the Learn Live session to experience a guided session on how to build, test and deploy an end-to-end intelligent app solution.

Deploying the Intelligent App on Azure Kubernetes Service (AKS)

Before we deploy our Intelligent App to AKS, we need to provision an AKS cluster and define Kubernetes manifests.

To provision an AKS cluster to host our application, we specify the desired configurations for the cluster, such as the number of nodes, node size, and networking options. But first, download and install the Kubernetes command-line tools (kubectl), a client credential plugin implementing Azure authentication:

az aks install-cli

If you’re using Linux, review this tutorial. Then run the following:

sudo az aks install-cli

Next, run the following command in your terminal to enable access to the networking-related resources and services provided by the `Microsoft.Network`` namespace in Azure:

az provider register --namespace Microsoft.Network

Now, we must create an AKS cluster. Run the following command to create an AKS cluster named aks-intelligent-app in the computer-vision resource group.

az aks create --resource-group computer-vision --name aks-intelligent-app --node-count 1 --generate-ssh-keys

The command above specifies the target resource group: computer-vision. The node pool is configured with one virtual machine (VM), and the secure shell (SSH) keys for secure node access are generated automatically.

Next, run the following command to update the AKS cluster you created by attaching it to your ACR. Doing so allows the AKS cluster to pull container images from the specified ACR when deploying workloads to the cluster.

az aks update -n aks-intelligent-app -g computer-vision --attach-acr <name-of-azure-container-registry>

Then, run the command below to configure kubectl to work with our AKS cluster in the computer-vision resource group.

az aks get-credentials --resource-group computer-vision --name aks-intelligent-app

The command above retrieves the necessary credentials and context information required for kubectl to communicate with the AKS cluster.

We still have to define the Kubernetes manifests written in YAML, describing the desired state of our application deployment, including containers, networking, and scaling rules. We’ll prepare these manifests, including Deployment and Service configurations, to define how our application should be deployed and exposed.

Create a folder named Deployment in the project root directory. Next, create two files in the deployment folder: deployment.yml and service.yml.

Add the following configuration to the deployment.yml file, replacing the <name-of-azure-container-registry> placeholder with your registry’s name:

apiVersion: apps/v1
kind: Deployment
metadata:
name: intelligent-app
spec:
replicas: 1
selector:
matchLabels:
app: intelligent-app
template:
metadata:
labels:
app: intelligent-app
spec:
nodeSelector:
kubernetes.io/os: linux
containers:
- name: intelligent-app
image: <name-of-azure-container-registry>.azurecr.io/intelligent-app:v1
resources:
limits:
memory: 512Mi
cpu: "1"
requests:
memory: 256Mi
cpu: "0.2"
ports:
- containerPort: 5000
env:
- name: FLASK_DEBUG
value: "1"
- name: VISION_KEY
value: <THE-KEY-1-VALUE-FROM-YOUR-AZURE-AI-SERVICE>
- name: VISION_ENDPOINT
value: <THE-ENDPOINT-VALUE-FROM-YOUR-AZURE-AI>

Additionally, edit the VISION_KEY and VISION_ENDPOINT environment variables above according to the API key and endpoint of your Azure AI Services instance.

Then, add the following configuration to the service.yml file:

apiVersion: v1
kind: Service
metadata:
name: intelligent-app-service
spec:
type: LoadBalancer
ports:
- protocol: TCP
port: 80
targetPort: 5000
name: port5000
selector:
app: intelligent-app

Now, we’ll deploy our application using kubectl. Applying the Kubernetes manifests creates the necessary resources and deploys our containerized application to the AKS cluster.

First, change the terminal to the deployment folder:

cd Deployment 

Then, run the following command to create or update Kubernetes resources defined in the deployment.yml file:

kubectl apply -f deployment.yml

Create a Kubernetes Service resource in a Kubernetes cluster based on the configuration defined in the service.yml file using the code below:

kubectl apply -f service.yml

Once you’ve applied the resource definition and the service configuration contained in the deployment.yml and the service.yml files, open the aks-intelligent-app Kubernetes Service in the Azure Portal, select Workloads under Kubernetes resources on the sidebar, and find the deployment named intelligent-app. It must have the status “Ready 1/1”. If you encounter an issue with this status, check out these troubleshooting resources.

image of selecting the deployment in Azure Portal

Testing the Intelligent App on AKS

To test the app on AKS, first, run the command below:

kubectl get services

This command lists the Services and their corresponding details, including the Service name, cluster IP address, external IP, and ports.

NAMETYPECLUSTER-IPEXTERNAL-IPPORT(S)AGE
intelligent-app-serviceLoadBalancer10.0.77.6020.121.76.15380:30936/TCP47s
kubernetesClusterIP10.0.0.1<none>443/TCP14m

The output above shows a Kubernetes Service named intelligent-app-service with a type set to LoadBalancer. It’s reachable from within the cluster using the cluster IP 10.0.77.60 and accessible externally via the external IP 20.121.76.153 on port 80 (mapped to port 30936).

Note: Your set of IP addresses will differ. Remember to use your unique external IP address when testing with Postman.

To test the deployed app, go to Postman, replace the URL with the external IP of the Kubernetes Service you just deployed, and click Send:

mage of sending the app in Postman

As we can see, our Intelligent App has successfully deployed to AKS and is functioning on the cloud as expected.

Exercise

Next Steps

In this two-part article, we explored the creation of an Intelligent App that leverages Azure AI Vision to analyze images and extract data. We learned how to build a Python Web API to perform OCR on uploaded images and subsequently deploy this API via Azure Kubernetes Service.

Besides OCR and Image Analysis, you can continue exploring Azure’s vast array of services and experiment further with Azure AI and AKS by applying various practical uses to your Intelligent Apps, including natural language processing, speech recognition and synthesis, sentiment analysis for customer feedback, and automated content moderation.

+ + \ No newline at end of file diff --git a/30daysofIA/tags/community-buzz/page/5/index.html b/30daysofIA/tags/community-buzz/page/5/index.html index 4d94ad44ef..73102cdb91 100644 --- a/30daysofIA/tags/community-buzz/page/5/index.html +++ b/30daysofIA/tags/community-buzz/page/5/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "community-buzz" | Build Intelligent Apps On Azure +11 posts tagged with "community-buzz" | Build Intelligent Apps On Azure @@ -14,13 +14,13 @@ - - + +
-

9 posts tagged with "community-buzz"

View All Tags

· 10 min read
It's 30DaysOfIA

Explore real-world examples of how Intelligent Apps revolutionize logistics, retail, finance, manufacturing, tech, and medicine.

What We'll Cover:

  • Real-world businesses revolutionizing operations with Intelligent Apps
  • High level architecture for industry scenarios
  • Implementation architecture overview across industries

A decorative header image for the blog

Real-world Success Stories

According to IBM’s 2022 Global AI Adoption Index, 35% of surveyed companies already use AI in their business, and 42% are exploring the idea. Organizations lagging behind the AI curve will quickly fall behind early adopters, who are already enjoying its benefits and boosting their investment in this technology.

In the first part of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” we explored how organizations can use AI-powered apps, or intelligent apps, to transform their operations, decision-making, and customer experiences, leaping ahead of the competition.

Intelligent apps significantly improve operational efficiency and customer engagement across industries like logistics, retail, finance, manufacturing, tech, and healthcare. In this article, we’ll spotlight real-world success stories of businesses revolutionizing operations with intelligent apps using Microsoft Azure’s app, data, and AI services.

info

Register for the Learn Live on September 21 for a live guided experience with SMEs on how to build end-to-end solution architecture for real world implementation of Intelligent Apps.

Streamlining Operations: Intelligent Apps in the Airline Industry

Aerospace pioneer Airbus leveraged intelligent apps to streamline its operations and innovate its services with two ground-breaking modernizations that use Azure apps, data, and AI services to reimagine pilot training and predict aircraft maintenance issues. 

Aviation technology’s rapid evolution compelled Airbus to reconceive their approach to training pilots. The aircraft manufacturer created a pilot training chatbot by combining Azure AI Services with Speech to Text and Text to Speech. The bot helps trainee pilots navigate over 6,000 pages of technical information. Pilots can ask questions verbally for quick answers, aiding their understanding of dense information and speeding up training. And when Airbus needed to accommodate its military consumers, deploying its Kubernetes cluster to a disconnected, “air-gapped” environment met the strict security requirements of military aircraft training.

A chatbot could look like the following diagram as Azure services work together to create a conversational experience. Its components could include security and governance, bot logic and user experience (UX), bot cognition and intelligence, data extract, transfer, and load (ETL) operations, quality assurance and enhancements, logging, and monitoring and reporting.

A diagram of a conversational experience enabled by Azure services

Airbus also used AI to improve its aircraft maintenance and safety. They deployed AI Anomaly Detector to gather and analyze telemetry data from multiple flights, enabling more profound insights into wear and tear on military aircraft operating in harsh conditions. This AI application helps Airbus better understand each airplane’s health. Airbus can predict and fix potential problems before they occur, improving the aircraft’s safety and operational readiness.

AI and Azure are changing how organizations like Airbus operate, even in strict, highly regulated markets with stringent security requirements. And by streamlining pilot training and staying ahead of aircraft maintenance, Airbus helps its military and commercial clients reliably deliver their goods across the globe, maintaining their critical supply chains.

Boosting Customer Service: Intelligent Apps in the Retail Industry

The retail industry has been among the first to adopt and benefit from intelligent apps. For example, the leading American used car retailer, CarMax, uses intelligent apps to transform the car shopping experience.

Before embracing AI, CarMax faced a significant challenge: Manually creating text summaries was time-consuming and labor-intensive. CarMax decided to leverage Azure OpenAI Service to automatically generate car research pages, offering customers valuable insights while enhancing the website’s search engine rankings.

This AI-driven approach has significantly streamlined the process, achieving in a few months what would have taken years manually. Also, the AI system condenses thousands of customer reviews into a few readable sentences, offering potential buyers a quick overview of others’ experiences.

CarMax has improved their customer experience while boosting operational efficiency through AI adoption. CarMax’s editorial staff focuses on creating strategic, longer-form content with the time saved, enhancing productivity, and driving more website traffic.

CarMax’s success story serves as a testament to the potential of intelligent apps in the retail industry.

Enhancing Cybersecurity: Intelligent Apps in Finance

Intelligent apps also impact the highly regulated financial services sector. Swift, a leading infrastructure provider for financial messaging services, uses intelligent apps to enhance decision-making and combat financial crime.

The massive growth of cross-border transactions and instant payment networks led to a surge in financial fraud that can cost the industry hundreds of billions of dollars every year. So, Swift and Microsoft partnered to build an anomaly detection model using federated learning techniques with Azure Machine Learning and Azure confidential computing.

Microsoft Purview helped govern data, as the federated learning method partially trained each model in a silo before aggregating them. This approach enabled Swift and Microsoft to develop a highly accurate model without copying or moving data from Swift members’ secure locations, ensuring the highest level of security and privacy.

Financial organizations can now deploy this trained ML model to run a real-time fraud detection system, lowering the cost of financial fraud.

Organizations can deploy a fraud detection system like the following example on Microsoft Azure. The system ingests data from streams or storage, runs analytics, and uses ML to detect fraud and store results. It then alerts users and other systems to the security issue.

A diagram of an example fraud detection system

Swift provides the innovative model to its banking partners, who can share data to further train the model. Azure confidential computing securely runs new models on current data to accumulate insights, generating an ongoing learning cycle. This iterative approach strengthens the global detection of fraudulent financial transactions through an increasingly faster and more accurate model. 

The collaboration between Swift and Microsoft demonstrates how combining various Azure services creates Intelligent Apps to enhance decision-making in finance. These apps solve real problems at an unprecedented global scale. 

Enhancing Efficiency: Intelligent Apps in Manufacturing

3M, known for Post-it Notes and other innovative products, wielded the power of Intelligent Apps to standardize, automate, and accelerate its sales forecasting. The company needed a unified, automated approach to replace its multiple manual methods.

It adopted Azure Machine Learning and other Azure AI Services to develop, train, and implement 1,500 custom models for each region and division, replacing manual methods. This solution delivers timely finance, sales, and marketing insights while freeing teams from time-consuming manual data manipulation tasks.

3M used Azure Machine Learning, automated machine learning (AutoML), and the Many Models Solution Accelerator to train and score numerous machine learning models in parallel. This approach significantly shortened the company’s development cycle time.

Then, 3M integrated Microsoft Power BI into their solution to compile and visualize results more effectively. Executives now have an unbiased and unfiltered view of data for better decision-making. With its improved ability to forecast sales, 3M can focus on manufacturing the products it needs efficiently.

3M’s success demonstrates how combining various Azure services can enhance efficiency in the manufacturing industry. These apps improve operational processes while enabling employees to focus on higher-level tasks. Strategic decisions are more targeted, and business conversations are more meaningful.

Driving Innovation: Intelligent Apps in the Technology Industry

As expected, the tech industry is at the forefront of leveraging Intelligent Apps to drive innovation. Elastic saw an opportunity to use intelligent apps to optimize its search services and meet customer needs more effectively. The company built a managed, secure, and scalable solution for its customers, Elastic Cloud, by leveraging Microsoft Azure.

Elastic Cloud uses Azure Machine Learning and other Azure AI Services to automate many basic management tasks, such as backing up data, upgrading, and scaling deployments. This approach enables Elastic’s customers to focus less on managing the software and more on serving their own clients. Customers can quickly deploy and manage Elastic Cloud on Kubernetes (ECK) using Azure Kubernetes Service.

As a result, Elastic’s cloud business is growing twice as fast as its on-premises solutions. Enterprise search users have reported significant improvements in system availability and employee satisfaction.

Additionally, Elastic incorporated Azure OpenAI Service into its Elasticsearch Relevance Engine (ESRE), providing more relevant answers to search queries. ESRE enables text, vector, and hybrid search, offering a more effective and accurate response to queries.

Elastic’s success story demonstrates how combining various Azure services can create intelligent Apps to drive innovation in the tech industry. These apps help improve operational processes and deliver better results quickly.

Revolutionizing Healthcare: Intelligent Apps in Medicine

Intelligent apps also drive transformative changes in the healthcare sector. Cambridgeshire and Peterborough NHS Foundation Trust, a healthcare provider in the United Kingdom, gradually shifted their on-premises systems to the cloud using Azure, improving flexibility, and lowering costs. They needed a solution for their electronic patient record (EPR) system that integrates 23 years of data across multiple systems and formats.

The Trust used Microsoft Azure and Azure Cognitive Search, including semantic search for understanding intent and contexts and cognitive capabilities for extracting insights from data, to standardize and automate data management. This approach made legacy, archive, and live data more discoverable, saving clinicians’ time. The Trust’s solution searches through millions of clinical documents in seconds, even recognizing handwritten notes without needing a separate optical character recognition (OCR) solution to extract text from images (Azure Cognitive Search automatically does it). Clinicians can now uncover previously inaccessible insights, enabling them to offer more personalized care.

The Trust also used Integration Services tools such as Azure Logic Apps to create workflows without writing code and Event Grid to connect Azure and third-party services through a publisher-subscriber model. They switched on a live feed from their new EPR system to Azure to feed real-time information into other applications. Researchers can now access anonymized data to fuel medical innovations beyond the NHS.

The healthcare provider also invested heavily in Power BI’s data visualization and analytical functions to sift through massive amounts of information. This level of data accessibility was previously impossible with their on-premises system. The new insights have significantly improved clinicians’ efficiency for better patient care.

Data accessibility and security are critical in the healthcare industry. Intelligent Apps empower organizations like the Trust to revolutionize medical care. AI capabilities helped the Trust improve its operational efficiency and user experiences, setting a new standard for healthcare in the digital age.

Summary

AI is driving transformation and real-world successes across industries. Intelligent apps are reshaping our world by streamlining operations in manufacturing and logistics, enhancing customer experience in the retail sector, improving decision-making in finance, driving innovation in tech, and revolutionizing healthcare.

As organizations adopt AI, they’re innovating in ways that were once too difficult, expensive, or outright impossible. Companies that wait to embrace intelligent apps risk the chance of falling behind. By embracing intelligent apps and leveraging the capabilities of Azure, you can harness the transformative potential of AI to remain on the cutting edge of technology.

Exercise

- - +
Skip to main content

11 posts tagged with "community-buzz"

View All Tags

· 10 min read
It's 30DaysOfIA

Explore how digital natives and Independent Software Vendors (ISVs) can transform their business operations and generate higher value for customers with generative AI and intelligent apps.

What We'll Cover:

  • Potential of Generative AI in application development
  • Challenges with Generative AI
  • Integrating Generative AI with business operations

image of generative AI and intelligent apps used in business

Unleashing Business Transformation: How Digital Natives Can Leverage Generative AI

Generative AI is no longer a technological flight of fancy. It’s here, and it’s rewriting the rules of business innovation. 

This type of AI leverages machine learning algorithms to create new data with similar characteristics to a given dataset. It goes beyond text generation using generative pretrained transformer (GPT) models, covering an array of applications that span from image synthesis to translations and more. It also powers a new class of intelligent applications, which combine AI capabilities with traditional software functions for enhanced user experiences and advanced business solutions. 

Generative AI is transforming how digital natives and independent software vendors (ISVs) operate. Generative AI offers new possibilities for creating unique business value, enhancing customer experiences, and driving growth. 

It plays a critical role in applications, improving functionality, aiding decision-making, and automating complex tasks to deliver dynamic, uniquely personalized user experiences. Generative AI also enables and automates data-driven outcomes, functions, and features.

Integrating AI capabilities into customer-facing apps and turning them into intelligent apps benefits both the customer and the business, as we discussed in Demystifying Intelligent Applications: Leveraging AI in App Development. You get faster product development workflows and better business intelligence for sales and marketing, while your users get more natural and context-aware features and interactions. 

That said, to realize generative AI’s potential, you need some strategy and knowledge of how and where it fits into your current business and operations. We’ll examine this technology’s potential to transform business operations, its risks, and rewards, and how to integrate it into your existing operations. 

Unpacking the Potential of Generative AI

Generative AI is creating new opportunities in the digital playing field. It empowers businesses to generate insights, design products, and automate processes in ways and at speeds that were previously unimaginable. You can now predict customer behavior more accurately, offer highly personalized user experiences, and prototype innovative ideas rapidly. 

For example, Microsoft used generative AI to reinvent web search in the new AI-powered Bing and Edge. The search experience has evolved from a solitary user flow of typing in keywords and scrolling through results. It’s now a conversational experience where Bing uses context to offer better quality and more personalized search queries and results. 

Similarly, Microsoft harnessed generative AI to transform its productivity suite into intelligent apps. Through Copilot for Microsoft 365, PowerPoint now empowers users to tell their stories by using DALL-E to generate images effortlessly. It also helps rephrase text-heavy slides to be more straightforward. Outlook now assists users to compose more effective emails, enabling better communication.

These are just some examples of how AI powers day-to-day work and operations. 

Businesses that ignore generative AI miss out on untapped market opportunities, letting competitors gain an edge in the digital race. By seeking ways to rethink assumptions and reinvent existing approaches, you can effectively use generative AI to create new opportunities, open products to new use cases and markets, unlock new growth and customer satisfaction levels, and increase your revenue.

info

Register for the intelligent apps webinar on Driving Business Value by Modernizing with Cloud-Native & AI with Microsoft and Forrester on September 26.

Explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries. 

The Crucial Role of Generative AI in Business Operations

Generative AI can be a powerful catalyst for business transformation. Let’s quickly tour some ways it can redefine business operations. 

Product development is traditionally a long and painstaking process. Now, tools like GitHub Copilot and TestPilot use generative AI to make coding and testing software easier, faster, and more robust. 

Similarly, machine learning as a service technology, such as Azure Machine Learning, democratizes the availability of machine learning. It helps collect, clean, and train models across datasets, making engineering and analyzing data more efficient and accessible.  

Generative AI also simplifies the process of creating intelligent apps. Previously, simulating thousands of prototypes, predicting their performance, and narrowing down to the most viable options could take weeks, months, or even years of iteration and data analysis. With generative AI, this process can be as quick as days, hours, or perhaps minutes. 

Customer experience is also ripe for transformation. Personalized recommendations drive engagement and sales, and generative AI can scale that personalization to every customer interaction using comprehensive and continually updated data. For instance, Microsoft leverages AI to enable personalization in Dynamics 365 Customer Insights. Using this tool, you can ask questions about your customers in natural language and gain valuable and actionable insights into your customer base. 

Lastly, marketing and sales can benefit from generative AI. For example, identifying current and future key markets and emerging trends helps companies stay ahead of the curve. Tools like Copilot in Power BI help you quickly understand and uncover insights about your business, enabling your team to craft targeted campaigns, create engaging content, and optimize your business strategy. 

Leveraging this full generative AI potential requires the right tools. GPT-4, for instance, can be instrumental in realizing effective business transformation. It excels in understanding context, generating human-like text, and learning from minimal data input, making it a formidable tool in any digital native's arsenal. Platforms like Azure OpenAI Service bring GPT-4’s power to your fingertips.

However, as with any powerful technology, it’s unwise to treat generative AI as a perfect solution. You must know the challenges and risks of not leveraging it effectively, including technical and human-factor considerations. 

Without generative AI, your company risks missing the opportunity to please customers via personalization, as discussed previously. You’ll miss sales opportunities via business intelligence insights, limiting your ability to grow revenue and keep up with the competition. 

One challenge of leveraging generative AI’s full potential is finding the right tools. Fortunately, you can implement generative AI in your organization without investing in infrastructure, computational power, and expertise. Platforms like Azure OpenAI Service and Azure AI Services make AI easily accessible. They also work with Azure compute and data services like Azure Functions and Azure Kubernetes Service (AKS), enabling you to build intelligent apps easily and quickly. 

However, generative AI can’t do everything. It may need human expertise and intervention to use it fully. 

For example, generative AI can create unique data. However, the data may have limited diversity and complexity. The AI may need help recognizing additional considerations or requirements specific to a problem. It may also lack context due to various factors, such as the information’s freshness. Also, since it’s an emerging field that’s potentially worth $4.4 trillion, we’re still learning about the nuances and potential regulatory impact of generative AI. 

Perhaps the most crucial and challenging aspect of adopting generative AI is an organization’s required cultural shift. Your teams must learn how to use AI as your company fosters an understanding of AI’s potential and limitations. 

Begin integrating AI literacy into your company’s DNA. Knowledge of its potential and the readily available tools can inspire your teams to incorporate generative AI into their daily workflows and business operations quickly, improve productivity, and deliver superior customer experiences. 

No company wants to be outpaced by its competitors. You can balance generative AI’s potential with your business operations by staying aware of and adapting to the dynamic AI landscape.

Achieving Harmony: Integrating Generative AI with Existing Business Operations

Integrating generative AI into existing business operations doesn’t have to be disruptive. With the correct strategy, you can do so seamlessly. Your goal should be to augment and enhance your operations, not disrupt and replace. 

For example, Microsoft Cloud technologies leveraged AI to relieve healthcare organizations’ challenges, such as talent shortage and workforce burnout. Providers automated their workflows, invested in specific applications and connectors, and enhanced support for data and operations. 

Azure AI Services, such as text analytics for health, enable healthcare organizations to understand their largely unstructured and untapped medical data. Project Health Insights leans on AI to help healthcare professionals gain actionable insights and inferences from patient data. 

Azure Health Bot, an Intelligent App, empowers organizations to build and deploy legally compliant, powerful conversational healthcare experiences to patients. 

A successful AI integration like this begins with understanding. Knowing what generative AI can do and setting realistic expectations can help you identify where and how AI can best serve your operations. 

Next, you can foster a culture of AI literacy across teams, speeding up adoption by helping them understand how it can aid their work. The more familiar your team is with AI and its possibilities, the smoother the transition. Training and education programs play a pivotal role here. 

Finally, integration requires a systematic approach. It’s not about overhauling everything at once — it's about identifying areas where AI can add the most value, then gradually experimenting and scaling its use. Successful integration stories often feature a phased approach, starting small and growing steadily. 

As we move toward a future where generative AI is a staple in business operations, remember that every journey is unique. Although there’s no one-size-fits-all solution, a strategic approach and a keen understanding of your business needs will help you navigate this new landscape successfully.

Conclusion

Generative AI offers plenty of opportunities for businesses that embrace it. It’s fueling the next generation of intelligent applications, transforming everything from product development to customer engagement to marketing. This technology is the key to unlocking unprecedented growth and innovation. 

While the journey may have challenges, the potential rewards make generative AI worth exploring. As generative AI advances, Azure AI’s broad range of tools and services will help you navigate this fascinating field.

Exercise

  • Watch Episode 02 of the serverless edition Learn Live session to learn how to build, test and deploy an end-to-end intelligent app solution.
  • Complete the Cloud Skills Challenge to build on your AI skills.
  • Register for Ask the Expert: Azure Functions on September 26 for a live Q&A with the Product Team on how to build AI apps using serverless computing.
+ + \ No newline at end of file diff --git a/30daysofIA/tags/community-buzz/page/6/index.html b/30daysofIA/tags/community-buzz/page/6/index.html index 7c933330a6..5d6b5679df 100644 --- a/30daysofIA/tags/community-buzz/page/6/index.html +++ b/30daysofIA/tags/community-buzz/page/6/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "community-buzz" | Build Intelligent Apps On Azure +11 posts tagged with "community-buzz" | Build Intelligent Apps On Azure @@ -14,13 +14,13 @@ - - + +
-

9 posts tagged with "community-buzz"

View All Tags

· 8 min read
It's 30DaysOfIA

Let’s start with understanding the power of intelligent applications.

What We'll Cover:

  • What are intelligent applications?
  • Categories of Intelligent Apps
  • Breaking down the role of AI in intelligent apps

A decorative header image for the blog

Demystifying Intelligent Applications: Leveraging AI in App Development

Until recent years, when you heard the term “artificial intelligence” (AI), you might have pictured autonomous robots from sci-fi films. Now, perhaps your mind jumps to big data and machine learning (ML). Maybe you’re even thinking about the latest ChatGPT plugins and transformer architectures. Today, AI is more than these imaginations: it’s a reality increasingly woven into our day-to-day routines through intelligent applications.

Intelligent apps combine cloud-scale compute, data, and predictive or generative AI. These applications help you automate tasks and make data-driven decisions, aiding in leveraging business intelligence to gain meaningful, practical outcomes. 

For developers, the AI landscape isn’t an abstract complexity. It’s a real, tangible opportunity for evolving traditional applications, making them “intelligent,” and delivering more value to users. And with tools like Azure Kubernetes Service, Azure Cosmos DB, and Azure AI Services, stepping into the world of intelligent apps isn’t a steep climb but a steady trek across a manageable learning curve.

The Three Categories of Intelligent Applications

Before we start building, it’s critical to understand what makes an app “intelligent.” It’s not a universal definition so much as an identifier for apps that fall into one or more of the three primary categories: 

  • Outcome-based — This designation represents the broadest category of intelligent apps, whose principal value for users is the intelligent outcomes they provide. These apps process data to provide insightful results that enhance decision-making or help with tasks like trend prediction, resource management, and customer relationship management.

  • Functionality-based — This type of intelligent app takes AI integration a degree further, integrating AI as a function of the app to create or process its outputs, enhancing the app’s overall interactivity and usefulness.

  • Feature-based — This is the narrowest category, in which the app’s AI/ML component is one of its core features and primary selling points

Let’s dive more deeply into each category.

Outcome-Based Apps: Intelligent Outcomes Drive Success

Outcome-based applications are all about the results provided to the user. They function beyond merely processing data to offer insightful outcomes that drive success.

Think of a personal fitness tracker app that uses AI to analyze the user’s activity and provides actionable recommendations for achieving a goal. This type of app not only tracks your steps, sleep patterns, and heart rate but uses that data to provide you with personalized health advice. It helps you make informed lifestyle choices — the “intelligent” outcome.

Another example is a social media marketing app that uses AI for time-saving data analysis, task automation, and trend prediction. AI-based sentiment analysis can gauge customer perception while natural language processing (NLP) accurately interprets customer inquiries. Task automation can schedule posts and automate responses to common customer queries, and trend forecasting helps model and predict customer behavior to further personalize targeted promotional efforts. Building this type of intelligence into your apps provides cost-effective, actionable insights that guide sales and marketing strategies.

Functionality-Based Apps: AI-Driven Features Enhance User Experiences

As the name suggests, functionality-based applications integrate AI into how the app functions, but the end user drives the action and outcome. In this category of apps, AI takes a more active role in the user’s interaction by allowing them to use AI-created or processed output. These apps leverage AI technologies like Azure AI Services for NLP, image recognition, and pattern recognition to improve user experience.

Consider a music app that generates personalized playlists based on your listening habits or your smartphone’s camera app that uses AI to recognize faces and optimize photo settings. Even more advanced are the language translation apps that translate text from one language to another and can even understand and maintain the context and subtleties of the conversation. They use NLP to accomplish these feats.

Similarly, consider the latest e-commerce apps, whose AI-driven functionalities enable customers to try on clothes virtually, significantly enhancing the online shopping experience.

Feature-Based Apps: Integrating Advanced AI/ML Components

Finally, there are feature-based intelligent apps. These apps incorporate advanced AI/ML components like neural networks, internal large language models (LLMs), and sophisticated algorithms as core features for the user. In this type of application, AI is the primary feature for users to engage with and the primary selling point for the app.

Some examples of feature-based applications are chatbots and virtual agents built using Azure Bot Service. These robo-representatives use AI to provide more natural and human-like conversation, balancing superior efficiency with a friendlier, more personalized user experience. The new Bing is a prime example of this implementation, integrating AI to give users a knowledgeable copilot for web searches.

Similarly, OpenAI’s ChatGPT allows users access to its state-of-the-art language models available as Microsoft’s Azure OpenAI Service. It leverages the power of the highly sophisticated GPT-3 LLM to perform tasks like drafting emails, writing code, answering general knowledge questions, translating between languages, and tutoring in numerous subjects.

These apps don’t just use AI to provide outputs or improve user experiences: They push the boundaries of what’s possible in app development by integrating AI/ML components.

info

Register for the live Q&A Ask The Expert session on September 26 with the Azure Functions product engineering team.

Breaking Down the Role of AI in Intelligent Applications

Intelligent apps reflect the merging of traditional software design with the dynamic capabilities of AI. Let’s explore how AI integrates and amplifies these applications.

At its core, AI augments applications with the capacity to self-learn and self-improve. It allows you to create applications that predict, react, and adapt to a constantly changing environment. In the power grid management system for a major city, an AI-infused application can forecast power usage trends, react to anomalies, and adapt to sudden demand surges, ensuring optimal efficiency and preventing blackouts.

But AI’s role doesn’t end at prediction or decision-making: It also automates tasks. In enterprise solutions, it has moved beyond simple automation to encompass intelligent process automation (IPA). In this arena, AI algorithms understand, learn, and then automate entire digital workflows. For developers, this is a fertile ground for creating applications that can dramatically improve the operational efficiency of businesses.

Even more, AI substantially aids in development tasks. For example, there’s GitHub Copilot, an AI-powered code assistant that offers suggestions based not only on syntax but on development best practices and the intent of the code. AI has evolved from a tool you use to build apps to a partner that helps in creating them. 

Using AI to build intelligent apps isn’t about throwing out what’s worked before. It’s about enhancing the components that have the most impact and achieve the best results. Incorporating AI in apps elevates their predictive abilities, streamlines complex tasks, and even aids in the development and creation process. When you build intelligent apps, you don’t just address current problems. You anticipate future challenges and craft solutions that adapt over time.

Summary

As developers the power of AI is in our hands, and it’s our responsibility to harness its full potential. Each category tells the story of AI's transformative potential, from applications that deliver intelligent outcomes to those with AI capabilities to enrich the user experience and those pushing boundaries with advanced AI/ML technology.

Accessible and robust platforms like Azure AI can simplify the process of developing Intelligent Applications. Whether you’re refining an existing app or building a new one, just entering the world of AI, or already familiar with the landscape, take a moment to appreciate the potential that AI integration offers. You’re embracing this exciting frontier and shaping the future of app development. 

Exercise

  • Complete the Cloud Skills Challenge to build on your apps and AI skills.
  • Watch Episode 01 of the Serverless Edition Learn Live session to learn how to build an end-to-end intelligent app solution.
- - +
Skip to main content

11 posts tagged with "community-buzz"

View All Tags

· 9 min read
It's 30DaysOfIA

In the part of our series on intelligent apps, we’ll explore how AI is transforming application development, from design and architecture to building.

What We'll Cover:

  • Revolutionizing application development using AI
  • Infusing AI in application design architecture
  • AI Assisted Pair Programming
  • Future of App Development

A decorative image of application development with AI

Reimagining Application Development With AI: A New Paradigm

Artificial intelligence (AI) is impacting every industry. AI-powered development tools offer new functionalities and assist software developers throughout the development process, revolutionizing app development. From initial design and architecture to coding and debugging, AI reshapes how we approach application conception, creation, and iteration.

In part one of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” we explored the new breed of intelligent apps and how they use AI. In this article, we’ll see how AI technologies enhance developer toolkits and shape the future of app development, particularly through AI-enabled coding assistance and debugging.

The Dawn of Intelligent Apps: Revolutionizing Application Development

AI is more than just another addition to the development toolbox—it represents a revolution bringing unprecedented efficiency, innovation, and user engagement to our applications. 

What sets intelligent apps apart is how they can adapt and learn, helping us deliver unique, real-time user experiences.  

One example is an AI-driven health application that can track biometric data, predict potential health issues, provide personalized health advice, and even guide emergency services during a medical crisis. An AI-enabled language learning app that adapts to our learning speeds, recognizes our problem areas, and customizes its content to help us improve our language proficiencies is another example.  

Intelligent apps like these don’t just use data for recommendations—they can make critical decisions and provide sophisticated services that were previously impossible. 

For developers, intelligent apps mean we’re no longer just coding—we’re teaching applications how to think, adapt, and make decisions. AI is reshaping every step of the development journey, helping us to build more intuitive and user-focused products. 

Next, we’ll examine the impact of AI on application design and architecture and how AI-assisted “pair programming” changes software development.

info

Register for the FREE webinar on Intelligent Apps with Microsoft and Forrester on September 26 to explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries.

There will be a showcase of real-world use cases that demonstrate how AI can be seamlessly integrated into cloud-native environments driving tangible business value.

AI in Design and Architecture: Fueling Creativity and Efficiency

AI technologies help developers automate and streamline their processes and provide new ways to think about design.  

In traditional application development, the design and architecture phase can take significant time, effort, and expertise. For instance, adopting a monolithic architecture or a microservices-based design can significantly increase coding, testing, and debugging complexity. The app may even behave differently when hosted locally than in a deployed environment. It may take hours to track down an issue manually—time developers could spend elsewhere. 

Conversely, we can generate numerous design iterations in seconds with AI, each optimized to fulfill specific user needs and business goals. AI tools can quickly model various scenarios' impacts and software design patterns in the architectural design phase. It provides insights into performance and potential issues and suggests improvements. AI can also generate skeleton code, allowing developers to prototype different solutions or test specific components and technologies rapidly. By reviewing thousands of lines of code in seconds, AI allows us to focus on creating apps that serve users through feature development instead of searching for bugs.  

Integrating AI into the design and architecture phase can even extend our creative capabilities. By leveraging the power of generative AI—like large language models (LLMs)—and their broad knowledge and context going beyond software, we can better brainstorm apps and feature ideas. We can go beyond code and software architecture and get ideas about user experience, logo and branding, or marketing concepts. And as AI tools deliver insights and ideas we would have never considered; it encourages more innovation.

info

Watch Episode 01 of the Learn Live session to learn how to build an end-to-end intelligent app solution architecture.

The Power of AI-Assisted Pair Programming in Building Applications

AI-assisted pair programming is a collaborative coding approach where an AI system—like GitHub Copilot or TestPilot—assists developers during coding. It’s an increasingly common approach that significantly impacts developer productivity. In fact, GitHub Copilot is now behind an average of 46 percent of developers’ code and users are seeing 55 percent faster task completion on average. 

For new software developers, or those interested in learning new skills, AI-assisted pair programming are training wheels for coding. With the benefits of code snippet suggestions, developers can avoid struggling with beginner pitfalls like language syntax. Tools like ChatGPT can act as a personal, on-demand tutor—answering questions, generating code samples, and explaining complex code syntax and logic. These tools dramatically speed the learning process and help developers gain confidence in their coding abilities. 

Building applications with AI tools hastens development and provides more robust code. For example, GitHub Copilot helps us write new code accurately. It also quickly makes sense of existing code, suggests whole new lines or blocks of code, and even detects errors and proposes fixes. It’s like having a second coder working alongside us—one who never tires and is trained on billions of lines of code. 

However, it’s also important to acknowledge the limitations of AI. While generative AI-assistance technologies like GitHub Copilot are impressive, they’re still imperfect. They can sometimes produce erroneous, non-functional, or vulnerable code. They can also lack nuanced, domain-specific knowledge.  

So, although these tools are helpful, we can’t rely on them fully—these are programming aids rather than definitive solutions. AI-assisted pair programming is about augmenting the human developer’s skills, not replacing them.

Debugging and Improving Code With AI: Enhancing Application Quality

Identifying and correcting code errors is often time-consuming and challenging for developers. AI streamlines the debugging process by automatically detecting anomalies, suggesting fixes, and learning from previous bugs to prevent them from recurring. 

We can use generative AI to create unit tests from code snippets, from simple parameter checking and input validation to complex mocks of database and network services. Automated AI testing improves code coverage and robustness. It saves significant time compared to manually crafting unit tests. 

AI also revolutionizes code reviews by scanning and highlighting potential issues—like code patterns leading to a bug or a coding standards violation—and suggesting ways to simplify or refactor the code for readability and performance. This reduces human error and makes the code review process more efficient. Its benefits extend to small teams, where the availability of developers is crucial to deadlines, and large teams, where effective collaboration depends on readability and code consistency. AI further saves time by triaging issues so developers can prioritize the most mission-critical ones. 

Aside from improving code quality, AI is critical to understanding system behavior. Intelligent AI-assisted software development tools can monitor and audit system behavior, giving developers a better insight into how their code performs in different scenarios. AI can track patterns, identify inefficiencies, and provide alerts when they detect unusual behavior—enabling developers to enhance their code further. 

By predicting and identifying potential vulnerabilities, intelligent AI-assisted coding apps like Microsoft Security Copilot are pivotal in ensuring application functionality, reliability, and security. These AI tools are continually trained and upgraded with large codebases and user feedback that refines their responses. This ability to learn from past mistakes makes AI an incredibly powerful tool that will continue to expand its capabilities in the future.

info

Complete the AI Cloud Skills Challenge to build on you AI application development skills with paired programming using generative AI and GitHub Copilot.

AI and the Future of Application Development

The rapid advancements in AI and machine learning technologies point to an exciting future for application development. 

In “The Future of Applications,” software developer Mike Cann illustrates how LLMs might revolutionize application development. Cann that suggests fully-autonomous coding—AI-generated applications created entirely from prompts—may supersede traditional hand-crafted applications. Dynamically generated user interfaces could offer personalized and customizable experiences while we can design APIs for machine consumption and efficiency rather than human readability. Furthermore, we can store data in less structured, more adaptable, and flexible forms. 

AI is already making application development more accessible. With AI handling the complex, technical aspects of development, less-technical individuals can take a greater role in building applications. Consequently, AI can potentially democratize software engineering. 

Intelligent apps will continue to evolve, becoming more efficient, adaptable, and capable of learning from user interactions. Similarly, AI will inevitably become integral in every stage of app development.

Summary

AI has already created a new development paradigm—one in which machine intelligence and human creativity come together to produce innovative, efficient, and robust applications. AI already assists us at every stage of app development, from design and architecture to coding and debugging. 

Developers who embrace AI tools and integrate them into their processes can enhance their productivity, improve the quality of their applications, and expand the possibilities of their application development.  

The future of development is automated and AI-driven, and it’s here today. Getting started is easy—try an AI-powered development tool like GitHub Copilot to see how AI-assisted pair programming can enhance your productivity and code quality.

Exercise

+ + \ No newline at end of file diff --git a/30daysofIA/tags/community-buzz/page/7/index.html b/30daysofIA/tags/community-buzz/page/7/index.html index 49fa7cdd53..c0e78f59df 100644 --- a/30daysofIA/tags/community-buzz/page/7/index.html +++ b/30daysofIA/tags/community-buzz/page/7/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "community-buzz" | Build Intelligent Apps On Azure +11 posts tagged with "community-buzz" | Build Intelligent Apps On Azure @@ -14,14 +14,13 @@ - - + +
-

9 posts tagged with "community-buzz"

View All Tags

· 4 min read
It's 30DaysOfIA

This Fall focus on building intelligent apps using AI and cloud-native technologies. #FallForIntelligentApps brings to you a learning journey to build your skills on creating differentiated experiences while modernizing your applications. It’s time to learn it all.

What We'll Cover

image

Get Ready To #FallForIntelligentApps starting September 18!

Today, we kick off the Fall season with content and activities to skill you up on all things Intelligent Apps or AI Apps on Azure with content, events, and community interactions! Read on to learn about what is coming!

Explore Our Initiatives

We have a number of initiatives planned for the month to help you learn and skill up on relevant technologies. Click on the links to visit the relevant pages for each

image

Register for the events!

What are 4 things you can do today, to jumpstart your learning journey?

#30Days Of Intelligent Apps

#30DaysOfIA is a series of daily blog posts grouped into themed weeks - taking you from core concepts to end-to-end solution examples in 30 days. Each article will provide conceptual lessons paired with exercises and resources to help you reinforce learnings and take next steps.

This series takes you through learning journey in six stages, each building on the previous week to help you skill up in a beginner-friendly way:

We will start with defining intelligent apps and then expand on how to build with cloud-native technologies like Azure Kubernetes Service, Azure Container Apps and Azure Functions, and take you through end-to-end scenarios for real world application development. Before we dive deep on intelligent apps, here is a high-level overview of the Intelligent Apps landscape on Azure:

image

Containers on Azure services offer you a wide range of capabilities, from simplicity to control to suit your different needs.

image -image

To start with the basics for developing Kubernetes applications, explore #30DaysOfCloudNative.

Cloud-native development when paired with serverless computing enhances your solution architecture for building cost optimized, resilient applications.

image

To start with the basics for serverless computing, explore #30DaysOfServerless.

Let’s Get Started

Now you know everything! We hope you are as excited as we are to dive into a full month of active learning and doing! Don't forget to subscribe for updates in your favorite feed reader! And look out for our first Intelligent Apps post Monday!

- - +
Skip to main content

11 posts tagged with "community-buzz"

View All Tags

· 10 min read
It's 30DaysOfIA

Explore real-world examples of how Intelligent Apps revolutionize logistics, retail, finance, manufacturing, tech, and medicine.

What We'll Cover:

  • Real-world businesses revolutionizing operations with Intelligent Apps
  • High level architecture for industry scenarios
  • Implementation architecture overview across industries

A decorative header image for the blog

Real-world Success Stories

According to IBM’s 2022 Global AI Adoption Index, 35% of surveyed companies already use AI in their business, and 42% are exploring the idea. Organizations lagging behind the AI curve will quickly fall behind early adopters, who are already enjoying its benefits and boosting their investment in this technology.

In the first part of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” we explored how organizations can use AI-powered apps, or intelligent apps, to transform their operations, decision-making, and customer experiences, leaping ahead of the competition.

Intelligent apps significantly improve operational efficiency and customer engagement across industries like logistics, retail, finance, manufacturing, tech, and healthcare. In this article, we’ll spotlight real-world success stories of businesses revolutionizing operations with intelligent apps using Microsoft Azure’s app, data, and AI services.

info

Register for the Learn Live on September 21 for a live guided experience with SMEs on how to build end-to-end solution architecture for real world implementation of Intelligent Apps.

Streamlining Operations: Intelligent Apps in the Airline Industry

Aerospace pioneer Airbus leveraged intelligent apps to streamline its operations and innovate its services with two ground-breaking modernizations that use Azure apps, data, and AI services to reimagine pilot training and predict aircraft maintenance issues. 

Aviation technology’s rapid evolution compelled Airbus to reconceive their approach to training pilots. The aircraft manufacturer created a pilot training chatbot by combining Azure AI Services with Speech to Text and Text to Speech. The bot helps trainee pilots navigate over 6,000 pages of technical information. Pilots can ask questions verbally for quick answers, aiding their understanding of dense information and speeding up training. And when Airbus needed to accommodate its military consumers, deploying its Kubernetes cluster to a disconnected, “air-gapped” environment met the strict security requirements of military aircraft training.

A chatbot could look like the following diagram as Azure services work together to create a conversational experience. Its components could include security and governance, bot logic and user experience (UX), bot cognition and intelligence, data extract, transfer, and load (ETL) operations, quality assurance and enhancements, logging, and monitoring and reporting.

A diagram of a conversational experience enabled by Azure services

Airbus also used AI to improve its aircraft maintenance and safety. They deployed AI Anomaly Detector to gather and analyze telemetry data from multiple flights, enabling more profound insights into wear and tear on military aircraft operating in harsh conditions. This AI application helps Airbus better understand each airplane’s health. Airbus can predict and fix potential problems before they occur, improving the aircraft’s safety and operational readiness.

AI and Azure are changing how organizations like Airbus operate, even in strict, highly regulated markets with stringent security requirements. And by streamlining pilot training and staying ahead of aircraft maintenance, Airbus helps its military and commercial clients reliably deliver their goods across the globe, maintaining their critical supply chains.

Boosting Customer Service: Intelligent Apps in the Retail Industry

The retail industry has been among the first to adopt and benefit from intelligent apps. For example, the leading American used car retailer, CarMax, uses intelligent apps to transform the car shopping experience.

Before embracing AI, CarMax faced a significant challenge: Manually creating text summaries was time-consuming and labor-intensive. CarMax decided to leverage Azure OpenAI Service to automatically generate car research pages, offering customers valuable insights while enhancing the website’s search engine rankings.

This AI-driven approach has significantly streamlined the process, achieving in a few months what would have taken years manually. Also, the AI system condenses thousands of customer reviews into a few readable sentences, offering potential buyers a quick overview of others’ experiences.

CarMax has improved their customer experience while boosting operational efficiency through AI adoption. CarMax’s editorial staff focuses on creating strategic, longer-form content with the time saved, enhancing productivity, and driving more website traffic.

CarMax’s success story serves as a testament to the potential of intelligent apps in the retail industry.

Enhancing Cybersecurity: Intelligent Apps in Finance

Intelligent apps also impact the highly regulated financial services sector. Swift, a leading infrastructure provider for financial messaging services, uses intelligent apps to enhance decision-making and combat financial crime.

The massive growth of cross-border transactions and instant payment networks led to a surge in financial fraud that can cost the industry hundreds of billions of dollars every year. So, Swift and Microsoft partnered to build an anomaly detection model using federated learning techniques with Azure Machine Learning and Azure confidential computing.

Microsoft Purview helped govern data, as the federated learning method partially trained each model in a silo before aggregating them. This approach enabled Swift and Microsoft to develop a highly accurate model without copying or moving data from Swift members’ secure locations, ensuring the highest level of security and privacy.

Financial organizations can now deploy this trained ML model to run a real-time fraud detection system, lowering the cost of financial fraud.

Organizations can deploy a fraud detection system like the following example on Microsoft Azure. The system ingests data from streams or storage, runs analytics, and uses ML to detect fraud and store results. It then alerts users and other systems to the security issue.

A diagram of an example fraud detection system

Swift provides the innovative model to its banking partners, who can share data to further train the model. Azure confidential computing securely runs new models on current data to accumulate insights, generating an ongoing learning cycle. This iterative approach strengthens the global detection of fraudulent financial transactions through an increasingly faster and more accurate model. 

The collaboration between Swift and Microsoft demonstrates how combining various Azure services creates Intelligent Apps to enhance decision-making in finance. These apps solve real problems at an unprecedented global scale. 

Enhancing Efficiency: Intelligent Apps in Manufacturing

3M, known for Post-it Notes and other innovative products, wielded the power of Intelligent Apps to standardize, automate, and accelerate its sales forecasting. The company needed a unified, automated approach to replace its multiple manual methods.

It adopted Azure Machine Learning and other Azure AI Services to develop, train, and implement 1,500 custom models for each region and division, replacing manual methods. This solution delivers timely finance, sales, and marketing insights while freeing teams from time-consuming manual data manipulation tasks.

3M used Azure Machine Learning, automated machine learning (AutoML), and the Many Models Solution Accelerator to train and score numerous machine learning models in parallel. This approach significantly shortened the company’s development cycle time.

Then, 3M integrated Microsoft Power BI into their solution to compile and visualize results more effectively. Executives now have an unbiased and unfiltered view of data for better decision-making. With its improved ability to forecast sales, 3M can focus on manufacturing the products it needs efficiently.

3M’s success demonstrates how combining various Azure services can enhance efficiency in the manufacturing industry. These apps improve operational processes while enabling employees to focus on higher-level tasks. Strategic decisions are more targeted, and business conversations are more meaningful.

Driving Innovation: Intelligent Apps in the Technology Industry

As expected, the tech industry is at the forefront of leveraging Intelligent Apps to drive innovation. Elastic saw an opportunity to use intelligent apps to optimize its search services and meet customer needs more effectively. The company built a managed, secure, and scalable solution for its customers, Elastic Cloud, by leveraging Microsoft Azure.

Elastic Cloud uses Azure Machine Learning and other Azure AI Services to automate many basic management tasks, such as backing up data, upgrading, and scaling deployments. This approach enables Elastic’s customers to focus less on managing the software and more on serving their own clients. Customers can quickly deploy and manage Elastic Cloud on Kubernetes (ECK) using Azure Kubernetes Service.

As a result, Elastic’s cloud business is growing twice as fast as its on-premises solutions. Enterprise search users have reported significant improvements in system availability and employee satisfaction.

Additionally, Elastic incorporated Azure OpenAI Service into its Elasticsearch Relevance Engine (ESRE), providing more relevant answers to search queries. ESRE enables text, vector, and hybrid search, offering a more effective and accurate response to queries.

Elastic’s success story demonstrates how combining various Azure services can create intelligent Apps to drive innovation in the tech industry. These apps help improve operational processes and deliver better results quickly.

Revolutionizing Healthcare: Intelligent Apps in Medicine

Intelligent apps also drive transformative changes in the healthcare sector. Cambridgeshire and Peterborough NHS Foundation Trust, a healthcare provider in the United Kingdom, gradually shifted their on-premises systems to the cloud using Azure, improving flexibility, and lowering costs. They needed a solution for their electronic patient record (EPR) system that integrates 23 years of data across multiple systems and formats.

The Trust used Microsoft Azure and Azure Cognitive Search, including semantic search for understanding intent and contexts and cognitive capabilities for extracting insights from data, to standardize and automate data management. This approach made legacy, archive, and live data more discoverable, saving clinicians’ time. The Trust’s solution searches through millions of clinical documents in seconds, even recognizing handwritten notes without needing a separate optical character recognition (OCR) solution to extract text from images (Azure Cognitive Search automatically does it). Clinicians can now uncover previously inaccessible insights, enabling them to offer more personalized care.

The Trust also used Integration Services tools such as Azure Logic Apps to create workflows without writing code and Event Grid to connect Azure and third-party services through a publisher-subscriber model. They switched on a live feed from their new EPR system to Azure to feed real-time information into other applications. Researchers can now access anonymized data to fuel medical innovations beyond the NHS.

The healthcare provider also invested heavily in Power BI’s data visualization and analytical functions to sift through massive amounts of information. This level of data accessibility was previously impossible with their on-premises system. The new insights have significantly improved clinicians’ efficiency for better patient care.

Data accessibility and security are critical in the healthcare industry. Intelligent Apps empower organizations like the Trust to revolutionize medical care. AI capabilities helped the Trust improve its operational efficiency and user experiences, setting a new standard for healthcare in the digital age.

Summary

AI is driving transformation and real-world successes across industries. Intelligent apps are reshaping our world by streamlining operations in manufacturing and logistics, enhancing customer experience in the retail sector, improving decision-making in finance, driving innovation in tech, and revolutionizing healthcare.

As organizations adopt AI, they’re innovating in ways that were once too difficult, expensive, or outright impossible. Companies that wait to embrace intelligent apps risk the chance of falling behind. By embracing intelligent apps and leveraging the capabilities of Azure, you can harness the transformative potential of AI to remain on the cutting edge of technology.

Exercise

+ + \ No newline at end of file diff --git a/30daysofIA/tags/community-buzz/page/8/index.html b/30daysofIA/tags/community-buzz/page/8/index.html index e34649610d..cfcbbf1c2f 100644 --- a/30daysofIA/tags/community-buzz/page/8/index.html +++ b/30daysofIA/tags/community-buzz/page/8/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "community-buzz" | Build Intelligent Apps On Azure +11 posts tagged with "community-buzz" | Build Intelligent Apps On Azure @@ -14,14 +14,13 @@ - - + +
-

9 posts tagged with "community-buzz"

View All Tags

· 4 min read
It's 30DaysOfIA

Continue The Learning Journey through Fall For Intelligent Apps! 🍂

What We'll Cover

Thank you! ♥️

image

It's hard to believe that JavaScript on Azure hack-together is ending! It seems like just yesterday that we launched this initiative, and yet here we are, 15 days later, with an incredible amount of learning and growth behind us. So... it's time for a wrap!

From the bottom of our hearts, we want to thank each and every one of you for your participation, engagement, and enthusiasm. It's been truly inspiring to see the passion and dedication from this strong community, and we're honored to be a part of it. ✨

Recap of The JavaScript on Azure Global Hack-Together

As we wrap up this exciting event, we wanted to take a moment to reflect on all that we've accomplished together. Over the last 15 days, we've covered a lot of ground, from the basics of contributing to Open source to the exploration of the Contoso Real Estate project from its Frontend to its Backend and future AI implementation.

Now that the hack-together is ending, we want to make sure that you have all the resources you need to continue honing your skills in the future. Whether you're looking to make your fist contribution to open source, become an open source maintainers, collaborate with others, or simply keep learning, there are plenty of resources out there to help you achieve your goals. So, let's dive in and explore all the ways you can continue to grow your JavaScript skills on Azure!

JSonAzure Hack-together Roadmap 📍:

hack-together-roadmap (2)

Recap on past Livestreams🌟:

Day 1️⃣: Opening Keynote (Hack-together Launch): Introduction to the Contoso Real Estate Open-source project!, managing complex and complex enterprise architecture, new announcements for JavaScript developers on Azure

Day 2️⃣: GitHub Copilot & Codespaces: Introduction to your AI pair programmer (GitHub Copilot) and your virtual developer environment on the cloud (GitHub Codespaces)

Day 6️⃣: Build your Frontend using Static Web Apps as part of a complex, modern composable frontends (or micro-frontends) and cloud-native applications.

Day 9️⃣: Build a Serverless Backend using Azure Functions

Day 1️⃣3️⃣: Easily connect to an Azure Cosmos DB, exploring its benefits and how to get started

Day 1️⃣5️⃣: Being in the AI Era, we dive into the Azure OpenAI Service and how you can start to build intelligent JavaScript applications

📖 Self-Learning Resources

  1. JavaScript on Azure Global Hack Together Module collection
  2. Lets #HackTogether: Javascript On Azure Keynote
  3. Step by Step Guide: Migrating v3 to v4 programming model for Azure Functions for Node.Js Application

Continue your journey with #FallForIntelligentApps

Join us this Fall on a learning journey to explore building intelligent apps. Combine the power of AI, cloud-native app development, and cloud-scale data to build highly differentiated digital experiences. Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure. Engage in a self-paced learning adventure by following along the #30Days of Intelligent Apps series, completing the Intelligent Apps Skills Challenge, joining the Product Group for a live Ask The Expert series or building an end to end solution architecture with a live guided experience through the Learn Live series. Discover more here.

Hands-on practice: Make your first contribution to open source!

Join our GitHUb Discussion Forum to connect with developers from every part of world, see contributions from other, find collaborators and make your first contribution to a real-world project! -Don't forget to give the repo a star ⭐

Resources

All resources are accessible on our landing page

- - +
Skip to main content

11 posts tagged with "community-buzz"

View All Tags

· 8 min read
It's 30DaysOfIA

Let’s start with understanding the power of intelligent applications.

What We'll Cover:

  • What are intelligent applications?
  • Categories of Intelligent Apps
  • Breaking down the role of AI in intelligent apps

A decorative header image for the blog

Demystifying Intelligent Applications: Leveraging AI in App Development

Until recent years, when you heard the term “artificial intelligence” (AI), you might have pictured autonomous robots from sci-fi films. Now, perhaps your mind jumps to big data and machine learning (ML). Maybe you’re even thinking about the latest ChatGPT plugins and transformer architectures. Today, AI is more than these imaginations: it’s a reality increasingly woven into our day-to-day routines through intelligent applications.

Intelligent apps combine cloud-scale compute, data, and predictive or generative AI. These applications help you automate tasks and make data-driven decisions, aiding in leveraging business intelligence to gain meaningful, practical outcomes. 

For developers, the AI landscape isn’t an abstract complexity. It’s a real, tangible opportunity for evolving traditional applications, making them “intelligent,” and delivering more value to users. And with tools like Azure Kubernetes Service, Azure Cosmos DB, and Azure AI Services, stepping into the world of intelligent apps isn’t a steep climb but a steady trek across a manageable learning curve.

The Three Categories of Intelligent Applications

Before we start building, it’s critical to understand what makes an app “intelligent.” It’s not a universal definition so much as an identifier for apps that fall into one or more of the three primary categories: 

  • Outcome-based — This designation represents the broadest category of intelligent apps, whose principal value for users is the intelligent outcomes they provide. These apps process data to provide insightful results that enhance decision-making or help with tasks like trend prediction, resource management, and customer relationship management.

  • Functionality-based — This type of intelligent app takes AI integration a degree further, integrating AI as a function of the app to create or process its outputs, enhancing the app’s overall interactivity and usefulness.

  • Feature-based — This is the narrowest category, in which the app’s AI/ML component is one of its core features and primary selling points

Let’s dive more deeply into each category.

Outcome-Based Apps: Intelligent Outcomes Drive Success

Outcome-based applications are all about the results provided to the user. They function beyond merely processing data to offer insightful outcomes that drive success.

Think of a personal fitness tracker app that uses AI to analyze the user’s activity and provides actionable recommendations for achieving a goal. This type of app not only tracks your steps, sleep patterns, and heart rate but uses that data to provide you with personalized health advice. It helps you make informed lifestyle choices — the “intelligent” outcome.

Another example is a social media marketing app that uses AI for time-saving data analysis, task automation, and trend prediction. AI-based sentiment analysis can gauge customer perception while natural language processing (NLP) accurately interprets customer inquiries. Task automation can schedule posts and automate responses to common customer queries, and trend forecasting helps model and predict customer behavior to further personalize targeted promotional efforts. Building this type of intelligence into your apps provides cost-effective, actionable insights that guide sales and marketing strategies.

Functionality-Based Apps: AI-Driven Features Enhance User Experiences

As the name suggests, functionality-based applications integrate AI into how the app functions, but the end user drives the action and outcome. In this category of apps, AI takes a more active role in the user’s interaction by allowing them to use AI-created or processed output. These apps leverage AI technologies like Azure AI Services for NLP, image recognition, and pattern recognition to improve user experience.

Consider a music app that generates personalized playlists based on your listening habits or your smartphone’s camera app that uses AI to recognize faces and optimize photo settings. Even more advanced are the language translation apps that translate text from one language to another and can even understand and maintain the context and subtleties of the conversation. They use NLP to accomplish these feats.

Similarly, consider the latest e-commerce apps, whose AI-driven functionalities enable customers to try on clothes virtually, significantly enhancing the online shopping experience.

Feature-Based Apps: Integrating Advanced AI/ML Components

Finally, there are feature-based intelligent apps. These apps incorporate advanced AI/ML components like neural networks, internal large language models (LLMs), and sophisticated algorithms as core features for the user. In this type of application, AI is the primary feature for users to engage with and the primary selling point for the app.

Some examples of feature-based applications are chatbots and virtual agents built using Azure Bot Service. These robo-representatives use AI to provide more natural and human-like conversation, balancing superior efficiency with a friendlier, more personalized user experience. The new Bing is a prime example of this implementation, integrating AI to give users a knowledgeable copilot for web searches.

Similarly, OpenAI’s ChatGPT allows users access to its state-of-the-art language models available as Microsoft’s Azure OpenAI Service. It leverages the power of the highly sophisticated GPT-3 LLM to perform tasks like drafting emails, writing code, answering general knowledge questions, translating between languages, and tutoring in numerous subjects.

These apps don’t just use AI to provide outputs or improve user experiences: They push the boundaries of what’s possible in app development by integrating AI/ML components.

info

Register for the live Q&A Ask The Expert session on September 26 with the Azure Functions product engineering team.

Breaking Down the Role of AI in Intelligent Applications

Intelligent apps reflect the merging of traditional software design with the dynamic capabilities of AI. Let’s explore how AI integrates and amplifies these applications.

At its core, AI augments applications with the capacity to self-learn and self-improve. It allows you to create applications that predict, react, and adapt to a constantly changing environment. In the power grid management system for a major city, an AI-infused application can forecast power usage trends, react to anomalies, and adapt to sudden demand surges, ensuring optimal efficiency and preventing blackouts.

But AI’s role doesn’t end at prediction or decision-making: It also automates tasks. In enterprise solutions, it has moved beyond simple automation to encompass intelligent process automation (IPA). In this arena, AI algorithms understand, learn, and then automate entire digital workflows. For developers, this is a fertile ground for creating applications that can dramatically improve the operational efficiency of businesses.

Even more, AI substantially aids in development tasks. For example, there’s GitHub Copilot, an AI-powered code assistant that offers suggestions based not only on syntax but on development best practices and the intent of the code. AI has evolved from a tool you use to build apps to a partner that helps in creating them. 

Using AI to build intelligent apps isn’t about throwing out what’s worked before. It’s about enhancing the components that have the most impact and achieve the best results. Incorporating AI in apps elevates their predictive abilities, streamlines complex tasks, and even aids in the development and creation process. When you build intelligent apps, you don’t just address current problems. You anticipate future challenges and craft solutions that adapt over time.

Summary

As developers the power of AI is in our hands, and it’s our responsibility to harness its full potential. Each category tells the story of AI's transformative potential, from applications that deliver intelligent outcomes to those with AI capabilities to enrich the user experience and those pushing boundaries with advanced AI/ML technology.

Accessible and robust platforms like Azure AI can simplify the process of developing Intelligent Applications. Whether you’re refining an existing app or building a new one, just entering the world of AI, or already familiar with the landscape, take a moment to appreciate the potential that AI integration offers. You’re embracing this exciting frontier and shaping the future of app development. 

Exercise

  • Complete the Cloud Skills Challenge to build on your apps and AI skills.
  • Watch Episode 01 of the Serverless Edition Learn Live session to learn how to build an end-to-end intelligent app solution.
+ + \ No newline at end of file diff --git a/30daysofIA/tags/community-buzz/page/9/index.html b/30daysofIA/tags/community-buzz/page/9/index.html index 01b3e2325a..2b0191aa27 100644 --- a/30daysofIA/tags/community-buzz/page/9/index.html +++ b/30daysofIA/tags/community-buzz/page/9/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "community-buzz" | Build Intelligent Apps On Azure +11 posts tagged with "community-buzz" | Build Intelligent Apps On Azure @@ -14,13 +14,14 @@ - - + +
-

9 posts tagged with "community-buzz"

View All Tags

· 2 min read
It's 30DaysOfIA

September is almost here - and that can only mean one thing!! It's time to 🍂 Fall for something new and exciting and spend a few weeks skilling up on relevant tools, techologies and solutions!!

Last year, we focused on #ServerlessSeptember. This year, we're building on that theme with the addition of cloud-scale Data, cloud-native Technologies and cloud-based AI integrations to help you modernize and build intelligent apps for the enterprise!

Watch this space - and join us in September to learn more!

- - +
Skip to main content

11 posts tagged with "community-buzz"

View All Tags

· 4 min read
It's 30DaysOfIA

This Fall focus on building intelligent apps using AI and cloud-native technologies. #FallForIntelligentApps brings to you a learning journey to build your skills on creating differentiated experiences while modernizing your applications. It’s time to learn it all.

What We'll Cover

image

Get Ready To #FallForIntelligentApps starting September 18!

Today, we kick off the Fall season with content and activities to skill you up on all things Intelligent Apps or AI Apps on Azure with content, events, and community interactions! Read on to learn about what is coming!

Explore Our Initiatives

We have a number of initiatives planned for the month to help you learn and skill up on relevant technologies. Click on the links to visit the relevant pages for each

image

Register for the events!

What are 4 things you can do today, to jumpstart your learning journey?

#30Days Of Intelligent Apps

#30DaysOfIA is a series of daily blog posts grouped into themed weeks - taking you from core concepts to end-to-end solution examples in 30 days. Each article will provide conceptual lessons paired with exercises and resources to help you reinforce learnings and take next steps.

This series takes you through learning journey in six stages, each building on the previous week to help you skill up in a beginner-friendly way:

We will start with defining intelligent apps and then expand on how to build with cloud-native technologies like Azure Kubernetes Service, Azure Container Apps and Azure Functions, and take you through end-to-end scenarios for real world application development. Before we dive deep on intelligent apps, here is a high-level overview of the Intelligent Apps landscape on Azure:

image

Containers on Azure services offer you a wide range of capabilities, from simplicity to control to suit your different needs.

image +image

To start with the basics for developing Kubernetes applications, explore #30DaysOfCloudNative.

Cloud-native development when paired with serverless computing enhances your solution architecture for building cost optimized, resilient applications.

image

To start with the basics for serverless computing, explore #30DaysOfServerless.

Let’s Get Started

Now you know everything! We hope you are as excited as we are to dive into a full month of active learning and doing! Don't forget to subscribe for updates in your favorite feed reader! And look out for our first Intelligent Apps post Monday!

+ + \ No newline at end of file diff --git a/30daysofIA/tags/fall-for-ia/index.html b/30daysofIA/tags/fall-for-ia/index.html index 4b9a0d9609..550c03b0de 100644 --- a/30daysofIA/tags/fall-for-ia/index.html +++ b/30daysofIA/tags/fall-for-ia/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "Fall-For-IA" | Build Intelligent Apps On Azure +11 posts tagged with "Fall-For-IA" | Build Intelligent Apps On Azure @@ -14,13 +14,13 @@ - - + +
-

9 posts tagged with "Fall-For-IA"

View All Tags

· 10 min read
It's 30DaysOfIA

Prepare your organization to adopt intelligent apps by taking a change management approach. Assess your readiness, upskill teams, create a culture of change and overcome resistance, and prepare for future evolution to pave the way for a smooth transition into an AI-centric company.

What We'll Cover:

  • Role of change management in modernizing to intelligent apps
  • Skills needed for building intelligent apps in your organization
  • How to build a culture of change to embrace AI
  • How to prepare for the future of intelligent apps and continuous evolution

image of modernizing AI solutions for intelligent apps

Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management

As we’ve discussed throughout this series, intelligent apps and generative AI are rewriting the rules of business and application development. The first article of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” explored how intelligent apps can enhance user experiences and drive businesses growth. These advancements are opening new opportunities and markets for companies ready to embrace them.

However, embracing this digital transformation is more complex than flipping a switch. It requires adjustments to the technical side, which we explored in the series’ fifth article, “Preparing the Path for Intelligent Apps: Transitioning from On-Premises/IaaS to Cloud Native Applications.” It also demands a shift in how organizations adapt to change and approach app design and development. Let’s explore that mindset transition with tips to help you make it happen.

The Role of Change Management in Transitioning to Intelligent Apps

Transitioning to intelligent apps isn’t merely a technological change. It’s a profound shift in how businesses perceive and operate. This move toward artificial intelligence (AI)-centric approach isn’t meant to replace humans with machines or radically overhaul all business operations.

Instead, intelligent apps should augment human capabilities, streamline processes, and foster an environment where innovation flourishes. They expand toolkits and techniques to include AI-assisted development tools like GitHub Copilot and AI-enabled workflows, helping us design and deliver AI-enhanced experiences.

Effective change management strategies are crucial in helping employees adapt to these new ways of working with intelligent apps, ensuring that the transition doesn’t disrupt productivity or morale.

Team and organization strategies should: 

  • Assess the impact of any proposed changes.  
  • Communicate the benefits and reasons behind the change to all affected team members.  
  • Provide the necessary training and support.  
  • Integrate the changes into the culture and daily operations. 

By incorporating this robust change management approach, you can identify and mitigate potential transition risks early. Moreover, this strategy ensures that employees are engaged throughout the process, fostering a sense of involvement and ownership. This, in turn, cultivates a culture suited for innovation through intelligent apps. 

These organizational changes focus on the following aspects: organizational readiness, skillset, culture of change and overcoming resistance, and adaptability. Let’s explore them below.

info

Register for the intelligent apps webinar on Driving Business Value by Modernizing with Cloud-Native & AI with Microsoft and Forrester on September 26. 

Explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries. 

Identifying Organizational Readiness for Intelligent Apps

The first step in preparing your organization for intelligent apps is to assess your current readiness for change. Analyze various elements of your organization, from infrastructure and workforce skills to strategic vision and overall culture.

Can your apps and systems integrate with AI platforms like Microsoft Azure AI? Is your workforce comfortable working alongside AI, or are they fearful and resistant? Does your strategic vision incorporate AI’s transformative potential, or does it fail to acknowledge AI as a critical driver of future growth?

These questions identify the challenges of integrating AI into an organization and determine the company’s readiness for intelligent apps.

Embracing intelligent apps involves more than being ready with your technological infrastructure or having the right technology or tools. You need flexible organizational structures and processes driving your business to accommodate new ideas and capabilities. Your teams must feel empowered to own and drive them. Much like Microsoft’s shift to AI-powered services—like AI-powered Bing and Edge and Microsoft 365 Copilot—embracing AI organization-wide requires a holistic vision and willingness to innovate, with teams capable of delivering on the plan.

After accessing these critical areas for organizational readiness, you can start planning a strategy for this transition to an AI-ready organization. Identify skill gaps and work to bridge them while nurturing an environment that appreciates and harnesses the potential of AI and intelligent apps.

Nurturing the Indispensable Skills for Intelligent Apps

Tasking your AI specialists with preparing your organization to use and build intelligent apps isn’t enough. Accomplishing this goal requires a cross-functional effort and a diverse skill set.

Developing intelligent apps involves data scientists managing and interpreting vast amounts of data, machine learning engineers building and training the models, and traditional developers integrating these models into usable applications. However, the impact of generative AI spans beyond creating apps. AI fluency across the entire organization—from sales and marketing to accounting and legal departments—offers substantial benefits, sparking new ideas and solutions from sometimes unexpected sources.

Teams' AI skills might roughly fall into three levels: 

  • Fundamental skills — Being familiar and efficient with using AI tools, for example, knowing how to write effective prompts for large language models (LLMs).
  • Visionary skills — Recognizing AI’s strengths and weaknesses, for example, its creativity and hallucinations, that is, unexpected output.
  • Expert skills — Managing data and training models and integrating AI technology into apps, such as incorporating the OpenAI application programming interface (API) into an Intelligent App.

Based on these levels, you can gauge your team's skill readiness to pinpoint the most significant gaps, whether in general AI knowledge across your organization or within technical development teams.

As intelligent apps become increasingly prevalent, upskilling your workforce is crucial. This transition opens a new realm of skills and capabilities for your team to master. You can offer machine learning, data analysis, and cloud computing training programs—such as Microsoft’s Azure AI Fundamentals certification—to empower your team to confidently navigate the intelligent apps landscape.

Nurturing these skills within your organization may require hiring new talent, reskilling current employees, and creating an environment that promotes learning and experimentation. Specifically, if your team understands tools like the Microsoft Azure AI platform, they could contribute significantly to developing and maintaining intelligent apps.

An upskilled team is more than a functional unit. It’s a think tank to drive innovation and improve your organization’s competitive standing in the digital marketplace.

Building a Culture of Change to Embrace AI

Even with the right people and skills in place, change remains an uphill battle for organizations committed to traditional processes. 

Transitioning to intelligent apps is as much about mindset as technology and skills. It requires fostering a culture open to change and innovation that understands AI’s transformative potential and embraces it to enhance rather than replace human capabilities. 

Leaders play a pivotal role in cultivating this mindset. As they model a positive attitude toward AI, emphasize its benefits, and address any concerns transparently, leaders significantly contribute to the organization’s readiness to adopt intelligent apps. 

The most critical factor in this transition is enabling an AI-friendly culture. It’s about encouraging curiosity, rewarding innovative thinking, and creating an environment where people thrive on learning and experimentation. 

Empowering teams to leverage practical AI tools and AI-driven data insights to aid decision-making helps motivate employees to use and explore AI’s potential, improve their skills, and contribute to the successful integration of intelligent apps. Such an environment is fertile ground to grow breakthrough ideas and innovative solutions. 

Overcoming Resistance to Change and Adopting Intelligent Apps

Despite AI’s potential, change isn’t easy. It takes effort and is especially challenging for organizations used to existing processes. This resistance to change is a significant roadblock when transitioning to intelligent apps. 

Overcoming this resistance involves:  

  • Understanding employees’ concerns. 
  • Challenging underlying assumptions about traditional systems and outdated processes that hinder change. 
  • Providing comprehensive training for workers to use AI effectively.
  • Demonstrating the benefits and opportunities of intelligent apps. 
  • Clear and continuous communication to dispel fears and misconceptions about AI.

These steps will foster greater acceptance and enthusiasm for intelligent apps in your teams. 

Preparing for the Future of Intelligent Apps and Continuous Evolution

Finally, it’s essential to understand that generative AI is just beginning to gain momentum. Adjusting to a world of intelligent apps isn’t a one-time change—it’s a continuous evolution. 

As AI technologies mature and evolve and more businesses realize their benefits, we can expect the landscape of intelligent apps to expand further. Organizations can cultivate a mindset of continuous learning, flexibility, and adaptation to prepare for this future. Staying ahead in this dynamic field requires organizations to keep up with the latest AI advancements, continually reassess their strategies, and pivot when necessary. 

The domain of intelligent apps is rapidly advancing. Organizations must prepare to navigate this dynamic landscape to stay ahead of the curve.

Summary

Preparing for AI and intelligent apps is a strategic decision. It requires thoughtful preparation and a culture that embraces change.

With the proper preparation and mindset, adopting AI can become a smoother, collaborative path to innovation for the whole organization. Integrating AI into your organization does not discard what has worked in the past. It simply enhances and augments those processes for a better future.

Consider the points above to assess your organization’s readiness and prepare to welcome intelligent apps.

In the first week of this series, you learned about intelligent apps, how organizations today find success through intelligent apps, what AI-assisted application development looks like, ways to leverage the power of generative AI, the technical changes needed to prepare for intelligent apps, and, in this article, the organizational changes required to harness the power of AI.

The future is intelligent. Use this series as a guideline to navigate challenges while preparing for and embracing AI's opportunities for an intelligent tomorrow.

Exercise

- - +
Skip to main content

11 posts tagged with "Fall-For-IA"

View All Tags

· 10 min read
It's 30DaysOfIA

Prepare your organization to adopt intelligent apps by taking a change management approach. Assess your readiness, upskill teams, create a culture of change and overcome resistance, and prepare for future evolution to pave the way for a smooth transition into an AI-centric company.

What We'll Cover:

  • Role of change management in modernizing to intelligent apps
  • Skills needed for building intelligent apps in your organization
  • How to build a culture of change to embrace AI
  • How to prepare for the future of intelligent apps and continuous evolution

image of modernizing AI solutions for intelligent apps

Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management

As we’ve discussed throughout this series, intelligent apps and generative AI are rewriting the rules of business and application development. The first article of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” explored how intelligent apps can enhance user experiences and drive businesses growth. These advancements are opening new opportunities and markets for companies ready to embrace them.

However, embracing this digital transformation is more complex than flipping a switch. It requires adjustments to the technical side, which we explored in the series’ fifth article, “Preparing the Path for Intelligent Apps: Transitioning from On-Premises/IaaS to Cloud Native Applications.” It also demands a shift in how organizations adapt to change and approach app design and development. Let’s explore that mindset transition with tips to help you make it happen.

The Role of Change Management in Transitioning to Intelligent Apps

Transitioning to intelligent apps isn’t merely a technological change. It’s a profound shift in how businesses perceive and operate. This move toward artificial intelligence (AI)-centric approach isn’t meant to replace humans with machines or radically overhaul all business operations.

Instead, intelligent apps should augment human capabilities, streamline processes, and foster an environment where innovation flourishes. They expand toolkits and techniques to include AI-assisted development tools like GitHub Copilot and AI-enabled workflows, helping us design and deliver AI-enhanced experiences.

Effective change management strategies are crucial in helping employees adapt to these new ways of working with intelligent apps, ensuring that the transition doesn’t disrupt productivity or morale.

Team and organization strategies should: 

  • Assess the impact of any proposed changes.  
  • Communicate the benefits and reasons behind the change to all affected team members.  
  • Provide the necessary training and support.  
  • Integrate the changes into the culture and daily operations. 

By incorporating this robust change management approach, you can identify and mitigate potential transition risks early. Moreover, this strategy ensures that employees are engaged throughout the process, fostering a sense of involvement and ownership. This, in turn, cultivates a culture suited for innovation through intelligent apps. 

These organizational changes focus on the following aspects: organizational readiness, skillset, culture of change and overcoming resistance, and adaptability. Let’s explore them below.

info

Register for the intelligent apps webinar on Driving Business Value by Modernizing with Cloud-Native & AI with Microsoft and Forrester on September 26. 

Explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries. 

Identifying Organizational Readiness for Intelligent Apps

The first step in preparing your organization for intelligent apps is to assess your current readiness for change. Analyze various elements of your organization, from infrastructure and workforce skills to strategic vision and overall culture.

Can your apps and systems integrate with AI platforms like Microsoft Azure AI? Is your workforce comfortable working alongside AI, or are they fearful and resistant? Does your strategic vision incorporate AI’s transformative potential, or does it fail to acknowledge AI as a critical driver of future growth?

These questions identify the challenges of integrating AI into an organization and determine the company’s readiness for intelligent apps.

Embracing intelligent apps involves more than being ready with your technological infrastructure or having the right technology or tools. You need flexible organizational structures and processes driving your business to accommodate new ideas and capabilities. Your teams must feel empowered to own and drive them. Much like Microsoft’s shift to AI-powered services—like AI-powered Bing and Edge and Microsoft 365 Copilot—embracing AI organization-wide requires a holistic vision and willingness to innovate, with teams capable of delivering on the plan.

After accessing these critical areas for organizational readiness, you can start planning a strategy for this transition to an AI-ready organization. Identify skill gaps and work to bridge them while nurturing an environment that appreciates and harnesses the potential of AI and intelligent apps.

Nurturing the Indispensable Skills for Intelligent Apps

Tasking your AI specialists with preparing your organization to use and build intelligent apps isn’t enough. Accomplishing this goal requires a cross-functional effort and a diverse skill set.

Developing intelligent apps involves data scientists managing and interpreting vast amounts of data, machine learning engineers building and training the models, and traditional developers integrating these models into usable applications. However, the impact of generative AI spans beyond creating apps. AI fluency across the entire organization—from sales and marketing to accounting and legal departments—offers substantial benefits, sparking new ideas and solutions from sometimes unexpected sources.

Teams' AI skills might roughly fall into three levels: 

  • Fundamental skills — Being familiar and efficient with using AI tools, for example, knowing how to write effective prompts for large language models (LLMs).
  • Visionary skills — Recognizing AI’s strengths and weaknesses, for example, its creativity and hallucinations, that is, unexpected output.
  • Expert skills — Managing data and training models and integrating AI technology into apps, such as incorporating the OpenAI application programming interface (API) into an Intelligent App.

Based on these levels, you can gauge your team's skill readiness to pinpoint the most significant gaps, whether in general AI knowledge across your organization or within technical development teams.

As intelligent apps become increasingly prevalent, upskilling your workforce is crucial. This transition opens a new realm of skills and capabilities for your team to master. You can offer machine learning, data analysis, and cloud computing training programs—such as Microsoft’s Azure AI Fundamentals certification—to empower your team to confidently navigate the intelligent apps landscape.

Nurturing these skills within your organization may require hiring new talent, reskilling current employees, and creating an environment that promotes learning and experimentation. Specifically, if your team understands tools like the Microsoft Azure AI platform, they could contribute significantly to developing and maintaining intelligent apps.

An upskilled team is more than a functional unit. It’s a think tank to drive innovation and improve your organization’s competitive standing in the digital marketplace.

Building a Culture of Change to Embrace AI

Even with the right people and skills in place, change remains an uphill battle for organizations committed to traditional processes. 

Transitioning to intelligent apps is as much about mindset as technology and skills. It requires fostering a culture open to change and innovation that understands AI’s transformative potential and embraces it to enhance rather than replace human capabilities. 

Leaders play a pivotal role in cultivating this mindset. As they model a positive attitude toward AI, emphasize its benefits, and address any concerns transparently, leaders significantly contribute to the organization’s readiness to adopt intelligent apps. 

The most critical factor in this transition is enabling an AI-friendly culture. It’s about encouraging curiosity, rewarding innovative thinking, and creating an environment where people thrive on learning and experimentation. 

Empowering teams to leverage practical AI tools and AI-driven data insights to aid decision-making helps motivate employees to use and explore AI’s potential, improve their skills, and contribute to the successful integration of intelligent apps. Such an environment is fertile ground to grow breakthrough ideas and innovative solutions. 

Overcoming Resistance to Change and Adopting Intelligent Apps

Despite AI’s potential, change isn’t easy. It takes effort and is especially challenging for organizations used to existing processes. This resistance to change is a significant roadblock when transitioning to intelligent apps. 

Overcoming this resistance involves:  

  • Understanding employees’ concerns. 
  • Challenging underlying assumptions about traditional systems and outdated processes that hinder change. 
  • Providing comprehensive training for workers to use AI effectively.
  • Demonstrating the benefits and opportunities of intelligent apps. 
  • Clear and continuous communication to dispel fears and misconceptions about AI.

These steps will foster greater acceptance and enthusiasm for intelligent apps in your teams. 

Preparing for the Future of Intelligent Apps and Continuous Evolution

Finally, it’s essential to understand that generative AI is just beginning to gain momentum. Adjusting to a world of intelligent apps isn’t a one-time change—it’s a continuous evolution. 

As AI technologies mature and evolve and more businesses realize their benefits, we can expect the landscape of intelligent apps to expand further. Organizations can cultivate a mindset of continuous learning, flexibility, and adaptation to prepare for this future. Staying ahead in this dynamic field requires organizations to keep up with the latest AI advancements, continually reassess their strategies, and pivot when necessary. 

The domain of intelligent apps is rapidly advancing. Organizations must prepare to navigate this dynamic landscape to stay ahead of the curve.

Summary

Preparing for AI and intelligent apps is a strategic decision. It requires thoughtful preparation and a culture that embraces change.

With the proper preparation and mindset, adopting AI can become a smoother, collaborative path to innovation for the whole organization. Integrating AI into your organization does not discard what has worked in the past. It simply enhances and augments those processes for a better future.

Consider the points above to assess your organization’s readiness and prepare to welcome intelligent apps.

In the first week of this series, you learned about intelligent apps, how organizations today find success through intelligent apps, what AI-assisted application development looks like, ways to leverage the power of generative AI, the technical changes needed to prepare for intelligent apps, and, in this article, the organizational changes required to harness the power of AI.

The future is intelligent. Use this series as a guideline to navigate challenges while preparing for and embracing AI's opportunities for an intelligent tomorrow.

Exercise

+ + \ No newline at end of file diff --git a/30daysofIA/tags/fall-for-ia/page/10/index.html b/30daysofIA/tags/fall-for-ia/page/10/index.html new file mode 100644 index 0000000000..d4e80d7fdb --- /dev/null +++ b/30daysofIA/tags/fall-for-ia/page/10/index.html @@ -0,0 +1,27 @@ + + + + + +11 posts tagged with "Fall-For-IA" | Build Intelligent Apps On Azure + + + + + + + + + + + + + + +
+

11 posts tagged with "Fall-For-IA"

View All Tags

· 4 min read
It's 30DaysOfIA

Continue The Learning Journey through Fall For Intelligent Apps! 🍂

What We'll Cover

Thank you! ♥️

image

It's hard to believe that JavaScript on Azure hack-together is ending! It seems like just yesterday that we launched this initiative, and yet here we are, 15 days later, with an incredible amount of learning and growth behind us. So... it's time for a wrap!

From the bottom of our hearts, we want to thank each and every one of you for your participation, engagement, and enthusiasm. It's been truly inspiring to see the passion and dedication from this strong community, and we're honored to be a part of it. ✨

Recap of The JavaScript on Azure Global Hack-Together

As we wrap up this exciting event, we wanted to take a moment to reflect on all that we've accomplished together. Over the last 15 days, we've covered a lot of ground, from the basics of contributing to Open source to the exploration of the Contoso Real Estate project from its Frontend to its Backend and future AI implementation.

Now that the hack-together is ending, we want to make sure that you have all the resources you need to continue honing your skills in the future. Whether you're looking to make your fist contribution to open source, become an open source maintainers, collaborate with others, or simply keep learning, there are plenty of resources out there to help you achieve your goals. So, let's dive in and explore all the ways you can continue to grow your JavaScript skills on Azure!

JSonAzure Hack-together Roadmap 📍:

hack-together-roadmap (2)

Recap on past Livestreams🌟:

Day 1️⃣: Opening Keynote (Hack-together Launch): Introduction to the Contoso Real Estate Open-source project!, managing complex and complex enterprise architecture, new announcements for JavaScript developers on Azure

Day 2️⃣: GitHub Copilot & Codespaces: Introduction to your AI pair programmer (GitHub Copilot) and your virtual developer environment on the cloud (GitHub Codespaces)

Day 6️⃣: Build your Frontend using Static Web Apps as part of a complex, modern composable frontends (or micro-frontends) and cloud-native applications.

Day 9️⃣: Build a Serverless Backend using Azure Functions

Day 1️⃣3️⃣: Easily connect to an Azure Cosmos DB, exploring its benefits and how to get started

Day 1️⃣5️⃣: Being in the AI Era, we dive into the Azure OpenAI Service and how you can start to build intelligent JavaScript applications

📖 Self-Learning Resources

  1. JavaScript on Azure Global Hack Together Module collection
  2. Lets #HackTogether: Javascript On Azure Keynote
  3. Step by Step Guide: Migrating v3 to v4 programming model for Azure Functions for Node.Js Application

Continue your journey with #FallForIntelligentApps

Join us this Fall on a learning journey to explore building intelligent apps. Combine the power of AI, cloud-native app development, and cloud-scale data to build highly differentiated digital experiences. Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure. Engage in a self-paced learning adventure by following along the #30Days of Intelligent Apps series, completing the Intelligent Apps Skills Challenge, joining the Product Group for a live Ask The Expert series or building an end to end solution architecture with a live guided experience through the Learn Live series. Discover more here.

Hands-on practice: Make your first contribution to open source!

Join our GitHUb Discussion Forum to connect with developers from every part of world, see contributions from other, find collaborators and make your first contribution to a real-world project! +Don't forget to give the repo a star ⭐

Resources

All resources are accessible on our landing page

+ + + + \ No newline at end of file diff --git a/30daysofIA/tags/fall-for-ia/page/11/index.html b/30daysofIA/tags/fall-for-ia/page/11/index.html new file mode 100644 index 0000000000..1cfc08cfb6 --- /dev/null +++ b/30daysofIA/tags/fall-for-ia/page/11/index.html @@ -0,0 +1,26 @@ + + + + + +11 posts tagged with "Fall-For-IA" | Build Intelligent Apps On Azure + + + + + + + + + + + + + + +
+

11 posts tagged with "Fall-For-IA"

View All Tags

· 2 min read
It's 30DaysOfIA

September is almost here - and that can only mean one thing!! It's time to 🍂 Fall for something new and exciting and spend a few weeks skilling up on relevant tools, techologies and solutions!!

Last year, we focused on #ServerlessSeptember. This year, we're building on that theme with the addition of cloud-scale Data, cloud-native Technologies and cloud-based AI integrations to help you modernize and build intelligent apps for the enterprise!

Watch this space - and join us in September to learn more!

+ + + + \ No newline at end of file diff --git a/30daysofIA/tags/fall-for-ia/page/2/index.html b/30daysofIA/tags/fall-for-ia/page/2/index.html index 587e2c7f8e..51c84771b7 100644 --- a/30daysofIA/tags/fall-for-ia/page/2/index.html +++ b/30daysofIA/tags/fall-for-ia/page/2/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "Fall-For-IA" | Build Intelligent Apps On Azure +11 posts tagged with "Fall-For-IA" | Build Intelligent Apps On Azure @@ -14,13 +14,13 @@ - - + +
-

9 posts tagged with "Fall-For-IA"

View All Tags

· 9 min read
It's 30DaysOfIA

Learn best practices for modernizing your on-premises or IaaS solutions to intelligent apps in the cloud.

What We'll Cover:

  • Traditional vs Intelligent Apps
  • On-Premises/IaaS vs. Cloud-Native Platforms
  • Modernization strategy for building Intelligent Apps

image of modernizing AI solutions for intelligent apps

Preparing the Path for Intelligent Apps: Transitioning from On-Premises/IaaS to Cloud-Native Applications

The proliferation of generative AI is paving the way for intelligent applications, software that leverages AI capabilities to deliver unparalleled functionality and user experience. As we covered in a previous article, “Demystifying Intelligent Applications: Leveraging AI in App Development,” intelligent Apps aren’t just products of advanced coding. They interact, learn, and evolve thanks to modern AI and machine learning (ML) breakthroughs. 

The paradigm shift from traditional to intelligent apps demands that we change our approach to the technical challenges of software and architecture design. It also necessitates adopting new ways of thinking and operating within an organization, which we explore in greater depth in “Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management.” 

This article explores the technical infrastructure powering intelligent apps, providing a roadmap to transition traditional on-premises or Infrastructure as a Service (IaaS) solutions to intelligent apps deployed to cloud native platforms and services. We’ll discover the differences between conventional and intelligent apps, options for re-architecting existing applications, and critical strategic considerations integral to modernizing the application-building approach. 

Understanding the Shift: Traditional vs. Intelligent Applications

Intelligent apps are more than just an incremental evolution from traditional apps. They involve a fundamental shift in software solution design, development, and deployment. A clear understanding of how each paradigm differs is crucial for comprehending the impact of this transition. 

Functionality and User Experience

Traditional applications, often rule-based and rigid, rely on preprogrammed operations. A traditional weather app, for example, typically presents structured, location-based forecasts using a predetermined data source. Meanwhile, an intelligent app leverages AI for data-driven decision-making and personalization. Rather than merely displaying the forecast, it integrates ML with additional data sources—like a user’s calendar and fitness data—to determine their favorite outdoor activities, provide relevant clothing suggestions, and even automatically design ideal vacations and times based on the user’s optimal weather preferences. 

Similarly, while traditional applications tend to offer a one-size-fits-all UI, intelligent apps use AI to create tailored interactions. For instance, an intelligent banking app might use generative AI to provide a voice-enabled UI that lets customers use natural language to ask about transactions and receive personalized financial advice.

Infrastructure

Intelligent apps integrating these sophisticated features require highly scalable, flexible infrastructure optimized for AI functionality and able to manage large volumes of real-time data. Unlike traditional applications deployed on-premises or in infrastructure as a service (IaaS) environments, intelligent apps demand the flexibility and scalability of cloud native architectures.

An on-premises or IaaS-based e-commerce application may struggle with connecting its data to AI services or scaling to accommodate voluminous real-time data and requests. But an intelligent app built with a cloud native approach and scalability at its core can handle variable demand much more efficiently. It also maintains its infrastructure components on demand, whether for collecting data for AI/ML consumption or user-facing features like AI-enabled customer support and personalization.

Consider an intelligent app built on Azure that creates ChatGPT-like experiences with custom data. It might connect Azure Open AI and Azure Cognitive Search to an app running on Azure Container Apps or Azure Kubernetes Service. 

The architecture might look like the following diagram based on this demo app:

diagram of a demo app architecture

While the cloud is well-equipped to handle the challenges associated with shifting to AI-centric infrastructure—handling real-time data processing and storage, integrating and managing AI services, configuring event-driven and microservices architectures, and ensuring security across the distributed environment—re-architecting a traditional app to an intelligent app requires careful planning and execution to ensure it benefits fully from the capabilities of AI and cloud computing.

On-Premises/IaaS vs. Cloud-Native Platforms

While on-premises and IaaS solutions provide substantial control over data and processes, this fine-grained control comes with setup and maintenance costs and may lack the scalability necessary for AI-driven Intelligent Apps. On-premises hosting involves purchasing and maintaining hardware, including servers and storage systems. IaaS involves leasing virtual hardware and then hiring and training IT personnel for the infrastructure’s operation, maintenance, and security.

Conversely, infrastructure built on cloud native app services with Microsoft Azure offers virtually unlimited scalability without ongoing hardware and maintenance costs. This feature is ideal for intelligent apps that need to adapt and scale to accommodate fluctuating workloads and data streams. And because these cloud infrastructures typically operate on a pay-as-you-go model, traditionally substantial upfront costs turn into predictable operating expenses, lowering both the cost and risk of launching applications.

Moreover, cloud service providers continually update their offerings, so building on sophisticated AI tools and services through the cloud, like the Azure AI Platform and orchestration capabilities with Azure Kubernetes Service, ensures that you have access to the latest AI and most advanced AI models and technologies.

Strategic Considerations for Transitioning to Intelligent Apps

When developing a strategy for transitioning to intelligent apps, you must consider costs, personnel skill development, and data management.

Monolithic architectures are unlikely to support the scale and ease of iteration required by intelligent apps, so you’ll likely need to implement code changes to support the transition. Your chosen hosting solution and the amount of data your app will handle affect cost and performance, in addition to determining privacy and compliance requirements.

An app that manages or stores terabytes or petabytes of data will require storage-optimized virtual machines or a managed database service hosting. Meanwhile, an app that processes video content or performs resource-intensive calculations should prioritize high-performance computing. Maximizing storage and performance can become extraordinarily costly.

Finally, existing applications shifting towards intelligent apps may encounter compatibility conflicts, depending on database type or the existing system’s underlying programming language. As you might imagine, a traditional app running on-premises and using a shared local database may need significant restructuring to support a cloud native architecture. You need plans for data migration, potential downtime, and training.

Next, let’s look at this transformation process.

Re-Architecting Monolithic Apps into Microservices

Shifting from monolithic applications to microservices is pivotal for transitioning to Intelligent Apps, and you can use this shift as an opportunity to transform your legacy software into intelligent apps that leverage the flexibility and power of microservices-enabled AI solutions.

Traditionally, a monolithic application (let’s use an online marketplace as an example) might handle all activities—from serving webpages to processing payments—within an on-premises server or a cloud server. However, this approach is challenging to scale and remains vulnerable to network failures or cyberattacks.

Breaking these applications into multiple services with specific responsibilities, such as authentication or notifications, can overcome these limitations. These services are typically deployed as microservices within container environments like Azure Kubernetes Service or serverless platforms like Azure Container Apps or Azure Functions using automated processes. This approach offers scalability, robustness, and improved performance, as each microservice can run independently and use its own database.

Shifting towards smaller, independent microservices in app architectures allows us to reap the benefits of AI-powered intelligent apps, for example, by using microservices to handle AI tasks such as processing natural language input, analyzing behavioral data, or personalizing content.

info

Watch Episode 1 and Episode 2 of the Learn Live series on how to build, test and deploy an end-to-end intelligent app solution using the Contoso Real Estate Sample.

Event-Driven Architectures: Knowing When to Use Them

Event-Driven Architectures (EDAs) can significantly enhance the performance and cost-effectiveness of intelligent apps. EDAs, typically implemented with microservices, respond in real time to events or state changes ranging from user interactions to real-time analytics.

Consider an intelligent app designed for personalized customer engagement. Instead of designing the application to check continuously for changes in user data, an EDA can react to such changes immediately, triggering appropriate microservices. For example, if users modify their preferences, an event could trigger an AI-driven recommendation microservice to update its suggestions immediately. This real-time responsiveness, enabled by services like Azure Event Grid, can improve user experiences and make intelligent apps more adaptive and proactive.

However, it’s worth noting that while EDAs provide significant benefits, they can introduce unique complexities, such as maintaining event consistency and debugging issues in event chains across multiple microservices. Therefore, it’s important to assess your application’s needs so you can understand where it can benefit from an EDA.

Conclusion

Transitioning to intelligent apps represents a shift in strategy, architecture, and infrastructure. By designing and re-architecting for cloud-based infrastructure, your apps gain scalability, cost-effectiveness, robust data management, access to sophisticated AI tools, enhanced security, and reduced operational overhead.  

But this transition extends well beyond its technical components. Organizations and the people working within them need equal attention. You can explore these facets in greater depth in “Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management.” 

The power of generative AI is here, and preparing for the move toward cloud-based intelligent applications will ensure that your apps remain performative and scalable and—most importantly—that your business is ready for the newest era of app innovation.

Exercise

- - +
Skip to main content

11 posts tagged with "Fall-For-IA"

View All Tags

· 9 min read
It's 30DaysOfIA

Learn best practices for modernizing your on-premises or IaaS solutions to intelligent apps in the cloud.

What We'll Cover:

  • Traditional vs Intelligent Apps
  • On-Premises/IaaS vs. Cloud-Native Platforms
  • Modernization strategy for building Intelligent Apps

image of modernizing AI solutions for intelligent apps

Preparing the Path for Intelligent Apps: Transitioning from On-Premises/IaaS to Cloud-Native Applications

The proliferation of generative AI is paving the way for intelligent applications, software that leverages AI capabilities to deliver unparalleled functionality and user experience. As we covered in a previous article, “Demystifying Intelligent Applications: Leveraging AI in App Development,” intelligent Apps aren’t just products of advanced coding. They interact, learn, and evolve thanks to modern AI and machine learning (ML) breakthroughs. 

The paradigm shift from traditional to intelligent apps demands that we change our approach to the technical challenges of software and architecture design. It also necessitates adopting new ways of thinking and operating within an organization, which we explore in greater depth in “Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management.” 

This article explores the technical infrastructure powering intelligent apps, providing a roadmap to transition traditional on-premises or Infrastructure as a Service (IaaS) solutions to intelligent apps deployed to cloud native platforms and services. We’ll discover the differences between conventional and intelligent apps, options for re-architecting existing applications, and critical strategic considerations integral to modernizing the application-building approach. 

Understanding the Shift: Traditional vs. Intelligent Applications

Intelligent apps are more than just an incremental evolution from traditional apps. They involve a fundamental shift in software solution design, development, and deployment. A clear understanding of how each paradigm differs is crucial for comprehending the impact of this transition. 

Functionality and User Experience

Traditional applications, often rule-based and rigid, rely on preprogrammed operations. A traditional weather app, for example, typically presents structured, location-based forecasts using a predetermined data source. Meanwhile, an intelligent app leverages AI for data-driven decision-making and personalization. Rather than merely displaying the forecast, it integrates ML with additional data sources—like a user’s calendar and fitness data—to determine their favorite outdoor activities, provide relevant clothing suggestions, and even automatically design ideal vacations and times based on the user’s optimal weather preferences. 

Similarly, while traditional applications tend to offer a one-size-fits-all UI, intelligent apps use AI to create tailored interactions. For instance, an intelligent banking app might use generative AI to provide a voice-enabled UI that lets customers use natural language to ask about transactions and receive personalized financial advice.

Infrastructure

Intelligent apps integrating these sophisticated features require highly scalable, flexible infrastructure optimized for AI functionality and able to manage large volumes of real-time data. Unlike traditional applications deployed on-premises or in infrastructure as a service (IaaS) environments, intelligent apps demand the flexibility and scalability of cloud native architectures.

An on-premises or IaaS-based e-commerce application may struggle with connecting its data to AI services or scaling to accommodate voluminous real-time data and requests. But an intelligent app built with a cloud native approach and scalability at its core can handle variable demand much more efficiently. It also maintains its infrastructure components on demand, whether for collecting data for AI/ML consumption or user-facing features like AI-enabled customer support and personalization.

Consider an intelligent app built on Azure that creates ChatGPT-like experiences with custom data. It might connect Azure Open AI and Azure Cognitive Search to an app running on Azure Container Apps or Azure Kubernetes Service. 

The architecture might look like the following diagram based on this demo app:

diagram of a demo app architecture

While the cloud is well-equipped to handle the challenges associated with shifting to AI-centric infrastructure—handling real-time data processing and storage, integrating and managing AI services, configuring event-driven and microservices architectures, and ensuring security across the distributed environment—re-architecting a traditional app to an intelligent app requires careful planning and execution to ensure it benefits fully from the capabilities of AI and cloud computing.

On-Premises/IaaS vs. Cloud-Native Platforms

While on-premises and IaaS solutions provide substantial control over data and processes, this fine-grained control comes with setup and maintenance costs and may lack the scalability necessary for AI-driven Intelligent Apps. On-premises hosting involves purchasing and maintaining hardware, including servers and storage systems. IaaS involves leasing virtual hardware and then hiring and training IT personnel for the infrastructure’s operation, maintenance, and security.

Conversely, infrastructure built on cloud native app services with Microsoft Azure offers virtually unlimited scalability without ongoing hardware and maintenance costs. This feature is ideal for intelligent apps that need to adapt and scale to accommodate fluctuating workloads and data streams. And because these cloud infrastructures typically operate on a pay-as-you-go model, traditionally substantial upfront costs turn into predictable operating expenses, lowering both the cost and risk of launching applications.

Moreover, cloud service providers continually update their offerings, so building on sophisticated AI tools and services through the cloud, like the Azure AI Platform and orchestration capabilities with Azure Kubernetes Service, ensures that you have access to the latest AI and most advanced AI models and technologies.

Strategic Considerations for Transitioning to Intelligent Apps

When developing a strategy for transitioning to intelligent apps, you must consider costs, personnel skill development, and data management.

Monolithic architectures are unlikely to support the scale and ease of iteration required by intelligent apps, so you’ll likely need to implement code changes to support the transition. Your chosen hosting solution and the amount of data your app will handle affect cost and performance, in addition to determining privacy and compliance requirements.

An app that manages or stores terabytes or petabytes of data will require storage-optimized virtual machines or a managed database service hosting. Meanwhile, an app that processes video content or performs resource-intensive calculations should prioritize high-performance computing. Maximizing storage and performance can become extraordinarily costly.

Finally, existing applications shifting towards intelligent apps may encounter compatibility conflicts, depending on database type or the existing system’s underlying programming language. As you might imagine, a traditional app running on-premises and using a shared local database may need significant restructuring to support a cloud native architecture. You need plans for data migration, potential downtime, and training.

Next, let’s look at this transformation process.

Re-Architecting Monolithic Apps into Microservices

Shifting from monolithic applications to microservices is pivotal for transitioning to Intelligent Apps, and you can use this shift as an opportunity to transform your legacy software into intelligent apps that leverage the flexibility and power of microservices-enabled AI solutions.

Traditionally, a monolithic application (let’s use an online marketplace as an example) might handle all activities—from serving webpages to processing payments—within an on-premises server or a cloud server. However, this approach is challenging to scale and remains vulnerable to network failures or cyberattacks.

Breaking these applications into multiple services with specific responsibilities, such as authentication or notifications, can overcome these limitations. These services are typically deployed as microservices within container environments like Azure Kubernetes Service or serverless platforms like Azure Container Apps or Azure Functions using automated processes. This approach offers scalability, robustness, and improved performance, as each microservice can run independently and use its own database.

Shifting towards smaller, independent microservices in app architectures allows us to reap the benefits of AI-powered intelligent apps, for example, by using microservices to handle AI tasks such as processing natural language input, analyzing behavioral data, or personalizing content.

info

Watch Episode 1 and Episode 2 of the Learn Live series on how to build, test and deploy an end-to-end intelligent app solution using the Contoso Real Estate Sample.

Event-Driven Architectures: Knowing When to Use Them

Event-Driven Architectures (EDAs) can significantly enhance the performance and cost-effectiveness of intelligent apps. EDAs, typically implemented with microservices, respond in real time to events or state changes ranging from user interactions to real-time analytics.

Consider an intelligent app designed for personalized customer engagement. Instead of designing the application to check continuously for changes in user data, an EDA can react to such changes immediately, triggering appropriate microservices. For example, if users modify their preferences, an event could trigger an AI-driven recommendation microservice to update its suggestions immediately. This real-time responsiveness, enabled by services like Azure Event Grid, can improve user experiences and make intelligent apps more adaptive and proactive.

However, it’s worth noting that while EDAs provide significant benefits, they can introduce unique complexities, such as maintaining event consistency and debugging issues in event chains across multiple microservices. Therefore, it’s important to assess your application’s needs so you can understand where it can benefit from an EDA.

Conclusion

Transitioning to intelligent apps represents a shift in strategy, architecture, and infrastructure. By designing and re-architecting for cloud-based infrastructure, your apps gain scalability, cost-effectiveness, robust data management, access to sophisticated AI tools, enhanced security, and reduced operational overhead.  

But this transition extends well beyond its technical components. Organizations and the people working within them need equal attention. You can explore these facets in greater depth in “Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management.” 

The power of generative AI is here, and preparing for the move toward cloud-based intelligent applications will ensure that your apps remain performative and scalable and—most importantly—that your business is ready for the newest era of app innovation.

Exercise

+ + \ No newline at end of file diff --git a/30daysofIA/tags/fall-for-ia/page/3/index.html b/30daysofIA/tags/fall-for-ia/page/3/index.html index 6570183071..3a28539ca1 100644 --- a/30daysofIA/tags/fall-for-ia/page/3/index.html +++ b/30daysofIA/tags/fall-for-ia/page/3/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "Fall-For-IA" | Build Intelligent Apps On Azure +11 posts tagged with "Fall-For-IA" | Build Intelligent Apps On Azure @@ -14,13 +14,13 @@ - - + +
-

9 posts tagged with "Fall-For-IA"

View All Tags

· 10 min read
It's 30DaysOfIA

Explore how digital natives and Independent Software Vendors (ISVs) can transform their business operations and generate higher value for customers with generative AI and intelligent apps.

What We'll Cover:

  • Potential of Generative AI in application development
  • Challenges with Generative AI
  • Integrating Generative AI with business operations

image of generative AI and intelligent apps used in business

Unleashing Business Transformation: How Digital Natives Can Leverage Generative AI

Generative AI is no longer a technological flight of fancy. It’s here, and it’s rewriting the rules of business innovation. 

This type of AI leverages machine learning algorithms to create new data with similar characteristics to a given dataset. It goes beyond text generation using generative pretrained transformer (GPT) models, covering an array of applications that span from image synthesis to translations and more. It also powers a new class of intelligent applications, which combine AI capabilities with traditional software functions for enhanced user experiences and advanced business solutions. 

Generative AI is transforming how digital natives and independent software vendors (ISVs) operate. Generative AI offers new possibilities for creating unique business value, enhancing customer experiences, and driving growth. 

It plays a critical role in applications, improving functionality, aiding decision-making, and automating complex tasks to deliver dynamic, uniquely personalized user experiences. Generative AI also enables and automates data-driven outcomes, functions, and features.

Integrating AI capabilities into customer-facing apps and turning them into intelligent apps benefits both the customer and the business, as we discussed in Demystifying Intelligent Applications: Leveraging AI in App Development. You get faster product development workflows and better business intelligence for sales and marketing, while your users get more natural and context-aware features and interactions. 

That said, to realize generative AI’s potential, you need some strategy and knowledge of how and where it fits into your current business and operations. We’ll examine this technology’s potential to transform business operations, its risks, and rewards, and how to integrate it into your existing operations. 

Unpacking the Potential of Generative AI

Generative AI is creating new opportunities in the digital playing field. It empowers businesses to generate insights, design products, and automate processes in ways and at speeds that were previously unimaginable. You can now predict customer behavior more accurately, offer highly personalized user experiences, and prototype innovative ideas rapidly. 

For example, Microsoft used generative AI to reinvent web search in the new AI-powered Bing and Edge. The search experience has evolved from a solitary user flow of typing in keywords and scrolling through results. It’s now a conversational experience where Bing uses context to offer better quality and more personalized search queries and results. 

Similarly, Microsoft harnessed generative AI to transform its productivity suite into intelligent apps. Through Copilot for Microsoft 365, PowerPoint now empowers users to tell their stories by using DALL-E to generate images effortlessly. It also helps rephrase text-heavy slides to be more straightforward. Outlook now assists users to compose more effective emails, enabling better communication.

These are just some examples of how AI powers day-to-day work and operations. 

Businesses that ignore generative AI miss out on untapped market opportunities, letting competitors gain an edge in the digital race. By seeking ways to rethink assumptions and reinvent existing approaches, you can effectively use generative AI to create new opportunities, open products to new use cases and markets, unlock new growth and customer satisfaction levels, and increase your revenue.

info

Register for the intelligent apps webinar on Driving Business Value by Modernizing with Cloud-Native & AI with Microsoft and Forrester on September 26.

Explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries. 

The Crucial Role of Generative AI in Business Operations

Generative AI can be a powerful catalyst for business transformation. Let’s quickly tour some ways it can redefine business operations. 

Product development is traditionally a long and painstaking process. Now, tools like GitHub Copilot and TestPilot use generative AI to make coding and testing software easier, faster, and more robust. 

Similarly, machine learning as a service technology, such as Azure Machine Learning, democratizes the availability of machine learning. It helps collect, clean, and train models across datasets, making engineering and analyzing data more efficient and accessible.  

Generative AI also simplifies the process of creating intelligent apps. Previously, simulating thousands of prototypes, predicting their performance, and narrowing down to the most viable options could take weeks, months, or even years of iteration and data analysis. With generative AI, this process can be as quick as days, hours, or perhaps minutes. 

Customer experience is also ripe for transformation. Personalized recommendations drive engagement and sales, and generative AI can scale that personalization to every customer interaction using comprehensive and continually updated data. For instance, Microsoft leverages AI to enable personalization in Dynamics 365 Customer Insights. Using this tool, you can ask questions about your customers in natural language and gain valuable and actionable insights into your customer base. 

Lastly, marketing and sales can benefit from generative AI. For example, identifying current and future key markets and emerging trends helps companies stay ahead of the curve. Tools like Copilot in Power BI help you quickly understand and uncover insights about your business, enabling your team to craft targeted campaigns, create engaging content, and optimize your business strategy. 

Leveraging this full generative AI potential requires the right tools. GPT-4, for instance, can be instrumental in realizing effective business transformation. It excels in understanding context, generating human-like text, and learning from minimal data input, making it a formidable tool in any digital native's arsenal. Platforms like Azure OpenAI Service bring GPT-4’s power to your fingertips.

However, as with any powerful technology, it’s unwise to treat generative AI as a perfect solution. You must know the challenges and risks of not leveraging it effectively, including technical and human-factor considerations. 

Without generative AI, your company risks missing the opportunity to please customers via personalization, as discussed previously. You’ll miss sales opportunities via business intelligence insights, limiting your ability to grow revenue and keep up with the competition. 

One challenge of leveraging generative AI’s full potential is finding the right tools. Fortunately, you can implement generative AI in your organization without investing in infrastructure, computational power, and expertise. Platforms like Azure OpenAI Service and Azure AI Services make AI easily accessible. They also work with Azure compute and data services like Azure Functions and Azure Kubernetes Service (AKS), enabling you to build intelligent apps easily and quickly. 

However, generative AI can’t do everything. It may need human expertise and intervention to use it fully. 

For example, generative AI can create unique data. However, the data may have limited diversity and complexity. The AI may need help recognizing additional considerations or requirements specific to a problem. It may also lack context due to various factors, such as the information’s freshness. Also, since it’s an emerging field that’s potentially worth $4.4 trillion, we’re still learning about the nuances and potential regulatory impact of generative AI. 

Perhaps the most crucial and challenging aspect of adopting generative AI is an organization’s required cultural shift. Your teams must learn how to use AI as your company fosters an understanding of AI’s potential and limitations. 

Begin integrating AI literacy into your company’s DNA. Knowledge of its potential and the readily available tools can inspire your teams to incorporate generative AI into their daily workflows and business operations quickly, improve productivity, and deliver superior customer experiences. 

No company wants to be outpaced by its competitors. You can balance generative AI’s potential with your business operations by staying aware of and adapting to the dynamic AI landscape.

Achieving Harmony: Integrating Generative AI with Existing Business Operations

Integrating generative AI into existing business operations doesn’t have to be disruptive. With the correct strategy, you can do so seamlessly. Your goal should be to augment and enhance your operations, not disrupt and replace. 

For example, Microsoft Cloud technologies leveraged AI to relieve healthcare organizations’ challenges, such as talent shortage and workforce burnout. Providers automated their workflows, invested in specific applications and connectors, and enhanced support for data and operations. 

Azure AI Services, such as text analytics for health, enable healthcare organizations to understand their largely unstructured and untapped medical data. Project Health Insights leans on AI to help healthcare professionals gain actionable insights and inferences from patient data. 

Azure Health Bot, an Intelligent App, empowers organizations to build and deploy legally compliant, powerful conversational healthcare experiences to patients. 

A successful AI integration like this begins with understanding. Knowing what generative AI can do and setting realistic expectations can help you identify where and how AI can best serve your operations. 

Next, you can foster a culture of AI literacy across teams, speeding up adoption by helping them understand how it can aid their work. The more familiar your team is with AI and its possibilities, the smoother the transition. Training and education programs play a pivotal role here. 

Finally, integration requires a systematic approach. It’s not about overhauling everything at once — it's about identifying areas where AI can add the most value, then gradually experimenting and scaling its use. Successful integration stories often feature a phased approach, starting small and growing steadily. 

As we move toward a future where generative AI is a staple in business operations, remember that every journey is unique. Although there’s no one-size-fits-all solution, a strategic approach and a keen understanding of your business needs will help you navigate this new landscape successfully.

Conclusion

Generative AI offers plenty of opportunities for businesses that embrace it. It’s fueling the next generation of intelligent applications, transforming everything from product development to customer engagement to marketing. This technology is the key to unlocking unprecedented growth and innovation. 

While the journey may have challenges, the potential rewards make generative AI worth exploring. As generative AI advances, Azure AI’s broad range of tools and services will help you navigate this fascinating field.

Exercise

  • Watch Episode 02 of the serverless edition Learn Live session to learn how to build, test and deploy an end-to-end intelligent app solution.
  • Complete the Cloud Skills Challenge to build on your AI skills.
  • Register for Ask the Expert: Azure Functions on September 26 for a live Q&A with the Product Team on how to build AI apps using serverless computing.
- - +
Skip to main content

11 posts tagged with "Fall-For-IA"

View All Tags

· 12 min read
It's 30DaysOfIA

Delve into creating an Intelligent App that leverages Azure AI Vision to analyze images and extract data. Develop an API to perform optical character recognition (OCR) on uploaded images and testing this API locally.

What We'll Cover:

  • Understanding Azure AI Vision and Azure Kubernetes Service
  • Build a Python Web API to perform OCR
  • Test the API locally

image depicting an Intelligent App using AI

Jumpstart Your AI Journey: Building Your First Intelligent App with Azure AI and AKS (1)

An Intelligent App is a software application that uses artificial intelligence (AI), such as machine learning (ML), to extract meaningful insights, enabling users to make informed decisions based on the extracted text, data, sound, images, and more.

Intelligent Apps can employ advanced algorithms and models to recognize objects, patterns, and features within images, allowing them to perform tasks like object identification, image classification, facial recognition, and image segmentation.

To explore the power of Intelligent Apps, let’s build a Python app that performs optical character recognition (OCR) on images containing numbers and performs aggregate functions on the extracted numbers, like sum and average. We’ll leverage Azure AI Services for the OCR functionality and Azure Kubernetes Service (AKS) to develop, publish, and maintain a cloud-based, scalable app in Azure.

Let’s get started!

Understanding Azure AI Vision and Azure Kubernetes Service

Azure AI Vision is a cloud-based service that offers a range of advanced computer vision capabilities enabling applications to interpret content, such as images and videos, using ML and other AI algorithms. Azure computer vision lets users integrate powerful vision features into their projects without ML experience.

OCR is one of the critical functionalities within Azure vision service, allowing applications to extract text from images. Within Azure AI Vision service, OCR offers several features, including text extraction, multilingual support, layout analysis of documents, and integration with Azure services, such as Azure Functions and Azure Machine Learning.

info

Watch the intelligent apps webinar on Driving Business Value by Modernizing with Cloud-Native & AI with Microsoft and Forrester on September 26.

AKS is Microsoft Azure’s container orchestration platform that enables us to quickly and efficiently deploy, manage, and scale containerized applications, such as our Intelligent App.

Let’s explore how to leverage these tools to build an Intelligent App that uses Azure AI Vision to analyze images and extract data.

Prerequisites

To follow this tutorial, ensure you have the following:

For a preview of this final project, take a look at the complete project code.

Building the API with Azure AI Vision Service

First, log in to your Azure account and navigate to the Azure Portal.

Click Create a resource and search for “resource group.” Create a new resource group named computer-vision.

image in Azure Portal of creating a new resource group

Return to the Azure Portal home page and click Create a resource.

image in Azure Portal of creating a new resource

Search for “computer vision” and select it from the results. Click Create.

image of searching for resource group in Azure Portal

Clicking Create displays the Create Computer Vision wizard. Select the resource group you just created as the Resource group from the dropdown. Enter a resource name similar to what is seen in the screenshot below. Note that this must be a unique name so choose one that is not currently in use. Select Standard S1 as the pricing tier and check the Responsible use of AI box in the field at the bottom.

Image of selecting the Responsible use of AI box

Image of project details

Click Review + Create, then click Create.

Next, get the API key and endpoint from the Azure AI services multi-service account you created. Once the resource is created, navigate to the resource details page. Under Resource Management, select Keys and Endpoint. Once on the Keys and Endpoint page, you’ll find the Key 1 and Endpoint values. These credentials are necessary to access the Azure AI APIs.

image of selecting Keys and Endpoint

second image of selecting Keys and Endpoint

Configuring the Local Environment Variables

Next, we need to define a local environment variable file. This convenient way to store our API keys in a simple text file enables us to manage sensitive information and separate configuration from our API code.

If you haven’t done so already, clone the starter project template from GitHub into your local computer. Open the starter project template in a code editor and create a new .env file in the root folder.

Note: Root folder in this case is the “/Microsoft_Series17-18_Code/intelligent-app-before" folder.

Add the following key-value pairs to the file, remembering to replace the placeholder values with those you obtained from the previous step.

VISION_KEY=<THE-KEY-1-VALUE-FROM-YOUR-AZURE-AI-SERVICE>
VISION_ENDPOINT=<THE-ENDPOINT-VALUE-FROM-YOUR-AZURE-AI-SERVICE>

Reviewing the Quickstart Code

Let’s review the app.py file in the starter project template.

The app.py file works as the entry point for the Flask application. It is responsible for initializing the web server, listening for incoming requests, dispatching them to the appropriate route handlers, and generating responses.

Below is the code contained in the app.py file:

import os
import json

from flask import Flask, request
from flask_restful import Resource, Api
from werkzeug.utils import secure_filename

app = Flask(__name__,
static_url_path='',
static_folder='static/files')

api = Api(app)

app = Flask(__name__)

app.config['UPLOAD_FOLDER'] = 'files'

api = Api(app)

class UploadHandler(Resource):

def allowed_file(self, filename):
return '.' in filename and \
filename.rsplit('.', 1)[1].lower() in {'png'}

def post(self):
form = request.form.to_dict()

if 'file' not in request.files:
return json.dumps({ "success": False, "error": "No file part"})

file = request.files.get("file")
if file and self.allowed_file(file.filename):
filename = secure_filename(file.filename)
upload_folder = "static/files"
if not os.path.exists(upload_folder):
os.makedirs(upload_folder)
local_file_path = os.path.join(upload_folder, filename)
file.save(local_file_path)

return f"File {filename} uploaded successfully to folder: {upload_folder}"

api.add_resource(UploadHandler, "/")

if __name__ == '__main__':
app.run(debug=True)

The UploadHandler class above is defined as a Flask RESTful resource to handle file uploads. The class contains two methods:

  • The allowed_file method checks whether the file extension is allowed for upload. In this case, only .png files are allowed.

  • The post method handles HTTP POST requests for file uploads. It saves the uploaded .png file to the static/files folder.

Finally, the application returns a text response informing us that the file has been uploaded successfully.

Implementing the REST API in Python

To implement image analysis in your REST API, open the starter project template in your terminal, create a virtual environment, and activate it.

Next, install the Azure AI Vision SDK:

pip install azure-ai-vision

Then, add the following line to the requirements.txt file to include the azure-ai-vision package:

azure-ai-vision==0.13.0b1

Now, create an ocr_helper.py file in the project’s root folder. This file is a Python module that provides functions for processing images using OCR. Add the following code to the file:

import os
from statistics import median
from decimal import Decimal
import azure.ai.vision as sdk

def process_ocr(source_image):
service_options = sdk.VisionServiceOptions(os.environ["VISION_ENDPOINT"],
os.environ["VISION_KEY"])

vision_source = sdk.VisionSource(filename=source_image)

analysis_options = sdk.ImageAnalysisOptions()

analysis_options.features = (
sdk.ImageAnalysisFeature.CAPTION |
sdk.ImageAnalysisFeature.TEXT
)

analysis_options.language = "en"

analysis_options.gender_neutral_caption = True

image_analyzer = sdk.ImageAnalyzer(service_options, vision_source, analysis_options)

result = image_analyzer.analyze()

ocr_result = get_ocr_result(result)

return ocr_result

def get_ocr_result(result):
string_list = []

if result.reason != sdk.ImageAnalysisResultReason.ANALYZED:
return sdk.ImageAnalysisErrorDetails.from_result(result)
else:
if result.text is not None:
for line in result.text.lines:
for word in line.words:
string_list.append(word.content)

number_list = convert_to_decimal_list(string_list)

aggregate_result = aggregate_operations(number_list)

return {
"aggregate_result": aggregate_result,
"numbers_read": string_list
}

def convert_to_decimal_list(string_list):
return list(map(Decimal, string_list))

def aggregate_operations(numbers):
result = {
'sum': sum(numbers),
'average': sum(numbers) / len(numbers),
'median': median(numbers),
'min': min(numbers),
'max': max(numbers)
`}`
`return result`

This module uses the azure-ai-vision package to analyze images, including capturing captions and extracting text from the image. The process_ocr function inputs an image file and performs the OCR analysis. The module is a convenient tool for analyzing text-based data in pictures and performing numerical computations based on that data.

Let’s review the different components of the ocr_helper.py module listed above and explore how its functions help our Intelligent App process images and perform aggregate operations:

  • The process_ocr function takes the parameter source_image, which is the path of the image to be processed. The function then initializes the VisionServiceOptions using environment variables VISION_ENDPOINT and VISION_KEY to connect to the Azure AI Vision API.
  • The process_ocr function creates a VisionSource object with the specified source_image file name. ImageAnalysisOptions specify the features to be analyzed, including CAPTION and TEXT. The language is set to English ("en"), and gender-neutral captions are enabled.
  • Finally, an ImageAnalyzer object is created with the service options, vision source, and analysis options. The image is then analyzed using the image_analyzer.analyze method to retrieve OCR results. The recognized text from the OCR result is extracted, with numerical values stored in the string_list variable.
  • The convert_to_decimal_list function converts the list of strings to a list of decimal numbers, which helps process numeric values extracted from the text.
  • The aggregate_operations function calculates various aggregate statistics (sum, average, median, minimum, maximum) from a list of numbers and returns the results in a dictionary.

Note that you must have the appropriate credentials (VISION_KEY) and endpoint (VISION_ENDPOINT) configured for the Azure AI Vision API to use this module.

Finally, we must modify the app.py file so our code can use the process_ocr function of the ocr_helper.py file.

Add the following import statement to the app.py file:

from ocr_helper import process_ocr

Then, replace this line:

return f"File {filename} uploaded successfully to folder: {upload_folder}"

With these two lines:

aggregates = process_ocr(local_file_path)
return json.dumps(aggregates, default=str)

Doing so allows our application to perform aggregate operations (sum, average, median, minimum, maximum) of any numeric values found in the text and return the operation’s result as a JSON response.

Running the Intelligent App Locally

Our final goal is to deploy the sample app to AKS, which will run the app in a container. However, as a good practice, let’s run the app in a container locally before moving it to AKS. Doing so ensures that our application and its dependencies are encapsulated and can be run consistently across different environments.

Deploying to Docker Desktop

For our application to run on Docker, it needs two additional files: a Dockerfile and docker-compose.yml. A Dockerfile creates a single container image by specifying the steps to build it, and a docker-compose.yml file manages multiple containers as a cohesive application, defining their configurations and relationships. Dockerfile builds the image, while docker-compose.yml orchestrates multi-container applications.

In the project root folder, add a file named Dockerfile with the content below:

# syntax=docker/dockerfile:1

FROM python:3.8-slim-buster

WORKDIR /intelligentapp

COPY requirements.txt requirements.txt
RUN pip3 install -r requirements.txt

RUN pip install debugpy

COPY . .

CMD [ "python3", "-m" , "flask", "run", "--host=0.0.0.0"]

Now create a file named docker-compose.yml file in the project’s root folder with the following code, replacing the VISION_KEY and the VISION_ENDPOINT according to the environment variables you configured earlier:

version: '3.8'
services:
intelligentapp:
build:
context: .
dockerfile: Dockerfile
image: intelligent-app
ports:
- 5000:5000
container_name: intelligent-app
environment:
- VISION_KEY=<THE-KEY-1-VALUE-FROM-YOUR-AZURE-AI-SERVICE>
- VISION_ENDPOINT=<THE-ENDPOINT-VALUE-FROM-YOUR-AZURE-AI-SERVICE>

Then, run the following command in the terminal to build the image and start a container for the Intelligent App services defined in the docker-compose.yml file:

docker-compose up --build --force-recreate

Next, open a new terminal and run the command below to list the image deployed to your local Docker:

docker images

docker images

Testing the Intelligent App Locally

Now, we’ll test the Intelligent App’s functionality using the following test image:

test image to test functionality

This image is provided as sample1.png in the sample app source files, so you can easily use it in the next step.

The Intelligent App will read the image above using OCR to extract numbers in the image and perform five aggregate functions:

  • Sum
  • Average
  • Median
  • Min
  • Max

To test the API, open Postman and fill out the fields as follows:

  • URL: http://localhost:5000/
  • Method: POST
  • Body:
    • Form-data
      • Key: File — Click the right end of the Key field and select File from the dropdown list.
      • Value: Click Select Files, then select the sample1.png file provided in the sample code.

image of selecting the sample file

Now click the Send button and review the result body:

"{\"aggregate_result\": {\"sum\": \"25821\", \"average\": \"5164.2\", \"median\": \"5622\", \"min\": \"1447\", \"max\": \"9802\"}, \"numbers_read\": [\"3049\", \"5901\", \"5622\", \"1447\", \"9802\"]}"

As we can see, the app running on a local container returned the correct results based on the sample image. So, we’re ready to prepare our Azure resources to run the Intelligent App on AKS.

Exercise

In this article, we explored the creation of an Intelligent App that leverages Azure AI Vision to analyze images and extract data. We learned how to build a Python Web API to perform OCR on uploaded images and subsequently test this API locally.

Head to the next part, to deploy this API via Azure Kubernetes Service.

+ + \ No newline at end of file diff --git a/30daysofIA/tags/fall-for-ia/page/4/index.html b/30daysofIA/tags/fall-for-ia/page/4/index.html index f4e33ebb77..204b350249 100644 --- a/30daysofIA/tags/fall-for-ia/page/4/index.html +++ b/30daysofIA/tags/fall-for-ia/page/4/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "Fall-For-IA" | Build Intelligent Apps On Azure +11 posts tagged with "Fall-For-IA" | Build Intelligent Apps On Azure @@ -14,13 +14,13 @@ - - + +
-

9 posts tagged with "Fall-For-IA"

View All Tags

· 9 min read
It's 30DaysOfIA

In the part of our series on intelligent apps, we’ll explore how AI is transforming application development, from design and architecture to building.

What We'll Cover:

  • Revolutionizing application development using AI
  • Infusing AI in application design architecture
  • AI Assisted Pair Programming
  • Future of App Development

A decorative image of application development with AI

Reimagining Application Development With AI: A New Paradigm

Artificial intelligence (AI) is impacting every industry. AI-powered development tools offer new functionalities and assist software developers throughout the development process, revolutionizing app development. From initial design and architecture to coding and debugging, AI reshapes how we approach application conception, creation, and iteration.

In part one of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” we explored the new breed of intelligent apps and how they use AI. In this article, we’ll see how AI technologies enhance developer toolkits and shape the future of app development, particularly through AI-enabled coding assistance and debugging.

The Dawn of Intelligent Apps: Revolutionizing Application Development

AI is more than just another addition to the development toolbox—it represents a revolution bringing unprecedented efficiency, innovation, and user engagement to our applications. 

What sets intelligent apps apart is how they can adapt and learn, helping us deliver unique, real-time user experiences.  

One example is an AI-driven health application that can track biometric data, predict potential health issues, provide personalized health advice, and even guide emergency services during a medical crisis. An AI-enabled language learning app that adapts to our learning speeds, recognizes our problem areas, and customizes its content to help us improve our language proficiencies is another example.  

Intelligent apps like these don’t just use data for recommendations—they can make critical decisions and provide sophisticated services that were previously impossible. 

For developers, intelligent apps mean we’re no longer just coding—we’re teaching applications how to think, adapt, and make decisions. AI is reshaping every step of the development journey, helping us to build more intuitive and user-focused products. 

Next, we’ll examine the impact of AI on application design and architecture and how AI-assisted “pair programming” changes software development.

info

Register for the FREE webinar on Intelligent Apps with Microsoft and Forrester on September 26 to explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries.

There will be a showcase of real-world use cases that demonstrate how AI can be seamlessly integrated into cloud-native environments driving tangible business value.

AI in Design and Architecture: Fueling Creativity and Efficiency

AI technologies help developers automate and streamline their processes and provide new ways to think about design.  

In traditional application development, the design and architecture phase can take significant time, effort, and expertise. For instance, adopting a monolithic architecture or a microservices-based design can significantly increase coding, testing, and debugging complexity. The app may even behave differently when hosted locally than in a deployed environment. It may take hours to track down an issue manually—time developers could spend elsewhere. 

Conversely, we can generate numerous design iterations in seconds with AI, each optimized to fulfill specific user needs and business goals. AI tools can quickly model various scenarios' impacts and software design patterns in the architectural design phase. It provides insights into performance and potential issues and suggests improvements. AI can also generate skeleton code, allowing developers to prototype different solutions or test specific components and technologies rapidly. By reviewing thousands of lines of code in seconds, AI allows us to focus on creating apps that serve users through feature development instead of searching for bugs.  

Integrating AI into the design and architecture phase can even extend our creative capabilities. By leveraging the power of generative AI—like large language models (LLMs)—and their broad knowledge and context going beyond software, we can better brainstorm apps and feature ideas. We can go beyond code and software architecture and get ideas about user experience, logo and branding, or marketing concepts. And as AI tools deliver insights and ideas we would have never considered; it encourages more innovation.

info

Watch Episode 01 of the Learn Live session to learn how to build an end-to-end intelligent app solution architecture.

The Power of AI-Assisted Pair Programming in Building Applications

AI-assisted pair programming is a collaborative coding approach where an AI system—like GitHub Copilot or TestPilot—assists developers during coding. It’s an increasingly common approach that significantly impacts developer productivity. In fact, GitHub Copilot is now behind an average of 46 percent of developers’ code and users are seeing 55 percent faster task completion on average. 

For new software developers, or those interested in learning new skills, AI-assisted pair programming are training wheels for coding. With the benefits of code snippet suggestions, developers can avoid struggling with beginner pitfalls like language syntax. Tools like ChatGPT can act as a personal, on-demand tutor—answering questions, generating code samples, and explaining complex code syntax and logic. These tools dramatically speed the learning process and help developers gain confidence in their coding abilities. 

Building applications with AI tools hastens development and provides more robust code. For example, GitHub Copilot helps us write new code accurately. It also quickly makes sense of existing code, suggests whole new lines or blocks of code, and even detects errors and proposes fixes. It’s like having a second coder working alongside us—one who never tires and is trained on billions of lines of code. 

However, it’s also important to acknowledge the limitations of AI. While generative AI-assistance technologies like GitHub Copilot are impressive, they’re still imperfect. They can sometimes produce erroneous, non-functional, or vulnerable code. They can also lack nuanced, domain-specific knowledge.  

So, although these tools are helpful, we can’t rely on them fully—these are programming aids rather than definitive solutions. AI-assisted pair programming is about augmenting the human developer’s skills, not replacing them.

Debugging and Improving Code With AI: Enhancing Application Quality

Identifying and correcting code errors is often time-consuming and challenging for developers. AI streamlines the debugging process by automatically detecting anomalies, suggesting fixes, and learning from previous bugs to prevent them from recurring. 

We can use generative AI to create unit tests from code snippets, from simple parameter checking and input validation to complex mocks of database and network services. Automated AI testing improves code coverage and robustness. It saves significant time compared to manually crafting unit tests. 

AI also revolutionizes code reviews by scanning and highlighting potential issues—like code patterns leading to a bug or a coding standards violation—and suggesting ways to simplify or refactor the code for readability and performance. This reduces human error and makes the code review process more efficient. Its benefits extend to small teams, where the availability of developers is crucial to deadlines, and large teams, where effective collaboration depends on readability and code consistency. AI further saves time by triaging issues so developers can prioritize the most mission-critical ones. 

Aside from improving code quality, AI is critical to understanding system behavior. Intelligent AI-assisted software development tools can monitor and audit system behavior, giving developers a better insight into how their code performs in different scenarios. AI can track patterns, identify inefficiencies, and provide alerts when they detect unusual behavior—enabling developers to enhance their code further. 

By predicting and identifying potential vulnerabilities, intelligent AI-assisted coding apps like Microsoft Security Copilot are pivotal in ensuring application functionality, reliability, and security. These AI tools are continually trained and upgraded with large codebases and user feedback that refines their responses. This ability to learn from past mistakes makes AI an incredibly powerful tool that will continue to expand its capabilities in the future.

info

Complete the AI Cloud Skills Challenge to build on you AI application development skills with paired programming using generative AI and GitHub Copilot.

AI and the Future of Application Development

The rapid advancements in AI and machine learning technologies point to an exciting future for application development. 

In “The Future of Applications,” software developer Mike Cann illustrates how LLMs might revolutionize application development. Cann that suggests fully-autonomous coding—AI-generated applications created entirely from prompts—may supersede traditional hand-crafted applications. Dynamically generated user interfaces could offer personalized and customizable experiences while we can design APIs for machine consumption and efficiency rather than human readability. Furthermore, we can store data in less structured, more adaptable, and flexible forms. 

AI is already making application development more accessible. With AI handling the complex, technical aspects of development, less-technical individuals can take a greater role in building applications. Consequently, AI can potentially democratize software engineering. 

Intelligent apps will continue to evolve, becoming more efficient, adaptable, and capable of learning from user interactions. Similarly, AI will inevitably become integral in every stage of app development.

Summary

AI has already created a new development paradigm—one in which machine intelligence and human creativity come together to produce innovative, efficient, and robust applications. AI already assists us at every stage of app development, from design and architecture to coding and debugging. 

Developers who embrace AI tools and integrate them into their processes can enhance their productivity, improve the quality of their applications, and expand the possibilities of their application development.  

The future of development is automated and AI-driven, and it’s here today. Getting started is easy—try an AI-powered development tool like GitHub Copilot to see how AI-assisted pair programming can enhance your productivity and code quality.

Exercise

- - +
Skip to main content

11 posts tagged with "Fall-For-IA"

View All Tags

· 9 min read
It's 30DaysOfIA

In the last article, we created an Intelligent App that leverages Azure AI Vision to analyze images and extract data. Develop an API to perform optical character recognition (OCR) on uploaded images and testing this API locally.

In this article, we will deploy the web API to the cloud platform using Azure Kubernetes Service.

What We'll Cover:

  • Understanding Azure AI Vision and Azure Kubernetes Service
  • Build a Python Web API to perform OCR
  • Test the API locally

image depicting an Intelligent App using AI

Jumpstart Your AI Journey: Building Your First Intelligent App with Azure AI and AKS (2)

In the previous article we explored the creation of an Intelligent App that leverages Azure AI Vision to analyze images and extract data. We learned how to build a Python Web API to perform OCR on uploaded images and subsequently test this API locally.

In this article we will use Azure Kubernetes Service (AKS) to develop, publish, and maintain our app in the cloud on Azure.

Let’s get started!

Prerequisites

To follow this tutorial, ensure you have completed Jumpstart Your AI Journey: Building Your First Intelligent App with Azure AI and AKS (1).

Pushing a Container Image to Azure Container Registry (ACR)

To start, open your CLI or terminal and type the following command:

az login

Follow the instructions displayed on your browser to enter your Azure credentials.

Once authenticated, you’ll initiate a secure connection between your local environment and Azure. This process grants you access to cloud services and resources.

Next, type the command below in your terminal to set up a new Azure Container Registry (ACR) to store your container images:

az acr create --resource-group computer-vision --name <name-of-azure-container-registry> --sku Basic

Remember to replace <name-of-azure-container-registry> with your container registry name. The name must be unique within Azure and comply with these rules.

The command above creates an Azure Container Registry (ACR) in the computer-vision resource group under the Basic SKU. This ACR is your secure and private repository for storing container images within Azure.

Next, log in to the registry with the following command:

az acr login -n <name-of-azure-container-registry>

The az acr login command above lets you securely authenticate and access the specified ACR without providing their Azure credentials each time.

Now, run the following command in your terminal. It will display the endpoint URL to log in to and interact with the ACR for pushing and pulling container images.

az acr show --name <name-of-azure-container-registry> --query loginServer --output table

This returns an endpoint URL as follows:

Result
----------------------------------
<name-of-azure-container-registry>.azurecr.io

Now, run the following command to show all container images, their repository, tags, and size:

docker images
REPOSITORYTAGIMAGE IDCREATEDSIZE
intelligent-applatesta7bf9f75361716 hours ago197MB

Tags are necessary to push Docker images to a remote registry like Azure Container Registry. They also let you differentiate between different versions of the same image and upload or download the one you want.

Run the following command to tag your Docker image:

docker tag intelligent-app <name-of-azure-container-registry>.azurecr.io/intelligent-app:v1

Then, run the docker images command again to check your tagged image:

docker images
REPOSITORYTAGIMAGE IDCREATEDSIZE
intelligent-applatestc52168039265About a minute ago197MB
<name-of-azure-container-registry>.azurecr.io/intelligent-appv1c52168039265About a minute ago197MB

Now run the following command so Docker can securely upload the image to your Azure Container Registry:

docker push <name-of-azure-container-registry>.azurecr.io/intelligent-app:v1

Once we've deployed the image to the container registry, AKS can access it during deployment.

info

Watch Episode 03 for the Learn Live session to experience a guided session on how to build, test and deploy an end-to-end intelligent app solution.

Deploying the Intelligent App on Azure Kubernetes Service (AKS)

Before we deploy our Intelligent App to AKS, we need to provision an AKS cluster and define Kubernetes manifests.

To provision an AKS cluster to host our application, we specify the desired configurations for the cluster, such as the number of nodes, node size, and networking options. But first, download and install the Kubernetes command-line tools (kubectl), a client credential plugin implementing Azure authentication:

az aks install-cli

If you’re using Linux, review this tutorial. Then run the following:

sudo az aks install-cli

Next, run the following command in your terminal to enable access to the networking-related resources and services provided by the `Microsoft.Network`` namespace in Azure:

az provider register --namespace Microsoft.Network

Now, we must create an AKS cluster. Run the following command to create an AKS cluster named aks-intelligent-app in the computer-vision resource group.

az aks create --resource-group computer-vision --name aks-intelligent-app --node-count 1 --generate-ssh-keys

The command above specifies the target resource group: computer-vision. The node pool is configured with one virtual machine (VM), and the secure shell (SSH) keys for secure node access are generated automatically.

Next, run the following command to update the AKS cluster you created by attaching it to your ACR. Doing so allows the AKS cluster to pull container images from the specified ACR when deploying workloads to the cluster.

az aks update -n aks-intelligent-app -g computer-vision --attach-acr <name-of-azure-container-registry>

Then, run the command below to configure kubectl to work with our AKS cluster in the computer-vision resource group.

az aks get-credentials --resource-group computer-vision --name aks-intelligent-app

The command above retrieves the necessary credentials and context information required for kubectl to communicate with the AKS cluster.

We still have to define the Kubernetes manifests written in YAML, describing the desired state of our application deployment, including containers, networking, and scaling rules. We’ll prepare these manifests, including Deployment and Service configurations, to define how our application should be deployed and exposed.

Create a folder named Deployment in the project root directory. Next, create two files in the deployment folder: deployment.yml and service.yml.

Add the following configuration to the deployment.yml file, replacing the <name-of-azure-container-registry> placeholder with your registry’s name:

apiVersion: apps/v1
kind: Deployment
metadata:
name: intelligent-app
spec:
replicas: 1
selector:
matchLabels:
app: intelligent-app
template:
metadata:
labels:
app: intelligent-app
spec:
nodeSelector:
kubernetes.io/os: linux
containers:
- name: intelligent-app
image: <name-of-azure-container-registry>.azurecr.io/intelligent-app:v1
resources:
limits:
memory: 512Mi
cpu: "1"
requests:
memory: 256Mi
cpu: "0.2"
ports:
- containerPort: 5000
env:
- name: FLASK_DEBUG
value: "1"
- name: VISION_KEY
value: <THE-KEY-1-VALUE-FROM-YOUR-AZURE-AI-SERVICE>
- name: VISION_ENDPOINT
value: <THE-ENDPOINT-VALUE-FROM-YOUR-AZURE-AI>

Additionally, edit the VISION_KEY and VISION_ENDPOINT environment variables above according to the API key and endpoint of your Azure AI Services instance.

Then, add the following configuration to the service.yml file:

apiVersion: v1
kind: Service
metadata:
name: intelligent-app-service
spec:
type: LoadBalancer
ports:
- protocol: TCP
port: 80
targetPort: 5000
name: port5000
selector:
app: intelligent-app

Now, we’ll deploy our application using kubectl. Applying the Kubernetes manifests creates the necessary resources and deploys our containerized application to the AKS cluster.

First, change the terminal to the deployment folder:

cd Deployment 

Then, run the following command to create or update Kubernetes resources defined in the deployment.yml file:

kubectl apply -f deployment.yml

Create a Kubernetes Service resource in a Kubernetes cluster based on the configuration defined in the service.yml file using the code below:

kubectl apply -f service.yml

Once you’ve applied the resource definition and the service configuration contained in the deployment.yml and the service.yml files, open the aks-intelligent-app Kubernetes Service in the Azure Portal, select Workloads under Kubernetes resources on the sidebar, and find the deployment named intelligent-app. It must have the status “Ready 1/1”. If you encounter an issue with this status, check out these troubleshooting resources.

image of selecting the deployment in Azure Portal

Testing the Intelligent App on AKS

To test the app on AKS, first, run the command below:

kubectl get services

This command lists the Services and their corresponding details, including the Service name, cluster IP address, external IP, and ports.

NAMETYPECLUSTER-IPEXTERNAL-IPPORT(S)AGE
intelligent-app-serviceLoadBalancer10.0.77.6020.121.76.15380:30936/TCP47s
kubernetesClusterIP10.0.0.1<none>443/TCP14m

The output above shows a Kubernetes Service named intelligent-app-service with a type set to LoadBalancer. It’s reachable from within the cluster using the cluster IP 10.0.77.60 and accessible externally via the external IP 20.121.76.153 on port 80 (mapped to port 30936).

Note: Your set of IP addresses will differ. Remember to use your unique external IP address when testing with Postman.

To test the deployed app, go to Postman, replace the URL with the external IP of the Kubernetes Service you just deployed, and click Send:

mage of sending the app in Postman

As we can see, our Intelligent App has successfully deployed to AKS and is functioning on the cloud as expected.

Exercise

Next Steps

In this two-part article, we explored the creation of an Intelligent App that leverages Azure AI Vision to analyze images and extract data. We learned how to build a Python Web API to perform OCR on uploaded images and subsequently deploy this API via Azure Kubernetes Service.

Besides OCR and Image Analysis, you can continue exploring Azure’s vast array of services and experiment further with Azure AI and AKS by applying various practical uses to your Intelligent Apps, including natural language processing, speech recognition and synthesis, sentiment analysis for customer feedback, and automated content moderation.

+ + \ No newline at end of file diff --git a/30daysofIA/tags/fall-for-ia/page/5/index.html b/30daysofIA/tags/fall-for-ia/page/5/index.html index 12b0d5460c..4132a8d165 100644 --- a/30daysofIA/tags/fall-for-ia/page/5/index.html +++ b/30daysofIA/tags/fall-for-ia/page/5/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "Fall-For-IA" | Build Intelligent Apps On Azure +11 posts tagged with "Fall-For-IA" | Build Intelligent Apps On Azure @@ -14,13 +14,13 @@ - - + +
-

9 posts tagged with "Fall-For-IA"

View All Tags

· 10 min read
It's 30DaysOfIA

Explore real-world examples of how Intelligent Apps revolutionize logistics, retail, finance, manufacturing, tech, and medicine.

What We'll Cover:

  • Real-world businesses revolutionizing operations with Intelligent Apps
  • High level architecture for industry scenarios
  • Implementation architecture overview across industries

A decorative header image for the blog

Real-world Success Stories

According to IBM’s 2022 Global AI Adoption Index, 35% of surveyed companies already use AI in their business, and 42% are exploring the idea. Organizations lagging behind the AI curve will quickly fall behind early adopters, who are already enjoying its benefits and boosting their investment in this technology.

In the first part of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” we explored how organizations can use AI-powered apps, or intelligent apps, to transform their operations, decision-making, and customer experiences, leaping ahead of the competition.

Intelligent apps significantly improve operational efficiency and customer engagement across industries like logistics, retail, finance, manufacturing, tech, and healthcare. In this article, we’ll spotlight real-world success stories of businesses revolutionizing operations with intelligent apps using Microsoft Azure’s app, data, and AI services.

info

Register for the Learn Live on September 21 for a live guided experience with SMEs on how to build end-to-end solution architecture for real world implementation of Intelligent Apps.

Streamlining Operations: Intelligent Apps in the Airline Industry

Aerospace pioneer Airbus leveraged intelligent apps to streamline its operations and innovate its services with two ground-breaking modernizations that use Azure apps, data, and AI services to reimagine pilot training and predict aircraft maintenance issues. 

Aviation technology’s rapid evolution compelled Airbus to reconceive their approach to training pilots. The aircraft manufacturer created a pilot training chatbot by combining Azure AI Services with Speech to Text and Text to Speech. The bot helps trainee pilots navigate over 6,000 pages of technical information. Pilots can ask questions verbally for quick answers, aiding their understanding of dense information and speeding up training. And when Airbus needed to accommodate its military consumers, deploying its Kubernetes cluster to a disconnected, “air-gapped” environment met the strict security requirements of military aircraft training.

A chatbot could look like the following diagram as Azure services work together to create a conversational experience. Its components could include security and governance, bot logic and user experience (UX), bot cognition and intelligence, data extract, transfer, and load (ETL) operations, quality assurance and enhancements, logging, and monitoring and reporting.

A diagram of a conversational experience enabled by Azure services

Airbus also used AI to improve its aircraft maintenance and safety. They deployed AI Anomaly Detector to gather and analyze telemetry data from multiple flights, enabling more profound insights into wear and tear on military aircraft operating in harsh conditions. This AI application helps Airbus better understand each airplane’s health. Airbus can predict and fix potential problems before they occur, improving the aircraft’s safety and operational readiness.

AI and Azure are changing how organizations like Airbus operate, even in strict, highly regulated markets with stringent security requirements. And by streamlining pilot training and staying ahead of aircraft maintenance, Airbus helps its military and commercial clients reliably deliver their goods across the globe, maintaining their critical supply chains.

Boosting Customer Service: Intelligent Apps in the Retail Industry

The retail industry has been among the first to adopt and benefit from intelligent apps. For example, the leading American used car retailer, CarMax, uses intelligent apps to transform the car shopping experience.

Before embracing AI, CarMax faced a significant challenge: Manually creating text summaries was time-consuming and labor-intensive. CarMax decided to leverage Azure OpenAI Service to automatically generate car research pages, offering customers valuable insights while enhancing the website’s search engine rankings.

This AI-driven approach has significantly streamlined the process, achieving in a few months what would have taken years manually. Also, the AI system condenses thousands of customer reviews into a few readable sentences, offering potential buyers a quick overview of others’ experiences.

CarMax has improved their customer experience while boosting operational efficiency through AI adoption. CarMax’s editorial staff focuses on creating strategic, longer-form content with the time saved, enhancing productivity, and driving more website traffic.

CarMax’s success story serves as a testament to the potential of intelligent apps in the retail industry.

Enhancing Cybersecurity: Intelligent Apps in Finance

Intelligent apps also impact the highly regulated financial services sector. Swift, a leading infrastructure provider for financial messaging services, uses intelligent apps to enhance decision-making and combat financial crime.

The massive growth of cross-border transactions and instant payment networks led to a surge in financial fraud that can cost the industry hundreds of billions of dollars every year. So, Swift and Microsoft partnered to build an anomaly detection model using federated learning techniques with Azure Machine Learning and Azure confidential computing.

Microsoft Purview helped govern data, as the federated learning method partially trained each model in a silo before aggregating them. This approach enabled Swift and Microsoft to develop a highly accurate model without copying or moving data from Swift members’ secure locations, ensuring the highest level of security and privacy.

Financial organizations can now deploy this trained ML model to run a real-time fraud detection system, lowering the cost of financial fraud.

Organizations can deploy a fraud detection system like the following example on Microsoft Azure. The system ingests data from streams or storage, runs analytics, and uses ML to detect fraud and store results. It then alerts users and other systems to the security issue.

A diagram of an example fraud detection system

Swift provides the innovative model to its banking partners, who can share data to further train the model. Azure confidential computing securely runs new models on current data to accumulate insights, generating an ongoing learning cycle. This iterative approach strengthens the global detection of fraudulent financial transactions through an increasingly faster and more accurate model. 

The collaboration between Swift and Microsoft demonstrates how combining various Azure services creates Intelligent Apps to enhance decision-making in finance. These apps solve real problems at an unprecedented global scale. 

Enhancing Efficiency: Intelligent Apps in Manufacturing

3M, known for Post-it Notes and other innovative products, wielded the power of Intelligent Apps to standardize, automate, and accelerate its sales forecasting. The company needed a unified, automated approach to replace its multiple manual methods.

It adopted Azure Machine Learning and other Azure AI Services to develop, train, and implement 1,500 custom models for each region and division, replacing manual methods. This solution delivers timely finance, sales, and marketing insights while freeing teams from time-consuming manual data manipulation tasks.

3M used Azure Machine Learning, automated machine learning (AutoML), and the Many Models Solution Accelerator to train and score numerous machine learning models in parallel. This approach significantly shortened the company’s development cycle time.

Then, 3M integrated Microsoft Power BI into their solution to compile and visualize results more effectively. Executives now have an unbiased and unfiltered view of data for better decision-making. With its improved ability to forecast sales, 3M can focus on manufacturing the products it needs efficiently.

3M’s success demonstrates how combining various Azure services can enhance efficiency in the manufacturing industry. These apps improve operational processes while enabling employees to focus on higher-level tasks. Strategic decisions are more targeted, and business conversations are more meaningful.

Driving Innovation: Intelligent Apps in the Technology Industry

As expected, the tech industry is at the forefront of leveraging Intelligent Apps to drive innovation. Elastic saw an opportunity to use intelligent apps to optimize its search services and meet customer needs more effectively. The company built a managed, secure, and scalable solution for its customers, Elastic Cloud, by leveraging Microsoft Azure.

Elastic Cloud uses Azure Machine Learning and other Azure AI Services to automate many basic management tasks, such as backing up data, upgrading, and scaling deployments. This approach enables Elastic’s customers to focus less on managing the software and more on serving their own clients. Customers can quickly deploy and manage Elastic Cloud on Kubernetes (ECK) using Azure Kubernetes Service.

As a result, Elastic’s cloud business is growing twice as fast as its on-premises solutions. Enterprise search users have reported significant improvements in system availability and employee satisfaction.

Additionally, Elastic incorporated Azure OpenAI Service into its Elasticsearch Relevance Engine (ESRE), providing more relevant answers to search queries. ESRE enables text, vector, and hybrid search, offering a more effective and accurate response to queries.

Elastic’s success story demonstrates how combining various Azure services can create intelligent Apps to drive innovation in the tech industry. These apps help improve operational processes and deliver better results quickly.

Revolutionizing Healthcare: Intelligent Apps in Medicine

Intelligent apps also drive transformative changes in the healthcare sector. Cambridgeshire and Peterborough NHS Foundation Trust, a healthcare provider in the United Kingdom, gradually shifted their on-premises systems to the cloud using Azure, improving flexibility, and lowering costs. They needed a solution for their electronic patient record (EPR) system that integrates 23 years of data across multiple systems and formats.

The Trust used Microsoft Azure and Azure Cognitive Search, including semantic search for understanding intent and contexts and cognitive capabilities for extracting insights from data, to standardize and automate data management. This approach made legacy, archive, and live data more discoverable, saving clinicians’ time. The Trust’s solution searches through millions of clinical documents in seconds, even recognizing handwritten notes without needing a separate optical character recognition (OCR) solution to extract text from images (Azure Cognitive Search automatically does it). Clinicians can now uncover previously inaccessible insights, enabling them to offer more personalized care.

The Trust also used Integration Services tools such as Azure Logic Apps to create workflows without writing code and Event Grid to connect Azure and third-party services through a publisher-subscriber model. They switched on a live feed from their new EPR system to Azure to feed real-time information into other applications. Researchers can now access anonymized data to fuel medical innovations beyond the NHS.

The healthcare provider also invested heavily in Power BI’s data visualization and analytical functions to sift through massive amounts of information. This level of data accessibility was previously impossible with their on-premises system. The new insights have significantly improved clinicians’ efficiency for better patient care.

Data accessibility and security are critical in the healthcare industry. Intelligent Apps empower organizations like the Trust to revolutionize medical care. AI capabilities helped the Trust improve its operational efficiency and user experiences, setting a new standard for healthcare in the digital age.

Summary

AI is driving transformation and real-world successes across industries. Intelligent apps are reshaping our world by streamlining operations in manufacturing and logistics, enhancing customer experience in the retail sector, improving decision-making in finance, driving innovation in tech, and revolutionizing healthcare.

As organizations adopt AI, they’re innovating in ways that were once too difficult, expensive, or outright impossible. Companies that wait to embrace intelligent apps risk the chance of falling behind. By embracing intelligent apps and leveraging the capabilities of Azure, you can harness the transformative potential of AI to remain on the cutting edge of technology.

Exercise

- - +
Skip to main content

11 posts tagged with "Fall-For-IA"

View All Tags

· 10 min read
It's 30DaysOfIA

Explore how digital natives and Independent Software Vendors (ISVs) can transform their business operations and generate higher value for customers with generative AI and intelligent apps.

What We'll Cover:

  • Potential of Generative AI in application development
  • Challenges with Generative AI
  • Integrating Generative AI with business operations

image of generative AI and intelligent apps used in business

Unleashing Business Transformation: How Digital Natives Can Leverage Generative AI

Generative AI is no longer a technological flight of fancy. It’s here, and it’s rewriting the rules of business innovation. 

This type of AI leverages machine learning algorithms to create new data with similar characteristics to a given dataset. It goes beyond text generation using generative pretrained transformer (GPT) models, covering an array of applications that span from image synthesis to translations and more. It also powers a new class of intelligent applications, which combine AI capabilities with traditional software functions for enhanced user experiences and advanced business solutions. 

Generative AI is transforming how digital natives and independent software vendors (ISVs) operate. Generative AI offers new possibilities for creating unique business value, enhancing customer experiences, and driving growth. 

It plays a critical role in applications, improving functionality, aiding decision-making, and automating complex tasks to deliver dynamic, uniquely personalized user experiences. Generative AI also enables and automates data-driven outcomes, functions, and features.

Integrating AI capabilities into customer-facing apps and turning them into intelligent apps benefits both the customer and the business, as we discussed in Demystifying Intelligent Applications: Leveraging AI in App Development. You get faster product development workflows and better business intelligence for sales and marketing, while your users get more natural and context-aware features and interactions. 

That said, to realize generative AI’s potential, you need some strategy and knowledge of how and where it fits into your current business and operations. We’ll examine this technology’s potential to transform business operations, its risks, and rewards, and how to integrate it into your existing operations. 

Unpacking the Potential of Generative AI

Generative AI is creating new opportunities in the digital playing field. It empowers businesses to generate insights, design products, and automate processes in ways and at speeds that were previously unimaginable. You can now predict customer behavior more accurately, offer highly personalized user experiences, and prototype innovative ideas rapidly. 

For example, Microsoft used generative AI to reinvent web search in the new AI-powered Bing and Edge. The search experience has evolved from a solitary user flow of typing in keywords and scrolling through results. It’s now a conversational experience where Bing uses context to offer better quality and more personalized search queries and results. 

Similarly, Microsoft harnessed generative AI to transform its productivity suite into intelligent apps. Through Copilot for Microsoft 365, PowerPoint now empowers users to tell their stories by using DALL-E to generate images effortlessly. It also helps rephrase text-heavy slides to be more straightforward. Outlook now assists users to compose more effective emails, enabling better communication.

These are just some examples of how AI powers day-to-day work and operations. 

Businesses that ignore generative AI miss out on untapped market opportunities, letting competitors gain an edge in the digital race. By seeking ways to rethink assumptions and reinvent existing approaches, you can effectively use generative AI to create new opportunities, open products to new use cases and markets, unlock new growth and customer satisfaction levels, and increase your revenue.

info

Register for the intelligent apps webinar on Driving Business Value by Modernizing with Cloud-Native & AI with Microsoft and Forrester on September 26.

Explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries. 

The Crucial Role of Generative AI in Business Operations

Generative AI can be a powerful catalyst for business transformation. Let’s quickly tour some ways it can redefine business operations. 

Product development is traditionally a long and painstaking process. Now, tools like GitHub Copilot and TestPilot use generative AI to make coding and testing software easier, faster, and more robust. 

Similarly, machine learning as a service technology, such as Azure Machine Learning, democratizes the availability of machine learning. It helps collect, clean, and train models across datasets, making engineering and analyzing data more efficient and accessible.  

Generative AI also simplifies the process of creating intelligent apps. Previously, simulating thousands of prototypes, predicting their performance, and narrowing down to the most viable options could take weeks, months, or even years of iteration and data analysis. With generative AI, this process can be as quick as days, hours, or perhaps minutes. 

Customer experience is also ripe for transformation. Personalized recommendations drive engagement and sales, and generative AI can scale that personalization to every customer interaction using comprehensive and continually updated data. For instance, Microsoft leverages AI to enable personalization in Dynamics 365 Customer Insights. Using this tool, you can ask questions about your customers in natural language and gain valuable and actionable insights into your customer base. 

Lastly, marketing and sales can benefit from generative AI. For example, identifying current and future key markets and emerging trends helps companies stay ahead of the curve. Tools like Copilot in Power BI help you quickly understand and uncover insights about your business, enabling your team to craft targeted campaigns, create engaging content, and optimize your business strategy. 

Leveraging this full generative AI potential requires the right tools. GPT-4, for instance, can be instrumental in realizing effective business transformation. It excels in understanding context, generating human-like text, and learning from minimal data input, making it a formidable tool in any digital native's arsenal. Platforms like Azure OpenAI Service bring GPT-4’s power to your fingertips.

However, as with any powerful technology, it’s unwise to treat generative AI as a perfect solution. You must know the challenges and risks of not leveraging it effectively, including technical and human-factor considerations. 

Without generative AI, your company risks missing the opportunity to please customers via personalization, as discussed previously. You’ll miss sales opportunities via business intelligence insights, limiting your ability to grow revenue and keep up with the competition. 

One challenge of leveraging generative AI’s full potential is finding the right tools. Fortunately, you can implement generative AI in your organization without investing in infrastructure, computational power, and expertise. Platforms like Azure OpenAI Service and Azure AI Services make AI easily accessible. They also work with Azure compute and data services like Azure Functions and Azure Kubernetes Service (AKS), enabling you to build intelligent apps easily and quickly. 

However, generative AI can’t do everything. It may need human expertise and intervention to use it fully. 

For example, generative AI can create unique data. However, the data may have limited diversity and complexity. The AI may need help recognizing additional considerations or requirements specific to a problem. It may also lack context due to various factors, such as the information’s freshness. Also, since it’s an emerging field that’s potentially worth $4.4 trillion, we’re still learning about the nuances and potential regulatory impact of generative AI. 

Perhaps the most crucial and challenging aspect of adopting generative AI is an organization’s required cultural shift. Your teams must learn how to use AI as your company fosters an understanding of AI’s potential and limitations. 

Begin integrating AI literacy into your company’s DNA. Knowledge of its potential and the readily available tools can inspire your teams to incorporate generative AI into their daily workflows and business operations quickly, improve productivity, and deliver superior customer experiences. 

No company wants to be outpaced by its competitors. You can balance generative AI’s potential with your business operations by staying aware of and adapting to the dynamic AI landscape.

Achieving Harmony: Integrating Generative AI with Existing Business Operations

Integrating generative AI into existing business operations doesn’t have to be disruptive. With the correct strategy, you can do so seamlessly. Your goal should be to augment and enhance your operations, not disrupt and replace. 

For example, Microsoft Cloud technologies leveraged AI to relieve healthcare organizations’ challenges, such as talent shortage and workforce burnout. Providers automated their workflows, invested in specific applications and connectors, and enhanced support for data and operations. 

Azure AI Services, such as text analytics for health, enable healthcare organizations to understand their largely unstructured and untapped medical data. Project Health Insights leans on AI to help healthcare professionals gain actionable insights and inferences from patient data. 

Azure Health Bot, an Intelligent App, empowers organizations to build and deploy legally compliant, powerful conversational healthcare experiences to patients. 

A successful AI integration like this begins with understanding. Knowing what generative AI can do and setting realistic expectations can help you identify where and how AI can best serve your operations. 

Next, you can foster a culture of AI literacy across teams, speeding up adoption by helping them understand how it can aid their work. The more familiar your team is with AI and its possibilities, the smoother the transition. Training and education programs play a pivotal role here. 

Finally, integration requires a systematic approach. It’s not about overhauling everything at once — it's about identifying areas where AI can add the most value, then gradually experimenting and scaling its use. Successful integration stories often feature a phased approach, starting small and growing steadily. 

As we move toward a future where generative AI is a staple in business operations, remember that every journey is unique. Although there’s no one-size-fits-all solution, a strategic approach and a keen understanding of your business needs will help you navigate this new landscape successfully.

Conclusion

Generative AI offers plenty of opportunities for businesses that embrace it. It’s fueling the next generation of intelligent applications, transforming everything from product development to customer engagement to marketing. This technology is the key to unlocking unprecedented growth and innovation. 

While the journey may have challenges, the potential rewards make generative AI worth exploring. As generative AI advances, Azure AI’s broad range of tools and services will help you navigate this fascinating field.

Exercise

  • Watch Episode 02 of the serverless edition Learn Live session to learn how to build, test and deploy an end-to-end intelligent app solution.
  • Complete the Cloud Skills Challenge to build on your AI skills.
  • Register for Ask the Expert: Azure Functions on September 26 for a live Q&A with the Product Team on how to build AI apps using serverless computing.
+ + \ No newline at end of file diff --git a/30daysofIA/tags/fall-for-ia/page/6/index.html b/30daysofIA/tags/fall-for-ia/page/6/index.html index 256d0d206c..4e90d4565d 100644 --- a/30daysofIA/tags/fall-for-ia/page/6/index.html +++ b/30daysofIA/tags/fall-for-ia/page/6/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "Fall-For-IA" | Build Intelligent Apps On Azure +11 posts tagged with "Fall-For-IA" | Build Intelligent Apps On Azure @@ -14,13 +14,13 @@ - - + +
-

9 posts tagged with "Fall-For-IA"

View All Tags

· 8 min read
It's 30DaysOfIA

Let’s start with understanding the power of intelligent applications.

What We'll Cover:

  • What are intelligent applications?
  • Categories of Intelligent Apps
  • Breaking down the role of AI in intelligent apps

A decorative header image for the blog

Demystifying Intelligent Applications: Leveraging AI in App Development

Until recent years, when you heard the term “artificial intelligence” (AI), you might have pictured autonomous robots from sci-fi films. Now, perhaps your mind jumps to big data and machine learning (ML). Maybe you’re even thinking about the latest ChatGPT plugins and transformer architectures. Today, AI is more than these imaginations: it’s a reality increasingly woven into our day-to-day routines through intelligent applications.

Intelligent apps combine cloud-scale compute, data, and predictive or generative AI. These applications help you automate tasks and make data-driven decisions, aiding in leveraging business intelligence to gain meaningful, practical outcomes. 

For developers, the AI landscape isn’t an abstract complexity. It’s a real, tangible opportunity for evolving traditional applications, making them “intelligent,” and delivering more value to users. And with tools like Azure Kubernetes Service, Azure Cosmos DB, and Azure AI Services, stepping into the world of intelligent apps isn’t a steep climb but a steady trek across a manageable learning curve.

The Three Categories of Intelligent Applications

Before we start building, it’s critical to understand what makes an app “intelligent.” It’s not a universal definition so much as an identifier for apps that fall into one or more of the three primary categories: 

  • Outcome-based — This designation represents the broadest category of intelligent apps, whose principal value for users is the intelligent outcomes they provide. These apps process data to provide insightful results that enhance decision-making or help with tasks like trend prediction, resource management, and customer relationship management.

  • Functionality-based — This type of intelligent app takes AI integration a degree further, integrating AI as a function of the app to create or process its outputs, enhancing the app’s overall interactivity and usefulness.

  • Feature-based — This is the narrowest category, in which the app’s AI/ML component is one of its core features and primary selling points

Let’s dive more deeply into each category.

Outcome-Based Apps: Intelligent Outcomes Drive Success

Outcome-based applications are all about the results provided to the user. They function beyond merely processing data to offer insightful outcomes that drive success.

Think of a personal fitness tracker app that uses AI to analyze the user’s activity and provides actionable recommendations for achieving a goal. This type of app not only tracks your steps, sleep patterns, and heart rate but uses that data to provide you with personalized health advice. It helps you make informed lifestyle choices — the “intelligent” outcome.

Another example is a social media marketing app that uses AI for time-saving data analysis, task automation, and trend prediction. AI-based sentiment analysis can gauge customer perception while natural language processing (NLP) accurately interprets customer inquiries. Task automation can schedule posts and automate responses to common customer queries, and trend forecasting helps model and predict customer behavior to further personalize targeted promotional efforts. Building this type of intelligence into your apps provides cost-effective, actionable insights that guide sales and marketing strategies.

Functionality-Based Apps: AI-Driven Features Enhance User Experiences

As the name suggests, functionality-based applications integrate AI into how the app functions, but the end user drives the action and outcome. In this category of apps, AI takes a more active role in the user’s interaction by allowing them to use AI-created or processed output. These apps leverage AI technologies like Azure AI Services for NLP, image recognition, and pattern recognition to improve user experience.

Consider a music app that generates personalized playlists based on your listening habits or your smartphone’s camera app that uses AI to recognize faces and optimize photo settings. Even more advanced are the language translation apps that translate text from one language to another and can even understand and maintain the context and subtleties of the conversation. They use NLP to accomplish these feats.

Similarly, consider the latest e-commerce apps, whose AI-driven functionalities enable customers to try on clothes virtually, significantly enhancing the online shopping experience.

Feature-Based Apps: Integrating Advanced AI/ML Components

Finally, there are feature-based intelligent apps. These apps incorporate advanced AI/ML components like neural networks, internal large language models (LLMs), and sophisticated algorithms as core features for the user. In this type of application, AI is the primary feature for users to engage with and the primary selling point for the app.

Some examples of feature-based applications are chatbots and virtual agents built using Azure Bot Service. These robo-representatives use AI to provide more natural and human-like conversation, balancing superior efficiency with a friendlier, more personalized user experience. The new Bing is a prime example of this implementation, integrating AI to give users a knowledgeable copilot for web searches.

Similarly, OpenAI’s ChatGPT allows users access to its state-of-the-art language models available as Microsoft’s Azure OpenAI Service. It leverages the power of the highly sophisticated GPT-3 LLM to perform tasks like drafting emails, writing code, answering general knowledge questions, translating between languages, and tutoring in numerous subjects.

These apps don’t just use AI to provide outputs or improve user experiences: They push the boundaries of what’s possible in app development by integrating AI/ML components.

info

Register for the live Q&A Ask The Expert session on September 26 with the Azure Functions product engineering team.

Breaking Down the Role of AI in Intelligent Applications

Intelligent apps reflect the merging of traditional software design with the dynamic capabilities of AI. Let’s explore how AI integrates and amplifies these applications.

At its core, AI augments applications with the capacity to self-learn and self-improve. It allows you to create applications that predict, react, and adapt to a constantly changing environment. In the power grid management system for a major city, an AI-infused application can forecast power usage trends, react to anomalies, and adapt to sudden demand surges, ensuring optimal efficiency and preventing blackouts.

But AI’s role doesn’t end at prediction or decision-making: It also automates tasks. In enterprise solutions, it has moved beyond simple automation to encompass intelligent process automation (IPA). In this arena, AI algorithms understand, learn, and then automate entire digital workflows. For developers, this is a fertile ground for creating applications that can dramatically improve the operational efficiency of businesses.

Even more, AI substantially aids in development tasks. For example, there’s GitHub Copilot, an AI-powered code assistant that offers suggestions based not only on syntax but on development best practices and the intent of the code. AI has evolved from a tool you use to build apps to a partner that helps in creating them. 

Using AI to build intelligent apps isn’t about throwing out what’s worked before. It’s about enhancing the components that have the most impact and achieve the best results. Incorporating AI in apps elevates their predictive abilities, streamlines complex tasks, and even aids in the development and creation process. When you build intelligent apps, you don’t just address current problems. You anticipate future challenges and craft solutions that adapt over time.

Summary

As developers the power of AI is in our hands, and it’s our responsibility to harness its full potential. Each category tells the story of AI's transformative potential, from applications that deliver intelligent outcomes to those with AI capabilities to enrich the user experience and those pushing boundaries with advanced AI/ML technology.

Accessible and robust platforms like Azure AI can simplify the process of developing Intelligent Applications. Whether you’re refining an existing app or building a new one, just entering the world of AI, or already familiar with the landscape, take a moment to appreciate the potential that AI integration offers. You’re embracing this exciting frontier and shaping the future of app development. 

Exercise

  • Complete the Cloud Skills Challenge to build on your apps and AI skills.
  • Watch Episode 01 of the Serverless Edition Learn Live session to learn how to build an end-to-end intelligent app solution.
- - +
Skip to main content

11 posts tagged with "Fall-For-IA"

View All Tags

· 9 min read
It's 30DaysOfIA

In the part of our series on intelligent apps, we’ll explore how AI is transforming application development, from design and architecture to building.

What We'll Cover:

  • Revolutionizing application development using AI
  • Infusing AI in application design architecture
  • AI Assisted Pair Programming
  • Future of App Development

A decorative image of application development with AI

Reimagining Application Development With AI: A New Paradigm

Artificial intelligence (AI) is impacting every industry. AI-powered development tools offer new functionalities and assist software developers throughout the development process, revolutionizing app development. From initial design and architecture to coding and debugging, AI reshapes how we approach application conception, creation, and iteration.

In part one of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” we explored the new breed of intelligent apps and how they use AI. In this article, we’ll see how AI technologies enhance developer toolkits and shape the future of app development, particularly through AI-enabled coding assistance and debugging.

The Dawn of Intelligent Apps: Revolutionizing Application Development

AI is more than just another addition to the development toolbox—it represents a revolution bringing unprecedented efficiency, innovation, and user engagement to our applications. 

What sets intelligent apps apart is how they can adapt and learn, helping us deliver unique, real-time user experiences.  

One example is an AI-driven health application that can track biometric data, predict potential health issues, provide personalized health advice, and even guide emergency services during a medical crisis. An AI-enabled language learning app that adapts to our learning speeds, recognizes our problem areas, and customizes its content to help us improve our language proficiencies is another example.  

Intelligent apps like these don’t just use data for recommendations—they can make critical decisions and provide sophisticated services that were previously impossible. 

For developers, intelligent apps mean we’re no longer just coding—we’re teaching applications how to think, adapt, and make decisions. AI is reshaping every step of the development journey, helping us to build more intuitive and user-focused products. 

Next, we’ll examine the impact of AI on application design and architecture and how AI-assisted “pair programming” changes software development.

info

Register for the FREE webinar on Intelligent Apps with Microsoft and Forrester on September 26 to explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries.

There will be a showcase of real-world use cases that demonstrate how AI can be seamlessly integrated into cloud-native environments driving tangible business value.

AI in Design and Architecture: Fueling Creativity and Efficiency

AI technologies help developers automate and streamline their processes and provide new ways to think about design.  

In traditional application development, the design and architecture phase can take significant time, effort, and expertise. For instance, adopting a monolithic architecture or a microservices-based design can significantly increase coding, testing, and debugging complexity. The app may even behave differently when hosted locally than in a deployed environment. It may take hours to track down an issue manually—time developers could spend elsewhere. 

Conversely, we can generate numerous design iterations in seconds with AI, each optimized to fulfill specific user needs and business goals. AI tools can quickly model various scenarios' impacts and software design patterns in the architectural design phase. It provides insights into performance and potential issues and suggests improvements. AI can also generate skeleton code, allowing developers to prototype different solutions or test specific components and technologies rapidly. By reviewing thousands of lines of code in seconds, AI allows us to focus on creating apps that serve users through feature development instead of searching for bugs.  

Integrating AI into the design and architecture phase can even extend our creative capabilities. By leveraging the power of generative AI—like large language models (LLMs)—and their broad knowledge and context going beyond software, we can better brainstorm apps and feature ideas. We can go beyond code and software architecture and get ideas about user experience, logo and branding, or marketing concepts. And as AI tools deliver insights and ideas we would have never considered; it encourages more innovation.

info

Watch Episode 01 of the Learn Live session to learn how to build an end-to-end intelligent app solution architecture.

The Power of AI-Assisted Pair Programming in Building Applications

AI-assisted pair programming is a collaborative coding approach where an AI system—like GitHub Copilot or TestPilot—assists developers during coding. It’s an increasingly common approach that significantly impacts developer productivity. In fact, GitHub Copilot is now behind an average of 46 percent of developers’ code and users are seeing 55 percent faster task completion on average. 

For new software developers, or those interested in learning new skills, AI-assisted pair programming are training wheels for coding. With the benefits of code snippet suggestions, developers can avoid struggling with beginner pitfalls like language syntax. Tools like ChatGPT can act as a personal, on-demand tutor—answering questions, generating code samples, and explaining complex code syntax and logic. These tools dramatically speed the learning process and help developers gain confidence in their coding abilities. 

Building applications with AI tools hastens development and provides more robust code. For example, GitHub Copilot helps us write new code accurately. It also quickly makes sense of existing code, suggests whole new lines or blocks of code, and even detects errors and proposes fixes. It’s like having a second coder working alongside us—one who never tires and is trained on billions of lines of code. 

However, it’s also important to acknowledge the limitations of AI. While generative AI-assistance technologies like GitHub Copilot are impressive, they’re still imperfect. They can sometimes produce erroneous, non-functional, or vulnerable code. They can also lack nuanced, domain-specific knowledge.  

So, although these tools are helpful, we can’t rely on them fully—these are programming aids rather than definitive solutions. AI-assisted pair programming is about augmenting the human developer’s skills, not replacing them.

Debugging and Improving Code With AI: Enhancing Application Quality

Identifying and correcting code errors is often time-consuming and challenging for developers. AI streamlines the debugging process by automatically detecting anomalies, suggesting fixes, and learning from previous bugs to prevent them from recurring. 

We can use generative AI to create unit tests from code snippets, from simple parameter checking and input validation to complex mocks of database and network services. Automated AI testing improves code coverage and robustness. It saves significant time compared to manually crafting unit tests. 

AI also revolutionizes code reviews by scanning and highlighting potential issues—like code patterns leading to a bug or a coding standards violation—and suggesting ways to simplify or refactor the code for readability and performance. This reduces human error and makes the code review process more efficient. Its benefits extend to small teams, where the availability of developers is crucial to deadlines, and large teams, where effective collaboration depends on readability and code consistency. AI further saves time by triaging issues so developers can prioritize the most mission-critical ones. 

Aside from improving code quality, AI is critical to understanding system behavior. Intelligent AI-assisted software development tools can monitor and audit system behavior, giving developers a better insight into how their code performs in different scenarios. AI can track patterns, identify inefficiencies, and provide alerts when they detect unusual behavior—enabling developers to enhance their code further. 

By predicting and identifying potential vulnerabilities, intelligent AI-assisted coding apps like Microsoft Security Copilot are pivotal in ensuring application functionality, reliability, and security. These AI tools are continually trained and upgraded with large codebases and user feedback that refines their responses. This ability to learn from past mistakes makes AI an incredibly powerful tool that will continue to expand its capabilities in the future.

info

Complete the AI Cloud Skills Challenge to build on you AI application development skills with paired programming using generative AI and GitHub Copilot.

AI and the Future of Application Development

The rapid advancements in AI and machine learning technologies point to an exciting future for application development. 

In “The Future of Applications,” software developer Mike Cann illustrates how LLMs might revolutionize application development. Cann that suggests fully-autonomous coding—AI-generated applications created entirely from prompts—may supersede traditional hand-crafted applications. Dynamically generated user interfaces could offer personalized and customizable experiences while we can design APIs for machine consumption and efficiency rather than human readability. Furthermore, we can store data in less structured, more adaptable, and flexible forms. 

AI is already making application development more accessible. With AI handling the complex, technical aspects of development, less-technical individuals can take a greater role in building applications. Consequently, AI can potentially democratize software engineering. 

Intelligent apps will continue to evolve, becoming more efficient, adaptable, and capable of learning from user interactions. Similarly, AI will inevitably become integral in every stage of app development.

Summary

AI has already created a new development paradigm—one in which machine intelligence and human creativity come together to produce innovative, efficient, and robust applications. AI already assists us at every stage of app development, from design and architecture to coding and debugging. 

Developers who embrace AI tools and integrate them into their processes can enhance their productivity, improve the quality of their applications, and expand the possibilities of their application development.  

The future of development is automated and AI-driven, and it’s here today. Getting started is easy—try an AI-powered development tool like GitHub Copilot to see how AI-assisted pair programming can enhance your productivity and code quality.

Exercise

+ + \ No newline at end of file diff --git a/30daysofIA/tags/fall-for-ia/page/7/index.html b/30daysofIA/tags/fall-for-ia/page/7/index.html index 1782c34f87..9ba8b8ead1 100644 --- a/30daysofIA/tags/fall-for-ia/page/7/index.html +++ b/30daysofIA/tags/fall-for-ia/page/7/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "Fall-For-IA" | Build Intelligent Apps On Azure +11 posts tagged with "Fall-For-IA" | Build Intelligent Apps On Azure @@ -14,14 +14,13 @@ - - + +
-

9 posts tagged with "Fall-For-IA"

View All Tags

· 4 min read
It's 30DaysOfIA

This Fall focus on building intelligent apps using AI and cloud-native technologies. #FallForIntelligentApps brings to you a learning journey to build your skills on creating differentiated experiences while modernizing your applications. It’s time to learn it all.

What We'll Cover

image

Get Ready To #FallForIntelligentApps starting September 18!

Today, we kick off the Fall season with content and activities to skill you up on all things Intelligent Apps or AI Apps on Azure with content, events, and community interactions! Read on to learn about what is coming!

Explore Our Initiatives

We have a number of initiatives planned for the month to help you learn and skill up on relevant technologies. Click on the links to visit the relevant pages for each

image

Register for the events!

What are 4 things you can do today, to jumpstart your learning journey?

#30Days Of Intelligent Apps

#30DaysOfIA is a series of daily blog posts grouped into themed weeks - taking you from core concepts to end-to-end solution examples in 30 days. Each article will provide conceptual lessons paired with exercises and resources to help you reinforce learnings and take next steps.

This series takes you through learning journey in six stages, each building on the previous week to help you skill up in a beginner-friendly way:

We will start with defining intelligent apps and then expand on how to build with cloud-native technologies like Azure Kubernetes Service, Azure Container Apps and Azure Functions, and take you through end-to-end scenarios for real world application development. Before we dive deep on intelligent apps, here is a high-level overview of the Intelligent Apps landscape on Azure:

image

Containers on Azure services offer you a wide range of capabilities, from simplicity to control to suit your different needs.

image -image

To start with the basics for developing Kubernetes applications, explore #30DaysOfCloudNative.

Cloud-native development when paired with serverless computing enhances your solution architecture for building cost optimized, resilient applications.

image

To start with the basics for serverless computing, explore #30DaysOfServerless.

Let’s Get Started

Now you know everything! We hope you are as excited as we are to dive into a full month of active learning and doing! Don't forget to subscribe for updates in your favorite feed reader! And look out for our first Intelligent Apps post Monday!

- - +
Skip to main content

11 posts tagged with "Fall-For-IA"

View All Tags

· 10 min read
It's 30DaysOfIA

Explore real-world examples of how Intelligent Apps revolutionize logistics, retail, finance, manufacturing, tech, and medicine.

What We'll Cover:

  • Real-world businesses revolutionizing operations with Intelligent Apps
  • High level architecture for industry scenarios
  • Implementation architecture overview across industries

A decorative header image for the blog

Real-world Success Stories

According to IBM’s 2022 Global AI Adoption Index, 35% of surveyed companies already use AI in their business, and 42% are exploring the idea. Organizations lagging behind the AI curve will quickly fall behind early adopters, who are already enjoying its benefits and boosting their investment in this technology.

In the first part of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” we explored how organizations can use AI-powered apps, or intelligent apps, to transform their operations, decision-making, and customer experiences, leaping ahead of the competition.

Intelligent apps significantly improve operational efficiency and customer engagement across industries like logistics, retail, finance, manufacturing, tech, and healthcare. In this article, we’ll spotlight real-world success stories of businesses revolutionizing operations with intelligent apps using Microsoft Azure’s app, data, and AI services.

info

Register for the Learn Live on September 21 for a live guided experience with SMEs on how to build end-to-end solution architecture for real world implementation of Intelligent Apps.

Streamlining Operations: Intelligent Apps in the Airline Industry

Aerospace pioneer Airbus leveraged intelligent apps to streamline its operations and innovate its services with two ground-breaking modernizations that use Azure apps, data, and AI services to reimagine pilot training and predict aircraft maintenance issues. 

Aviation technology’s rapid evolution compelled Airbus to reconceive their approach to training pilots. The aircraft manufacturer created a pilot training chatbot by combining Azure AI Services with Speech to Text and Text to Speech. The bot helps trainee pilots navigate over 6,000 pages of technical information. Pilots can ask questions verbally for quick answers, aiding their understanding of dense information and speeding up training. And when Airbus needed to accommodate its military consumers, deploying its Kubernetes cluster to a disconnected, “air-gapped” environment met the strict security requirements of military aircraft training.

A chatbot could look like the following diagram as Azure services work together to create a conversational experience. Its components could include security and governance, bot logic and user experience (UX), bot cognition and intelligence, data extract, transfer, and load (ETL) operations, quality assurance and enhancements, logging, and monitoring and reporting.

A diagram of a conversational experience enabled by Azure services

Airbus also used AI to improve its aircraft maintenance and safety. They deployed AI Anomaly Detector to gather and analyze telemetry data from multiple flights, enabling more profound insights into wear and tear on military aircraft operating in harsh conditions. This AI application helps Airbus better understand each airplane’s health. Airbus can predict and fix potential problems before they occur, improving the aircraft’s safety and operational readiness.

AI and Azure are changing how organizations like Airbus operate, even in strict, highly regulated markets with stringent security requirements. And by streamlining pilot training and staying ahead of aircraft maintenance, Airbus helps its military and commercial clients reliably deliver their goods across the globe, maintaining their critical supply chains.

Boosting Customer Service: Intelligent Apps in the Retail Industry

The retail industry has been among the first to adopt and benefit from intelligent apps. For example, the leading American used car retailer, CarMax, uses intelligent apps to transform the car shopping experience.

Before embracing AI, CarMax faced a significant challenge: Manually creating text summaries was time-consuming and labor-intensive. CarMax decided to leverage Azure OpenAI Service to automatically generate car research pages, offering customers valuable insights while enhancing the website’s search engine rankings.

This AI-driven approach has significantly streamlined the process, achieving in a few months what would have taken years manually. Also, the AI system condenses thousands of customer reviews into a few readable sentences, offering potential buyers a quick overview of others’ experiences.

CarMax has improved their customer experience while boosting operational efficiency through AI adoption. CarMax’s editorial staff focuses on creating strategic, longer-form content with the time saved, enhancing productivity, and driving more website traffic.

CarMax’s success story serves as a testament to the potential of intelligent apps in the retail industry.

Enhancing Cybersecurity: Intelligent Apps in Finance

Intelligent apps also impact the highly regulated financial services sector. Swift, a leading infrastructure provider for financial messaging services, uses intelligent apps to enhance decision-making and combat financial crime.

The massive growth of cross-border transactions and instant payment networks led to a surge in financial fraud that can cost the industry hundreds of billions of dollars every year. So, Swift and Microsoft partnered to build an anomaly detection model using federated learning techniques with Azure Machine Learning and Azure confidential computing.

Microsoft Purview helped govern data, as the federated learning method partially trained each model in a silo before aggregating them. This approach enabled Swift and Microsoft to develop a highly accurate model without copying or moving data from Swift members’ secure locations, ensuring the highest level of security and privacy.

Financial organizations can now deploy this trained ML model to run a real-time fraud detection system, lowering the cost of financial fraud.

Organizations can deploy a fraud detection system like the following example on Microsoft Azure. The system ingests data from streams or storage, runs analytics, and uses ML to detect fraud and store results. It then alerts users and other systems to the security issue.

A diagram of an example fraud detection system

Swift provides the innovative model to its banking partners, who can share data to further train the model. Azure confidential computing securely runs new models on current data to accumulate insights, generating an ongoing learning cycle. This iterative approach strengthens the global detection of fraudulent financial transactions through an increasingly faster and more accurate model. 

The collaboration between Swift and Microsoft demonstrates how combining various Azure services creates Intelligent Apps to enhance decision-making in finance. These apps solve real problems at an unprecedented global scale. 

Enhancing Efficiency: Intelligent Apps in Manufacturing

3M, known for Post-it Notes and other innovative products, wielded the power of Intelligent Apps to standardize, automate, and accelerate its sales forecasting. The company needed a unified, automated approach to replace its multiple manual methods.

It adopted Azure Machine Learning and other Azure AI Services to develop, train, and implement 1,500 custom models for each region and division, replacing manual methods. This solution delivers timely finance, sales, and marketing insights while freeing teams from time-consuming manual data manipulation tasks.

3M used Azure Machine Learning, automated machine learning (AutoML), and the Many Models Solution Accelerator to train and score numerous machine learning models in parallel. This approach significantly shortened the company’s development cycle time.

Then, 3M integrated Microsoft Power BI into their solution to compile and visualize results more effectively. Executives now have an unbiased and unfiltered view of data for better decision-making. With its improved ability to forecast sales, 3M can focus on manufacturing the products it needs efficiently.

3M’s success demonstrates how combining various Azure services can enhance efficiency in the manufacturing industry. These apps improve operational processes while enabling employees to focus on higher-level tasks. Strategic decisions are more targeted, and business conversations are more meaningful.

Driving Innovation: Intelligent Apps in the Technology Industry

As expected, the tech industry is at the forefront of leveraging Intelligent Apps to drive innovation. Elastic saw an opportunity to use intelligent apps to optimize its search services and meet customer needs more effectively. The company built a managed, secure, and scalable solution for its customers, Elastic Cloud, by leveraging Microsoft Azure.

Elastic Cloud uses Azure Machine Learning and other Azure AI Services to automate many basic management tasks, such as backing up data, upgrading, and scaling deployments. This approach enables Elastic’s customers to focus less on managing the software and more on serving their own clients. Customers can quickly deploy and manage Elastic Cloud on Kubernetes (ECK) using Azure Kubernetes Service.

As a result, Elastic’s cloud business is growing twice as fast as its on-premises solutions. Enterprise search users have reported significant improvements in system availability and employee satisfaction.

Additionally, Elastic incorporated Azure OpenAI Service into its Elasticsearch Relevance Engine (ESRE), providing more relevant answers to search queries. ESRE enables text, vector, and hybrid search, offering a more effective and accurate response to queries.

Elastic’s success story demonstrates how combining various Azure services can create intelligent Apps to drive innovation in the tech industry. These apps help improve operational processes and deliver better results quickly.

Revolutionizing Healthcare: Intelligent Apps in Medicine

Intelligent apps also drive transformative changes in the healthcare sector. Cambridgeshire and Peterborough NHS Foundation Trust, a healthcare provider in the United Kingdom, gradually shifted their on-premises systems to the cloud using Azure, improving flexibility, and lowering costs. They needed a solution for their electronic patient record (EPR) system that integrates 23 years of data across multiple systems and formats.

The Trust used Microsoft Azure and Azure Cognitive Search, including semantic search for understanding intent and contexts and cognitive capabilities for extracting insights from data, to standardize and automate data management. This approach made legacy, archive, and live data more discoverable, saving clinicians’ time. The Trust’s solution searches through millions of clinical documents in seconds, even recognizing handwritten notes without needing a separate optical character recognition (OCR) solution to extract text from images (Azure Cognitive Search automatically does it). Clinicians can now uncover previously inaccessible insights, enabling them to offer more personalized care.

The Trust also used Integration Services tools such as Azure Logic Apps to create workflows without writing code and Event Grid to connect Azure and third-party services through a publisher-subscriber model. They switched on a live feed from their new EPR system to Azure to feed real-time information into other applications. Researchers can now access anonymized data to fuel medical innovations beyond the NHS.

The healthcare provider also invested heavily in Power BI’s data visualization and analytical functions to sift through massive amounts of information. This level of data accessibility was previously impossible with their on-premises system. The new insights have significantly improved clinicians’ efficiency for better patient care.

Data accessibility and security are critical in the healthcare industry. Intelligent Apps empower organizations like the Trust to revolutionize medical care. AI capabilities helped the Trust improve its operational efficiency and user experiences, setting a new standard for healthcare in the digital age.

Summary

AI is driving transformation and real-world successes across industries. Intelligent apps are reshaping our world by streamlining operations in manufacturing and logistics, enhancing customer experience in the retail sector, improving decision-making in finance, driving innovation in tech, and revolutionizing healthcare.

As organizations adopt AI, they’re innovating in ways that were once too difficult, expensive, or outright impossible. Companies that wait to embrace intelligent apps risk the chance of falling behind. By embracing intelligent apps and leveraging the capabilities of Azure, you can harness the transformative potential of AI to remain on the cutting edge of technology.

Exercise

+ + \ No newline at end of file diff --git a/30daysofIA/tags/fall-for-ia/page/8/index.html b/30daysofIA/tags/fall-for-ia/page/8/index.html index 27e52726d5..c2e49b56f8 100644 --- a/30daysofIA/tags/fall-for-ia/page/8/index.html +++ b/30daysofIA/tags/fall-for-ia/page/8/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "Fall-For-IA" | Build Intelligent Apps On Azure +11 posts tagged with "Fall-For-IA" | Build Intelligent Apps On Azure @@ -14,14 +14,13 @@ - - + +
-

9 posts tagged with "Fall-For-IA"

View All Tags

· 4 min read
It's 30DaysOfIA

Continue The Learning Journey through Fall For Intelligent Apps! 🍂

What We'll Cover

Thank you! ♥️

image

It's hard to believe that JavaScript on Azure hack-together is ending! It seems like just yesterday that we launched this initiative, and yet here we are, 15 days later, with an incredible amount of learning and growth behind us. So... it's time for a wrap!

From the bottom of our hearts, we want to thank each and every one of you for your participation, engagement, and enthusiasm. It's been truly inspiring to see the passion and dedication from this strong community, and we're honored to be a part of it. ✨

Recap of The JavaScript on Azure Global Hack-Together

As we wrap up this exciting event, we wanted to take a moment to reflect on all that we've accomplished together. Over the last 15 days, we've covered a lot of ground, from the basics of contributing to Open source to the exploration of the Contoso Real Estate project from its Frontend to its Backend and future AI implementation.

Now that the hack-together is ending, we want to make sure that you have all the resources you need to continue honing your skills in the future. Whether you're looking to make your fist contribution to open source, become an open source maintainers, collaborate with others, or simply keep learning, there are plenty of resources out there to help you achieve your goals. So, let's dive in and explore all the ways you can continue to grow your JavaScript skills on Azure!

JSonAzure Hack-together Roadmap 📍:

hack-together-roadmap (2)

Recap on past Livestreams🌟:

Day 1️⃣: Opening Keynote (Hack-together Launch): Introduction to the Contoso Real Estate Open-source project!, managing complex and complex enterprise architecture, new announcements for JavaScript developers on Azure

Day 2️⃣: GitHub Copilot & Codespaces: Introduction to your AI pair programmer (GitHub Copilot) and your virtual developer environment on the cloud (GitHub Codespaces)

Day 6️⃣: Build your Frontend using Static Web Apps as part of a complex, modern composable frontends (or micro-frontends) and cloud-native applications.

Day 9️⃣: Build a Serverless Backend using Azure Functions

Day 1️⃣3️⃣: Easily connect to an Azure Cosmos DB, exploring its benefits and how to get started

Day 1️⃣5️⃣: Being in the AI Era, we dive into the Azure OpenAI Service and how you can start to build intelligent JavaScript applications

📖 Self-Learning Resources

  1. JavaScript on Azure Global Hack Together Module collection
  2. Lets #HackTogether: Javascript On Azure Keynote
  3. Step by Step Guide: Migrating v3 to v4 programming model for Azure Functions for Node.Js Application

Continue your journey with #FallForIntelligentApps

Join us this Fall on a learning journey to explore building intelligent apps. Combine the power of AI, cloud-native app development, and cloud-scale data to build highly differentiated digital experiences. Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure. Engage in a self-paced learning adventure by following along the #30Days of Intelligent Apps series, completing the Intelligent Apps Skills Challenge, joining the Product Group for a live Ask The Expert series or building an end to end solution architecture with a live guided experience through the Learn Live series. Discover more here.

Hands-on practice: Make your first contribution to open source!

Join our GitHUb Discussion Forum to connect with developers from every part of world, see contributions from other, find collaborators and make your first contribution to a real-world project! -Don't forget to give the repo a star ⭐

Resources

All resources are accessible on our landing page

- - +
Skip to main content

11 posts tagged with "Fall-For-IA"

View All Tags

· 8 min read
It's 30DaysOfIA

Let’s start with understanding the power of intelligent applications.

What We'll Cover:

  • What are intelligent applications?
  • Categories of Intelligent Apps
  • Breaking down the role of AI in intelligent apps

A decorative header image for the blog

Demystifying Intelligent Applications: Leveraging AI in App Development

Until recent years, when you heard the term “artificial intelligence” (AI), you might have pictured autonomous robots from sci-fi films. Now, perhaps your mind jumps to big data and machine learning (ML). Maybe you’re even thinking about the latest ChatGPT plugins and transformer architectures. Today, AI is more than these imaginations: it’s a reality increasingly woven into our day-to-day routines through intelligent applications.

Intelligent apps combine cloud-scale compute, data, and predictive or generative AI. These applications help you automate tasks and make data-driven decisions, aiding in leveraging business intelligence to gain meaningful, practical outcomes. 

For developers, the AI landscape isn’t an abstract complexity. It’s a real, tangible opportunity for evolving traditional applications, making them “intelligent,” and delivering more value to users. And with tools like Azure Kubernetes Service, Azure Cosmos DB, and Azure AI Services, stepping into the world of intelligent apps isn’t a steep climb but a steady trek across a manageable learning curve.

The Three Categories of Intelligent Applications

Before we start building, it’s critical to understand what makes an app “intelligent.” It’s not a universal definition so much as an identifier for apps that fall into one or more of the three primary categories: 

  • Outcome-based — This designation represents the broadest category of intelligent apps, whose principal value for users is the intelligent outcomes they provide. These apps process data to provide insightful results that enhance decision-making or help with tasks like trend prediction, resource management, and customer relationship management.

  • Functionality-based — This type of intelligent app takes AI integration a degree further, integrating AI as a function of the app to create or process its outputs, enhancing the app’s overall interactivity and usefulness.

  • Feature-based — This is the narrowest category, in which the app’s AI/ML component is one of its core features and primary selling points

Let’s dive more deeply into each category.

Outcome-Based Apps: Intelligent Outcomes Drive Success

Outcome-based applications are all about the results provided to the user. They function beyond merely processing data to offer insightful outcomes that drive success.

Think of a personal fitness tracker app that uses AI to analyze the user’s activity and provides actionable recommendations for achieving a goal. This type of app not only tracks your steps, sleep patterns, and heart rate but uses that data to provide you with personalized health advice. It helps you make informed lifestyle choices — the “intelligent” outcome.

Another example is a social media marketing app that uses AI for time-saving data analysis, task automation, and trend prediction. AI-based sentiment analysis can gauge customer perception while natural language processing (NLP) accurately interprets customer inquiries. Task automation can schedule posts and automate responses to common customer queries, and trend forecasting helps model and predict customer behavior to further personalize targeted promotional efforts. Building this type of intelligence into your apps provides cost-effective, actionable insights that guide sales and marketing strategies.

Functionality-Based Apps: AI-Driven Features Enhance User Experiences

As the name suggests, functionality-based applications integrate AI into how the app functions, but the end user drives the action and outcome. In this category of apps, AI takes a more active role in the user’s interaction by allowing them to use AI-created or processed output. These apps leverage AI technologies like Azure AI Services for NLP, image recognition, and pattern recognition to improve user experience.

Consider a music app that generates personalized playlists based on your listening habits or your smartphone’s camera app that uses AI to recognize faces and optimize photo settings. Even more advanced are the language translation apps that translate text from one language to another and can even understand and maintain the context and subtleties of the conversation. They use NLP to accomplish these feats.

Similarly, consider the latest e-commerce apps, whose AI-driven functionalities enable customers to try on clothes virtually, significantly enhancing the online shopping experience.

Feature-Based Apps: Integrating Advanced AI/ML Components

Finally, there are feature-based intelligent apps. These apps incorporate advanced AI/ML components like neural networks, internal large language models (LLMs), and sophisticated algorithms as core features for the user. In this type of application, AI is the primary feature for users to engage with and the primary selling point for the app.

Some examples of feature-based applications are chatbots and virtual agents built using Azure Bot Service. These robo-representatives use AI to provide more natural and human-like conversation, balancing superior efficiency with a friendlier, more personalized user experience. The new Bing is a prime example of this implementation, integrating AI to give users a knowledgeable copilot for web searches.

Similarly, OpenAI’s ChatGPT allows users access to its state-of-the-art language models available as Microsoft’s Azure OpenAI Service. It leverages the power of the highly sophisticated GPT-3 LLM to perform tasks like drafting emails, writing code, answering general knowledge questions, translating between languages, and tutoring in numerous subjects.

These apps don’t just use AI to provide outputs or improve user experiences: They push the boundaries of what’s possible in app development by integrating AI/ML components.

info

Register for the live Q&A Ask The Expert session on September 26 with the Azure Functions product engineering team.

Breaking Down the Role of AI in Intelligent Applications

Intelligent apps reflect the merging of traditional software design with the dynamic capabilities of AI. Let’s explore how AI integrates and amplifies these applications.

At its core, AI augments applications with the capacity to self-learn and self-improve. It allows you to create applications that predict, react, and adapt to a constantly changing environment. In the power grid management system for a major city, an AI-infused application can forecast power usage trends, react to anomalies, and adapt to sudden demand surges, ensuring optimal efficiency and preventing blackouts.

But AI’s role doesn’t end at prediction or decision-making: It also automates tasks. In enterprise solutions, it has moved beyond simple automation to encompass intelligent process automation (IPA). In this arena, AI algorithms understand, learn, and then automate entire digital workflows. For developers, this is a fertile ground for creating applications that can dramatically improve the operational efficiency of businesses.

Even more, AI substantially aids in development tasks. For example, there’s GitHub Copilot, an AI-powered code assistant that offers suggestions based not only on syntax but on development best practices and the intent of the code. AI has evolved from a tool you use to build apps to a partner that helps in creating them. 

Using AI to build intelligent apps isn’t about throwing out what’s worked before. It’s about enhancing the components that have the most impact and achieve the best results. Incorporating AI in apps elevates their predictive abilities, streamlines complex tasks, and even aids in the development and creation process. When you build intelligent apps, you don’t just address current problems. You anticipate future challenges and craft solutions that adapt over time.

Summary

As developers the power of AI is in our hands, and it’s our responsibility to harness its full potential. Each category tells the story of AI's transformative potential, from applications that deliver intelligent outcomes to those with AI capabilities to enrich the user experience and those pushing boundaries with advanced AI/ML technology.

Accessible and robust platforms like Azure AI can simplify the process of developing Intelligent Applications. Whether you’re refining an existing app or building a new one, just entering the world of AI, or already familiar with the landscape, take a moment to appreciate the potential that AI integration offers. You’re embracing this exciting frontier and shaping the future of app development. 

Exercise

  • Complete the Cloud Skills Challenge to build on your apps and AI skills.
  • Watch Episode 01 of the Serverless Edition Learn Live session to learn how to build an end-to-end intelligent app solution.
+ + \ No newline at end of file diff --git a/30daysofIA/tags/fall-for-ia/page/9/index.html b/30daysofIA/tags/fall-for-ia/page/9/index.html index 283c37c75f..435737905a 100644 --- a/30daysofIA/tags/fall-for-ia/page/9/index.html +++ b/30daysofIA/tags/fall-for-ia/page/9/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "Fall-For-IA" | Build Intelligent Apps On Azure +11 posts tagged with "Fall-For-IA" | Build Intelligent Apps On Azure @@ -14,13 +14,14 @@ - - + +
-

9 posts tagged with "Fall-For-IA"

View All Tags

· 2 min read
It's 30DaysOfIA

September is almost here - and that can only mean one thing!! It's time to 🍂 Fall for something new and exciting and spend a few weeks skilling up on relevant tools, techologies and solutions!!

Last year, we focused on #ServerlessSeptember. This year, we're building on that theme with the addition of cloud-scale Data, cloud-native Technologies and cloud-based AI integrations to help you modernize and build intelligent apps for the enterprise!

Watch this space - and join us in September to learn more!

- - +
Skip to main content

11 posts tagged with "Fall-For-IA"

View All Tags

· 4 min read
It's 30DaysOfIA

This Fall focus on building intelligent apps using AI and cloud-native technologies. #FallForIntelligentApps brings to you a learning journey to build your skills on creating differentiated experiences while modernizing your applications. It’s time to learn it all.

What We'll Cover

image

Get Ready To #FallForIntelligentApps starting September 18!

Today, we kick off the Fall season with content and activities to skill you up on all things Intelligent Apps or AI Apps on Azure with content, events, and community interactions! Read on to learn about what is coming!

Explore Our Initiatives

We have a number of initiatives planned for the month to help you learn and skill up on relevant technologies. Click on the links to visit the relevant pages for each

image

Register for the events!

What are 4 things you can do today, to jumpstart your learning journey?

#30Days Of Intelligent Apps

#30DaysOfIA is a series of daily blog posts grouped into themed weeks - taking you from core concepts to end-to-end solution examples in 30 days. Each article will provide conceptual lessons paired with exercises and resources to help you reinforce learnings and take next steps.

This series takes you through learning journey in six stages, each building on the previous week to help you skill up in a beginner-friendly way:

We will start with defining intelligent apps and then expand on how to build with cloud-native technologies like Azure Kubernetes Service, Azure Container Apps and Azure Functions, and take you through end-to-end scenarios for real world application development. Before we dive deep on intelligent apps, here is a high-level overview of the Intelligent Apps landscape on Azure:

image

Containers on Azure services offer you a wide range of capabilities, from simplicity to control to suit your different needs.

image +image

To start with the basics for developing Kubernetes applications, explore #30DaysOfCloudNative.

Cloud-native development when paired with serverless computing enhances your solution architecture for building cost optimized, resilient applications.

image

To start with the basics for serverless computing, explore #30DaysOfServerless.

Let’s Get Started

Now you know everything! We hope you are as excited as we are to dive into a full month of active learning and doing! Don't forget to subscribe for updates in your favorite feed reader! And look out for our first Intelligent Apps post Monday!

+ + \ No newline at end of file diff --git a/30daysofIA/tags/github-actions/index.html b/30daysofIA/tags/github-actions/index.html index 0f7582c091..dad16188e7 100644 --- a/30daysofIA/tags/github-actions/index.html +++ b/30daysofIA/tags/github-actions/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "github-actions" | Build Intelligent Apps On Azure +11 posts tagged with "github-actions" | Build Intelligent Apps On Azure @@ -14,13 +14,13 @@ - - + +
-

9 posts tagged with "github-actions"

View All Tags

· 10 min read
It's 30DaysOfIA

Prepare your organization to adopt intelligent apps by taking a change management approach. Assess your readiness, upskill teams, create a culture of change and overcome resistance, and prepare for future evolution to pave the way for a smooth transition into an AI-centric company.

What We'll Cover:

  • Role of change management in modernizing to intelligent apps
  • Skills needed for building intelligent apps in your organization
  • How to build a culture of change to embrace AI
  • How to prepare for the future of intelligent apps and continuous evolution

image of modernizing AI solutions for intelligent apps

Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management

As we’ve discussed throughout this series, intelligent apps and generative AI are rewriting the rules of business and application development. The first article of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” explored how intelligent apps can enhance user experiences and drive businesses growth. These advancements are opening new opportunities and markets for companies ready to embrace them.

However, embracing this digital transformation is more complex than flipping a switch. It requires adjustments to the technical side, which we explored in the series’ fifth article, “Preparing the Path for Intelligent Apps: Transitioning from On-Premises/IaaS to Cloud Native Applications.” It also demands a shift in how organizations adapt to change and approach app design and development. Let’s explore that mindset transition with tips to help you make it happen.

The Role of Change Management in Transitioning to Intelligent Apps

Transitioning to intelligent apps isn’t merely a technological change. It’s a profound shift in how businesses perceive and operate. This move toward artificial intelligence (AI)-centric approach isn’t meant to replace humans with machines or radically overhaul all business operations.

Instead, intelligent apps should augment human capabilities, streamline processes, and foster an environment where innovation flourishes. They expand toolkits and techniques to include AI-assisted development tools like GitHub Copilot and AI-enabled workflows, helping us design and deliver AI-enhanced experiences.

Effective change management strategies are crucial in helping employees adapt to these new ways of working with intelligent apps, ensuring that the transition doesn’t disrupt productivity or morale.

Team and organization strategies should: 

  • Assess the impact of any proposed changes.  
  • Communicate the benefits and reasons behind the change to all affected team members.  
  • Provide the necessary training and support.  
  • Integrate the changes into the culture and daily operations. 

By incorporating this robust change management approach, you can identify and mitigate potential transition risks early. Moreover, this strategy ensures that employees are engaged throughout the process, fostering a sense of involvement and ownership. This, in turn, cultivates a culture suited for innovation through intelligent apps. 

These organizational changes focus on the following aspects: organizational readiness, skillset, culture of change and overcoming resistance, and adaptability. Let’s explore them below.

info

Register for the intelligent apps webinar on Driving Business Value by Modernizing with Cloud-Native & AI with Microsoft and Forrester on September 26. 

Explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries. 

Identifying Organizational Readiness for Intelligent Apps

The first step in preparing your organization for intelligent apps is to assess your current readiness for change. Analyze various elements of your organization, from infrastructure and workforce skills to strategic vision and overall culture.

Can your apps and systems integrate with AI platforms like Microsoft Azure AI? Is your workforce comfortable working alongside AI, or are they fearful and resistant? Does your strategic vision incorporate AI’s transformative potential, or does it fail to acknowledge AI as a critical driver of future growth?

These questions identify the challenges of integrating AI into an organization and determine the company’s readiness for intelligent apps.

Embracing intelligent apps involves more than being ready with your technological infrastructure or having the right technology or tools. You need flexible organizational structures and processes driving your business to accommodate new ideas and capabilities. Your teams must feel empowered to own and drive them. Much like Microsoft’s shift to AI-powered services—like AI-powered Bing and Edge and Microsoft 365 Copilot—embracing AI organization-wide requires a holistic vision and willingness to innovate, with teams capable of delivering on the plan.

After accessing these critical areas for organizational readiness, you can start planning a strategy for this transition to an AI-ready organization. Identify skill gaps and work to bridge them while nurturing an environment that appreciates and harnesses the potential of AI and intelligent apps.

Nurturing the Indispensable Skills for Intelligent Apps

Tasking your AI specialists with preparing your organization to use and build intelligent apps isn’t enough. Accomplishing this goal requires a cross-functional effort and a diverse skill set.

Developing intelligent apps involves data scientists managing and interpreting vast amounts of data, machine learning engineers building and training the models, and traditional developers integrating these models into usable applications. However, the impact of generative AI spans beyond creating apps. AI fluency across the entire organization—from sales and marketing to accounting and legal departments—offers substantial benefits, sparking new ideas and solutions from sometimes unexpected sources.

Teams' AI skills might roughly fall into three levels: 

  • Fundamental skills — Being familiar and efficient with using AI tools, for example, knowing how to write effective prompts for large language models (LLMs).
  • Visionary skills — Recognizing AI’s strengths and weaknesses, for example, its creativity and hallucinations, that is, unexpected output.
  • Expert skills — Managing data and training models and integrating AI technology into apps, such as incorporating the OpenAI application programming interface (API) into an Intelligent App.

Based on these levels, you can gauge your team's skill readiness to pinpoint the most significant gaps, whether in general AI knowledge across your organization or within technical development teams.

As intelligent apps become increasingly prevalent, upskilling your workforce is crucial. This transition opens a new realm of skills and capabilities for your team to master. You can offer machine learning, data analysis, and cloud computing training programs—such as Microsoft’s Azure AI Fundamentals certification—to empower your team to confidently navigate the intelligent apps landscape.

Nurturing these skills within your organization may require hiring new talent, reskilling current employees, and creating an environment that promotes learning and experimentation. Specifically, if your team understands tools like the Microsoft Azure AI platform, they could contribute significantly to developing and maintaining intelligent apps.

An upskilled team is more than a functional unit. It’s a think tank to drive innovation and improve your organization’s competitive standing in the digital marketplace.

Building a Culture of Change to Embrace AI

Even with the right people and skills in place, change remains an uphill battle for organizations committed to traditional processes. 

Transitioning to intelligent apps is as much about mindset as technology and skills. It requires fostering a culture open to change and innovation that understands AI’s transformative potential and embraces it to enhance rather than replace human capabilities. 

Leaders play a pivotal role in cultivating this mindset. As they model a positive attitude toward AI, emphasize its benefits, and address any concerns transparently, leaders significantly contribute to the organization’s readiness to adopt intelligent apps. 

The most critical factor in this transition is enabling an AI-friendly culture. It’s about encouraging curiosity, rewarding innovative thinking, and creating an environment where people thrive on learning and experimentation. 

Empowering teams to leverage practical AI tools and AI-driven data insights to aid decision-making helps motivate employees to use and explore AI’s potential, improve their skills, and contribute to the successful integration of intelligent apps. Such an environment is fertile ground to grow breakthrough ideas and innovative solutions. 

Overcoming Resistance to Change and Adopting Intelligent Apps

Despite AI’s potential, change isn’t easy. It takes effort and is especially challenging for organizations used to existing processes. This resistance to change is a significant roadblock when transitioning to intelligent apps. 

Overcoming this resistance involves:  

  • Understanding employees’ concerns. 
  • Challenging underlying assumptions about traditional systems and outdated processes that hinder change. 
  • Providing comprehensive training for workers to use AI effectively.
  • Demonstrating the benefits and opportunities of intelligent apps. 
  • Clear and continuous communication to dispel fears and misconceptions about AI.

These steps will foster greater acceptance and enthusiasm for intelligent apps in your teams. 

Preparing for the Future of Intelligent Apps and Continuous Evolution

Finally, it’s essential to understand that generative AI is just beginning to gain momentum. Adjusting to a world of intelligent apps isn’t a one-time change—it’s a continuous evolution. 

As AI technologies mature and evolve and more businesses realize their benefits, we can expect the landscape of intelligent apps to expand further. Organizations can cultivate a mindset of continuous learning, flexibility, and adaptation to prepare for this future. Staying ahead in this dynamic field requires organizations to keep up with the latest AI advancements, continually reassess their strategies, and pivot when necessary. 

The domain of intelligent apps is rapidly advancing. Organizations must prepare to navigate this dynamic landscape to stay ahead of the curve.

Summary

Preparing for AI and intelligent apps is a strategic decision. It requires thoughtful preparation and a culture that embraces change.

With the proper preparation and mindset, adopting AI can become a smoother, collaborative path to innovation for the whole organization. Integrating AI into your organization does not discard what has worked in the past. It simply enhances and augments those processes for a better future.

Consider the points above to assess your organization’s readiness and prepare to welcome intelligent apps.

In the first week of this series, you learned about intelligent apps, how organizations today find success through intelligent apps, what AI-assisted application development looks like, ways to leverage the power of generative AI, the technical changes needed to prepare for intelligent apps, and, in this article, the organizational changes required to harness the power of AI.

The future is intelligent. Use this series as a guideline to navigate challenges while preparing for and embracing AI's opportunities for an intelligent tomorrow.

Exercise

- - +
Skip to main content

11 posts tagged with "github-actions"

View All Tags

· 10 min read
It's 30DaysOfIA

Prepare your organization to adopt intelligent apps by taking a change management approach. Assess your readiness, upskill teams, create a culture of change and overcome resistance, and prepare for future evolution to pave the way for a smooth transition into an AI-centric company.

What We'll Cover:

  • Role of change management in modernizing to intelligent apps
  • Skills needed for building intelligent apps in your organization
  • How to build a culture of change to embrace AI
  • How to prepare for the future of intelligent apps and continuous evolution

image of modernizing AI solutions for intelligent apps

Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management

As we’ve discussed throughout this series, intelligent apps and generative AI are rewriting the rules of business and application development. The first article of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” explored how intelligent apps can enhance user experiences and drive businesses growth. These advancements are opening new opportunities and markets for companies ready to embrace them.

However, embracing this digital transformation is more complex than flipping a switch. It requires adjustments to the technical side, which we explored in the series’ fifth article, “Preparing the Path for Intelligent Apps: Transitioning from On-Premises/IaaS to Cloud Native Applications.” It also demands a shift in how organizations adapt to change and approach app design and development. Let’s explore that mindset transition with tips to help you make it happen.

The Role of Change Management in Transitioning to Intelligent Apps

Transitioning to intelligent apps isn’t merely a technological change. It’s a profound shift in how businesses perceive and operate. This move toward artificial intelligence (AI)-centric approach isn’t meant to replace humans with machines or radically overhaul all business operations.

Instead, intelligent apps should augment human capabilities, streamline processes, and foster an environment where innovation flourishes. They expand toolkits and techniques to include AI-assisted development tools like GitHub Copilot and AI-enabled workflows, helping us design and deliver AI-enhanced experiences.

Effective change management strategies are crucial in helping employees adapt to these new ways of working with intelligent apps, ensuring that the transition doesn’t disrupt productivity or morale.

Team and organization strategies should: 

  • Assess the impact of any proposed changes.  
  • Communicate the benefits and reasons behind the change to all affected team members.  
  • Provide the necessary training and support.  
  • Integrate the changes into the culture and daily operations. 

By incorporating this robust change management approach, you can identify and mitigate potential transition risks early. Moreover, this strategy ensures that employees are engaged throughout the process, fostering a sense of involvement and ownership. This, in turn, cultivates a culture suited for innovation through intelligent apps. 

These organizational changes focus on the following aspects: organizational readiness, skillset, culture of change and overcoming resistance, and adaptability. Let’s explore them below.

info

Register for the intelligent apps webinar on Driving Business Value by Modernizing with Cloud-Native & AI with Microsoft and Forrester on September 26. 

Explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries. 

Identifying Organizational Readiness for Intelligent Apps

The first step in preparing your organization for intelligent apps is to assess your current readiness for change. Analyze various elements of your organization, from infrastructure and workforce skills to strategic vision and overall culture.

Can your apps and systems integrate with AI platforms like Microsoft Azure AI? Is your workforce comfortable working alongside AI, or are they fearful and resistant? Does your strategic vision incorporate AI’s transformative potential, or does it fail to acknowledge AI as a critical driver of future growth?

These questions identify the challenges of integrating AI into an organization and determine the company’s readiness for intelligent apps.

Embracing intelligent apps involves more than being ready with your technological infrastructure or having the right technology or tools. You need flexible organizational structures and processes driving your business to accommodate new ideas and capabilities. Your teams must feel empowered to own and drive them. Much like Microsoft’s shift to AI-powered services—like AI-powered Bing and Edge and Microsoft 365 Copilot—embracing AI organization-wide requires a holistic vision and willingness to innovate, with teams capable of delivering on the plan.

After accessing these critical areas for organizational readiness, you can start planning a strategy for this transition to an AI-ready organization. Identify skill gaps and work to bridge them while nurturing an environment that appreciates and harnesses the potential of AI and intelligent apps.

Nurturing the Indispensable Skills for Intelligent Apps

Tasking your AI specialists with preparing your organization to use and build intelligent apps isn’t enough. Accomplishing this goal requires a cross-functional effort and a diverse skill set.

Developing intelligent apps involves data scientists managing and interpreting vast amounts of data, machine learning engineers building and training the models, and traditional developers integrating these models into usable applications. However, the impact of generative AI spans beyond creating apps. AI fluency across the entire organization—from sales and marketing to accounting and legal departments—offers substantial benefits, sparking new ideas and solutions from sometimes unexpected sources.

Teams' AI skills might roughly fall into three levels: 

  • Fundamental skills — Being familiar and efficient with using AI tools, for example, knowing how to write effective prompts for large language models (LLMs).
  • Visionary skills — Recognizing AI’s strengths and weaknesses, for example, its creativity and hallucinations, that is, unexpected output.
  • Expert skills — Managing data and training models and integrating AI technology into apps, such as incorporating the OpenAI application programming interface (API) into an Intelligent App.

Based on these levels, you can gauge your team's skill readiness to pinpoint the most significant gaps, whether in general AI knowledge across your organization or within technical development teams.

As intelligent apps become increasingly prevalent, upskilling your workforce is crucial. This transition opens a new realm of skills and capabilities for your team to master. You can offer machine learning, data analysis, and cloud computing training programs—such as Microsoft’s Azure AI Fundamentals certification—to empower your team to confidently navigate the intelligent apps landscape.

Nurturing these skills within your organization may require hiring new talent, reskilling current employees, and creating an environment that promotes learning and experimentation. Specifically, if your team understands tools like the Microsoft Azure AI platform, they could contribute significantly to developing and maintaining intelligent apps.

An upskilled team is more than a functional unit. It’s a think tank to drive innovation and improve your organization’s competitive standing in the digital marketplace.

Building a Culture of Change to Embrace AI

Even with the right people and skills in place, change remains an uphill battle for organizations committed to traditional processes. 

Transitioning to intelligent apps is as much about mindset as technology and skills. It requires fostering a culture open to change and innovation that understands AI’s transformative potential and embraces it to enhance rather than replace human capabilities. 

Leaders play a pivotal role in cultivating this mindset. As they model a positive attitude toward AI, emphasize its benefits, and address any concerns transparently, leaders significantly contribute to the organization’s readiness to adopt intelligent apps. 

The most critical factor in this transition is enabling an AI-friendly culture. It’s about encouraging curiosity, rewarding innovative thinking, and creating an environment where people thrive on learning and experimentation. 

Empowering teams to leverage practical AI tools and AI-driven data insights to aid decision-making helps motivate employees to use and explore AI’s potential, improve their skills, and contribute to the successful integration of intelligent apps. Such an environment is fertile ground to grow breakthrough ideas and innovative solutions. 

Overcoming Resistance to Change and Adopting Intelligent Apps

Despite AI’s potential, change isn’t easy. It takes effort and is especially challenging for organizations used to existing processes. This resistance to change is a significant roadblock when transitioning to intelligent apps. 

Overcoming this resistance involves:  

  • Understanding employees’ concerns. 
  • Challenging underlying assumptions about traditional systems and outdated processes that hinder change. 
  • Providing comprehensive training for workers to use AI effectively.
  • Demonstrating the benefits and opportunities of intelligent apps. 
  • Clear and continuous communication to dispel fears and misconceptions about AI.

These steps will foster greater acceptance and enthusiasm for intelligent apps in your teams. 

Preparing for the Future of Intelligent Apps and Continuous Evolution

Finally, it’s essential to understand that generative AI is just beginning to gain momentum. Adjusting to a world of intelligent apps isn’t a one-time change—it’s a continuous evolution. 

As AI technologies mature and evolve and more businesses realize their benefits, we can expect the landscape of intelligent apps to expand further. Organizations can cultivate a mindset of continuous learning, flexibility, and adaptation to prepare for this future. Staying ahead in this dynamic field requires organizations to keep up with the latest AI advancements, continually reassess their strategies, and pivot when necessary. 

The domain of intelligent apps is rapidly advancing. Organizations must prepare to navigate this dynamic landscape to stay ahead of the curve.

Summary

Preparing for AI and intelligent apps is a strategic decision. It requires thoughtful preparation and a culture that embraces change.

With the proper preparation and mindset, adopting AI can become a smoother, collaborative path to innovation for the whole organization. Integrating AI into your organization does not discard what has worked in the past. It simply enhances and augments those processes for a better future.

Consider the points above to assess your organization’s readiness and prepare to welcome intelligent apps.

In the first week of this series, you learned about intelligent apps, how organizations today find success through intelligent apps, what AI-assisted application development looks like, ways to leverage the power of generative AI, the technical changes needed to prepare for intelligent apps, and, in this article, the organizational changes required to harness the power of AI.

The future is intelligent. Use this series as a guideline to navigate challenges while preparing for and embracing AI's opportunities for an intelligent tomorrow.

Exercise

+ + \ No newline at end of file diff --git a/30daysofIA/tags/github-actions/page/10/index.html b/30daysofIA/tags/github-actions/page/10/index.html new file mode 100644 index 0000000000..a93d589cfc --- /dev/null +++ b/30daysofIA/tags/github-actions/page/10/index.html @@ -0,0 +1,27 @@ + + + + + +11 posts tagged with "github-actions" | Build Intelligent Apps On Azure + + + + + + + + + + + + + + +
+

11 posts tagged with "github-actions"

View All Tags

· 4 min read
It's 30DaysOfIA

Continue The Learning Journey through Fall For Intelligent Apps! 🍂

What We'll Cover

Thank you! ♥️

image

It's hard to believe that JavaScript on Azure hack-together is ending! It seems like just yesterday that we launched this initiative, and yet here we are, 15 days later, with an incredible amount of learning and growth behind us. So... it's time for a wrap!

From the bottom of our hearts, we want to thank each and every one of you for your participation, engagement, and enthusiasm. It's been truly inspiring to see the passion and dedication from this strong community, and we're honored to be a part of it. ✨

Recap of The JavaScript on Azure Global Hack-Together

As we wrap up this exciting event, we wanted to take a moment to reflect on all that we've accomplished together. Over the last 15 days, we've covered a lot of ground, from the basics of contributing to Open source to the exploration of the Contoso Real Estate project from its Frontend to its Backend and future AI implementation.

Now that the hack-together is ending, we want to make sure that you have all the resources you need to continue honing your skills in the future. Whether you're looking to make your fist contribution to open source, become an open source maintainers, collaborate with others, or simply keep learning, there are plenty of resources out there to help you achieve your goals. So, let's dive in and explore all the ways you can continue to grow your JavaScript skills on Azure!

JSonAzure Hack-together Roadmap 📍:

hack-together-roadmap (2)

Recap on past Livestreams🌟:

Day 1️⃣: Opening Keynote (Hack-together Launch): Introduction to the Contoso Real Estate Open-source project!, managing complex and complex enterprise architecture, new announcements for JavaScript developers on Azure

Day 2️⃣: GitHub Copilot & Codespaces: Introduction to your AI pair programmer (GitHub Copilot) and your virtual developer environment on the cloud (GitHub Codespaces)

Day 6️⃣: Build your Frontend using Static Web Apps as part of a complex, modern composable frontends (or micro-frontends) and cloud-native applications.

Day 9️⃣: Build a Serverless Backend using Azure Functions

Day 1️⃣3️⃣: Easily connect to an Azure Cosmos DB, exploring its benefits and how to get started

Day 1️⃣5️⃣: Being in the AI Era, we dive into the Azure OpenAI Service and how you can start to build intelligent JavaScript applications

📖 Self-Learning Resources

  1. JavaScript on Azure Global Hack Together Module collection
  2. Lets #HackTogether: Javascript On Azure Keynote
  3. Step by Step Guide: Migrating v3 to v4 programming model for Azure Functions for Node.Js Application

Continue your journey with #FallForIntelligentApps

Join us this Fall on a learning journey to explore building intelligent apps. Combine the power of AI, cloud-native app development, and cloud-scale data to build highly differentiated digital experiences. Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure. Engage in a self-paced learning adventure by following along the #30Days of Intelligent Apps series, completing the Intelligent Apps Skills Challenge, joining the Product Group for a live Ask The Expert series or building an end to end solution architecture with a live guided experience through the Learn Live series. Discover more here.

Hands-on practice: Make your first contribution to open source!

Join our GitHUb Discussion Forum to connect with developers from every part of world, see contributions from other, find collaborators and make your first contribution to a real-world project! +Don't forget to give the repo a star ⭐

Resources

All resources are accessible on our landing page

+ + + + \ No newline at end of file diff --git a/30daysofIA/tags/github-actions/page/11/index.html b/30daysofIA/tags/github-actions/page/11/index.html new file mode 100644 index 0000000000..0a116315a0 --- /dev/null +++ b/30daysofIA/tags/github-actions/page/11/index.html @@ -0,0 +1,26 @@ + + + + + +11 posts tagged with "github-actions" | Build Intelligent Apps On Azure + + + + + + + + + + + + + + +
+

11 posts tagged with "github-actions"

View All Tags

· 2 min read
It's 30DaysOfIA

September is almost here - and that can only mean one thing!! It's time to 🍂 Fall for something new and exciting and spend a few weeks skilling up on relevant tools, techologies and solutions!!

Last year, we focused on #ServerlessSeptember. This year, we're building on that theme with the addition of cloud-scale Data, cloud-native Technologies and cloud-based AI integrations to help you modernize and build intelligent apps for the enterprise!

Watch this space - and join us in September to learn more!

+ + + + \ No newline at end of file diff --git a/30daysofIA/tags/github-actions/page/2/index.html b/30daysofIA/tags/github-actions/page/2/index.html index a1e0511363..ff727a4477 100644 --- a/30daysofIA/tags/github-actions/page/2/index.html +++ b/30daysofIA/tags/github-actions/page/2/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "github-actions" | Build Intelligent Apps On Azure +11 posts tagged with "github-actions" | Build Intelligent Apps On Azure @@ -14,13 +14,13 @@ - - + +
-

9 posts tagged with "github-actions"

View All Tags

· 9 min read
It's 30DaysOfIA

Learn best practices for modernizing your on-premises or IaaS solutions to intelligent apps in the cloud.

What We'll Cover:

  • Traditional vs Intelligent Apps
  • On-Premises/IaaS vs. Cloud-Native Platforms
  • Modernization strategy for building Intelligent Apps

image of modernizing AI solutions for intelligent apps

Preparing the Path for Intelligent Apps: Transitioning from On-Premises/IaaS to Cloud-Native Applications

The proliferation of generative AI is paving the way for intelligent applications, software that leverages AI capabilities to deliver unparalleled functionality and user experience. As we covered in a previous article, “Demystifying Intelligent Applications: Leveraging AI in App Development,” intelligent Apps aren’t just products of advanced coding. They interact, learn, and evolve thanks to modern AI and machine learning (ML) breakthroughs. 

The paradigm shift from traditional to intelligent apps demands that we change our approach to the technical challenges of software and architecture design. It also necessitates adopting new ways of thinking and operating within an organization, which we explore in greater depth in “Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management.” 

This article explores the technical infrastructure powering intelligent apps, providing a roadmap to transition traditional on-premises or Infrastructure as a Service (IaaS) solutions to intelligent apps deployed to cloud native platforms and services. We’ll discover the differences between conventional and intelligent apps, options for re-architecting existing applications, and critical strategic considerations integral to modernizing the application-building approach. 

Understanding the Shift: Traditional vs. Intelligent Applications

Intelligent apps are more than just an incremental evolution from traditional apps. They involve a fundamental shift in software solution design, development, and deployment. A clear understanding of how each paradigm differs is crucial for comprehending the impact of this transition. 

Functionality and User Experience

Traditional applications, often rule-based and rigid, rely on preprogrammed operations. A traditional weather app, for example, typically presents structured, location-based forecasts using a predetermined data source. Meanwhile, an intelligent app leverages AI for data-driven decision-making and personalization. Rather than merely displaying the forecast, it integrates ML with additional data sources—like a user’s calendar and fitness data—to determine their favorite outdoor activities, provide relevant clothing suggestions, and even automatically design ideal vacations and times based on the user’s optimal weather preferences. 

Similarly, while traditional applications tend to offer a one-size-fits-all UI, intelligent apps use AI to create tailored interactions. For instance, an intelligent banking app might use generative AI to provide a voice-enabled UI that lets customers use natural language to ask about transactions and receive personalized financial advice.

Infrastructure

Intelligent apps integrating these sophisticated features require highly scalable, flexible infrastructure optimized for AI functionality and able to manage large volumes of real-time data. Unlike traditional applications deployed on-premises or in infrastructure as a service (IaaS) environments, intelligent apps demand the flexibility and scalability of cloud native architectures.

An on-premises or IaaS-based e-commerce application may struggle with connecting its data to AI services or scaling to accommodate voluminous real-time data and requests. But an intelligent app built with a cloud native approach and scalability at its core can handle variable demand much more efficiently. It also maintains its infrastructure components on demand, whether for collecting data for AI/ML consumption or user-facing features like AI-enabled customer support and personalization.

Consider an intelligent app built on Azure that creates ChatGPT-like experiences with custom data. It might connect Azure Open AI and Azure Cognitive Search to an app running on Azure Container Apps or Azure Kubernetes Service. 

The architecture might look like the following diagram based on this demo app:

diagram of a demo app architecture

While the cloud is well-equipped to handle the challenges associated with shifting to AI-centric infrastructure—handling real-time data processing and storage, integrating and managing AI services, configuring event-driven and microservices architectures, and ensuring security across the distributed environment—re-architecting a traditional app to an intelligent app requires careful planning and execution to ensure it benefits fully from the capabilities of AI and cloud computing.

On-Premises/IaaS vs. Cloud-Native Platforms

While on-premises and IaaS solutions provide substantial control over data and processes, this fine-grained control comes with setup and maintenance costs and may lack the scalability necessary for AI-driven Intelligent Apps. On-premises hosting involves purchasing and maintaining hardware, including servers and storage systems. IaaS involves leasing virtual hardware and then hiring and training IT personnel for the infrastructure’s operation, maintenance, and security.

Conversely, infrastructure built on cloud native app services with Microsoft Azure offers virtually unlimited scalability without ongoing hardware and maintenance costs. This feature is ideal for intelligent apps that need to adapt and scale to accommodate fluctuating workloads and data streams. And because these cloud infrastructures typically operate on a pay-as-you-go model, traditionally substantial upfront costs turn into predictable operating expenses, lowering both the cost and risk of launching applications.

Moreover, cloud service providers continually update their offerings, so building on sophisticated AI tools and services through the cloud, like the Azure AI Platform and orchestration capabilities with Azure Kubernetes Service, ensures that you have access to the latest AI and most advanced AI models and technologies.

Strategic Considerations for Transitioning to Intelligent Apps

When developing a strategy for transitioning to intelligent apps, you must consider costs, personnel skill development, and data management.

Monolithic architectures are unlikely to support the scale and ease of iteration required by intelligent apps, so you’ll likely need to implement code changes to support the transition. Your chosen hosting solution and the amount of data your app will handle affect cost and performance, in addition to determining privacy and compliance requirements.

An app that manages or stores terabytes or petabytes of data will require storage-optimized virtual machines or a managed database service hosting. Meanwhile, an app that processes video content or performs resource-intensive calculations should prioritize high-performance computing. Maximizing storage and performance can become extraordinarily costly.

Finally, existing applications shifting towards intelligent apps may encounter compatibility conflicts, depending on database type or the existing system’s underlying programming language. As you might imagine, a traditional app running on-premises and using a shared local database may need significant restructuring to support a cloud native architecture. You need plans for data migration, potential downtime, and training.

Next, let’s look at this transformation process.

Re-Architecting Monolithic Apps into Microservices

Shifting from monolithic applications to microservices is pivotal for transitioning to Intelligent Apps, and you can use this shift as an opportunity to transform your legacy software into intelligent apps that leverage the flexibility and power of microservices-enabled AI solutions.

Traditionally, a monolithic application (let’s use an online marketplace as an example) might handle all activities—from serving webpages to processing payments—within an on-premises server or a cloud server. However, this approach is challenging to scale and remains vulnerable to network failures or cyberattacks.

Breaking these applications into multiple services with specific responsibilities, such as authentication or notifications, can overcome these limitations. These services are typically deployed as microservices within container environments like Azure Kubernetes Service or serverless platforms like Azure Container Apps or Azure Functions using automated processes. This approach offers scalability, robustness, and improved performance, as each microservice can run independently and use its own database.

Shifting towards smaller, independent microservices in app architectures allows us to reap the benefits of AI-powered intelligent apps, for example, by using microservices to handle AI tasks such as processing natural language input, analyzing behavioral data, or personalizing content.

info

Watch Episode 1 and Episode 2 of the Learn Live series on how to build, test and deploy an end-to-end intelligent app solution using the Contoso Real Estate Sample.

Event-Driven Architectures: Knowing When to Use Them

Event-Driven Architectures (EDAs) can significantly enhance the performance and cost-effectiveness of intelligent apps. EDAs, typically implemented with microservices, respond in real time to events or state changes ranging from user interactions to real-time analytics.

Consider an intelligent app designed for personalized customer engagement. Instead of designing the application to check continuously for changes in user data, an EDA can react to such changes immediately, triggering appropriate microservices. For example, if users modify their preferences, an event could trigger an AI-driven recommendation microservice to update its suggestions immediately. This real-time responsiveness, enabled by services like Azure Event Grid, can improve user experiences and make intelligent apps more adaptive and proactive.

However, it’s worth noting that while EDAs provide significant benefits, they can introduce unique complexities, such as maintaining event consistency and debugging issues in event chains across multiple microservices. Therefore, it’s important to assess your application’s needs so you can understand where it can benefit from an EDA.

Conclusion

Transitioning to intelligent apps represents a shift in strategy, architecture, and infrastructure. By designing and re-architecting for cloud-based infrastructure, your apps gain scalability, cost-effectiveness, robust data management, access to sophisticated AI tools, enhanced security, and reduced operational overhead.  

But this transition extends well beyond its technical components. Organizations and the people working within them need equal attention. You can explore these facets in greater depth in “Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management.” 

The power of generative AI is here, and preparing for the move toward cloud-based intelligent applications will ensure that your apps remain performative and scalable and—most importantly—that your business is ready for the newest era of app innovation.

Exercise

- - +
Skip to main content

11 posts tagged with "github-actions"

View All Tags

· 9 min read
It's 30DaysOfIA

Learn best practices for modernizing your on-premises or IaaS solutions to intelligent apps in the cloud.

What We'll Cover:

  • Traditional vs Intelligent Apps
  • On-Premises/IaaS vs. Cloud-Native Platforms
  • Modernization strategy for building Intelligent Apps

image of modernizing AI solutions for intelligent apps

Preparing the Path for Intelligent Apps: Transitioning from On-Premises/IaaS to Cloud-Native Applications

The proliferation of generative AI is paving the way for intelligent applications, software that leverages AI capabilities to deliver unparalleled functionality and user experience. As we covered in a previous article, “Demystifying Intelligent Applications: Leveraging AI in App Development,” intelligent Apps aren’t just products of advanced coding. They interact, learn, and evolve thanks to modern AI and machine learning (ML) breakthroughs. 

The paradigm shift from traditional to intelligent apps demands that we change our approach to the technical challenges of software and architecture design. It also necessitates adopting new ways of thinking and operating within an organization, which we explore in greater depth in “Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management.” 

This article explores the technical infrastructure powering intelligent apps, providing a roadmap to transition traditional on-premises or Infrastructure as a Service (IaaS) solutions to intelligent apps deployed to cloud native platforms and services. We’ll discover the differences between conventional and intelligent apps, options for re-architecting existing applications, and critical strategic considerations integral to modernizing the application-building approach. 

Understanding the Shift: Traditional vs. Intelligent Applications

Intelligent apps are more than just an incremental evolution from traditional apps. They involve a fundamental shift in software solution design, development, and deployment. A clear understanding of how each paradigm differs is crucial for comprehending the impact of this transition. 

Functionality and User Experience

Traditional applications, often rule-based and rigid, rely on preprogrammed operations. A traditional weather app, for example, typically presents structured, location-based forecasts using a predetermined data source. Meanwhile, an intelligent app leverages AI for data-driven decision-making and personalization. Rather than merely displaying the forecast, it integrates ML with additional data sources—like a user’s calendar and fitness data—to determine their favorite outdoor activities, provide relevant clothing suggestions, and even automatically design ideal vacations and times based on the user’s optimal weather preferences. 

Similarly, while traditional applications tend to offer a one-size-fits-all UI, intelligent apps use AI to create tailored interactions. For instance, an intelligent banking app might use generative AI to provide a voice-enabled UI that lets customers use natural language to ask about transactions and receive personalized financial advice.

Infrastructure

Intelligent apps integrating these sophisticated features require highly scalable, flexible infrastructure optimized for AI functionality and able to manage large volumes of real-time data. Unlike traditional applications deployed on-premises or in infrastructure as a service (IaaS) environments, intelligent apps demand the flexibility and scalability of cloud native architectures.

An on-premises or IaaS-based e-commerce application may struggle with connecting its data to AI services or scaling to accommodate voluminous real-time data and requests. But an intelligent app built with a cloud native approach and scalability at its core can handle variable demand much more efficiently. It also maintains its infrastructure components on demand, whether for collecting data for AI/ML consumption or user-facing features like AI-enabled customer support and personalization.

Consider an intelligent app built on Azure that creates ChatGPT-like experiences with custom data. It might connect Azure Open AI and Azure Cognitive Search to an app running on Azure Container Apps or Azure Kubernetes Service. 

The architecture might look like the following diagram based on this demo app:

diagram of a demo app architecture

While the cloud is well-equipped to handle the challenges associated with shifting to AI-centric infrastructure—handling real-time data processing and storage, integrating and managing AI services, configuring event-driven and microservices architectures, and ensuring security across the distributed environment—re-architecting a traditional app to an intelligent app requires careful planning and execution to ensure it benefits fully from the capabilities of AI and cloud computing.

On-Premises/IaaS vs. Cloud-Native Platforms

While on-premises and IaaS solutions provide substantial control over data and processes, this fine-grained control comes with setup and maintenance costs and may lack the scalability necessary for AI-driven Intelligent Apps. On-premises hosting involves purchasing and maintaining hardware, including servers and storage systems. IaaS involves leasing virtual hardware and then hiring and training IT personnel for the infrastructure’s operation, maintenance, and security.

Conversely, infrastructure built on cloud native app services with Microsoft Azure offers virtually unlimited scalability without ongoing hardware and maintenance costs. This feature is ideal for intelligent apps that need to adapt and scale to accommodate fluctuating workloads and data streams. And because these cloud infrastructures typically operate on a pay-as-you-go model, traditionally substantial upfront costs turn into predictable operating expenses, lowering both the cost and risk of launching applications.

Moreover, cloud service providers continually update their offerings, so building on sophisticated AI tools and services through the cloud, like the Azure AI Platform and orchestration capabilities with Azure Kubernetes Service, ensures that you have access to the latest AI and most advanced AI models and technologies.

Strategic Considerations for Transitioning to Intelligent Apps

When developing a strategy for transitioning to intelligent apps, you must consider costs, personnel skill development, and data management.

Monolithic architectures are unlikely to support the scale and ease of iteration required by intelligent apps, so you’ll likely need to implement code changes to support the transition. Your chosen hosting solution and the amount of data your app will handle affect cost and performance, in addition to determining privacy and compliance requirements.

An app that manages or stores terabytes or petabytes of data will require storage-optimized virtual machines or a managed database service hosting. Meanwhile, an app that processes video content or performs resource-intensive calculations should prioritize high-performance computing. Maximizing storage and performance can become extraordinarily costly.

Finally, existing applications shifting towards intelligent apps may encounter compatibility conflicts, depending on database type or the existing system’s underlying programming language. As you might imagine, a traditional app running on-premises and using a shared local database may need significant restructuring to support a cloud native architecture. You need plans for data migration, potential downtime, and training.

Next, let’s look at this transformation process.

Re-Architecting Monolithic Apps into Microservices

Shifting from monolithic applications to microservices is pivotal for transitioning to Intelligent Apps, and you can use this shift as an opportunity to transform your legacy software into intelligent apps that leverage the flexibility and power of microservices-enabled AI solutions.

Traditionally, a monolithic application (let’s use an online marketplace as an example) might handle all activities—from serving webpages to processing payments—within an on-premises server or a cloud server. However, this approach is challenging to scale and remains vulnerable to network failures or cyberattacks.

Breaking these applications into multiple services with specific responsibilities, such as authentication or notifications, can overcome these limitations. These services are typically deployed as microservices within container environments like Azure Kubernetes Service or serverless platforms like Azure Container Apps or Azure Functions using automated processes. This approach offers scalability, robustness, and improved performance, as each microservice can run independently and use its own database.

Shifting towards smaller, independent microservices in app architectures allows us to reap the benefits of AI-powered intelligent apps, for example, by using microservices to handle AI tasks such as processing natural language input, analyzing behavioral data, or personalizing content.

info

Watch Episode 1 and Episode 2 of the Learn Live series on how to build, test and deploy an end-to-end intelligent app solution using the Contoso Real Estate Sample.

Event-Driven Architectures: Knowing When to Use Them

Event-Driven Architectures (EDAs) can significantly enhance the performance and cost-effectiveness of intelligent apps. EDAs, typically implemented with microservices, respond in real time to events or state changes ranging from user interactions to real-time analytics.

Consider an intelligent app designed for personalized customer engagement. Instead of designing the application to check continuously for changes in user data, an EDA can react to such changes immediately, triggering appropriate microservices. For example, if users modify their preferences, an event could trigger an AI-driven recommendation microservice to update its suggestions immediately. This real-time responsiveness, enabled by services like Azure Event Grid, can improve user experiences and make intelligent apps more adaptive and proactive.

However, it’s worth noting that while EDAs provide significant benefits, they can introduce unique complexities, such as maintaining event consistency and debugging issues in event chains across multiple microservices. Therefore, it’s important to assess your application’s needs so you can understand where it can benefit from an EDA.

Conclusion

Transitioning to intelligent apps represents a shift in strategy, architecture, and infrastructure. By designing and re-architecting for cloud-based infrastructure, your apps gain scalability, cost-effectiveness, robust data management, access to sophisticated AI tools, enhanced security, and reduced operational overhead.  

But this transition extends well beyond its technical components. Organizations and the people working within them need equal attention. You can explore these facets in greater depth in “Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management.” 

The power of generative AI is here, and preparing for the move toward cloud-based intelligent applications will ensure that your apps remain performative and scalable and—most importantly—that your business is ready for the newest era of app innovation.

Exercise

+ + \ No newline at end of file diff --git a/30daysofIA/tags/github-actions/page/3/index.html b/30daysofIA/tags/github-actions/page/3/index.html index f450a8f200..8a35b15417 100644 --- a/30daysofIA/tags/github-actions/page/3/index.html +++ b/30daysofIA/tags/github-actions/page/3/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "github-actions" | Build Intelligent Apps On Azure +11 posts tagged with "github-actions" | Build Intelligent Apps On Azure @@ -14,13 +14,13 @@ - - + +
-

9 posts tagged with "github-actions"

View All Tags

· 10 min read
It's 30DaysOfIA

Explore how digital natives and Independent Software Vendors (ISVs) can transform their business operations and generate higher value for customers with generative AI and intelligent apps.

What We'll Cover:

  • Potential of Generative AI in application development
  • Challenges with Generative AI
  • Integrating Generative AI with business operations

image of generative AI and intelligent apps used in business

Unleashing Business Transformation: How Digital Natives Can Leverage Generative AI

Generative AI is no longer a technological flight of fancy. It’s here, and it’s rewriting the rules of business innovation. 

This type of AI leverages machine learning algorithms to create new data with similar characteristics to a given dataset. It goes beyond text generation using generative pretrained transformer (GPT) models, covering an array of applications that span from image synthesis to translations and more. It also powers a new class of intelligent applications, which combine AI capabilities with traditional software functions for enhanced user experiences and advanced business solutions. 

Generative AI is transforming how digital natives and independent software vendors (ISVs) operate. Generative AI offers new possibilities for creating unique business value, enhancing customer experiences, and driving growth. 

It plays a critical role in applications, improving functionality, aiding decision-making, and automating complex tasks to deliver dynamic, uniquely personalized user experiences. Generative AI also enables and automates data-driven outcomes, functions, and features.

Integrating AI capabilities into customer-facing apps and turning them into intelligent apps benefits both the customer and the business, as we discussed in Demystifying Intelligent Applications: Leveraging AI in App Development. You get faster product development workflows and better business intelligence for sales and marketing, while your users get more natural and context-aware features and interactions. 

That said, to realize generative AI’s potential, you need some strategy and knowledge of how and where it fits into your current business and operations. We’ll examine this technology’s potential to transform business operations, its risks, and rewards, and how to integrate it into your existing operations. 

Unpacking the Potential of Generative AI

Generative AI is creating new opportunities in the digital playing field. It empowers businesses to generate insights, design products, and automate processes in ways and at speeds that were previously unimaginable. You can now predict customer behavior more accurately, offer highly personalized user experiences, and prototype innovative ideas rapidly. 

For example, Microsoft used generative AI to reinvent web search in the new AI-powered Bing and Edge. The search experience has evolved from a solitary user flow of typing in keywords and scrolling through results. It’s now a conversational experience where Bing uses context to offer better quality and more personalized search queries and results. 

Similarly, Microsoft harnessed generative AI to transform its productivity suite into intelligent apps. Through Copilot for Microsoft 365, PowerPoint now empowers users to tell their stories by using DALL-E to generate images effortlessly. It also helps rephrase text-heavy slides to be more straightforward. Outlook now assists users to compose more effective emails, enabling better communication.

These are just some examples of how AI powers day-to-day work and operations. 

Businesses that ignore generative AI miss out on untapped market opportunities, letting competitors gain an edge in the digital race. By seeking ways to rethink assumptions and reinvent existing approaches, you can effectively use generative AI to create new opportunities, open products to new use cases and markets, unlock new growth and customer satisfaction levels, and increase your revenue.

info

Register for the intelligent apps webinar on Driving Business Value by Modernizing with Cloud-Native & AI with Microsoft and Forrester on September 26.

Explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries. 

The Crucial Role of Generative AI in Business Operations

Generative AI can be a powerful catalyst for business transformation. Let’s quickly tour some ways it can redefine business operations. 

Product development is traditionally a long and painstaking process. Now, tools like GitHub Copilot and TestPilot use generative AI to make coding and testing software easier, faster, and more robust. 

Similarly, machine learning as a service technology, such as Azure Machine Learning, democratizes the availability of machine learning. It helps collect, clean, and train models across datasets, making engineering and analyzing data more efficient and accessible.  

Generative AI also simplifies the process of creating intelligent apps. Previously, simulating thousands of prototypes, predicting their performance, and narrowing down to the most viable options could take weeks, months, or even years of iteration and data analysis. With generative AI, this process can be as quick as days, hours, or perhaps minutes. 

Customer experience is also ripe for transformation. Personalized recommendations drive engagement and sales, and generative AI can scale that personalization to every customer interaction using comprehensive and continually updated data. For instance, Microsoft leverages AI to enable personalization in Dynamics 365 Customer Insights. Using this tool, you can ask questions about your customers in natural language and gain valuable and actionable insights into your customer base. 

Lastly, marketing and sales can benefit from generative AI. For example, identifying current and future key markets and emerging trends helps companies stay ahead of the curve. Tools like Copilot in Power BI help you quickly understand and uncover insights about your business, enabling your team to craft targeted campaigns, create engaging content, and optimize your business strategy. 

Leveraging this full generative AI potential requires the right tools. GPT-4, for instance, can be instrumental in realizing effective business transformation. It excels in understanding context, generating human-like text, and learning from minimal data input, making it a formidable tool in any digital native's arsenal. Platforms like Azure OpenAI Service bring GPT-4’s power to your fingertips.

However, as with any powerful technology, it’s unwise to treat generative AI as a perfect solution. You must know the challenges and risks of not leveraging it effectively, including technical and human-factor considerations. 

Without generative AI, your company risks missing the opportunity to please customers via personalization, as discussed previously. You’ll miss sales opportunities via business intelligence insights, limiting your ability to grow revenue and keep up with the competition. 

One challenge of leveraging generative AI’s full potential is finding the right tools. Fortunately, you can implement generative AI in your organization without investing in infrastructure, computational power, and expertise. Platforms like Azure OpenAI Service and Azure AI Services make AI easily accessible. They also work with Azure compute and data services like Azure Functions and Azure Kubernetes Service (AKS), enabling you to build intelligent apps easily and quickly. 

However, generative AI can’t do everything. It may need human expertise and intervention to use it fully. 

For example, generative AI can create unique data. However, the data may have limited diversity and complexity. The AI may need help recognizing additional considerations or requirements specific to a problem. It may also lack context due to various factors, such as the information’s freshness. Also, since it’s an emerging field that’s potentially worth $4.4 trillion, we’re still learning about the nuances and potential regulatory impact of generative AI. 

Perhaps the most crucial and challenging aspect of adopting generative AI is an organization’s required cultural shift. Your teams must learn how to use AI as your company fosters an understanding of AI’s potential and limitations. 

Begin integrating AI literacy into your company’s DNA. Knowledge of its potential and the readily available tools can inspire your teams to incorporate generative AI into their daily workflows and business operations quickly, improve productivity, and deliver superior customer experiences. 

No company wants to be outpaced by its competitors. You can balance generative AI’s potential with your business operations by staying aware of and adapting to the dynamic AI landscape.

Achieving Harmony: Integrating Generative AI with Existing Business Operations

Integrating generative AI into existing business operations doesn’t have to be disruptive. With the correct strategy, you can do so seamlessly. Your goal should be to augment and enhance your operations, not disrupt and replace. 

For example, Microsoft Cloud technologies leveraged AI to relieve healthcare organizations’ challenges, such as talent shortage and workforce burnout. Providers automated their workflows, invested in specific applications and connectors, and enhanced support for data and operations. 

Azure AI Services, such as text analytics for health, enable healthcare organizations to understand their largely unstructured and untapped medical data. Project Health Insights leans on AI to help healthcare professionals gain actionable insights and inferences from patient data. 

Azure Health Bot, an Intelligent App, empowers organizations to build and deploy legally compliant, powerful conversational healthcare experiences to patients. 

A successful AI integration like this begins with understanding. Knowing what generative AI can do and setting realistic expectations can help you identify where and how AI can best serve your operations. 

Next, you can foster a culture of AI literacy across teams, speeding up adoption by helping them understand how it can aid their work. The more familiar your team is with AI and its possibilities, the smoother the transition. Training and education programs play a pivotal role here. 

Finally, integration requires a systematic approach. It’s not about overhauling everything at once — it's about identifying areas where AI can add the most value, then gradually experimenting and scaling its use. Successful integration stories often feature a phased approach, starting small and growing steadily. 

As we move toward a future where generative AI is a staple in business operations, remember that every journey is unique. Although there’s no one-size-fits-all solution, a strategic approach and a keen understanding of your business needs will help you navigate this new landscape successfully.

Conclusion

Generative AI offers plenty of opportunities for businesses that embrace it. It’s fueling the next generation of intelligent applications, transforming everything from product development to customer engagement to marketing. This technology is the key to unlocking unprecedented growth and innovation. 

While the journey may have challenges, the potential rewards make generative AI worth exploring. As generative AI advances, Azure AI’s broad range of tools and services will help you navigate this fascinating field.

Exercise

  • Watch Episode 02 of the serverless edition Learn Live session to learn how to build, test and deploy an end-to-end intelligent app solution.
  • Complete the Cloud Skills Challenge to build on your AI skills.
  • Register for Ask the Expert: Azure Functions on September 26 for a live Q&A with the Product Team on how to build AI apps using serverless computing.
- - +
Skip to main content

11 posts tagged with "github-actions"

View All Tags

· 12 min read
It's 30DaysOfIA

Delve into creating an Intelligent App that leverages Azure AI Vision to analyze images and extract data. Develop an API to perform optical character recognition (OCR) on uploaded images and testing this API locally.

What We'll Cover:

  • Understanding Azure AI Vision and Azure Kubernetes Service
  • Build a Python Web API to perform OCR
  • Test the API locally

image depicting an Intelligent App using AI

Jumpstart Your AI Journey: Building Your First Intelligent App with Azure AI and AKS (1)

An Intelligent App is a software application that uses artificial intelligence (AI), such as machine learning (ML), to extract meaningful insights, enabling users to make informed decisions based on the extracted text, data, sound, images, and more.

Intelligent Apps can employ advanced algorithms and models to recognize objects, patterns, and features within images, allowing them to perform tasks like object identification, image classification, facial recognition, and image segmentation.

To explore the power of Intelligent Apps, let’s build a Python app that performs optical character recognition (OCR) on images containing numbers and performs aggregate functions on the extracted numbers, like sum and average. We’ll leverage Azure AI Services for the OCR functionality and Azure Kubernetes Service (AKS) to develop, publish, and maintain a cloud-based, scalable app in Azure.

Let’s get started!

Understanding Azure AI Vision and Azure Kubernetes Service

Azure AI Vision is a cloud-based service that offers a range of advanced computer vision capabilities enabling applications to interpret content, such as images and videos, using ML and other AI algorithms. Azure computer vision lets users integrate powerful vision features into their projects without ML experience.

OCR is one of the critical functionalities within Azure vision service, allowing applications to extract text from images. Within Azure AI Vision service, OCR offers several features, including text extraction, multilingual support, layout analysis of documents, and integration with Azure services, such as Azure Functions and Azure Machine Learning.

info

Watch the intelligent apps webinar on Driving Business Value by Modernizing with Cloud-Native & AI with Microsoft and Forrester on September 26.

AKS is Microsoft Azure’s container orchestration platform that enables us to quickly and efficiently deploy, manage, and scale containerized applications, such as our Intelligent App.

Let’s explore how to leverage these tools to build an Intelligent App that uses Azure AI Vision to analyze images and extract data.

Prerequisites

To follow this tutorial, ensure you have the following:

For a preview of this final project, take a look at the complete project code.

Building the API with Azure AI Vision Service

First, log in to your Azure account and navigate to the Azure Portal.

Click Create a resource and search for “resource group.” Create a new resource group named computer-vision.

image in Azure Portal of creating a new resource group

Return to the Azure Portal home page and click Create a resource.

image in Azure Portal of creating a new resource

Search for “computer vision” and select it from the results. Click Create.

image of searching for resource group in Azure Portal

Clicking Create displays the Create Computer Vision wizard. Select the resource group you just created as the Resource group from the dropdown. Enter a resource name similar to what is seen in the screenshot below. Note that this must be a unique name so choose one that is not currently in use. Select Standard S1 as the pricing tier and check the Responsible use of AI box in the field at the bottom.

Image of selecting the Responsible use of AI box

Image of project details

Click Review + Create, then click Create.

Next, get the API key and endpoint from the Azure AI services multi-service account you created. Once the resource is created, navigate to the resource details page. Under Resource Management, select Keys and Endpoint. Once on the Keys and Endpoint page, you’ll find the Key 1 and Endpoint values. These credentials are necessary to access the Azure AI APIs.

image of selecting Keys and Endpoint

second image of selecting Keys and Endpoint

Configuring the Local Environment Variables

Next, we need to define a local environment variable file. This convenient way to store our API keys in a simple text file enables us to manage sensitive information and separate configuration from our API code.

If you haven’t done so already, clone the starter project template from GitHub into your local computer. Open the starter project template in a code editor and create a new .env file in the root folder.

Note: Root folder in this case is the “/Microsoft_Series17-18_Code/intelligent-app-before" folder.

Add the following key-value pairs to the file, remembering to replace the placeholder values with those you obtained from the previous step.

VISION_KEY=<THE-KEY-1-VALUE-FROM-YOUR-AZURE-AI-SERVICE>
VISION_ENDPOINT=<THE-ENDPOINT-VALUE-FROM-YOUR-AZURE-AI-SERVICE>

Reviewing the Quickstart Code

Let’s review the app.py file in the starter project template.

The app.py file works as the entry point for the Flask application. It is responsible for initializing the web server, listening for incoming requests, dispatching them to the appropriate route handlers, and generating responses.

Below is the code contained in the app.py file:

import os
import json

from flask import Flask, request
from flask_restful import Resource, Api
from werkzeug.utils import secure_filename

app = Flask(__name__,
static_url_path='',
static_folder='static/files')

api = Api(app)

app = Flask(__name__)

app.config['UPLOAD_FOLDER'] = 'files'

api = Api(app)

class UploadHandler(Resource):

def allowed_file(self, filename):
return '.' in filename and \
filename.rsplit('.', 1)[1].lower() in {'png'}

def post(self):
form = request.form.to_dict()

if 'file' not in request.files:
return json.dumps({ "success": False, "error": "No file part"})

file = request.files.get("file")
if file and self.allowed_file(file.filename):
filename = secure_filename(file.filename)
upload_folder = "static/files"
if not os.path.exists(upload_folder):
os.makedirs(upload_folder)
local_file_path = os.path.join(upload_folder, filename)
file.save(local_file_path)

return f"File {filename} uploaded successfully to folder: {upload_folder}"

api.add_resource(UploadHandler, "/")

if __name__ == '__main__':
app.run(debug=True)

The UploadHandler class above is defined as a Flask RESTful resource to handle file uploads. The class contains two methods:

  • The allowed_file method checks whether the file extension is allowed for upload. In this case, only .png files are allowed.

  • The post method handles HTTP POST requests for file uploads. It saves the uploaded .png file to the static/files folder.

Finally, the application returns a text response informing us that the file has been uploaded successfully.

Implementing the REST API in Python

To implement image analysis in your REST API, open the starter project template in your terminal, create a virtual environment, and activate it.

Next, install the Azure AI Vision SDK:

pip install azure-ai-vision

Then, add the following line to the requirements.txt file to include the azure-ai-vision package:

azure-ai-vision==0.13.0b1

Now, create an ocr_helper.py file in the project’s root folder. This file is a Python module that provides functions for processing images using OCR. Add the following code to the file:

import os
from statistics import median
from decimal import Decimal
import azure.ai.vision as sdk

def process_ocr(source_image):
service_options = sdk.VisionServiceOptions(os.environ["VISION_ENDPOINT"],
os.environ["VISION_KEY"])

vision_source = sdk.VisionSource(filename=source_image)

analysis_options = sdk.ImageAnalysisOptions()

analysis_options.features = (
sdk.ImageAnalysisFeature.CAPTION |
sdk.ImageAnalysisFeature.TEXT
)

analysis_options.language = "en"

analysis_options.gender_neutral_caption = True

image_analyzer = sdk.ImageAnalyzer(service_options, vision_source, analysis_options)

result = image_analyzer.analyze()

ocr_result = get_ocr_result(result)

return ocr_result

def get_ocr_result(result):
string_list = []

if result.reason != sdk.ImageAnalysisResultReason.ANALYZED:
return sdk.ImageAnalysisErrorDetails.from_result(result)
else:
if result.text is not None:
for line in result.text.lines:
for word in line.words:
string_list.append(word.content)

number_list = convert_to_decimal_list(string_list)

aggregate_result = aggregate_operations(number_list)

return {
"aggregate_result": aggregate_result,
"numbers_read": string_list
}

def convert_to_decimal_list(string_list):
return list(map(Decimal, string_list))

def aggregate_operations(numbers):
result = {
'sum': sum(numbers),
'average': sum(numbers) / len(numbers),
'median': median(numbers),
'min': min(numbers),
'max': max(numbers)
`}`
`return result`

This module uses the azure-ai-vision package to analyze images, including capturing captions and extracting text from the image. The process_ocr function inputs an image file and performs the OCR analysis. The module is a convenient tool for analyzing text-based data in pictures and performing numerical computations based on that data.

Let’s review the different components of the ocr_helper.py module listed above and explore how its functions help our Intelligent App process images and perform aggregate operations:

  • The process_ocr function takes the parameter source_image, which is the path of the image to be processed. The function then initializes the VisionServiceOptions using environment variables VISION_ENDPOINT and VISION_KEY to connect to the Azure AI Vision API.
  • The process_ocr function creates a VisionSource object with the specified source_image file name. ImageAnalysisOptions specify the features to be analyzed, including CAPTION and TEXT. The language is set to English ("en"), and gender-neutral captions are enabled.
  • Finally, an ImageAnalyzer object is created with the service options, vision source, and analysis options. The image is then analyzed using the image_analyzer.analyze method to retrieve OCR results. The recognized text from the OCR result is extracted, with numerical values stored in the string_list variable.
  • The convert_to_decimal_list function converts the list of strings to a list of decimal numbers, which helps process numeric values extracted from the text.
  • The aggregate_operations function calculates various aggregate statistics (sum, average, median, minimum, maximum) from a list of numbers and returns the results in a dictionary.

Note that you must have the appropriate credentials (VISION_KEY) and endpoint (VISION_ENDPOINT) configured for the Azure AI Vision API to use this module.

Finally, we must modify the app.py file so our code can use the process_ocr function of the ocr_helper.py file.

Add the following import statement to the app.py file:

from ocr_helper import process_ocr

Then, replace this line:

return f"File {filename} uploaded successfully to folder: {upload_folder}"

With these two lines:

aggregates = process_ocr(local_file_path)
return json.dumps(aggregates, default=str)

Doing so allows our application to perform aggregate operations (sum, average, median, minimum, maximum) of any numeric values found in the text and return the operation’s result as a JSON response.

Running the Intelligent App Locally

Our final goal is to deploy the sample app to AKS, which will run the app in a container. However, as a good practice, let’s run the app in a container locally before moving it to AKS. Doing so ensures that our application and its dependencies are encapsulated and can be run consistently across different environments.

Deploying to Docker Desktop

For our application to run on Docker, it needs two additional files: a Dockerfile and docker-compose.yml. A Dockerfile creates a single container image by specifying the steps to build it, and a docker-compose.yml file manages multiple containers as a cohesive application, defining their configurations and relationships. Dockerfile builds the image, while docker-compose.yml orchestrates multi-container applications.

In the project root folder, add a file named Dockerfile with the content below:

# syntax=docker/dockerfile:1

FROM python:3.8-slim-buster

WORKDIR /intelligentapp

COPY requirements.txt requirements.txt
RUN pip3 install -r requirements.txt

RUN pip install debugpy

COPY . .

CMD [ "python3", "-m" , "flask", "run", "--host=0.0.0.0"]

Now create a file named docker-compose.yml file in the project’s root folder with the following code, replacing the VISION_KEY and the VISION_ENDPOINT according to the environment variables you configured earlier:

version: '3.8'
services:
intelligentapp:
build:
context: .
dockerfile: Dockerfile
image: intelligent-app
ports:
- 5000:5000
container_name: intelligent-app
environment:
- VISION_KEY=<THE-KEY-1-VALUE-FROM-YOUR-AZURE-AI-SERVICE>
- VISION_ENDPOINT=<THE-ENDPOINT-VALUE-FROM-YOUR-AZURE-AI-SERVICE>

Then, run the following command in the terminal to build the image and start a container for the Intelligent App services defined in the docker-compose.yml file:

docker-compose up --build --force-recreate

Next, open a new terminal and run the command below to list the image deployed to your local Docker:

docker images

docker images

Testing the Intelligent App Locally

Now, we’ll test the Intelligent App’s functionality using the following test image:

test image to test functionality

This image is provided as sample1.png in the sample app source files, so you can easily use it in the next step.

The Intelligent App will read the image above using OCR to extract numbers in the image and perform five aggregate functions:

  • Sum
  • Average
  • Median
  • Min
  • Max

To test the API, open Postman and fill out the fields as follows:

  • URL: http://localhost:5000/
  • Method: POST
  • Body:
    • Form-data
      • Key: File — Click the right end of the Key field and select File from the dropdown list.
      • Value: Click Select Files, then select the sample1.png file provided in the sample code.

image of selecting the sample file

Now click the Send button and review the result body:

"{\"aggregate_result\": {\"sum\": \"25821\", \"average\": \"5164.2\", \"median\": \"5622\", \"min\": \"1447\", \"max\": \"9802\"}, \"numbers_read\": [\"3049\", \"5901\", \"5622\", \"1447\", \"9802\"]}"

As we can see, the app running on a local container returned the correct results based on the sample image. So, we’re ready to prepare our Azure resources to run the Intelligent App on AKS.

Exercise

In this article, we explored the creation of an Intelligent App that leverages Azure AI Vision to analyze images and extract data. We learned how to build a Python Web API to perform OCR on uploaded images and subsequently test this API locally.

Head to the next part, to deploy this API via Azure Kubernetes Service.

+ + \ No newline at end of file diff --git a/30daysofIA/tags/github-actions/page/4/index.html b/30daysofIA/tags/github-actions/page/4/index.html index c2aa16bbbf..fece9d9b16 100644 --- a/30daysofIA/tags/github-actions/page/4/index.html +++ b/30daysofIA/tags/github-actions/page/4/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "github-actions" | Build Intelligent Apps On Azure +11 posts tagged with "github-actions" | Build Intelligent Apps On Azure @@ -14,13 +14,13 @@ - - + +
-

9 posts tagged with "github-actions"

View All Tags

· 9 min read
It's 30DaysOfIA

In the part of our series on intelligent apps, we’ll explore how AI is transforming application development, from design and architecture to building.

What We'll Cover:

  • Revolutionizing application development using AI
  • Infusing AI in application design architecture
  • AI Assisted Pair Programming
  • Future of App Development

A decorative image of application development with AI

Reimagining Application Development With AI: A New Paradigm

Artificial intelligence (AI) is impacting every industry. AI-powered development tools offer new functionalities and assist software developers throughout the development process, revolutionizing app development. From initial design and architecture to coding and debugging, AI reshapes how we approach application conception, creation, and iteration.

In part one of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” we explored the new breed of intelligent apps and how they use AI. In this article, we’ll see how AI technologies enhance developer toolkits and shape the future of app development, particularly through AI-enabled coding assistance and debugging.

The Dawn of Intelligent Apps: Revolutionizing Application Development

AI is more than just another addition to the development toolbox—it represents a revolution bringing unprecedented efficiency, innovation, and user engagement to our applications. 

What sets intelligent apps apart is how they can adapt and learn, helping us deliver unique, real-time user experiences.  

One example is an AI-driven health application that can track biometric data, predict potential health issues, provide personalized health advice, and even guide emergency services during a medical crisis. An AI-enabled language learning app that adapts to our learning speeds, recognizes our problem areas, and customizes its content to help us improve our language proficiencies is another example.  

Intelligent apps like these don’t just use data for recommendations—they can make critical decisions and provide sophisticated services that were previously impossible. 

For developers, intelligent apps mean we’re no longer just coding—we’re teaching applications how to think, adapt, and make decisions. AI is reshaping every step of the development journey, helping us to build more intuitive and user-focused products. 

Next, we’ll examine the impact of AI on application design and architecture and how AI-assisted “pair programming” changes software development.

info

Register for the FREE webinar on Intelligent Apps with Microsoft and Forrester on September 26 to explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries.

There will be a showcase of real-world use cases that demonstrate how AI can be seamlessly integrated into cloud-native environments driving tangible business value.

AI in Design and Architecture: Fueling Creativity and Efficiency

AI technologies help developers automate and streamline their processes and provide new ways to think about design.  

In traditional application development, the design and architecture phase can take significant time, effort, and expertise. For instance, adopting a monolithic architecture or a microservices-based design can significantly increase coding, testing, and debugging complexity. The app may even behave differently when hosted locally than in a deployed environment. It may take hours to track down an issue manually—time developers could spend elsewhere. 

Conversely, we can generate numerous design iterations in seconds with AI, each optimized to fulfill specific user needs and business goals. AI tools can quickly model various scenarios' impacts and software design patterns in the architectural design phase. It provides insights into performance and potential issues and suggests improvements. AI can also generate skeleton code, allowing developers to prototype different solutions or test specific components and technologies rapidly. By reviewing thousands of lines of code in seconds, AI allows us to focus on creating apps that serve users through feature development instead of searching for bugs.  

Integrating AI into the design and architecture phase can even extend our creative capabilities. By leveraging the power of generative AI—like large language models (LLMs)—and their broad knowledge and context going beyond software, we can better brainstorm apps and feature ideas. We can go beyond code and software architecture and get ideas about user experience, logo and branding, or marketing concepts. And as AI tools deliver insights and ideas we would have never considered; it encourages more innovation.

info

Watch Episode 01 of the Learn Live session to learn how to build an end-to-end intelligent app solution architecture.

The Power of AI-Assisted Pair Programming in Building Applications

AI-assisted pair programming is a collaborative coding approach where an AI system—like GitHub Copilot or TestPilot—assists developers during coding. It’s an increasingly common approach that significantly impacts developer productivity. In fact, GitHub Copilot is now behind an average of 46 percent of developers’ code and users are seeing 55 percent faster task completion on average. 

For new software developers, or those interested in learning new skills, AI-assisted pair programming are training wheels for coding. With the benefits of code snippet suggestions, developers can avoid struggling with beginner pitfalls like language syntax. Tools like ChatGPT can act as a personal, on-demand tutor—answering questions, generating code samples, and explaining complex code syntax and logic. These tools dramatically speed the learning process and help developers gain confidence in their coding abilities. 

Building applications with AI tools hastens development and provides more robust code. For example, GitHub Copilot helps us write new code accurately. It also quickly makes sense of existing code, suggests whole new lines or blocks of code, and even detects errors and proposes fixes. It’s like having a second coder working alongside us—one who never tires and is trained on billions of lines of code. 

However, it’s also important to acknowledge the limitations of AI. While generative AI-assistance technologies like GitHub Copilot are impressive, they’re still imperfect. They can sometimes produce erroneous, non-functional, or vulnerable code. They can also lack nuanced, domain-specific knowledge.  

So, although these tools are helpful, we can’t rely on them fully—these are programming aids rather than definitive solutions. AI-assisted pair programming is about augmenting the human developer’s skills, not replacing them.

Debugging and Improving Code With AI: Enhancing Application Quality

Identifying and correcting code errors is often time-consuming and challenging for developers. AI streamlines the debugging process by automatically detecting anomalies, suggesting fixes, and learning from previous bugs to prevent them from recurring. 

We can use generative AI to create unit tests from code snippets, from simple parameter checking and input validation to complex mocks of database and network services. Automated AI testing improves code coverage and robustness. It saves significant time compared to manually crafting unit tests. 

AI also revolutionizes code reviews by scanning and highlighting potential issues—like code patterns leading to a bug or a coding standards violation—and suggesting ways to simplify or refactor the code for readability and performance. This reduces human error and makes the code review process more efficient. Its benefits extend to small teams, where the availability of developers is crucial to deadlines, and large teams, where effective collaboration depends on readability and code consistency. AI further saves time by triaging issues so developers can prioritize the most mission-critical ones. 

Aside from improving code quality, AI is critical to understanding system behavior. Intelligent AI-assisted software development tools can monitor and audit system behavior, giving developers a better insight into how their code performs in different scenarios. AI can track patterns, identify inefficiencies, and provide alerts when they detect unusual behavior—enabling developers to enhance their code further. 

By predicting and identifying potential vulnerabilities, intelligent AI-assisted coding apps like Microsoft Security Copilot are pivotal in ensuring application functionality, reliability, and security. These AI tools are continually trained and upgraded with large codebases and user feedback that refines their responses. This ability to learn from past mistakes makes AI an incredibly powerful tool that will continue to expand its capabilities in the future.

info

Complete the AI Cloud Skills Challenge to build on you AI application development skills with paired programming using generative AI and GitHub Copilot.

AI and the Future of Application Development

The rapid advancements in AI and machine learning technologies point to an exciting future for application development. 

In “The Future of Applications,” software developer Mike Cann illustrates how LLMs might revolutionize application development. Cann that suggests fully-autonomous coding—AI-generated applications created entirely from prompts—may supersede traditional hand-crafted applications. Dynamically generated user interfaces could offer personalized and customizable experiences while we can design APIs for machine consumption and efficiency rather than human readability. Furthermore, we can store data in less structured, more adaptable, and flexible forms. 

AI is already making application development more accessible. With AI handling the complex, technical aspects of development, less-technical individuals can take a greater role in building applications. Consequently, AI can potentially democratize software engineering. 

Intelligent apps will continue to evolve, becoming more efficient, adaptable, and capable of learning from user interactions. Similarly, AI will inevitably become integral in every stage of app development.

Summary

AI has already created a new development paradigm—one in which machine intelligence and human creativity come together to produce innovative, efficient, and robust applications. AI already assists us at every stage of app development, from design and architecture to coding and debugging. 

Developers who embrace AI tools and integrate them into their processes can enhance their productivity, improve the quality of their applications, and expand the possibilities of their application development.  

The future of development is automated and AI-driven, and it’s here today. Getting started is easy—try an AI-powered development tool like GitHub Copilot to see how AI-assisted pair programming can enhance your productivity and code quality.

Exercise

- - +
Skip to main content

11 posts tagged with "github-actions"

View All Tags

· 9 min read
It's 30DaysOfIA

In the last article, we created an Intelligent App that leverages Azure AI Vision to analyze images and extract data. Develop an API to perform optical character recognition (OCR) on uploaded images and testing this API locally.

In this article, we will deploy the web API to the cloud platform using Azure Kubernetes Service.

What We'll Cover:

  • Understanding Azure AI Vision and Azure Kubernetes Service
  • Build a Python Web API to perform OCR
  • Test the API locally

image depicting an Intelligent App using AI

Jumpstart Your AI Journey: Building Your First Intelligent App with Azure AI and AKS (2)

In the previous article we explored the creation of an Intelligent App that leverages Azure AI Vision to analyze images and extract data. We learned how to build a Python Web API to perform OCR on uploaded images and subsequently test this API locally.

In this article we will use Azure Kubernetes Service (AKS) to develop, publish, and maintain our app in the cloud on Azure.

Let’s get started!

Prerequisites

To follow this tutorial, ensure you have completed Jumpstart Your AI Journey: Building Your First Intelligent App with Azure AI and AKS (1).

Pushing a Container Image to Azure Container Registry (ACR)

To start, open your CLI or terminal and type the following command:

az login

Follow the instructions displayed on your browser to enter your Azure credentials.

Once authenticated, you’ll initiate a secure connection between your local environment and Azure. This process grants you access to cloud services and resources.

Next, type the command below in your terminal to set up a new Azure Container Registry (ACR) to store your container images:

az acr create --resource-group computer-vision --name <name-of-azure-container-registry> --sku Basic

Remember to replace <name-of-azure-container-registry> with your container registry name. The name must be unique within Azure and comply with these rules.

The command above creates an Azure Container Registry (ACR) in the computer-vision resource group under the Basic SKU. This ACR is your secure and private repository for storing container images within Azure.

Next, log in to the registry with the following command:

az acr login -n <name-of-azure-container-registry>

The az acr login command above lets you securely authenticate and access the specified ACR without providing their Azure credentials each time.

Now, run the following command in your terminal. It will display the endpoint URL to log in to and interact with the ACR for pushing and pulling container images.

az acr show --name <name-of-azure-container-registry> --query loginServer --output table

This returns an endpoint URL as follows:

Result
----------------------------------
<name-of-azure-container-registry>.azurecr.io

Now, run the following command to show all container images, their repository, tags, and size:

docker images
REPOSITORYTAGIMAGE IDCREATEDSIZE
intelligent-applatesta7bf9f75361716 hours ago197MB

Tags are necessary to push Docker images to a remote registry like Azure Container Registry. They also let you differentiate between different versions of the same image and upload or download the one you want.

Run the following command to tag your Docker image:

docker tag intelligent-app <name-of-azure-container-registry>.azurecr.io/intelligent-app:v1

Then, run the docker images command again to check your tagged image:

docker images
REPOSITORYTAGIMAGE IDCREATEDSIZE
intelligent-applatestc52168039265About a minute ago197MB
<name-of-azure-container-registry>.azurecr.io/intelligent-appv1c52168039265About a minute ago197MB

Now run the following command so Docker can securely upload the image to your Azure Container Registry:

docker push <name-of-azure-container-registry>.azurecr.io/intelligent-app:v1

Once we've deployed the image to the container registry, AKS can access it during deployment.

info

Watch Episode 03 for the Learn Live session to experience a guided session on how to build, test and deploy an end-to-end intelligent app solution.

Deploying the Intelligent App on Azure Kubernetes Service (AKS)

Before we deploy our Intelligent App to AKS, we need to provision an AKS cluster and define Kubernetes manifests.

To provision an AKS cluster to host our application, we specify the desired configurations for the cluster, such as the number of nodes, node size, and networking options. But first, download and install the Kubernetes command-line tools (kubectl), a client credential plugin implementing Azure authentication:

az aks install-cli

If you’re using Linux, review this tutorial. Then run the following:

sudo az aks install-cli

Next, run the following command in your terminal to enable access to the networking-related resources and services provided by the `Microsoft.Network`` namespace in Azure:

az provider register --namespace Microsoft.Network

Now, we must create an AKS cluster. Run the following command to create an AKS cluster named aks-intelligent-app in the computer-vision resource group.

az aks create --resource-group computer-vision --name aks-intelligent-app --node-count 1 --generate-ssh-keys

The command above specifies the target resource group: computer-vision. The node pool is configured with one virtual machine (VM), and the secure shell (SSH) keys for secure node access are generated automatically.

Next, run the following command to update the AKS cluster you created by attaching it to your ACR. Doing so allows the AKS cluster to pull container images from the specified ACR when deploying workloads to the cluster.

az aks update -n aks-intelligent-app -g computer-vision --attach-acr <name-of-azure-container-registry>

Then, run the command below to configure kubectl to work with our AKS cluster in the computer-vision resource group.

az aks get-credentials --resource-group computer-vision --name aks-intelligent-app

The command above retrieves the necessary credentials and context information required for kubectl to communicate with the AKS cluster.

We still have to define the Kubernetes manifests written in YAML, describing the desired state of our application deployment, including containers, networking, and scaling rules. We’ll prepare these manifests, including Deployment and Service configurations, to define how our application should be deployed and exposed.

Create a folder named Deployment in the project root directory. Next, create two files in the deployment folder: deployment.yml and service.yml.

Add the following configuration to the deployment.yml file, replacing the <name-of-azure-container-registry> placeholder with your registry’s name:

apiVersion: apps/v1
kind: Deployment
metadata:
name: intelligent-app
spec:
replicas: 1
selector:
matchLabels:
app: intelligent-app
template:
metadata:
labels:
app: intelligent-app
spec:
nodeSelector:
kubernetes.io/os: linux
containers:
- name: intelligent-app
image: <name-of-azure-container-registry>.azurecr.io/intelligent-app:v1
resources:
limits:
memory: 512Mi
cpu: "1"
requests:
memory: 256Mi
cpu: "0.2"
ports:
- containerPort: 5000
env:
- name: FLASK_DEBUG
value: "1"
- name: VISION_KEY
value: <THE-KEY-1-VALUE-FROM-YOUR-AZURE-AI-SERVICE>
- name: VISION_ENDPOINT
value: <THE-ENDPOINT-VALUE-FROM-YOUR-AZURE-AI>

Additionally, edit the VISION_KEY and VISION_ENDPOINT environment variables above according to the API key and endpoint of your Azure AI Services instance.

Then, add the following configuration to the service.yml file:

apiVersion: v1
kind: Service
metadata:
name: intelligent-app-service
spec:
type: LoadBalancer
ports:
- protocol: TCP
port: 80
targetPort: 5000
name: port5000
selector:
app: intelligent-app

Now, we’ll deploy our application using kubectl. Applying the Kubernetes manifests creates the necessary resources and deploys our containerized application to the AKS cluster.

First, change the terminal to the deployment folder:

cd Deployment 

Then, run the following command to create or update Kubernetes resources defined in the deployment.yml file:

kubectl apply -f deployment.yml

Create a Kubernetes Service resource in a Kubernetes cluster based on the configuration defined in the service.yml file using the code below:

kubectl apply -f service.yml

Once you’ve applied the resource definition and the service configuration contained in the deployment.yml and the service.yml files, open the aks-intelligent-app Kubernetes Service in the Azure Portal, select Workloads under Kubernetes resources on the sidebar, and find the deployment named intelligent-app. It must have the status “Ready 1/1”. If you encounter an issue with this status, check out these troubleshooting resources.

image of selecting the deployment in Azure Portal

Testing the Intelligent App on AKS

To test the app on AKS, first, run the command below:

kubectl get services

This command lists the Services and their corresponding details, including the Service name, cluster IP address, external IP, and ports.

NAMETYPECLUSTER-IPEXTERNAL-IPPORT(S)AGE
intelligent-app-serviceLoadBalancer10.0.77.6020.121.76.15380:30936/TCP47s
kubernetesClusterIP10.0.0.1<none>443/TCP14m

The output above shows a Kubernetes Service named intelligent-app-service with a type set to LoadBalancer. It’s reachable from within the cluster using the cluster IP 10.0.77.60 and accessible externally via the external IP 20.121.76.153 on port 80 (mapped to port 30936).

Note: Your set of IP addresses will differ. Remember to use your unique external IP address when testing with Postman.

To test the deployed app, go to Postman, replace the URL with the external IP of the Kubernetes Service you just deployed, and click Send:

mage of sending the app in Postman

As we can see, our Intelligent App has successfully deployed to AKS and is functioning on the cloud as expected.

Exercise

Next Steps

In this two-part article, we explored the creation of an Intelligent App that leverages Azure AI Vision to analyze images and extract data. We learned how to build a Python Web API to perform OCR on uploaded images and subsequently deploy this API via Azure Kubernetes Service.

Besides OCR and Image Analysis, you can continue exploring Azure’s vast array of services and experiment further with Azure AI and AKS by applying various practical uses to your Intelligent Apps, including natural language processing, speech recognition and synthesis, sentiment analysis for customer feedback, and automated content moderation.

+ + \ No newline at end of file diff --git a/30daysofIA/tags/github-actions/page/5/index.html b/30daysofIA/tags/github-actions/page/5/index.html index c60f06ce71..cdca076797 100644 --- a/30daysofIA/tags/github-actions/page/5/index.html +++ b/30daysofIA/tags/github-actions/page/5/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "github-actions" | Build Intelligent Apps On Azure +11 posts tagged with "github-actions" | Build Intelligent Apps On Azure @@ -14,13 +14,13 @@ - - + +
-

9 posts tagged with "github-actions"

View All Tags

· 10 min read
It's 30DaysOfIA

Explore real-world examples of how Intelligent Apps revolutionize logistics, retail, finance, manufacturing, tech, and medicine.

What We'll Cover:

  • Real-world businesses revolutionizing operations with Intelligent Apps
  • High level architecture for industry scenarios
  • Implementation architecture overview across industries

A decorative header image for the blog

Real-world Success Stories

According to IBM’s 2022 Global AI Adoption Index, 35% of surveyed companies already use AI in their business, and 42% are exploring the idea. Organizations lagging behind the AI curve will quickly fall behind early adopters, who are already enjoying its benefits and boosting their investment in this technology.

In the first part of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” we explored how organizations can use AI-powered apps, or intelligent apps, to transform their operations, decision-making, and customer experiences, leaping ahead of the competition.

Intelligent apps significantly improve operational efficiency and customer engagement across industries like logistics, retail, finance, manufacturing, tech, and healthcare. In this article, we’ll spotlight real-world success stories of businesses revolutionizing operations with intelligent apps using Microsoft Azure’s app, data, and AI services.

info

Register for the Learn Live on September 21 for a live guided experience with SMEs on how to build end-to-end solution architecture for real world implementation of Intelligent Apps.

Streamlining Operations: Intelligent Apps in the Airline Industry

Aerospace pioneer Airbus leveraged intelligent apps to streamline its operations and innovate its services with two ground-breaking modernizations that use Azure apps, data, and AI services to reimagine pilot training and predict aircraft maintenance issues. 

Aviation technology’s rapid evolution compelled Airbus to reconceive their approach to training pilots. The aircraft manufacturer created a pilot training chatbot by combining Azure AI Services with Speech to Text and Text to Speech. The bot helps trainee pilots navigate over 6,000 pages of technical information. Pilots can ask questions verbally for quick answers, aiding their understanding of dense information and speeding up training. And when Airbus needed to accommodate its military consumers, deploying its Kubernetes cluster to a disconnected, “air-gapped” environment met the strict security requirements of military aircraft training.

A chatbot could look like the following diagram as Azure services work together to create a conversational experience. Its components could include security and governance, bot logic and user experience (UX), bot cognition and intelligence, data extract, transfer, and load (ETL) operations, quality assurance and enhancements, logging, and monitoring and reporting.

A diagram of a conversational experience enabled by Azure services

Airbus also used AI to improve its aircraft maintenance and safety. They deployed AI Anomaly Detector to gather and analyze telemetry data from multiple flights, enabling more profound insights into wear and tear on military aircraft operating in harsh conditions. This AI application helps Airbus better understand each airplane’s health. Airbus can predict and fix potential problems before they occur, improving the aircraft’s safety and operational readiness.

AI and Azure are changing how organizations like Airbus operate, even in strict, highly regulated markets with stringent security requirements. And by streamlining pilot training and staying ahead of aircraft maintenance, Airbus helps its military and commercial clients reliably deliver their goods across the globe, maintaining their critical supply chains.

Boosting Customer Service: Intelligent Apps in the Retail Industry

The retail industry has been among the first to adopt and benefit from intelligent apps. For example, the leading American used car retailer, CarMax, uses intelligent apps to transform the car shopping experience.

Before embracing AI, CarMax faced a significant challenge: Manually creating text summaries was time-consuming and labor-intensive. CarMax decided to leverage Azure OpenAI Service to automatically generate car research pages, offering customers valuable insights while enhancing the website’s search engine rankings.

This AI-driven approach has significantly streamlined the process, achieving in a few months what would have taken years manually. Also, the AI system condenses thousands of customer reviews into a few readable sentences, offering potential buyers a quick overview of others’ experiences.

CarMax has improved their customer experience while boosting operational efficiency through AI adoption. CarMax’s editorial staff focuses on creating strategic, longer-form content with the time saved, enhancing productivity, and driving more website traffic.

CarMax’s success story serves as a testament to the potential of intelligent apps in the retail industry.

Enhancing Cybersecurity: Intelligent Apps in Finance

Intelligent apps also impact the highly regulated financial services sector. Swift, a leading infrastructure provider for financial messaging services, uses intelligent apps to enhance decision-making and combat financial crime.

The massive growth of cross-border transactions and instant payment networks led to a surge in financial fraud that can cost the industry hundreds of billions of dollars every year. So, Swift and Microsoft partnered to build an anomaly detection model using federated learning techniques with Azure Machine Learning and Azure confidential computing.

Microsoft Purview helped govern data, as the federated learning method partially trained each model in a silo before aggregating them. This approach enabled Swift and Microsoft to develop a highly accurate model without copying or moving data from Swift members’ secure locations, ensuring the highest level of security and privacy.

Financial organizations can now deploy this trained ML model to run a real-time fraud detection system, lowering the cost of financial fraud.

Organizations can deploy a fraud detection system like the following example on Microsoft Azure. The system ingests data from streams or storage, runs analytics, and uses ML to detect fraud and store results. It then alerts users and other systems to the security issue.

A diagram of an example fraud detection system

Swift provides the innovative model to its banking partners, who can share data to further train the model. Azure confidential computing securely runs new models on current data to accumulate insights, generating an ongoing learning cycle. This iterative approach strengthens the global detection of fraudulent financial transactions through an increasingly faster and more accurate model. 

The collaboration between Swift and Microsoft demonstrates how combining various Azure services creates Intelligent Apps to enhance decision-making in finance. These apps solve real problems at an unprecedented global scale. 

Enhancing Efficiency: Intelligent Apps in Manufacturing

3M, known for Post-it Notes and other innovative products, wielded the power of Intelligent Apps to standardize, automate, and accelerate its sales forecasting. The company needed a unified, automated approach to replace its multiple manual methods.

It adopted Azure Machine Learning and other Azure AI Services to develop, train, and implement 1,500 custom models for each region and division, replacing manual methods. This solution delivers timely finance, sales, and marketing insights while freeing teams from time-consuming manual data manipulation tasks.

3M used Azure Machine Learning, automated machine learning (AutoML), and the Many Models Solution Accelerator to train and score numerous machine learning models in parallel. This approach significantly shortened the company’s development cycle time.

Then, 3M integrated Microsoft Power BI into their solution to compile and visualize results more effectively. Executives now have an unbiased and unfiltered view of data for better decision-making. With its improved ability to forecast sales, 3M can focus on manufacturing the products it needs efficiently.

3M’s success demonstrates how combining various Azure services can enhance efficiency in the manufacturing industry. These apps improve operational processes while enabling employees to focus on higher-level tasks. Strategic decisions are more targeted, and business conversations are more meaningful.

Driving Innovation: Intelligent Apps in the Technology Industry

As expected, the tech industry is at the forefront of leveraging Intelligent Apps to drive innovation. Elastic saw an opportunity to use intelligent apps to optimize its search services and meet customer needs more effectively. The company built a managed, secure, and scalable solution for its customers, Elastic Cloud, by leveraging Microsoft Azure.

Elastic Cloud uses Azure Machine Learning and other Azure AI Services to automate many basic management tasks, such as backing up data, upgrading, and scaling deployments. This approach enables Elastic’s customers to focus less on managing the software and more on serving their own clients. Customers can quickly deploy and manage Elastic Cloud on Kubernetes (ECK) using Azure Kubernetes Service.

As a result, Elastic’s cloud business is growing twice as fast as its on-premises solutions. Enterprise search users have reported significant improvements in system availability and employee satisfaction.

Additionally, Elastic incorporated Azure OpenAI Service into its Elasticsearch Relevance Engine (ESRE), providing more relevant answers to search queries. ESRE enables text, vector, and hybrid search, offering a more effective and accurate response to queries.

Elastic’s success story demonstrates how combining various Azure services can create intelligent Apps to drive innovation in the tech industry. These apps help improve operational processes and deliver better results quickly.

Revolutionizing Healthcare: Intelligent Apps in Medicine

Intelligent apps also drive transformative changes in the healthcare sector. Cambridgeshire and Peterborough NHS Foundation Trust, a healthcare provider in the United Kingdom, gradually shifted their on-premises systems to the cloud using Azure, improving flexibility, and lowering costs. They needed a solution for their electronic patient record (EPR) system that integrates 23 years of data across multiple systems and formats.

The Trust used Microsoft Azure and Azure Cognitive Search, including semantic search for understanding intent and contexts and cognitive capabilities for extracting insights from data, to standardize and automate data management. This approach made legacy, archive, and live data more discoverable, saving clinicians’ time. The Trust’s solution searches through millions of clinical documents in seconds, even recognizing handwritten notes without needing a separate optical character recognition (OCR) solution to extract text from images (Azure Cognitive Search automatically does it). Clinicians can now uncover previously inaccessible insights, enabling them to offer more personalized care.

The Trust also used Integration Services tools such as Azure Logic Apps to create workflows without writing code and Event Grid to connect Azure and third-party services through a publisher-subscriber model. They switched on a live feed from their new EPR system to Azure to feed real-time information into other applications. Researchers can now access anonymized data to fuel medical innovations beyond the NHS.

The healthcare provider also invested heavily in Power BI’s data visualization and analytical functions to sift through massive amounts of information. This level of data accessibility was previously impossible with their on-premises system. The new insights have significantly improved clinicians’ efficiency for better patient care.

Data accessibility and security are critical in the healthcare industry. Intelligent Apps empower organizations like the Trust to revolutionize medical care. AI capabilities helped the Trust improve its operational efficiency and user experiences, setting a new standard for healthcare in the digital age.

Summary

AI is driving transformation and real-world successes across industries. Intelligent apps are reshaping our world by streamlining operations in manufacturing and logistics, enhancing customer experience in the retail sector, improving decision-making in finance, driving innovation in tech, and revolutionizing healthcare.

As organizations adopt AI, they’re innovating in ways that were once too difficult, expensive, or outright impossible. Companies that wait to embrace intelligent apps risk the chance of falling behind. By embracing intelligent apps and leveraging the capabilities of Azure, you can harness the transformative potential of AI to remain on the cutting edge of technology.

Exercise

- - +
Skip to main content

11 posts tagged with "github-actions"

View All Tags

· 10 min read
It's 30DaysOfIA

Explore how digital natives and Independent Software Vendors (ISVs) can transform their business operations and generate higher value for customers with generative AI and intelligent apps.

What We'll Cover:

  • Potential of Generative AI in application development
  • Challenges with Generative AI
  • Integrating Generative AI with business operations

image of generative AI and intelligent apps used in business

Unleashing Business Transformation: How Digital Natives Can Leverage Generative AI

Generative AI is no longer a technological flight of fancy. It’s here, and it’s rewriting the rules of business innovation. 

This type of AI leverages machine learning algorithms to create new data with similar characteristics to a given dataset. It goes beyond text generation using generative pretrained transformer (GPT) models, covering an array of applications that span from image synthesis to translations and more. It also powers a new class of intelligent applications, which combine AI capabilities with traditional software functions for enhanced user experiences and advanced business solutions. 

Generative AI is transforming how digital natives and independent software vendors (ISVs) operate. Generative AI offers new possibilities for creating unique business value, enhancing customer experiences, and driving growth. 

It plays a critical role in applications, improving functionality, aiding decision-making, and automating complex tasks to deliver dynamic, uniquely personalized user experiences. Generative AI also enables and automates data-driven outcomes, functions, and features.

Integrating AI capabilities into customer-facing apps and turning them into intelligent apps benefits both the customer and the business, as we discussed in Demystifying Intelligent Applications: Leveraging AI in App Development. You get faster product development workflows and better business intelligence for sales and marketing, while your users get more natural and context-aware features and interactions. 

That said, to realize generative AI’s potential, you need some strategy and knowledge of how and where it fits into your current business and operations. We’ll examine this technology’s potential to transform business operations, its risks, and rewards, and how to integrate it into your existing operations. 

Unpacking the Potential of Generative AI

Generative AI is creating new opportunities in the digital playing field. It empowers businesses to generate insights, design products, and automate processes in ways and at speeds that were previously unimaginable. You can now predict customer behavior more accurately, offer highly personalized user experiences, and prototype innovative ideas rapidly. 

For example, Microsoft used generative AI to reinvent web search in the new AI-powered Bing and Edge. The search experience has evolved from a solitary user flow of typing in keywords and scrolling through results. It’s now a conversational experience where Bing uses context to offer better quality and more personalized search queries and results. 

Similarly, Microsoft harnessed generative AI to transform its productivity suite into intelligent apps. Through Copilot for Microsoft 365, PowerPoint now empowers users to tell their stories by using DALL-E to generate images effortlessly. It also helps rephrase text-heavy slides to be more straightforward. Outlook now assists users to compose more effective emails, enabling better communication.

These are just some examples of how AI powers day-to-day work and operations. 

Businesses that ignore generative AI miss out on untapped market opportunities, letting competitors gain an edge in the digital race. By seeking ways to rethink assumptions and reinvent existing approaches, you can effectively use generative AI to create new opportunities, open products to new use cases and markets, unlock new growth and customer satisfaction levels, and increase your revenue.

info

Register for the intelligent apps webinar on Driving Business Value by Modernizing with Cloud-Native & AI with Microsoft and Forrester on September 26.

Explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries. 

The Crucial Role of Generative AI in Business Operations

Generative AI can be a powerful catalyst for business transformation. Let’s quickly tour some ways it can redefine business operations. 

Product development is traditionally a long and painstaking process. Now, tools like GitHub Copilot and TestPilot use generative AI to make coding and testing software easier, faster, and more robust. 

Similarly, machine learning as a service technology, such as Azure Machine Learning, democratizes the availability of machine learning. It helps collect, clean, and train models across datasets, making engineering and analyzing data more efficient and accessible.  

Generative AI also simplifies the process of creating intelligent apps. Previously, simulating thousands of prototypes, predicting their performance, and narrowing down to the most viable options could take weeks, months, or even years of iteration and data analysis. With generative AI, this process can be as quick as days, hours, or perhaps minutes. 

Customer experience is also ripe for transformation. Personalized recommendations drive engagement and sales, and generative AI can scale that personalization to every customer interaction using comprehensive and continually updated data. For instance, Microsoft leverages AI to enable personalization in Dynamics 365 Customer Insights. Using this tool, you can ask questions about your customers in natural language and gain valuable and actionable insights into your customer base. 

Lastly, marketing and sales can benefit from generative AI. For example, identifying current and future key markets and emerging trends helps companies stay ahead of the curve. Tools like Copilot in Power BI help you quickly understand and uncover insights about your business, enabling your team to craft targeted campaigns, create engaging content, and optimize your business strategy. 

Leveraging this full generative AI potential requires the right tools. GPT-4, for instance, can be instrumental in realizing effective business transformation. It excels in understanding context, generating human-like text, and learning from minimal data input, making it a formidable tool in any digital native's arsenal. Platforms like Azure OpenAI Service bring GPT-4’s power to your fingertips.

However, as with any powerful technology, it’s unwise to treat generative AI as a perfect solution. You must know the challenges and risks of not leveraging it effectively, including technical and human-factor considerations. 

Without generative AI, your company risks missing the opportunity to please customers via personalization, as discussed previously. You’ll miss sales opportunities via business intelligence insights, limiting your ability to grow revenue and keep up with the competition. 

One challenge of leveraging generative AI’s full potential is finding the right tools. Fortunately, you can implement generative AI in your organization without investing in infrastructure, computational power, and expertise. Platforms like Azure OpenAI Service and Azure AI Services make AI easily accessible. They also work with Azure compute and data services like Azure Functions and Azure Kubernetes Service (AKS), enabling you to build intelligent apps easily and quickly. 

However, generative AI can’t do everything. It may need human expertise and intervention to use it fully. 

For example, generative AI can create unique data. However, the data may have limited diversity and complexity. The AI may need help recognizing additional considerations or requirements specific to a problem. It may also lack context due to various factors, such as the information’s freshness. Also, since it’s an emerging field that’s potentially worth $4.4 trillion, we’re still learning about the nuances and potential regulatory impact of generative AI. 

Perhaps the most crucial and challenging aspect of adopting generative AI is an organization’s required cultural shift. Your teams must learn how to use AI as your company fosters an understanding of AI’s potential and limitations. 

Begin integrating AI literacy into your company’s DNA. Knowledge of its potential and the readily available tools can inspire your teams to incorporate generative AI into their daily workflows and business operations quickly, improve productivity, and deliver superior customer experiences. 

No company wants to be outpaced by its competitors. You can balance generative AI’s potential with your business operations by staying aware of and adapting to the dynamic AI landscape.

Achieving Harmony: Integrating Generative AI with Existing Business Operations

Integrating generative AI into existing business operations doesn’t have to be disruptive. With the correct strategy, you can do so seamlessly. Your goal should be to augment and enhance your operations, not disrupt and replace. 

For example, Microsoft Cloud technologies leveraged AI to relieve healthcare organizations’ challenges, such as talent shortage and workforce burnout. Providers automated their workflows, invested in specific applications and connectors, and enhanced support for data and operations. 

Azure AI Services, such as text analytics for health, enable healthcare organizations to understand their largely unstructured and untapped medical data. Project Health Insights leans on AI to help healthcare professionals gain actionable insights and inferences from patient data. 

Azure Health Bot, an Intelligent App, empowers organizations to build and deploy legally compliant, powerful conversational healthcare experiences to patients. 

A successful AI integration like this begins with understanding. Knowing what generative AI can do and setting realistic expectations can help you identify where and how AI can best serve your operations. 

Next, you can foster a culture of AI literacy across teams, speeding up adoption by helping them understand how it can aid their work. The more familiar your team is with AI and its possibilities, the smoother the transition. Training and education programs play a pivotal role here. 

Finally, integration requires a systematic approach. It’s not about overhauling everything at once — it's about identifying areas where AI can add the most value, then gradually experimenting and scaling its use. Successful integration stories often feature a phased approach, starting small and growing steadily. 

As we move toward a future where generative AI is a staple in business operations, remember that every journey is unique. Although there’s no one-size-fits-all solution, a strategic approach and a keen understanding of your business needs will help you navigate this new landscape successfully.

Conclusion

Generative AI offers plenty of opportunities for businesses that embrace it. It’s fueling the next generation of intelligent applications, transforming everything from product development to customer engagement to marketing. This technology is the key to unlocking unprecedented growth and innovation. 

While the journey may have challenges, the potential rewards make generative AI worth exploring. As generative AI advances, Azure AI’s broad range of tools and services will help you navigate this fascinating field.

Exercise

  • Watch Episode 02 of the serverless edition Learn Live session to learn how to build, test and deploy an end-to-end intelligent app solution.
  • Complete the Cloud Skills Challenge to build on your AI skills.
  • Register for Ask the Expert: Azure Functions on September 26 for a live Q&A with the Product Team on how to build AI apps using serverless computing.
+ + \ No newline at end of file diff --git a/30daysofIA/tags/github-actions/page/6/index.html b/30daysofIA/tags/github-actions/page/6/index.html index e16aacabe3..09317e839e 100644 --- a/30daysofIA/tags/github-actions/page/6/index.html +++ b/30daysofIA/tags/github-actions/page/6/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "github-actions" | Build Intelligent Apps On Azure +11 posts tagged with "github-actions" | Build Intelligent Apps On Azure @@ -14,13 +14,13 @@ - - + +
-

9 posts tagged with "github-actions"

View All Tags

· 8 min read
It's 30DaysOfIA

Let’s start with understanding the power of intelligent applications.

What We'll Cover:

  • What are intelligent applications?
  • Categories of Intelligent Apps
  • Breaking down the role of AI in intelligent apps

A decorative header image for the blog

Demystifying Intelligent Applications: Leveraging AI in App Development

Until recent years, when you heard the term “artificial intelligence” (AI), you might have pictured autonomous robots from sci-fi films. Now, perhaps your mind jumps to big data and machine learning (ML). Maybe you’re even thinking about the latest ChatGPT plugins and transformer architectures. Today, AI is more than these imaginations: it’s a reality increasingly woven into our day-to-day routines through intelligent applications.

Intelligent apps combine cloud-scale compute, data, and predictive or generative AI. These applications help you automate tasks and make data-driven decisions, aiding in leveraging business intelligence to gain meaningful, practical outcomes. 

For developers, the AI landscape isn’t an abstract complexity. It’s a real, tangible opportunity for evolving traditional applications, making them “intelligent,” and delivering more value to users. And with tools like Azure Kubernetes Service, Azure Cosmos DB, and Azure AI Services, stepping into the world of intelligent apps isn’t a steep climb but a steady trek across a manageable learning curve.

The Three Categories of Intelligent Applications

Before we start building, it’s critical to understand what makes an app “intelligent.” It’s not a universal definition so much as an identifier for apps that fall into one or more of the three primary categories: 

  • Outcome-based — This designation represents the broadest category of intelligent apps, whose principal value for users is the intelligent outcomes they provide. These apps process data to provide insightful results that enhance decision-making or help with tasks like trend prediction, resource management, and customer relationship management.

  • Functionality-based — This type of intelligent app takes AI integration a degree further, integrating AI as a function of the app to create or process its outputs, enhancing the app’s overall interactivity and usefulness.

  • Feature-based — This is the narrowest category, in which the app’s AI/ML component is one of its core features and primary selling points

Let’s dive more deeply into each category.

Outcome-Based Apps: Intelligent Outcomes Drive Success

Outcome-based applications are all about the results provided to the user. They function beyond merely processing data to offer insightful outcomes that drive success.

Think of a personal fitness tracker app that uses AI to analyze the user’s activity and provides actionable recommendations for achieving a goal. This type of app not only tracks your steps, sleep patterns, and heart rate but uses that data to provide you with personalized health advice. It helps you make informed lifestyle choices — the “intelligent” outcome.

Another example is a social media marketing app that uses AI for time-saving data analysis, task automation, and trend prediction. AI-based sentiment analysis can gauge customer perception while natural language processing (NLP) accurately interprets customer inquiries. Task automation can schedule posts and automate responses to common customer queries, and trend forecasting helps model and predict customer behavior to further personalize targeted promotional efforts. Building this type of intelligence into your apps provides cost-effective, actionable insights that guide sales and marketing strategies.

Functionality-Based Apps: AI-Driven Features Enhance User Experiences

As the name suggests, functionality-based applications integrate AI into how the app functions, but the end user drives the action and outcome. In this category of apps, AI takes a more active role in the user’s interaction by allowing them to use AI-created or processed output. These apps leverage AI technologies like Azure AI Services for NLP, image recognition, and pattern recognition to improve user experience.

Consider a music app that generates personalized playlists based on your listening habits or your smartphone’s camera app that uses AI to recognize faces and optimize photo settings. Even more advanced are the language translation apps that translate text from one language to another and can even understand and maintain the context and subtleties of the conversation. They use NLP to accomplish these feats.

Similarly, consider the latest e-commerce apps, whose AI-driven functionalities enable customers to try on clothes virtually, significantly enhancing the online shopping experience.

Feature-Based Apps: Integrating Advanced AI/ML Components

Finally, there are feature-based intelligent apps. These apps incorporate advanced AI/ML components like neural networks, internal large language models (LLMs), and sophisticated algorithms as core features for the user. In this type of application, AI is the primary feature for users to engage with and the primary selling point for the app.

Some examples of feature-based applications are chatbots and virtual agents built using Azure Bot Service. These robo-representatives use AI to provide more natural and human-like conversation, balancing superior efficiency with a friendlier, more personalized user experience. The new Bing is a prime example of this implementation, integrating AI to give users a knowledgeable copilot for web searches.

Similarly, OpenAI’s ChatGPT allows users access to its state-of-the-art language models available as Microsoft’s Azure OpenAI Service. It leverages the power of the highly sophisticated GPT-3 LLM to perform tasks like drafting emails, writing code, answering general knowledge questions, translating between languages, and tutoring in numerous subjects.

These apps don’t just use AI to provide outputs or improve user experiences: They push the boundaries of what’s possible in app development by integrating AI/ML components.

info

Register for the live Q&A Ask The Expert session on September 26 with the Azure Functions product engineering team.

Breaking Down the Role of AI in Intelligent Applications

Intelligent apps reflect the merging of traditional software design with the dynamic capabilities of AI. Let’s explore how AI integrates and amplifies these applications.

At its core, AI augments applications with the capacity to self-learn and self-improve. It allows you to create applications that predict, react, and adapt to a constantly changing environment. In the power grid management system for a major city, an AI-infused application can forecast power usage trends, react to anomalies, and adapt to sudden demand surges, ensuring optimal efficiency and preventing blackouts.

But AI’s role doesn’t end at prediction or decision-making: It also automates tasks. In enterprise solutions, it has moved beyond simple automation to encompass intelligent process automation (IPA). In this arena, AI algorithms understand, learn, and then automate entire digital workflows. For developers, this is a fertile ground for creating applications that can dramatically improve the operational efficiency of businesses.

Even more, AI substantially aids in development tasks. For example, there’s GitHub Copilot, an AI-powered code assistant that offers suggestions based not only on syntax but on development best practices and the intent of the code. AI has evolved from a tool you use to build apps to a partner that helps in creating them. 

Using AI to build intelligent apps isn’t about throwing out what’s worked before. It’s about enhancing the components that have the most impact and achieve the best results. Incorporating AI in apps elevates their predictive abilities, streamlines complex tasks, and even aids in the development and creation process. When you build intelligent apps, you don’t just address current problems. You anticipate future challenges and craft solutions that adapt over time.

Summary

As developers the power of AI is in our hands, and it’s our responsibility to harness its full potential. Each category tells the story of AI's transformative potential, from applications that deliver intelligent outcomes to those with AI capabilities to enrich the user experience and those pushing boundaries with advanced AI/ML technology.

Accessible and robust platforms like Azure AI can simplify the process of developing Intelligent Applications. Whether you’re refining an existing app or building a new one, just entering the world of AI, or already familiar with the landscape, take a moment to appreciate the potential that AI integration offers. You’re embracing this exciting frontier and shaping the future of app development. 

Exercise

  • Complete the Cloud Skills Challenge to build on your apps and AI skills.
  • Watch Episode 01 of the Serverless Edition Learn Live session to learn how to build an end-to-end intelligent app solution.
- - +
Skip to main content

11 posts tagged with "github-actions"

View All Tags

· 9 min read
It's 30DaysOfIA

In the part of our series on intelligent apps, we’ll explore how AI is transforming application development, from design and architecture to building.

What We'll Cover:

  • Revolutionizing application development using AI
  • Infusing AI in application design architecture
  • AI Assisted Pair Programming
  • Future of App Development

A decorative image of application development with AI

Reimagining Application Development With AI: A New Paradigm

Artificial intelligence (AI) is impacting every industry. AI-powered development tools offer new functionalities and assist software developers throughout the development process, revolutionizing app development. From initial design and architecture to coding and debugging, AI reshapes how we approach application conception, creation, and iteration.

In part one of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” we explored the new breed of intelligent apps and how they use AI. In this article, we’ll see how AI technologies enhance developer toolkits and shape the future of app development, particularly through AI-enabled coding assistance and debugging.

The Dawn of Intelligent Apps: Revolutionizing Application Development

AI is more than just another addition to the development toolbox—it represents a revolution bringing unprecedented efficiency, innovation, and user engagement to our applications. 

What sets intelligent apps apart is how they can adapt and learn, helping us deliver unique, real-time user experiences.  

One example is an AI-driven health application that can track biometric data, predict potential health issues, provide personalized health advice, and even guide emergency services during a medical crisis. An AI-enabled language learning app that adapts to our learning speeds, recognizes our problem areas, and customizes its content to help us improve our language proficiencies is another example.  

Intelligent apps like these don’t just use data for recommendations—they can make critical decisions and provide sophisticated services that were previously impossible. 

For developers, intelligent apps mean we’re no longer just coding—we’re teaching applications how to think, adapt, and make decisions. AI is reshaping every step of the development journey, helping us to build more intuitive and user-focused products. 

Next, we’ll examine the impact of AI on application design and architecture and how AI-assisted “pair programming” changes software development.

info

Register for the FREE webinar on Intelligent Apps with Microsoft and Forrester on September 26 to explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries.

There will be a showcase of real-world use cases that demonstrate how AI can be seamlessly integrated into cloud-native environments driving tangible business value.

AI in Design and Architecture: Fueling Creativity and Efficiency

AI technologies help developers automate and streamline their processes and provide new ways to think about design.  

In traditional application development, the design and architecture phase can take significant time, effort, and expertise. For instance, adopting a monolithic architecture or a microservices-based design can significantly increase coding, testing, and debugging complexity. The app may even behave differently when hosted locally than in a deployed environment. It may take hours to track down an issue manually—time developers could spend elsewhere. 

Conversely, we can generate numerous design iterations in seconds with AI, each optimized to fulfill specific user needs and business goals. AI tools can quickly model various scenarios' impacts and software design patterns in the architectural design phase. It provides insights into performance and potential issues and suggests improvements. AI can also generate skeleton code, allowing developers to prototype different solutions or test specific components and technologies rapidly. By reviewing thousands of lines of code in seconds, AI allows us to focus on creating apps that serve users through feature development instead of searching for bugs.  

Integrating AI into the design and architecture phase can even extend our creative capabilities. By leveraging the power of generative AI—like large language models (LLMs)—and their broad knowledge and context going beyond software, we can better brainstorm apps and feature ideas. We can go beyond code and software architecture and get ideas about user experience, logo and branding, or marketing concepts. And as AI tools deliver insights and ideas we would have never considered; it encourages more innovation.

info

Watch Episode 01 of the Learn Live session to learn how to build an end-to-end intelligent app solution architecture.

The Power of AI-Assisted Pair Programming in Building Applications

AI-assisted pair programming is a collaborative coding approach where an AI system—like GitHub Copilot or TestPilot—assists developers during coding. It’s an increasingly common approach that significantly impacts developer productivity. In fact, GitHub Copilot is now behind an average of 46 percent of developers’ code and users are seeing 55 percent faster task completion on average. 

For new software developers, or those interested in learning new skills, AI-assisted pair programming are training wheels for coding. With the benefits of code snippet suggestions, developers can avoid struggling with beginner pitfalls like language syntax. Tools like ChatGPT can act as a personal, on-demand tutor—answering questions, generating code samples, and explaining complex code syntax and logic. These tools dramatically speed the learning process and help developers gain confidence in their coding abilities. 

Building applications with AI tools hastens development and provides more robust code. For example, GitHub Copilot helps us write new code accurately. It also quickly makes sense of existing code, suggests whole new lines or blocks of code, and even detects errors and proposes fixes. It’s like having a second coder working alongside us—one who never tires and is trained on billions of lines of code. 

However, it’s also important to acknowledge the limitations of AI. While generative AI-assistance technologies like GitHub Copilot are impressive, they’re still imperfect. They can sometimes produce erroneous, non-functional, or vulnerable code. They can also lack nuanced, domain-specific knowledge.  

So, although these tools are helpful, we can’t rely on them fully—these are programming aids rather than definitive solutions. AI-assisted pair programming is about augmenting the human developer’s skills, not replacing them.

Debugging and Improving Code With AI: Enhancing Application Quality

Identifying and correcting code errors is often time-consuming and challenging for developers. AI streamlines the debugging process by automatically detecting anomalies, suggesting fixes, and learning from previous bugs to prevent them from recurring. 

We can use generative AI to create unit tests from code snippets, from simple parameter checking and input validation to complex mocks of database and network services. Automated AI testing improves code coverage and robustness. It saves significant time compared to manually crafting unit tests. 

AI also revolutionizes code reviews by scanning and highlighting potential issues—like code patterns leading to a bug or a coding standards violation—and suggesting ways to simplify or refactor the code for readability and performance. This reduces human error and makes the code review process more efficient. Its benefits extend to small teams, where the availability of developers is crucial to deadlines, and large teams, where effective collaboration depends on readability and code consistency. AI further saves time by triaging issues so developers can prioritize the most mission-critical ones. 

Aside from improving code quality, AI is critical to understanding system behavior. Intelligent AI-assisted software development tools can monitor and audit system behavior, giving developers a better insight into how their code performs in different scenarios. AI can track patterns, identify inefficiencies, and provide alerts when they detect unusual behavior—enabling developers to enhance their code further. 

By predicting and identifying potential vulnerabilities, intelligent AI-assisted coding apps like Microsoft Security Copilot are pivotal in ensuring application functionality, reliability, and security. These AI tools are continually trained and upgraded with large codebases and user feedback that refines their responses. This ability to learn from past mistakes makes AI an incredibly powerful tool that will continue to expand its capabilities in the future.

info

Complete the AI Cloud Skills Challenge to build on you AI application development skills with paired programming using generative AI and GitHub Copilot.

AI and the Future of Application Development

The rapid advancements in AI and machine learning technologies point to an exciting future for application development. 

In “The Future of Applications,” software developer Mike Cann illustrates how LLMs might revolutionize application development. Cann that suggests fully-autonomous coding—AI-generated applications created entirely from prompts—may supersede traditional hand-crafted applications. Dynamically generated user interfaces could offer personalized and customizable experiences while we can design APIs for machine consumption and efficiency rather than human readability. Furthermore, we can store data in less structured, more adaptable, and flexible forms. 

AI is already making application development more accessible. With AI handling the complex, technical aspects of development, less-technical individuals can take a greater role in building applications. Consequently, AI can potentially democratize software engineering. 

Intelligent apps will continue to evolve, becoming more efficient, adaptable, and capable of learning from user interactions. Similarly, AI will inevitably become integral in every stage of app development.

Summary

AI has already created a new development paradigm—one in which machine intelligence and human creativity come together to produce innovative, efficient, and robust applications. AI already assists us at every stage of app development, from design and architecture to coding and debugging. 

Developers who embrace AI tools and integrate them into their processes can enhance their productivity, improve the quality of their applications, and expand the possibilities of their application development.  

The future of development is automated and AI-driven, and it’s here today. Getting started is easy—try an AI-powered development tool like GitHub Copilot to see how AI-assisted pair programming can enhance your productivity and code quality.

Exercise

+ + \ No newline at end of file diff --git a/30daysofIA/tags/github-actions/page/7/index.html b/30daysofIA/tags/github-actions/page/7/index.html index 2403f03222..4f15574029 100644 --- a/30daysofIA/tags/github-actions/page/7/index.html +++ b/30daysofIA/tags/github-actions/page/7/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "github-actions" | Build Intelligent Apps On Azure +11 posts tagged with "github-actions" | Build Intelligent Apps On Azure @@ -14,14 +14,13 @@ - - + +
-

9 posts tagged with "github-actions"

View All Tags

· 4 min read
It's 30DaysOfIA

This Fall focus on building intelligent apps using AI and cloud-native technologies. #FallForIntelligentApps brings to you a learning journey to build your skills on creating differentiated experiences while modernizing your applications. It’s time to learn it all.

What We'll Cover

image

Get Ready To #FallForIntelligentApps starting September 18!

Today, we kick off the Fall season with content and activities to skill you up on all things Intelligent Apps or AI Apps on Azure with content, events, and community interactions! Read on to learn about what is coming!

Explore Our Initiatives

We have a number of initiatives planned for the month to help you learn and skill up on relevant technologies. Click on the links to visit the relevant pages for each

image

Register for the events!

What are 4 things you can do today, to jumpstart your learning journey?

#30Days Of Intelligent Apps

#30DaysOfIA is a series of daily blog posts grouped into themed weeks - taking you from core concepts to end-to-end solution examples in 30 days. Each article will provide conceptual lessons paired with exercises and resources to help you reinforce learnings and take next steps.

This series takes you through learning journey in six stages, each building on the previous week to help you skill up in a beginner-friendly way:

We will start with defining intelligent apps and then expand on how to build with cloud-native technologies like Azure Kubernetes Service, Azure Container Apps and Azure Functions, and take you through end-to-end scenarios for real world application development. Before we dive deep on intelligent apps, here is a high-level overview of the Intelligent Apps landscape on Azure:

image

Containers on Azure services offer you a wide range of capabilities, from simplicity to control to suit your different needs.

image -image

To start with the basics for developing Kubernetes applications, explore #30DaysOfCloudNative.

Cloud-native development when paired with serverless computing enhances your solution architecture for building cost optimized, resilient applications.

image

To start with the basics for serverless computing, explore #30DaysOfServerless.

Let’s Get Started

Now you know everything! We hope you are as excited as we are to dive into a full month of active learning and doing! Don't forget to subscribe for updates in your favorite feed reader! And look out for our first Intelligent Apps post Monday!

- - +
Skip to main content

11 posts tagged with "github-actions"

View All Tags

· 10 min read
It's 30DaysOfIA

Explore real-world examples of how Intelligent Apps revolutionize logistics, retail, finance, manufacturing, tech, and medicine.

What We'll Cover:

  • Real-world businesses revolutionizing operations with Intelligent Apps
  • High level architecture for industry scenarios
  • Implementation architecture overview across industries

A decorative header image for the blog

Real-world Success Stories

According to IBM’s 2022 Global AI Adoption Index, 35% of surveyed companies already use AI in their business, and 42% are exploring the idea. Organizations lagging behind the AI curve will quickly fall behind early adopters, who are already enjoying its benefits and boosting their investment in this technology.

In the first part of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” we explored how organizations can use AI-powered apps, or intelligent apps, to transform their operations, decision-making, and customer experiences, leaping ahead of the competition.

Intelligent apps significantly improve operational efficiency and customer engagement across industries like logistics, retail, finance, manufacturing, tech, and healthcare. In this article, we’ll spotlight real-world success stories of businesses revolutionizing operations with intelligent apps using Microsoft Azure’s app, data, and AI services.

info

Register for the Learn Live on September 21 for a live guided experience with SMEs on how to build end-to-end solution architecture for real world implementation of Intelligent Apps.

Streamlining Operations: Intelligent Apps in the Airline Industry

Aerospace pioneer Airbus leveraged intelligent apps to streamline its operations and innovate its services with two ground-breaking modernizations that use Azure apps, data, and AI services to reimagine pilot training and predict aircraft maintenance issues. 

Aviation technology’s rapid evolution compelled Airbus to reconceive their approach to training pilots. The aircraft manufacturer created a pilot training chatbot by combining Azure AI Services with Speech to Text and Text to Speech. The bot helps trainee pilots navigate over 6,000 pages of technical information. Pilots can ask questions verbally for quick answers, aiding their understanding of dense information and speeding up training. And when Airbus needed to accommodate its military consumers, deploying its Kubernetes cluster to a disconnected, “air-gapped” environment met the strict security requirements of military aircraft training.

A chatbot could look like the following diagram as Azure services work together to create a conversational experience. Its components could include security and governance, bot logic and user experience (UX), bot cognition and intelligence, data extract, transfer, and load (ETL) operations, quality assurance and enhancements, logging, and monitoring and reporting.

A diagram of a conversational experience enabled by Azure services

Airbus also used AI to improve its aircraft maintenance and safety. They deployed AI Anomaly Detector to gather and analyze telemetry data from multiple flights, enabling more profound insights into wear and tear on military aircraft operating in harsh conditions. This AI application helps Airbus better understand each airplane’s health. Airbus can predict and fix potential problems before they occur, improving the aircraft’s safety and operational readiness.

AI and Azure are changing how organizations like Airbus operate, even in strict, highly regulated markets with stringent security requirements. And by streamlining pilot training and staying ahead of aircraft maintenance, Airbus helps its military and commercial clients reliably deliver their goods across the globe, maintaining their critical supply chains.

Boosting Customer Service: Intelligent Apps in the Retail Industry

The retail industry has been among the first to adopt and benefit from intelligent apps. For example, the leading American used car retailer, CarMax, uses intelligent apps to transform the car shopping experience.

Before embracing AI, CarMax faced a significant challenge: Manually creating text summaries was time-consuming and labor-intensive. CarMax decided to leverage Azure OpenAI Service to automatically generate car research pages, offering customers valuable insights while enhancing the website’s search engine rankings.

This AI-driven approach has significantly streamlined the process, achieving in a few months what would have taken years manually. Also, the AI system condenses thousands of customer reviews into a few readable sentences, offering potential buyers a quick overview of others’ experiences.

CarMax has improved their customer experience while boosting operational efficiency through AI adoption. CarMax’s editorial staff focuses on creating strategic, longer-form content with the time saved, enhancing productivity, and driving more website traffic.

CarMax’s success story serves as a testament to the potential of intelligent apps in the retail industry.

Enhancing Cybersecurity: Intelligent Apps in Finance

Intelligent apps also impact the highly regulated financial services sector. Swift, a leading infrastructure provider for financial messaging services, uses intelligent apps to enhance decision-making and combat financial crime.

The massive growth of cross-border transactions and instant payment networks led to a surge in financial fraud that can cost the industry hundreds of billions of dollars every year. So, Swift and Microsoft partnered to build an anomaly detection model using federated learning techniques with Azure Machine Learning and Azure confidential computing.

Microsoft Purview helped govern data, as the federated learning method partially trained each model in a silo before aggregating them. This approach enabled Swift and Microsoft to develop a highly accurate model without copying or moving data from Swift members’ secure locations, ensuring the highest level of security and privacy.

Financial organizations can now deploy this trained ML model to run a real-time fraud detection system, lowering the cost of financial fraud.

Organizations can deploy a fraud detection system like the following example on Microsoft Azure. The system ingests data from streams or storage, runs analytics, and uses ML to detect fraud and store results. It then alerts users and other systems to the security issue.

A diagram of an example fraud detection system

Swift provides the innovative model to its banking partners, who can share data to further train the model. Azure confidential computing securely runs new models on current data to accumulate insights, generating an ongoing learning cycle. This iterative approach strengthens the global detection of fraudulent financial transactions through an increasingly faster and more accurate model. 

The collaboration between Swift and Microsoft demonstrates how combining various Azure services creates Intelligent Apps to enhance decision-making in finance. These apps solve real problems at an unprecedented global scale. 

Enhancing Efficiency: Intelligent Apps in Manufacturing

3M, known for Post-it Notes and other innovative products, wielded the power of Intelligent Apps to standardize, automate, and accelerate its sales forecasting. The company needed a unified, automated approach to replace its multiple manual methods.

It adopted Azure Machine Learning and other Azure AI Services to develop, train, and implement 1,500 custom models for each region and division, replacing manual methods. This solution delivers timely finance, sales, and marketing insights while freeing teams from time-consuming manual data manipulation tasks.

3M used Azure Machine Learning, automated machine learning (AutoML), and the Many Models Solution Accelerator to train and score numerous machine learning models in parallel. This approach significantly shortened the company’s development cycle time.

Then, 3M integrated Microsoft Power BI into their solution to compile and visualize results more effectively. Executives now have an unbiased and unfiltered view of data for better decision-making. With its improved ability to forecast sales, 3M can focus on manufacturing the products it needs efficiently.

3M’s success demonstrates how combining various Azure services can enhance efficiency in the manufacturing industry. These apps improve operational processes while enabling employees to focus on higher-level tasks. Strategic decisions are more targeted, and business conversations are more meaningful.

Driving Innovation: Intelligent Apps in the Technology Industry

As expected, the tech industry is at the forefront of leveraging Intelligent Apps to drive innovation. Elastic saw an opportunity to use intelligent apps to optimize its search services and meet customer needs more effectively. The company built a managed, secure, and scalable solution for its customers, Elastic Cloud, by leveraging Microsoft Azure.

Elastic Cloud uses Azure Machine Learning and other Azure AI Services to automate many basic management tasks, such as backing up data, upgrading, and scaling deployments. This approach enables Elastic’s customers to focus less on managing the software and more on serving their own clients. Customers can quickly deploy and manage Elastic Cloud on Kubernetes (ECK) using Azure Kubernetes Service.

As a result, Elastic’s cloud business is growing twice as fast as its on-premises solutions. Enterprise search users have reported significant improvements in system availability and employee satisfaction.

Additionally, Elastic incorporated Azure OpenAI Service into its Elasticsearch Relevance Engine (ESRE), providing more relevant answers to search queries. ESRE enables text, vector, and hybrid search, offering a more effective and accurate response to queries.

Elastic’s success story demonstrates how combining various Azure services can create intelligent Apps to drive innovation in the tech industry. These apps help improve operational processes and deliver better results quickly.

Revolutionizing Healthcare: Intelligent Apps in Medicine

Intelligent apps also drive transformative changes in the healthcare sector. Cambridgeshire and Peterborough NHS Foundation Trust, a healthcare provider in the United Kingdom, gradually shifted their on-premises systems to the cloud using Azure, improving flexibility, and lowering costs. They needed a solution for their electronic patient record (EPR) system that integrates 23 years of data across multiple systems and formats.

The Trust used Microsoft Azure and Azure Cognitive Search, including semantic search for understanding intent and contexts and cognitive capabilities for extracting insights from data, to standardize and automate data management. This approach made legacy, archive, and live data more discoverable, saving clinicians’ time. The Trust’s solution searches through millions of clinical documents in seconds, even recognizing handwritten notes without needing a separate optical character recognition (OCR) solution to extract text from images (Azure Cognitive Search automatically does it). Clinicians can now uncover previously inaccessible insights, enabling them to offer more personalized care.

The Trust also used Integration Services tools such as Azure Logic Apps to create workflows without writing code and Event Grid to connect Azure and third-party services through a publisher-subscriber model. They switched on a live feed from their new EPR system to Azure to feed real-time information into other applications. Researchers can now access anonymized data to fuel medical innovations beyond the NHS.

The healthcare provider also invested heavily in Power BI’s data visualization and analytical functions to sift through massive amounts of information. This level of data accessibility was previously impossible with their on-premises system. The new insights have significantly improved clinicians’ efficiency for better patient care.

Data accessibility and security are critical in the healthcare industry. Intelligent Apps empower organizations like the Trust to revolutionize medical care. AI capabilities helped the Trust improve its operational efficiency and user experiences, setting a new standard for healthcare in the digital age.

Summary

AI is driving transformation and real-world successes across industries. Intelligent apps are reshaping our world by streamlining operations in manufacturing and logistics, enhancing customer experience in the retail sector, improving decision-making in finance, driving innovation in tech, and revolutionizing healthcare.

As organizations adopt AI, they’re innovating in ways that were once too difficult, expensive, or outright impossible. Companies that wait to embrace intelligent apps risk the chance of falling behind. By embracing intelligent apps and leveraging the capabilities of Azure, you can harness the transformative potential of AI to remain on the cutting edge of technology.

Exercise

+ + \ No newline at end of file diff --git a/30daysofIA/tags/github-actions/page/8/index.html b/30daysofIA/tags/github-actions/page/8/index.html index 56aef03fd7..3ab80e4bd5 100644 --- a/30daysofIA/tags/github-actions/page/8/index.html +++ b/30daysofIA/tags/github-actions/page/8/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "github-actions" | Build Intelligent Apps On Azure +11 posts tagged with "github-actions" | Build Intelligent Apps On Azure @@ -14,14 +14,13 @@ - - + +
-

9 posts tagged with "github-actions"

View All Tags

· 4 min read
It's 30DaysOfIA

Continue The Learning Journey through Fall For Intelligent Apps! 🍂

What We'll Cover

Thank you! ♥️

image

It's hard to believe that JavaScript on Azure hack-together is ending! It seems like just yesterday that we launched this initiative, and yet here we are, 15 days later, with an incredible amount of learning and growth behind us. So... it's time for a wrap!

From the bottom of our hearts, we want to thank each and every one of you for your participation, engagement, and enthusiasm. It's been truly inspiring to see the passion and dedication from this strong community, and we're honored to be a part of it. ✨

Recap of The JavaScript on Azure Global Hack-Together

As we wrap up this exciting event, we wanted to take a moment to reflect on all that we've accomplished together. Over the last 15 days, we've covered a lot of ground, from the basics of contributing to Open source to the exploration of the Contoso Real Estate project from its Frontend to its Backend and future AI implementation.

Now that the hack-together is ending, we want to make sure that you have all the resources you need to continue honing your skills in the future. Whether you're looking to make your fist contribution to open source, become an open source maintainers, collaborate with others, or simply keep learning, there are plenty of resources out there to help you achieve your goals. So, let's dive in and explore all the ways you can continue to grow your JavaScript skills on Azure!

JSonAzure Hack-together Roadmap 📍:

hack-together-roadmap (2)

Recap on past Livestreams🌟:

Day 1️⃣: Opening Keynote (Hack-together Launch): Introduction to the Contoso Real Estate Open-source project!, managing complex and complex enterprise architecture, new announcements for JavaScript developers on Azure

Day 2️⃣: GitHub Copilot & Codespaces: Introduction to your AI pair programmer (GitHub Copilot) and your virtual developer environment on the cloud (GitHub Codespaces)

Day 6️⃣: Build your Frontend using Static Web Apps as part of a complex, modern composable frontends (or micro-frontends) and cloud-native applications.

Day 9️⃣: Build a Serverless Backend using Azure Functions

Day 1️⃣3️⃣: Easily connect to an Azure Cosmos DB, exploring its benefits and how to get started

Day 1️⃣5️⃣: Being in the AI Era, we dive into the Azure OpenAI Service and how you can start to build intelligent JavaScript applications

📖 Self-Learning Resources

  1. JavaScript on Azure Global Hack Together Module collection
  2. Lets #HackTogether: Javascript On Azure Keynote
  3. Step by Step Guide: Migrating v3 to v4 programming model for Azure Functions for Node.Js Application

Continue your journey with #FallForIntelligentApps

Join us this Fall on a learning journey to explore building intelligent apps. Combine the power of AI, cloud-native app development, and cloud-scale data to build highly differentiated digital experiences. Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure. Engage in a self-paced learning adventure by following along the #30Days of Intelligent Apps series, completing the Intelligent Apps Skills Challenge, joining the Product Group for a live Ask The Expert series or building an end to end solution architecture with a live guided experience through the Learn Live series. Discover more here.

Hands-on practice: Make your first contribution to open source!

Join our GitHUb Discussion Forum to connect with developers from every part of world, see contributions from other, find collaborators and make your first contribution to a real-world project! -Don't forget to give the repo a star ⭐

Resources

All resources are accessible on our landing page

- - +
Skip to main content

11 posts tagged with "github-actions"

View All Tags

· 8 min read
It's 30DaysOfIA

Let’s start with understanding the power of intelligent applications.

What We'll Cover:

  • What are intelligent applications?
  • Categories of Intelligent Apps
  • Breaking down the role of AI in intelligent apps

A decorative header image for the blog

Demystifying Intelligent Applications: Leveraging AI in App Development

Until recent years, when you heard the term “artificial intelligence” (AI), you might have pictured autonomous robots from sci-fi films. Now, perhaps your mind jumps to big data and machine learning (ML). Maybe you’re even thinking about the latest ChatGPT plugins and transformer architectures. Today, AI is more than these imaginations: it’s a reality increasingly woven into our day-to-day routines through intelligent applications.

Intelligent apps combine cloud-scale compute, data, and predictive or generative AI. These applications help you automate tasks and make data-driven decisions, aiding in leveraging business intelligence to gain meaningful, practical outcomes. 

For developers, the AI landscape isn’t an abstract complexity. It’s a real, tangible opportunity for evolving traditional applications, making them “intelligent,” and delivering more value to users. And with tools like Azure Kubernetes Service, Azure Cosmos DB, and Azure AI Services, stepping into the world of intelligent apps isn’t a steep climb but a steady trek across a manageable learning curve.

The Three Categories of Intelligent Applications

Before we start building, it’s critical to understand what makes an app “intelligent.” It’s not a universal definition so much as an identifier for apps that fall into one or more of the three primary categories: 

  • Outcome-based — This designation represents the broadest category of intelligent apps, whose principal value for users is the intelligent outcomes they provide. These apps process data to provide insightful results that enhance decision-making or help with tasks like trend prediction, resource management, and customer relationship management.

  • Functionality-based — This type of intelligent app takes AI integration a degree further, integrating AI as a function of the app to create or process its outputs, enhancing the app’s overall interactivity and usefulness.

  • Feature-based — This is the narrowest category, in which the app’s AI/ML component is one of its core features and primary selling points

Let’s dive more deeply into each category.

Outcome-Based Apps: Intelligent Outcomes Drive Success

Outcome-based applications are all about the results provided to the user. They function beyond merely processing data to offer insightful outcomes that drive success.

Think of a personal fitness tracker app that uses AI to analyze the user’s activity and provides actionable recommendations for achieving a goal. This type of app not only tracks your steps, sleep patterns, and heart rate but uses that data to provide you with personalized health advice. It helps you make informed lifestyle choices — the “intelligent” outcome.

Another example is a social media marketing app that uses AI for time-saving data analysis, task automation, and trend prediction. AI-based sentiment analysis can gauge customer perception while natural language processing (NLP) accurately interprets customer inquiries. Task automation can schedule posts and automate responses to common customer queries, and trend forecasting helps model and predict customer behavior to further personalize targeted promotional efforts. Building this type of intelligence into your apps provides cost-effective, actionable insights that guide sales and marketing strategies.

Functionality-Based Apps: AI-Driven Features Enhance User Experiences

As the name suggests, functionality-based applications integrate AI into how the app functions, but the end user drives the action and outcome. In this category of apps, AI takes a more active role in the user’s interaction by allowing them to use AI-created or processed output. These apps leverage AI technologies like Azure AI Services for NLP, image recognition, and pattern recognition to improve user experience.

Consider a music app that generates personalized playlists based on your listening habits or your smartphone’s camera app that uses AI to recognize faces and optimize photo settings. Even more advanced are the language translation apps that translate text from one language to another and can even understand and maintain the context and subtleties of the conversation. They use NLP to accomplish these feats.

Similarly, consider the latest e-commerce apps, whose AI-driven functionalities enable customers to try on clothes virtually, significantly enhancing the online shopping experience.

Feature-Based Apps: Integrating Advanced AI/ML Components

Finally, there are feature-based intelligent apps. These apps incorporate advanced AI/ML components like neural networks, internal large language models (LLMs), and sophisticated algorithms as core features for the user. In this type of application, AI is the primary feature for users to engage with and the primary selling point for the app.

Some examples of feature-based applications are chatbots and virtual agents built using Azure Bot Service. These robo-representatives use AI to provide more natural and human-like conversation, balancing superior efficiency with a friendlier, more personalized user experience. The new Bing is a prime example of this implementation, integrating AI to give users a knowledgeable copilot for web searches.

Similarly, OpenAI’s ChatGPT allows users access to its state-of-the-art language models available as Microsoft’s Azure OpenAI Service. It leverages the power of the highly sophisticated GPT-3 LLM to perform tasks like drafting emails, writing code, answering general knowledge questions, translating between languages, and tutoring in numerous subjects.

These apps don’t just use AI to provide outputs or improve user experiences: They push the boundaries of what’s possible in app development by integrating AI/ML components.

info

Register for the live Q&A Ask The Expert session on September 26 with the Azure Functions product engineering team.

Breaking Down the Role of AI in Intelligent Applications

Intelligent apps reflect the merging of traditional software design with the dynamic capabilities of AI. Let’s explore how AI integrates and amplifies these applications.

At its core, AI augments applications with the capacity to self-learn and self-improve. It allows you to create applications that predict, react, and adapt to a constantly changing environment. In the power grid management system for a major city, an AI-infused application can forecast power usage trends, react to anomalies, and adapt to sudden demand surges, ensuring optimal efficiency and preventing blackouts.

But AI’s role doesn’t end at prediction or decision-making: It also automates tasks. In enterprise solutions, it has moved beyond simple automation to encompass intelligent process automation (IPA). In this arena, AI algorithms understand, learn, and then automate entire digital workflows. For developers, this is a fertile ground for creating applications that can dramatically improve the operational efficiency of businesses.

Even more, AI substantially aids in development tasks. For example, there’s GitHub Copilot, an AI-powered code assistant that offers suggestions based not only on syntax but on development best practices and the intent of the code. AI has evolved from a tool you use to build apps to a partner that helps in creating them. 

Using AI to build intelligent apps isn’t about throwing out what’s worked before. It’s about enhancing the components that have the most impact and achieve the best results. Incorporating AI in apps elevates their predictive abilities, streamlines complex tasks, and even aids in the development and creation process. When you build intelligent apps, you don’t just address current problems. You anticipate future challenges and craft solutions that adapt over time.

Summary

As developers the power of AI is in our hands, and it’s our responsibility to harness its full potential. Each category tells the story of AI's transformative potential, from applications that deliver intelligent outcomes to those with AI capabilities to enrich the user experience and those pushing boundaries with advanced AI/ML technology.

Accessible and robust platforms like Azure AI can simplify the process of developing Intelligent Applications. Whether you’re refining an existing app or building a new one, just entering the world of AI, or already familiar with the landscape, take a moment to appreciate the potential that AI integration offers. You’re embracing this exciting frontier and shaping the future of app development. 

Exercise

  • Complete the Cloud Skills Challenge to build on your apps and AI skills.
  • Watch Episode 01 of the Serverless Edition Learn Live session to learn how to build an end-to-end intelligent app solution.
+ + \ No newline at end of file diff --git a/30daysofIA/tags/github-actions/page/9/index.html b/30daysofIA/tags/github-actions/page/9/index.html index 5d453ebedb..df157310f0 100644 --- a/30daysofIA/tags/github-actions/page/9/index.html +++ b/30daysofIA/tags/github-actions/page/9/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "github-actions" | Build Intelligent Apps On Azure +11 posts tagged with "github-actions" | Build Intelligent Apps On Azure @@ -14,13 +14,14 @@ - - + +
-

9 posts tagged with "github-actions"

View All Tags

· 2 min read
It's 30DaysOfIA

September is almost here - and that can only mean one thing!! It's time to 🍂 Fall for something new and exciting and spend a few weeks skilling up on relevant tools, techologies and solutions!!

Last year, we focused on #ServerlessSeptember. This year, we're building on that theme with the addition of cloud-scale Data, cloud-native Technologies and cloud-based AI integrations to help you modernize and build intelligent apps for the enterprise!

Watch this space - and join us in September to learn more!

- - +
Skip to main content

11 posts tagged with "github-actions"

View All Tags

· 4 min read
It's 30DaysOfIA

This Fall focus on building intelligent apps using AI and cloud-native technologies. #FallForIntelligentApps brings to you a learning journey to build your skills on creating differentiated experiences while modernizing your applications. It’s time to learn it all.

What We'll Cover

image

Get Ready To #FallForIntelligentApps starting September 18!

Today, we kick off the Fall season with content and activities to skill you up on all things Intelligent Apps or AI Apps on Azure with content, events, and community interactions! Read on to learn about what is coming!

Explore Our Initiatives

We have a number of initiatives planned for the month to help you learn and skill up on relevant technologies. Click on the links to visit the relevant pages for each

image

Register for the events!

What are 4 things you can do today, to jumpstart your learning journey?

#30Days Of Intelligent Apps

#30DaysOfIA is a series of daily blog posts grouped into themed weeks - taking you from core concepts to end-to-end solution examples in 30 days. Each article will provide conceptual lessons paired with exercises and resources to help you reinforce learnings and take next steps.

This series takes you through learning journey in six stages, each building on the previous week to help you skill up in a beginner-friendly way:

We will start with defining intelligent apps and then expand on how to build with cloud-native technologies like Azure Kubernetes Service, Azure Container Apps and Azure Functions, and take you through end-to-end scenarios for real world application development. Before we dive deep on intelligent apps, here is a high-level overview of the Intelligent Apps landscape on Azure:

image

Containers on Azure services offer you a wide range of capabilities, from simplicity to control to suit your different needs.

image +image

To start with the basics for developing Kubernetes applications, explore #30DaysOfCloudNative.

Cloud-native development when paired with serverless computing enhances your solution architecture for building cost optimized, resilient applications.

image

To start with the basics for serverless computing, explore #30DaysOfServerless.

Let’s Get Started

Now you know everything! We hope you are as excited as we are to dive into a full month of active learning and doing! Don't forget to subscribe for updates in your favorite feed reader! And look out for our first Intelligent Apps post Monday!

+ + \ No newline at end of file diff --git a/30daysofIA/tags/github-codespaces/index.html b/30daysofIA/tags/github-codespaces/index.html index 6cacae6ff1..2d6837ff62 100644 --- a/30daysofIA/tags/github-codespaces/index.html +++ b/30daysofIA/tags/github-codespaces/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "github-codespaces" | Build Intelligent Apps On Azure +11 posts tagged with "github-codespaces" | Build Intelligent Apps On Azure @@ -14,13 +14,13 @@ - - + +
-

9 posts tagged with "github-codespaces"

View All Tags

· 10 min read
It's 30DaysOfIA

Prepare your organization to adopt intelligent apps by taking a change management approach. Assess your readiness, upskill teams, create a culture of change and overcome resistance, and prepare for future evolution to pave the way for a smooth transition into an AI-centric company.

What We'll Cover:

  • Role of change management in modernizing to intelligent apps
  • Skills needed for building intelligent apps in your organization
  • How to build a culture of change to embrace AI
  • How to prepare for the future of intelligent apps and continuous evolution

image of modernizing AI solutions for intelligent apps

Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management

As we’ve discussed throughout this series, intelligent apps and generative AI are rewriting the rules of business and application development. The first article of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” explored how intelligent apps can enhance user experiences and drive businesses growth. These advancements are opening new opportunities and markets for companies ready to embrace them.

However, embracing this digital transformation is more complex than flipping a switch. It requires adjustments to the technical side, which we explored in the series’ fifth article, “Preparing the Path for Intelligent Apps: Transitioning from On-Premises/IaaS to Cloud Native Applications.” It also demands a shift in how organizations adapt to change and approach app design and development. Let’s explore that mindset transition with tips to help you make it happen.

The Role of Change Management in Transitioning to Intelligent Apps

Transitioning to intelligent apps isn’t merely a technological change. It’s a profound shift in how businesses perceive and operate. This move toward artificial intelligence (AI)-centric approach isn’t meant to replace humans with machines or radically overhaul all business operations.

Instead, intelligent apps should augment human capabilities, streamline processes, and foster an environment where innovation flourishes. They expand toolkits and techniques to include AI-assisted development tools like GitHub Copilot and AI-enabled workflows, helping us design and deliver AI-enhanced experiences.

Effective change management strategies are crucial in helping employees adapt to these new ways of working with intelligent apps, ensuring that the transition doesn’t disrupt productivity or morale.

Team and organization strategies should: 

  • Assess the impact of any proposed changes.  
  • Communicate the benefits and reasons behind the change to all affected team members.  
  • Provide the necessary training and support.  
  • Integrate the changes into the culture and daily operations. 

By incorporating this robust change management approach, you can identify and mitigate potential transition risks early. Moreover, this strategy ensures that employees are engaged throughout the process, fostering a sense of involvement and ownership. This, in turn, cultivates a culture suited for innovation through intelligent apps. 

These organizational changes focus on the following aspects: organizational readiness, skillset, culture of change and overcoming resistance, and adaptability. Let’s explore them below.

info

Register for the intelligent apps webinar on Driving Business Value by Modernizing with Cloud-Native & AI with Microsoft and Forrester on September 26. 

Explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries. 

Identifying Organizational Readiness for Intelligent Apps

The first step in preparing your organization for intelligent apps is to assess your current readiness for change. Analyze various elements of your organization, from infrastructure and workforce skills to strategic vision and overall culture.

Can your apps and systems integrate with AI platforms like Microsoft Azure AI? Is your workforce comfortable working alongside AI, or are they fearful and resistant? Does your strategic vision incorporate AI’s transformative potential, or does it fail to acknowledge AI as a critical driver of future growth?

These questions identify the challenges of integrating AI into an organization and determine the company’s readiness for intelligent apps.

Embracing intelligent apps involves more than being ready with your technological infrastructure or having the right technology or tools. You need flexible organizational structures and processes driving your business to accommodate new ideas and capabilities. Your teams must feel empowered to own and drive them. Much like Microsoft’s shift to AI-powered services—like AI-powered Bing and Edge and Microsoft 365 Copilot—embracing AI organization-wide requires a holistic vision and willingness to innovate, with teams capable of delivering on the plan.

After accessing these critical areas for organizational readiness, you can start planning a strategy for this transition to an AI-ready organization. Identify skill gaps and work to bridge them while nurturing an environment that appreciates and harnesses the potential of AI and intelligent apps.

Nurturing the Indispensable Skills for Intelligent Apps

Tasking your AI specialists with preparing your organization to use and build intelligent apps isn’t enough. Accomplishing this goal requires a cross-functional effort and a diverse skill set.

Developing intelligent apps involves data scientists managing and interpreting vast amounts of data, machine learning engineers building and training the models, and traditional developers integrating these models into usable applications. However, the impact of generative AI spans beyond creating apps. AI fluency across the entire organization—from sales and marketing to accounting and legal departments—offers substantial benefits, sparking new ideas and solutions from sometimes unexpected sources.

Teams' AI skills might roughly fall into three levels: 

  • Fundamental skills — Being familiar and efficient with using AI tools, for example, knowing how to write effective prompts for large language models (LLMs).
  • Visionary skills — Recognizing AI’s strengths and weaknesses, for example, its creativity and hallucinations, that is, unexpected output.
  • Expert skills — Managing data and training models and integrating AI technology into apps, such as incorporating the OpenAI application programming interface (API) into an Intelligent App.

Based on these levels, you can gauge your team's skill readiness to pinpoint the most significant gaps, whether in general AI knowledge across your organization or within technical development teams.

As intelligent apps become increasingly prevalent, upskilling your workforce is crucial. This transition opens a new realm of skills and capabilities for your team to master. You can offer machine learning, data analysis, and cloud computing training programs—such as Microsoft’s Azure AI Fundamentals certification—to empower your team to confidently navigate the intelligent apps landscape.

Nurturing these skills within your organization may require hiring new talent, reskilling current employees, and creating an environment that promotes learning and experimentation. Specifically, if your team understands tools like the Microsoft Azure AI platform, they could contribute significantly to developing and maintaining intelligent apps.

An upskilled team is more than a functional unit. It’s a think tank to drive innovation and improve your organization’s competitive standing in the digital marketplace.

Building a Culture of Change to Embrace AI

Even with the right people and skills in place, change remains an uphill battle for organizations committed to traditional processes. 

Transitioning to intelligent apps is as much about mindset as technology and skills. It requires fostering a culture open to change and innovation that understands AI’s transformative potential and embraces it to enhance rather than replace human capabilities. 

Leaders play a pivotal role in cultivating this mindset. As they model a positive attitude toward AI, emphasize its benefits, and address any concerns transparently, leaders significantly contribute to the organization’s readiness to adopt intelligent apps. 

The most critical factor in this transition is enabling an AI-friendly culture. It’s about encouraging curiosity, rewarding innovative thinking, and creating an environment where people thrive on learning and experimentation. 

Empowering teams to leverage practical AI tools and AI-driven data insights to aid decision-making helps motivate employees to use and explore AI’s potential, improve their skills, and contribute to the successful integration of intelligent apps. Such an environment is fertile ground to grow breakthrough ideas and innovative solutions. 

Overcoming Resistance to Change and Adopting Intelligent Apps

Despite AI’s potential, change isn’t easy. It takes effort and is especially challenging for organizations used to existing processes. This resistance to change is a significant roadblock when transitioning to intelligent apps. 

Overcoming this resistance involves:  

  • Understanding employees’ concerns. 
  • Challenging underlying assumptions about traditional systems and outdated processes that hinder change. 
  • Providing comprehensive training for workers to use AI effectively.
  • Demonstrating the benefits and opportunities of intelligent apps. 
  • Clear and continuous communication to dispel fears and misconceptions about AI.

These steps will foster greater acceptance and enthusiasm for intelligent apps in your teams. 

Preparing for the Future of Intelligent Apps and Continuous Evolution

Finally, it’s essential to understand that generative AI is just beginning to gain momentum. Adjusting to a world of intelligent apps isn’t a one-time change—it’s a continuous evolution. 

As AI technologies mature and evolve and more businesses realize their benefits, we can expect the landscape of intelligent apps to expand further. Organizations can cultivate a mindset of continuous learning, flexibility, and adaptation to prepare for this future. Staying ahead in this dynamic field requires organizations to keep up with the latest AI advancements, continually reassess their strategies, and pivot when necessary. 

The domain of intelligent apps is rapidly advancing. Organizations must prepare to navigate this dynamic landscape to stay ahead of the curve.

Summary

Preparing for AI and intelligent apps is a strategic decision. It requires thoughtful preparation and a culture that embraces change.

With the proper preparation and mindset, adopting AI can become a smoother, collaborative path to innovation for the whole organization. Integrating AI into your organization does not discard what has worked in the past. It simply enhances and augments those processes for a better future.

Consider the points above to assess your organization’s readiness and prepare to welcome intelligent apps.

In the first week of this series, you learned about intelligent apps, how organizations today find success through intelligent apps, what AI-assisted application development looks like, ways to leverage the power of generative AI, the technical changes needed to prepare for intelligent apps, and, in this article, the organizational changes required to harness the power of AI.

The future is intelligent. Use this series as a guideline to navigate challenges while preparing for and embracing AI's opportunities for an intelligent tomorrow.

Exercise

- - +
Skip to main content

11 posts tagged with "github-codespaces"

View All Tags

· 10 min read
It's 30DaysOfIA

Prepare your organization to adopt intelligent apps by taking a change management approach. Assess your readiness, upskill teams, create a culture of change and overcome resistance, and prepare for future evolution to pave the way for a smooth transition into an AI-centric company.

What We'll Cover:

  • Role of change management in modernizing to intelligent apps
  • Skills needed for building intelligent apps in your organization
  • How to build a culture of change to embrace AI
  • How to prepare for the future of intelligent apps and continuous evolution

image of modernizing AI solutions for intelligent apps

Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management

As we’ve discussed throughout this series, intelligent apps and generative AI are rewriting the rules of business and application development. The first article of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” explored how intelligent apps can enhance user experiences and drive businesses growth. These advancements are opening new opportunities and markets for companies ready to embrace them.

However, embracing this digital transformation is more complex than flipping a switch. It requires adjustments to the technical side, which we explored in the series’ fifth article, “Preparing the Path for Intelligent Apps: Transitioning from On-Premises/IaaS to Cloud Native Applications.” It also demands a shift in how organizations adapt to change and approach app design and development. Let’s explore that mindset transition with tips to help you make it happen.

The Role of Change Management in Transitioning to Intelligent Apps

Transitioning to intelligent apps isn’t merely a technological change. It’s a profound shift in how businesses perceive and operate. This move toward artificial intelligence (AI)-centric approach isn’t meant to replace humans with machines or radically overhaul all business operations.

Instead, intelligent apps should augment human capabilities, streamline processes, and foster an environment where innovation flourishes. They expand toolkits and techniques to include AI-assisted development tools like GitHub Copilot and AI-enabled workflows, helping us design and deliver AI-enhanced experiences.

Effective change management strategies are crucial in helping employees adapt to these new ways of working with intelligent apps, ensuring that the transition doesn’t disrupt productivity or morale.

Team and organization strategies should: 

  • Assess the impact of any proposed changes.  
  • Communicate the benefits and reasons behind the change to all affected team members.  
  • Provide the necessary training and support.  
  • Integrate the changes into the culture and daily operations. 

By incorporating this robust change management approach, you can identify and mitigate potential transition risks early. Moreover, this strategy ensures that employees are engaged throughout the process, fostering a sense of involvement and ownership. This, in turn, cultivates a culture suited for innovation through intelligent apps. 

These organizational changes focus on the following aspects: organizational readiness, skillset, culture of change and overcoming resistance, and adaptability. Let’s explore them below.

info

Register for the intelligent apps webinar on Driving Business Value by Modernizing with Cloud-Native & AI with Microsoft and Forrester on September 26. 

Explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries. 

Identifying Organizational Readiness for Intelligent Apps

The first step in preparing your organization for intelligent apps is to assess your current readiness for change. Analyze various elements of your organization, from infrastructure and workforce skills to strategic vision and overall culture.

Can your apps and systems integrate with AI platforms like Microsoft Azure AI? Is your workforce comfortable working alongside AI, or are they fearful and resistant? Does your strategic vision incorporate AI’s transformative potential, or does it fail to acknowledge AI as a critical driver of future growth?

These questions identify the challenges of integrating AI into an organization and determine the company’s readiness for intelligent apps.

Embracing intelligent apps involves more than being ready with your technological infrastructure or having the right technology or tools. You need flexible organizational structures and processes driving your business to accommodate new ideas and capabilities. Your teams must feel empowered to own and drive them. Much like Microsoft’s shift to AI-powered services—like AI-powered Bing and Edge and Microsoft 365 Copilot—embracing AI organization-wide requires a holistic vision and willingness to innovate, with teams capable of delivering on the plan.

After accessing these critical areas for organizational readiness, you can start planning a strategy for this transition to an AI-ready organization. Identify skill gaps and work to bridge them while nurturing an environment that appreciates and harnesses the potential of AI and intelligent apps.

Nurturing the Indispensable Skills for Intelligent Apps

Tasking your AI specialists with preparing your organization to use and build intelligent apps isn’t enough. Accomplishing this goal requires a cross-functional effort and a diverse skill set.

Developing intelligent apps involves data scientists managing and interpreting vast amounts of data, machine learning engineers building and training the models, and traditional developers integrating these models into usable applications. However, the impact of generative AI spans beyond creating apps. AI fluency across the entire organization—from sales and marketing to accounting and legal departments—offers substantial benefits, sparking new ideas and solutions from sometimes unexpected sources.

Teams' AI skills might roughly fall into three levels: 

  • Fundamental skills — Being familiar and efficient with using AI tools, for example, knowing how to write effective prompts for large language models (LLMs).
  • Visionary skills — Recognizing AI’s strengths and weaknesses, for example, its creativity and hallucinations, that is, unexpected output.
  • Expert skills — Managing data and training models and integrating AI technology into apps, such as incorporating the OpenAI application programming interface (API) into an Intelligent App.

Based on these levels, you can gauge your team's skill readiness to pinpoint the most significant gaps, whether in general AI knowledge across your organization or within technical development teams.

As intelligent apps become increasingly prevalent, upskilling your workforce is crucial. This transition opens a new realm of skills and capabilities for your team to master. You can offer machine learning, data analysis, and cloud computing training programs—such as Microsoft’s Azure AI Fundamentals certification—to empower your team to confidently navigate the intelligent apps landscape.

Nurturing these skills within your organization may require hiring new talent, reskilling current employees, and creating an environment that promotes learning and experimentation. Specifically, if your team understands tools like the Microsoft Azure AI platform, they could contribute significantly to developing and maintaining intelligent apps.

An upskilled team is more than a functional unit. It’s a think tank to drive innovation and improve your organization’s competitive standing in the digital marketplace.

Building a Culture of Change to Embrace AI

Even with the right people and skills in place, change remains an uphill battle for organizations committed to traditional processes. 

Transitioning to intelligent apps is as much about mindset as technology and skills. It requires fostering a culture open to change and innovation that understands AI’s transformative potential and embraces it to enhance rather than replace human capabilities. 

Leaders play a pivotal role in cultivating this mindset. As they model a positive attitude toward AI, emphasize its benefits, and address any concerns transparently, leaders significantly contribute to the organization’s readiness to adopt intelligent apps. 

The most critical factor in this transition is enabling an AI-friendly culture. It’s about encouraging curiosity, rewarding innovative thinking, and creating an environment where people thrive on learning and experimentation. 

Empowering teams to leverage practical AI tools and AI-driven data insights to aid decision-making helps motivate employees to use and explore AI’s potential, improve their skills, and contribute to the successful integration of intelligent apps. Such an environment is fertile ground to grow breakthrough ideas and innovative solutions. 

Overcoming Resistance to Change and Adopting Intelligent Apps

Despite AI’s potential, change isn’t easy. It takes effort and is especially challenging for organizations used to existing processes. This resistance to change is a significant roadblock when transitioning to intelligent apps. 

Overcoming this resistance involves:  

  • Understanding employees’ concerns. 
  • Challenging underlying assumptions about traditional systems and outdated processes that hinder change. 
  • Providing comprehensive training for workers to use AI effectively.
  • Demonstrating the benefits and opportunities of intelligent apps. 
  • Clear and continuous communication to dispel fears and misconceptions about AI.

These steps will foster greater acceptance and enthusiasm for intelligent apps in your teams. 

Preparing for the Future of Intelligent Apps and Continuous Evolution

Finally, it’s essential to understand that generative AI is just beginning to gain momentum. Adjusting to a world of intelligent apps isn’t a one-time change—it’s a continuous evolution. 

As AI technologies mature and evolve and more businesses realize their benefits, we can expect the landscape of intelligent apps to expand further. Organizations can cultivate a mindset of continuous learning, flexibility, and adaptation to prepare for this future. Staying ahead in this dynamic field requires organizations to keep up with the latest AI advancements, continually reassess their strategies, and pivot when necessary. 

The domain of intelligent apps is rapidly advancing. Organizations must prepare to navigate this dynamic landscape to stay ahead of the curve.

Summary

Preparing for AI and intelligent apps is a strategic decision. It requires thoughtful preparation and a culture that embraces change.

With the proper preparation and mindset, adopting AI can become a smoother, collaborative path to innovation for the whole organization. Integrating AI into your organization does not discard what has worked in the past. It simply enhances and augments those processes for a better future.

Consider the points above to assess your organization’s readiness and prepare to welcome intelligent apps.

In the first week of this series, you learned about intelligent apps, how organizations today find success through intelligent apps, what AI-assisted application development looks like, ways to leverage the power of generative AI, the technical changes needed to prepare for intelligent apps, and, in this article, the organizational changes required to harness the power of AI.

The future is intelligent. Use this series as a guideline to navigate challenges while preparing for and embracing AI's opportunities for an intelligent tomorrow.

Exercise

+ + \ No newline at end of file diff --git a/30daysofIA/tags/github-codespaces/page/10/index.html b/30daysofIA/tags/github-codespaces/page/10/index.html new file mode 100644 index 0000000000..d9797bc1ab --- /dev/null +++ b/30daysofIA/tags/github-codespaces/page/10/index.html @@ -0,0 +1,27 @@ + + + + + +11 posts tagged with "github-codespaces" | Build Intelligent Apps On Azure + + + + + + + + + + + + + + +
+

11 posts tagged with "github-codespaces"

View All Tags

· 4 min read
It's 30DaysOfIA

Continue The Learning Journey through Fall For Intelligent Apps! 🍂

What We'll Cover

Thank you! ♥️

image

It's hard to believe that JavaScript on Azure hack-together is ending! It seems like just yesterday that we launched this initiative, and yet here we are, 15 days later, with an incredible amount of learning and growth behind us. So... it's time for a wrap!

From the bottom of our hearts, we want to thank each and every one of you for your participation, engagement, and enthusiasm. It's been truly inspiring to see the passion and dedication from this strong community, and we're honored to be a part of it. ✨

Recap of The JavaScript on Azure Global Hack-Together

As we wrap up this exciting event, we wanted to take a moment to reflect on all that we've accomplished together. Over the last 15 days, we've covered a lot of ground, from the basics of contributing to Open source to the exploration of the Contoso Real Estate project from its Frontend to its Backend and future AI implementation.

Now that the hack-together is ending, we want to make sure that you have all the resources you need to continue honing your skills in the future. Whether you're looking to make your fist contribution to open source, become an open source maintainers, collaborate with others, or simply keep learning, there are plenty of resources out there to help you achieve your goals. So, let's dive in and explore all the ways you can continue to grow your JavaScript skills on Azure!

JSonAzure Hack-together Roadmap 📍:

hack-together-roadmap (2)

Recap on past Livestreams🌟:

Day 1️⃣: Opening Keynote (Hack-together Launch): Introduction to the Contoso Real Estate Open-source project!, managing complex and complex enterprise architecture, new announcements for JavaScript developers on Azure

Day 2️⃣: GitHub Copilot & Codespaces: Introduction to your AI pair programmer (GitHub Copilot) and your virtual developer environment on the cloud (GitHub Codespaces)

Day 6️⃣: Build your Frontend using Static Web Apps as part of a complex, modern composable frontends (or micro-frontends) and cloud-native applications.

Day 9️⃣: Build a Serverless Backend using Azure Functions

Day 1️⃣3️⃣: Easily connect to an Azure Cosmos DB, exploring its benefits and how to get started

Day 1️⃣5️⃣: Being in the AI Era, we dive into the Azure OpenAI Service and how you can start to build intelligent JavaScript applications

📖 Self-Learning Resources

  1. JavaScript on Azure Global Hack Together Module collection
  2. Lets #HackTogether: Javascript On Azure Keynote
  3. Step by Step Guide: Migrating v3 to v4 programming model for Azure Functions for Node.Js Application

Continue your journey with #FallForIntelligentApps

Join us this Fall on a learning journey to explore building intelligent apps. Combine the power of AI, cloud-native app development, and cloud-scale data to build highly differentiated digital experiences. Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure. Engage in a self-paced learning adventure by following along the #30Days of Intelligent Apps series, completing the Intelligent Apps Skills Challenge, joining the Product Group for a live Ask The Expert series or building an end to end solution architecture with a live guided experience through the Learn Live series. Discover more here.

Hands-on practice: Make your first contribution to open source!

Join our GitHUb Discussion Forum to connect with developers from every part of world, see contributions from other, find collaborators and make your first contribution to a real-world project! +Don't forget to give the repo a star ⭐

Resources

All resources are accessible on our landing page

+ + + + \ No newline at end of file diff --git a/30daysofIA/tags/github-codespaces/page/11/index.html b/30daysofIA/tags/github-codespaces/page/11/index.html new file mode 100644 index 0000000000..9b19883280 --- /dev/null +++ b/30daysofIA/tags/github-codespaces/page/11/index.html @@ -0,0 +1,26 @@ + + + + + +11 posts tagged with "github-codespaces" | Build Intelligent Apps On Azure + + + + + + + + + + + + + + +
+

11 posts tagged with "github-codespaces"

View All Tags

· 2 min read
It's 30DaysOfIA

September is almost here - and that can only mean one thing!! It's time to 🍂 Fall for something new and exciting and spend a few weeks skilling up on relevant tools, techologies and solutions!!

Last year, we focused on #ServerlessSeptember. This year, we're building on that theme with the addition of cloud-scale Data, cloud-native Technologies and cloud-based AI integrations to help you modernize and build intelligent apps for the enterprise!

Watch this space - and join us in September to learn more!

+ + + + \ No newline at end of file diff --git a/30daysofIA/tags/github-codespaces/page/2/index.html b/30daysofIA/tags/github-codespaces/page/2/index.html index 28836f8a03..2c5d78927c 100644 --- a/30daysofIA/tags/github-codespaces/page/2/index.html +++ b/30daysofIA/tags/github-codespaces/page/2/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "github-codespaces" | Build Intelligent Apps On Azure +11 posts tagged with "github-codespaces" | Build Intelligent Apps On Azure @@ -14,13 +14,13 @@ - - + +
-

9 posts tagged with "github-codespaces"

View All Tags

· 9 min read
It's 30DaysOfIA

Learn best practices for modernizing your on-premises or IaaS solutions to intelligent apps in the cloud.

What We'll Cover:

  • Traditional vs Intelligent Apps
  • On-Premises/IaaS vs. Cloud-Native Platforms
  • Modernization strategy for building Intelligent Apps

image of modernizing AI solutions for intelligent apps

Preparing the Path for Intelligent Apps: Transitioning from On-Premises/IaaS to Cloud-Native Applications

The proliferation of generative AI is paving the way for intelligent applications, software that leverages AI capabilities to deliver unparalleled functionality and user experience. As we covered in a previous article, “Demystifying Intelligent Applications: Leveraging AI in App Development,” intelligent Apps aren’t just products of advanced coding. They interact, learn, and evolve thanks to modern AI and machine learning (ML) breakthroughs. 

The paradigm shift from traditional to intelligent apps demands that we change our approach to the technical challenges of software and architecture design. It also necessitates adopting new ways of thinking and operating within an organization, which we explore in greater depth in “Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management.” 

This article explores the technical infrastructure powering intelligent apps, providing a roadmap to transition traditional on-premises or Infrastructure as a Service (IaaS) solutions to intelligent apps deployed to cloud native platforms and services. We’ll discover the differences between conventional and intelligent apps, options for re-architecting existing applications, and critical strategic considerations integral to modernizing the application-building approach. 

Understanding the Shift: Traditional vs. Intelligent Applications

Intelligent apps are more than just an incremental evolution from traditional apps. They involve a fundamental shift in software solution design, development, and deployment. A clear understanding of how each paradigm differs is crucial for comprehending the impact of this transition. 

Functionality and User Experience

Traditional applications, often rule-based and rigid, rely on preprogrammed operations. A traditional weather app, for example, typically presents structured, location-based forecasts using a predetermined data source. Meanwhile, an intelligent app leverages AI for data-driven decision-making and personalization. Rather than merely displaying the forecast, it integrates ML with additional data sources—like a user’s calendar and fitness data—to determine their favorite outdoor activities, provide relevant clothing suggestions, and even automatically design ideal vacations and times based on the user’s optimal weather preferences. 

Similarly, while traditional applications tend to offer a one-size-fits-all UI, intelligent apps use AI to create tailored interactions. For instance, an intelligent banking app might use generative AI to provide a voice-enabled UI that lets customers use natural language to ask about transactions and receive personalized financial advice.

Infrastructure

Intelligent apps integrating these sophisticated features require highly scalable, flexible infrastructure optimized for AI functionality and able to manage large volumes of real-time data. Unlike traditional applications deployed on-premises or in infrastructure as a service (IaaS) environments, intelligent apps demand the flexibility and scalability of cloud native architectures.

An on-premises or IaaS-based e-commerce application may struggle with connecting its data to AI services or scaling to accommodate voluminous real-time data and requests. But an intelligent app built with a cloud native approach and scalability at its core can handle variable demand much more efficiently. It also maintains its infrastructure components on demand, whether for collecting data for AI/ML consumption or user-facing features like AI-enabled customer support and personalization.

Consider an intelligent app built on Azure that creates ChatGPT-like experiences with custom data. It might connect Azure Open AI and Azure Cognitive Search to an app running on Azure Container Apps or Azure Kubernetes Service. 

The architecture might look like the following diagram based on this demo app:

diagram of a demo app architecture

While the cloud is well-equipped to handle the challenges associated with shifting to AI-centric infrastructure—handling real-time data processing and storage, integrating and managing AI services, configuring event-driven and microservices architectures, and ensuring security across the distributed environment—re-architecting a traditional app to an intelligent app requires careful planning and execution to ensure it benefits fully from the capabilities of AI and cloud computing.

On-Premises/IaaS vs. Cloud-Native Platforms

While on-premises and IaaS solutions provide substantial control over data and processes, this fine-grained control comes with setup and maintenance costs and may lack the scalability necessary for AI-driven Intelligent Apps. On-premises hosting involves purchasing and maintaining hardware, including servers and storage systems. IaaS involves leasing virtual hardware and then hiring and training IT personnel for the infrastructure’s operation, maintenance, and security.

Conversely, infrastructure built on cloud native app services with Microsoft Azure offers virtually unlimited scalability without ongoing hardware and maintenance costs. This feature is ideal for intelligent apps that need to adapt and scale to accommodate fluctuating workloads and data streams. And because these cloud infrastructures typically operate on a pay-as-you-go model, traditionally substantial upfront costs turn into predictable operating expenses, lowering both the cost and risk of launching applications.

Moreover, cloud service providers continually update their offerings, so building on sophisticated AI tools and services through the cloud, like the Azure AI Platform and orchestration capabilities with Azure Kubernetes Service, ensures that you have access to the latest AI and most advanced AI models and technologies.

Strategic Considerations for Transitioning to Intelligent Apps

When developing a strategy for transitioning to intelligent apps, you must consider costs, personnel skill development, and data management.

Monolithic architectures are unlikely to support the scale and ease of iteration required by intelligent apps, so you’ll likely need to implement code changes to support the transition. Your chosen hosting solution and the amount of data your app will handle affect cost and performance, in addition to determining privacy and compliance requirements.

An app that manages or stores terabytes or petabytes of data will require storage-optimized virtual machines or a managed database service hosting. Meanwhile, an app that processes video content or performs resource-intensive calculations should prioritize high-performance computing. Maximizing storage and performance can become extraordinarily costly.

Finally, existing applications shifting towards intelligent apps may encounter compatibility conflicts, depending on database type or the existing system’s underlying programming language. As you might imagine, a traditional app running on-premises and using a shared local database may need significant restructuring to support a cloud native architecture. You need plans for data migration, potential downtime, and training.

Next, let’s look at this transformation process.

Re-Architecting Monolithic Apps into Microservices

Shifting from monolithic applications to microservices is pivotal for transitioning to Intelligent Apps, and you can use this shift as an opportunity to transform your legacy software into intelligent apps that leverage the flexibility and power of microservices-enabled AI solutions.

Traditionally, a monolithic application (let’s use an online marketplace as an example) might handle all activities—from serving webpages to processing payments—within an on-premises server or a cloud server. However, this approach is challenging to scale and remains vulnerable to network failures or cyberattacks.

Breaking these applications into multiple services with specific responsibilities, such as authentication or notifications, can overcome these limitations. These services are typically deployed as microservices within container environments like Azure Kubernetes Service or serverless platforms like Azure Container Apps or Azure Functions using automated processes. This approach offers scalability, robustness, and improved performance, as each microservice can run independently and use its own database.

Shifting towards smaller, independent microservices in app architectures allows us to reap the benefits of AI-powered intelligent apps, for example, by using microservices to handle AI tasks such as processing natural language input, analyzing behavioral data, or personalizing content.

info

Watch Episode 1 and Episode 2 of the Learn Live series on how to build, test and deploy an end-to-end intelligent app solution using the Contoso Real Estate Sample.

Event-Driven Architectures: Knowing When to Use Them

Event-Driven Architectures (EDAs) can significantly enhance the performance and cost-effectiveness of intelligent apps. EDAs, typically implemented with microservices, respond in real time to events or state changes ranging from user interactions to real-time analytics.

Consider an intelligent app designed for personalized customer engagement. Instead of designing the application to check continuously for changes in user data, an EDA can react to such changes immediately, triggering appropriate microservices. For example, if users modify their preferences, an event could trigger an AI-driven recommendation microservice to update its suggestions immediately. This real-time responsiveness, enabled by services like Azure Event Grid, can improve user experiences and make intelligent apps more adaptive and proactive.

However, it’s worth noting that while EDAs provide significant benefits, they can introduce unique complexities, such as maintaining event consistency and debugging issues in event chains across multiple microservices. Therefore, it’s important to assess your application’s needs so you can understand where it can benefit from an EDA.

Conclusion

Transitioning to intelligent apps represents a shift in strategy, architecture, and infrastructure. By designing and re-architecting for cloud-based infrastructure, your apps gain scalability, cost-effectiveness, robust data management, access to sophisticated AI tools, enhanced security, and reduced operational overhead.  

But this transition extends well beyond its technical components. Organizations and the people working within them need equal attention. You can explore these facets in greater depth in “Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management.” 

The power of generative AI is here, and preparing for the move toward cloud-based intelligent applications will ensure that your apps remain performative and scalable and—most importantly—that your business is ready for the newest era of app innovation.

Exercise

- - +
Skip to main content

11 posts tagged with "github-codespaces"

View All Tags

· 9 min read
It's 30DaysOfIA

Learn best practices for modernizing your on-premises or IaaS solutions to intelligent apps in the cloud.

What We'll Cover:

  • Traditional vs Intelligent Apps
  • On-Premises/IaaS vs. Cloud-Native Platforms
  • Modernization strategy for building Intelligent Apps

image of modernizing AI solutions for intelligent apps

Preparing the Path for Intelligent Apps: Transitioning from On-Premises/IaaS to Cloud-Native Applications

The proliferation of generative AI is paving the way for intelligent applications, software that leverages AI capabilities to deliver unparalleled functionality and user experience. As we covered in a previous article, “Demystifying Intelligent Applications: Leveraging AI in App Development,” intelligent Apps aren’t just products of advanced coding. They interact, learn, and evolve thanks to modern AI and machine learning (ML) breakthroughs. 

The paradigm shift from traditional to intelligent apps demands that we change our approach to the technical challenges of software and architecture design. It also necessitates adopting new ways of thinking and operating within an organization, which we explore in greater depth in “Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management.” 

This article explores the technical infrastructure powering intelligent apps, providing a roadmap to transition traditional on-premises or Infrastructure as a Service (IaaS) solutions to intelligent apps deployed to cloud native platforms and services. We’ll discover the differences between conventional and intelligent apps, options for re-architecting existing applications, and critical strategic considerations integral to modernizing the application-building approach. 

Understanding the Shift: Traditional vs. Intelligent Applications

Intelligent apps are more than just an incremental evolution from traditional apps. They involve a fundamental shift in software solution design, development, and deployment. A clear understanding of how each paradigm differs is crucial for comprehending the impact of this transition. 

Functionality and User Experience

Traditional applications, often rule-based and rigid, rely on preprogrammed operations. A traditional weather app, for example, typically presents structured, location-based forecasts using a predetermined data source. Meanwhile, an intelligent app leverages AI for data-driven decision-making and personalization. Rather than merely displaying the forecast, it integrates ML with additional data sources—like a user’s calendar and fitness data—to determine their favorite outdoor activities, provide relevant clothing suggestions, and even automatically design ideal vacations and times based on the user’s optimal weather preferences. 

Similarly, while traditional applications tend to offer a one-size-fits-all UI, intelligent apps use AI to create tailored interactions. For instance, an intelligent banking app might use generative AI to provide a voice-enabled UI that lets customers use natural language to ask about transactions and receive personalized financial advice.

Infrastructure

Intelligent apps integrating these sophisticated features require highly scalable, flexible infrastructure optimized for AI functionality and able to manage large volumes of real-time data. Unlike traditional applications deployed on-premises or in infrastructure as a service (IaaS) environments, intelligent apps demand the flexibility and scalability of cloud native architectures.

An on-premises or IaaS-based e-commerce application may struggle with connecting its data to AI services or scaling to accommodate voluminous real-time data and requests. But an intelligent app built with a cloud native approach and scalability at its core can handle variable demand much more efficiently. It also maintains its infrastructure components on demand, whether for collecting data for AI/ML consumption or user-facing features like AI-enabled customer support and personalization.

Consider an intelligent app built on Azure that creates ChatGPT-like experiences with custom data. It might connect Azure Open AI and Azure Cognitive Search to an app running on Azure Container Apps or Azure Kubernetes Service. 

The architecture might look like the following diagram based on this demo app:

diagram of a demo app architecture

While the cloud is well-equipped to handle the challenges associated with shifting to AI-centric infrastructure—handling real-time data processing and storage, integrating and managing AI services, configuring event-driven and microservices architectures, and ensuring security across the distributed environment—re-architecting a traditional app to an intelligent app requires careful planning and execution to ensure it benefits fully from the capabilities of AI and cloud computing.

On-Premises/IaaS vs. Cloud-Native Platforms

While on-premises and IaaS solutions provide substantial control over data and processes, this fine-grained control comes with setup and maintenance costs and may lack the scalability necessary for AI-driven Intelligent Apps. On-premises hosting involves purchasing and maintaining hardware, including servers and storage systems. IaaS involves leasing virtual hardware and then hiring and training IT personnel for the infrastructure’s operation, maintenance, and security.

Conversely, infrastructure built on cloud native app services with Microsoft Azure offers virtually unlimited scalability without ongoing hardware and maintenance costs. This feature is ideal for intelligent apps that need to adapt and scale to accommodate fluctuating workloads and data streams. And because these cloud infrastructures typically operate on a pay-as-you-go model, traditionally substantial upfront costs turn into predictable operating expenses, lowering both the cost and risk of launching applications.

Moreover, cloud service providers continually update their offerings, so building on sophisticated AI tools and services through the cloud, like the Azure AI Platform and orchestration capabilities with Azure Kubernetes Service, ensures that you have access to the latest AI and most advanced AI models and technologies.

Strategic Considerations for Transitioning to Intelligent Apps

When developing a strategy for transitioning to intelligent apps, you must consider costs, personnel skill development, and data management.

Monolithic architectures are unlikely to support the scale and ease of iteration required by intelligent apps, so you’ll likely need to implement code changes to support the transition. Your chosen hosting solution and the amount of data your app will handle affect cost and performance, in addition to determining privacy and compliance requirements.

An app that manages or stores terabytes or petabytes of data will require storage-optimized virtual machines or a managed database service hosting. Meanwhile, an app that processes video content or performs resource-intensive calculations should prioritize high-performance computing. Maximizing storage and performance can become extraordinarily costly.

Finally, existing applications shifting towards intelligent apps may encounter compatibility conflicts, depending on database type or the existing system’s underlying programming language. As you might imagine, a traditional app running on-premises and using a shared local database may need significant restructuring to support a cloud native architecture. You need plans for data migration, potential downtime, and training.

Next, let’s look at this transformation process.

Re-Architecting Monolithic Apps into Microservices

Shifting from monolithic applications to microservices is pivotal for transitioning to Intelligent Apps, and you can use this shift as an opportunity to transform your legacy software into intelligent apps that leverage the flexibility and power of microservices-enabled AI solutions.

Traditionally, a monolithic application (let’s use an online marketplace as an example) might handle all activities—from serving webpages to processing payments—within an on-premises server or a cloud server. However, this approach is challenging to scale and remains vulnerable to network failures or cyberattacks.

Breaking these applications into multiple services with specific responsibilities, such as authentication or notifications, can overcome these limitations. These services are typically deployed as microservices within container environments like Azure Kubernetes Service or serverless platforms like Azure Container Apps or Azure Functions using automated processes. This approach offers scalability, robustness, and improved performance, as each microservice can run independently and use its own database.

Shifting towards smaller, independent microservices in app architectures allows us to reap the benefits of AI-powered intelligent apps, for example, by using microservices to handle AI tasks such as processing natural language input, analyzing behavioral data, or personalizing content.

info

Watch Episode 1 and Episode 2 of the Learn Live series on how to build, test and deploy an end-to-end intelligent app solution using the Contoso Real Estate Sample.

Event-Driven Architectures: Knowing When to Use Them

Event-Driven Architectures (EDAs) can significantly enhance the performance and cost-effectiveness of intelligent apps. EDAs, typically implemented with microservices, respond in real time to events or state changes ranging from user interactions to real-time analytics.

Consider an intelligent app designed for personalized customer engagement. Instead of designing the application to check continuously for changes in user data, an EDA can react to such changes immediately, triggering appropriate microservices. For example, if users modify their preferences, an event could trigger an AI-driven recommendation microservice to update its suggestions immediately. This real-time responsiveness, enabled by services like Azure Event Grid, can improve user experiences and make intelligent apps more adaptive and proactive.

However, it’s worth noting that while EDAs provide significant benefits, they can introduce unique complexities, such as maintaining event consistency and debugging issues in event chains across multiple microservices. Therefore, it’s important to assess your application’s needs so you can understand where it can benefit from an EDA.

Conclusion

Transitioning to intelligent apps represents a shift in strategy, architecture, and infrastructure. By designing and re-architecting for cloud-based infrastructure, your apps gain scalability, cost-effectiveness, robust data management, access to sophisticated AI tools, enhanced security, and reduced operational overhead.  

But this transition extends well beyond its technical components. Organizations and the people working within them need equal attention. You can explore these facets in greater depth in “Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management.” 

The power of generative AI is here, and preparing for the move toward cloud-based intelligent applications will ensure that your apps remain performative and scalable and—most importantly—that your business is ready for the newest era of app innovation.

Exercise

+ + \ No newline at end of file diff --git a/30daysofIA/tags/github-codespaces/page/3/index.html b/30daysofIA/tags/github-codespaces/page/3/index.html index 67a97c8a1d..5c0388d5fd 100644 --- a/30daysofIA/tags/github-codespaces/page/3/index.html +++ b/30daysofIA/tags/github-codespaces/page/3/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "github-codespaces" | Build Intelligent Apps On Azure +11 posts tagged with "github-codespaces" | Build Intelligent Apps On Azure @@ -14,13 +14,13 @@ - - + +
-

9 posts tagged with "github-codespaces"

View All Tags

· 10 min read
It's 30DaysOfIA

Explore how digital natives and Independent Software Vendors (ISVs) can transform their business operations and generate higher value for customers with generative AI and intelligent apps.

What We'll Cover:

  • Potential of Generative AI in application development
  • Challenges with Generative AI
  • Integrating Generative AI with business operations

image of generative AI and intelligent apps used in business

Unleashing Business Transformation: How Digital Natives Can Leverage Generative AI

Generative AI is no longer a technological flight of fancy. It’s here, and it’s rewriting the rules of business innovation. 

This type of AI leverages machine learning algorithms to create new data with similar characteristics to a given dataset. It goes beyond text generation using generative pretrained transformer (GPT) models, covering an array of applications that span from image synthesis to translations and more. It also powers a new class of intelligent applications, which combine AI capabilities with traditional software functions for enhanced user experiences and advanced business solutions. 

Generative AI is transforming how digital natives and independent software vendors (ISVs) operate. Generative AI offers new possibilities for creating unique business value, enhancing customer experiences, and driving growth. 

It plays a critical role in applications, improving functionality, aiding decision-making, and automating complex tasks to deliver dynamic, uniquely personalized user experiences. Generative AI also enables and automates data-driven outcomes, functions, and features.

Integrating AI capabilities into customer-facing apps and turning them into intelligent apps benefits both the customer and the business, as we discussed in Demystifying Intelligent Applications: Leveraging AI in App Development. You get faster product development workflows and better business intelligence for sales and marketing, while your users get more natural and context-aware features and interactions. 

That said, to realize generative AI’s potential, you need some strategy and knowledge of how and where it fits into your current business and operations. We’ll examine this technology’s potential to transform business operations, its risks, and rewards, and how to integrate it into your existing operations. 

Unpacking the Potential of Generative AI

Generative AI is creating new opportunities in the digital playing field. It empowers businesses to generate insights, design products, and automate processes in ways and at speeds that were previously unimaginable. You can now predict customer behavior more accurately, offer highly personalized user experiences, and prototype innovative ideas rapidly. 

For example, Microsoft used generative AI to reinvent web search in the new AI-powered Bing and Edge. The search experience has evolved from a solitary user flow of typing in keywords and scrolling through results. It’s now a conversational experience where Bing uses context to offer better quality and more personalized search queries and results. 

Similarly, Microsoft harnessed generative AI to transform its productivity suite into intelligent apps. Through Copilot for Microsoft 365, PowerPoint now empowers users to tell their stories by using DALL-E to generate images effortlessly. It also helps rephrase text-heavy slides to be more straightforward. Outlook now assists users to compose more effective emails, enabling better communication.

These are just some examples of how AI powers day-to-day work and operations. 

Businesses that ignore generative AI miss out on untapped market opportunities, letting competitors gain an edge in the digital race. By seeking ways to rethink assumptions and reinvent existing approaches, you can effectively use generative AI to create new opportunities, open products to new use cases and markets, unlock new growth and customer satisfaction levels, and increase your revenue.

info

Register for the intelligent apps webinar on Driving Business Value by Modernizing with Cloud-Native & AI with Microsoft and Forrester on September 26.

Explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries. 

The Crucial Role of Generative AI in Business Operations

Generative AI can be a powerful catalyst for business transformation. Let’s quickly tour some ways it can redefine business operations. 

Product development is traditionally a long and painstaking process. Now, tools like GitHub Copilot and TestPilot use generative AI to make coding and testing software easier, faster, and more robust. 

Similarly, machine learning as a service technology, such as Azure Machine Learning, democratizes the availability of machine learning. It helps collect, clean, and train models across datasets, making engineering and analyzing data more efficient and accessible.  

Generative AI also simplifies the process of creating intelligent apps. Previously, simulating thousands of prototypes, predicting their performance, and narrowing down to the most viable options could take weeks, months, or even years of iteration and data analysis. With generative AI, this process can be as quick as days, hours, or perhaps minutes. 

Customer experience is also ripe for transformation. Personalized recommendations drive engagement and sales, and generative AI can scale that personalization to every customer interaction using comprehensive and continually updated data. For instance, Microsoft leverages AI to enable personalization in Dynamics 365 Customer Insights. Using this tool, you can ask questions about your customers in natural language and gain valuable and actionable insights into your customer base. 

Lastly, marketing and sales can benefit from generative AI. For example, identifying current and future key markets and emerging trends helps companies stay ahead of the curve. Tools like Copilot in Power BI help you quickly understand and uncover insights about your business, enabling your team to craft targeted campaigns, create engaging content, and optimize your business strategy. 

Leveraging this full generative AI potential requires the right tools. GPT-4, for instance, can be instrumental in realizing effective business transformation. It excels in understanding context, generating human-like text, and learning from minimal data input, making it a formidable tool in any digital native's arsenal. Platforms like Azure OpenAI Service bring GPT-4’s power to your fingertips.

However, as with any powerful technology, it’s unwise to treat generative AI as a perfect solution. You must know the challenges and risks of not leveraging it effectively, including technical and human-factor considerations. 

Without generative AI, your company risks missing the opportunity to please customers via personalization, as discussed previously. You’ll miss sales opportunities via business intelligence insights, limiting your ability to grow revenue and keep up with the competition. 

One challenge of leveraging generative AI’s full potential is finding the right tools. Fortunately, you can implement generative AI in your organization without investing in infrastructure, computational power, and expertise. Platforms like Azure OpenAI Service and Azure AI Services make AI easily accessible. They also work with Azure compute and data services like Azure Functions and Azure Kubernetes Service (AKS), enabling you to build intelligent apps easily and quickly. 

However, generative AI can’t do everything. It may need human expertise and intervention to use it fully. 

For example, generative AI can create unique data. However, the data may have limited diversity and complexity. The AI may need help recognizing additional considerations or requirements specific to a problem. It may also lack context due to various factors, such as the information’s freshness. Also, since it’s an emerging field that’s potentially worth $4.4 trillion, we’re still learning about the nuances and potential regulatory impact of generative AI. 

Perhaps the most crucial and challenging aspect of adopting generative AI is an organization’s required cultural shift. Your teams must learn how to use AI as your company fosters an understanding of AI’s potential and limitations. 

Begin integrating AI literacy into your company’s DNA. Knowledge of its potential and the readily available tools can inspire your teams to incorporate generative AI into their daily workflows and business operations quickly, improve productivity, and deliver superior customer experiences. 

No company wants to be outpaced by its competitors. You can balance generative AI’s potential with your business operations by staying aware of and adapting to the dynamic AI landscape.

Achieving Harmony: Integrating Generative AI with Existing Business Operations

Integrating generative AI into existing business operations doesn’t have to be disruptive. With the correct strategy, you can do so seamlessly. Your goal should be to augment and enhance your operations, not disrupt and replace. 

For example, Microsoft Cloud technologies leveraged AI to relieve healthcare organizations’ challenges, such as talent shortage and workforce burnout. Providers automated their workflows, invested in specific applications and connectors, and enhanced support for data and operations. 

Azure AI Services, such as text analytics for health, enable healthcare organizations to understand their largely unstructured and untapped medical data. Project Health Insights leans on AI to help healthcare professionals gain actionable insights and inferences from patient data. 

Azure Health Bot, an Intelligent App, empowers organizations to build and deploy legally compliant, powerful conversational healthcare experiences to patients. 

A successful AI integration like this begins with understanding. Knowing what generative AI can do and setting realistic expectations can help you identify where and how AI can best serve your operations. 

Next, you can foster a culture of AI literacy across teams, speeding up adoption by helping them understand how it can aid their work. The more familiar your team is with AI and its possibilities, the smoother the transition. Training and education programs play a pivotal role here. 

Finally, integration requires a systematic approach. It’s not about overhauling everything at once — it's about identifying areas where AI can add the most value, then gradually experimenting and scaling its use. Successful integration stories often feature a phased approach, starting small and growing steadily. 

As we move toward a future where generative AI is a staple in business operations, remember that every journey is unique. Although there’s no one-size-fits-all solution, a strategic approach and a keen understanding of your business needs will help you navigate this new landscape successfully.

Conclusion

Generative AI offers plenty of opportunities for businesses that embrace it. It’s fueling the next generation of intelligent applications, transforming everything from product development to customer engagement to marketing. This technology is the key to unlocking unprecedented growth and innovation. 

While the journey may have challenges, the potential rewards make generative AI worth exploring. As generative AI advances, Azure AI’s broad range of tools and services will help you navigate this fascinating field.

Exercise

  • Watch Episode 02 of the serverless edition Learn Live session to learn how to build, test and deploy an end-to-end intelligent app solution.
  • Complete the Cloud Skills Challenge to build on your AI skills.
  • Register for Ask the Expert: Azure Functions on September 26 for a live Q&A with the Product Team on how to build AI apps using serverless computing.
- - +
Skip to main content

11 posts tagged with "github-codespaces"

View All Tags

· 12 min read
It's 30DaysOfIA

Delve into creating an Intelligent App that leverages Azure AI Vision to analyze images and extract data. Develop an API to perform optical character recognition (OCR) on uploaded images and testing this API locally.

What We'll Cover:

  • Understanding Azure AI Vision and Azure Kubernetes Service
  • Build a Python Web API to perform OCR
  • Test the API locally

image depicting an Intelligent App using AI

Jumpstart Your AI Journey: Building Your First Intelligent App with Azure AI and AKS (1)

An Intelligent App is a software application that uses artificial intelligence (AI), such as machine learning (ML), to extract meaningful insights, enabling users to make informed decisions based on the extracted text, data, sound, images, and more.

Intelligent Apps can employ advanced algorithms and models to recognize objects, patterns, and features within images, allowing them to perform tasks like object identification, image classification, facial recognition, and image segmentation.

To explore the power of Intelligent Apps, let’s build a Python app that performs optical character recognition (OCR) on images containing numbers and performs aggregate functions on the extracted numbers, like sum and average. We’ll leverage Azure AI Services for the OCR functionality and Azure Kubernetes Service (AKS) to develop, publish, and maintain a cloud-based, scalable app in Azure.

Let’s get started!

Understanding Azure AI Vision and Azure Kubernetes Service

Azure AI Vision is a cloud-based service that offers a range of advanced computer vision capabilities enabling applications to interpret content, such as images and videos, using ML and other AI algorithms. Azure computer vision lets users integrate powerful vision features into their projects without ML experience.

OCR is one of the critical functionalities within Azure vision service, allowing applications to extract text from images. Within Azure AI Vision service, OCR offers several features, including text extraction, multilingual support, layout analysis of documents, and integration with Azure services, such as Azure Functions and Azure Machine Learning.

info

Watch the intelligent apps webinar on Driving Business Value by Modernizing with Cloud-Native & AI with Microsoft and Forrester on September 26.

AKS is Microsoft Azure’s container orchestration platform that enables us to quickly and efficiently deploy, manage, and scale containerized applications, such as our Intelligent App.

Let’s explore how to leverage these tools to build an Intelligent App that uses Azure AI Vision to analyze images and extract data.

Prerequisites

To follow this tutorial, ensure you have the following:

For a preview of this final project, take a look at the complete project code.

Building the API with Azure AI Vision Service

First, log in to your Azure account and navigate to the Azure Portal.

Click Create a resource and search for “resource group.” Create a new resource group named computer-vision.

image in Azure Portal of creating a new resource group

Return to the Azure Portal home page and click Create a resource.

image in Azure Portal of creating a new resource

Search for “computer vision” and select it from the results. Click Create.

image of searching for resource group in Azure Portal

Clicking Create displays the Create Computer Vision wizard. Select the resource group you just created as the Resource group from the dropdown. Enter a resource name similar to what is seen in the screenshot below. Note that this must be a unique name so choose one that is not currently in use. Select Standard S1 as the pricing tier and check the Responsible use of AI box in the field at the bottom.

Image of selecting the Responsible use of AI box

Image of project details

Click Review + Create, then click Create.

Next, get the API key and endpoint from the Azure AI services multi-service account you created. Once the resource is created, navigate to the resource details page. Under Resource Management, select Keys and Endpoint. Once on the Keys and Endpoint page, you’ll find the Key 1 and Endpoint values. These credentials are necessary to access the Azure AI APIs.

image of selecting Keys and Endpoint

second image of selecting Keys and Endpoint

Configuring the Local Environment Variables

Next, we need to define a local environment variable file. This convenient way to store our API keys in a simple text file enables us to manage sensitive information and separate configuration from our API code.

If you haven’t done so already, clone the starter project template from GitHub into your local computer. Open the starter project template in a code editor and create a new .env file in the root folder.

Note: Root folder in this case is the “/Microsoft_Series17-18_Code/intelligent-app-before" folder.

Add the following key-value pairs to the file, remembering to replace the placeholder values with those you obtained from the previous step.

VISION_KEY=<THE-KEY-1-VALUE-FROM-YOUR-AZURE-AI-SERVICE>
VISION_ENDPOINT=<THE-ENDPOINT-VALUE-FROM-YOUR-AZURE-AI-SERVICE>

Reviewing the Quickstart Code

Let’s review the app.py file in the starter project template.

The app.py file works as the entry point for the Flask application. It is responsible for initializing the web server, listening for incoming requests, dispatching them to the appropriate route handlers, and generating responses.

Below is the code contained in the app.py file:

import os
import json

from flask import Flask, request
from flask_restful import Resource, Api
from werkzeug.utils import secure_filename

app = Flask(__name__,
static_url_path='',
static_folder='static/files')

api = Api(app)

app = Flask(__name__)

app.config['UPLOAD_FOLDER'] = 'files'

api = Api(app)

class UploadHandler(Resource):

def allowed_file(self, filename):
return '.' in filename and \
filename.rsplit('.', 1)[1].lower() in {'png'}

def post(self):
form = request.form.to_dict()

if 'file' not in request.files:
return json.dumps({ "success": False, "error": "No file part"})

file = request.files.get("file")
if file and self.allowed_file(file.filename):
filename = secure_filename(file.filename)
upload_folder = "static/files"
if not os.path.exists(upload_folder):
os.makedirs(upload_folder)
local_file_path = os.path.join(upload_folder, filename)
file.save(local_file_path)

return f"File {filename} uploaded successfully to folder: {upload_folder}"

api.add_resource(UploadHandler, "/")

if __name__ == '__main__':
app.run(debug=True)

The UploadHandler class above is defined as a Flask RESTful resource to handle file uploads. The class contains two methods:

  • The allowed_file method checks whether the file extension is allowed for upload. In this case, only .png files are allowed.

  • The post method handles HTTP POST requests for file uploads. It saves the uploaded .png file to the static/files folder.

Finally, the application returns a text response informing us that the file has been uploaded successfully.

Implementing the REST API in Python

To implement image analysis in your REST API, open the starter project template in your terminal, create a virtual environment, and activate it.

Next, install the Azure AI Vision SDK:

pip install azure-ai-vision

Then, add the following line to the requirements.txt file to include the azure-ai-vision package:

azure-ai-vision==0.13.0b1

Now, create an ocr_helper.py file in the project’s root folder. This file is a Python module that provides functions for processing images using OCR. Add the following code to the file:

import os
from statistics import median
from decimal import Decimal
import azure.ai.vision as sdk

def process_ocr(source_image):
service_options = sdk.VisionServiceOptions(os.environ["VISION_ENDPOINT"],
os.environ["VISION_KEY"])

vision_source = sdk.VisionSource(filename=source_image)

analysis_options = sdk.ImageAnalysisOptions()

analysis_options.features = (
sdk.ImageAnalysisFeature.CAPTION |
sdk.ImageAnalysisFeature.TEXT
)

analysis_options.language = "en"

analysis_options.gender_neutral_caption = True

image_analyzer = sdk.ImageAnalyzer(service_options, vision_source, analysis_options)

result = image_analyzer.analyze()

ocr_result = get_ocr_result(result)

return ocr_result

def get_ocr_result(result):
string_list = []

if result.reason != sdk.ImageAnalysisResultReason.ANALYZED:
return sdk.ImageAnalysisErrorDetails.from_result(result)
else:
if result.text is not None:
for line in result.text.lines:
for word in line.words:
string_list.append(word.content)

number_list = convert_to_decimal_list(string_list)

aggregate_result = aggregate_operations(number_list)

return {
"aggregate_result": aggregate_result,
"numbers_read": string_list
}

def convert_to_decimal_list(string_list):
return list(map(Decimal, string_list))

def aggregate_operations(numbers):
result = {
'sum': sum(numbers),
'average': sum(numbers) / len(numbers),
'median': median(numbers),
'min': min(numbers),
'max': max(numbers)
`}`
`return result`

This module uses the azure-ai-vision package to analyze images, including capturing captions and extracting text from the image. The process_ocr function inputs an image file and performs the OCR analysis. The module is a convenient tool for analyzing text-based data in pictures and performing numerical computations based on that data.

Let’s review the different components of the ocr_helper.py module listed above and explore how its functions help our Intelligent App process images and perform aggregate operations:

  • The process_ocr function takes the parameter source_image, which is the path of the image to be processed. The function then initializes the VisionServiceOptions using environment variables VISION_ENDPOINT and VISION_KEY to connect to the Azure AI Vision API.
  • The process_ocr function creates a VisionSource object with the specified source_image file name. ImageAnalysisOptions specify the features to be analyzed, including CAPTION and TEXT. The language is set to English ("en"), and gender-neutral captions are enabled.
  • Finally, an ImageAnalyzer object is created with the service options, vision source, and analysis options. The image is then analyzed using the image_analyzer.analyze method to retrieve OCR results. The recognized text from the OCR result is extracted, with numerical values stored in the string_list variable.
  • The convert_to_decimal_list function converts the list of strings to a list of decimal numbers, which helps process numeric values extracted from the text.
  • The aggregate_operations function calculates various aggregate statistics (sum, average, median, minimum, maximum) from a list of numbers and returns the results in a dictionary.

Note that you must have the appropriate credentials (VISION_KEY) and endpoint (VISION_ENDPOINT) configured for the Azure AI Vision API to use this module.

Finally, we must modify the app.py file so our code can use the process_ocr function of the ocr_helper.py file.

Add the following import statement to the app.py file:

from ocr_helper import process_ocr

Then, replace this line:

return f"File {filename} uploaded successfully to folder: {upload_folder}"

With these two lines:

aggregates = process_ocr(local_file_path)
return json.dumps(aggregates, default=str)

Doing so allows our application to perform aggregate operations (sum, average, median, minimum, maximum) of any numeric values found in the text and return the operation’s result as a JSON response.

Running the Intelligent App Locally

Our final goal is to deploy the sample app to AKS, which will run the app in a container. However, as a good practice, let’s run the app in a container locally before moving it to AKS. Doing so ensures that our application and its dependencies are encapsulated and can be run consistently across different environments.

Deploying to Docker Desktop

For our application to run on Docker, it needs two additional files: a Dockerfile and docker-compose.yml. A Dockerfile creates a single container image by specifying the steps to build it, and a docker-compose.yml file manages multiple containers as a cohesive application, defining their configurations and relationships. Dockerfile builds the image, while docker-compose.yml orchestrates multi-container applications.

In the project root folder, add a file named Dockerfile with the content below:

# syntax=docker/dockerfile:1

FROM python:3.8-slim-buster

WORKDIR /intelligentapp

COPY requirements.txt requirements.txt
RUN pip3 install -r requirements.txt

RUN pip install debugpy

COPY . .

CMD [ "python3", "-m" , "flask", "run", "--host=0.0.0.0"]

Now create a file named docker-compose.yml file in the project’s root folder with the following code, replacing the VISION_KEY and the VISION_ENDPOINT according to the environment variables you configured earlier:

version: '3.8'
services:
intelligentapp:
build:
context: .
dockerfile: Dockerfile
image: intelligent-app
ports:
- 5000:5000
container_name: intelligent-app
environment:
- VISION_KEY=<THE-KEY-1-VALUE-FROM-YOUR-AZURE-AI-SERVICE>
- VISION_ENDPOINT=<THE-ENDPOINT-VALUE-FROM-YOUR-AZURE-AI-SERVICE>

Then, run the following command in the terminal to build the image and start a container for the Intelligent App services defined in the docker-compose.yml file:

docker-compose up --build --force-recreate

Next, open a new terminal and run the command below to list the image deployed to your local Docker:

docker images

docker images

Testing the Intelligent App Locally

Now, we’ll test the Intelligent App’s functionality using the following test image:

test image to test functionality

This image is provided as sample1.png in the sample app source files, so you can easily use it in the next step.

The Intelligent App will read the image above using OCR to extract numbers in the image and perform five aggregate functions:

  • Sum
  • Average
  • Median
  • Min
  • Max

To test the API, open Postman and fill out the fields as follows:

  • URL: http://localhost:5000/
  • Method: POST
  • Body:
    • Form-data
      • Key: File — Click the right end of the Key field and select File from the dropdown list.
      • Value: Click Select Files, then select the sample1.png file provided in the sample code.

image of selecting the sample file

Now click the Send button and review the result body:

"{\"aggregate_result\": {\"sum\": \"25821\", \"average\": \"5164.2\", \"median\": \"5622\", \"min\": \"1447\", \"max\": \"9802\"}, \"numbers_read\": [\"3049\", \"5901\", \"5622\", \"1447\", \"9802\"]}"

As we can see, the app running on a local container returned the correct results based on the sample image. So, we’re ready to prepare our Azure resources to run the Intelligent App on AKS.

Exercise

In this article, we explored the creation of an Intelligent App that leverages Azure AI Vision to analyze images and extract data. We learned how to build a Python Web API to perform OCR on uploaded images and subsequently test this API locally.

Head to the next part, to deploy this API via Azure Kubernetes Service.

+ + \ No newline at end of file diff --git a/30daysofIA/tags/github-codespaces/page/4/index.html b/30daysofIA/tags/github-codespaces/page/4/index.html index 5cd61687c1..fad1981719 100644 --- a/30daysofIA/tags/github-codespaces/page/4/index.html +++ b/30daysofIA/tags/github-codespaces/page/4/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "github-codespaces" | Build Intelligent Apps On Azure +11 posts tagged with "github-codespaces" | Build Intelligent Apps On Azure @@ -14,13 +14,13 @@ - - + +
-

9 posts tagged with "github-codespaces"

View All Tags

· 9 min read
It's 30DaysOfIA

In the part of our series on intelligent apps, we’ll explore how AI is transforming application development, from design and architecture to building.

What We'll Cover:

  • Revolutionizing application development using AI
  • Infusing AI in application design architecture
  • AI Assisted Pair Programming
  • Future of App Development

A decorative image of application development with AI

Reimagining Application Development With AI: A New Paradigm

Artificial intelligence (AI) is impacting every industry. AI-powered development tools offer new functionalities and assist software developers throughout the development process, revolutionizing app development. From initial design and architecture to coding and debugging, AI reshapes how we approach application conception, creation, and iteration.

In part one of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” we explored the new breed of intelligent apps and how they use AI. In this article, we’ll see how AI technologies enhance developer toolkits and shape the future of app development, particularly through AI-enabled coding assistance and debugging.

The Dawn of Intelligent Apps: Revolutionizing Application Development

AI is more than just another addition to the development toolbox—it represents a revolution bringing unprecedented efficiency, innovation, and user engagement to our applications. 

What sets intelligent apps apart is how they can adapt and learn, helping us deliver unique, real-time user experiences.  

One example is an AI-driven health application that can track biometric data, predict potential health issues, provide personalized health advice, and even guide emergency services during a medical crisis. An AI-enabled language learning app that adapts to our learning speeds, recognizes our problem areas, and customizes its content to help us improve our language proficiencies is another example.  

Intelligent apps like these don’t just use data for recommendations—they can make critical decisions and provide sophisticated services that were previously impossible. 

For developers, intelligent apps mean we’re no longer just coding—we’re teaching applications how to think, adapt, and make decisions. AI is reshaping every step of the development journey, helping us to build more intuitive and user-focused products. 

Next, we’ll examine the impact of AI on application design and architecture and how AI-assisted “pair programming” changes software development.

info

Register for the FREE webinar on Intelligent Apps with Microsoft and Forrester on September 26 to explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries.

There will be a showcase of real-world use cases that demonstrate how AI can be seamlessly integrated into cloud-native environments driving tangible business value.

AI in Design and Architecture: Fueling Creativity and Efficiency

AI technologies help developers automate and streamline their processes and provide new ways to think about design.  

In traditional application development, the design and architecture phase can take significant time, effort, and expertise. For instance, adopting a monolithic architecture or a microservices-based design can significantly increase coding, testing, and debugging complexity. The app may even behave differently when hosted locally than in a deployed environment. It may take hours to track down an issue manually—time developers could spend elsewhere. 

Conversely, we can generate numerous design iterations in seconds with AI, each optimized to fulfill specific user needs and business goals. AI tools can quickly model various scenarios' impacts and software design patterns in the architectural design phase. It provides insights into performance and potential issues and suggests improvements. AI can also generate skeleton code, allowing developers to prototype different solutions or test specific components and technologies rapidly. By reviewing thousands of lines of code in seconds, AI allows us to focus on creating apps that serve users through feature development instead of searching for bugs.  

Integrating AI into the design and architecture phase can even extend our creative capabilities. By leveraging the power of generative AI—like large language models (LLMs)—and their broad knowledge and context going beyond software, we can better brainstorm apps and feature ideas. We can go beyond code and software architecture and get ideas about user experience, logo and branding, or marketing concepts. And as AI tools deliver insights and ideas we would have never considered; it encourages more innovation.

info

Watch Episode 01 of the Learn Live session to learn how to build an end-to-end intelligent app solution architecture.

The Power of AI-Assisted Pair Programming in Building Applications

AI-assisted pair programming is a collaborative coding approach where an AI system—like GitHub Copilot or TestPilot—assists developers during coding. It’s an increasingly common approach that significantly impacts developer productivity. In fact, GitHub Copilot is now behind an average of 46 percent of developers’ code and users are seeing 55 percent faster task completion on average. 

For new software developers, or those interested in learning new skills, AI-assisted pair programming are training wheels for coding. With the benefits of code snippet suggestions, developers can avoid struggling with beginner pitfalls like language syntax. Tools like ChatGPT can act as a personal, on-demand tutor—answering questions, generating code samples, and explaining complex code syntax and logic. These tools dramatically speed the learning process and help developers gain confidence in their coding abilities. 

Building applications with AI tools hastens development and provides more robust code. For example, GitHub Copilot helps us write new code accurately. It also quickly makes sense of existing code, suggests whole new lines or blocks of code, and even detects errors and proposes fixes. It’s like having a second coder working alongside us—one who never tires and is trained on billions of lines of code. 

However, it’s also important to acknowledge the limitations of AI. While generative AI-assistance technologies like GitHub Copilot are impressive, they’re still imperfect. They can sometimes produce erroneous, non-functional, or vulnerable code. They can also lack nuanced, domain-specific knowledge.  

So, although these tools are helpful, we can’t rely on them fully—these are programming aids rather than definitive solutions. AI-assisted pair programming is about augmenting the human developer’s skills, not replacing them.

Debugging and Improving Code With AI: Enhancing Application Quality

Identifying and correcting code errors is often time-consuming and challenging for developers. AI streamlines the debugging process by automatically detecting anomalies, suggesting fixes, and learning from previous bugs to prevent them from recurring. 

We can use generative AI to create unit tests from code snippets, from simple parameter checking and input validation to complex mocks of database and network services. Automated AI testing improves code coverage and robustness. It saves significant time compared to manually crafting unit tests. 

AI also revolutionizes code reviews by scanning and highlighting potential issues—like code patterns leading to a bug or a coding standards violation—and suggesting ways to simplify or refactor the code for readability and performance. This reduces human error and makes the code review process more efficient. Its benefits extend to small teams, where the availability of developers is crucial to deadlines, and large teams, where effective collaboration depends on readability and code consistency. AI further saves time by triaging issues so developers can prioritize the most mission-critical ones. 

Aside from improving code quality, AI is critical to understanding system behavior. Intelligent AI-assisted software development tools can monitor and audit system behavior, giving developers a better insight into how their code performs in different scenarios. AI can track patterns, identify inefficiencies, and provide alerts when they detect unusual behavior—enabling developers to enhance their code further. 

By predicting and identifying potential vulnerabilities, intelligent AI-assisted coding apps like Microsoft Security Copilot are pivotal in ensuring application functionality, reliability, and security. These AI tools are continually trained and upgraded with large codebases and user feedback that refines their responses. This ability to learn from past mistakes makes AI an incredibly powerful tool that will continue to expand its capabilities in the future.

info

Complete the AI Cloud Skills Challenge to build on you AI application development skills with paired programming using generative AI and GitHub Copilot.

AI and the Future of Application Development

The rapid advancements in AI and machine learning technologies point to an exciting future for application development. 

In “The Future of Applications,” software developer Mike Cann illustrates how LLMs might revolutionize application development. Cann that suggests fully-autonomous coding—AI-generated applications created entirely from prompts—may supersede traditional hand-crafted applications. Dynamically generated user interfaces could offer personalized and customizable experiences while we can design APIs for machine consumption and efficiency rather than human readability. Furthermore, we can store data in less structured, more adaptable, and flexible forms. 

AI is already making application development more accessible. With AI handling the complex, technical aspects of development, less-technical individuals can take a greater role in building applications. Consequently, AI can potentially democratize software engineering. 

Intelligent apps will continue to evolve, becoming more efficient, adaptable, and capable of learning from user interactions. Similarly, AI will inevitably become integral in every stage of app development.

Summary

AI has already created a new development paradigm—one in which machine intelligence and human creativity come together to produce innovative, efficient, and robust applications. AI already assists us at every stage of app development, from design and architecture to coding and debugging. 

Developers who embrace AI tools and integrate them into their processes can enhance their productivity, improve the quality of their applications, and expand the possibilities of their application development.  

The future of development is automated and AI-driven, and it’s here today. Getting started is easy—try an AI-powered development tool like GitHub Copilot to see how AI-assisted pair programming can enhance your productivity and code quality.

Exercise

- - +
Skip to main content

11 posts tagged with "github-codespaces"

View All Tags

· 9 min read
It's 30DaysOfIA

In the last article, we created an Intelligent App that leverages Azure AI Vision to analyze images and extract data. Develop an API to perform optical character recognition (OCR) on uploaded images and testing this API locally.

In this article, we will deploy the web API to the cloud platform using Azure Kubernetes Service.

What We'll Cover:

  • Understanding Azure AI Vision and Azure Kubernetes Service
  • Build a Python Web API to perform OCR
  • Test the API locally

image depicting an Intelligent App using AI

Jumpstart Your AI Journey: Building Your First Intelligent App with Azure AI and AKS (2)

In the previous article we explored the creation of an Intelligent App that leverages Azure AI Vision to analyze images and extract data. We learned how to build a Python Web API to perform OCR on uploaded images and subsequently test this API locally.

In this article we will use Azure Kubernetes Service (AKS) to develop, publish, and maintain our app in the cloud on Azure.

Let’s get started!

Prerequisites

To follow this tutorial, ensure you have completed Jumpstart Your AI Journey: Building Your First Intelligent App with Azure AI and AKS (1).

Pushing a Container Image to Azure Container Registry (ACR)

To start, open your CLI or terminal and type the following command:

az login

Follow the instructions displayed on your browser to enter your Azure credentials.

Once authenticated, you’ll initiate a secure connection between your local environment and Azure. This process grants you access to cloud services and resources.

Next, type the command below in your terminal to set up a new Azure Container Registry (ACR) to store your container images:

az acr create --resource-group computer-vision --name <name-of-azure-container-registry> --sku Basic

Remember to replace <name-of-azure-container-registry> with your container registry name. The name must be unique within Azure and comply with these rules.

The command above creates an Azure Container Registry (ACR) in the computer-vision resource group under the Basic SKU. This ACR is your secure and private repository for storing container images within Azure.

Next, log in to the registry with the following command:

az acr login -n <name-of-azure-container-registry>

The az acr login command above lets you securely authenticate and access the specified ACR without providing their Azure credentials each time.

Now, run the following command in your terminal. It will display the endpoint URL to log in to and interact with the ACR for pushing and pulling container images.

az acr show --name <name-of-azure-container-registry> --query loginServer --output table

This returns an endpoint URL as follows:

Result
----------------------------------
<name-of-azure-container-registry>.azurecr.io

Now, run the following command to show all container images, their repository, tags, and size:

docker images
REPOSITORYTAGIMAGE IDCREATEDSIZE
intelligent-applatesta7bf9f75361716 hours ago197MB

Tags are necessary to push Docker images to a remote registry like Azure Container Registry. They also let you differentiate between different versions of the same image and upload or download the one you want.

Run the following command to tag your Docker image:

docker tag intelligent-app <name-of-azure-container-registry>.azurecr.io/intelligent-app:v1

Then, run the docker images command again to check your tagged image:

docker images
REPOSITORYTAGIMAGE IDCREATEDSIZE
intelligent-applatestc52168039265About a minute ago197MB
<name-of-azure-container-registry>.azurecr.io/intelligent-appv1c52168039265About a minute ago197MB

Now run the following command so Docker can securely upload the image to your Azure Container Registry:

docker push <name-of-azure-container-registry>.azurecr.io/intelligent-app:v1

Once we've deployed the image to the container registry, AKS can access it during deployment.

info

Watch Episode 03 for the Learn Live session to experience a guided session on how to build, test and deploy an end-to-end intelligent app solution.

Deploying the Intelligent App on Azure Kubernetes Service (AKS)

Before we deploy our Intelligent App to AKS, we need to provision an AKS cluster and define Kubernetes manifests.

To provision an AKS cluster to host our application, we specify the desired configurations for the cluster, such as the number of nodes, node size, and networking options. But first, download and install the Kubernetes command-line tools (kubectl), a client credential plugin implementing Azure authentication:

az aks install-cli

If you’re using Linux, review this tutorial. Then run the following:

sudo az aks install-cli

Next, run the following command in your terminal to enable access to the networking-related resources and services provided by the `Microsoft.Network`` namespace in Azure:

az provider register --namespace Microsoft.Network

Now, we must create an AKS cluster. Run the following command to create an AKS cluster named aks-intelligent-app in the computer-vision resource group.

az aks create --resource-group computer-vision --name aks-intelligent-app --node-count 1 --generate-ssh-keys

The command above specifies the target resource group: computer-vision. The node pool is configured with one virtual machine (VM), and the secure shell (SSH) keys for secure node access are generated automatically.

Next, run the following command to update the AKS cluster you created by attaching it to your ACR. Doing so allows the AKS cluster to pull container images from the specified ACR when deploying workloads to the cluster.

az aks update -n aks-intelligent-app -g computer-vision --attach-acr <name-of-azure-container-registry>

Then, run the command below to configure kubectl to work with our AKS cluster in the computer-vision resource group.

az aks get-credentials --resource-group computer-vision --name aks-intelligent-app

The command above retrieves the necessary credentials and context information required for kubectl to communicate with the AKS cluster.

We still have to define the Kubernetes manifests written in YAML, describing the desired state of our application deployment, including containers, networking, and scaling rules. We’ll prepare these manifests, including Deployment and Service configurations, to define how our application should be deployed and exposed.

Create a folder named Deployment in the project root directory. Next, create two files in the deployment folder: deployment.yml and service.yml.

Add the following configuration to the deployment.yml file, replacing the <name-of-azure-container-registry> placeholder with your registry’s name:

apiVersion: apps/v1
kind: Deployment
metadata:
name: intelligent-app
spec:
replicas: 1
selector:
matchLabels:
app: intelligent-app
template:
metadata:
labels:
app: intelligent-app
spec:
nodeSelector:
kubernetes.io/os: linux
containers:
- name: intelligent-app
image: <name-of-azure-container-registry>.azurecr.io/intelligent-app:v1
resources:
limits:
memory: 512Mi
cpu: "1"
requests:
memory: 256Mi
cpu: "0.2"
ports:
- containerPort: 5000
env:
- name: FLASK_DEBUG
value: "1"
- name: VISION_KEY
value: <THE-KEY-1-VALUE-FROM-YOUR-AZURE-AI-SERVICE>
- name: VISION_ENDPOINT
value: <THE-ENDPOINT-VALUE-FROM-YOUR-AZURE-AI>

Additionally, edit the VISION_KEY and VISION_ENDPOINT environment variables above according to the API key and endpoint of your Azure AI Services instance.

Then, add the following configuration to the service.yml file:

apiVersion: v1
kind: Service
metadata:
name: intelligent-app-service
spec:
type: LoadBalancer
ports:
- protocol: TCP
port: 80
targetPort: 5000
name: port5000
selector:
app: intelligent-app

Now, we’ll deploy our application using kubectl. Applying the Kubernetes manifests creates the necessary resources and deploys our containerized application to the AKS cluster.

First, change the terminal to the deployment folder:

cd Deployment 

Then, run the following command to create or update Kubernetes resources defined in the deployment.yml file:

kubectl apply -f deployment.yml

Create a Kubernetes Service resource in a Kubernetes cluster based on the configuration defined in the service.yml file using the code below:

kubectl apply -f service.yml

Once you’ve applied the resource definition and the service configuration contained in the deployment.yml and the service.yml files, open the aks-intelligent-app Kubernetes Service in the Azure Portal, select Workloads under Kubernetes resources on the sidebar, and find the deployment named intelligent-app. It must have the status “Ready 1/1”. If you encounter an issue with this status, check out these troubleshooting resources.

image of selecting the deployment in Azure Portal

Testing the Intelligent App on AKS

To test the app on AKS, first, run the command below:

kubectl get services

This command lists the Services and their corresponding details, including the Service name, cluster IP address, external IP, and ports.

NAMETYPECLUSTER-IPEXTERNAL-IPPORT(S)AGE
intelligent-app-serviceLoadBalancer10.0.77.6020.121.76.15380:30936/TCP47s
kubernetesClusterIP10.0.0.1<none>443/TCP14m

The output above shows a Kubernetes Service named intelligent-app-service with a type set to LoadBalancer. It’s reachable from within the cluster using the cluster IP 10.0.77.60 and accessible externally via the external IP 20.121.76.153 on port 80 (mapped to port 30936).

Note: Your set of IP addresses will differ. Remember to use your unique external IP address when testing with Postman.

To test the deployed app, go to Postman, replace the URL with the external IP of the Kubernetes Service you just deployed, and click Send:

mage of sending the app in Postman

As we can see, our Intelligent App has successfully deployed to AKS and is functioning on the cloud as expected.

Exercise

Next Steps

In this two-part article, we explored the creation of an Intelligent App that leverages Azure AI Vision to analyze images and extract data. We learned how to build a Python Web API to perform OCR on uploaded images and subsequently deploy this API via Azure Kubernetes Service.

Besides OCR and Image Analysis, you can continue exploring Azure’s vast array of services and experiment further with Azure AI and AKS by applying various practical uses to your Intelligent Apps, including natural language processing, speech recognition and synthesis, sentiment analysis for customer feedback, and automated content moderation.

+ + \ No newline at end of file diff --git a/30daysofIA/tags/github-codespaces/page/5/index.html b/30daysofIA/tags/github-codespaces/page/5/index.html index b8217f961f..dd29ff3cf1 100644 --- a/30daysofIA/tags/github-codespaces/page/5/index.html +++ b/30daysofIA/tags/github-codespaces/page/5/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "github-codespaces" | Build Intelligent Apps On Azure +11 posts tagged with "github-codespaces" | Build Intelligent Apps On Azure @@ -14,13 +14,13 @@ - - + +
-

9 posts tagged with "github-codespaces"

View All Tags

· 10 min read
It's 30DaysOfIA

Explore real-world examples of how Intelligent Apps revolutionize logistics, retail, finance, manufacturing, tech, and medicine.

What We'll Cover:

  • Real-world businesses revolutionizing operations with Intelligent Apps
  • High level architecture for industry scenarios
  • Implementation architecture overview across industries

A decorative header image for the blog

Real-world Success Stories

According to IBM’s 2022 Global AI Adoption Index, 35% of surveyed companies already use AI in their business, and 42% are exploring the idea. Organizations lagging behind the AI curve will quickly fall behind early adopters, who are already enjoying its benefits and boosting their investment in this technology.

In the first part of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” we explored how organizations can use AI-powered apps, or intelligent apps, to transform their operations, decision-making, and customer experiences, leaping ahead of the competition.

Intelligent apps significantly improve operational efficiency and customer engagement across industries like logistics, retail, finance, manufacturing, tech, and healthcare. In this article, we’ll spotlight real-world success stories of businesses revolutionizing operations with intelligent apps using Microsoft Azure’s app, data, and AI services.

info

Register for the Learn Live on September 21 for a live guided experience with SMEs on how to build end-to-end solution architecture for real world implementation of Intelligent Apps.

Streamlining Operations: Intelligent Apps in the Airline Industry

Aerospace pioneer Airbus leveraged intelligent apps to streamline its operations and innovate its services with two ground-breaking modernizations that use Azure apps, data, and AI services to reimagine pilot training and predict aircraft maintenance issues. 

Aviation technology’s rapid evolution compelled Airbus to reconceive their approach to training pilots. The aircraft manufacturer created a pilot training chatbot by combining Azure AI Services with Speech to Text and Text to Speech. The bot helps trainee pilots navigate over 6,000 pages of technical information. Pilots can ask questions verbally for quick answers, aiding their understanding of dense information and speeding up training. And when Airbus needed to accommodate its military consumers, deploying its Kubernetes cluster to a disconnected, “air-gapped” environment met the strict security requirements of military aircraft training.

A chatbot could look like the following diagram as Azure services work together to create a conversational experience. Its components could include security and governance, bot logic and user experience (UX), bot cognition and intelligence, data extract, transfer, and load (ETL) operations, quality assurance and enhancements, logging, and monitoring and reporting.

A diagram of a conversational experience enabled by Azure services

Airbus also used AI to improve its aircraft maintenance and safety. They deployed AI Anomaly Detector to gather and analyze telemetry data from multiple flights, enabling more profound insights into wear and tear on military aircraft operating in harsh conditions. This AI application helps Airbus better understand each airplane’s health. Airbus can predict and fix potential problems before they occur, improving the aircraft’s safety and operational readiness.

AI and Azure are changing how organizations like Airbus operate, even in strict, highly regulated markets with stringent security requirements. And by streamlining pilot training and staying ahead of aircraft maintenance, Airbus helps its military and commercial clients reliably deliver their goods across the globe, maintaining their critical supply chains.

Boosting Customer Service: Intelligent Apps in the Retail Industry

The retail industry has been among the first to adopt and benefit from intelligent apps. For example, the leading American used car retailer, CarMax, uses intelligent apps to transform the car shopping experience.

Before embracing AI, CarMax faced a significant challenge: Manually creating text summaries was time-consuming and labor-intensive. CarMax decided to leverage Azure OpenAI Service to automatically generate car research pages, offering customers valuable insights while enhancing the website’s search engine rankings.

This AI-driven approach has significantly streamlined the process, achieving in a few months what would have taken years manually. Also, the AI system condenses thousands of customer reviews into a few readable sentences, offering potential buyers a quick overview of others’ experiences.

CarMax has improved their customer experience while boosting operational efficiency through AI adoption. CarMax’s editorial staff focuses on creating strategic, longer-form content with the time saved, enhancing productivity, and driving more website traffic.

CarMax’s success story serves as a testament to the potential of intelligent apps in the retail industry.

Enhancing Cybersecurity: Intelligent Apps in Finance

Intelligent apps also impact the highly regulated financial services sector. Swift, a leading infrastructure provider for financial messaging services, uses intelligent apps to enhance decision-making and combat financial crime.

The massive growth of cross-border transactions and instant payment networks led to a surge in financial fraud that can cost the industry hundreds of billions of dollars every year. So, Swift and Microsoft partnered to build an anomaly detection model using federated learning techniques with Azure Machine Learning and Azure confidential computing.

Microsoft Purview helped govern data, as the federated learning method partially trained each model in a silo before aggregating them. This approach enabled Swift and Microsoft to develop a highly accurate model without copying or moving data from Swift members’ secure locations, ensuring the highest level of security and privacy.

Financial organizations can now deploy this trained ML model to run a real-time fraud detection system, lowering the cost of financial fraud.

Organizations can deploy a fraud detection system like the following example on Microsoft Azure. The system ingests data from streams or storage, runs analytics, and uses ML to detect fraud and store results. It then alerts users and other systems to the security issue.

A diagram of an example fraud detection system

Swift provides the innovative model to its banking partners, who can share data to further train the model. Azure confidential computing securely runs new models on current data to accumulate insights, generating an ongoing learning cycle. This iterative approach strengthens the global detection of fraudulent financial transactions through an increasingly faster and more accurate model. 

The collaboration between Swift and Microsoft demonstrates how combining various Azure services creates Intelligent Apps to enhance decision-making in finance. These apps solve real problems at an unprecedented global scale. 

Enhancing Efficiency: Intelligent Apps in Manufacturing

3M, known for Post-it Notes and other innovative products, wielded the power of Intelligent Apps to standardize, automate, and accelerate its sales forecasting. The company needed a unified, automated approach to replace its multiple manual methods.

It adopted Azure Machine Learning and other Azure AI Services to develop, train, and implement 1,500 custom models for each region and division, replacing manual methods. This solution delivers timely finance, sales, and marketing insights while freeing teams from time-consuming manual data manipulation tasks.

3M used Azure Machine Learning, automated machine learning (AutoML), and the Many Models Solution Accelerator to train and score numerous machine learning models in parallel. This approach significantly shortened the company’s development cycle time.

Then, 3M integrated Microsoft Power BI into their solution to compile and visualize results more effectively. Executives now have an unbiased and unfiltered view of data for better decision-making. With its improved ability to forecast sales, 3M can focus on manufacturing the products it needs efficiently.

3M’s success demonstrates how combining various Azure services can enhance efficiency in the manufacturing industry. These apps improve operational processes while enabling employees to focus on higher-level tasks. Strategic decisions are more targeted, and business conversations are more meaningful.

Driving Innovation: Intelligent Apps in the Technology Industry

As expected, the tech industry is at the forefront of leveraging Intelligent Apps to drive innovation. Elastic saw an opportunity to use intelligent apps to optimize its search services and meet customer needs more effectively. The company built a managed, secure, and scalable solution for its customers, Elastic Cloud, by leveraging Microsoft Azure.

Elastic Cloud uses Azure Machine Learning and other Azure AI Services to automate many basic management tasks, such as backing up data, upgrading, and scaling deployments. This approach enables Elastic’s customers to focus less on managing the software and more on serving their own clients. Customers can quickly deploy and manage Elastic Cloud on Kubernetes (ECK) using Azure Kubernetes Service.

As a result, Elastic’s cloud business is growing twice as fast as its on-premises solutions. Enterprise search users have reported significant improvements in system availability and employee satisfaction.

Additionally, Elastic incorporated Azure OpenAI Service into its Elasticsearch Relevance Engine (ESRE), providing more relevant answers to search queries. ESRE enables text, vector, and hybrid search, offering a more effective and accurate response to queries.

Elastic’s success story demonstrates how combining various Azure services can create intelligent Apps to drive innovation in the tech industry. These apps help improve operational processes and deliver better results quickly.

Revolutionizing Healthcare: Intelligent Apps in Medicine

Intelligent apps also drive transformative changes in the healthcare sector. Cambridgeshire and Peterborough NHS Foundation Trust, a healthcare provider in the United Kingdom, gradually shifted their on-premises systems to the cloud using Azure, improving flexibility, and lowering costs. They needed a solution for their electronic patient record (EPR) system that integrates 23 years of data across multiple systems and formats.

The Trust used Microsoft Azure and Azure Cognitive Search, including semantic search for understanding intent and contexts and cognitive capabilities for extracting insights from data, to standardize and automate data management. This approach made legacy, archive, and live data more discoverable, saving clinicians’ time. The Trust’s solution searches through millions of clinical documents in seconds, even recognizing handwritten notes without needing a separate optical character recognition (OCR) solution to extract text from images (Azure Cognitive Search automatically does it). Clinicians can now uncover previously inaccessible insights, enabling them to offer more personalized care.

The Trust also used Integration Services tools such as Azure Logic Apps to create workflows without writing code and Event Grid to connect Azure and third-party services through a publisher-subscriber model. They switched on a live feed from their new EPR system to Azure to feed real-time information into other applications. Researchers can now access anonymized data to fuel medical innovations beyond the NHS.

The healthcare provider also invested heavily in Power BI’s data visualization and analytical functions to sift through massive amounts of information. This level of data accessibility was previously impossible with their on-premises system. The new insights have significantly improved clinicians’ efficiency for better patient care.

Data accessibility and security are critical in the healthcare industry. Intelligent Apps empower organizations like the Trust to revolutionize medical care. AI capabilities helped the Trust improve its operational efficiency and user experiences, setting a new standard for healthcare in the digital age.

Summary

AI is driving transformation and real-world successes across industries. Intelligent apps are reshaping our world by streamlining operations in manufacturing and logistics, enhancing customer experience in the retail sector, improving decision-making in finance, driving innovation in tech, and revolutionizing healthcare.

As organizations adopt AI, they’re innovating in ways that were once too difficult, expensive, or outright impossible. Companies that wait to embrace intelligent apps risk the chance of falling behind. By embracing intelligent apps and leveraging the capabilities of Azure, you can harness the transformative potential of AI to remain on the cutting edge of technology.

Exercise

- - +
Skip to main content

11 posts tagged with "github-codespaces"

View All Tags

· 10 min read
It's 30DaysOfIA

Explore how digital natives and Independent Software Vendors (ISVs) can transform their business operations and generate higher value for customers with generative AI and intelligent apps.

What We'll Cover:

  • Potential of Generative AI in application development
  • Challenges with Generative AI
  • Integrating Generative AI with business operations

image of generative AI and intelligent apps used in business

Unleashing Business Transformation: How Digital Natives Can Leverage Generative AI

Generative AI is no longer a technological flight of fancy. It’s here, and it’s rewriting the rules of business innovation. 

This type of AI leverages machine learning algorithms to create new data with similar characteristics to a given dataset. It goes beyond text generation using generative pretrained transformer (GPT) models, covering an array of applications that span from image synthesis to translations and more. It also powers a new class of intelligent applications, which combine AI capabilities with traditional software functions for enhanced user experiences and advanced business solutions. 

Generative AI is transforming how digital natives and independent software vendors (ISVs) operate. Generative AI offers new possibilities for creating unique business value, enhancing customer experiences, and driving growth. 

It plays a critical role in applications, improving functionality, aiding decision-making, and automating complex tasks to deliver dynamic, uniquely personalized user experiences. Generative AI also enables and automates data-driven outcomes, functions, and features.

Integrating AI capabilities into customer-facing apps and turning them into intelligent apps benefits both the customer and the business, as we discussed in Demystifying Intelligent Applications: Leveraging AI in App Development. You get faster product development workflows and better business intelligence for sales and marketing, while your users get more natural and context-aware features and interactions. 

That said, to realize generative AI’s potential, you need some strategy and knowledge of how and where it fits into your current business and operations. We’ll examine this technology’s potential to transform business operations, its risks, and rewards, and how to integrate it into your existing operations. 

Unpacking the Potential of Generative AI

Generative AI is creating new opportunities in the digital playing field. It empowers businesses to generate insights, design products, and automate processes in ways and at speeds that were previously unimaginable. You can now predict customer behavior more accurately, offer highly personalized user experiences, and prototype innovative ideas rapidly. 

For example, Microsoft used generative AI to reinvent web search in the new AI-powered Bing and Edge. The search experience has evolved from a solitary user flow of typing in keywords and scrolling through results. It’s now a conversational experience where Bing uses context to offer better quality and more personalized search queries and results. 

Similarly, Microsoft harnessed generative AI to transform its productivity suite into intelligent apps. Through Copilot for Microsoft 365, PowerPoint now empowers users to tell their stories by using DALL-E to generate images effortlessly. It also helps rephrase text-heavy slides to be more straightforward. Outlook now assists users to compose more effective emails, enabling better communication.

These are just some examples of how AI powers day-to-day work and operations. 

Businesses that ignore generative AI miss out on untapped market opportunities, letting competitors gain an edge in the digital race. By seeking ways to rethink assumptions and reinvent existing approaches, you can effectively use generative AI to create new opportunities, open products to new use cases and markets, unlock new growth and customer satisfaction levels, and increase your revenue.

info

Register for the intelligent apps webinar on Driving Business Value by Modernizing with Cloud-Native & AI with Microsoft and Forrester on September 26.

Explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries. 

The Crucial Role of Generative AI in Business Operations

Generative AI can be a powerful catalyst for business transformation. Let’s quickly tour some ways it can redefine business operations. 

Product development is traditionally a long and painstaking process. Now, tools like GitHub Copilot and TestPilot use generative AI to make coding and testing software easier, faster, and more robust. 

Similarly, machine learning as a service technology, such as Azure Machine Learning, democratizes the availability of machine learning. It helps collect, clean, and train models across datasets, making engineering and analyzing data more efficient and accessible.  

Generative AI also simplifies the process of creating intelligent apps. Previously, simulating thousands of prototypes, predicting their performance, and narrowing down to the most viable options could take weeks, months, or even years of iteration and data analysis. With generative AI, this process can be as quick as days, hours, or perhaps minutes. 

Customer experience is also ripe for transformation. Personalized recommendations drive engagement and sales, and generative AI can scale that personalization to every customer interaction using comprehensive and continually updated data. For instance, Microsoft leverages AI to enable personalization in Dynamics 365 Customer Insights. Using this tool, you can ask questions about your customers in natural language and gain valuable and actionable insights into your customer base. 

Lastly, marketing and sales can benefit from generative AI. For example, identifying current and future key markets and emerging trends helps companies stay ahead of the curve. Tools like Copilot in Power BI help you quickly understand and uncover insights about your business, enabling your team to craft targeted campaigns, create engaging content, and optimize your business strategy. 

Leveraging this full generative AI potential requires the right tools. GPT-4, for instance, can be instrumental in realizing effective business transformation. It excels in understanding context, generating human-like text, and learning from minimal data input, making it a formidable tool in any digital native's arsenal. Platforms like Azure OpenAI Service bring GPT-4’s power to your fingertips.

However, as with any powerful technology, it’s unwise to treat generative AI as a perfect solution. You must know the challenges and risks of not leveraging it effectively, including technical and human-factor considerations. 

Without generative AI, your company risks missing the opportunity to please customers via personalization, as discussed previously. You’ll miss sales opportunities via business intelligence insights, limiting your ability to grow revenue and keep up with the competition. 

One challenge of leveraging generative AI’s full potential is finding the right tools. Fortunately, you can implement generative AI in your organization without investing in infrastructure, computational power, and expertise. Platforms like Azure OpenAI Service and Azure AI Services make AI easily accessible. They also work with Azure compute and data services like Azure Functions and Azure Kubernetes Service (AKS), enabling you to build intelligent apps easily and quickly. 

However, generative AI can’t do everything. It may need human expertise and intervention to use it fully. 

For example, generative AI can create unique data. However, the data may have limited diversity and complexity. The AI may need help recognizing additional considerations or requirements specific to a problem. It may also lack context due to various factors, such as the information’s freshness. Also, since it’s an emerging field that’s potentially worth $4.4 trillion, we’re still learning about the nuances and potential regulatory impact of generative AI. 

Perhaps the most crucial and challenging aspect of adopting generative AI is an organization’s required cultural shift. Your teams must learn how to use AI as your company fosters an understanding of AI’s potential and limitations. 

Begin integrating AI literacy into your company’s DNA. Knowledge of its potential and the readily available tools can inspire your teams to incorporate generative AI into their daily workflows and business operations quickly, improve productivity, and deliver superior customer experiences. 

No company wants to be outpaced by its competitors. You can balance generative AI’s potential with your business operations by staying aware of and adapting to the dynamic AI landscape.

Achieving Harmony: Integrating Generative AI with Existing Business Operations

Integrating generative AI into existing business operations doesn’t have to be disruptive. With the correct strategy, you can do so seamlessly. Your goal should be to augment and enhance your operations, not disrupt and replace. 

For example, Microsoft Cloud technologies leveraged AI to relieve healthcare organizations’ challenges, such as talent shortage and workforce burnout. Providers automated their workflows, invested in specific applications and connectors, and enhanced support for data and operations. 

Azure AI Services, such as text analytics for health, enable healthcare organizations to understand their largely unstructured and untapped medical data. Project Health Insights leans on AI to help healthcare professionals gain actionable insights and inferences from patient data. 

Azure Health Bot, an Intelligent App, empowers organizations to build and deploy legally compliant, powerful conversational healthcare experiences to patients. 

A successful AI integration like this begins with understanding. Knowing what generative AI can do and setting realistic expectations can help you identify where and how AI can best serve your operations. 

Next, you can foster a culture of AI literacy across teams, speeding up adoption by helping them understand how it can aid their work. The more familiar your team is with AI and its possibilities, the smoother the transition. Training and education programs play a pivotal role here. 

Finally, integration requires a systematic approach. It’s not about overhauling everything at once — it's about identifying areas where AI can add the most value, then gradually experimenting and scaling its use. Successful integration stories often feature a phased approach, starting small and growing steadily. 

As we move toward a future where generative AI is a staple in business operations, remember that every journey is unique. Although there’s no one-size-fits-all solution, a strategic approach and a keen understanding of your business needs will help you navigate this new landscape successfully.

Conclusion

Generative AI offers plenty of opportunities for businesses that embrace it. It’s fueling the next generation of intelligent applications, transforming everything from product development to customer engagement to marketing. This technology is the key to unlocking unprecedented growth and innovation. 

While the journey may have challenges, the potential rewards make generative AI worth exploring. As generative AI advances, Azure AI’s broad range of tools and services will help you navigate this fascinating field.

Exercise

  • Watch Episode 02 of the serverless edition Learn Live session to learn how to build, test and deploy an end-to-end intelligent app solution.
  • Complete the Cloud Skills Challenge to build on your AI skills.
  • Register for Ask the Expert: Azure Functions on September 26 for a live Q&A with the Product Team on how to build AI apps using serverless computing.
+ + \ No newline at end of file diff --git a/30daysofIA/tags/github-codespaces/page/6/index.html b/30daysofIA/tags/github-codespaces/page/6/index.html index ce69e09164..d75c8e3ca2 100644 --- a/30daysofIA/tags/github-codespaces/page/6/index.html +++ b/30daysofIA/tags/github-codespaces/page/6/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "github-codespaces" | Build Intelligent Apps On Azure +11 posts tagged with "github-codespaces" | Build Intelligent Apps On Azure @@ -14,13 +14,13 @@ - - + +
-

9 posts tagged with "github-codespaces"

View All Tags

· 8 min read
It's 30DaysOfIA

Let’s start with understanding the power of intelligent applications.

What We'll Cover:

  • What are intelligent applications?
  • Categories of Intelligent Apps
  • Breaking down the role of AI in intelligent apps

A decorative header image for the blog

Demystifying Intelligent Applications: Leveraging AI in App Development

Until recent years, when you heard the term “artificial intelligence” (AI), you might have pictured autonomous robots from sci-fi films. Now, perhaps your mind jumps to big data and machine learning (ML). Maybe you’re even thinking about the latest ChatGPT plugins and transformer architectures. Today, AI is more than these imaginations: it’s a reality increasingly woven into our day-to-day routines through intelligent applications.

Intelligent apps combine cloud-scale compute, data, and predictive or generative AI. These applications help you automate tasks and make data-driven decisions, aiding in leveraging business intelligence to gain meaningful, practical outcomes. 

For developers, the AI landscape isn’t an abstract complexity. It’s a real, tangible opportunity for evolving traditional applications, making them “intelligent,” and delivering more value to users. And with tools like Azure Kubernetes Service, Azure Cosmos DB, and Azure AI Services, stepping into the world of intelligent apps isn’t a steep climb but a steady trek across a manageable learning curve.

The Three Categories of Intelligent Applications

Before we start building, it’s critical to understand what makes an app “intelligent.” It’s not a universal definition so much as an identifier for apps that fall into one or more of the three primary categories: 

  • Outcome-based — This designation represents the broadest category of intelligent apps, whose principal value for users is the intelligent outcomes they provide. These apps process data to provide insightful results that enhance decision-making or help with tasks like trend prediction, resource management, and customer relationship management.

  • Functionality-based — This type of intelligent app takes AI integration a degree further, integrating AI as a function of the app to create or process its outputs, enhancing the app’s overall interactivity and usefulness.

  • Feature-based — This is the narrowest category, in which the app’s AI/ML component is one of its core features and primary selling points

Let’s dive more deeply into each category.

Outcome-Based Apps: Intelligent Outcomes Drive Success

Outcome-based applications are all about the results provided to the user. They function beyond merely processing data to offer insightful outcomes that drive success.

Think of a personal fitness tracker app that uses AI to analyze the user’s activity and provides actionable recommendations for achieving a goal. This type of app not only tracks your steps, sleep patterns, and heart rate but uses that data to provide you with personalized health advice. It helps you make informed lifestyle choices — the “intelligent” outcome.

Another example is a social media marketing app that uses AI for time-saving data analysis, task automation, and trend prediction. AI-based sentiment analysis can gauge customer perception while natural language processing (NLP) accurately interprets customer inquiries. Task automation can schedule posts and automate responses to common customer queries, and trend forecasting helps model and predict customer behavior to further personalize targeted promotional efforts. Building this type of intelligence into your apps provides cost-effective, actionable insights that guide sales and marketing strategies.

Functionality-Based Apps: AI-Driven Features Enhance User Experiences

As the name suggests, functionality-based applications integrate AI into how the app functions, but the end user drives the action and outcome. In this category of apps, AI takes a more active role in the user’s interaction by allowing them to use AI-created or processed output. These apps leverage AI technologies like Azure AI Services for NLP, image recognition, and pattern recognition to improve user experience.

Consider a music app that generates personalized playlists based on your listening habits or your smartphone’s camera app that uses AI to recognize faces and optimize photo settings. Even more advanced are the language translation apps that translate text from one language to another and can even understand and maintain the context and subtleties of the conversation. They use NLP to accomplish these feats.

Similarly, consider the latest e-commerce apps, whose AI-driven functionalities enable customers to try on clothes virtually, significantly enhancing the online shopping experience.

Feature-Based Apps: Integrating Advanced AI/ML Components

Finally, there are feature-based intelligent apps. These apps incorporate advanced AI/ML components like neural networks, internal large language models (LLMs), and sophisticated algorithms as core features for the user. In this type of application, AI is the primary feature for users to engage with and the primary selling point for the app.

Some examples of feature-based applications are chatbots and virtual agents built using Azure Bot Service. These robo-representatives use AI to provide more natural and human-like conversation, balancing superior efficiency with a friendlier, more personalized user experience. The new Bing is a prime example of this implementation, integrating AI to give users a knowledgeable copilot for web searches.

Similarly, OpenAI’s ChatGPT allows users access to its state-of-the-art language models available as Microsoft’s Azure OpenAI Service. It leverages the power of the highly sophisticated GPT-3 LLM to perform tasks like drafting emails, writing code, answering general knowledge questions, translating between languages, and tutoring in numerous subjects.

These apps don’t just use AI to provide outputs or improve user experiences: They push the boundaries of what’s possible in app development by integrating AI/ML components.

info

Register for the live Q&A Ask The Expert session on September 26 with the Azure Functions product engineering team.

Breaking Down the Role of AI in Intelligent Applications

Intelligent apps reflect the merging of traditional software design with the dynamic capabilities of AI. Let’s explore how AI integrates and amplifies these applications.

At its core, AI augments applications with the capacity to self-learn and self-improve. It allows you to create applications that predict, react, and adapt to a constantly changing environment. In the power grid management system for a major city, an AI-infused application can forecast power usage trends, react to anomalies, and adapt to sudden demand surges, ensuring optimal efficiency and preventing blackouts.

But AI’s role doesn’t end at prediction or decision-making: It also automates tasks. In enterprise solutions, it has moved beyond simple automation to encompass intelligent process automation (IPA). In this arena, AI algorithms understand, learn, and then automate entire digital workflows. For developers, this is a fertile ground for creating applications that can dramatically improve the operational efficiency of businesses.

Even more, AI substantially aids in development tasks. For example, there’s GitHub Copilot, an AI-powered code assistant that offers suggestions based not only on syntax but on development best practices and the intent of the code. AI has evolved from a tool you use to build apps to a partner that helps in creating them. 

Using AI to build intelligent apps isn’t about throwing out what’s worked before. It’s about enhancing the components that have the most impact and achieve the best results. Incorporating AI in apps elevates their predictive abilities, streamlines complex tasks, and even aids in the development and creation process. When you build intelligent apps, you don’t just address current problems. You anticipate future challenges and craft solutions that adapt over time.

Summary

As developers the power of AI is in our hands, and it’s our responsibility to harness its full potential. Each category tells the story of AI's transformative potential, from applications that deliver intelligent outcomes to those with AI capabilities to enrich the user experience and those pushing boundaries with advanced AI/ML technology.

Accessible and robust platforms like Azure AI can simplify the process of developing Intelligent Applications. Whether you’re refining an existing app or building a new one, just entering the world of AI, or already familiar with the landscape, take a moment to appreciate the potential that AI integration offers. You’re embracing this exciting frontier and shaping the future of app development. 

Exercise

  • Complete the Cloud Skills Challenge to build on your apps and AI skills.
  • Watch Episode 01 of the Serverless Edition Learn Live session to learn how to build an end-to-end intelligent app solution.
- - +
Skip to main content

11 posts tagged with "github-codespaces"

View All Tags

· 9 min read
It's 30DaysOfIA

In the part of our series on intelligent apps, we’ll explore how AI is transforming application development, from design and architecture to building.

What We'll Cover:

  • Revolutionizing application development using AI
  • Infusing AI in application design architecture
  • AI Assisted Pair Programming
  • Future of App Development

A decorative image of application development with AI

Reimagining Application Development With AI: A New Paradigm

Artificial intelligence (AI) is impacting every industry. AI-powered development tools offer new functionalities and assist software developers throughout the development process, revolutionizing app development. From initial design and architecture to coding and debugging, AI reshapes how we approach application conception, creation, and iteration.

In part one of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” we explored the new breed of intelligent apps and how they use AI. In this article, we’ll see how AI technologies enhance developer toolkits and shape the future of app development, particularly through AI-enabled coding assistance and debugging.

The Dawn of Intelligent Apps: Revolutionizing Application Development

AI is more than just another addition to the development toolbox—it represents a revolution bringing unprecedented efficiency, innovation, and user engagement to our applications. 

What sets intelligent apps apart is how they can adapt and learn, helping us deliver unique, real-time user experiences.  

One example is an AI-driven health application that can track biometric data, predict potential health issues, provide personalized health advice, and even guide emergency services during a medical crisis. An AI-enabled language learning app that adapts to our learning speeds, recognizes our problem areas, and customizes its content to help us improve our language proficiencies is another example.  

Intelligent apps like these don’t just use data for recommendations—they can make critical decisions and provide sophisticated services that were previously impossible. 

For developers, intelligent apps mean we’re no longer just coding—we’re teaching applications how to think, adapt, and make decisions. AI is reshaping every step of the development journey, helping us to build more intuitive and user-focused products. 

Next, we’ll examine the impact of AI on application design and architecture and how AI-assisted “pair programming” changes software development.

info

Register for the FREE webinar on Intelligent Apps with Microsoft and Forrester on September 26 to explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries.

There will be a showcase of real-world use cases that demonstrate how AI can be seamlessly integrated into cloud-native environments driving tangible business value.

AI in Design and Architecture: Fueling Creativity and Efficiency

AI technologies help developers automate and streamline their processes and provide new ways to think about design.  

In traditional application development, the design and architecture phase can take significant time, effort, and expertise. For instance, adopting a monolithic architecture or a microservices-based design can significantly increase coding, testing, and debugging complexity. The app may even behave differently when hosted locally than in a deployed environment. It may take hours to track down an issue manually—time developers could spend elsewhere. 

Conversely, we can generate numerous design iterations in seconds with AI, each optimized to fulfill specific user needs and business goals. AI tools can quickly model various scenarios' impacts and software design patterns in the architectural design phase. It provides insights into performance and potential issues and suggests improvements. AI can also generate skeleton code, allowing developers to prototype different solutions or test specific components and technologies rapidly. By reviewing thousands of lines of code in seconds, AI allows us to focus on creating apps that serve users through feature development instead of searching for bugs.  

Integrating AI into the design and architecture phase can even extend our creative capabilities. By leveraging the power of generative AI—like large language models (LLMs)—and their broad knowledge and context going beyond software, we can better brainstorm apps and feature ideas. We can go beyond code and software architecture and get ideas about user experience, logo and branding, or marketing concepts. And as AI tools deliver insights and ideas we would have never considered; it encourages more innovation.

info

Watch Episode 01 of the Learn Live session to learn how to build an end-to-end intelligent app solution architecture.

The Power of AI-Assisted Pair Programming in Building Applications

AI-assisted pair programming is a collaborative coding approach where an AI system—like GitHub Copilot or TestPilot—assists developers during coding. It’s an increasingly common approach that significantly impacts developer productivity. In fact, GitHub Copilot is now behind an average of 46 percent of developers’ code and users are seeing 55 percent faster task completion on average. 

For new software developers, or those interested in learning new skills, AI-assisted pair programming are training wheels for coding. With the benefits of code snippet suggestions, developers can avoid struggling with beginner pitfalls like language syntax. Tools like ChatGPT can act as a personal, on-demand tutor—answering questions, generating code samples, and explaining complex code syntax and logic. These tools dramatically speed the learning process and help developers gain confidence in their coding abilities. 

Building applications with AI tools hastens development and provides more robust code. For example, GitHub Copilot helps us write new code accurately. It also quickly makes sense of existing code, suggests whole new lines or blocks of code, and even detects errors and proposes fixes. It’s like having a second coder working alongside us—one who never tires and is trained on billions of lines of code. 

However, it’s also important to acknowledge the limitations of AI. While generative AI-assistance technologies like GitHub Copilot are impressive, they’re still imperfect. They can sometimes produce erroneous, non-functional, or vulnerable code. They can also lack nuanced, domain-specific knowledge.  

So, although these tools are helpful, we can’t rely on them fully—these are programming aids rather than definitive solutions. AI-assisted pair programming is about augmenting the human developer’s skills, not replacing them.

Debugging and Improving Code With AI: Enhancing Application Quality

Identifying and correcting code errors is often time-consuming and challenging for developers. AI streamlines the debugging process by automatically detecting anomalies, suggesting fixes, and learning from previous bugs to prevent them from recurring. 

We can use generative AI to create unit tests from code snippets, from simple parameter checking and input validation to complex mocks of database and network services. Automated AI testing improves code coverage and robustness. It saves significant time compared to manually crafting unit tests. 

AI also revolutionizes code reviews by scanning and highlighting potential issues—like code patterns leading to a bug or a coding standards violation—and suggesting ways to simplify or refactor the code for readability and performance. This reduces human error and makes the code review process more efficient. Its benefits extend to small teams, where the availability of developers is crucial to deadlines, and large teams, where effective collaboration depends on readability and code consistency. AI further saves time by triaging issues so developers can prioritize the most mission-critical ones. 

Aside from improving code quality, AI is critical to understanding system behavior. Intelligent AI-assisted software development tools can monitor and audit system behavior, giving developers a better insight into how their code performs in different scenarios. AI can track patterns, identify inefficiencies, and provide alerts when they detect unusual behavior—enabling developers to enhance their code further. 

By predicting and identifying potential vulnerabilities, intelligent AI-assisted coding apps like Microsoft Security Copilot are pivotal in ensuring application functionality, reliability, and security. These AI tools are continually trained and upgraded with large codebases and user feedback that refines their responses. This ability to learn from past mistakes makes AI an incredibly powerful tool that will continue to expand its capabilities in the future.

info

Complete the AI Cloud Skills Challenge to build on you AI application development skills with paired programming using generative AI and GitHub Copilot.

AI and the Future of Application Development

The rapid advancements in AI and machine learning technologies point to an exciting future for application development. 

In “The Future of Applications,” software developer Mike Cann illustrates how LLMs might revolutionize application development. Cann that suggests fully-autonomous coding—AI-generated applications created entirely from prompts—may supersede traditional hand-crafted applications. Dynamically generated user interfaces could offer personalized and customizable experiences while we can design APIs for machine consumption and efficiency rather than human readability. Furthermore, we can store data in less structured, more adaptable, and flexible forms. 

AI is already making application development more accessible. With AI handling the complex, technical aspects of development, less-technical individuals can take a greater role in building applications. Consequently, AI can potentially democratize software engineering. 

Intelligent apps will continue to evolve, becoming more efficient, adaptable, and capable of learning from user interactions. Similarly, AI will inevitably become integral in every stage of app development.

Summary

AI has already created a new development paradigm—one in which machine intelligence and human creativity come together to produce innovative, efficient, and robust applications. AI already assists us at every stage of app development, from design and architecture to coding and debugging. 

Developers who embrace AI tools and integrate them into their processes can enhance their productivity, improve the quality of their applications, and expand the possibilities of their application development.  

The future of development is automated and AI-driven, and it’s here today. Getting started is easy—try an AI-powered development tool like GitHub Copilot to see how AI-assisted pair programming can enhance your productivity and code quality.

Exercise

+ + \ No newline at end of file diff --git a/30daysofIA/tags/github-codespaces/page/7/index.html b/30daysofIA/tags/github-codespaces/page/7/index.html index 1421c7dbc0..40a166385a 100644 --- a/30daysofIA/tags/github-codespaces/page/7/index.html +++ b/30daysofIA/tags/github-codespaces/page/7/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "github-codespaces" | Build Intelligent Apps On Azure +11 posts tagged with "github-codespaces" | Build Intelligent Apps On Azure @@ -14,14 +14,13 @@ - - + +
-

9 posts tagged with "github-codespaces"

View All Tags

· 4 min read
It's 30DaysOfIA

This Fall focus on building intelligent apps using AI and cloud-native technologies. #FallForIntelligentApps brings to you a learning journey to build your skills on creating differentiated experiences while modernizing your applications. It’s time to learn it all.

What We'll Cover

image

Get Ready To #FallForIntelligentApps starting September 18!

Today, we kick off the Fall season with content and activities to skill you up on all things Intelligent Apps or AI Apps on Azure with content, events, and community interactions! Read on to learn about what is coming!

Explore Our Initiatives

We have a number of initiatives planned for the month to help you learn and skill up on relevant technologies. Click on the links to visit the relevant pages for each

image

Register for the events!

What are 4 things you can do today, to jumpstart your learning journey?

#30Days Of Intelligent Apps

#30DaysOfIA is a series of daily blog posts grouped into themed weeks - taking you from core concepts to end-to-end solution examples in 30 days. Each article will provide conceptual lessons paired with exercises and resources to help you reinforce learnings and take next steps.

This series takes you through learning journey in six stages, each building on the previous week to help you skill up in a beginner-friendly way:

We will start with defining intelligent apps and then expand on how to build with cloud-native technologies like Azure Kubernetes Service, Azure Container Apps and Azure Functions, and take you through end-to-end scenarios for real world application development. Before we dive deep on intelligent apps, here is a high-level overview of the Intelligent Apps landscape on Azure:

image

Containers on Azure services offer you a wide range of capabilities, from simplicity to control to suit your different needs.

image -image

To start with the basics for developing Kubernetes applications, explore #30DaysOfCloudNative.

Cloud-native development when paired with serverless computing enhances your solution architecture for building cost optimized, resilient applications.

image

To start with the basics for serverless computing, explore #30DaysOfServerless.

Let’s Get Started

Now you know everything! We hope you are as excited as we are to dive into a full month of active learning and doing! Don't forget to subscribe for updates in your favorite feed reader! And look out for our first Intelligent Apps post Monday!

- - +
Skip to main content

11 posts tagged with "github-codespaces"

View All Tags

· 10 min read
It's 30DaysOfIA

Explore real-world examples of how Intelligent Apps revolutionize logistics, retail, finance, manufacturing, tech, and medicine.

What We'll Cover:

  • Real-world businesses revolutionizing operations with Intelligent Apps
  • High level architecture for industry scenarios
  • Implementation architecture overview across industries

A decorative header image for the blog

Real-world Success Stories

According to IBM’s 2022 Global AI Adoption Index, 35% of surveyed companies already use AI in their business, and 42% are exploring the idea. Organizations lagging behind the AI curve will quickly fall behind early adopters, who are already enjoying its benefits and boosting their investment in this technology.

In the first part of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” we explored how organizations can use AI-powered apps, or intelligent apps, to transform their operations, decision-making, and customer experiences, leaping ahead of the competition.

Intelligent apps significantly improve operational efficiency and customer engagement across industries like logistics, retail, finance, manufacturing, tech, and healthcare. In this article, we’ll spotlight real-world success stories of businesses revolutionizing operations with intelligent apps using Microsoft Azure’s app, data, and AI services.

info

Register for the Learn Live on September 21 for a live guided experience with SMEs on how to build end-to-end solution architecture for real world implementation of Intelligent Apps.

Streamlining Operations: Intelligent Apps in the Airline Industry

Aerospace pioneer Airbus leveraged intelligent apps to streamline its operations and innovate its services with two ground-breaking modernizations that use Azure apps, data, and AI services to reimagine pilot training and predict aircraft maintenance issues. 

Aviation technology’s rapid evolution compelled Airbus to reconceive their approach to training pilots. The aircraft manufacturer created a pilot training chatbot by combining Azure AI Services with Speech to Text and Text to Speech. The bot helps trainee pilots navigate over 6,000 pages of technical information. Pilots can ask questions verbally for quick answers, aiding their understanding of dense information and speeding up training. And when Airbus needed to accommodate its military consumers, deploying its Kubernetes cluster to a disconnected, “air-gapped” environment met the strict security requirements of military aircraft training.

A chatbot could look like the following diagram as Azure services work together to create a conversational experience. Its components could include security and governance, bot logic and user experience (UX), bot cognition and intelligence, data extract, transfer, and load (ETL) operations, quality assurance and enhancements, logging, and monitoring and reporting.

A diagram of a conversational experience enabled by Azure services

Airbus also used AI to improve its aircraft maintenance and safety. They deployed AI Anomaly Detector to gather and analyze telemetry data from multiple flights, enabling more profound insights into wear and tear on military aircraft operating in harsh conditions. This AI application helps Airbus better understand each airplane’s health. Airbus can predict and fix potential problems before they occur, improving the aircraft’s safety and operational readiness.

AI and Azure are changing how organizations like Airbus operate, even in strict, highly regulated markets with stringent security requirements. And by streamlining pilot training and staying ahead of aircraft maintenance, Airbus helps its military and commercial clients reliably deliver their goods across the globe, maintaining their critical supply chains.

Boosting Customer Service: Intelligent Apps in the Retail Industry

The retail industry has been among the first to adopt and benefit from intelligent apps. For example, the leading American used car retailer, CarMax, uses intelligent apps to transform the car shopping experience.

Before embracing AI, CarMax faced a significant challenge: Manually creating text summaries was time-consuming and labor-intensive. CarMax decided to leverage Azure OpenAI Service to automatically generate car research pages, offering customers valuable insights while enhancing the website’s search engine rankings.

This AI-driven approach has significantly streamlined the process, achieving in a few months what would have taken years manually. Also, the AI system condenses thousands of customer reviews into a few readable sentences, offering potential buyers a quick overview of others’ experiences.

CarMax has improved their customer experience while boosting operational efficiency through AI adoption. CarMax’s editorial staff focuses on creating strategic, longer-form content with the time saved, enhancing productivity, and driving more website traffic.

CarMax’s success story serves as a testament to the potential of intelligent apps in the retail industry.

Enhancing Cybersecurity: Intelligent Apps in Finance

Intelligent apps also impact the highly regulated financial services sector. Swift, a leading infrastructure provider for financial messaging services, uses intelligent apps to enhance decision-making and combat financial crime.

The massive growth of cross-border transactions and instant payment networks led to a surge in financial fraud that can cost the industry hundreds of billions of dollars every year. So, Swift and Microsoft partnered to build an anomaly detection model using federated learning techniques with Azure Machine Learning and Azure confidential computing.

Microsoft Purview helped govern data, as the federated learning method partially trained each model in a silo before aggregating them. This approach enabled Swift and Microsoft to develop a highly accurate model without copying or moving data from Swift members’ secure locations, ensuring the highest level of security and privacy.

Financial organizations can now deploy this trained ML model to run a real-time fraud detection system, lowering the cost of financial fraud.

Organizations can deploy a fraud detection system like the following example on Microsoft Azure. The system ingests data from streams or storage, runs analytics, and uses ML to detect fraud and store results. It then alerts users and other systems to the security issue.

A diagram of an example fraud detection system

Swift provides the innovative model to its banking partners, who can share data to further train the model. Azure confidential computing securely runs new models on current data to accumulate insights, generating an ongoing learning cycle. This iterative approach strengthens the global detection of fraudulent financial transactions through an increasingly faster and more accurate model. 

The collaboration between Swift and Microsoft demonstrates how combining various Azure services creates Intelligent Apps to enhance decision-making in finance. These apps solve real problems at an unprecedented global scale. 

Enhancing Efficiency: Intelligent Apps in Manufacturing

3M, known for Post-it Notes and other innovative products, wielded the power of Intelligent Apps to standardize, automate, and accelerate its sales forecasting. The company needed a unified, automated approach to replace its multiple manual methods.

It adopted Azure Machine Learning and other Azure AI Services to develop, train, and implement 1,500 custom models for each region and division, replacing manual methods. This solution delivers timely finance, sales, and marketing insights while freeing teams from time-consuming manual data manipulation tasks.

3M used Azure Machine Learning, automated machine learning (AutoML), and the Many Models Solution Accelerator to train and score numerous machine learning models in parallel. This approach significantly shortened the company’s development cycle time.

Then, 3M integrated Microsoft Power BI into their solution to compile and visualize results more effectively. Executives now have an unbiased and unfiltered view of data for better decision-making. With its improved ability to forecast sales, 3M can focus on manufacturing the products it needs efficiently.

3M’s success demonstrates how combining various Azure services can enhance efficiency in the manufacturing industry. These apps improve operational processes while enabling employees to focus on higher-level tasks. Strategic decisions are more targeted, and business conversations are more meaningful.

Driving Innovation: Intelligent Apps in the Technology Industry

As expected, the tech industry is at the forefront of leveraging Intelligent Apps to drive innovation. Elastic saw an opportunity to use intelligent apps to optimize its search services and meet customer needs more effectively. The company built a managed, secure, and scalable solution for its customers, Elastic Cloud, by leveraging Microsoft Azure.

Elastic Cloud uses Azure Machine Learning and other Azure AI Services to automate many basic management tasks, such as backing up data, upgrading, and scaling deployments. This approach enables Elastic’s customers to focus less on managing the software and more on serving their own clients. Customers can quickly deploy and manage Elastic Cloud on Kubernetes (ECK) using Azure Kubernetes Service.

As a result, Elastic’s cloud business is growing twice as fast as its on-premises solutions. Enterprise search users have reported significant improvements in system availability and employee satisfaction.

Additionally, Elastic incorporated Azure OpenAI Service into its Elasticsearch Relevance Engine (ESRE), providing more relevant answers to search queries. ESRE enables text, vector, and hybrid search, offering a more effective and accurate response to queries.

Elastic’s success story demonstrates how combining various Azure services can create intelligent Apps to drive innovation in the tech industry. These apps help improve operational processes and deliver better results quickly.

Revolutionizing Healthcare: Intelligent Apps in Medicine

Intelligent apps also drive transformative changes in the healthcare sector. Cambridgeshire and Peterborough NHS Foundation Trust, a healthcare provider in the United Kingdom, gradually shifted their on-premises systems to the cloud using Azure, improving flexibility, and lowering costs. They needed a solution for their electronic patient record (EPR) system that integrates 23 years of data across multiple systems and formats.

The Trust used Microsoft Azure and Azure Cognitive Search, including semantic search for understanding intent and contexts and cognitive capabilities for extracting insights from data, to standardize and automate data management. This approach made legacy, archive, and live data more discoverable, saving clinicians’ time. The Trust’s solution searches through millions of clinical documents in seconds, even recognizing handwritten notes without needing a separate optical character recognition (OCR) solution to extract text from images (Azure Cognitive Search automatically does it). Clinicians can now uncover previously inaccessible insights, enabling them to offer more personalized care.

The Trust also used Integration Services tools such as Azure Logic Apps to create workflows without writing code and Event Grid to connect Azure and third-party services through a publisher-subscriber model. They switched on a live feed from their new EPR system to Azure to feed real-time information into other applications. Researchers can now access anonymized data to fuel medical innovations beyond the NHS.

The healthcare provider also invested heavily in Power BI’s data visualization and analytical functions to sift through massive amounts of information. This level of data accessibility was previously impossible with their on-premises system. The new insights have significantly improved clinicians’ efficiency for better patient care.

Data accessibility and security are critical in the healthcare industry. Intelligent Apps empower organizations like the Trust to revolutionize medical care. AI capabilities helped the Trust improve its operational efficiency and user experiences, setting a new standard for healthcare in the digital age.

Summary

AI is driving transformation and real-world successes across industries. Intelligent apps are reshaping our world by streamlining operations in manufacturing and logistics, enhancing customer experience in the retail sector, improving decision-making in finance, driving innovation in tech, and revolutionizing healthcare.

As organizations adopt AI, they’re innovating in ways that were once too difficult, expensive, or outright impossible. Companies that wait to embrace intelligent apps risk the chance of falling behind. By embracing intelligent apps and leveraging the capabilities of Azure, you can harness the transformative potential of AI to remain on the cutting edge of technology.

Exercise

+ + \ No newline at end of file diff --git a/30daysofIA/tags/github-codespaces/page/8/index.html b/30daysofIA/tags/github-codespaces/page/8/index.html index d76206e8ed..126a6abc70 100644 --- a/30daysofIA/tags/github-codespaces/page/8/index.html +++ b/30daysofIA/tags/github-codespaces/page/8/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "github-codespaces" | Build Intelligent Apps On Azure +11 posts tagged with "github-codespaces" | Build Intelligent Apps On Azure @@ -14,14 +14,13 @@ - - + +
-

9 posts tagged with "github-codespaces"

View All Tags

· 4 min read
It's 30DaysOfIA

Continue The Learning Journey through Fall For Intelligent Apps! 🍂

What We'll Cover

Thank you! ♥️

image

It's hard to believe that JavaScript on Azure hack-together is ending! It seems like just yesterday that we launched this initiative, and yet here we are, 15 days later, with an incredible amount of learning and growth behind us. So... it's time for a wrap!

From the bottom of our hearts, we want to thank each and every one of you for your participation, engagement, and enthusiasm. It's been truly inspiring to see the passion and dedication from this strong community, and we're honored to be a part of it. ✨

Recap of The JavaScript on Azure Global Hack-Together

As we wrap up this exciting event, we wanted to take a moment to reflect on all that we've accomplished together. Over the last 15 days, we've covered a lot of ground, from the basics of contributing to Open source to the exploration of the Contoso Real Estate project from its Frontend to its Backend and future AI implementation.

Now that the hack-together is ending, we want to make sure that you have all the resources you need to continue honing your skills in the future. Whether you're looking to make your fist contribution to open source, become an open source maintainers, collaborate with others, or simply keep learning, there are plenty of resources out there to help you achieve your goals. So, let's dive in and explore all the ways you can continue to grow your JavaScript skills on Azure!

JSonAzure Hack-together Roadmap 📍:

hack-together-roadmap (2)

Recap on past Livestreams🌟:

Day 1️⃣: Opening Keynote (Hack-together Launch): Introduction to the Contoso Real Estate Open-source project!, managing complex and complex enterprise architecture, new announcements for JavaScript developers on Azure

Day 2️⃣: GitHub Copilot & Codespaces: Introduction to your AI pair programmer (GitHub Copilot) and your virtual developer environment on the cloud (GitHub Codespaces)

Day 6️⃣: Build your Frontend using Static Web Apps as part of a complex, modern composable frontends (or micro-frontends) and cloud-native applications.

Day 9️⃣: Build a Serverless Backend using Azure Functions

Day 1️⃣3️⃣: Easily connect to an Azure Cosmos DB, exploring its benefits and how to get started

Day 1️⃣5️⃣: Being in the AI Era, we dive into the Azure OpenAI Service and how you can start to build intelligent JavaScript applications

📖 Self-Learning Resources

  1. JavaScript on Azure Global Hack Together Module collection
  2. Lets #HackTogether: Javascript On Azure Keynote
  3. Step by Step Guide: Migrating v3 to v4 programming model for Azure Functions for Node.Js Application

Continue your journey with #FallForIntelligentApps

Join us this Fall on a learning journey to explore building intelligent apps. Combine the power of AI, cloud-native app development, and cloud-scale data to build highly differentiated digital experiences. Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure. Engage in a self-paced learning adventure by following along the #30Days of Intelligent Apps series, completing the Intelligent Apps Skills Challenge, joining the Product Group for a live Ask The Expert series or building an end to end solution architecture with a live guided experience through the Learn Live series. Discover more here.

Hands-on practice: Make your first contribution to open source!

Join our GitHUb Discussion Forum to connect with developers from every part of world, see contributions from other, find collaborators and make your first contribution to a real-world project! -Don't forget to give the repo a star ⭐

Resources

All resources are accessible on our landing page

- - +
Skip to main content

11 posts tagged with "github-codespaces"

View All Tags

· 8 min read
It's 30DaysOfIA

Let’s start with understanding the power of intelligent applications.

What We'll Cover:

  • What are intelligent applications?
  • Categories of Intelligent Apps
  • Breaking down the role of AI in intelligent apps

A decorative header image for the blog

Demystifying Intelligent Applications: Leveraging AI in App Development

Until recent years, when you heard the term “artificial intelligence” (AI), you might have pictured autonomous robots from sci-fi films. Now, perhaps your mind jumps to big data and machine learning (ML). Maybe you’re even thinking about the latest ChatGPT plugins and transformer architectures. Today, AI is more than these imaginations: it’s a reality increasingly woven into our day-to-day routines through intelligent applications.

Intelligent apps combine cloud-scale compute, data, and predictive or generative AI. These applications help you automate tasks and make data-driven decisions, aiding in leveraging business intelligence to gain meaningful, practical outcomes. 

For developers, the AI landscape isn’t an abstract complexity. It’s a real, tangible opportunity for evolving traditional applications, making them “intelligent,” and delivering more value to users. And with tools like Azure Kubernetes Service, Azure Cosmos DB, and Azure AI Services, stepping into the world of intelligent apps isn’t a steep climb but a steady trek across a manageable learning curve.

The Three Categories of Intelligent Applications

Before we start building, it’s critical to understand what makes an app “intelligent.” It’s not a universal definition so much as an identifier for apps that fall into one or more of the three primary categories: 

  • Outcome-based — This designation represents the broadest category of intelligent apps, whose principal value for users is the intelligent outcomes they provide. These apps process data to provide insightful results that enhance decision-making or help with tasks like trend prediction, resource management, and customer relationship management.

  • Functionality-based — This type of intelligent app takes AI integration a degree further, integrating AI as a function of the app to create or process its outputs, enhancing the app’s overall interactivity and usefulness.

  • Feature-based — This is the narrowest category, in which the app’s AI/ML component is one of its core features and primary selling points

Let’s dive more deeply into each category.

Outcome-Based Apps: Intelligent Outcomes Drive Success

Outcome-based applications are all about the results provided to the user. They function beyond merely processing data to offer insightful outcomes that drive success.

Think of a personal fitness tracker app that uses AI to analyze the user’s activity and provides actionable recommendations for achieving a goal. This type of app not only tracks your steps, sleep patterns, and heart rate but uses that data to provide you with personalized health advice. It helps you make informed lifestyle choices — the “intelligent” outcome.

Another example is a social media marketing app that uses AI for time-saving data analysis, task automation, and trend prediction. AI-based sentiment analysis can gauge customer perception while natural language processing (NLP) accurately interprets customer inquiries. Task automation can schedule posts and automate responses to common customer queries, and trend forecasting helps model and predict customer behavior to further personalize targeted promotional efforts. Building this type of intelligence into your apps provides cost-effective, actionable insights that guide sales and marketing strategies.

Functionality-Based Apps: AI-Driven Features Enhance User Experiences

As the name suggests, functionality-based applications integrate AI into how the app functions, but the end user drives the action and outcome. In this category of apps, AI takes a more active role in the user’s interaction by allowing them to use AI-created or processed output. These apps leverage AI technologies like Azure AI Services for NLP, image recognition, and pattern recognition to improve user experience.

Consider a music app that generates personalized playlists based on your listening habits or your smartphone’s camera app that uses AI to recognize faces and optimize photo settings. Even more advanced are the language translation apps that translate text from one language to another and can even understand and maintain the context and subtleties of the conversation. They use NLP to accomplish these feats.

Similarly, consider the latest e-commerce apps, whose AI-driven functionalities enable customers to try on clothes virtually, significantly enhancing the online shopping experience.

Feature-Based Apps: Integrating Advanced AI/ML Components

Finally, there are feature-based intelligent apps. These apps incorporate advanced AI/ML components like neural networks, internal large language models (LLMs), and sophisticated algorithms as core features for the user. In this type of application, AI is the primary feature for users to engage with and the primary selling point for the app.

Some examples of feature-based applications are chatbots and virtual agents built using Azure Bot Service. These robo-representatives use AI to provide more natural and human-like conversation, balancing superior efficiency with a friendlier, more personalized user experience. The new Bing is a prime example of this implementation, integrating AI to give users a knowledgeable copilot for web searches.

Similarly, OpenAI’s ChatGPT allows users access to its state-of-the-art language models available as Microsoft’s Azure OpenAI Service. It leverages the power of the highly sophisticated GPT-3 LLM to perform tasks like drafting emails, writing code, answering general knowledge questions, translating between languages, and tutoring in numerous subjects.

These apps don’t just use AI to provide outputs or improve user experiences: They push the boundaries of what’s possible in app development by integrating AI/ML components.

info

Register for the live Q&A Ask The Expert session on September 26 with the Azure Functions product engineering team.

Breaking Down the Role of AI in Intelligent Applications

Intelligent apps reflect the merging of traditional software design with the dynamic capabilities of AI. Let’s explore how AI integrates and amplifies these applications.

At its core, AI augments applications with the capacity to self-learn and self-improve. It allows you to create applications that predict, react, and adapt to a constantly changing environment. In the power grid management system for a major city, an AI-infused application can forecast power usage trends, react to anomalies, and adapt to sudden demand surges, ensuring optimal efficiency and preventing blackouts.

But AI’s role doesn’t end at prediction or decision-making: It also automates tasks. In enterprise solutions, it has moved beyond simple automation to encompass intelligent process automation (IPA). In this arena, AI algorithms understand, learn, and then automate entire digital workflows. For developers, this is a fertile ground for creating applications that can dramatically improve the operational efficiency of businesses.

Even more, AI substantially aids in development tasks. For example, there’s GitHub Copilot, an AI-powered code assistant that offers suggestions based not only on syntax but on development best practices and the intent of the code. AI has evolved from a tool you use to build apps to a partner that helps in creating them. 

Using AI to build intelligent apps isn’t about throwing out what’s worked before. It’s about enhancing the components that have the most impact and achieve the best results. Incorporating AI in apps elevates their predictive abilities, streamlines complex tasks, and even aids in the development and creation process. When you build intelligent apps, you don’t just address current problems. You anticipate future challenges and craft solutions that adapt over time.

Summary

As developers the power of AI is in our hands, and it’s our responsibility to harness its full potential. Each category tells the story of AI's transformative potential, from applications that deliver intelligent outcomes to those with AI capabilities to enrich the user experience and those pushing boundaries with advanced AI/ML technology.

Accessible and robust platforms like Azure AI can simplify the process of developing Intelligent Applications. Whether you’re refining an existing app or building a new one, just entering the world of AI, or already familiar with the landscape, take a moment to appreciate the potential that AI integration offers. You’re embracing this exciting frontier and shaping the future of app development. 

Exercise

  • Complete the Cloud Skills Challenge to build on your apps and AI skills.
  • Watch Episode 01 of the Serverless Edition Learn Live session to learn how to build an end-to-end intelligent app solution.
+ + \ No newline at end of file diff --git a/30daysofIA/tags/github-codespaces/page/9/index.html b/30daysofIA/tags/github-codespaces/page/9/index.html index 6cede04b74..d6a31fcac6 100644 --- a/30daysofIA/tags/github-codespaces/page/9/index.html +++ b/30daysofIA/tags/github-codespaces/page/9/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "github-codespaces" | Build Intelligent Apps On Azure +11 posts tagged with "github-codespaces" | Build Intelligent Apps On Azure @@ -14,13 +14,14 @@ - - + +
-

9 posts tagged with "github-codespaces"

View All Tags

· 2 min read
It's 30DaysOfIA

September is almost here - and that can only mean one thing!! It's time to 🍂 Fall for something new and exciting and spend a few weeks skilling up on relevant tools, techologies and solutions!!

Last year, we focused on #ServerlessSeptember. This year, we're building on that theme with the addition of cloud-scale Data, cloud-native Technologies and cloud-based AI integrations to help you modernize and build intelligent apps for the enterprise!

Watch this space - and join us in September to learn more!

- - +
Skip to main content

11 posts tagged with "github-codespaces"

View All Tags

· 4 min read
It's 30DaysOfIA

This Fall focus on building intelligent apps using AI and cloud-native technologies. #FallForIntelligentApps brings to you a learning journey to build your skills on creating differentiated experiences while modernizing your applications. It’s time to learn it all.

What We'll Cover

image

Get Ready To #FallForIntelligentApps starting September 18!

Today, we kick off the Fall season with content and activities to skill you up on all things Intelligent Apps or AI Apps on Azure with content, events, and community interactions! Read on to learn about what is coming!

Explore Our Initiatives

We have a number of initiatives planned for the month to help you learn and skill up on relevant technologies. Click on the links to visit the relevant pages for each

image

Register for the events!

What are 4 things you can do today, to jumpstart your learning journey?

#30Days Of Intelligent Apps

#30DaysOfIA is a series of daily blog posts grouped into themed weeks - taking you from core concepts to end-to-end solution examples in 30 days. Each article will provide conceptual lessons paired with exercises and resources to help you reinforce learnings and take next steps.

This series takes you through learning journey in six stages, each building on the previous week to help you skill up in a beginner-friendly way:

We will start with defining intelligent apps and then expand on how to build with cloud-native technologies like Azure Kubernetes Service, Azure Container Apps and Azure Functions, and take you through end-to-end scenarios for real world application development. Before we dive deep on intelligent apps, here is a high-level overview of the Intelligent Apps landscape on Azure:

image

Containers on Azure services offer you a wide range of capabilities, from simplicity to control to suit your different needs.

image +image

To start with the basics for developing Kubernetes applications, explore #30DaysOfCloudNative.

Cloud-native development when paired with serverless computing enhances your solution architecture for building cost optimized, resilient applications.

image

To start with the basics for serverless computing, explore #30DaysOfServerless.

Let’s Get Started

Now you know everything! We hope you are as excited as we are to dive into a full month of active learning and doing! Don't forget to subscribe for updates in your favorite feed reader! And look out for our first Intelligent Apps post Monday!

+ + \ No newline at end of file diff --git a/30daysofIA/tags/github-copilot/index.html b/30daysofIA/tags/github-copilot/index.html index 0b6a320df5..d00e972c22 100644 --- a/30daysofIA/tags/github-copilot/index.html +++ b/30daysofIA/tags/github-copilot/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "github-copilot" | Build Intelligent Apps On Azure +11 posts tagged with "github-copilot" | Build Intelligent Apps On Azure @@ -14,13 +14,13 @@ - - + +
-

9 posts tagged with "github-copilot"

View All Tags

· 10 min read
It's 30DaysOfIA

Prepare your organization to adopt intelligent apps by taking a change management approach. Assess your readiness, upskill teams, create a culture of change and overcome resistance, and prepare for future evolution to pave the way for a smooth transition into an AI-centric company.

What We'll Cover:

  • Role of change management in modernizing to intelligent apps
  • Skills needed for building intelligent apps in your organization
  • How to build a culture of change to embrace AI
  • How to prepare for the future of intelligent apps and continuous evolution

image of modernizing AI solutions for intelligent apps

Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management

As we’ve discussed throughout this series, intelligent apps and generative AI are rewriting the rules of business and application development. The first article of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” explored how intelligent apps can enhance user experiences and drive businesses growth. These advancements are opening new opportunities and markets for companies ready to embrace them.

However, embracing this digital transformation is more complex than flipping a switch. It requires adjustments to the technical side, which we explored in the series’ fifth article, “Preparing the Path for Intelligent Apps: Transitioning from On-Premises/IaaS to Cloud Native Applications.” It also demands a shift in how organizations adapt to change and approach app design and development. Let’s explore that mindset transition with tips to help you make it happen.

The Role of Change Management in Transitioning to Intelligent Apps

Transitioning to intelligent apps isn’t merely a technological change. It’s a profound shift in how businesses perceive and operate. This move toward artificial intelligence (AI)-centric approach isn’t meant to replace humans with machines or radically overhaul all business operations.

Instead, intelligent apps should augment human capabilities, streamline processes, and foster an environment where innovation flourishes. They expand toolkits and techniques to include AI-assisted development tools like GitHub Copilot and AI-enabled workflows, helping us design and deliver AI-enhanced experiences.

Effective change management strategies are crucial in helping employees adapt to these new ways of working with intelligent apps, ensuring that the transition doesn’t disrupt productivity or morale.

Team and organization strategies should: 

  • Assess the impact of any proposed changes.  
  • Communicate the benefits and reasons behind the change to all affected team members.  
  • Provide the necessary training and support.  
  • Integrate the changes into the culture and daily operations. 

By incorporating this robust change management approach, you can identify and mitigate potential transition risks early. Moreover, this strategy ensures that employees are engaged throughout the process, fostering a sense of involvement and ownership. This, in turn, cultivates a culture suited for innovation through intelligent apps. 

These organizational changes focus on the following aspects: organizational readiness, skillset, culture of change and overcoming resistance, and adaptability. Let’s explore them below.

info

Register for the intelligent apps webinar on Driving Business Value by Modernizing with Cloud-Native & AI with Microsoft and Forrester on September 26. 

Explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries. 

Identifying Organizational Readiness for Intelligent Apps

The first step in preparing your organization for intelligent apps is to assess your current readiness for change. Analyze various elements of your organization, from infrastructure and workforce skills to strategic vision and overall culture.

Can your apps and systems integrate with AI platforms like Microsoft Azure AI? Is your workforce comfortable working alongside AI, or are they fearful and resistant? Does your strategic vision incorporate AI’s transformative potential, or does it fail to acknowledge AI as a critical driver of future growth?

These questions identify the challenges of integrating AI into an organization and determine the company’s readiness for intelligent apps.

Embracing intelligent apps involves more than being ready with your technological infrastructure or having the right technology or tools. You need flexible organizational structures and processes driving your business to accommodate new ideas and capabilities. Your teams must feel empowered to own and drive them. Much like Microsoft’s shift to AI-powered services—like AI-powered Bing and Edge and Microsoft 365 Copilot—embracing AI organization-wide requires a holistic vision and willingness to innovate, with teams capable of delivering on the plan.

After accessing these critical areas for organizational readiness, you can start planning a strategy for this transition to an AI-ready organization. Identify skill gaps and work to bridge them while nurturing an environment that appreciates and harnesses the potential of AI and intelligent apps.

Nurturing the Indispensable Skills for Intelligent Apps

Tasking your AI specialists with preparing your organization to use and build intelligent apps isn’t enough. Accomplishing this goal requires a cross-functional effort and a diverse skill set.

Developing intelligent apps involves data scientists managing and interpreting vast amounts of data, machine learning engineers building and training the models, and traditional developers integrating these models into usable applications. However, the impact of generative AI spans beyond creating apps. AI fluency across the entire organization—from sales and marketing to accounting and legal departments—offers substantial benefits, sparking new ideas and solutions from sometimes unexpected sources.

Teams' AI skills might roughly fall into three levels: 

  • Fundamental skills — Being familiar and efficient with using AI tools, for example, knowing how to write effective prompts for large language models (LLMs).
  • Visionary skills — Recognizing AI’s strengths and weaknesses, for example, its creativity and hallucinations, that is, unexpected output.
  • Expert skills — Managing data and training models and integrating AI technology into apps, such as incorporating the OpenAI application programming interface (API) into an Intelligent App.

Based on these levels, you can gauge your team's skill readiness to pinpoint the most significant gaps, whether in general AI knowledge across your organization or within technical development teams.

As intelligent apps become increasingly prevalent, upskilling your workforce is crucial. This transition opens a new realm of skills and capabilities for your team to master. You can offer machine learning, data analysis, and cloud computing training programs—such as Microsoft’s Azure AI Fundamentals certification—to empower your team to confidently navigate the intelligent apps landscape.

Nurturing these skills within your organization may require hiring new talent, reskilling current employees, and creating an environment that promotes learning and experimentation. Specifically, if your team understands tools like the Microsoft Azure AI platform, they could contribute significantly to developing and maintaining intelligent apps.

An upskilled team is more than a functional unit. It’s a think tank to drive innovation and improve your organization’s competitive standing in the digital marketplace.

Building a Culture of Change to Embrace AI

Even with the right people and skills in place, change remains an uphill battle for organizations committed to traditional processes. 

Transitioning to intelligent apps is as much about mindset as technology and skills. It requires fostering a culture open to change and innovation that understands AI’s transformative potential and embraces it to enhance rather than replace human capabilities. 

Leaders play a pivotal role in cultivating this mindset. As they model a positive attitude toward AI, emphasize its benefits, and address any concerns transparently, leaders significantly contribute to the organization’s readiness to adopt intelligent apps. 

The most critical factor in this transition is enabling an AI-friendly culture. It’s about encouraging curiosity, rewarding innovative thinking, and creating an environment where people thrive on learning and experimentation. 

Empowering teams to leverage practical AI tools and AI-driven data insights to aid decision-making helps motivate employees to use and explore AI’s potential, improve their skills, and contribute to the successful integration of intelligent apps. Such an environment is fertile ground to grow breakthrough ideas and innovative solutions. 

Overcoming Resistance to Change and Adopting Intelligent Apps

Despite AI’s potential, change isn’t easy. It takes effort and is especially challenging for organizations used to existing processes. This resistance to change is a significant roadblock when transitioning to intelligent apps. 

Overcoming this resistance involves:  

  • Understanding employees’ concerns. 
  • Challenging underlying assumptions about traditional systems and outdated processes that hinder change. 
  • Providing comprehensive training for workers to use AI effectively.
  • Demonstrating the benefits and opportunities of intelligent apps. 
  • Clear and continuous communication to dispel fears and misconceptions about AI.

These steps will foster greater acceptance and enthusiasm for intelligent apps in your teams. 

Preparing for the Future of Intelligent Apps and Continuous Evolution

Finally, it’s essential to understand that generative AI is just beginning to gain momentum. Adjusting to a world of intelligent apps isn’t a one-time change—it’s a continuous evolution. 

As AI technologies mature and evolve and more businesses realize their benefits, we can expect the landscape of intelligent apps to expand further. Organizations can cultivate a mindset of continuous learning, flexibility, and adaptation to prepare for this future. Staying ahead in this dynamic field requires organizations to keep up with the latest AI advancements, continually reassess their strategies, and pivot when necessary. 

The domain of intelligent apps is rapidly advancing. Organizations must prepare to navigate this dynamic landscape to stay ahead of the curve.

Summary

Preparing for AI and intelligent apps is a strategic decision. It requires thoughtful preparation and a culture that embraces change.

With the proper preparation and mindset, adopting AI can become a smoother, collaborative path to innovation for the whole organization. Integrating AI into your organization does not discard what has worked in the past. It simply enhances and augments those processes for a better future.

Consider the points above to assess your organization’s readiness and prepare to welcome intelligent apps.

In the first week of this series, you learned about intelligent apps, how organizations today find success through intelligent apps, what AI-assisted application development looks like, ways to leverage the power of generative AI, the technical changes needed to prepare for intelligent apps, and, in this article, the organizational changes required to harness the power of AI.

The future is intelligent. Use this series as a guideline to navigate challenges while preparing for and embracing AI's opportunities for an intelligent tomorrow.

Exercise

- - +
Skip to main content

11 posts tagged with "github-copilot"

View All Tags

· 10 min read
It's 30DaysOfIA

Prepare your organization to adopt intelligent apps by taking a change management approach. Assess your readiness, upskill teams, create a culture of change and overcome resistance, and prepare for future evolution to pave the way for a smooth transition into an AI-centric company.

What We'll Cover:

  • Role of change management in modernizing to intelligent apps
  • Skills needed for building intelligent apps in your organization
  • How to build a culture of change to embrace AI
  • How to prepare for the future of intelligent apps and continuous evolution

image of modernizing AI solutions for intelligent apps

Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management

As we’ve discussed throughout this series, intelligent apps and generative AI are rewriting the rules of business and application development. The first article of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” explored how intelligent apps can enhance user experiences and drive businesses growth. These advancements are opening new opportunities and markets for companies ready to embrace them.

However, embracing this digital transformation is more complex than flipping a switch. It requires adjustments to the technical side, which we explored in the series’ fifth article, “Preparing the Path for Intelligent Apps: Transitioning from On-Premises/IaaS to Cloud Native Applications.” It also demands a shift in how organizations adapt to change and approach app design and development. Let’s explore that mindset transition with tips to help you make it happen.

The Role of Change Management in Transitioning to Intelligent Apps

Transitioning to intelligent apps isn’t merely a technological change. It’s a profound shift in how businesses perceive and operate. This move toward artificial intelligence (AI)-centric approach isn’t meant to replace humans with machines or radically overhaul all business operations.

Instead, intelligent apps should augment human capabilities, streamline processes, and foster an environment where innovation flourishes. They expand toolkits and techniques to include AI-assisted development tools like GitHub Copilot and AI-enabled workflows, helping us design and deliver AI-enhanced experiences.

Effective change management strategies are crucial in helping employees adapt to these new ways of working with intelligent apps, ensuring that the transition doesn’t disrupt productivity or morale.

Team and organization strategies should: 

  • Assess the impact of any proposed changes.  
  • Communicate the benefits and reasons behind the change to all affected team members.  
  • Provide the necessary training and support.  
  • Integrate the changes into the culture and daily operations. 

By incorporating this robust change management approach, you can identify and mitigate potential transition risks early. Moreover, this strategy ensures that employees are engaged throughout the process, fostering a sense of involvement and ownership. This, in turn, cultivates a culture suited for innovation through intelligent apps. 

These organizational changes focus on the following aspects: organizational readiness, skillset, culture of change and overcoming resistance, and adaptability. Let’s explore them below.

info

Register for the intelligent apps webinar on Driving Business Value by Modernizing with Cloud-Native & AI with Microsoft and Forrester on September 26. 

Explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries. 

Identifying Organizational Readiness for Intelligent Apps

The first step in preparing your organization for intelligent apps is to assess your current readiness for change. Analyze various elements of your organization, from infrastructure and workforce skills to strategic vision and overall culture.

Can your apps and systems integrate with AI platforms like Microsoft Azure AI? Is your workforce comfortable working alongside AI, or are they fearful and resistant? Does your strategic vision incorporate AI’s transformative potential, or does it fail to acknowledge AI as a critical driver of future growth?

These questions identify the challenges of integrating AI into an organization and determine the company’s readiness for intelligent apps.

Embracing intelligent apps involves more than being ready with your technological infrastructure or having the right technology or tools. You need flexible organizational structures and processes driving your business to accommodate new ideas and capabilities. Your teams must feel empowered to own and drive them. Much like Microsoft’s shift to AI-powered services—like AI-powered Bing and Edge and Microsoft 365 Copilot—embracing AI organization-wide requires a holistic vision and willingness to innovate, with teams capable of delivering on the plan.

After accessing these critical areas for organizational readiness, you can start planning a strategy for this transition to an AI-ready organization. Identify skill gaps and work to bridge them while nurturing an environment that appreciates and harnesses the potential of AI and intelligent apps.

Nurturing the Indispensable Skills for Intelligent Apps

Tasking your AI specialists with preparing your organization to use and build intelligent apps isn’t enough. Accomplishing this goal requires a cross-functional effort and a diverse skill set.

Developing intelligent apps involves data scientists managing and interpreting vast amounts of data, machine learning engineers building and training the models, and traditional developers integrating these models into usable applications. However, the impact of generative AI spans beyond creating apps. AI fluency across the entire organization—from sales and marketing to accounting and legal departments—offers substantial benefits, sparking new ideas and solutions from sometimes unexpected sources.

Teams' AI skills might roughly fall into three levels: 

  • Fundamental skills — Being familiar and efficient with using AI tools, for example, knowing how to write effective prompts for large language models (LLMs).
  • Visionary skills — Recognizing AI’s strengths and weaknesses, for example, its creativity and hallucinations, that is, unexpected output.
  • Expert skills — Managing data and training models and integrating AI technology into apps, such as incorporating the OpenAI application programming interface (API) into an Intelligent App.

Based on these levels, you can gauge your team's skill readiness to pinpoint the most significant gaps, whether in general AI knowledge across your organization or within technical development teams.

As intelligent apps become increasingly prevalent, upskilling your workforce is crucial. This transition opens a new realm of skills and capabilities for your team to master. You can offer machine learning, data analysis, and cloud computing training programs—such as Microsoft’s Azure AI Fundamentals certification—to empower your team to confidently navigate the intelligent apps landscape.

Nurturing these skills within your organization may require hiring new talent, reskilling current employees, and creating an environment that promotes learning and experimentation. Specifically, if your team understands tools like the Microsoft Azure AI platform, they could contribute significantly to developing and maintaining intelligent apps.

An upskilled team is more than a functional unit. It’s a think tank to drive innovation and improve your organization’s competitive standing in the digital marketplace.

Building a Culture of Change to Embrace AI

Even with the right people and skills in place, change remains an uphill battle for organizations committed to traditional processes. 

Transitioning to intelligent apps is as much about mindset as technology and skills. It requires fostering a culture open to change and innovation that understands AI’s transformative potential and embraces it to enhance rather than replace human capabilities. 

Leaders play a pivotal role in cultivating this mindset. As they model a positive attitude toward AI, emphasize its benefits, and address any concerns transparently, leaders significantly contribute to the organization’s readiness to adopt intelligent apps. 

The most critical factor in this transition is enabling an AI-friendly culture. It’s about encouraging curiosity, rewarding innovative thinking, and creating an environment where people thrive on learning and experimentation. 

Empowering teams to leverage practical AI tools and AI-driven data insights to aid decision-making helps motivate employees to use and explore AI’s potential, improve their skills, and contribute to the successful integration of intelligent apps. Such an environment is fertile ground to grow breakthrough ideas and innovative solutions. 

Overcoming Resistance to Change and Adopting Intelligent Apps

Despite AI’s potential, change isn’t easy. It takes effort and is especially challenging for organizations used to existing processes. This resistance to change is a significant roadblock when transitioning to intelligent apps. 

Overcoming this resistance involves:  

  • Understanding employees’ concerns. 
  • Challenging underlying assumptions about traditional systems and outdated processes that hinder change. 
  • Providing comprehensive training for workers to use AI effectively.
  • Demonstrating the benefits and opportunities of intelligent apps. 
  • Clear and continuous communication to dispel fears and misconceptions about AI.

These steps will foster greater acceptance and enthusiasm for intelligent apps in your teams. 

Preparing for the Future of Intelligent Apps and Continuous Evolution

Finally, it’s essential to understand that generative AI is just beginning to gain momentum. Adjusting to a world of intelligent apps isn’t a one-time change—it’s a continuous evolution. 

As AI technologies mature and evolve and more businesses realize their benefits, we can expect the landscape of intelligent apps to expand further. Organizations can cultivate a mindset of continuous learning, flexibility, and adaptation to prepare for this future. Staying ahead in this dynamic field requires organizations to keep up with the latest AI advancements, continually reassess their strategies, and pivot when necessary. 

The domain of intelligent apps is rapidly advancing. Organizations must prepare to navigate this dynamic landscape to stay ahead of the curve.

Summary

Preparing for AI and intelligent apps is a strategic decision. It requires thoughtful preparation and a culture that embraces change.

With the proper preparation and mindset, adopting AI can become a smoother, collaborative path to innovation for the whole organization. Integrating AI into your organization does not discard what has worked in the past. It simply enhances and augments those processes for a better future.

Consider the points above to assess your organization’s readiness and prepare to welcome intelligent apps.

In the first week of this series, you learned about intelligent apps, how organizations today find success through intelligent apps, what AI-assisted application development looks like, ways to leverage the power of generative AI, the technical changes needed to prepare for intelligent apps, and, in this article, the organizational changes required to harness the power of AI.

The future is intelligent. Use this series as a guideline to navigate challenges while preparing for and embracing AI's opportunities for an intelligent tomorrow.

Exercise

+ + \ No newline at end of file diff --git a/30daysofIA/tags/github-copilot/page/10/index.html b/30daysofIA/tags/github-copilot/page/10/index.html new file mode 100644 index 0000000000..21bc1e6d83 --- /dev/null +++ b/30daysofIA/tags/github-copilot/page/10/index.html @@ -0,0 +1,27 @@ + + + + + +11 posts tagged with "github-copilot" | Build Intelligent Apps On Azure + + + + + + + + + + + + + + +
+

11 posts tagged with "github-copilot"

View All Tags

· 4 min read
It's 30DaysOfIA

Continue The Learning Journey through Fall For Intelligent Apps! 🍂

What We'll Cover

Thank you! ♥️

image

It's hard to believe that JavaScript on Azure hack-together is ending! It seems like just yesterday that we launched this initiative, and yet here we are, 15 days later, with an incredible amount of learning and growth behind us. So... it's time for a wrap!

From the bottom of our hearts, we want to thank each and every one of you for your participation, engagement, and enthusiasm. It's been truly inspiring to see the passion and dedication from this strong community, and we're honored to be a part of it. ✨

Recap of The JavaScript on Azure Global Hack-Together

As we wrap up this exciting event, we wanted to take a moment to reflect on all that we've accomplished together. Over the last 15 days, we've covered a lot of ground, from the basics of contributing to Open source to the exploration of the Contoso Real Estate project from its Frontend to its Backend and future AI implementation.

Now that the hack-together is ending, we want to make sure that you have all the resources you need to continue honing your skills in the future. Whether you're looking to make your fist contribution to open source, become an open source maintainers, collaborate with others, or simply keep learning, there are plenty of resources out there to help you achieve your goals. So, let's dive in and explore all the ways you can continue to grow your JavaScript skills on Azure!

JSonAzure Hack-together Roadmap 📍:

hack-together-roadmap (2)

Recap on past Livestreams🌟:

Day 1️⃣: Opening Keynote (Hack-together Launch): Introduction to the Contoso Real Estate Open-source project!, managing complex and complex enterprise architecture, new announcements for JavaScript developers on Azure

Day 2️⃣: GitHub Copilot & Codespaces: Introduction to your AI pair programmer (GitHub Copilot) and your virtual developer environment on the cloud (GitHub Codespaces)

Day 6️⃣: Build your Frontend using Static Web Apps as part of a complex, modern composable frontends (or micro-frontends) and cloud-native applications.

Day 9️⃣: Build a Serverless Backend using Azure Functions

Day 1️⃣3️⃣: Easily connect to an Azure Cosmos DB, exploring its benefits and how to get started

Day 1️⃣5️⃣: Being in the AI Era, we dive into the Azure OpenAI Service and how you can start to build intelligent JavaScript applications

📖 Self-Learning Resources

  1. JavaScript on Azure Global Hack Together Module collection
  2. Lets #HackTogether: Javascript On Azure Keynote
  3. Step by Step Guide: Migrating v3 to v4 programming model for Azure Functions for Node.Js Application

Continue your journey with #FallForIntelligentApps

Join us this Fall on a learning journey to explore building intelligent apps. Combine the power of AI, cloud-native app development, and cloud-scale data to build highly differentiated digital experiences. Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure. Engage in a self-paced learning adventure by following along the #30Days of Intelligent Apps series, completing the Intelligent Apps Skills Challenge, joining the Product Group for a live Ask The Expert series or building an end to end solution architecture with a live guided experience through the Learn Live series. Discover more here.

Hands-on practice: Make your first contribution to open source!

Join our GitHUb Discussion Forum to connect with developers from every part of world, see contributions from other, find collaborators and make your first contribution to a real-world project! +Don't forget to give the repo a star ⭐

Resources

All resources are accessible on our landing page

+ + + + \ No newline at end of file diff --git a/30daysofIA/tags/github-copilot/page/11/index.html b/30daysofIA/tags/github-copilot/page/11/index.html new file mode 100644 index 0000000000..fbb7c12d7e --- /dev/null +++ b/30daysofIA/tags/github-copilot/page/11/index.html @@ -0,0 +1,26 @@ + + + + + +11 posts tagged with "github-copilot" | Build Intelligent Apps On Azure + + + + + + + + + + + + + + +
+

11 posts tagged with "github-copilot"

View All Tags

· 2 min read
It's 30DaysOfIA

September is almost here - and that can only mean one thing!! It's time to 🍂 Fall for something new and exciting and spend a few weeks skilling up on relevant tools, techologies and solutions!!

Last year, we focused on #ServerlessSeptember. This year, we're building on that theme with the addition of cloud-scale Data, cloud-native Technologies and cloud-based AI integrations to help you modernize and build intelligent apps for the enterprise!

Watch this space - and join us in September to learn more!

+ + + + \ No newline at end of file diff --git a/30daysofIA/tags/github-copilot/page/2/index.html b/30daysofIA/tags/github-copilot/page/2/index.html index 3c406a94fc..f356c1984c 100644 --- a/30daysofIA/tags/github-copilot/page/2/index.html +++ b/30daysofIA/tags/github-copilot/page/2/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "github-copilot" | Build Intelligent Apps On Azure +11 posts tagged with "github-copilot" | Build Intelligent Apps On Azure @@ -14,13 +14,13 @@ - - + +
-

9 posts tagged with "github-copilot"

View All Tags

· 9 min read
It's 30DaysOfIA

Learn best practices for modernizing your on-premises or IaaS solutions to intelligent apps in the cloud.

What We'll Cover:

  • Traditional vs Intelligent Apps
  • On-Premises/IaaS vs. Cloud-Native Platforms
  • Modernization strategy for building Intelligent Apps

image of modernizing AI solutions for intelligent apps

Preparing the Path for Intelligent Apps: Transitioning from On-Premises/IaaS to Cloud-Native Applications

The proliferation of generative AI is paving the way for intelligent applications, software that leverages AI capabilities to deliver unparalleled functionality and user experience. As we covered in a previous article, “Demystifying Intelligent Applications: Leveraging AI in App Development,” intelligent Apps aren’t just products of advanced coding. They interact, learn, and evolve thanks to modern AI and machine learning (ML) breakthroughs. 

The paradigm shift from traditional to intelligent apps demands that we change our approach to the technical challenges of software and architecture design. It also necessitates adopting new ways of thinking and operating within an organization, which we explore in greater depth in “Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management.” 

This article explores the technical infrastructure powering intelligent apps, providing a roadmap to transition traditional on-premises or Infrastructure as a Service (IaaS) solutions to intelligent apps deployed to cloud native platforms and services. We’ll discover the differences between conventional and intelligent apps, options for re-architecting existing applications, and critical strategic considerations integral to modernizing the application-building approach. 

Understanding the Shift: Traditional vs. Intelligent Applications

Intelligent apps are more than just an incremental evolution from traditional apps. They involve a fundamental shift in software solution design, development, and deployment. A clear understanding of how each paradigm differs is crucial for comprehending the impact of this transition. 

Functionality and User Experience

Traditional applications, often rule-based and rigid, rely on preprogrammed operations. A traditional weather app, for example, typically presents structured, location-based forecasts using a predetermined data source. Meanwhile, an intelligent app leverages AI for data-driven decision-making and personalization. Rather than merely displaying the forecast, it integrates ML with additional data sources—like a user’s calendar and fitness data—to determine their favorite outdoor activities, provide relevant clothing suggestions, and even automatically design ideal vacations and times based on the user’s optimal weather preferences. 

Similarly, while traditional applications tend to offer a one-size-fits-all UI, intelligent apps use AI to create tailored interactions. For instance, an intelligent banking app might use generative AI to provide a voice-enabled UI that lets customers use natural language to ask about transactions and receive personalized financial advice.

Infrastructure

Intelligent apps integrating these sophisticated features require highly scalable, flexible infrastructure optimized for AI functionality and able to manage large volumes of real-time data. Unlike traditional applications deployed on-premises or in infrastructure as a service (IaaS) environments, intelligent apps demand the flexibility and scalability of cloud native architectures.

An on-premises or IaaS-based e-commerce application may struggle with connecting its data to AI services or scaling to accommodate voluminous real-time data and requests. But an intelligent app built with a cloud native approach and scalability at its core can handle variable demand much more efficiently. It also maintains its infrastructure components on demand, whether for collecting data for AI/ML consumption or user-facing features like AI-enabled customer support and personalization.

Consider an intelligent app built on Azure that creates ChatGPT-like experiences with custom data. It might connect Azure Open AI and Azure Cognitive Search to an app running on Azure Container Apps or Azure Kubernetes Service. 

The architecture might look like the following diagram based on this demo app:

diagram of a demo app architecture

While the cloud is well-equipped to handle the challenges associated with shifting to AI-centric infrastructure—handling real-time data processing and storage, integrating and managing AI services, configuring event-driven and microservices architectures, and ensuring security across the distributed environment—re-architecting a traditional app to an intelligent app requires careful planning and execution to ensure it benefits fully from the capabilities of AI and cloud computing.

On-Premises/IaaS vs. Cloud-Native Platforms

While on-premises and IaaS solutions provide substantial control over data and processes, this fine-grained control comes with setup and maintenance costs and may lack the scalability necessary for AI-driven Intelligent Apps. On-premises hosting involves purchasing and maintaining hardware, including servers and storage systems. IaaS involves leasing virtual hardware and then hiring and training IT personnel for the infrastructure’s operation, maintenance, and security.

Conversely, infrastructure built on cloud native app services with Microsoft Azure offers virtually unlimited scalability without ongoing hardware and maintenance costs. This feature is ideal for intelligent apps that need to adapt and scale to accommodate fluctuating workloads and data streams. And because these cloud infrastructures typically operate on a pay-as-you-go model, traditionally substantial upfront costs turn into predictable operating expenses, lowering both the cost and risk of launching applications.

Moreover, cloud service providers continually update their offerings, so building on sophisticated AI tools and services through the cloud, like the Azure AI Platform and orchestration capabilities with Azure Kubernetes Service, ensures that you have access to the latest AI and most advanced AI models and technologies.

Strategic Considerations for Transitioning to Intelligent Apps

When developing a strategy for transitioning to intelligent apps, you must consider costs, personnel skill development, and data management.

Monolithic architectures are unlikely to support the scale and ease of iteration required by intelligent apps, so you’ll likely need to implement code changes to support the transition. Your chosen hosting solution and the amount of data your app will handle affect cost and performance, in addition to determining privacy and compliance requirements.

An app that manages or stores terabytes or petabytes of data will require storage-optimized virtual machines or a managed database service hosting. Meanwhile, an app that processes video content or performs resource-intensive calculations should prioritize high-performance computing. Maximizing storage and performance can become extraordinarily costly.

Finally, existing applications shifting towards intelligent apps may encounter compatibility conflicts, depending on database type or the existing system’s underlying programming language. As you might imagine, a traditional app running on-premises and using a shared local database may need significant restructuring to support a cloud native architecture. You need plans for data migration, potential downtime, and training.

Next, let’s look at this transformation process.

Re-Architecting Monolithic Apps into Microservices

Shifting from monolithic applications to microservices is pivotal for transitioning to Intelligent Apps, and you can use this shift as an opportunity to transform your legacy software into intelligent apps that leverage the flexibility and power of microservices-enabled AI solutions.

Traditionally, a monolithic application (let’s use an online marketplace as an example) might handle all activities—from serving webpages to processing payments—within an on-premises server or a cloud server. However, this approach is challenging to scale and remains vulnerable to network failures or cyberattacks.

Breaking these applications into multiple services with specific responsibilities, such as authentication or notifications, can overcome these limitations. These services are typically deployed as microservices within container environments like Azure Kubernetes Service or serverless platforms like Azure Container Apps or Azure Functions using automated processes. This approach offers scalability, robustness, and improved performance, as each microservice can run independently and use its own database.

Shifting towards smaller, independent microservices in app architectures allows us to reap the benefits of AI-powered intelligent apps, for example, by using microservices to handle AI tasks such as processing natural language input, analyzing behavioral data, or personalizing content.

info

Watch Episode 1 and Episode 2 of the Learn Live series on how to build, test and deploy an end-to-end intelligent app solution using the Contoso Real Estate Sample.

Event-Driven Architectures: Knowing When to Use Them

Event-Driven Architectures (EDAs) can significantly enhance the performance and cost-effectiveness of intelligent apps. EDAs, typically implemented with microservices, respond in real time to events or state changes ranging from user interactions to real-time analytics.

Consider an intelligent app designed for personalized customer engagement. Instead of designing the application to check continuously for changes in user data, an EDA can react to such changes immediately, triggering appropriate microservices. For example, if users modify their preferences, an event could trigger an AI-driven recommendation microservice to update its suggestions immediately. This real-time responsiveness, enabled by services like Azure Event Grid, can improve user experiences and make intelligent apps more adaptive and proactive.

However, it’s worth noting that while EDAs provide significant benefits, they can introduce unique complexities, such as maintaining event consistency and debugging issues in event chains across multiple microservices. Therefore, it’s important to assess your application’s needs so you can understand where it can benefit from an EDA.

Conclusion

Transitioning to intelligent apps represents a shift in strategy, architecture, and infrastructure. By designing and re-architecting for cloud-based infrastructure, your apps gain scalability, cost-effectiveness, robust data management, access to sophisticated AI tools, enhanced security, and reduced operational overhead.  

But this transition extends well beyond its technical components. Organizations and the people working within them need equal attention. You can explore these facets in greater depth in “Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management.” 

The power of generative AI is here, and preparing for the move toward cloud-based intelligent applications will ensure that your apps remain performative and scalable and—most importantly—that your business is ready for the newest era of app innovation.

Exercise

- - +
Skip to main content

11 posts tagged with "github-copilot"

View All Tags

· 9 min read
It's 30DaysOfIA

Learn best practices for modernizing your on-premises or IaaS solutions to intelligent apps in the cloud.

What We'll Cover:

  • Traditional vs Intelligent Apps
  • On-Premises/IaaS vs. Cloud-Native Platforms
  • Modernization strategy for building Intelligent Apps

image of modernizing AI solutions for intelligent apps

Preparing the Path for Intelligent Apps: Transitioning from On-Premises/IaaS to Cloud-Native Applications

The proliferation of generative AI is paving the way for intelligent applications, software that leverages AI capabilities to deliver unparalleled functionality and user experience. As we covered in a previous article, “Demystifying Intelligent Applications: Leveraging AI in App Development,” intelligent Apps aren’t just products of advanced coding. They interact, learn, and evolve thanks to modern AI and machine learning (ML) breakthroughs. 

The paradigm shift from traditional to intelligent apps demands that we change our approach to the technical challenges of software and architecture design. It also necessitates adopting new ways of thinking and operating within an organization, which we explore in greater depth in “Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management.” 

This article explores the technical infrastructure powering intelligent apps, providing a roadmap to transition traditional on-premises or Infrastructure as a Service (IaaS) solutions to intelligent apps deployed to cloud native platforms and services. We’ll discover the differences between conventional and intelligent apps, options for re-architecting existing applications, and critical strategic considerations integral to modernizing the application-building approach. 

Understanding the Shift: Traditional vs. Intelligent Applications

Intelligent apps are more than just an incremental evolution from traditional apps. They involve a fundamental shift in software solution design, development, and deployment. A clear understanding of how each paradigm differs is crucial for comprehending the impact of this transition. 

Functionality and User Experience

Traditional applications, often rule-based and rigid, rely on preprogrammed operations. A traditional weather app, for example, typically presents structured, location-based forecasts using a predetermined data source. Meanwhile, an intelligent app leverages AI for data-driven decision-making and personalization. Rather than merely displaying the forecast, it integrates ML with additional data sources—like a user’s calendar and fitness data—to determine their favorite outdoor activities, provide relevant clothing suggestions, and even automatically design ideal vacations and times based on the user’s optimal weather preferences. 

Similarly, while traditional applications tend to offer a one-size-fits-all UI, intelligent apps use AI to create tailored interactions. For instance, an intelligent banking app might use generative AI to provide a voice-enabled UI that lets customers use natural language to ask about transactions and receive personalized financial advice.

Infrastructure

Intelligent apps integrating these sophisticated features require highly scalable, flexible infrastructure optimized for AI functionality and able to manage large volumes of real-time data. Unlike traditional applications deployed on-premises or in infrastructure as a service (IaaS) environments, intelligent apps demand the flexibility and scalability of cloud native architectures.

An on-premises or IaaS-based e-commerce application may struggle with connecting its data to AI services or scaling to accommodate voluminous real-time data and requests. But an intelligent app built with a cloud native approach and scalability at its core can handle variable demand much more efficiently. It also maintains its infrastructure components on demand, whether for collecting data for AI/ML consumption or user-facing features like AI-enabled customer support and personalization.

Consider an intelligent app built on Azure that creates ChatGPT-like experiences with custom data. It might connect Azure Open AI and Azure Cognitive Search to an app running on Azure Container Apps or Azure Kubernetes Service. 

The architecture might look like the following diagram based on this demo app:

diagram of a demo app architecture

While the cloud is well-equipped to handle the challenges associated with shifting to AI-centric infrastructure—handling real-time data processing and storage, integrating and managing AI services, configuring event-driven and microservices architectures, and ensuring security across the distributed environment—re-architecting a traditional app to an intelligent app requires careful planning and execution to ensure it benefits fully from the capabilities of AI and cloud computing.

On-Premises/IaaS vs. Cloud-Native Platforms

While on-premises and IaaS solutions provide substantial control over data and processes, this fine-grained control comes with setup and maintenance costs and may lack the scalability necessary for AI-driven Intelligent Apps. On-premises hosting involves purchasing and maintaining hardware, including servers and storage systems. IaaS involves leasing virtual hardware and then hiring and training IT personnel for the infrastructure’s operation, maintenance, and security.

Conversely, infrastructure built on cloud native app services with Microsoft Azure offers virtually unlimited scalability without ongoing hardware and maintenance costs. This feature is ideal for intelligent apps that need to adapt and scale to accommodate fluctuating workloads and data streams. And because these cloud infrastructures typically operate on a pay-as-you-go model, traditionally substantial upfront costs turn into predictable operating expenses, lowering both the cost and risk of launching applications.

Moreover, cloud service providers continually update their offerings, so building on sophisticated AI tools and services through the cloud, like the Azure AI Platform and orchestration capabilities with Azure Kubernetes Service, ensures that you have access to the latest AI and most advanced AI models and technologies.

Strategic Considerations for Transitioning to Intelligent Apps

When developing a strategy for transitioning to intelligent apps, you must consider costs, personnel skill development, and data management.

Monolithic architectures are unlikely to support the scale and ease of iteration required by intelligent apps, so you’ll likely need to implement code changes to support the transition. Your chosen hosting solution and the amount of data your app will handle affect cost and performance, in addition to determining privacy and compliance requirements.

An app that manages or stores terabytes or petabytes of data will require storage-optimized virtual machines or a managed database service hosting. Meanwhile, an app that processes video content or performs resource-intensive calculations should prioritize high-performance computing. Maximizing storage and performance can become extraordinarily costly.

Finally, existing applications shifting towards intelligent apps may encounter compatibility conflicts, depending on database type or the existing system’s underlying programming language. As you might imagine, a traditional app running on-premises and using a shared local database may need significant restructuring to support a cloud native architecture. You need plans for data migration, potential downtime, and training.

Next, let’s look at this transformation process.

Re-Architecting Monolithic Apps into Microservices

Shifting from monolithic applications to microservices is pivotal for transitioning to Intelligent Apps, and you can use this shift as an opportunity to transform your legacy software into intelligent apps that leverage the flexibility and power of microservices-enabled AI solutions.

Traditionally, a monolithic application (let’s use an online marketplace as an example) might handle all activities—from serving webpages to processing payments—within an on-premises server or a cloud server. However, this approach is challenging to scale and remains vulnerable to network failures or cyberattacks.

Breaking these applications into multiple services with specific responsibilities, such as authentication or notifications, can overcome these limitations. These services are typically deployed as microservices within container environments like Azure Kubernetes Service or serverless platforms like Azure Container Apps or Azure Functions using automated processes. This approach offers scalability, robustness, and improved performance, as each microservice can run independently and use its own database.

Shifting towards smaller, independent microservices in app architectures allows us to reap the benefits of AI-powered intelligent apps, for example, by using microservices to handle AI tasks such as processing natural language input, analyzing behavioral data, or personalizing content.

info

Watch Episode 1 and Episode 2 of the Learn Live series on how to build, test and deploy an end-to-end intelligent app solution using the Contoso Real Estate Sample.

Event-Driven Architectures: Knowing When to Use Them

Event-Driven Architectures (EDAs) can significantly enhance the performance and cost-effectiveness of intelligent apps. EDAs, typically implemented with microservices, respond in real time to events or state changes ranging from user interactions to real-time analytics.

Consider an intelligent app designed for personalized customer engagement. Instead of designing the application to check continuously for changes in user data, an EDA can react to such changes immediately, triggering appropriate microservices. For example, if users modify their preferences, an event could trigger an AI-driven recommendation microservice to update its suggestions immediately. This real-time responsiveness, enabled by services like Azure Event Grid, can improve user experiences and make intelligent apps more adaptive and proactive.

However, it’s worth noting that while EDAs provide significant benefits, they can introduce unique complexities, such as maintaining event consistency and debugging issues in event chains across multiple microservices. Therefore, it’s important to assess your application’s needs so you can understand where it can benefit from an EDA.

Conclusion

Transitioning to intelligent apps represents a shift in strategy, architecture, and infrastructure. By designing and re-architecting for cloud-based infrastructure, your apps gain scalability, cost-effectiveness, robust data management, access to sophisticated AI tools, enhanced security, and reduced operational overhead.  

But this transition extends well beyond its technical components. Organizations and the people working within them need equal attention. You can explore these facets in greater depth in “Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management.” 

The power of generative AI is here, and preparing for the move toward cloud-based intelligent applications will ensure that your apps remain performative and scalable and—most importantly—that your business is ready for the newest era of app innovation.

Exercise

+ + \ No newline at end of file diff --git a/30daysofIA/tags/github-copilot/page/3/index.html b/30daysofIA/tags/github-copilot/page/3/index.html index 0e4b4320bf..d46723983a 100644 --- a/30daysofIA/tags/github-copilot/page/3/index.html +++ b/30daysofIA/tags/github-copilot/page/3/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "github-copilot" | Build Intelligent Apps On Azure +11 posts tagged with "github-copilot" | Build Intelligent Apps On Azure @@ -14,13 +14,13 @@ - - + +
-

9 posts tagged with "github-copilot"

View All Tags

· 10 min read
It's 30DaysOfIA

Explore how digital natives and Independent Software Vendors (ISVs) can transform their business operations and generate higher value for customers with generative AI and intelligent apps.

What We'll Cover:

  • Potential of Generative AI in application development
  • Challenges with Generative AI
  • Integrating Generative AI with business operations

image of generative AI and intelligent apps used in business

Unleashing Business Transformation: How Digital Natives Can Leverage Generative AI

Generative AI is no longer a technological flight of fancy. It’s here, and it’s rewriting the rules of business innovation. 

This type of AI leverages machine learning algorithms to create new data with similar characteristics to a given dataset. It goes beyond text generation using generative pretrained transformer (GPT) models, covering an array of applications that span from image synthesis to translations and more. It also powers a new class of intelligent applications, which combine AI capabilities with traditional software functions for enhanced user experiences and advanced business solutions. 

Generative AI is transforming how digital natives and independent software vendors (ISVs) operate. Generative AI offers new possibilities for creating unique business value, enhancing customer experiences, and driving growth. 

It plays a critical role in applications, improving functionality, aiding decision-making, and automating complex tasks to deliver dynamic, uniquely personalized user experiences. Generative AI also enables and automates data-driven outcomes, functions, and features.

Integrating AI capabilities into customer-facing apps and turning them into intelligent apps benefits both the customer and the business, as we discussed in Demystifying Intelligent Applications: Leveraging AI in App Development. You get faster product development workflows and better business intelligence for sales and marketing, while your users get more natural and context-aware features and interactions. 

That said, to realize generative AI’s potential, you need some strategy and knowledge of how and where it fits into your current business and operations. We’ll examine this technology’s potential to transform business operations, its risks, and rewards, and how to integrate it into your existing operations. 

Unpacking the Potential of Generative AI

Generative AI is creating new opportunities in the digital playing field. It empowers businesses to generate insights, design products, and automate processes in ways and at speeds that were previously unimaginable. You can now predict customer behavior more accurately, offer highly personalized user experiences, and prototype innovative ideas rapidly. 

For example, Microsoft used generative AI to reinvent web search in the new AI-powered Bing and Edge. The search experience has evolved from a solitary user flow of typing in keywords and scrolling through results. It’s now a conversational experience where Bing uses context to offer better quality and more personalized search queries and results. 

Similarly, Microsoft harnessed generative AI to transform its productivity suite into intelligent apps. Through Copilot for Microsoft 365, PowerPoint now empowers users to tell their stories by using DALL-E to generate images effortlessly. It also helps rephrase text-heavy slides to be more straightforward. Outlook now assists users to compose more effective emails, enabling better communication.

These are just some examples of how AI powers day-to-day work and operations. 

Businesses that ignore generative AI miss out on untapped market opportunities, letting competitors gain an edge in the digital race. By seeking ways to rethink assumptions and reinvent existing approaches, you can effectively use generative AI to create new opportunities, open products to new use cases and markets, unlock new growth and customer satisfaction levels, and increase your revenue.

info

Register for the intelligent apps webinar on Driving Business Value by Modernizing with Cloud-Native & AI with Microsoft and Forrester on September 26.

Explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries. 

The Crucial Role of Generative AI in Business Operations

Generative AI can be a powerful catalyst for business transformation. Let’s quickly tour some ways it can redefine business operations. 

Product development is traditionally a long and painstaking process. Now, tools like GitHub Copilot and TestPilot use generative AI to make coding and testing software easier, faster, and more robust. 

Similarly, machine learning as a service technology, such as Azure Machine Learning, democratizes the availability of machine learning. It helps collect, clean, and train models across datasets, making engineering and analyzing data more efficient and accessible.  

Generative AI also simplifies the process of creating intelligent apps. Previously, simulating thousands of prototypes, predicting their performance, and narrowing down to the most viable options could take weeks, months, or even years of iteration and data analysis. With generative AI, this process can be as quick as days, hours, or perhaps minutes. 

Customer experience is also ripe for transformation. Personalized recommendations drive engagement and sales, and generative AI can scale that personalization to every customer interaction using comprehensive and continually updated data. For instance, Microsoft leverages AI to enable personalization in Dynamics 365 Customer Insights. Using this tool, you can ask questions about your customers in natural language and gain valuable and actionable insights into your customer base. 

Lastly, marketing and sales can benefit from generative AI. For example, identifying current and future key markets and emerging trends helps companies stay ahead of the curve. Tools like Copilot in Power BI help you quickly understand and uncover insights about your business, enabling your team to craft targeted campaigns, create engaging content, and optimize your business strategy. 

Leveraging this full generative AI potential requires the right tools. GPT-4, for instance, can be instrumental in realizing effective business transformation. It excels in understanding context, generating human-like text, and learning from minimal data input, making it a formidable tool in any digital native's arsenal. Platforms like Azure OpenAI Service bring GPT-4’s power to your fingertips.

However, as with any powerful technology, it’s unwise to treat generative AI as a perfect solution. You must know the challenges and risks of not leveraging it effectively, including technical and human-factor considerations. 

Without generative AI, your company risks missing the opportunity to please customers via personalization, as discussed previously. You’ll miss sales opportunities via business intelligence insights, limiting your ability to grow revenue and keep up with the competition. 

One challenge of leveraging generative AI’s full potential is finding the right tools. Fortunately, you can implement generative AI in your organization without investing in infrastructure, computational power, and expertise. Platforms like Azure OpenAI Service and Azure AI Services make AI easily accessible. They also work with Azure compute and data services like Azure Functions and Azure Kubernetes Service (AKS), enabling you to build intelligent apps easily and quickly. 

However, generative AI can’t do everything. It may need human expertise and intervention to use it fully. 

For example, generative AI can create unique data. However, the data may have limited diversity and complexity. The AI may need help recognizing additional considerations or requirements specific to a problem. It may also lack context due to various factors, such as the information’s freshness. Also, since it’s an emerging field that’s potentially worth $4.4 trillion, we’re still learning about the nuances and potential regulatory impact of generative AI. 

Perhaps the most crucial and challenging aspect of adopting generative AI is an organization’s required cultural shift. Your teams must learn how to use AI as your company fosters an understanding of AI’s potential and limitations. 

Begin integrating AI literacy into your company’s DNA. Knowledge of its potential and the readily available tools can inspire your teams to incorporate generative AI into their daily workflows and business operations quickly, improve productivity, and deliver superior customer experiences. 

No company wants to be outpaced by its competitors. You can balance generative AI’s potential with your business operations by staying aware of and adapting to the dynamic AI landscape.

Achieving Harmony: Integrating Generative AI with Existing Business Operations

Integrating generative AI into existing business operations doesn’t have to be disruptive. With the correct strategy, you can do so seamlessly. Your goal should be to augment and enhance your operations, not disrupt and replace. 

For example, Microsoft Cloud technologies leveraged AI to relieve healthcare organizations’ challenges, such as talent shortage and workforce burnout. Providers automated their workflows, invested in specific applications and connectors, and enhanced support for data and operations. 

Azure AI Services, such as text analytics for health, enable healthcare organizations to understand their largely unstructured and untapped medical data. Project Health Insights leans on AI to help healthcare professionals gain actionable insights and inferences from patient data. 

Azure Health Bot, an Intelligent App, empowers organizations to build and deploy legally compliant, powerful conversational healthcare experiences to patients. 

A successful AI integration like this begins with understanding. Knowing what generative AI can do and setting realistic expectations can help you identify where and how AI can best serve your operations. 

Next, you can foster a culture of AI literacy across teams, speeding up adoption by helping them understand how it can aid their work. The more familiar your team is with AI and its possibilities, the smoother the transition. Training and education programs play a pivotal role here. 

Finally, integration requires a systematic approach. It’s not about overhauling everything at once — it's about identifying areas where AI can add the most value, then gradually experimenting and scaling its use. Successful integration stories often feature a phased approach, starting small and growing steadily. 

As we move toward a future where generative AI is a staple in business operations, remember that every journey is unique. Although there’s no one-size-fits-all solution, a strategic approach and a keen understanding of your business needs will help you navigate this new landscape successfully.

Conclusion

Generative AI offers plenty of opportunities for businesses that embrace it. It’s fueling the next generation of intelligent applications, transforming everything from product development to customer engagement to marketing. This technology is the key to unlocking unprecedented growth and innovation. 

While the journey may have challenges, the potential rewards make generative AI worth exploring. As generative AI advances, Azure AI’s broad range of tools and services will help you navigate this fascinating field.

Exercise

  • Watch Episode 02 of the serverless edition Learn Live session to learn how to build, test and deploy an end-to-end intelligent app solution.
  • Complete the Cloud Skills Challenge to build on your AI skills.
  • Register for Ask the Expert: Azure Functions on September 26 for a live Q&A with the Product Team on how to build AI apps using serverless computing.
- - +
Skip to main content

11 posts tagged with "github-copilot"

View All Tags

· 12 min read
It's 30DaysOfIA

Delve into creating an Intelligent App that leverages Azure AI Vision to analyze images and extract data. Develop an API to perform optical character recognition (OCR) on uploaded images and testing this API locally.

What We'll Cover:

  • Understanding Azure AI Vision and Azure Kubernetes Service
  • Build a Python Web API to perform OCR
  • Test the API locally

image depicting an Intelligent App using AI

Jumpstart Your AI Journey: Building Your First Intelligent App with Azure AI and AKS (1)

An Intelligent App is a software application that uses artificial intelligence (AI), such as machine learning (ML), to extract meaningful insights, enabling users to make informed decisions based on the extracted text, data, sound, images, and more.

Intelligent Apps can employ advanced algorithms and models to recognize objects, patterns, and features within images, allowing them to perform tasks like object identification, image classification, facial recognition, and image segmentation.

To explore the power of Intelligent Apps, let’s build a Python app that performs optical character recognition (OCR) on images containing numbers and performs aggregate functions on the extracted numbers, like sum and average. We’ll leverage Azure AI Services for the OCR functionality and Azure Kubernetes Service (AKS) to develop, publish, and maintain a cloud-based, scalable app in Azure.

Let’s get started!

Understanding Azure AI Vision and Azure Kubernetes Service

Azure AI Vision is a cloud-based service that offers a range of advanced computer vision capabilities enabling applications to interpret content, such as images and videos, using ML and other AI algorithms. Azure computer vision lets users integrate powerful vision features into their projects without ML experience.

OCR is one of the critical functionalities within Azure vision service, allowing applications to extract text from images. Within Azure AI Vision service, OCR offers several features, including text extraction, multilingual support, layout analysis of documents, and integration with Azure services, such as Azure Functions and Azure Machine Learning.

info

Watch the intelligent apps webinar on Driving Business Value by Modernizing with Cloud-Native & AI with Microsoft and Forrester on September 26.

AKS is Microsoft Azure’s container orchestration platform that enables us to quickly and efficiently deploy, manage, and scale containerized applications, such as our Intelligent App.

Let’s explore how to leverage these tools to build an Intelligent App that uses Azure AI Vision to analyze images and extract data.

Prerequisites

To follow this tutorial, ensure you have the following:

For a preview of this final project, take a look at the complete project code.

Building the API with Azure AI Vision Service

First, log in to your Azure account and navigate to the Azure Portal.

Click Create a resource and search for “resource group.” Create a new resource group named computer-vision.

image in Azure Portal of creating a new resource group

Return to the Azure Portal home page and click Create a resource.

image in Azure Portal of creating a new resource

Search for “computer vision” and select it from the results. Click Create.

image of searching for resource group in Azure Portal

Clicking Create displays the Create Computer Vision wizard. Select the resource group you just created as the Resource group from the dropdown. Enter a resource name similar to what is seen in the screenshot below. Note that this must be a unique name so choose one that is not currently in use. Select Standard S1 as the pricing tier and check the Responsible use of AI box in the field at the bottom.

Image of selecting the Responsible use of AI box

Image of project details

Click Review + Create, then click Create.

Next, get the API key and endpoint from the Azure AI services multi-service account you created. Once the resource is created, navigate to the resource details page. Under Resource Management, select Keys and Endpoint. Once on the Keys and Endpoint page, you’ll find the Key 1 and Endpoint values. These credentials are necessary to access the Azure AI APIs.

image of selecting Keys and Endpoint

second image of selecting Keys and Endpoint

Configuring the Local Environment Variables

Next, we need to define a local environment variable file. This convenient way to store our API keys in a simple text file enables us to manage sensitive information and separate configuration from our API code.

If you haven’t done so already, clone the starter project template from GitHub into your local computer. Open the starter project template in a code editor and create a new .env file in the root folder.

Note: Root folder in this case is the “/Microsoft_Series17-18_Code/intelligent-app-before" folder.

Add the following key-value pairs to the file, remembering to replace the placeholder values with those you obtained from the previous step.

VISION_KEY=<THE-KEY-1-VALUE-FROM-YOUR-AZURE-AI-SERVICE>
VISION_ENDPOINT=<THE-ENDPOINT-VALUE-FROM-YOUR-AZURE-AI-SERVICE>

Reviewing the Quickstart Code

Let’s review the app.py file in the starter project template.

The app.py file works as the entry point for the Flask application. It is responsible for initializing the web server, listening for incoming requests, dispatching them to the appropriate route handlers, and generating responses.

Below is the code contained in the app.py file:

import os
import json

from flask import Flask, request
from flask_restful import Resource, Api
from werkzeug.utils import secure_filename

app = Flask(__name__,
static_url_path='',
static_folder='static/files')

api = Api(app)

app = Flask(__name__)

app.config['UPLOAD_FOLDER'] = 'files'

api = Api(app)

class UploadHandler(Resource):

def allowed_file(self, filename):
return '.' in filename and \
filename.rsplit('.', 1)[1].lower() in {'png'}

def post(self):
form = request.form.to_dict()

if 'file' not in request.files:
return json.dumps({ "success": False, "error": "No file part"})

file = request.files.get("file")
if file and self.allowed_file(file.filename):
filename = secure_filename(file.filename)
upload_folder = "static/files"
if not os.path.exists(upload_folder):
os.makedirs(upload_folder)
local_file_path = os.path.join(upload_folder, filename)
file.save(local_file_path)

return f"File {filename} uploaded successfully to folder: {upload_folder}"

api.add_resource(UploadHandler, "/")

if __name__ == '__main__':
app.run(debug=True)

The UploadHandler class above is defined as a Flask RESTful resource to handle file uploads. The class contains two methods:

  • The allowed_file method checks whether the file extension is allowed for upload. In this case, only .png files are allowed.

  • The post method handles HTTP POST requests for file uploads. It saves the uploaded .png file to the static/files folder.

Finally, the application returns a text response informing us that the file has been uploaded successfully.

Implementing the REST API in Python

To implement image analysis in your REST API, open the starter project template in your terminal, create a virtual environment, and activate it.

Next, install the Azure AI Vision SDK:

pip install azure-ai-vision

Then, add the following line to the requirements.txt file to include the azure-ai-vision package:

azure-ai-vision==0.13.0b1

Now, create an ocr_helper.py file in the project’s root folder. This file is a Python module that provides functions for processing images using OCR. Add the following code to the file:

import os
from statistics import median
from decimal import Decimal
import azure.ai.vision as sdk

def process_ocr(source_image):
service_options = sdk.VisionServiceOptions(os.environ["VISION_ENDPOINT"],
os.environ["VISION_KEY"])

vision_source = sdk.VisionSource(filename=source_image)

analysis_options = sdk.ImageAnalysisOptions()

analysis_options.features = (
sdk.ImageAnalysisFeature.CAPTION |
sdk.ImageAnalysisFeature.TEXT
)

analysis_options.language = "en"

analysis_options.gender_neutral_caption = True

image_analyzer = sdk.ImageAnalyzer(service_options, vision_source, analysis_options)

result = image_analyzer.analyze()

ocr_result = get_ocr_result(result)

return ocr_result

def get_ocr_result(result):
string_list = []

if result.reason != sdk.ImageAnalysisResultReason.ANALYZED:
return sdk.ImageAnalysisErrorDetails.from_result(result)
else:
if result.text is not None:
for line in result.text.lines:
for word in line.words:
string_list.append(word.content)

number_list = convert_to_decimal_list(string_list)

aggregate_result = aggregate_operations(number_list)

return {
"aggregate_result": aggregate_result,
"numbers_read": string_list
}

def convert_to_decimal_list(string_list):
return list(map(Decimal, string_list))

def aggregate_operations(numbers):
result = {
'sum': sum(numbers),
'average': sum(numbers) / len(numbers),
'median': median(numbers),
'min': min(numbers),
'max': max(numbers)
`}`
`return result`

This module uses the azure-ai-vision package to analyze images, including capturing captions and extracting text from the image. The process_ocr function inputs an image file and performs the OCR analysis. The module is a convenient tool for analyzing text-based data in pictures and performing numerical computations based on that data.

Let’s review the different components of the ocr_helper.py module listed above and explore how its functions help our Intelligent App process images and perform aggregate operations:

  • The process_ocr function takes the parameter source_image, which is the path of the image to be processed. The function then initializes the VisionServiceOptions using environment variables VISION_ENDPOINT and VISION_KEY to connect to the Azure AI Vision API.
  • The process_ocr function creates a VisionSource object with the specified source_image file name. ImageAnalysisOptions specify the features to be analyzed, including CAPTION and TEXT. The language is set to English ("en"), and gender-neutral captions are enabled.
  • Finally, an ImageAnalyzer object is created with the service options, vision source, and analysis options. The image is then analyzed using the image_analyzer.analyze method to retrieve OCR results. The recognized text from the OCR result is extracted, with numerical values stored in the string_list variable.
  • The convert_to_decimal_list function converts the list of strings to a list of decimal numbers, which helps process numeric values extracted from the text.
  • The aggregate_operations function calculates various aggregate statistics (sum, average, median, minimum, maximum) from a list of numbers and returns the results in a dictionary.

Note that you must have the appropriate credentials (VISION_KEY) and endpoint (VISION_ENDPOINT) configured for the Azure AI Vision API to use this module.

Finally, we must modify the app.py file so our code can use the process_ocr function of the ocr_helper.py file.

Add the following import statement to the app.py file:

from ocr_helper import process_ocr

Then, replace this line:

return f"File {filename} uploaded successfully to folder: {upload_folder}"

With these two lines:

aggregates = process_ocr(local_file_path)
return json.dumps(aggregates, default=str)

Doing so allows our application to perform aggregate operations (sum, average, median, minimum, maximum) of any numeric values found in the text and return the operation’s result as a JSON response.

Running the Intelligent App Locally

Our final goal is to deploy the sample app to AKS, which will run the app in a container. However, as a good practice, let’s run the app in a container locally before moving it to AKS. Doing so ensures that our application and its dependencies are encapsulated and can be run consistently across different environments.

Deploying to Docker Desktop

For our application to run on Docker, it needs two additional files: a Dockerfile and docker-compose.yml. A Dockerfile creates a single container image by specifying the steps to build it, and a docker-compose.yml file manages multiple containers as a cohesive application, defining their configurations and relationships. Dockerfile builds the image, while docker-compose.yml orchestrates multi-container applications.

In the project root folder, add a file named Dockerfile with the content below:

# syntax=docker/dockerfile:1

FROM python:3.8-slim-buster

WORKDIR /intelligentapp

COPY requirements.txt requirements.txt
RUN pip3 install -r requirements.txt

RUN pip install debugpy

COPY . .

CMD [ "python3", "-m" , "flask", "run", "--host=0.0.0.0"]

Now create a file named docker-compose.yml file in the project’s root folder with the following code, replacing the VISION_KEY and the VISION_ENDPOINT according to the environment variables you configured earlier:

version: '3.8'
services:
intelligentapp:
build:
context: .
dockerfile: Dockerfile
image: intelligent-app
ports:
- 5000:5000
container_name: intelligent-app
environment:
- VISION_KEY=<THE-KEY-1-VALUE-FROM-YOUR-AZURE-AI-SERVICE>
- VISION_ENDPOINT=<THE-ENDPOINT-VALUE-FROM-YOUR-AZURE-AI-SERVICE>

Then, run the following command in the terminal to build the image and start a container for the Intelligent App services defined in the docker-compose.yml file:

docker-compose up --build --force-recreate

Next, open a new terminal and run the command below to list the image deployed to your local Docker:

docker images

docker images

Testing the Intelligent App Locally

Now, we’ll test the Intelligent App’s functionality using the following test image:

test image to test functionality

This image is provided as sample1.png in the sample app source files, so you can easily use it in the next step.

The Intelligent App will read the image above using OCR to extract numbers in the image and perform five aggregate functions:

  • Sum
  • Average
  • Median
  • Min
  • Max

To test the API, open Postman and fill out the fields as follows:

  • URL: http://localhost:5000/
  • Method: POST
  • Body:
    • Form-data
      • Key: File — Click the right end of the Key field and select File from the dropdown list.
      • Value: Click Select Files, then select the sample1.png file provided in the sample code.

image of selecting the sample file

Now click the Send button and review the result body:

"{\"aggregate_result\": {\"sum\": \"25821\", \"average\": \"5164.2\", \"median\": \"5622\", \"min\": \"1447\", \"max\": \"9802\"}, \"numbers_read\": [\"3049\", \"5901\", \"5622\", \"1447\", \"9802\"]}"

As we can see, the app running on a local container returned the correct results based on the sample image. So, we’re ready to prepare our Azure resources to run the Intelligent App on AKS.

Exercise

In this article, we explored the creation of an Intelligent App that leverages Azure AI Vision to analyze images and extract data. We learned how to build a Python Web API to perform OCR on uploaded images and subsequently test this API locally.

Head to the next part, to deploy this API via Azure Kubernetes Service.

+ + \ No newline at end of file diff --git a/30daysofIA/tags/github-copilot/page/4/index.html b/30daysofIA/tags/github-copilot/page/4/index.html index 95761cda97..55af3c5b59 100644 --- a/30daysofIA/tags/github-copilot/page/4/index.html +++ b/30daysofIA/tags/github-copilot/page/4/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "github-copilot" | Build Intelligent Apps On Azure +11 posts tagged with "github-copilot" | Build Intelligent Apps On Azure @@ -14,13 +14,13 @@ - - + +
-

9 posts tagged with "github-copilot"

View All Tags

· 9 min read
It's 30DaysOfIA

In the part of our series on intelligent apps, we’ll explore how AI is transforming application development, from design and architecture to building.

What We'll Cover:

  • Revolutionizing application development using AI
  • Infusing AI in application design architecture
  • AI Assisted Pair Programming
  • Future of App Development

A decorative image of application development with AI

Reimagining Application Development With AI: A New Paradigm

Artificial intelligence (AI) is impacting every industry. AI-powered development tools offer new functionalities and assist software developers throughout the development process, revolutionizing app development. From initial design and architecture to coding and debugging, AI reshapes how we approach application conception, creation, and iteration.

In part one of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” we explored the new breed of intelligent apps and how they use AI. In this article, we’ll see how AI technologies enhance developer toolkits and shape the future of app development, particularly through AI-enabled coding assistance and debugging.

The Dawn of Intelligent Apps: Revolutionizing Application Development

AI is more than just another addition to the development toolbox—it represents a revolution bringing unprecedented efficiency, innovation, and user engagement to our applications. 

What sets intelligent apps apart is how they can adapt and learn, helping us deliver unique, real-time user experiences.  

One example is an AI-driven health application that can track biometric data, predict potential health issues, provide personalized health advice, and even guide emergency services during a medical crisis. An AI-enabled language learning app that adapts to our learning speeds, recognizes our problem areas, and customizes its content to help us improve our language proficiencies is another example.  

Intelligent apps like these don’t just use data for recommendations—they can make critical decisions and provide sophisticated services that were previously impossible. 

For developers, intelligent apps mean we’re no longer just coding—we’re teaching applications how to think, adapt, and make decisions. AI is reshaping every step of the development journey, helping us to build more intuitive and user-focused products. 

Next, we’ll examine the impact of AI on application design and architecture and how AI-assisted “pair programming” changes software development.

info

Register for the FREE webinar on Intelligent Apps with Microsoft and Forrester on September 26 to explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries.

There will be a showcase of real-world use cases that demonstrate how AI can be seamlessly integrated into cloud-native environments driving tangible business value.

AI in Design and Architecture: Fueling Creativity and Efficiency

AI technologies help developers automate and streamline their processes and provide new ways to think about design.  

In traditional application development, the design and architecture phase can take significant time, effort, and expertise. For instance, adopting a monolithic architecture or a microservices-based design can significantly increase coding, testing, and debugging complexity. The app may even behave differently when hosted locally than in a deployed environment. It may take hours to track down an issue manually—time developers could spend elsewhere. 

Conversely, we can generate numerous design iterations in seconds with AI, each optimized to fulfill specific user needs and business goals. AI tools can quickly model various scenarios' impacts and software design patterns in the architectural design phase. It provides insights into performance and potential issues and suggests improvements. AI can also generate skeleton code, allowing developers to prototype different solutions or test specific components and technologies rapidly. By reviewing thousands of lines of code in seconds, AI allows us to focus on creating apps that serve users through feature development instead of searching for bugs.  

Integrating AI into the design and architecture phase can even extend our creative capabilities. By leveraging the power of generative AI—like large language models (LLMs)—and their broad knowledge and context going beyond software, we can better brainstorm apps and feature ideas. We can go beyond code and software architecture and get ideas about user experience, logo and branding, or marketing concepts. And as AI tools deliver insights and ideas we would have never considered; it encourages more innovation.

info

Watch Episode 01 of the Learn Live session to learn how to build an end-to-end intelligent app solution architecture.

The Power of AI-Assisted Pair Programming in Building Applications

AI-assisted pair programming is a collaborative coding approach where an AI system—like GitHub Copilot or TestPilot—assists developers during coding. It’s an increasingly common approach that significantly impacts developer productivity. In fact, GitHub Copilot is now behind an average of 46 percent of developers’ code and users are seeing 55 percent faster task completion on average. 

For new software developers, or those interested in learning new skills, AI-assisted pair programming are training wheels for coding. With the benefits of code snippet suggestions, developers can avoid struggling with beginner pitfalls like language syntax. Tools like ChatGPT can act as a personal, on-demand tutor—answering questions, generating code samples, and explaining complex code syntax and logic. These tools dramatically speed the learning process and help developers gain confidence in their coding abilities. 

Building applications with AI tools hastens development and provides more robust code. For example, GitHub Copilot helps us write new code accurately. It also quickly makes sense of existing code, suggests whole new lines or blocks of code, and even detects errors and proposes fixes. It’s like having a second coder working alongside us—one who never tires and is trained on billions of lines of code. 

However, it’s also important to acknowledge the limitations of AI. While generative AI-assistance technologies like GitHub Copilot are impressive, they’re still imperfect. They can sometimes produce erroneous, non-functional, or vulnerable code. They can also lack nuanced, domain-specific knowledge.  

So, although these tools are helpful, we can’t rely on them fully—these are programming aids rather than definitive solutions. AI-assisted pair programming is about augmenting the human developer’s skills, not replacing them.

Debugging and Improving Code With AI: Enhancing Application Quality

Identifying and correcting code errors is often time-consuming and challenging for developers. AI streamlines the debugging process by automatically detecting anomalies, suggesting fixes, and learning from previous bugs to prevent them from recurring. 

We can use generative AI to create unit tests from code snippets, from simple parameter checking and input validation to complex mocks of database and network services. Automated AI testing improves code coverage and robustness. It saves significant time compared to manually crafting unit tests. 

AI also revolutionizes code reviews by scanning and highlighting potential issues—like code patterns leading to a bug or a coding standards violation—and suggesting ways to simplify or refactor the code for readability and performance. This reduces human error and makes the code review process more efficient. Its benefits extend to small teams, where the availability of developers is crucial to deadlines, and large teams, where effective collaboration depends on readability and code consistency. AI further saves time by triaging issues so developers can prioritize the most mission-critical ones. 

Aside from improving code quality, AI is critical to understanding system behavior. Intelligent AI-assisted software development tools can monitor and audit system behavior, giving developers a better insight into how their code performs in different scenarios. AI can track patterns, identify inefficiencies, and provide alerts when they detect unusual behavior—enabling developers to enhance their code further. 

By predicting and identifying potential vulnerabilities, intelligent AI-assisted coding apps like Microsoft Security Copilot are pivotal in ensuring application functionality, reliability, and security. These AI tools are continually trained and upgraded with large codebases and user feedback that refines their responses. This ability to learn from past mistakes makes AI an incredibly powerful tool that will continue to expand its capabilities in the future.

info

Complete the AI Cloud Skills Challenge to build on you AI application development skills with paired programming using generative AI and GitHub Copilot.

AI and the Future of Application Development

The rapid advancements in AI and machine learning technologies point to an exciting future for application development. 

In “The Future of Applications,” software developer Mike Cann illustrates how LLMs might revolutionize application development. Cann that suggests fully-autonomous coding—AI-generated applications created entirely from prompts—may supersede traditional hand-crafted applications. Dynamically generated user interfaces could offer personalized and customizable experiences while we can design APIs for machine consumption and efficiency rather than human readability. Furthermore, we can store data in less structured, more adaptable, and flexible forms. 

AI is already making application development more accessible. With AI handling the complex, technical aspects of development, less-technical individuals can take a greater role in building applications. Consequently, AI can potentially democratize software engineering. 

Intelligent apps will continue to evolve, becoming more efficient, adaptable, and capable of learning from user interactions. Similarly, AI will inevitably become integral in every stage of app development.

Summary

AI has already created a new development paradigm—one in which machine intelligence and human creativity come together to produce innovative, efficient, and robust applications. AI already assists us at every stage of app development, from design and architecture to coding and debugging. 

Developers who embrace AI tools and integrate them into their processes can enhance their productivity, improve the quality of their applications, and expand the possibilities of their application development.  

The future of development is automated and AI-driven, and it’s here today. Getting started is easy—try an AI-powered development tool like GitHub Copilot to see how AI-assisted pair programming can enhance your productivity and code quality.

Exercise

- - +
Skip to main content

11 posts tagged with "github-copilot"

View All Tags

· 9 min read
It's 30DaysOfIA

In the last article, we created an Intelligent App that leverages Azure AI Vision to analyze images and extract data. Develop an API to perform optical character recognition (OCR) on uploaded images and testing this API locally.

In this article, we will deploy the web API to the cloud platform using Azure Kubernetes Service.

What We'll Cover:

  • Understanding Azure AI Vision and Azure Kubernetes Service
  • Build a Python Web API to perform OCR
  • Test the API locally

image depicting an Intelligent App using AI

Jumpstart Your AI Journey: Building Your First Intelligent App with Azure AI and AKS (2)

In the previous article we explored the creation of an Intelligent App that leverages Azure AI Vision to analyze images and extract data. We learned how to build a Python Web API to perform OCR on uploaded images and subsequently test this API locally.

In this article we will use Azure Kubernetes Service (AKS) to develop, publish, and maintain our app in the cloud on Azure.

Let’s get started!

Prerequisites

To follow this tutorial, ensure you have completed Jumpstart Your AI Journey: Building Your First Intelligent App with Azure AI and AKS (1).

Pushing a Container Image to Azure Container Registry (ACR)

To start, open your CLI or terminal and type the following command:

az login

Follow the instructions displayed on your browser to enter your Azure credentials.

Once authenticated, you’ll initiate a secure connection between your local environment and Azure. This process grants you access to cloud services and resources.

Next, type the command below in your terminal to set up a new Azure Container Registry (ACR) to store your container images:

az acr create --resource-group computer-vision --name <name-of-azure-container-registry> --sku Basic

Remember to replace <name-of-azure-container-registry> with your container registry name. The name must be unique within Azure and comply with these rules.

The command above creates an Azure Container Registry (ACR) in the computer-vision resource group under the Basic SKU. This ACR is your secure and private repository for storing container images within Azure.

Next, log in to the registry with the following command:

az acr login -n <name-of-azure-container-registry>

The az acr login command above lets you securely authenticate and access the specified ACR without providing their Azure credentials each time.

Now, run the following command in your terminal. It will display the endpoint URL to log in to and interact with the ACR for pushing and pulling container images.

az acr show --name <name-of-azure-container-registry> --query loginServer --output table

This returns an endpoint URL as follows:

Result
----------------------------------
<name-of-azure-container-registry>.azurecr.io

Now, run the following command to show all container images, their repository, tags, and size:

docker images
REPOSITORYTAGIMAGE IDCREATEDSIZE
intelligent-applatesta7bf9f75361716 hours ago197MB

Tags are necessary to push Docker images to a remote registry like Azure Container Registry. They also let you differentiate between different versions of the same image and upload or download the one you want.

Run the following command to tag your Docker image:

docker tag intelligent-app <name-of-azure-container-registry>.azurecr.io/intelligent-app:v1

Then, run the docker images command again to check your tagged image:

docker images
REPOSITORYTAGIMAGE IDCREATEDSIZE
intelligent-applatestc52168039265About a minute ago197MB
<name-of-azure-container-registry>.azurecr.io/intelligent-appv1c52168039265About a minute ago197MB

Now run the following command so Docker can securely upload the image to your Azure Container Registry:

docker push <name-of-azure-container-registry>.azurecr.io/intelligent-app:v1

Once we've deployed the image to the container registry, AKS can access it during deployment.

info

Watch Episode 03 for the Learn Live session to experience a guided session on how to build, test and deploy an end-to-end intelligent app solution.

Deploying the Intelligent App on Azure Kubernetes Service (AKS)

Before we deploy our Intelligent App to AKS, we need to provision an AKS cluster and define Kubernetes manifests.

To provision an AKS cluster to host our application, we specify the desired configurations for the cluster, such as the number of nodes, node size, and networking options. But first, download and install the Kubernetes command-line tools (kubectl), a client credential plugin implementing Azure authentication:

az aks install-cli

If you’re using Linux, review this tutorial. Then run the following:

sudo az aks install-cli

Next, run the following command in your terminal to enable access to the networking-related resources and services provided by the `Microsoft.Network`` namespace in Azure:

az provider register --namespace Microsoft.Network

Now, we must create an AKS cluster. Run the following command to create an AKS cluster named aks-intelligent-app in the computer-vision resource group.

az aks create --resource-group computer-vision --name aks-intelligent-app --node-count 1 --generate-ssh-keys

The command above specifies the target resource group: computer-vision. The node pool is configured with one virtual machine (VM), and the secure shell (SSH) keys for secure node access are generated automatically.

Next, run the following command to update the AKS cluster you created by attaching it to your ACR. Doing so allows the AKS cluster to pull container images from the specified ACR when deploying workloads to the cluster.

az aks update -n aks-intelligent-app -g computer-vision --attach-acr <name-of-azure-container-registry>

Then, run the command below to configure kubectl to work with our AKS cluster in the computer-vision resource group.

az aks get-credentials --resource-group computer-vision --name aks-intelligent-app

The command above retrieves the necessary credentials and context information required for kubectl to communicate with the AKS cluster.

We still have to define the Kubernetes manifests written in YAML, describing the desired state of our application deployment, including containers, networking, and scaling rules. We’ll prepare these manifests, including Deployment and Service configurations, to define how our application should be deployed and exposed.

Create a folder named Deployment in the project root directory. Next, create two files in the deployment folder: deployment.yml and service.yml.

Add the following configuration to the deployment.yml file, replacing the <name-of-azure-container-registry> placeholder with your registry’s name:

apiVersion: apps/v1
kind: Deployment
metadata:
name: intelligent-app
spec:
replicas: 1
selector:
matchLabels:
app: intelligent-app
template:
metadata:
labels:
app: intelligent-app
spec:
nodeSelector:
kubernetes.io/os: linux
containers:
- name: intelligent-app
image: <name-of-azure-container-registry>.azurecr.io/intelligent-app:v1
resources:
limits:
memory: 512Mi
cpu: "1"
requests:
memory: 256Mi
cpu: "0.2"
ports:
- containerPort: 5000
env:
- name: FLASK_DEBUG
value: "1"
- name: VISION_KEY
value: <THE-KEY-1-VALUE-FROM-YOUR-AZURE-AI-SERVICE>
- name: VISION_ENDPOINT
value: <THE-ENDPOINT-VALUE-FROM-YOUR-AZURE-AI>

Additionally, edit the VISION_KEY and VISION_ENDPOINT environment variables above according to the API key and endpoint of your Azure AI Services instance.

Then, add the following configuration to the service.yml file:

apiVersion: v1
kind: Service
metadata:
name: intelligent-app-service
spec:
type: LoadBalancer
ports:
- protocol: TCP
port: 80
targetPort: 5000
name: port5000
selector:
app: intelligent-app

Now, we’ll deploy our application using kubectl. Applying the Kubernetes manifests creates the necessary resources and deploys our containerized application to the AKS cluster.

First, change the terminal to the deployment folder:

cd Deployment 

Then, run the following command to create or update Kubernetes resources defined in the deployment.yml file:

kubectl apply -f deployment.yml

Create a Kubernetes Service resource in a Kubernetes cluster based on the configuration defined in the service.yml file using the code below:

kubectl apply -f service.yml

Once you’ve applied the resource definition and the service configuration contained in the deployment.yml and the service.yml files, open the aks-intelligent-app Kubernetes Service in the Azure Portal, select Workloads under Kubernetes resources on the sidebar, and find the deployment named intelligent-app. It must have the status “Ready 1/1”. If you encounter an issue with this status, check out these troubleshooting resources.

image of selecting the deployment in Azure Portal

Testing the Intelligent App on AKS

To test the app on AKS, first, run the command below:

kubectl get services

This command lists the Services and their corresponding details, including the Service name, cluster IP address, external IP, and ports.

NAMETYPECLUSTER-IPEXTERNAL-IPPORT(S)AGE
intelligent-app-serviceLoadBalancer10.0.77.6020.121.76.15380:30936/TCP47s
kubernetesClusterIP10.0.0.1<none>443/TCP14m

The output above shows a Kubernetes Service named intelligent-app-service with a type set to LoadBalancer. It’s reachable from within the cluster using the cluster IP 10.0.77.60 and accessible externally via the external IP 20.121.76.153 on port 80 (mapped to port 30936).

Note: Your set of IP addresses will differ. Remember to use your unique external IP address when testing with Postman.

To test the deployed app, go to Postman, replace the URL with the external IP of the Kubernetes Service you just deployed, and click Send:

mage of sending the app in Postman

As we can see, our Intelligent App has successfully deployed to AKS and is functioning on the cloud as expected.

Exercise

Next Steps

In this two-part article, we explored the creation of an Intelligent App that leverages Azure AI Vision to analyze images and extract data. We learned how to build a Python Web API to perform OCR on uploaded images and subsequently deploy this API via Azure Kubernetes Service.

Besides OCR and Image Analysis, you can continue exploring Azure’s vast array of services and experiment further with Azure AI and AKS by applying various practical uses to your Intelligent Apps, including natural language processing, speech recognition and synthesis, sentiment analysis for customer feedback, and automated content moderation.

+ + \ No newline at end of file diff --git a/30daysofIA/tags/github-copilot/page/5/index.html b/30daysofIA/tags/github-copilot/page/5/index.html index 1728f82b03..bb5be371c4 100644 --- a/30daysofIA/tags/github-copilot/page/5/index.html +++ b/30daysofIA/tags/github-copilot/page/5/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "github-copilot" | Build Intelligent Apps On Azure +11 posts tagged with "github-copilot" | Build Intelligent Apps On Azure @@ -14,13 +14,13 @@ - - + +
-

9 posts tagged with "github-copilot"

View All Tags

· 10 min read
It's 30DaysOfIA

Explore real-world examples of how Intelligent Apps revolutionize logistics, retail, finance, manufacturing, tech, and medicine.

What We'll Cover:

  • Real-world businesses revolutionizing operations with Intelligent Apps
  • High level architecture for industry scenarios
  • Implementation architecture overview across industries

A decorative header image for the blog

Real-world Success Stories

According to IBM’s 2022 Global AI Adoption Index, 35% of surveyed companies already use AI in their business, and 42% are exploring the idea. Organizations lagging behind the AI curve will quickly fall behind early adopters, who are already enjoying its benefits and boosting their investment in this technology.

In the first part of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” we explored how organizations can use AI-powered apps, or intelligent apps, to transform their operations, decision-making, and customer experiences, leaping ahead of the competition.

Intelligent apps significantly improve operational efficiency and customer engagement across industries like logistics, retail, finance, manufacturing, tech, and healthcare. In this article, we’ll spotlight real-world success stories of businesses revolutionizing operations with intelligent apps using Microsoft Azure’s app, data, and AI services.

info

Register for the Learn Live on September 21 for a live guided experience with SMEs on how to build end-to-end solution architecture for real world implementation of Intelligent Apps.

Streamlining Operations: Intelligent Apps in the Airline Industry

Aerospace pioneer Airbus leveraged intelligent apps to streamline its operations and innovate its services with two ground-breaking modernizations that use Azure apps, data, and AI services to reimagine pilot training and predict aircraft maintenance issues. 

Aviation technology’s rapid evolution compelled Airbus to reconceive their approach to training pilots. The aircraft manufacturer created a pilot training chatbot by combining Azure AI Services with Speech to Text and Text to Speech. The bot helps trainee pilots navigate over 6,000 pages of technical information. Pilots can ask questions verbally for quick answers, aiding their understanding of dense information and speeding up training. And when Airbus needed to accommodate its military consumers, deploying its Kubernetes cluster to a disconnected, “air-gapped” environment met the strict security requirements of military aircraft training.

A chatbot could look like the following diagram as Azure services work together to create a conversational experience. Its components could include security and governance, bot logic and user experience (UX), bot cognition and intelligence, data extract, transfer, and load (ETL) operations, quality assurance and enhancements, logging, and monitoring and reporting.

A diagram of a conversational experience enabled by Azure services

Airbus also used AI to improve its aircraft maintenance and safety. They deployed AI Anomaly Detector to gather and analyze telemetry data from multiple flights, enabling more profound insights into wear and tear on military aircraft operating in harsh conditions. This AI application helps Airbus better understand each airplane’s health. Airbus can predict and fix potential problems before they occur, improving the aircraft’s safety and operational readiness.

AI and Azure are changing how organizations like Airbus operate, even in strict, highly regulated markets with stringent security requirements. And by streamlining pilot training and staying ahead of aircraft maintenance, Airbus helps its military and commercial clients reliably deliver their goods across the globe, maintaining their critical supply chains.

Boosting Customer Service: Intelligent Apps in the Retail Industry

The retail industry has been among the first to adopt and benefit from intelligent apps. For example, the leading American used car retailer, CarMax, uses intelligent apps to transform the car shopping experience.

Before embracing AI, CarMax faced a significant challenge: Manually creating text summaries was time-consuming and labor-intensive. CarMax decided to leverage Azure OpenAI Service to automatically generate car research pages, offering customers valuable insights while enhancing the website’s search engine rankings.

This AI-driven approach has significantly streamlined the process, achieving in a few months what would have taken years manually. Also, the AI system condenses thousands of customer reviews into a few readable sentences, offering potential buyers a quick overview of others’ experiences.

CarMax has improved their customer experience while boosting operational efficiency through AI adoption. CarMax’s editorial staff focuses on creating strategic, longer-form content with the time saved, enhancing productivity, and driving more website traffic.

CarMax’s success story serves as a testament to the potential of intelligent apps in the retail industry.

Enhancing Cybersecurity: Intelligent Apps in Finance

Intelligent apps also impact the highly regulated financial services sector. Swift, a leading infrastructure provider for financial messaging services, uses intelligent apps to enhance decision-making and combat financial crime.

The massive growth of cross-border transactions and instant payment networks led to a surge in financial fraud that can cost the industry hundreds of billions of dollars every year. So, Swift and Microsoft partnered to build an anomaly detection model using federated learning techniques with Azure Machine Learning and Azure confidential computing.

Microsoft Purview helped govern data, as the federated learning method partially trained each model in a silo before aggregating them. This approach enabled Swift and Microsoft to develop a highly accurate model without copying or moving data from Swift members’ secure locations, ensuring the highest level of security and privacy.

Financial organizations can now deploy this trained ML model to run a real-time fraud detection system, lowering the cost of financial fraud.

Organizations can deploy a fraud detection system like the following example on Microsoft Azure. The system ingests data from streams or storage, runs analytics, and uses ML to detect fraud and store results. It then alerts users and other systems to the security issue.

A diagram of an example fraud detection system

Swift provides the innovative model to its banking partners, who can share data to further train the model. Azure confidential computing securely runs new models on current data to accumulate insights, generating an ongoing learning cycle. This iterative approach strengthens the global detection of fraudulent financial transactions through an increasingly faster and more accurate model. 

The collaboration between Swift and Microsoft demonstrates how combining various Azure services creates Intelligent Apps to enhance decision-making in finance. These apps solve real problems at an unprecedented global scale. 

Enhancing Efficiency: Intelligent Apps in Manufacturing

3M, known for Post-it Notes and other innovative products, wielded the power of Intelligent Apps to standardize, automate, and accelerate its sales forecasting. The company needed a unified, automated approach to replace its multiple manual methods.

It adopted Azure Machine Learning and other Azure AI Services to develop, train, and implement 1,500 custom models for each region and division, replacing manual methods. This solution delivers timely finance, sales, and marketing insights while freeing teams from time-consuming manual data manipulation tasks.

3M used Azure Machine Learning, automated machine learning (AutoML), and the Many Models Solution Accelerator to train and score numerous machine learning models in parallel. This approach significantly shortened the company’s development cycle time.

Then, 3M integrated Microsoft Power BI into their solution to compile and visualize results more effectively. Executives now have an unbiased and unfiltered view of data for better decision-making. With its improved ability to forecast sales, 3M can focus on manufacturing the products it needs efficiently.

3M’s success demonstrates how combining various Azure services can enhance efficiency in the manufacturing industry. These apps improve operational processes while enabling employees to focus on higher-level tasks. Strategic decisions are more targeted, and business conversations are more meaningful.

Driving Innovation: Intelligent Apps in the Technology Industry

As expected, the tech industry is at the forefront of leveraging Intelligent Apps to drive innovation. Elastic saw an opportunity to use intelligent apps to optimize its search services and meet customer needs more effectively. The company built a managed, secure, and scalable solution for its customers, Elastic Cloud, by leveraging Microsoft Azure.

Elastic Cloud uses Azure Machine Learning and other Azure AI Services to automate many basic management tasks, such as backing up data, upgrading, and scaling deployments. This approach enables Elastic’s customers to focus less on managing the software and more on serving their own clients. Customers can quickly deploy and manage Elastic Cloud on Kubernetes (ECK) using Azure Kubernetes Service.

As a result, Elastic’s cloud business is growing twice as fast as its on-premises solutions. Enterprise search users have reported significant improvements in system availability and employee satisfaction.

Additionally, Elastic incorporated Azure OpenAI Service into its Elasticsearch Relevance Engine (ESRE), providing more relevant answers to search queries. ESRE enables text, vector, and hybrid search, offering a more effective and accurate response to queries.

Elastic’s success story demonstrates how combining various Azure services can create intelligent Apps to drive innovation in the tech industry. These apps help improve operational processes and deliver better results quickly.

Revolutionizing Healthcare: Intelligent Apps in Medicine

Intelligent apps also drive transformative changes in the healthcare sector. Cambridgeshire and Peterborough NHS Foundation Trust, a healthcare provider in the United Kingdom, gradually shifted their on-premises systems to the cloud using Azure, improving flexibility, and lowering costs. They needed a solution for their electronic patient record (EPR) system that integrates 23 years of data across multiple systems and formats.

The Trust used Microsoft Azure and Azure Cognitive Search, including semantic search for understanding intent and contexts and cognitive capabilities for extracting insights from data, to standardize and automate data management. This approach made legacy, archive, and live data more discoverable, saving clinicians’ time. The Trust’s solution searches through millions of clinical documents in seconds, even recognizing handwritten notes without needing a separate optical character recognition (OCR) solution to extract text from images (Azure Cognitive Search automatically does it). Clinicians can now uncover previously inaccessible insights, enabling them to offer more personalized care.

The Trust also used Integration Services tools such as Azure Logic Apps to create workflows without writing code and Event Grid to connect Azure and third-party services through a publisher-subscriber model. They switched on a live feed from their new EPR system to Azure to feed real-time information into other applications. Researchers can now access anonymized data to fuel medical innovations beyond the NHS.

The healthcare provider also invested heavily in Power BI’s data visualization and analytical functions to sift through massive amounts of information. This level of data accessibility was previously impossible with their on-premises system. The new insights have significantly improved clinicians’ efficiency for better patient care.

Data accessibility and security are critical in the healthcare industry. Intelligent Apps empower organizations like the Trust to revolutionize medical care. AI capabilities helped the Trust improve its operational efficiency and user experiences, setting a new standard for healthcare in the digital age.

Summary

AI is driving transformation and real-world successes across industries. Intelligent apps are reshaping our world by streamlining operations in manufacturing and logistics, enhancing customer experience in the retail sector, improving decision-making in finance, driving innovation in tech, and revolutionizing healthcare.

As organizations adopt AI, they’re innovating in ways that were once too difficult, expensive, or outright impossible. Companies that wait to embrace intelligent apps risk the chance of falling behind. By embracing intelligent apps and leveraging the capabilities of Azure, you can harness the transformative potential of AI to remain on the cutting edge of technology.

Exercise

- - +
Skip to main content

11 posts tagged with "github-copilot"

View All Tags

· 10 min read
It's 30DaysOfIA

Explore how digital natives and Independent Software Vendors (ISVs) can transform their business operations and generate higher value for customers with generative AI and intelligent apps.

What We'll Cover:

  • Potential of Generative AI in application development
  • Challenges with Generative AI
  • Integrating Generative AI with business operations

image of generative AI and intelligent apps used in business

Unleashing Business Transformation: How Digital Natives Can Leverage Generative AI

Generative AI is no longer a technological flight of fancy. It’s here, and it’s rewriting the rules of business innovation. 

This type of AI leverages machine learning algorithms to create new data with similar characteristics to a given dataset. It goes beyond text generation using generative pretrained transformer (GPT) models, covering an array of applications that span from image synthesis to translations and more. It also powers a new class of intelligent applications, which combine AI capabilities with traditional software functions for enhanced user experiences and advanced business solutions. 

Generative AI is transforming how digital natives and independent software vendors (ISVs) operate. Generative AI offers new possibilities for creating unique business value, enhancing customer experiences, and driving growth. 

It plays a critical role in applications, improving functionality, aiding decision-making, and automating complex tasks to deliver dynamic, uniquely personalized user experiences. Generative AI also enables and automates data-driven outcomes, functions, and features.

Integrating AI capabilities into customer-facing apps and turning them into intelligent apps benefits both the customer and the business, as we discussed in Demystifying Intelligent Applications: Leveraging AI in App Development. You get faster product development workflows and better business intelligence for sales and marketing, while your users get more natural and context-aware features and interactions. 

That said, to realize generative AI’s potential, you need some strategy and knowledge of how and where it fits into your current business and operations. We’ll examine this technology’s potential to transform business operations, its risks, and rewards, and how to integrate it into your existing operations. 

Unpacking the Potential of Generative AI

Generative AI is creating new opportunities in the digital playing field. It empowers businesses to generate insights, design products, and automate processes in ways and at speeds that were previously unimaginable. You can now predict customer behavior more accurately, offer highly personalized user experiences, and prototype innovative ideas rapidly. 

For example, Microsoft used generative AI to reinvent web search in the new AI-powered Bing and Edge. The search experience has evolved from a solitary user flow of typing in keywords and scrolling through results. It’s now a conversational experience where Bing uses context to offer better quality and more personalized search queries and results. 

Similarly, Microsoft harnessed generative AI to transform its productivity suite into intelligent apps. Through Copilot for Microsoft 365, PowerPoint now empowers users to tell their stories by using DALL-E to generate images effortlessly. It also helps rephrase text-heavy slides to be more straightforward. Outlook now assists users to compose more effective emails, enabling better communication.

These are just some examples of how AI powers day-to-day work and operations. 

Businesses that ignore generative AI miss out on untapped market opportunities, letting competitors gain an edge in the digital race. By seeking ways to rethink assumptions and reinvent existing approaches, you can effectively use generative AI to create new opportunities, open products to new use cases and markets, unlock new growth and customer satisfaction levels, and increase your revenue.

info

Register for the intelligent apps webinar on Driving Business Value by Modernizing with Cloud-Native & AI with Microsoft and Forrester on September 26.

Explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries. 

The Crucial Role of Generative AI in Business Operations

Generative AI can be a powerful catalyst for business transformation. Let’s quickly tour some ways it can redefine business operations. 

Product development is traditionally a long and painstaking process. Now, tools like GitHub Copilot and TestPilot use generative AI to make coding and testing software easier, faster, and more robust. 

Similarly, machine learning as a service technology, such as Azure Machine Learning, democratizes the availability of machine learning. It helps collect, clean, and train models across datasets, making engineering and analyzing data more efficient and accessible.  

Generative AI also simplifies the process of creating intelligent apps. Previously, simulating thousands of prototypes, predicting their performance, and narrowing down to the most viable options could take weeks, months, or even years of iteration and data analysis. With generative AI, this process can be as quick as days, hours, or perhaps minutes. 

Customer experience is also ripe for transformation. Personalized recommendations drive engagement and sales, and generative AI can scale that personalization to every customer interaction using comprehensive and continually updated data. For instance, Microsoft leverages AI to enable personalization in Dynamics 365 Customer Insights. Using this tool, you can ask questions about your customers in natural language and gain valuable and actionable insights into your customer base. 

Lastly, marketing and sales can benefit from generative AI. For example, identifying current and future key markets and emerging trends helps companies stay ahead of the curve. Tools like Copilot in Power BI help you quickly understand and uncover insights about your business, enabling your team to craft targeted campaigns, create engaging content, and optimize your business strategy. 

Leveraging this full generative AI potential requires the right tools. GPT-4, for instance, can be instrumental in realizing effective business transformation. It excels in understanding context, generating human-like text, and learning from minimal data input, making it a formidable tool in any digital native's arsenal. Platforms like Azure OpenAI Service bring GPT-4’s power to your fingertips.

However, as with any powerful technology, it’s unwise to treat generative AI as a perfect solution. You must know the challenges and risks of not leveraging it effectively, including technical and human-factor considerations. 

Without generative AI, your company risks missing the opportunity to please customers via personalization, as discussed previously. You’ll miss sales opportunities via business intelligence insights, limiting your ability to grow revenue and keep up with the competition. 

One challenge of leveraging generative AI’s full potential is finding the right tools. Fortunately, you can implement generative AI in your organization without investing in infrastructure, computational power, and expertise. Platforms like Azure OpenAI Service and Azure AI Services make AI easily accessible. They also work with Azure compute and data services like Azure Functions and Azure Kubernetes Service (AKS), enabling you to build intelligent apps easily and quickly. 

However, generative AI can’t do everything. It may need human expertise and intervention to use it fully. 

For example, generative AI can create unique data. However, the data may have limited diversity and complexity. The AI may need help recognizing additional considerations or requirements specific to a problem. It may also lack context due to various factors, such as the information’s freshness. Also, since it’s an emerging field that’s potentially worth $4.4 trillion, we’re still learning about the nuances and potential regulatory impact of generative AI. 

Perhaps the most crucial and challenging aspect of adopting generative AI is an organization’s required cultural shift. Your teams must learn how to use AI as your company fosters an understanding of AI’s potential and limitations. 

Begin integrating AI literacy into your company’s DNA. Knowledge of its potential and the readily available tools can inspire your teams to incorporate generative AI into their daily workflows and business operations quickly, improve productivity, and deliver superior customer experiences. 

No company wants to be outpaced by its competitors. You can balance generative AI’s potential with your business operations by staying aware of and adapting to the dynamic AI landscape.

Achieving Harmony: Integrating Generative AI with Existing Business Operations

Integrating generative AI into existing business operations doesn’t have to be disruptive. With the correct strategy, you can do so seamlessly. Your goal should be to augment and enhance your operations, not disrupt and replace. 

For example, Microsoft Cloud technologies leveraged AI to relieve healthcare organizations’ challenges, such as talent shortage and workforce burnout. Providers automated their workflows, invested in specific applications and connectors, and enhanced support for data and operations. 

Azure AI Services, such as text analytics for health, enable healthcare organizations to understand their largely unstructured and untapped medical data. Project Health Insights leans on AI to help healthcare professionals gain actionable insights and inferences from patient data. 

Azure Health Bot, an Intelligent App, empowers organizations to build and deploy legally compliant, powerful conversational healthcare experiences to patients. 

A successful AI integration like this begins with understanding. Knowing what generative AI can do and setting realistic expectations can help you identify where and how AI can best serve your operations. 

Next, you can foster a culture of AI literacy across teams, speeding up adoption by helping them understand how it can aid their work. The more familiar your team is with AI and its possibilities, the smoother the transition. Training and education programs play a pivotal role here. 

Finally, integration requires a systematic approach. It’s not about overhauling everything at once — it's about identifying areas where AI can add the most value, then gradually experimenting and scaling its use. Successful integration stories often feature a phased approach, starting small and growing steadily. 

As we move toward a future where generative AI is a staple in business operations, remember that every journey is unique. Although there’s no one-size-fits-all solution, a strategic approach and a keen understanding of your business needs will help you navigate this new landscape successfully.

Conclusion

Generative AI offers plenty of opportunities for businesses that embrace it. It’s fueling the next generation of intelligent applications, transforming everything from product development to customer engagement to marketing. This technology is the key to unlocking unprecedented growth and innovation. 

While the journey may have challenges, the potential rewards make generative AI worth exploring. As generative AI advances, Azure AI’s broad range of tools and services will help you navigate this fascinating field.

Exercise

  • Watch Episode 02 of the serverless edition Learn Live session to learn how to build, test and deploy an end-to-end intelligent app solution.
  • Complete the Cloud Skills Challenge to build on your AI skills.
  • Register for Ask the Expert: Azure Functions on September 26 for a live Q&A with the Product Team on how to build AI apps using serverless computing.
+ + \ No newline at end of file diff --git a/30daysofIA/tags/github-copilot/page/6/index.html b/30daysofIA/tags/github-copilot/page/6/index.html index c89dec1bb8..f55c82f7dc 100644 --- a/30daysofIA/tags/github-copilot/page/6/index.html +++ b/30daysofIA/tags/github-copilot/page/6/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "github-copilot" | Build Intelligent Apps On Azure +11 posts tagged with "github-copilot" | Build Intelligent Apps On Azure @@ -14,13 +14,13 @@ - - + +
-

9 posts tagged with "github-copilot"

View All Tags

· 8 min read
It's 30DaysOfIA

Let’s start with understanding the power of intelligent applications.

What We'll Cover:

  • What are intelligent applications?
  • Categories of Intelligent Apps
  • Breaking down the role of AI in intelligent apps

A decorative header image for the blog

Demystifying Intelligent Applications: Leveraging AI in App Development

Until recent years, when you heard the term “artificial intelligence” (AI), you might have pictured autonomous robots from sci-fi films. Now, perhaps your mind jumps to big data and machine learning (ML). Maybe you’re even thinking about the latest ChatGPT plugins and transformer architectures. Today, AI is more than these imaginations: it’s a reality increasingly woven into our day-to-day routines through intelligent applications.

Intelligent apps combine cloud-scale compute, data, and predictive or generative AI. These applications help you automate tasks and make data-driven decisions, aiding in leveraging business intelligence to gain meaningful, practical outcomes. 

For developers, the AI landscape isn’t an abstract complexity. It’s a real, tangible opportunity for evolving traditional applications, making them “intelligent,” and delivering more value to users. And with tools like Azure Kubernetes Service, Azure Cosmos DB, and Azure AI Services, stepping into the world of intelligent apps isn’t a steep climb but a steady trek across a manageable learning curve.

The Three Categories of Intelligent Applications

Before we start building, it’s critical to understand what makes an app “intelligent.” It’s not a universal definition so much as an identifier for apps that fall into one or more of the three primary categories: 

  • Outcome-based — This designation represents the broadest category of intelligent apps, whose principal value for users is the intelligent outcomes they provide. These apps process data to provide insightful results that enhance decision-making or help with tasks like trend prediction, resource management, and customer relationship management.

  • Functionality-based — This type of intelligent app takes AI integration a degree further, integrating AI as a function of the app to create or process its outputs, enhancing the app’s overall interactivity and usefulness.

  • Feature-based — This is the narrowest category, in which the app’s AI/ML component is one of its core features and primary selling points

Let’s dive more deeply into each category.

Outcome-Based Apps: Intelligent Outcomes Drive Success

Outcome-based applications are all about the results provided to the user. They function beyond merely processing data to offer insightful outcomes that drive success.

Think of a personal fitness tracker app that uses AI to analyze the user’s activity and provides actionable recommendations for achieving a goal. This type of app not only tracks your steps, sleep patterns, and heart rate but uses that data to provide you with personalized health advice. It helps you make informed lifestyle choices — the “intelligent” outcome.

Another example is a social media marketing app that uses AI for time-saving data analysis, task automation, and trend prediction. AI-based sentiment analysis can gauge customer perception while natural language processing (NLP) accurately interprets customer inquiries. Task automation can schedule posts and automate responses to common customer queries, and trend forecasting helps model and predict customer behavior to further personalize targeted promotional efforts. Building this type of intelligence into your apps provides cost-effective, actionable insights that guide sales and marketing strategies.

Functionality-Based Apps: AI-Driven Features Enhance User Experiences

As the name suggests, functionality-based applications integrate AI into how the app functions, but the end user drives the action and outcome. In this category of apps, AI takes a more active role in the user’s interaction by allowing them to use AI-created or processed output. These apps leverage AI technologies like Azure AI Services for NLP, image recognition, and pattern recognition to improve user experience.

Consider a music app that generates personalized playlists based on your listening habits or your smartphone’s camera app that uses AI to recognize faces and optimize photo settings. Even more advanced are the language translation apps that translate text from one language to another and can even understand and maintain the context and subtleties of the conversation. They use NLP to accomplish these feats.

Similarly, consider the latest e-commerce apps, whose AI-driven functionalities enable customers to try on clothes virtually, significantly enhancing the online shopping experience.

Feature-Based Apps: Integrating Advanced AI/ML Components

Finally, there are feature-based intelligent apps. These apps incorporate advanced AI/ML components like neural networks, internal large language models (LLMs), and sophisticated algorithms as core features for the user. In this type of application, AI is the primary feature for users to engage with and the primary selling point for the app.

Some examples of feature-based applications are chatbots and virtual agents built using Azure Bot Service. These robo-representatives use AI to provide more natural and human-like conversation, balancing superior efficiency with a friendlier, more personalized user experience. The new Bing is a prime example of this implementation, integrating AI to give users a knowledgeable copilot for web searches.

Similarly, OpenAI’s ChatGPT allows users access to its state-of-the-art language models available as Microsoft’s Azure OpenAI Service. It leverages the power of the highly sophisticated GPT-3 LLM to perform tasks like drafting emails, writing code, answering general knowledge questions, translating between languages, and tutoring in numerous subjects.

These apps don’t just use AI to provide outputs or improve user experiences: They push the boundaries of what’s possible in app development by integrating AI/ML components.

info

Register for the live Q&A Ask The Expert session on September 26 with the Azure Functions product engineering team.

Breaking Down the Role of AI in Intelligent Applications

Intelligent apps reflect the merging of traditional software design with the dynamic capabilities of AI. Let’s explore how AI integrates and amplifies these applications.

At its core, AI augments applications with the capacity to self-learn and self-improve. It allows you to create applications that predict, react, and adapt to a constantly changing environment. In the power grid management system for a major city, an AI-infused application can forecast power usage trends, react to anomalies, and adapt to sudden demand surges, ensuring optimal efficiency and preventing blackouts.

But AI’s role doesn’t end at prediction or decision-making: It also automates tasks. In enterprise solutions, it has moved beyond simple automation to encompass intelligent process automation (IPA). In this arena, AI algorithms understand, learn, and then automate entire digital workflows. For developers, this is a fertile ground for creating applications that can dramatically improve the operational efficiency of businesses.

Even more, AI substantially aids in development tasks. For example, there’s GitHub Copilot, an AI-powered code assistant that offers suggestions based not only on syntax but on development best practices and the intent of the code. AI has evolved from a tool you use to build apps to a partner that helps in creating them. 

Using AI to build intelligent apps isn’t about throwing out what’s worked before. It’s about enhancing the components that have the most impact and achieve the best results. Incorporating AI in apps elevates their predictive abilities, streamlines complex tasks, and even aids in the development and creation process. When you build intelligent apps, you don’t just address current problems. You anticipate future challenges and craft solutions that adapt over time.

Summary

As developers the power of AI is in our hands, and it’s our responsibility to harness its full potential. Each category tells the story of AI's transformative potential, from applications that deliver intelligent outcomes to those with AI capabilities to enrich the user experience and those pushing boundaries with advanced AI/ML technology.

Accessible and robust platforms like Azure AI can simplify the process of developing Intelligent Applications. Whether you’re refining an existing app or building a new one, just entering the world of AI, or already familiar with the landscape, take a moment to appreciate the potential that AI integration offers. You’re embracing this exciting frontier and shaping the future of app development. 

Exercise

  • Complete the Cloud Skills Challenge to build on your apps and AI skills.
  • Watch Episode 01 of the Serverless Edition Learn Live session to learn how to build an end-to-end intelligent app solution.
- - +
Skip to main content

11 posts tagged with "github-copilot"

View All Tags

· 9 min read
It's 30DaysOfIA

In the part of our series on intelligent apps, we’ll explore how AI is transforming application development, from design and architecture to building.

What We'll Cover:

  • Revolutionizing application development using AI
  • Infusing AI in application design architecture
  • AI Assisted Pair Programming
  • Future of App Development

A decorative image of application development with AI

Reimagining Application Development With AI: A New Paradigm

Artificial intelligence (AI) is impacting every industry. AI-powered development tools offer new functionalities and assist software developers throughout the development process, revolutionizing app development. From initial design and architecture to coding and debugging, AI reshapes how we approach application conception, creation, and iteration.

In part one of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” we explored the new breed of intelligent apps and how they use AI. In this article, we’ll see how AI technologies enhance developer toolkits and shape the future of app development, particularly through AI-enabled coding assistance and debugging.

The Dawn of Intelligent Apps: Revolutionizing Application Development

AI is more than just another addition to the development toolbox—it represents a revolution bringing unprecedented efficiency, innovation, and user engagement to our applications. 

What sets intelligent apps apart is how they can adapt and learn, helping us deliver unique, real-time user experiences.  

One example is an AI-driven health application that can track biometric data, predict potential health issues, provide personalized health advice, and even guide emergency services during a medical crisis. An AI-enabled language learning app that adapts to our learning speeds, recognizes our problem areas, and customizes its content to help us improve our language proficiencies is another example.  

Intelligent apps like these don’t just use data for recommendations—they can make critical decisions and provide sophisticated services that were previously impossible. 

For developers, intelligent apps mean we’re no longer just coding—we’re teaching applications how to think, adapt, and make decisions. AI is reshaping every step of the development journey, helping us to build more intuitive and user-focused products. 

Next, we’ll examine the impact of AI on application design and architecture and how AI-assisted “pair programming” changes software development.

info

Register for the FREE webinar on Intelligent Apps with Microsoft and Forrester on September 26 to explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries.

There will be a showcase of real-world use cases that demonstrate how AI can be seamlessly integrated into cloud-native environments driving tangible business value.

AI in Design and Architecture: Fueling Creativity and Efficiency

AI technologies help developers automate and streamline their processes and provide new ways to think about design.  

In traditional application development, the design and architecture phase can take significant time, effort, and expertise. For instance, adopting a monolithic architecture or a microservices-based design can significantly increase coding, testing, and debugging complexity. The app may even behave differently when hosted locally than in a deployed environment. It may take hours to track down an issue manually—time developers could spend elsewhere. 

Conversely, we can generate numerous design iterations in seconds with AI, each optimized to fulfill specific user needs and business goals. AI tools can quickly model various scenarios' impacts and software design patterns in the architectural design phase. It provides insights into performance and potential issues and suggests improvements. AI can also generate skeleton code, allowing developers to prototype different solutions or test specific components and technologies rapidly. By reviewing thousands of lines of code in seconds, AI allows us to focus on creating apps that serve users through feature development instead of searching for bugs.  

Integrating AI into the design and architecture phase can even extend our creative capabilities. By leveraging the power of generative AI—like large language models (LLMs)—and their broad knowledge and context going beyond software, we can better brainstorm apps and feature ideas. We can go beyond code and software architecture and get ideas about user experience, logo and branding, or marketing concepts. And as AI tools deliver insights and ideas we would have never considered; it encourages more innovation.

info

Watch Episode 01 of the Learn Live session to learn how to build an end-to-end intelligent app solution architecture.

The Power of AI-Assisted Pair Programming in Building Applications

AI-assisted pair programming is a collaborative coding approach where an AI system—like GitHub Copilot or TestPilot—assists developers during coding. It’s an increasingly common approach that significantly impacts developer productivity. In fact, GitHub Copilot is now behind an average of 46 percent of developers’ code and users are seeing 55 percent faster task completion on average. 

For new software developers, or those interested in learning new skills, AI-assisted pair programming are training wheels for coding. With the benefits of code snippet suggestions, developers can avoid struggling with beginner pitfalls like language syntax. Tools like ChatGPT can act as a personal, on-demand tutor—answering questions, generating code samples, and explaining complex code syntax and logic. These tools dramatically speed the learning process and help developers gain confidence in their coding abilities. 

Building applications with AI tools hastens development and provides more robust code. For example, GitHub Copilot helps us write new code accurately. It also quickly makes sense of existing code, suggests whole new lines or blocks of code, and even detects errors and proposes fixes. It’s like having a second coder working alongside us—one who never tires and is trained on billions of lines of code. 

However, it’s also important to acknowledge the limitations of AI. While generative AI-assistance technologies like GitHub Copilot are impressive, they’re still imperfect. They can sometimes produce erroneous, non-functional, or vulnerable code. They can also lack nuanced, domain-specific knowledge.  

So, although these tools are helpful, we can’t rely on them fully—these are programming aids rather than definitive solutions. AI-assisted pair programming is about augmenting the human developer’s skills, not replacing them.

Debugging and Improving Code With AI: Enhancing Application Quality

Identifying and correcting code errors is often time-consuming and challenging for developers. AI streamlines the debugging process by automatically detecting anomalies, suggesting fixes, and learning from previous bugs to prevent them from recurring. 

We can use generative AI to create unit tests from code snippets, from simple parameter checking and input validation to complex mocks of database and network services. Automated AI testing improves code coverage and robustness. It saves significant time compared to manually crafting unit tests. 

AI also revolutionizes code reviews by scanning and highlighting potential issues—like code patterns leading to a bug or a coding standards violation—and suggesting ways to simplify or refactor the code for readability and performance. This reduces human error and makes the code review process more efficient. Its benefits extend to small teams, where the availability of developers is crucial to deadlines, and large teams, where effective collaboration depends on readability and code consistency. AI further saves time by triaging issues so developers can prioritize the most mission-critical ones. 

Aside from improving code quality, AI is critical to understanding system behavior. Intelligent AI-assisted software development tools can monitor and audit system behavior, giving developers a better insight into how their code performs in different scenarios. AI can track patterns, identify inefficiencies, and provide alerts when they detect unusual behavior—enabling developers to enhance their code further. 

By predicting and identifying potential vulnerabilities, intelligent AI-assisted coding apps like Microsoft Security Copilot are pivotal in ensuring application functionality, reliability, and security. These AI tools are continually trained and upgraded with large codebases and user feedback that refines their responses. This ability to learn from past mistakes makes AI an incredibly powerful tool that will continue to expand its capabilities in the future.

info

Complete the AI Cloud Skills Challenge to build on you AI application development skills with paired programming using generative AI and GitHub Copilot.

AI and the Future of Application Development

The rapid advancements in AI and machine learning technologies point to an exciting future for application development. 

In “The Future of Applications,” software developer Mike Cann illustrates how LLMs might revolutionize application development. Cann that suggests fully-autonomous coding—AI-generated applications created entirely from prompts—may supersede traditional hand-crafted applications. Dynamically generated user interfaces could offer personalized and customizable experiences while we can design APIs for machine consumption and efficiency rather than human readability. Furthermore, we can store data in less structured, more adaptable, and flexible forms. 

AI is already making application development more accessible. With AI handling the complex, technical aspects of development, less-technical individuals can take a greater role in building applications. Consequently, AI can potentially democratize software engineering. 

Intelligent apps will continue to evolve, becoming more efficient, adaptable, and capable of learning from user interactions. Similarly, AI will inevitably become integral in every stage of app development.

Summary

AI has already created a new development paradigm—one in which machine intelligence and human creativity come together to produce innovative, efficient, and robust applications. AI already assists us at every stage of app development, from design and architecture to coding and debugging. 

Developers who embrace AI tools and integrate them into their processes can enhance their productivity, improve the quality of their applications, and expand the possibilities of their application development.  

The future of development is automated and AI-driven, and it’s here today. Getting started is easy—try an AI-powered development tool like GitHub Copilot to see how AI-assisted pair programming can enhance your productivity and code quality.

Exercise

+ + \ No newline at end of file diff --git a/30daysofIA/tags/github-copilot/page/7/index.html b/30daysofIA/tags/github-copilot/page/7/index.html index 2fbd720a22..9b0d194a37 100644 --- a/30daysofIA/tags/github-copilot/page/7/index.html +++ b/30daysofIA/tags/github-copilot/page/7/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "github-copilot" | Build Intelligent Apps On Azure +11 posts tagged with "github-copilot" | Build Intelligent Apps On Azure @@ -14,14 +14,13 @@ - - + +
-

9 posts tagged with "github-copilot"

View All Tags

· 4 min read
It's 30DaysOfIA

This Fall focus on building intelligent apps using AI and cloud-native technologies. #FallForIntelligentApps brings to you a learning journey to build your skills on creating differentiated experiences while modernizing your applications. It’s time to learn it all.

What We'll Cover

image

Get Ready To #FallForIntelligentApps starting September 18!

Today, we kick off the Fall season with content and activities to skill you up on all things Intelligent Apps or AI Apps on Azure with content, events, and community interactions! Read on to learn about what is coming!

Explore Our Initiatives

We have a number of initiatives planned for the month to help you learn and skill up on relevant technologies. Click on the links to visit the relevant pages for each

image

Register for the events!

What are 4 things you can do today, to jumpstart your learning journey?

#30Days Of Intelligent Apps

#30DaysOfIA is a series of daily blog posts grouped into themed weeks - taking you from core concepts to end-to-end solution examples in 30 days. Each article will provide conceptual lessons paired with exercises and resources to help you reinforce learnings and take next steps.

This series takes you through learning journey in six stages, each building on the previous week to help you skill up in a beginner-friendly way:

We will start with defining intelligent apps and then expand on how to build with cloud-native technologies like Azure Kubernetes Service, Azure Container Apps and Azure Functions, and take you through end-to-end scenarios for real world application development. Before we dive deep on intelligent apps, here is a high-level overview of the Intelligent Apps landscape on Azure:

image

Containers on Azure services offer you a wide range of capabilities, from simplicity to control to suit your different needs.

image -image

To start with the basics for developing Kubernetes applications, explore #30DaysOfCloudNative.

Cloud-native development when paired with serverless computing enhances your solution architecture for building cost optimized, resilient applications.

image

To start with the basics for serverless computing, explore #30DaysOfServerless.

Let’s Get Started

Now you know everything! We hope you are as excited as we are to dive into a full month of active learning and doing! Don't forget to subscribe for updates in your favorite feed reader! And look out for our first Intelligent Apps post Monday!

- - +
Skip to main content

11 posts tagged with "github-copilot"

View All Tags

· 10 min read
It's 30DaysOfIA

Explore real-world examples of how Intelligent Apps revolutionize logistics, retail, finance, manufacturing, tech, and medicine.

What We'll Cover:

  • Real-world businesses revolutionizing operations with Intelligent Apps
  • High level architecture for industry scenarios
  • Implementation architecture overview across industries

A decorative header image for the blog

Real-world Success Stories

According to IBM’s 2022 Global AI Adoption Index, 35% of surveyed companies already use AI in their business, and 42% are exploring the idea. Organizations lagging behind the AI curve will quickly fall behind early adopters, who are already enjoying its benefits and boosting their investment in this technology.

In the first part of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” we explored how organizations can use AI-powered apps, or intelligent apps, to transform their operations, decision-making, and customer experiences, leaping ahead of the competition.

Intelligent apps significantly improve operational efficiency and customer engagement across industries like logistics, retail, finance, manufacturing, tech, and healthcare. In this article, we’ll spotlight real-world success stories of businesses revolutionizing operations with intelligent apps using Microsoft Azure’s app, data, and AI services.

info

Register for the Learn Live on September 21 for a live guided experience with SMEs on how to build end-to-end solution architecture for real world implementation of Intelligent Apps.

Streamlining Operations: Intelligent Apps in the Airline Industry

Aerospace pioneer Airbus leveraged intelligent apps to streamline its operations and innovate its services with two ground-breaking modernizations that use Azure apps, data, and AI services to reimagine pilot training and predict aircraft maintenance issues. 

Aviation technology’s rapid evolution compelled Airbus to reconceive their approach to training pilots. The aircraft manufacturer created a pilot training chatbot by combining Azure AI Services with Speech to Text and Text to Speech. The bot helps trainee pilots navigate over 6,000 pages of technical information. Pilots can ask questions verbally for quick answers, aiding their understanding of dense information and speeding up training. And when Airbus needed to accommodate its military consumers, deploying its Kubernetes cluster to a disconnected, “air-gapped” environment met the strict security requirements of military aircraft training.

A chatbot could look like the following diagram as Azure services work together to create a conversational experience. Its components could include security and governance, bot logic and user experience (UX), bot cognition and intelligence, data extract, transfer, and load (ETL) operations, quality assurance and enhancements, logging, and monitoring and reporting.

A diagram of a conversational experience enabled by Azure services

Airbus also used AI to improve its aircraft maintenance and safety. They deployed AI Anomaly Detector to gather and analyze telemetry data from multiple flights, enabling more profound insights into wear and tear on military aircraft operating in harsh conditions. This AI application helps Airbus better understand each airplane’s health. Airbus can predict and fix potential problems before they occur, improving the aircraft’s safety and operational readiness.

AI and Azure are changing how organizations like Airbus operate, even in strict, highly regulated markets with stringent security requirements. And by streamlining pilot training and staying ahead of aircraft maintenance, Airbus helps its military and commercial clients reliably deliver their goods across the globe, maintaining their critical supply chains.

Boosting Customer Service: Intelligent Apps in the Retail Industry

The retail industry has been among the first to adopt and benefit from intelligent apps. For example, the leading American used car retailer, CarMax, uses intelligent apps to transform the car shopping experience.

Before embracing AI, CarMax faced a significant challenge: Manually creating text summaries was time-consuming and labor-intensive. CarMax decided to leverage Azure OpenAI Service to automatically generate car research pages, offering customers valuable insights while enhancing the website’s search engine rankings.

This AI-driven approach has significantly streamlined the process, achieving in a few months what would have taken years manually. Also, the AI system condenses thousands of customer reviews into a few readable sentences, offering potential buyers a quick overview of others’ experiences.

CarMax has improved their customer experience while boosting operational efficiency through AI adoption. CarMax’s editorial staff focuses on creating strategic, longer-form content with the time saved, enhancing productivity, and driving more website traffic.

CarMax’s success story serves as a testament to the potential of intelligent apps in the retail industry.

Enhancing Cybersecurity: Intelligent Apps in Finance

Intelligent apps also impact the highly regulated financial services sector. Swift, a leading infrastructure provider for financial messaging services, uses intelligent apps to enhance decision-making and combat financial crime.

The massive growth of cross-border transactions and instant payment networks led to a surge in financial fraud that can cost the industry hundreds of billions of dollars every year. So, Swift and Microsoft partnered to build an anomaly detection model using federated learning techniques with Azure Machine Learning and Azure confidential computing.

Microsoft Purview helped govern data, as the federated learning method partially trained each model in a silo before aggregating them. This approach enabled Swift and Microsoft to develop a highly accurate model without copying or moving data from Swift members’ secure locations, ensuring the highest level of security and privacy.

Financial organizations can now deploy this trained ML model to run a real-time fraud detection system, lowering the cost of financial fraud.

Organizations can deploy a fraud detection system like the following example on Microsoft Azure. The system ingests data from streams or storage, runs analytics, and uses ML to detect fraud and store results. It then alerts users and other systems to the security issue.

A diagram of an example fraud detection system

Swift provides the innovative model to its banking partners, who can share data to further train the model. Azure confidential computing securely runs new models on current data to accumulate insights, generating an ongoing learning cycle. This iterative approach strengthens the global detection of fraudulent financial transactions through an increasingly faster and more accurate model. 

The collaboration between Swift and Microsoft demonstrates how combining various Azure services creates Intelligent Apps to enhance decision-making in finance. These apps solve real problems at an unprecedented global scale. 

Enhancing Efficiency: Intelligent Apps in Manufacturing

3M, known for Post-it Notes and other innovative products, wielded the power of Intelligent Apps to standardize, automate, and accelerate its sales forecasting. The company needed a unified, automated approach to replace its multiple manual methods.

It adopted Azure Machine Learning and other Azure AI Services to develop, train, and implement 1,500 custom models for each region and division, replacing manual methods. This solution delivers timely finance, sales, and marketing insights while freeing teams from time-consuming manual data manipulation tasks.

3M used Azure Machine Learning, automated machine learning (AutoML), and the Many Models Solution Accelerator to train and score numerous machine learning models in parallel. This approach significantly shortened the company’s development cycle time.

Then, 3M integrated Microsoft Power BI into their solution to compile and visualize results more effectively. Executives now have an unbiased and unfiltered view of data for better decision-making. With its improved ability to forecast sales, 3M can focus on manufacturing the products it needs efficiently.

3M’s success demonstrates how combining various Azure services can enhance efficiency in the manufacturing industry. These apps improve operational processes while enabling employees to focus on higher-level tasks. Strategic decisions are more targeted, and business conversations are more meaningful.

Driving Innovation: Intelligent Apps in the Technology Industry

As expected, the tech industry is at the forefront of leveraging Intelligent Apps to drive innovation. Elastic saw an opportunity to use intelligent apps to optimize its search services and meet customer needs more effectively. The company built a managed, secure, and scalable solution for its customers, Elastic Cloud, by leveraging Microsoft Azure.

Elastic Cloud uses Azure Machine Learning and other Azure AI Services to automate many basic management tasks, such as backing up data, upgrading, and scaling deployments. This approach enables Elastic’s customers to focus less on managing the software and more on serving their own clients. Customers can quickly deploy and manage Elastic Cloud on Kubernetes (ECK) using Azure Kubernetes Service.

As a result, Elastic’s cloud business is growing twice as fast as its on-premises solutions. Enterprise search users have reported significant improvements in system availability and employee satisfaction.

Additionally, Elastic incorporated Azure OpenAI Service into its Elasticsearch Relevance Engine (ESRE), providing more relevant answers to search queries. ESRE enables text, vector, and hybrid search, offering a more effective and accurate response to queries.

Elastic’s success story demonstrates how combining various Azure services can create intelligent Apps to drive innovation in the tech industry. These apps help improve operational processes and deliver better results quickly.

Revolutionizing Healthcare: Intelligent Apps in Medicine

Intelligent apps also drive transformative changes in the healthcare sector. Cambridgeshire and Peterborough NHS Foundation Trust, a healthcare provider in the United Kingdom, gradually shifted their on-premises systems to the cloud using Azure, improving flexibility, and lowering costs. They needed a solution for their electronic patient record (EPR) system that integrates 23 years of data across multiple systems and formats.

The Trust used Microsoft Azure and Azure Cognitive Search, including semantic search for understanding intent and contexts and cognitive capabilities for extracting insights from data, to standardize and automate data management. This approach made legacy, archive, and live data more discoverable, saving clinicians’ time. The Trust’s solution searches through millions of clinical documents in seconds, even recognizing handwritten notes without needing a separate optical character recognition (OCR) solution to extract text from images (Azure Cognitive Search automatically does it). Clinicians can now uncover previously inaccessible insights, enabling them to offer more personalized care.

The Trust also used Integration Services tools such as Azure Logic Apps to create workflows without writing code and Event Grid to connect Azure and third-party services through a publisher-subscriber model. They switched on a live feed from their new EPR system to Azure to feed real-time information into other applications. Researchers can now access anonymized data to fuel medical innovations beyond the NHS.

The healthcare provider also invested heavily in Power BI’s data visualization and analytical functions to sift through massive amounts of information. This level of data accessibility was previously impossible with their on-premises system. The new insights have significantly improved clinicians’ efficiency for better patient care.

Data accessibility and security are critical in the healthcare industry. Intelligent Apps empower organizations like the Trust to revolutionize medical care. AI capabilities helped the Trust improve its operational efficiency and user experiences, setting a new standard for healthcare in the digital age.

Summary

AI is driving transformation and real-world successes across industries. Intelligent apps are reshaping our world by streamlining operations in manufacturing and logistics, enhancing customer experience in the retail sector, improving decision-making in finance, driving innovation in tech, and revolutionizing healthcare.

As organizations adopt AI, they’re innovating in ways that were once too difficult, expensive, or outright impossible. Companies that wait to embrace intelligent apps risk the chance of falling behind. By embracing intelligent apps and leveraging the capabilities of Azure, you can harness the transformative potential of AI to remain on the cutting edge of technology.

Exercise

+ + \ No newline at end of file diff --git a/30daysofIA/tags/github-copilot/page/8/index.html b/30daysofIA/tags/github-copilot/page/8/index.html index b980040e28..c7a0390c33 100644 --- a/30daysofIA/tags/github-copilot/page/8/index.html +++ b/30daysofIA/tags/github-copilot/page/8/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "github-copilot" | Build Intelligent Apps On Azure +11 posts tagged with "github-copilot" | Build Intelligent Apps On Azure @@ -14,14 +14,13 @@ - - + +
-

9 posts tagged with "github-copilot"

View All Tags

· 4 min read
It's 30DaysOfIA

Continue The Learning Journey through Fall For Intelligent Apps! 🍂

What We'll Cover

Thank you! ♥️

image

It's hard to believe that JavaScript on Azure hack-together is ending! It seems like just yesterday that we launched this initiative, and yet here we are, 15 days later, with an incredible amount of learning and growth behind us. So... it's time for a wrap!

From the bottom of our hearts, we want to thank each and every one of you for your participation, engagement, and enthusiasm. It's been truly inspiring to see the passion and dedication from this strong community, and we're honored to be a part of it. ✨

Recap of The JavaScript on Azure Global Hack-Together

As we wrap up this exciting event, we wanted to take a moment to reflect on all that we've accomplished together. Over the last 15 days, we've covered a lot of ground, from the basics of contributing to Open source to the exploration of the Contoso Real Estate project from its Frontend to its Backend and future AI implementation.

Now that the hack-together is ending, we want to make sure that you have all the resources you need to continue honing your skills in the future. Whether you're looking to make your fist contribution to open source, become an open source maintainers, collaborate with others, or simply keep learning, there are plenty of resources out there to help you achieve your goals. So, let's dive in and explore all the ways you can continue to grow your JavaScript skills on Azure!

JSonAzure Hack-together Roadmap 📍:

hack-together-roadmap (2)

Recap on past Livestreams🌟:

Day 1️⃣: Opening Keynote (Hack-together Launch): Introduction to the Contoso Real Estate Open-source project!, managing complex and complex enterprise architecture, new announcements for JavaScript developers on Azure

Day 2️⃣: GitHub Copilot & Codespaces: Introduction to your AI pair programmer (GitHub Copilot) and your virtual developer environment on the cloud (GitHub Codespaces)

Day 6️⃣: Build your Frontend using Static Web Apps as part of a complex, modern composable frontends (or micro-frontends) and cloud-native applications.

Day 9️⃣: Build a Serverless Backend using Azure Functions

Day 1️⃣3️⃣: Easily connect to an Azure Cosmos DB, exploring its benefits and how to get started

Day 1️⃣5️⃣: Being in the AI Era, we dive into the Azure OpenAI Service and how you can start to build intelligent JavaScript applications

📖 Self-Learning Resources

  1. JavaScript on Azure Global Hack Together Module collection
  2. Lets #HackTogether: Javascript On Azure Keynote
  3. Step by Step Guide: Migrating v3 to v4 programming model for Azure Functions for Node.Js Application

Continue your journey with #FallForIntelligentApps

Join us this Fall on a learning journey to explore building intelligent apps. Combine the power of AI, cloud-native app development, and cloud-scale data to build highly differentiated digital experiences. Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure. Engage in a self-paced learning adventure by following along the #30Days of Intelligent Apps series, completing the Intelligent Apps Skills Challenge, joining the Product Group for a live Ask The Expert series or building an end to end solution architecture with a live guided experience through the Learn Live series. Discover more here.

Hands-on practice: Make your first contribution to open source!

Join our GitHUb Discussion Forum to connect with developers from every part of world, see contributions from other, find collaborators and make your first contribution to a real-world project! -Don't forget to give the repo a star ⭐

Resources

All resources are accessible on our landing page

- - +
Skip to main content

11 posts tagged with "github-copilot"

View All Tags

· 8 min read
It's 30DaysOfIA

Let’s start with understanding the power of intelligent applications.

What We'll Cover:

  • What are intelligent applications?
  • Categories of Intelligent Apps
  • Breaking down the role of AI in intelligent apps

A decorative header image for the blog

Demystifying Intelligent Applications: Leveraging AI in App Development

Until recent years, when you heard the term “artificial intelligence” (AI), you might have pictured autonomous robots from sci-fi films. Now, perhaps your mind jumps to big data and machine learning (ML). Maybe you’re even thinking about the latest ChatGPT plugins and transformer architectures. Today, AI is more than these imaginations: it’s a reality increasingly woven into our day-to-day routines through intelligent applications.

Intelligent apps combine cloud-scale compute, data, and predictive or generative AI. These applications help you automate tasks and make data-driven decisions, aiding in leveraging business intelligence to gain meaningful, practical outcomes. 

For developers, the AI landscape isn’t an abstract complexity. It’s a real, tangible opportunity for evolving traditional applications, making them “intelligent,” and delivering more value to users. And with tools like Azure Kubernetes Service, Azure Cosmos DB, and Azure AI Services, stepping into the world of intelligent apps isn’t a steep climb but a steady trek across a manageable learning curve.

The Three Categories of Intelligent Applications

Before we start building, it’s critical to understand what makes an app “intelligent.” It’s not a universal definition so much as an identifier for apps that fall into one or more of the three primary categories: 

  • Outcome-based — This designation represents the broadest category of intelligent apps, whose principal value for users is the intelligent outcomes they provide. These apps process data to provide insightful results that enhance decision-making or help with tasks like trend prediction, resource management, and customer relationship management.

  • Functionality-based — This type of intelligent app takes AI integration a degree further, integrating AI as a function of the app to create or process its outputs, enhancing the app’s overall interactivity and usefulness.

  • Feature-based — This is the narrowest category, in which the app’s AI/ML component is one of its core features and primary selling points

Let’s dive more deeply into each category.

Outcome-Based Apps: Intelligent Outcomes Drive Success

Outcome-based applications are all about the results provided to the user. They function beyond merely processing data to offer insightful outcomes that drive success.

Think of a personal fitness tracker app that uses AI to analyze the user’s activity and provides actionable recommendations for achieving a goal. This type of app not only tracks your steps, sleep patterns, and heart rate but uses that data to provide you with personalized health advice. It helps you make informed lifestyle choices — the “intelligent” outcome.

Another example is a social media marketing app that uses AI for time-saving data analysis, task automation, and trend prediction. AI-based sentiment analysis can gauge customer perception while natural language processing (NLP) accurately interprets customer inquiries. Task automation can schedule posts and automate responses to common customer queries, and trend forecasting helps model and predict customer behavior to further personalize targeted promotional efforts. Building this type of intelligence into your apps provides cost-effective, actionable insights that guide sales and marketing strategies.

Functionality-Based Apps: AI-Driven Features Enhance User Experiences

As the name suggests, functionality-based applications integrate AI into how the app functions, but the end user drives the action and outcome. In this category of apps, AI takes a more active role in the user’s interaction by allowing them to use AI-created or processed output. These apps leverage AI technologies like Azure AI Services for NLP, image recognition, and pattern recognition to improve user experience.

Consider a music app that generates personalized playlists based on your listening habits or your smartphone’s camera app that uses AI to recognize faces and optimize photo settings. Even more advanced are the language translation apps that translate text from one language to another and can even understand and maintain the context and subtleties of the conversation. They use NLP to accomplish these feats.

Similarly, consider the latest e-commerce apps, whose AI-driven functionalities enable customers to try on clothes virtually, significantly enhancing the online shopping experience.

Feature-Based Apps: Integrating Advanced AI/ML Components

Finally, there are feature-based intelligent apps. These apps incorporate advanced AI/ML components like neural networks, internal large language models (LLMs), and sophisticated algorithms as core features for the user. In this type of application, AI is the primary feature for users to engage with and the primary selling point for the app.

Some examples of feature-based applications are chatbots and virtual agents built using Azure Bot Service. These robo-representatives use AI to provide more natural and human-like conversation, balancing superior efficiency with a friendlier, more personalized user experience. The new Bing is a prime example of this implementation, integrating AI to give users a knowledgeable copilot for web searches.

Similarly, OpenAI’s ChatGPT allows users access to its state-of-the-art language models available as Microsoft’s Azure OpenAI Service. It leverages the power of the highly sophisticated GPT-3 LLM to perform tasks like drafting emails, writing code, answering general knowledge questions, translating between languages, and tutoring in numerous subjects.

These apps don’t just use AI to provide outputs or improve user experiences: They push the boundaries of what’s possible in app development by integrating AI/ML components.

info

Register for the live Q&A Ask The Expert session on September 26 with the Azure Functions product engineering team.

Breaking Down the Role of AI in Intelligent Applications

Intelligent apps reflect the merging of traditional software design with the dynamic capabilities of AI. Let’s explore how AI integrates and amplifies these applications.

At its core, AI augments applications with the capacity to self-learn and self-improve. It allows you to create applications that predict, react, and adapt to a constantly changing environment. In the power grid management system for a major city, an AI-infused application can forecast power usage trends, react to anomalies, and adapt to sudden demand surges, ensuring optimal efficiency and preventing blackouts.

But AI’s role doesn’t end at prediction or decision-making: It also automates tasks. In enterprise solutions, it has moved beyond simple automation to encompass intelligent process automation (IPA). In this arena, AI algorithms understand, learn, and then automate entire digital workflows. For developers, this is a fertile ground for creating applications that can dramatically improve the operational efficiency of businesses.

Even more, AI substantially aids in development tasks. For example, there’s GitHub Copilot, an AI-powered code assistant that offers suggestions based not only on syntax but on development best practices and the intent of the code. AI has evolved from a tool you use to build apps to a partner that helps in creating them. 

Using AI to build intelligent apps isn’t about throwing out what’s worked before. It’s about enhancing the components that have the most impact and achieve the best results. Incorporating AI in apps elevates their predictive abilities, streamlines complex tasks, and even aids in the development and creation process. When you build intelligent apps, you don’t just address current problems. You anticipate future challenges and craft solutions that adapt over time.

Summary

As developers the power of AI is in our hands, and it’s our responsibility to harness its full potential. Each category tells the story of AI's transformative potential, from applications that deliver intelligent outcomes to those with AI capabilities to enrich the user experience and those pushing boundaries with advanced AI/ML technology.

Accessible and robust platforms like Azure AI can simplify the process of developing Intelligent Applications. Whether you’re refining an existing app or building a new one, just entering the world of AI, or already familiar with the landscape, take a moment to appreciate the potential that AI integration offers. You’re embracing this exciting frontier and shaping the future of app development. 

Exercise

  • Complete the Cloud Skills Challenge to build on your apps and AI skills.
  • Watch Episode 01 of the Serverless Edition Learn Live session to learn how to build an end-to-end intelligent app solution.
+ + \ No newline at end of file diff --git a/30daysofIA/tags/github-copilot/page/9/index.html b/30daysofIA/tags/github-copilot/page/9/index.html index 4a153002b1..4323edb475 100644 --- a/30daysofIA/tags/github-copilot/page/9/index.html +++ b/30daysofIA/tags/github-copilot/page/9/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "github-copilot" | Build Intelligent Apps On Azure +11 posts tagged with "github-copilot" | Build Intelligent Apps On Azure @@ -14,13 +14,14 @@ - - + +
-

9 posts tagged with "github-copilot"

View All Tags

· 2 min read
It's 30DaysOfIA

September is almost here - and that can only mean one thing!! It's time to 🍂 Fall for something new and exciting and spend a few weeks skilling up on relevant tools, techologies and solutions!!

Last year, we focused on #ServerlessSeptember. This year, we're building on that theme with the addition of cloud-scale Data, cloud-native Technologies and cloud-based AI integrations to help you modernize and build intelligent apps for the enterprise!

Watch this space - and join us in September to learn more!

- - +
Skip to main content

11 posts tagged with "github-copilot"

View All Tags

· 4 min read
It's 30DaysOfIA

This Fall focus on building intelligent apps using AI and cloud-native technologies. #FallForIntelligentApps brings to you a learning journey to build your skills on creating differentiated experiences while modernizing your applications. It’s time to learn it all.

What We'll Cover

image

Get Ready To #FallForIntelligentApps starting September 18!

Today, we kick off the Fall season with content and activities to skill you up on all things Intelligent Apps or AI Apps on Azure with content, events, and community interactions! Read on to learn about what is coming!

Explore Our Initiatives

We have a number of initiatives planned for the month to help you learn and skill up on relevant technologies. Click on the links to visit the relevant pages for each

image

Register for the events!

What are 4 things you can do today, to jumpstart your learning journey?

#30Days Of Intelligent Apps

#30DaysOfIA is a series of daily blog posts grouped into themed weeks - taking you from core concepts to end-to-end solution examples in 30 days. Each article will provide conceptual lessons paired with exercises and resources to help you reinforce learnings and take next steps.

This series takes you through learning journey in six stages, each building on the previous week to help you skill up in a beginner-friendly way:

We will start with defining intelligent apps and then expand on how to build with cloud-native technologies like Azure Kubernetes Service, Azure Container Apps and Azure Functions, and take you through end-to-end scenarios for real world application development. Before we dive deep on intelligent apps, here is a high-level overview of the Intelligent Apps landscape on Azure:

image

Containers on Azure services offer you a wide range of capabilities, from simplicity to control to suit your different needs.

image +image

To start with the basics for developing Kubernetes applications, explore #30DaysOfCloudNative.

Cloud-native development when paired with serverless computing enhances your solution architecture for building cost optimized, resilient applications.

image

To start with the basics for serverless computing, explore #30DaysOfServerless.

Let’s Get Started

Now you know everything! We hope you are as excited as we are to dive into a full month of active learning and doing! Don't forget to subscribe for updates in your favorite feed reader! And look out for our first Intelligent Apps post Monday!

+ + \ No newline at end of file diff --git a/30daysofIA/tags/hack-together/index.html b/30daysofIA/tags/hack-together/index.html index 4407196e00..c37f170692 100644 --- a/30daysofIA/tags/hack-together/index.html +++ b/30daysofIA/tags/hack-together/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "hack-together" | Build Intelligent Apps On Azure +11 posts tagged with "hack-together" | Build Intelligent Apps On Azure @@ -14,13 +14,13 @@ - - + +
-

9 posts tagged with "hack-together"

View All Tags

· 10 min read
It's 30DaysOfIA

Prepare your organization to adopt intelligent apps by taking a change management approach. Assess your readiness, upskill teams, create a culture of change and overcome resistance, and prepare for future evolution to pave the way for a smooth transition into an AI-centric company.

What We'll Cover:

  • Role of change management in modernizing to intelligent apps
  • Skills needed for building intelligent apps in your organization
  • How to build a culture of change to embrace AI
  • How to prepare for the future of intelligent apps and continuous evolution

image of modernizing AI solutions for intelligent apps

Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management

As we’ve discussed throughout this series, intelligent apps and generative AI are rewriting the rules of business and application development. The first article of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” explored how intelligent apps can enhance user experiences and drive businesses growth. These advancements are opening new opportunities and markets for companies ready to embrace them.

However, embracing this digital transformation is more complex than flipping a switch. It requires adjustments to the technical side, which we explored in the series’ fifth article, “Preparing the Path for Intelligent Apps: Transitioning from On-Premises/IaaS to Cloud Native Applications.” It also demands a shift in how organizations adapt to change and approach app design and development. Let’s explore that mindset transition with tips to help you make it happen.

The Role of Change Management in Transitioning to Intelligent Apps

Transitioning to intelligent apps isn’t merely a technological change. It’s a profound shift in how businesses perceive and operate. This move toward artificial intelligence (AI)-centric approach isn’t meant to replace humans with machines or radically overhaul all business operations.

Instead, intelligent apps should augment human capabilities, streamline processes, and foster an environment where innovation flourishes. They expand toolkits and techniques to include AI-assisted development tools like GitHub Copilot and AI-enabled workflows, helping us design and deliver AI-enhanced experiences.

Effective change management strategies are crucial in helping employees adapt to these new ways of working with intelligent apps, ensuring that the transition doesn’t disrupt productivity or morale.

Team and organization strategies should: 

  • Assess the impact of any proposed changes.  
  • Communicate the benefits and reasons behind the change to all affected team members.  
  • Provide the necessary training and support.  
  • Integrate the changes into the culture and daily operations. 

By incorporating this robust change management approach, you can identify and mitigate potential transition risks early. Moreover, this strategy ensures that employees are engaged throughout the process, fostering a sense of involvement and ownership. This, in turn, cultivates a culture suited for innovation through intelligent apps. 

These organizational changes focus on the following aspects: organizational readiness, skillset, culture of change and overcoming resistance, and adaptability. Let’s explore them below.

info

Register for the intelligent apps webinar on Driving Business Value by Modernizing with Cloud-Native & AI with Microsoft and Forrester on September 26. 

Explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries. 

Identifying Organizational Readiness for Intelligent Apps

The first step in preparing your organization for intelligent apps is to assess your current readiness for change. Analyze various elements of your organization, from infrastructure and workforce skills to strategic vision and overall culture.

Can your apps and systems integrate with AI platforms like Microsoft Azure AI? Is your workforce comfortable working alongside AI, or are they fearful and resistant? Does your strategic vision incorporate AI’s transformative potential, or does it fail to acknowledge AI as a critical driver of future growth?

These questions identify the challenges of integrating AI into an organization and determine the company’s readiness for intelligent apps.

Embracing intelligent apps involves more than being ready with your technological infrastructure or having the right technology or tools. You need flexible organizational structures and processes driving your business to accommodate new ideas and capabilities. Your teams must feel empowered to own and drive them. Much like Microsoft’s shift to AI-powered services—like AI-powered Bing and Edge and Microsoft 365 Copilot—embracing AI organization-wide requires a holistic vision and willingness to innovate, with teams capable of delivering on the plan.

After accessing these critical areas for organizational readiness, you can start planning a strategy for this transition to an AI-ready organization. Identify skill gaps and work to bridge them while nurturing an environment that appreciates and harnesses the potential of AI and intelligent apps.

Nurturing the Indispensable Skills for Intelligent Apps

Tasking your AI specialists with preparing your organization to use and build intelligent apps isn’t enough. Accomplishing this goal requires a cross-functional effort and a diverse skill set.

Developing intelligent apps involves data scientists managing and interpreting vast amounts of data, machine learning engineers building and training the models, and traditional developers integrating these models into usable applications. However, the impact of generative AI spans beyond creating apps. AI fluency across the entire organization—from sales and marketing to accounting and legal departments—offers substantial benefits, sparking new ideas and solutions from sometimes unexpected sources.

Teams' AI skills might roughly fall into three levels: 

  • Fundamental skills — Being familiar and efficient with using AI tools, for example, knowing how to write effective prompts for large language models (LLMs).
  • Visionary skills — Recognizing AI’s strengths and weaknesses, for example, its creativity and hallucinations, that is, unexpected output.
  • Expert skills — Managing data and training models and integrating AI technology into apps, such as incorporating the OpenAI application programming interface (API) into an Intelligent App.

Based on these levels, you can gauge your team's skill readiness to pinpoint the most significant gaps, whether in general AI knowledge across your organization or within technical development teams.

As intelligent apps become increasingly prevalent, upskilling your workforce is crucial. This transition opens a new realm of skills and capabilities for your team to master. You can offer machine learning, data analysis, and cloud computing training programs—such as Microsoft’s Azure AI Fundamentals certification—to empower your team to confidently navigate the intelligent apps landscape.

Nurturing these skills within your organization may require hiring new talent, reskilling current employees, and creating an environment that promotes learning and experimentation. Specifically, if your team understands tools like the Microsoft Azure AI platform, they could contribute significantly to developing and maintaining intelligent apps.

An upskilled team is more than a functional unit. It’s a think tank to drive innovation and improve your organization’s competitive standing in the digital marketplace.

Building a Culture of Change to Embrace AI

Even with the right people and skills in place, change remains an uphill battle for organizations committed to traditional processes. 

Transitioning to intelligent apps is as much about mindset as technology and skills. It requires fostering a culture open to change and innovation that understands AI’s transformative potential and embraces it to enhance rather than replace human capabilities. 

Leaders play a pivotal role in cultivating this mindset. As they model a positive attitude toward AI, emphasize its benefits, and address any concerns transparently, leaders significantly contribute to the organization’s readiness to adopt intelligent apps. 

The most critical factor in this transition is enabling an AI-friendly culture. It’s about encouraging curiosity, rewarding innovative thinking, and creating an environment where people thrive on learning and experimentation. 

Empowering teams to leverage practical AI tools and AI-driven data insights to aid decision-making helps motivate employees to use and explore AI’s potential, improve their skills, and contribute to the successful integration of intelligent apps. Such an environment is fertile ground to grow breakthrough ideas and innovative solutions. 

Overcoming Resistance to Change and Adopting Intelligent Apps

Despite AI’s potential, change isn’t easy. It takes effort and is especially challenging for organizations used to existing processes. This resistance to change is a significant roadblock when transitioning to intelligent apps. 

Overcoming this resistance involves:  

  • Understanding employees’ concerns. 
  • Challenging underlying assumptions about traditional systems and outdated processes that hinder change. 
  • Providing comprehensive training for workers to use AI effectively.
  • Demonstrating the benefits and opportunities of intelligent apps. 
  • Clear and continuous communication to dispel fears and misconceptions about AI.

These steps will foster greater acceptance and enthusiasm for intelligent apps in your teams. 

Preparing for the Future of Intelligent Apps and Continuous Evolution

Finally, it’s essential to understand that generative AI is just beginning to gain momentum. Adjusting to a world of intelligent apps isn’t a one-time change—it’s a continuous evolution. 

As AI technologies mature and evolve and more businesses realize their benefits, we can expect the landscape of intelligent apps to expand further. Organizations can cultivate a mindset of continuous learning, flexibility, and adaptation to prepare for this future. Staying ahead in this dynamic field requires organizations to keep up with the latest AI advancements, continually reassess their strategies, and pivot when necessary. 

The domain of intelligent apps is rapidly advancing. Organizations must prepare to navigate this dynamic landscape to stay ahead of the curve.

Summary

Preparing for AI and intelligent apps is a strategic decision. It requires thoughtful preparation and a culture that embraces change.

With the proper preparation and mindset, adopting AI can become a smoother, collaborative path to innovation for the whole organization. Integrating AI into your organization does not discard what has worked in the past. It simply enhances and augments those processes for a better future.

Consider the points above to assess your organization’s readiness and prepare to welcome intelligent apps.

In the first week of this series, you learned about intelligent apps, how organizations today find success through intelligent apps, what AI-assisted application development looks like, ways to leverage the power of generative AI, the technical changes needed to prepare for intelligent apps, and, in this article, the organizational changes required to harness the power of AI.

The future is intelligent. Use this series as a guideline to navigate challenges while preparing for and embracing AI's opportunities for an intelligent tomorrow.

Exercise

- - +
Skip to main content

11 posts tagged with "hack-together"

View All Tags

· 10 min read
It's 30DaysOfIA

Prepare your organization to adopt intelligent apps by taking a change management approach. Assess your readiness, upskill teams, create a culture of change and overcome resistance, and prepare for future evolution to pave the way for a smooth transition into an AI-centric company.

What We'll Cover:

  • Role of change management in modernizing to intelligent apps
  • Skills needed for building intelligent apps in your organization
  • How to build a culture of change to embrace AI
  • How to prepare for the future of intelligent apps and continuous evolution

image of modernizing AI solutions for intelligent apps

Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management

As we’ve discussed throughout this series, intelligent apps and generative AI are rewriting the rules of business and application development. The first article of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” explored how intelligent apps can enhance user experiences and drive businesses growth. These advancements are opening new opportunities and markets for companies ready to embrace them.

However, embracing this digital transformation is more complex than flipping a switch. It requires adjustments to the technical side, which we explored in the series’ fifth article, “Preparing the Path for Intelligent Apps: Transitioning from On-Premises/IaaS to Cloud Native Applications.” It also demands a shift in how organizations adapt to change and approach app design and development. Let’s explore that mindset transition with tips to help you make it happen.

The Role of Change Management in Transitioning to Intelligent Apps

Transitioning to intelligent apps isn’t merely a technological change. It’s a profound shift in how businesses perceive and operate. This move toward artificial intelligence (AI)-centric approach isn’t meant to replace humans with machines or radically overhaul all business operations.

Instead, intelligent apps should augment human capabilities, streamline processes, and foster an environment where innovation flourishes. They expand toolkits and techniques to include AI-assisted development tools like GitHub Copilot and AI-enabled workflows, helping us design and deliver AI-enhanced experiences.

Effective change management strategies are crucial in helping employees adapt to these new ways of working with intelligent apps, ensuring that the transition doesn’t disrupt productivity or morale.

Team and organization strategies should: 

  • Assess the impact of any proposed changes.  
  • Communicate the benefits and reasons behind the change to all affected team members.  
  • Provide the necessary training and support.  
  • Integrate the changes into the culture and daily operations. 

By incorporating this robust change management approach, you can identify and mitigate potential transition risks early. Moreover, this strategy ensures that employees are engaged throughout the process, fostering a sense of involvement and ownership. This, in turn, cultivates a culture suited for innovation through intelligent apps. 

These organizational changes focus on the following aspects: organizational readiness, skillset, culture of change and overcoming resistance, and adaptability. Let’s explore them below.

info

Register for the intelligent apps webinar on Driving Business Value by Modernizing with Cloud-Native & AI with Microsoft and Forrester on September 26. 

Explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries. 

Identifying Organizational Readiness for Intelligent Apps

The first step in preparing your organization for intelligent apps is to assess your current readiness for change. Analyze various elements of your organization, from infrastructure and workforce skills to strategic vision and overall culture.

Can your apps and systems integrate with AI platforms like Microsoft Azure AI? Is your workforce comfortable working alongside AI, or are they fearful and resistant? Does your strategic vision incorporate AI’s transformative potential, or does it fail to acknowledge AI as a critical driver of future growth?

These questions identify the challenges of integrating AI into an organization and determine the company’s readiness for intelligent apps.

Embracing intelligent apps involves more than being ready with your technological infrastructure or having the right technology or tools. You need flexible organizational structures and processes driving your business to accommodate new ideas and capabilities. Your teams must feel empowered to own and drive them. Much like Microsoft’s shift to AI-powered services—like AI-powered Bing and Edge and Microsoft 365 Copilot—embracing AI organization-wide requires a holistic vision and willingness to innovate, with teams capable of delivering on the plan.

After accessing these critical areas for organizational readiness, you can start planning a strategy for this transition to an AI-ready organization. Identify skill gaps and work to bridge them while nurturing an environment that appreciates and harnesses the potential of AI and intelligent apps.

Nurturing the Indispensable Skills for Intelligent Apps

Tasking your AI specialists with preparing your organization to use and build intelligent apps isn’t enough. Accomplishing this goal requires a cross-functional effort and a diverse skill set.

Developing intelligent apps involves data scientists managing and interpreting vast amounts of data, machine learning engineers building and training the models, and traditional developers integrating these models into usable applications. However, the impact of generative AI spans beyond creating apps. AI fluency across the entire organization—from sales and marketing to accounting and legal departments—offers substantial benefits, sparking new ideas and solutions from sometimes unexpected sources.

Teams' AI skills might roughly fall into three levels: 

  • Fundamental skills — Being familiar and efficient with using AI tools, for example, knowing how to write effective prompts for large language models (LLMs).
  • Visionary skills — Recognizing AI’s strengths and weaknesses, for example, its creativity and hallucinations, that is, unexpected output.
  • Expert skills — Managing data and training models and integrating AI technology into apps, such as incorporating the OpenAI application programming interface (API) into an Intelligent App.

Based on these levels, you can gauge your team's skill readiness to pinpoint the most significant gaps, whether in general AI knowledge across your organization or within technical development teams.

As intelligent apps become increasingly prevalent, upskilling your workforce is crucial. This transition opens a new realm of skills and capabilities for your team to master. You can offer machine learning, data analysis, and cloud computing training programs—such as Microsoft’s Azure AI Fundamentals certification—to empower your team to confidently navigate the intelligent apps landscape.

Nurturing these skills within your organization may require hiring new talent, reskilling current employees, and creating an environment that promotes learning and experimentation. Specifically, if your team understands tools like the Microsoft Azure AI platform, they could contribute significantly to developing and maintaining intelligent apps.

An upskilled team is more than a functional unit. It’s a think tank to drive innovation and improve your organization’s competitive standing in the digital marketplace.

Building a Culture of Change to Embrace AI

Even with the right people and skills in place, change remains an uphill battle for organizations committed to traditional processes. 

Transitioning to intelligent apps is as much about mindset as technology and skills. It requires fostering a culture open to change and innovation that understands AI’s transformative potential and embraces it to enhance rather than replace human capabilities. 

Leaders play a pivotal role in cultivating this mindset. As they model a positive attitude toward AI, emphasize its benefits, and address any concerns transparently, leaders significantly contribute to the organization’s readiness to adopt intelligent apps. 

The most critical factor in this transition is enabling an AI-friendly culture. It’s about encouraging curiosity, rewarding innovative thinking, and creating an environment where people thrive on learning and experimentation. 

Empowering teams to leverage practical AI tools and AI-driven data insights to aid decision-making helps motivate employees to use and explore AI’s potential, improve their skills, and contribute to the successful integration of intelligent apps. Such an environment is fertile ground to grow breakthrough ideas and innovative solutions. 

Overcoming Resistance to Change and Adopting Intelligent Apps

Despite AI’s potential, change isn’t easy. It takes effort and is especially challenging for organizations used to existing processes. This resistance to change is a significant roadblock when transitioning to intelligent apps. 

Overcoming this resistance involves:  

  • Understanding employees’ concerns. 
  • Challenging underlying assumptions about traditional systems and outdated processes that hinder change. 
  • Providing comprehensive training for workers to use AI effectively.
  • Demonstrating the benefits and opportunities of intelligent apps. 
  • Clear and continuous communication to dispel fears and misconceptions about AI.

These steps will foster greater acceptance and enthusiasm for intelligent apps in your teams. 

Preparing for the Future of Intelligent Apps and Continuous Evolution

Finally, it’s essential to understand that generative AI is just beginning to gain momentum. Adjusting to a world of intelligent apps isn’t a one-time change—it’s a continuous evolution. 

As AI technologies mature and evolve and more businesses realize their benefits, we can expect the landscape of intelligent apps to expand further. Organizations can cultivate a mindset of continuous learning, flexibility, and adaptation to prepare for this future. Staying ahead in this dynamic field requires organizations to keep up with the latest AI advancements, continually reassess their strategies, and pivot when necessary. 

The domain of intelligent apps is rapidly advancing. Organizations must prepare to navigate this dynamic landscape to stay ahead of the curve.

Summary

Preparing for AI and intelligent apps is a strategic decision. It requires thoughtful preparation and a culture that embraces change.

With the proper preparation and mindset, adopting AI can become a smoother, collaborative path to innovation for the whole organization. Integrating AI into your organization does not discard what has worked in the past. It simply enhances and augments those processes for a better future.

Consider the points above to assess your organization’s readiness and prepare to welcome intelligent apps.

In the first week of this series, you learned about intelligent apps, how organizations today find success through intelligent apps, what AI-assisted application development looks like, ways to leverage the power of generative AI, the technical changes needed to prepare for intelligent apps, and, in this article, the organizational changes required to harness the power of AI.

The future is intelligent. Use this series as a guideline to navigate challenges while preparing for and embracing AI's opportunities for an intelligent tomorrow.

Exercise

+ + \ No newline at end of file diff --git a/30daysofIA/tags/hack-together/page/10/index.html b/30daysofIA/tags/hack-together/page/10/index.html new file mode 100644 index 0000000000..6c2ee9242d --- /dev/null +++ b/30daysofIA/tags/hack-together/page/10/index.html @@ -0,0 +1,27 @@ + + + + + +11 posts tagged with "hack-together" | Build Intelligent Apps On Azure + + + + + + + + + + + + + + +
+

11 posts tagged with "hack-together"

View All Tags

· 4 min read
It's 30DaysOfIA

Continue The Learning Journey through Fall For Intelligent Apps! 🍂

What We'll Cover

Thank you! ♥️

image

It's hard to believe that JavaScript on Azure hack-together is ending! It seems like just yesterday that we launched this initiative, and yet here we are, 15 days later, with an incredible amount of learning and growth behind us. So... it's time for a wrap!

From the bottom of our hearts, we want to thank each and every one of you for your participation, engagement, and enthusiasm. It's been truly inspiring to see the passion and dedication from this strong community, and we're honored to be a part of it. ✨

Recap of The JavaScript on Azure Global Hack-Together

As we wrap up this exciting event, we wanted to take a moment to reflect on all that we've accomplished together. Over the last 15 days, we've covered a lot of ground, from the basics of contributing to Open source to the exploration of the Contoso Real Estate project from its Frontend to its Backend and future AI implementation.

Now that the hack-together is ending, we want to make sure that you have all the resources you need to continue honing your skills in the future. Whether you're looking to make your fist contribution to open source, become an open source maintainers, collaborate with others, or simply keep learning, there are plenty of resources out there to help you achieve your goals. So, let's dive in and explore all the ways you can continue to grow your JavaScript skills on Azure!

JSonAzure Hack-together Roadmap 📍:

hack-together-roadmap (2)

Recap on past Livestreams🌟:

Day 1️⃣: Opening Keynote (Hack-together Launch): Introduction to the Contoso Real Estate Open-source project!, managing complex and complex enterprise architecture, new announcements for JavaScript developers on Azure

Day 2️⃣: GitHub Copilot & Codespaces: Introduction to your AI pair programmer (GitHub Copilot) and your virtual developer environment on the cloud (GitHub Codespaces)

Day 6️⃣: Build your Frontend using Static Web Apps as part of a complex, modern composable frontends (or micro-frontends) and cloud-native applications.

Day 9️⃣: Build a Serverless Backend using Azure Functions

Day 1️⃣3️⃣: Easily connect to an Azure Cosmos DB, exploring its benefits and how to get started

Day 1️⃣5️⃣: Being in the AI Era, we dive into the Azure OpenAI Service and how you can start to build intelligent JavaScript applications

📖 Self-Learning Resources

  1. JavaScript on Azure Global Hack Together Module collection
  2. Lets #HackTogether: Javascript On Azure Keynote
  3. Step by Step Guide: Migrating v3 to v4 programming model for Azure Functions for Node.Js Application

Continue your journey with #FallForIntelligentApps

Join us this Fall on a learning journey to explore building intelligent apps. Combine the power of AI, cloud-native app development, and cloud-scale data to build highly differentiated digital experiences. Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure. Engage in a self-paced learning adventure by following along the #30Days of Intelligent Apps series, completing the Intelligent Apps Skills Challenge, joining the Product Group for a live Ask The Expert series or building an end to end solution architecture with a live guided experience through the Learn Live series. Discover more here.

Hands-on practice: Make your first contribution to open source!

Join our GitHUb Discussion Forum to connect with developers from every part of world, see contributions from other, find collaborators and make your first contribution to a real-world project! +Don't forget to give the repo a star ⭐

Resources

All resources are accessible on our landing page

+ + + + \ No newline at end of file diff --git a/30daysofIA/tags/hack-together/page/11/index.html b/30daysofIA/tags/hack-together/page/11/index.html new file mode 100644 index 0000000000..3b511cd289 --- /dev/null +++ b/30daysofIA/tags/hack-together/page/11/index.html @@ -0,0 +1,26 @@ + + + + + +11 posts tagged with "hack-together" | Build Intelligent Apps On Azure + + + + + + + + + + + + + + +
+

11 posts tagged with "hack-together"

View All Tags

· 2 min read
It's 30DaysOfIA

September is almost here - and that can only mean one thing!! It's time to 🍂 Fall for something new and exciting and spend a few weeks skilling up on relevant tools, techologies and solutions!!

Last year, we focused on #ServerlessSeptember. This year, we're building on that theme with the addition of cloud-scale Data, cloud-native Technologies and cloud-based AI integrations to help you modernize and build intelligent apps for the enterprise!

Watch this space - and join us in September to learn more!

+ + + + \ No newline at end of file diff --git a/30daysofIA/tags/hack-together/page/2/index.html b/30daysofIA/tags/hack-together/page/2/index.html index 99722b8786..c9e29e13d1 100644 --- a/30daysofIA/tags/hack-together/page/2/index.html +++ b/30daysofIA/tags/hack-together/page/2/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "hack-together" | Build Intelligent Apps On Azure +11 posts tagged with "hack-together" | Build Intelligent Apps On Azure @@ -14,13 +14,13 @@ - - + +
-

9 posts tagged with "hack-together"

View All Tags

· 9 min read
It's 30DaysOfIA

Learn best practices for modernizing your on-premises or IaaS solutions to intelligent apps in the cloud.

What We'll Cover:

  • Traditional vs Intelligent Apps
  • On-Premises/IaaS vs. Cloud-Native Platforms
  • Modernization strategy for building Intelligent Apps

image of modernizing AI solutions for intelligent apps

Preparing the Path for Intelligent Apps: Transitioning from On-Premises/IaaS to Cloud-Native Applications

The proliferation of generative AI is paving the way for intelligent applications, software that leverages AI capabilities to deliver unparalleled functionality and user experience. As we covered in a previous article, “Demystifying Intelligent Applications: Leveraging AI in App Development,” intelligent Apps aren’t just products of advanced coding. They interact, learn, and evolve thanks to modern AI and machine learning (ML) breakthroughs. 

The paradigm shift from traditional to intelligent apps demands that we change our approach to the technical challenges of software and architecture design. It also necessitates adopting new ways of thinking and operating within an organization, which we explore in greater depth in “Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management.” 

This article explores the technical infrastructure powering intelligent apps, providing a roadmap to transition traditional on-premises or Infrastructure as a Service (IaaS) solutions to intelligent apps deployed to cloud native platforms and services. We’ll discover the differences between conventional and intelligent apps, options for re-architecting existing applications, and critical strategic considerations integral to modernizing the application-building approach. 

Understanding the Shift: Traditional vs. Intelligent Applications

Intelligent apps are more than just an incremental evolution from traditional apps. They involve a fundamental shift in software solution design, development, and deployment. A clear understanding of how each paradigm differs is crucial for comprehending the impact of this transition. 

Functionality and User Experience

Traditional applications, often rule-based and rigid, rely on preprogrammed operations. A traditional weather app, for example, typically presents structured, location-based forecasts using a predetermined data source. Meanwhile, an intelligent app leverages AI for data-driven decision-making and personalization. Rather than merely displaying the forecast, it integrates ML with additional data sources—like a user’s calendar and fitness data—to determine their favorite outdoor activities, provide relevant clothing suggestions, and even automatically design ideal vacations and times based on the user’s optimal weather preferences. 

Similarly, while traditional applications tend to offer a one-size-fits-all UI, intelligent apps use AI to create tailored interactions. For instance, an intelligent banking app might use generative AI to provide a voice-enabled UI that lets customers use natural language to ask about transactions and receive personalized financial advice.

Infrastructure

Intelligent apps integrating these sophisticated features require highly scalable, flexible infrastructure optimized for AI functionality and able to manage large volumes of real-time data. Unlike traditional applications deployed on-premises or in infrastructure as a service (IaaS) environments, intelligent apps demand the flexibility and scalability of cloud native architectures.

An on-premises or IaaS-based e-commerce application may struggle with connecting its data to AI services or scaling to accommodate voluminous real-time data and requests. But an intelligent app built with a cloud native approach and scalability at its core can handle variable demand much more efficiently. It also maintains its infrastructure components on demand, whether for collecting data for AI/ML consumption or user-facing features like AI-enabled customer support and personalization.

Consider an intelligent app built on Azure that creates ChatGPT-like experiences with custom data. It might connect Azure Open AI and Azure Cognitive Search to an app running on Azure Container Apps or Azure Kubernetes Service. 

The architecture might look like the following diagram based on this demo app:

diagram of a demo app architecture

While the cloud is well-equipped to handle the challenges associated with shifting to AI-centric infrastructure—handling real-time data processing and storage, integrating and managing AI services, configuring event-driven and microservices architectures, and ensuring security across the distributed environment—re-architecting a traditional app to an intelligent app requires careful planning and execution to ensure it benefits fully from the capabilities of AI and cloud computing.

On-Premises/IaaS vs. Cloud-Native Platforms

While on-premises and IaaS solutions provide substantial control over data and processes, this fine-grained control comes with setup and maintenance costs and may lack the scalability necessary for AI-driven Intelligent Apps. On-premises hosting involves purchasing and maintaining hardware, including servers and storage systems. IaaS involves leasing virtual hardware and then hiring and training IT personnel for the infrastructure’s operation, maintenance, and security.

Conversely, infrastructure built on cloud native app services with Microsoft Azure offers virtually unlimited scalability without ongoing hardware and maintenance costs. This feature is ideal for intelligent apps that need to adapt and scale to accommodate fluctuating workloads and data streams. And because these cloud infrastructures typically operate on a pay-as-you-go model, traditionally substantial upfront costs turn into predictable operating expenses, lowering both the cost and risk of launching applications.

Moreover, cloud service providers continually update their offerings, so building on sophisticated AI tools and services through the cloud, like the Azure AI Platform and orchestration capabilities with Azure Kubernetes Service, ensures that you have access to the latest AI and most advanced AI models and technologies.

Strategic Considerations for Transitioning to Intelligent Apps

When developing a strategy for transitioning to intelligent apps, you must consider costs, personnel skill development, and data management.

Monolithic architectures are unlikely to support the scale and ease of iteration required by intelligent apps, so you’ll likely need to implement code changes to support the transition. Your chosen hosting solution and the amount of data your app will handle affect cost and performance, in addition to determining privacy and compliance requirements.

An app that manages or stores terabytes or petabytes of data will require storage-optimized virtual machines or a managed database service hosting. Meanwhile, an app that processes video content or performs resource-intensive calculations should prioritize high-performance computing. Maximizing storage and performance can become extraordinarily costly.

Finally, existing applications shifting towards intelligent apps may encounter compatibility conflicts, depending on database type or the existing system’s underlying programming language. As you might imagine, a traditional app running on-premises and using a shared local database may need significant restructuring to support a cloud native architecture. You need plans for data migration, potential downtime, and training.

Next, let’s look at this transformation process.

Re-Architecting Monolithic Apps into Microservices

Shifting from monolithic applications to microservices is pivotal for transitioning to Intelligent Apps, and you can use this shift as an opportunity to transform your legacy software into intelligent apps that leverage the flexibility and power of microservices-enabled AI solutions.

Traditionally, a monolithic application (let’s use an online marketplace as an example) might handle all activities—from serving webpages to processing payments—within an on-premises server or a cloud server. However, this approach is challenging to scale and remains vulnerable to network failures or cyberattacks.

Breaking these applications into multiple services with specific responsibilities, such as authentication or notifications, can overcome these limitations. These services are typically deployed as microservices within container environments like Azure Kubernetes Service or serverless platforms like Azure Container Apps or Azure Functions using automated processes. This approach offers scalability, robustness, and improved performance, as each microservice can run independently and use its own database.

Shifting towards smaller, independent microservices in app architectures allows us to reap the benefits of AI-powered intelligent apps, for example, by using microservices to handle AI tasks such as processing natural language input, analyzing behavioral data, or personalizing content.

info

Watch Episode 1 and Episode 2 of the Learn Live series on how to build, test and deploy an end-to-end intelligent app solution using the Contoso Real Estate Sample.

Event-Driven Architectures: Knowing When to Use Them

Event-Driven Architectures (EDAs) can significantly enhance the performance and cost-effectiveness of intelligent apps. EDAs, typically implemented with microservices, respond in real time to events or state changes ranging from user interactions to real-time analytics.

Consider an intelligent app designed for personalized customer engagement. Instead of designing the application to check continuously for changes in user data, an EDA can react to such changes immediately, triggering appropriate microservices. For example, if users modify their preferences, an event could trigger an AI-driven recommendation microservice to update its suggestions immediately. This real-time responsiveness, enabled by services like Azure Event Grid, can improve user experiences and make intelligent apps more adaptive and proactive.

However, it’s worth noting that while EDAs provide significant benefits, they can introduce unique complexities, such as maintaining event consistency and debugging issues in event chains across multiple microservices. Therefore, it’s important to assess your application’s needs so you can understand where it can benefit from an EDA.

Conclusion

Transitioning to intelligent apps represents a shift in strategy, architecture, and infrastructure. By designing and re-architecting for cloud-based infrastructure, your apps gain scalability, cost-effectiveness, robust data management, access to sophisticated AI tools, enhanced security, and reduced operational overhead.  

But this transition extends well beyond its technical components. Organizations and the people working within them need equal attention. You can explore these facets in greater depth in “Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management.” 

The power of generative AI is here, and preparing for the move toward cloud-based intelligent applications will ensure that your apps remain performative and scalable and—most importantly—that your business is ready for the newest era of app innovation.

Exercise

- - +
Skip to main content

11 posts tagged with "hack-together"

View All Tags

· 9 min read
It's 30DaysOfIA

Learn best practices for modernizing your on-premises or IaaS solutions to intelligent apps in the cloud.

What We'll Cover:

  • Traditional vs Intelligent Apps
  • On-Premises/IaaS vs. Cloud-Native Platforms
  • Modernization strategy for building Intelligent Apps

image of modernizing AI solutions for intelligent apps

Preparing the Path for Intelligent Apps: Transitioning from On-Premises/IaaS to Cloud-Native Applications

The proliferation of generative AI is paving the way for intelligent applications, software that leverages AI capabilities to deliver unparalleled functionality and user experience. As we covered in a previous article, “Demystifying Intelligent Applications: Leveraging AI in App Development,” intelligent Apps aren’t just products of advanced coding. They interact, learn, and evolve thanks to modern AI and machine learning (ML) breakthroughs. 

The paradigm shift from traditional to intelligent apps demands that we change our approach to the technical challenges of software and architecture design. It also necessitates adopting new ways of thinking and operating within an organization, which we explore in greater depth in “Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management.” 

This article explores the technical infrastructure powering intelligent apps, providing a roadmap to transition traditional on-premises or Infrastructure as a Service (IaaS) solutions to intelligent apps deployed to cloud native platforms and services. We’ll discover the differences between conventional and intelligent apps, options for re-architecting existing applications, and critical strategic considerations integral to modernizing the application-building approach. 

Understanding the Shift: Traditional vs. Intelligent Applications

Intelligent apps are more than just an incremental evolution from traditional apps. They involve a fundamental shift in software solution design, development, and deployment. A clear understanding of how each paradigm differs is crucial for comprehending the impact of this transition. 

Functionality and User Experience

Traditional applications, often rule-based and rigid, rely on preprogrammed operations. A traditional weather app, for example, typically presents structured, location-based forecasts using a predetermined data source. Meanwhile, an intelligent app leverages AI for data-driven decision-making and personalization. Rather than merely displaying the forecast, it integrates ML with additional data sources—like a user’s calendar and fitness data—to determine their favorite outdoor activities, provide relevant clothing suggestions, and even automatically design ideal vacations and times based on the user’s optimal weather preferences. 

Similarly, while traditional applications tend to offer a one-size-fits-all UI, intelligent apps use AI to create tailored interactions. For instance, an intelligent banking app might use generative AI to provide a voice-enabled UI that lets customers use natural language to ask about transactions and receive personalized financial advice.

Infrastructure

Intelligent apps integrating these sophisticated features require highly scalable, flexible infrastructure optimized for AI functionality and able to manage large volumes of real-time data. Unlike traditional applications deployed on-premises or in infrastructure as a service (IaaS) environments, intelligent apps demand the flexibility and scalability of cloud native architectures.

An on-premises or IaaS-based e-commerce application may struggle with connecting its data to AI services or scaling to accommodate voluminous real-time data and requests. But an intelligent app built with a cloud native approach and scalability at its core can handle variable demand much more efficiently. It also maintains its infrastructure components on demand, whether for collecting data for AI/ML consumption or user-facing features like AI-enabled customer support and personalization.

Consider an intelligent app built on Azure that creates ChatGPT-like experiences with custom data. It might connect Azure Open AI and Azure Cognitive Search to an app running on Azure Container Apps or Azure Kubernetes Service. 

The architecture might look like the following diagram based on this demo app:

diagram of a demo app architecture

While the cloud is well-equipped to handle the challenges associated with shifting to AI-centric infrastructure—handling real-time data processing and storage, integrating and managing AI services, configuring event-driven and microservices architectures, and ensuring security across the distributed environment—re-architecting a traditional app to an intelligent app requires careful planning and execution to ensure it benefits fully from the capabilities of AI and cloud computing.

On-Premises/IaaS vs. Cloud-Native Platforms

While on-premises and IaaS solutions provide substantial control over data and processes, this fine-grained control comes with setup and maintenance costs and may lack the scalability necessary for AI-driven Intelligent Apps. On-premises hosting involves purchasing and maintaining hardware, including servers and storage systems. IaaS involves leasing virtual hardware and then hiring and training IT personnel for the infrastructure’s operation, maintenance, and security.

Conversely, infrastructure built on cloud native app services with Microsoft Azure offers virtually unlimited scalability without ongoing hardware and maintenance costs. This feature is ideal for intelligent apps that need to adapt and scale to accommodate fluctuating workloads and data streams. And because these cloud infrastructures typically operate on a pay-as-you-go model, traditionally substantial upfront costs turn into predictable operating expenses, lowering both the cost and risk of launching applications.

Moreover, cloud service providers continually update their offerings, so building on sophisticated AI tools and services through the cloud, like the Azure AI Platform and orchestration capabilities with Azure Kubernetes Service, ensures that you have access to the latest AI and most advanced AI models and technologies.

Strategic Considerations for Transitioning to Intelligent Apps

When developing a strategy for transitioning to intelligent apps, you must consider costs, personnel skill development, and data management.

Monolithic architectures are unlikely to support the scale and ease of iteration required by intelligent apps, so you’ll likely need to implement code changes to support the transition. Your chosen hosting solution and the amount of data your app will handle affect cost and performance, in addition to determining privacy and compliance requirements.

An app that manages or stores terabytes or petabytes of data will require storage-optimized virtual machines or a managed database service hosting. Meanwhile, an app that processes video content or performs resource-intensive calculations should prioritize high-performance computing. Maximizing storage and performance can become extraordinarily costly.

Finally, existing applications shifting towards intelligent apps may encounter compatibility conflicts, depending on database type or the existing system’s underlying programming language. As you might imagine, a traditional app running on-premises and using a shared local database may need significant restructuring to support a cloud native architecture. You need plans for data migration, potential downtime, and training.

Next, let’s look at this transformation process.

Re-Architecting Monolithic Apps into Microservices

Shifting from monolithic applications to microservices is pivotal for transitioning to Intelligent Apps, and you can use this shift as an opportunity to transform your legacy software into intelligent apps that leverage the flexibility and power of microservices-enabled AI solutions.

Traditionally, a monolithic application (let’s use an online marketplace as an example) might handle all activities—from serving webpages to processing payments—within an on-premises server or a cloud server. However, this approach is challenging to scale and remains vulnerable to network failures or cyberattacks.

Breaking these applications into multiple services with specific responsibilities, such as authentication or notifications, can overcome these limitations. These services are typically deployed as microservices within container environments like Azure Kubernetes Service or serverless platforms like Azure Container Apps or Azure Functions using automated processes. This approach offers scalability, robustness, and improved performance, as each microservice can run independently and use its own database.

Shifting towards smaller, independent microservices in app architectures allows us to reap the benefits of AI-powered intelligent apps, for example, by using microservices to handle AI tasks such as processing natural language input, analyzing behavioral data, or personalizing content.

info

Watch Episode 1 and Episode 2 of the Learn Live series on how to build, test and deploy an end-to-end intelligent app solution using the Contoso Real Estate Sample.

Event-Driven Architectures: Knowing When to Use Them

Event-Driven Architectures (EDAs) can significantly enhance the performance and cost-effectiveness of intelligent apps. EDAs, typically implemented with microservices, respond in real time to events or state changes ranging from user interactions to real-time analytics.

Consider an intelligent app designed for personalized customer engagement. Instead of designing the application to check continuously for changes in user data, an EDA can react to such changes immediately, triggering appropriate microservices. For example, if users modify their preferences, an event could trigger an AI-driven recommendation microservice to update its suggestions immediately. This real-time responsiveness, enabled by services like Azure Event Grid, can improve user experiences and make intelligent apps more adaptive and proactive.

However, it’s worth noting that while EDAs provide significant benefits, they can introduce unique complexities, such as maintaining event consistency and debugging issues in event chains across multiple microservices. Therefore, it’s important to assess your application’s needs so you can understand where it can benefit from an EDA.

Conclusion

Transitioning to intelligent apps represents a shift in strategy, architecture, and infrastructure. By designing and re-architecting for cloud-based infrastructure, your apps gain scalability, cost-effectiveness, robust data management, access to sophisticated AI tools, enhanced security, and reduced operational overhead.  

But this transition extends well beyond its technical components. Organizations and the people working within them need equal attention. You can explore these facets in greater depth in “Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management.” 

The power of generative AI is here, and preparing for the move toward cloud-based intelligent applications will ensure that your apps remain performative and scalable and—most importantly—that your business is ready for the newest era of app innovation.

Exercise

+ + \ No newline at end of file diff --git a/30daysofIA/tags/hack-together/page/3/index.html b/30daysofIA/tags/hack-together/page/3/index.html index 6bf9a0d29d..4fc4590019 100644 --- a/30daysofIA/tags/hack-together/page/3/index.html +++ b/30daysofIA/tags/hack-together/page/3/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "hack-together" | Build Intelligent Apps On Azure +11 posts tagged with "hack-together" | Build Intelligent Apps On Azure @@ -14,13 +14,13 @@ - - + +
-

9 posts tagged with "hack-together"

View All Tags

· 10 min read
It's 30DaysOfIA

Explore how digital natives and Independent Software Vendors (ISVs) can transform their business operations and generate higher value for customers with generative AI and intelligent apps.

What We'll Cover:

  • Potential of Generative AI in application development
  • Challenges with Generative AI
  • Integrating Generative AI with business operations

image of generative AI and intelligent apps used in business

Unleashing Business Transformation: How Digital Natives Can Leverage Generative AI

Generative AI is no longer a technological flight of fancy. It’s here, and it’s rewriting the rules of business innovation. 

This type of AI leverages machine learning algorithms to create new data with similar characteristics to a given dataset. It goes beyond text generation using generative pretrained transformer (GPT) models, covering an array of applications that span from image synthesis to translations and more. It also powers a new class of intelligent applications, which combine AI capabilities with traditional software functions for enhanced user experiences and advanced business solutions. 

Generative AI is transforming how digital natives and independent software vendors (ISVs) operate. Generative AI offers new possibilities for creating unique business value, enhancing customer experiences, and driving growth. 

It plays a critical role in applications, improving functionality, aiding decision-making, and automating complex tasks to deliver dynamic, uniquely personalized user experiences. Generative AI also enables and automates data-driven outcomes, functions, and features.

Integrating AI capabilities into customer-facing apps and turning them into intelligent apps benefits both the customer and the business, as we discussed in Demystifying Intelligent Applications: Leveraging AI in App Development. You get faster product development workflows and better business intelligence for sales and marketing, while your users get more natural and context-aware features and interactions. 

That said, to realize generative AI’s potential, you need some strategy and knowledge of how and where it fits into your current business and operations. We’ll examine this technology’s potential to transform business operations, its risks, and rewards, and how to integrate it into your existing operations. 

Unpacking the Potential of Generative AI

Generative AI is creating new opportunities in the digital playing field. It empowers businesses to generate insights, design products, and automate processes in ways and at speeds that were previously unimaginable. You can now predict customer behavior more accurately, offer highly personalized user experiences, and prototype innovative ideas rapidly. 

For example, Microsoft used generative AI to reinvent web search in the new AI-powered Bing and Edge. The search experience has evolved from a solitary user flow of typing in keywords and scrolling through results. It’s now a conversational experience where Bing uses context to offer better quality and more personalized search queries and results. 

Similarly, Microsoft harnessed generative AI to transform its productivity suite into intelligent apps. Through Copilot for Microsoft 365, PowerPoint now empowers users to tell their stories by using DALL-E to generate images effortlessly. It also helps rephrase text-heavy slides to be more straightforward. Outlook now assists users to compose more effective emails, enabling better communication.

These are just some examples of how AI powers day-to-day work and operations. 

Businesses that ignore generative AI miss out on untapped market opportunities, letting competitors gain an edge in the digital race. By seeking ways to rethink assumptions and reinvent existing approaches, you can effectively use generative AI to create new opportunities, open products to new use cases and markets, unlock new growth and customer satisfaction levels, and increase your revenue.

info

Register for the intelligent apps webinar on Driving Business Value by Modernizing with Cloud-Native & AI with Microsoft and Forrester on September 26.

Explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries. 

The Crucial Role of Generative AI in Business Operations

Generative AI can be a powerful catalyst for business transformation. Let’s quickly tour some ways it can redefine business operations. 

Product development is traditionally a long and painstaking process. Now, tools like GitHub Copilot and TestPilot use generative AI to make coding and testing software easier, faster, and more robust. 

Similarly, machine learning as a service technology, such as Azure Machine Learning, democratizes the availability of machine learning. It helps collect, clean, and train models across datasets, making engineering and analyzing data more efficient and accessible.  

Generative AI also simplifies the process of creating intelligent apps. Previously, simulating thousands of prototypes, predicting their performance, and narrowing down to the most viable options could take weeks, months, or even years of iteration and data analysis. With generative AI, this process can be as quick as days, hours, or perhaps minutes. 

Customer experience is also ripe for transformation. Personalized recommendations drive engagement and sales, and generative AI can scale that personalization to every customer interaction using comprehensive and continually updated data. For instance, Microsoft leverages AI to enable personalization in Dynamics 365 Customer Insights. Using this tool, you can ask questions about your customers in natural language and gain valuable and actionable insights into your customer base. 

Lastly, marketing and sales can benefit from generative AI. For example, identifying current and future key markets and emerging trends helps companies stay ahead of the curve. Tools like Copilot in Power BI help you quickly understand and uncover insights about your business, enabling your team to craft targeted campaigns, create engaging content, and optimize your business strategy. 

Leveraging this full generative AI potential requires the right tools. GPT-4, for instance, can be instrumental in realizing effective business transformation. It excels in understanding context, generating human-like text, and learning from minimal data input, making it a formidable tool in any digital native's arsenal. Platforms like Azure OpenAI Service bring GPT-4’s power to your fingertips.

However, as with any powerful technology, it’s unwise to treat generative AI as a perfect solution. You must know the challenges and risks of not leveraging it effectively, including technical and human-factor considerations. 

Without generative AI, your company risks missing the opportunity to please customers via personalization, as discussed previously. You’ll miss sales opportunities via business intelligence insights, limiting your ability to grow revenue and keep up with the competition. 

One challenge of leveraging generative AI’s full potential is finding the right tools. Fortunately, you can implement generative AI in your organization without investing in infrastructure, computational power, and expertise. Platforms like Azure OpenAI Service and Azure AI Services make AI easily accessible. They also work with Azure compute and data services like Azure Functions and Azure Kubernetes Service (AKS), enabling you to build intelligent apps easily and quickly. 

However, generative AI can’t do everything. It may need human expertise and intervention to use it fully. 

For example, generative AI can create unique data. However, the data may have limited diversity and complexity. The AI may need help recognizing additional considerations or requirements specific to a problem. It may also lack context due to various factors, such as the information’s freshness. Also, since it’s an emerging field that’s potentially worth $4.4 trillion, we’re still learning about the nuances and potential regulatory impact of generative AI. 

Perhaps the most crucial and challenging aspect of adopting generative AI is an organization’s required cultural shift. Your teams must learn how to use AI as your company fosters an understanding of AI’s potential and limitations. 

Begin integrating AI literacy into your company’s DNA. Knowledge of its potential and the readily available tools can inspire your teams to incorporate generative AI into their daily workflows and business operations quickly, improve productivity, and deliver superior customer experiences. 

No company wants to be outpaced by its competitors. You can balance generative AI’s potential with your business operations by staying aware of and adapting to the dynamic AI landscape.

Achieving Harmony: Integrating Generative AI with Existing Business Operations

Integrating generative AI into existing business operations doesn’t have to be disruptive. With the correct strategy, you can do so seamlessly. Your goal should be to augment and enhance your operations, not disrupt and replace. 

For example, Microsoft Cloud technologies leveraged AI to relieve healthcare organizations’ challenges, such as talent shortage and workforce burnout. Providers automated their workflows, invested in specific applications and connectors, and enhanced support for data and operations. 

Azure AI Services, such as text analytics for health, enable healthcare organizations to understand their largely unstructured and untapped medical data. Project Health Insights leans on AI to help healthcare professionals gain actionable insights and inferences from patient data. 

Azure Health Bot, an Intelligent App, empowers organizations to build and deploy legally compliant, powerful conversational healthcare experiences to patients. 

A successful AI integration like this begins with understanding. Knowing what generative AI can do and setting realistic expectations can help you identify where and how AI can best serve your operations. 

Next, you can foster a culture of AI literacy across teams, speeding up adoption by helping them understand how it can aid their work. The more familiar your team is with AI and its possibilities, the smoother the transition. Training and education programs play a pivotal role here. 

Finally, integration requires a systematic approach. It’s not about overhauling everything at once — it's about identifying areas where AI can add the most value, then gradually experimenting and scaling its use. Successful integration stories often feature a phased approach, starting small and growing steadily. 

As we move toward a future where generative AI is a staple in business operations, remember that every journey is unique. Although there’s no one-size-fits-all solution, a strategic approach and a keen understanding of your business needs will help you navigate this new landscape successfully.

Conclusion

Generative AI offers plenty of opportunities for businesses that embrace it. It’s fueling the next generation of intelligent applications, transforming everything from product development to customer engagement to marketing. This technology is the key to unlocking unprecedented growth and innovation. 

While the journey may have challenges, the potential rewards make generative AI worth exploring. As generative AI advances, Azure AI’s broad range of tools and services will help you navigate this fascinating field.

Exercise

  • Watch Episode 02 of the serverless edition Learn Live session to learn how to build, test and deploy an end-to-end intelligent app solution.
  • Complete the Cloud Skills Challenge to build on your AI skills.
  • Register for Ask the Expert: Azure Functions on September 26 for a live Q&A with the Product Team on how to build AI apps using serverless computing.
- - +
Skip to main content

11 posts tagged with "hack-together"

View All Tags

· 12 min read
It's 30DaysOfIA

Delve into creating an Intelligent App that leverages Azure AI Vision to analyze images and extract data. Develop an API to perform optical character recognition (OCR) on uploaded images and testing this API locally.

What We'll Cover:

  • Understanding Azure AI Vision and Azure Kubernetes Service
  • Build a Python Web API to perform OCR
  • Test the API locally

image depicting an Intelligent App using AI

Jumpstart Your AI Journey: Building Your First Intelligent App with Azure AI and AKS (1)

An Intelligent App is a software application that uses artificial intelligence (AI), such as machine learning (ML), to extract meaningful insights, enabling users to make informed decisions based on the extracted text, data, sound, images, and more.

Intelligent Apps can employ advanced algorithms and models to recognize objects, patterns, and features within images, allowing them to perform tasks like object identification, image classification, facial recognition, and image segmentation.

To explore the power of Intelligent Apps, let’s build a Python app that performs optical character recognition (OCR) on images containing numbers and performs aggregate functions on the extracted numbers, like sum and average. We’ll leverage Azure AI Services for the OCR functionality and Azure Kubernetes Service (AKS) to develop, publish, and maintain a cloud-based, scalable app in Azure.

Let’s get started!

Understanding Azure AI Vision and Azure Kubernetes Service

Azure AI Vision is a cloud-based service that offers a range of advanced computer vision capabilities enabling applications to interpret content, such as images and videos, using ML and other AI algorithms. Azure computer vision lets users integrate powerful vision features into their projects without ML experience.

OCR is one of the critical functionalities within Azure vision service, allowing applications to extract text from images. Within Azure AI Vision service, OCR offers several features, including text extraction, multilingual support, layout analysis of documents, and integration with Azure services, such as Azure Functions and Azure Machine Learning.

info

Watch the intelligent apps webinar on Driving Business Value by Modernizing with Cloud-Native & AI with Microsoft and Forrester on September 26.

AKS is Microsoft Azure’s container orchestration platform that enables us to quickly and efficiently deploy, manage, and scale containerized applications, such as our Intelligent App.

Let’s explore how to leverage these tools to build an Intelligent App that uses Azure AI Vision to analyze images and extract data.

Prerequisites

To follow this tutorial, ensure you have the following:

For a preview of this final project, take a look at the complete project code.

Building the API with Azure AI Vision Service

First, log in to your Azure account and navigate to the Azure Portal.

Click Create a resource and search for “resource group.” Create a new resource group named computer-vision.

image in Azure Portal of creating a new resource group

Return to the Azure Portal home page and click Create a resource.

image in Azure Portal of creating a new resource

Search for “computer vision” and select it from the results. Click Create.

image of searching for resource group in Azure Portal

Clicking Create displays the Create Computer Vision wizard. Select the resource group you just created as the Resource group from the dropdown. Enter a resource name similar to what is seen in the screenshot below. Note that this must be a unique name so choose one that is not currently in use. Select Standard S1 as the pricing tier and check the Responsible use of AI box in the field at the bottom.

Image of selecting the Responsible use of AI box

Image of project details

Click Review + Create, then click Create.

Next, get the API key and endpoint from the Azure AI services multi-service account you created. Once the resource is created, navigate to the resource details page. Under Resource Management, select Keys and Endpoint. Once on the Keys and Endpoint page, you’ll find the Key 1 and Endpoint values. These credentials are necessary to access the Azure AI APIs.

image of selecting Keys and Endpoint

second image of selecting Keys and Endpoint

Configuring the Local Environment Variables

Next, we need to define a local environment variable file. This convenient way to store our API keys in a simple text file enables us to manage sensitive information and separate configuration from our API code.

If you haven’t done so already, clone the starter project template from GitHub into your local computer. Open the starter project template in a code editor and create a new .env file in the root folder.

Note: Root folder in this case is the “/Microsoft_Series17-18_Code/intelligent-app-before" folder.

Add the following key-value pairs to the file, remembering to replace the placeholder values with those you obtained from the previous step.

VISION_KEY=<THE-KEY-1-VALUE-FROM-YOUR-AZURE-AI-SERVICE>
VISION_ENDPOINT=<THE-ENDPOINT-VALUE-FROM-YOUR-AZURE-AI-SERVICE>

Reviewing the Quickstart Code

Let’s review the app.py file in the starter project template.

The app.py file works as the entry point for the Flask application. It is responsible for initializing the web server, listening for incoming requests, dispatching them to the appropriate route handlers, and generating responses.

Below is the code contained in the app.py file:

import os
import json

from flask import Flask, request
from flask_restful import Resource, Api
from werkzeug.utils import secure_filename

app = Flask(__name__,
static_url_path='',
static_folder='static/files')

api = Api(app)

app = Flask(__name__)

app.config['UPLOAD_FOLDER'] = 'files'

api = Api(app)

class UploadHandler(Resource):

def allowed_file(self, filename):
return '.' in filename and \
filename.rsplit('.', 1)[1].lower() in {'png'}

def post(self):
form = request.form.to_dict()

if 'file' not in request.files:
return json.dumps({ "success": False, "error": "No file part"})

file = request.files.get("file")
if file and self.allowed_file(file.filename):
filename = secure_filename(file.filename)
upload_folder = "static/files"
if not os.path.exists(upload_folder):
os.makedirs(upload_folder)
local_file_path = os.path.join(upload_folder, filename)
file.save(local_file_path)

return f"File {filename} uploaded successfully to folder: {upload_folder}"

api.add_resource(UploadHandler, "/")

if __name__ == '__main__':
app.run(debug=True)

The UploadHandler class above is defined as a Flask RESTful resource to handle file uploads. The class contains two methods:

  • The allowed_file method checks whether the file extension is allowed for upload. In this case, only .png files are allowed.

  • The post method handles HTTP POST requests for file uploads. It saves the uploaded .png file to the static/files folder.

Finally, the application returns a text response informing us that the file has been uploaded successfully.

Implementing the REST API in Python

To implement image analysis in your REST API, open the starter project template in your terminal, create a virtual environment, and activate it.

Next, install the Azure AI Vision SDK:

pip install azure-ai-vision

Then, add the following line to the requirements.txt file to include the azure-ai-vision package:

azure-ai-vision==0.13.0b1

Now, create an ocr_helper.py file in the project’s root folder. This file is a Python module that provides functions for processing images using OCR. Add the following code to the file:

import os
from statistics import median
from decimal import Decimal
import azure.ai.vision as sdk

def process_ocr(source_image):
service_options = sdk.VisionServiceOptions(os.environ["VISION_ENDPOINT"],
os.environ["VISION_KEY"])

vision_source = sdk.VisionSource(filename=source_image)

analysis_options = sdk.ImageAnalysisOptions()

analysis_options.features = (
sdk.ImageAnalysisFeature.CAPTION |
sdk.ImageAnalysisFeature.TEXT
)

analysis_options.language = "en"

analysis_options.gender_neutral_caption = True

image_analyzer = sdk.ImageAnalyzer(service_options, vision_source, analysis_options)

result = image_analyzer.analyze()

ocr_result = get_ocr_result(result)

return ocr_result

def get_ocr_result(result):
string_list = []

if result.reason != sdk.ImageAnalysisResultReason.ANALYZED:
return sdk.ImageAnalysisErrorDetails.from_result(result)
else:
if result.text is not None:
for line in result.text.lines:
for word in line.words:
string_list.append(word.content)

number_list = convert_to_decimal_list(string_list)

aggregate_result = aggregate_operations(number_list)

return {
"aggregate_result": aggregate_result,
"numbers_read": string_list
}

def convert_to_decimal_list(string_list):
return list(map(Decimal, string_list))

def aggregate_operations(numbers):
result = {
'sum': sum(numbers),
'average': sum(numbers) / len(numbers),
'median': median(numbers),
'min': min(numbers),
'max': max(numbers)
`}`
`return result`

This module uses the azure-ai-vision package to analyze images, including capturing captions and extracting text from the image. The process_ocr function inputs an image file and performs the OCR analysis. The module is a convenient tool for analyzing text-based data in pictures and performing numerical computations based on that data.

Let’s review the different components of the ocr_helper.py module listed above and explore how its functions help our Intelligent App process images and perform aggregate operations:

  • The process_ocr function takes the parameter source_image, which is the path of the image to be processed. The function then initializes the VisionServiceOptions using environment variables VISION_ENDPOINT and VISION_KEY to connect to the Azure AI Vision API.
  • The process_ocr function creates a VisionSource object with the specified source_image file name. ImageAnalysisOptions specify the features to be analyzed, including CAPTION and TEXT. The language is set to English ("en"), and gender-neutral captions are enabled.
  • Finally, an ImageAnalyzer object is created with the service options, vision source, and analysis options. The image is then analyzed using the image_analyzer.analyze method to retrieve OCR results. The recognized text from the OCR result is extracted, with numerical values stored in the string_list variable.
  • The convert_to_decimal_list function converts the list of strings to a list of decimal numbers, which helps process numeric values extracted from the text.
  • The aggregate_operations function calculates various aggregate statistics (sum, average, median, minimum, maximum) from a list of numbers and returns the results in a dictionary.

Note that you must have the appropriate credentials (VISION_KEY) and endpoint (VISION_ENDPOINT) configured for the Azure AI Vision API to use this module.

Finally, we must modify the app.py file so our code can use the process_ocr function of the ocr_helper.py file.

Add the following import statement to the app.py file:

from ocr_helper import process_ocr

Then, replace this line:

return f"File {filename} uploaded successfully to folder: {upload_folder}"

With these two lines:

aggregates = process_ocr(local_file_path)
return json.dumps(aggregates, default=str)

Doing so allows our application to perform aggregate operations (sum, average, median, minimum, maximum) of any numeric values found in the text and return the operation’s result as a JSON response.

Running the Intelligent App Locally

Our final goal is to deploy the sample app to AKS, which will run the app in a container. However, as a good practice, let’s run the app in a container locally before moving it to AKS. Doing so ensures that our application and its dependencies are encapsulated and can be run consistently across different environments.

Deploying to Docker Desktop

For our application to run on Docker, it needs two additional files: a Dockerfile and docker-compose.yml. A Dockerfile creates a single container image by specifying the steps to build it, and a docker-compose.yml file manages multiple containers as a cohesive application, defining their configurations and relationships. Dockerfile builds the image, while docker-compose.yml orchestrates multi-container applications.

In the project root folder, add a file named Dockerfile with the content below:

# syntax=docker/dockerfile:1

FROM python:3.8-slim-buster

WORKDIR /intelligentapp

COPY requirements.txt requirements.txt
RUN pip3 install -r requirements.txt

RUN pip install debugpy

COPY . .

CMD [ "python3", "-m" , "flask", "run", "--host=0.0.0.0"]

Now create a file named docker-compose.yml file in the project’s root folder with the following code, replacing the VISION_KEY and the VISION_ENDPOINT according to the environment variables you configured earlier:

version: '3.8'
services:
intelligentapp:
build:
context: .
dockerfile: Dockerfile
image: intelligent-app
ports:
- 5000:5000
container_name: intelligent-app
environment:
- VISION_KEY=<THE-KEY-1-VALUE-FROM-YOUR-AZURE-AI-SERVICE>
- VISION_ENDPOINT=<THE-ENDPOINT-VALUE-FROM-YOUR-AZURE-AI-SERVICE>

Then, run the following command in the terminal to build the image and start a container for the Intelligent App services defined in the docker-compose.yml file:

docker-compose up --build --force-recreate

Next, open a new terminal and run the command below to list the image deployed to your local Docker:

docker images

docker images

Testing the Intelligent App Locally

Now, we’ll test the Intelligent App’s functionality using the following test image:

test image to test functionality

This image is provided as sample1.png in the sample app source files, so you can easily use it in the next step.

The Intelligent App will read the image above using OCR to extract numbers in the image and perform five aggregate functions:

  • Sum
  • Average
  • Median
  • Min
  • Max

To test the API, open Postman and fill out the fields as follows:

  • URL: http://localhost:5000/
  • Method: POST
  • Body:
    • Form-data
      • Key: File — Click the right end of the Key field and select File from the dropdown list.
      • Value: Click Select Files, then select the sample1.png file provided in the sample code.

image of selecting the sample file

Now click the Send button and review the result body:

"{\"aggregate_result\": {\"sum\": \"25821\", \"average\": \"5164.2\", \"median\": \"5622\", \"min\": \"1447\", \"max\": \"9802\"}, \"numbers_read\": [\"3049\", \"5901\", \"5622\", \"1447\", \"9802\"]}"

As we can see, the app running on a local container returned the correct results based on the sample image. So, we’re ready to prepare our Azure resources to run the Intelligent App on AKS.

Exercise

In this article, we explored the creation of an Intelligent App that leverages Azure AI Vision to analyze images and extract data. We learned how to build a Python Web API to perform OCR on uploaded images and subsequently test this API locally.

Head to the next part, to deploy this API via Azure Kubernetes Service.

+ + \ No newline at end of file diff --git a/30daysofIA/tags/hack-together/page/4/index.html b/30daysofIA/tags/hack-together/page/4/index.html index 8f92464d3d..7dd6fa8b2b 100644 --- a/30daysofIA/tags/hack-together/page/4/index.html +++ b/30daysofIA/tags/hack-together/page/4/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "hack-together" | Build Intelligent Apps On Azure +11 posts tagged with "hack-together" | Build Intelligent Apps On Azure @@ -14,13 +14,13 @@ - - + +
-

9 posts tagged with "hack-together"

View All Tags

· 9 min read
It's 30DaysOfIA

In the part of our series on intelligent apps, we’ll explore how AI is transforming application development, from design and architecture to building.

What We'll Cover:

  • Revolutionizing application development using AI
  • Infusing AI in application design architecture
  • AI Assisted Pair Programming
  • Future of App Development

A decorative image of application development with AI

Reimagining Application Development With AI: A New Paradigm

Artificial intelligence (AI) is impacting every industry. AI-powered development tools offer new functionalities and assist software developers throughout the development process, revolutionizing app development. From initial design and architecture to coding and debugging, AI reshapes how we approach application conception, creation, and iteration.

In part one of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” we explored the new breed of intelligent apps and how they use AI. In this article, we’ll see how AI technologies enhance developer toolkits and shape the future of app development, particularly through AI-enabled coding assistance and debugging.

The Dawn of Intelligent Apps: Revolutionizing Application Development

AI is more than just another addition to the development toolbox—it represents a revolution bringing unprecedented efficiency, innovation, and user engagement to our applications. 

What sets intelligent apps apart is how they can adapt and learn, helping us deliver unique, real-time user experiences.  

One example is an AI-driven health application that can track biometric data, predict potential health issues, provide personalized health advice, and even guide emergency services during a medical crisis. An AI-enabled language learning app that adapts to our learning speeds, recognizes our problem areas, and customizes its content to help us improve our language proficiencies is another example.  

Intelligent apps like these don’t just use data for recommendations—they can make critical decisions and provide sophisticated services that were previously impossible. 

For developers, intelligent apps mean we’re no longer just coding—we’re teaching applications how to think, adapt, and make decisions. AI is reshaping every step of the development journey, helping us to build more intuitive and user-focused products. 

Next, we’ll examine the impact of AI on application design and architecture and how AI-assisted “pair programming” changes software development.

info

Register for the FREE webinar on Intelligent Apps with Microsoft and Forrester on September 26 to explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries.

There will be a showcase of real-world use cases that demonstrate how AI can be seamlessly integrated into cloud-native environments driving tangible business value.

AI in Design and Architecture: Fueling Creativity and Efficiency

AI technologies help developers automate and streamline their processes and provide new ways to think about design.  

In traditional application development, the design and architecture phase can take significant time, effort, and expertise. For instance, adopting a monolithic architecture or a microservices-based design can significantly increase coding, testing, and debugging complexity. The app may even behave differently when hosted locally than in a deployed environment. It may take hours to track down an issue manually—time developers could spend elsewhere. 

Conversely, we can generate numerous design iterations in seconds with AI, each optimized to fulfill specific user needs and business goals. AI tools can quickly model various scenarios' impacts and software design patterns in the architectural design phase. It provides insights into performance and potential issues and suggests improvements. AI can also generate skeleton code, allowing developers to prototype different solutions or test specific components and technologies rapidly. By reviewing thousands of lines of code in seconds, AI allows us to focus on creating apps that serve users through feature development instead of searching for bugs.  

Integrating AI into the design and architecture phase can even extend our creative capabilities. By leveraging the power of generative AI—like large language models (LLMs)—and their broad knowledge and context going beyond software, we can better brainstorm apps and feature ideas. We can go beyond code and software architecture and get ideas about user experience, logo and branding, or marketing concepts. And as AI tools deliver insights and ideas we would have never considered; it encourages more innovation.

info

Watch Episode 01 of the Learn Live session to learn how to build an end-to-end intelligent app solution architecture.

The Power of AI-Assisted Pair Programming in Building Applications

AI-assisted pair programming is a collaborative coding approach where an AI system—like GitHub Copilot or TestPilot—assists developers during coding. It’s an increasingly common approach that significantly impacts developer productivity. In fact, GitHub Copilot is now behind an average of 46 percent of developers’ code and users are seeing 55 percent faster task completion on average. 

For new software developers, or those interested in learning new skills, AI-assisted pair programming are training wheels for coding. With the benefits of code snippet suggestions, developers can avoid struggling with beginner pitfalls like language syntax. Tools like ChatGPT can act as a personal, on-demand tutor—answering questions, generating code samples, and explaining complex code syntax and logic. These tools dramatically speed the learning process and help developers gain confidence in their coding abilities. 

Building applications with AI tools hastens development and provides more robust code. For example, GitHub Copilot helps us write new code accurately. It also quickly makes sense of existing code, suggests whole new lines or blocks of code, and even detects errors and proposes fixes. It’s like having a second coder working alongside us—one who never tires and is trained on billions of lines of code. 

However, it’s also important to acknowledge the limitations of AI. While generative AI-assistance technologies like GitHub Copilot are impressive, they’re still imperfect. They can sometimes produce erroneous, non-functional, or vulnerable code. They can also lack nuanced, domain-specific knowledge.  

So, although these tools are helpful, we can’t rely on them fully—these are programming aids rather than definitive solutions. AI-assisted pair programming is about augmenting the human developer’s skills, not replacing them.

Debugging and Improving Code With AI: Enhancing Application Quality

Identifying and correcting code errors is often time-consuming and challenging for developers. AI streamlines the debugging process by automatically detecting anomalies, suggesting fixes, and learning from previous bugs to prevent them from recurring. 

We can use generative AI to create unit tests from code snippets, from simple parameter checking and input validation to complex mocks of database and network services. Automated AI testing improves code coverage and robustness. It saves significant time compared to manually crafting unit tests. 

AI also revolutionizes code reviews by scanning and highlighting potential issues—like code patterns leading to a bug or a coding standards violation—and suggesting ways to simplify or refactor the code for readability and performance. This reduces human error and makes the code review process more efficient. Its benefits extend to small teams, where the availability of developers is crucial to deadlines, and large teams, where effective collaboration depends on readability and code consistency. AI further saves time by triaging issues so developers can prioritize the most mission-critical ones. 

Aside from improving code quality, AI is critical to understanding system behavior. Intelligent AI-assisted software development tools can monitor and audit system behavior, giving developers a better insight into how their code performs in different scenarios. AI can track patterns, identify inefficiencies, and provide alerts when they detect unusual behavior—enabling developers to enhance their code further. 

By predicting and identifying potential vulnerabilities, intelligent AI-assisted coding apps like Microsoft Security Copilot are pivotal in ensuring application functionality, reliability, and security. These AI tools are continually trained and upgraded with large codebases and user feedback that refines their responses. This ability to learn from past mistakes makes AI an incredibly powerful tool that will continue to expand its capabilities in the future.

info

Complete the AI Cloud Skills Challenge to build on you AI application development skills with paired programming using generative AI and GitHub Copilot.

AI and the Future of Application Development

The rapid advancements in AI and machine learning technologies point to an exciting future for application development. 

In “The Future of Applications,” software developer Mike Cann illustrates how LLMs might revolutionize application development. Cann that suggests fully-autonomous coding—AI-generated applications created entirely from prompts—may supersede traditional hand-crafted applications. Dynamically generated user interfaces could offer personalized and customizable experiences while we can design APIs for machine consumption and efficiency rather than human readability. Furthermore, we can store data in less structured, more adaptable, and flexible forms. 

AI is already making application development more accessible. With AI handling the complex, technical aspects of development, less-technical individuals can take a greater role in building applications. Consequently, AI can potentially democratize software engineering. 

Intelligent apps will continue to evolve, becoming more efficient, adaptable, and capable of learning from user interactions. Similarly, AI will inevitably become integral in every stage of app development.

Summary

AI has already created a new development paradigm—one in which machine intelligence and human creativity come together to produce innovative, efficient, and robust applications. AI already assists us at every stage of app development, from design and architecture to coding and debugging. 

Developers who embrace AI tools and integrate them into their processes can enhance their productivity, improve the quality of their applications, and expand the possibilities of their application development.  

The future of development is automated and AI-driven, and it’s here today. Getting started is easy—try an AI-powered development tool like GitHub Copilot to see how AI-assisted pair programming can enhance your productivity and code quality.

Exercise

- - +
Skip to main content

11 posts tagged with "hack-together"

View All Tags

· 9 min read
It's 30DaysOfIA

In the last article, we created an Intelligent App that leverages Azure AI Vision to analyze images and extract data. Develop an API to perform optical character recognition (OCR) on uploaded images and testing this API locally.

In this article, we will deploy the web API to the cloud platform using Azure Kubernetes Service.

What We'll Cover:

  • Understanding Azure AI Vision and Azure Kubernetes Service
  • Build a Python Web API to perform OCR
  • Test the API locally

image depicting an Intelligent App using AI

Jumpstart Your AI Journey: Building Your First Intelligent App with Azure AI and AKS (2)

In the previous article we explored the creation of an Intelligent App that leverages Azure AI Vision to analyze images and extract data. We learned how to build a Python Web API to perform OCR on uploaded images and subsequently test this API locally.

In this article we will use Azure Kubernetes Service (AKS) to develop, publish, and maintain our app in the cloud on Azure.

Let’s get started!

Prerequisites

To follow this tutorial, ensure you have completed Jumpstart Your AI Journey: Building Your First Intelligent App with Azure AI and AKS (1).

Pushing a Container Image to Azure Container Registry (ACR)

To start, open your CLI or terminal and type the following command:

az login

Follow the instructions displayed on your browser to enter your Azure credentials.

Once authenticated, you’ll initiate a secure connection between your local environment and Azure. This process grants you access to cloud services and resources.

Next, type the command below in your terminal to set up a new Azure Container Registry (ACR) to store your container images:

az acr create --resource-group computer-vision --name <name-of-azure-container-registry> --sku Basic

Remember to replace <name-of-azure-container-registry> with your container registry name. The name must be unique within Azure and comply with these rules.

The command above creates an Azure Container Registry (ACR) in the computer-vision resource group under the Basic SKU. This ACR is your secure and private repository for storing container images within Azure.

Next, log in to the registry with the following command:

az acr login -n <name-of-azure-container-registry>

The az acr login command above lets you securely authenticate and access the specified ACR without providing their Azure credentials each time.

Now, run the following command in your terminal. It will display the endpoint URL to log in to and interact with the ACR for pushing and pulling container images.

az acr show --name <name-of-azure-container-registry> --query loginServer --output table

This returns an endpoint URL as follows:

Result
----------------------------------
<name-of-azure-container-registry>.azurecr.io

Now, run the following command to show all container images, their repository, tags, and size:

docker images
REPOSITORYTAGIMAGE IDCREATEDSIZE
intelligent-applatesta7bf9f75361716 hours ago197MB

Tags are necessary to push Docker images to a remote registry like Azure Container Registry. They also let you differentiate between different versions of the same image and upload or download the one you want.

Run the following command to tag your Docker image:

docker tag intelligent-app <name-of-azure-container-registry>.azurecr.io/intelligent-app:v1

Then, run the docker images command again to check your tagged image:

docker images
REPOSITORYTAGIMAGE IDCREATEDSIZE
intelligent-applatestc52168039265About a minute ago197MB
<name-of-azure-container-registry>.azurecr.io/intelligent-appv1c52168039265About a minute ago197MB

Now run the following command so Docker can securely upload the image to your Azure Container Registry:

docker push <name-of-azure-container-registry>.azurecr.io/intelligent-app:v1

Once we've deployed the image to the container registry, AKS can access it during deployment.

info

Watch Episode 03 for the Learn Live session to experience a guided session on how to build, test and deploy an end-to-end intelligent app solution.

Deploying the Intelligent App on Azure Kubernetes Service (AKS)

Before we deploy our Intelligent App to AKS, we need to provision an AKS cluster and define Kubernetes manifests.

To provision an AKS cluster to host our application, we specify the desired configurations for the cluster, such as the number of nodes, node size, and networking options. But first, download and install the Kubernetes command-line tools (kubectl), a client credential plugin implementing Azure authentication:

az aks install-cli

If you’re using Linux, review this tutorial. Then run the following:

sudo az aks install-cli

Next, run the following command in your terminal to enable access to the networking-related resources and services provided by the `Microsoft.Network`` namespace in Azure:

az provider register --namespace Microsoft.Network

Now, we must create an AKS cluster. Run the following command to create an AKS cluster named aks-intelligent-app in the computer-vision resource group.

az aks create --resource-group computer-vision --name aks-intelligent-app --node-count 1 --generate-ssh-keys

The command above specifies the target resource group: computer-vision. The node pool is configured with one virtual machine (VM), and the secure shell (SSH) keys for secure node access are generated automatically.

Next, run the following command to update the AKS cluster you created by attaching it to your ACR. Doing so allows the AKS cluster to pull container images from the specified ACR when deploying workloads to the cluster.

az aks update -n aks-intelligent-app -g computer-vision --attach-acr <name-of-azure-container-registry>

Then, run the command below to configure kubectl to work with our AKS cluster in the computer-vision resource group.

az aks get-credentials --resource-group computer-vision --name aks-intelligent-app

The command above retrieves the necessary credentials and context information required for kubectl to communicate with the AKS cluster.

We still have to define the Kubernetes manifests written in YAML, describing the desired state of our application deployment, including containers, networking, and scaling rules. We’ll prepare these manifests, including Deployment and Service configurations, to define how our application should be deployed and exposed.

Create a folder named Deployment in the project root directory. Next, create two files in the deployment folder: deployment.yml and service.yml.

Add the following configuration to the deployment.yml file, replacing the <name-of-azure-container-registry> placeholder with your registry’s name:

apiVersion: apps/v1
kind: Deployment
metadata:
name: intelligent-app
spec:
replicas: 1
selector:
matchLabels:
app: intelligent-app
template:
metadata:
labels:
app: intelligent-app
spec:
nodeSelector:
kubernetes.io/os: linux
containers:
- name: intelligent-app
image: <name-of-azure-container-registry>.azurecr.io/intelligent-app:v1
resources:
limits:
memory: 512Mi
cpu: "1"
requests:
memory: 256Mi
cpu: "0.2"
ports:
- containerPort: 5000
env:
- name: FLASK_DEBUG
value: "1"
- name: VISION_KEY
value: <THE-KEY-1-VALUE-FROM-YOUR-AZURE-AI-SERVICE>
- name: VISION_ENDPOINT
value: <THE-ENDPOINT-VALUE-FROM-YOUR-AZURE-AI>

Additionally, edit the VISION_KEY and VISION_ENDPOINT environment variables above according to the API key and endpoint of your Azure AI Services instance.

Then, add the following configuration to the service.yml file:

apiVersion: v1
kind: Service
metadata:
name: intelligent-app-service
spec:
type: LoadBalancer
ports:
- protocol: TCP
port: 80
targetPort: 5000
name: port5000
selector:
app: intelligent-app

Now, we’ll deploy our application using kubectl. Applying the Kubernetes manifests creates the necessary resources and deploys our containerized application to the AKS cluster.

First, change the terminal to the deployment folder:

cd Deployment 

Then, run the following command to create or update Kubernetes resources defined in the deployment.yml file:

kubectl apply -f deployment.yml

Create a Kubernetes Service resource in a Kubernetes cluster based on the configuration defined in the service.yml file using the code below:

kubectl apply -f service.yml

Once you’ve applied the resource definition and the service configuration contained in the deployment.yml and the service.yml files, open the aks-intelligent-app Kubernetes Service in the Azure Portal, select Workloads under Kubernetes resources on the sidebar, and find the deployment named intelligent-app. It must have the status “Ready 1/1”. If you encounter an issue with this status, check out these troubleshooting resources.

image of selecting the deployment in Azure Portal

Testing the Intelligent App on AKS

To test the app on AKS, first, run the command below:

kubectl get services

This command lists the Services and their corresponding details, including the Service name, cluster IP address, external IP, and ports.

NAMETYPECLUSTER-IPEXTERNAL-IPPORT(S)AGE
intelligent-app-serviceLoadBalancer10.0.77.6020.121.76.15380:30936/TCP47s
kubernetesClusterIP10.0.0.1<none>443/TCP14m

The output above shows a Kubernetes Service named intelligent-app-service with a type set to LoadBalancer. It’s reachable from within the cluster using the cluster IP 10.0.77.60 and accessible externally via the external IP 20.121.76.153 on port 80 (mapped to port 30936).

Note: Your set of IP addresses will differ. Remember to use your unique external IP address when testing with Postman.

To test the deployed app, go to Postman, replace the URL with the external IP of the Kubernetes Service you just deployed, and click Send:

mage of sending the app in Postman

As we can see, our Intelligent App has successfully deployed to AKS and is functioning on the cloud as expected.

Exercise

Next Steps

In this two-part article, we explored the creation of an Intelligent App that leverages Azure AI Vision to analyze images and extract data. We learned how to build a Python Web API to perform OCR on uploaded images and subsequently deploy this API via Azure Kubernetes Service.

Besides OCR and Image Analysis, you can continue exploring Azure’s vast array of services and experiment further with Azure AI and AKS by applying various practical uses to your Intelligent Apps, including natural language processing, speech recognition and synthesis, sentiment analysis for customer feedback, and automated content moderation.

+ + \ No newline at end of file diff --git a/30daysofIA/tags/hack-together/page/5/index.html b/30daysofIA/tags/hack-together/page/5/index.html index e6abb8d4da..1a94388d10 100644 --- a/30daysofIA/tags/hack-together/page/5/index.html +++ b/30daysofIA/tags/hack-together/page/5/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "hack-together" | Build Intelligent Apps On Azure +11 posts tagged with "hack-together" | Build Intelligent Apps On Azure @@ -14,13 +14,13 @@ - - + +
-

9 posts tagged with "hack-together"

View All Tags

· 10 min read
It's 30DaysOfIA

Explore real-world examples of how Intelligent Apps revolutionize logistics, retail, finance, manufacturing, tech, and medicine.

What We'll Cover:

  • Real-world businesses revolutionizing operations with Intelligent Apps
  • High level architecture for industry scenarios
  • Implementation architecture overview across industries

A decorative header image for the blog

Real-world Success Stories

According to IBM’s 2022 Global AI Adoption Index, 35% of surveyed companies already use AI in their business, and 42% are exploring the idea. Organizations lagging behind the AI curve will quickly fall behind early adopters, who are already enjoying its benefits and boosting their investment in this technology.

In the first part of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” we explored how organizations can use AI-powered apps, or intelligent apps, to transform their operations, decision-making, and customer experiences, leaping ahead of the competition.

Intelligent apps significantly improve operational efficiency and customer engagement across industries like logistics, retail, finance, manufacturing, tech, and healthcare. In this article, we’ll spotlight real-world success stories of businesses revolutionizing operations with intelligent apps using Microsoft Azure’s app, data, and AI services.

info

Register for the Learn Live on September 21 for a live guided experience with SMEs on how to build end-to-end solution architecture for real world implementation of Intelligent Apps.

Streamlining Operations: Intelligent Apps in the Airline Industry

Aerospace pioneer Airbus leveraged intelligent apps to streamline its operations and innovate its services with two ground-breaking modernizations that use Azure apps, data, and AI services to reimagine pilot training and predict aircraft maintenance issues. 

Aviation technology’s rapid evolution compelled Airbus to reconceive their approach to training pilots. The aircraft manufacturer created a pilot training chatbot by combining Azure AI Services with Speech to Text and Text to Speech. The bot helps trainee pilots navigate over 6,000 pages of technical information. Pilots can ask questions verbally for quick answers, aiding their understanding of dense information and speeding up training. And when Airbus needed to accommodate its military consumers, deploying its Kubernetes cluster to a disconnected, “air-gapped” environment met the strict security requirements of military aircraft training.

A chatbot could look like the following diagram as Azure services work together to create a conversational experience. Its components could include security and governance, bot logic and user experience (UX), bot cognition and intelligence, data extract, transfer, and load (ETL) operations, quality assurance and enhancements, logging, and monitoring and reporting.

A diagram of a conversational experience enabled by Azure services

Airbus also used AI to improve its aircraft maintenance and safety. They deployed AI Anomaly Detector to gather and analyze telemetry data from multiple flights, enabling more profound insights into wear and tear on military aircraft operating in harsh conditions. This AI application helps Airbus better understand each airplane’s health. Airbus can predict and fix potential problems before they occur, improving the aircraft’s safety and operational readiness.

AI and Azure are changing how organizations like Airbus operate, even in strict, highly regulated markets with stringent security requirements. And by streamlining pilot training and staying ahead of aircraft maintenance, Airbus helps its military and commercial clients reliably deliver their goods across the globe, maintaining their critical supply chains.

Boosting Customer Service: Intelligent Apps in the Retail Industry

The retail industry has been among the first to adopt and benefit from intelligent apps. For example, the leading American used car retailer, CarMax, uses intelligent apps to transform the car shopping experience.

Before embracing AI, CarMax faced a significant challenge: Manually creating text summaries was time-consuming and labor-intensive. CarMax decided to leverage Azure OpenAI Service to automatically generate car research pages, offering customers valuable insights while enhancing the website’s search engine rankings.

This AI-driven approach has significantly streamlined the process, achieving in a few months what would have taken years manually. Also, the AI system condenses thousands of customer reviews into a few readable sentences, offering potential buyers a quick overview of others’ experiences.

CarMax has improved their customer experience while boosting operational efficiency through AI adoption. CarMax’s editorial staff focuses on creating strategic, longer-form content with the time saved, enhancing productivity, and driving more website traffic.

CarMax’s success story serves as a testament to the potential of intelligent apps in the retail industry.

Enhancing Cybersecurity: Intelligent Apps in Finance

Intelligent apps also impact the highly regulated financial services sector. Swift, a leading infrastructure provider for financial messaging services, uses intelligent apps to enhance decision-making and combat financial crime.

The massive growth of cross-border transactions and instant payment networks led to a surge in financial fraud that can cost the industry hundreds of billions of dollars every year. So, Swift and Microsoft partnered to build an anomaly detection model using federated learning techniques with Azure Machine Learning and Azure confidential computing.

Microsoft Purview helped govern data, as the federated learning method partially trained each model in a silo before aggregating them. This approach enabled Swift and Microsoft to develop a highly accurate model without copying or moving data from Swift members’ secure locations, ensuring the highest level of security and privacy.

Financial organizations can now deploy this trained ML model to run a real-time fraud detection system, lowering the cost of financial fraud.

Organizations can deploy a fraud detection system like the following example on Microsoft Azure. The system ingests data from streams or storage, runs analytics, and uses ML to detect fraud and store results. It then alerts users and other systems to the security issue.

A diagram of an example fraud detection system

Swift provides the innovative model to its banking partners, who can share data to further train the model. Azure confidential computing securely runs new models on current data to accumulate insights, generating an ongoing learning cycle. This iterative approach strengthens the global detection of fraudulent financial transactions through an increasingly faster and more accurate model. 

The collaboration between Swift and Microsoft demonstrates how combining various Azure services creates Intelligent Apps to enhance decision-making in finance. These apps solve real problems at an unprecedented global scale. 

Enhancing Efficiency: Intelligent Apps in Manufacturing

3M, known for Post-it Notes and other innovative products, wielded the power of Intelligent Apps to standardize, automate, and accelerate its sales forecasting. The company needed a unified, automated approach to replace its multiple manual methods.

It adopted Azure Machine Learning and other Azure AI Services to develop, train, and implement 1,500 custom models for each region and division, replacing manual methods. This solution delivers timely finance, sales, and marketing insights while freeing teams from time-consuming manual data manipulation tasks.

3M used Azure Machine Learning, automated machine learning (AutoML), and the Many Models Solution Accelerator to train and score numerous machine learning models in parallel. This approach significantly shortened the company’s development cycle time.

Then, 3M integrated Microsoft Power BI into their solution to compile and visualize results more effectively. Executives now have an unbiased and unfiltered view of data for better decision-making. With its improved ability to forecast sales, 3M can focus on manufacturing the products it needs efficiently.

3M’s success demonstrates how combining various Azure services can enhance efficiency in the manufacturing industry. These apps improve operational processes while enabling employees to focus on higher-level tasks. Strategic decisions are more targeted, and business conversations are more meaningful.

Driving Innovation: Intelligent Apps in the Technology Industry

As expected, the tech industry is at the forefront of leveraging Intelligent Apps to drive innovation. Elastic saw an opportunity to use intelligent apps to optimize its search services and meet customer needs more effectively. The company built a managed, secure, and scalable solution for its customers, Elastic Cloud, by leveraging Microsoft Azure.

Elastic Cloud uses Azure Machine Learning and other Azure AI Services to automate many basic management tasks, such as backing up data, upgrading, and scaling deployments. This approach enables Elastic’s customers to focus less on managing the software and more on serving their own clients. Customers can quickly deploy and manage Elastic Cloud on Kubernetes (ECK) using Azure Kubernetes Service.

As a result, Elastic’s cloud business is growing twice as fast as its on-premises solutions. Enterprise search users have reported significant improvements in system availability and employee satisfaction.

Additionally, Elastic incorporated Azure OpenAI Service into its Elasticsearch Relevance Engine (ESRE), providing more relevant answers to search queries. ESRE enables text, vector, and hybrid search, offering a more effective and accurate response to queries.

Elastic’s success story demonstrates how combining various Azure services can create intelligent Apps to drive innovation in the tech industry. These apps help improve operational processes and deliver better results quickly.

Revolutionizing Healthcare: Intelligent Apps in Medicine

Intelligent apps also drive transformative changes in the healthcare sector. Cambridgeshire and Peterborough NHS Foundation Trust, a healthcare provider in the United Kingdom, gradually shifted their on-premises systems to the cloud using Azure, improving flexibility, and lowering costs. They needed a solution for their electronic patient record (EPR) system that integrates 23 years of data across multiple systems and formats.

The Trust used Microsoft Azure and Azure Cognitive Search, including semantic search for understanding intent and contexts and cognitive capabilities for extracting insights from data, to standardize and automate data management. This approach made legacy, archive, and live data more discoverable, saving clinicians’ time. The Trust’s solution searches through millions of clinical documents in seconds, even recognizing handwritten notes without needing a separate optical character recognition (OCR) solution to extract text from images (Azure Cognitive Search automatically does it). Clinicians can now uncover previously inaccessible insights, enabling them to offer more personalized care.

The Trust also used Integration Services tools such as Azure Logic Apps to create workflows without writing code and Event Grid to connect Azure and third-party services through a publisher-subscriber model. They switched on a live feed from their new EPR system to Azure to feed real-time information into other applications. Researchers can now access anonymized data to fuel medical innovations beyond the NHS.

The healthcare provider also invested heavily in Power BI’s data visualization and analytical functions to sift through massive amounts of information. This level of data accessibility was previously impossible with their on-premises system. The new insights have significantly improved clinicians’ efficiency for better patient care.

Data accessibility and security are critical in the healthcare industry. Intelligent Apps empower organizations like the Trust to revolutionize medical care. AI capabilities helped the Trust improve its operational efficiency and user experiences, setting a new standard for healthcare in the digital age.

Summary

AI is driving transformation and real-world successes across industries. Intelligent apps are reshaping our world by streamlining operations in manufacturing and logistics, enhancing customer experience in the retail sector, improving decision-making in finance, driving innovation in tech, and revolutionizing healthcare.

As organizations adopt AI, they’re innovating in ways that were once too difficult, expensive, or outright impossible. Companies that wait to embrace intelligent apps risk the chance of falling behind. By embracing intelligent apps and leveraging the capabilities of Azure, you can harness the transformative potential of AI to remain on the cutting edge of technology.

Exercise

- - +
Skip to main content

11 posts tagged with "hack-together"

View All Tags

· 10 min read
It's 30DaysOfIA

Explore how digital natives and Independent Software Vendors (ISVs) can transform their business operations and generate higher value for customers with generative AI and intelligent apps.

What We'll Cover:

  • Potential of Generative AI in application development
  • Challenges with Generative AI
  • Integrating Generative AI with business operations

image of generative AI and intelligent apps used in business

Unleashing Business Transformation: How Digital Natives Can Leverage Generative AI

Generative AI is no longer a technological flight of fancy. It’s here, and it’s rewriting the rules of business innovation. 

This type of AI leverages machine learning algorithms to create new data with similar characteristics to a given dataset. It goes beyond text generation using generative pretrained transformer (GPT) models, covering an array of applications that span from image synthesis to translations and more. It also powers a new class of intelligent applications, which combine AI capabilities with traditional software functions for enhanced user experiences and advanced business solutions. 

Generative AI is transforming how digital natives and independent software vendors (ISVs) operate. Generative AI offers new possibilities for creating unique business value, enhancing customer experiences, and driving growth. 

It plays a critical role in applications, improving functionality, aiding decision-making, and automating complex tasks to deliver dynamic, uniquely personalized user experiences. Generative AI also enables and automates data-driven outcomes, functions, and features.

Integrating AI capabilities into customer-facing apps and turning them into intelligent apps benefits both the customer and the business, as we discussed in Demystifying Intelligent Applications: Leveraging AI in App Development. You get faster product development workflows and better business intelligence for sales and marketing, while your users get more natural and context-aware features and interactions. 

That said, to realize generative AI’s potential, you need some strategy and knowledge of how and where it fits into your current business and operations. We’ll examine this technology’s potential to transform business operations, its risks, and rewards, and how to integrate it into your existing operations. 

Unpacking the Potential of Generative AI

Generative AI is creating new opportunities in the digital playing field. It empowers businesses to generate insights, design products, and automate processes in ways and at speeds that were previously unimaginable. You can now predict customer behavior more accurately, offer highly personalized user experiences, and prototype innovative ideas rapidly. 

For example, Microsoft used generative AI to reinvent web search in the new AI-powered Bing and Edge. The search experience has evolved from a solitary user flow of typing in keywords and scrolling through results. It’s now a conversational experience where Bing uses context to offer better quality and more personalized search queries and results. 

Similarly, Microsoft harnessed generative AI to transform its productivity suite into intelligent apps. Through Copilot for Microsoft 365, PowerPoint now empowers users to tell their stories by using DALL-E to generate images effortlessly. It also helps rephrase text-heavy slides to be more straightforward. Outlook now assists users to compose more effective emails, enabling better communication.

These are just some examples of how AI powers day-to-day work and operations. 

Businesses that ignore generative AI miss out on untapped market opportunities, letting competitors gain an edge in the digital race. By seeking ways to rethink assumptions and reinvent existing approaches, you can effectively use generative AI to create new opportunities, open products to new use cases and markets, unlock new growth and customer satisfaction levels, and increase your revenue.

info

Register for the intelligent apps webinar on Driving Business Value by Modernizing with Cloud-Native & AI with Microsoft and Forrester on September 26.

Explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries. 

The Crucial Role of Generative AI in Business Operations

Generative AI can be a powerful catalyst for business transformation. Let’s quickly tour some ways it can redefine business operations. 

Product development is traditionally a long and painstaking process. Now, tools like GitHub Copilot and TestPilot use generative AI to make coding and testing software easier, faster, and more robust. 

Similarly, machine learning as a service technology, such as Azure Machine Learning, democratizes the availability of machine learning. It helps collect, clean, and train models across datasets, making engineering and analyzing data more efficient and accessible.  

Generative AI also simplifies the process of creating intelligent apps. Previously, simulating thousands of prototypes, predicting their performance, and narrowing down to the most viable options could take weeks, months, or even years of iteration and data analysis. With generative AI, this process can be as quick as days, hours, or perhaps minutes. 

Customer experience is also ripe for transformation. Personalized recommendations drive engagement and sales, and generative AI can scale that personalization to every customer interaction using comprehensive and continually updated data. For instance, Microsoft leverages AI to enable personalization in Dynamics 365 Customer Insights. Using this tool, you can ask questions about your customers in natural language and gain valuable and actionable insights into your customer base. 

Lastly, marketing and sales can benefit from generative AI. For example, identifying current and future key markets and emerging trends helps companies stay ahead of the curve. Tools like Copilot in Power BI help you quickly understand and uncover insights about your business, enabling your team to craft targeted campaigns, create engaging content, and optimize your business strategy. 

Leveraging this full generative AI potential requires the right tools. GPT-4, for instance, can be instrumental in realizing effective business transformation. It excels in understanding context, generating human-like text, and learning from minimal data input, making it a formidable tool in any digital native's arsenal. Platforms like Azure OpenAI Service bring GPT-4’s power to your fingertips.

However, as with any powerful technology, it’s unwise to treat generative AI as a perfect solution. You must know the challenges and risks of not leveraging it effectively, including technical and human-factor considerations. 

Without generative AI, your company risks missing the opportunity to please customers via personalization, as discussed previously. You’ll miss sales opportunities via business intelligence insights, limiting your ability to grow revenue and keep up with the competition. 

One challenge of leveraging generative AI’s full potential is finding the right tools. Fortunately, you can implement generative AI in your organization without investing in infrastructure, computational power, and expertise. Platforms like Azure OpenAI Service and Azure AI Services make AI easily accessible. They also work with Azure compute and data services like Azure Functions and Azure Kubernetes Service (AKS), enabling you to build intelligent apps easily and quickly. 

However, generative AI can’t do everything. It may need human expertise and intervention to use it fully. 

For example, generative AI can create unique data. However, the data may have limited diversity and complexity. The AI may need help recognizing additional considerations or requirements specific to a problem. It may also lack context due to various factors, such as the information’s freshness. Also, since it’s an emerging field that’s potentially worth $4.4 trillion, we’re still learning about the nuances and potential regulatory impact of generative AI. 

Perhaps the most crucial and challenging aspect of adopting generative AI is an organization’s required cultural shift. Your teams must learn how to use AI as your company fosters an understanding of AI’s potential and limitations. 

Begin integrating AI literacy into your company’s DNA. Knowledge of its potential and the readily available tools can inspire your teams to incorporate generative AI into their daily workflows and business operations quickly, improve productivity, and deliver superior customer experiences. 

No company wants to be outpaced by its competitors. You can balance generative AI’s potential with your business operations by staying aware of and adapting to the dynamic AI landscape.

Achieving Harmony: Integrating Generative AI with Existing Business Operations

Integrating generative AI into existing business operations doesn’t have to be disruptive. With the correct strategy, you can do so seamlessly. Your goal should be to augment and enhance your operations, not disrupt and replace. 

For example, Microsoft Cloud technologies leveraged AI to relieve healthcare organizations’ challenges, such as talent shortage and workforce burnout. Providers automated their workflows, invested in specific applications and connectors, and enhanced support for data and operations. 

Azure AI Services, such as text analytics for health, enable healthcare organizations to understand their largely unstructured and untapped medical data. Project Health Insights leans on AI to help healthcare professionals gain actionable insights and inferences from patient data. 

Azure Health Bot, an Intelligent App, empowers organizations to build and deploy legally compliant, powerful conversational healthcare experiences to patients. 

A successful AI integration like this begins with understanding. Knowing what generative AI can do and setting realistic expectations can help you identify where and how AI can best serve your operations. 

Next, you can foster a culture of AI literacy across teams, speeding up adoption by helping them understand how it can aid their work. The more familiar your team is with AI and its possibilities, the smoother the transition. Training and education programs play a pivotal role here. 

Finally, integration requires a systematic approach. It’s not about overhauling everything at once — it's about identifying areas where AI can add the most value, then gradually experimenting and scaling its use. Successful integration stories often feature a phased approach, starting small and growing steadily. 

As we move toward a future where generative AI is a staple in business operations, remember that every journey is unique. Although there’s no one-size-fits-all solution, a strategic approach and a keen understanding of your business needs will help you navigate this new landscape successfully.

Conclusion

Generative AI offers plenty of opportunities for businesses that embrace it. It’s fueling the next generation of intelligent applications, transforming everything from product development to customer engagement to marketing. This technology is the key to unlocking unprecedented growth and innovation. 

While the journey may have challenges, the potential rewards make generative AI worth exploring. As generative AI advances, Azure AI’s broad range of tools and services will help you navigate this fascinating field.

Exercise

  • Watch Episode 02 of the serverless edition Learn Live session to learn how to build, test and deploy an end-to-end intelligent app solution.
  • Complete the Cloud Skills Challenge to build on your AI skills.
  • Register for Ask the Expert: Azure Functions on September 26 for a live Q&A with the Product Team on how to build AI apps using serverless computing.
+ + \ No newline at end of file diff --git a/30daysofIA/tags/hack-together/page/6/index.html b/30daysofIA/tags/hack-together/page/6/index.html index 1631088056..fe5f6162f3 100644 --- a/30daysofIA/tags/hack-together/page/6/index.html +++ b/30daysofIA/tags/hack-together/page/6/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "hack-together" | Build Intelligent Apps On Azure +11 posts tagged with "hack-together" | Build Intelligent Apps On Azure @@ -14,13 +14,13 @@ - - + +
-

9 posts tagged with "hack-together"

View All Tags

· 8 min read
It's 30DaysOfIA

Let’s start with understanding the power of intelligent applications.

What We'll Cover:

  • What are intelligent applications?
  • Categories of Intelligent Apps
  • Breaking down the role of AI in intelligent apps

A decorative header image for the blog

Demystifying Intelligent Applications: Leveraging AI in App Development

Until recent years, when you heard the term “artificial intelligence” (AI), you might have pictured autonomous robots from sci-fi films. Now, perhaps your mind jumps to big data and machine learning (ML). Maybe you’re even thinking about the latest ChatGPT plugins and transformer architectures. Today, AI is more than these imaginations: it’s a reality increasingly woven into our day-to-day routines through intelligent applications.

Intelligent apps combine cloud-scale compute, data, and predictive or generative AI. These applications help you automate tasks and make data-driven decisions, aiding in leveraging business intelligence to gain meaningful, practical outcomes. 

For developers, the AI landscape isn’t an abstract complexity. It’s a real, tangible opportunity for evolving traditional applications, making them “intelligent,” and delivering more value to users. And with tools like Azure Kubernetes Service, Azure Cosmos DB, and Azure AI Services, stepping into the world of intelligent apps isn’t a steep climb but a steady trek across a manageable learning curve.

The Three Categories of Intelligent Applications

Before we start building, it’s critical to understand what makes an app “intelligent.” It’s not a universal definition so much as an identifier for apps that fall into one or more of the three primary categories: 

  • Outcome-based — This designation represents the broadest category of intelligent apps, whose principal value for users is the intelligent outcomes they provide. These apps process data to provide insightful results that enhance decision-making or help with tasks like trend prediction, resource management, and customer relationship management.

  • Functionality-based — This type of intelligent app takes AI integration a degree further, integrating AI as a function of the app to create or process its outputs, enhancing the app’s overall interactivity and usefulness.

  • Feature-based — This is the narrowest category, in which the app’s AI/ML component is one of its core features and primary selling points

Let’s dive more deeply into each category.

Outcome-Based Apps: Intelligent Outcomes Drive Success

Outcome-based applications are all about the results provided to the user. They function beyond merely processing data to offer insightful outcomes that drive success.

Think of a personal fitness tracker app that uses AI to analyze the user’s activity and provides actionable recommendations for achieving a goal. This type of app not only tracks your steps, sleep patterns, and heart rate but uses that data to provide you with personalized health advice. It helps you make informed lifestyle choices — the “intelligent” outcome.

Another example is a social media marketing app that uses AI for time-saving data analysis, task automation, and trend prediction. AI-based sentiment analysis can gauge customer perception while natural language processing (NLP) accurately interprets customer inquiries. Task automation can schedule posts and automate responses to common customer queries, and trend forecasting helps model and predict customer behavior to further personalize targeted promotional efforts. Building this type of intelligence into your apps provides cost-effective, actionable insights that guide sales and marketing strategies.

Functionality-Based Apps: AI-Driven Features Enhance User Experiences

As the name suggests, functionality-based applications integrate AI into how the app functions, but the end user drives the action and outcome. In this category of apps, AI takes a more active role in the user’s interaction by allowing them to use AI-created or processed output. These apps leverage AI technologies like Azure AI Services for NLP, image recognition, and pattern recognition to improve user experience.

Consider a music app that generates personalized playlists based on your listening habits or your smartphone’s camera app that uses AI to recognize faces and optimize photo settings. Even more advanced are the language translation apps that translate text from one language to another and can even understand and maintain the context and subtleties of the conversation. They use NLP to accomplish these feats.

Similarly, consider the latest e-commerce apps, whose AI-driven functionalities enable customers to try on clothes virtually, significantly enhancing the online shopping experience.

Feature-Based Apps: Integrating Advanced AI/ML Components

Finally, there are feature-based intelligent apps. These apps incorporate advanced AI/ML components like neural networks, internal large language models (LLMs), and sophisticated algorithms as core features for the user. In this type of application, AI is the primary feature for users to engage with and the primary selling point for the app.

Some examples of feature-based applications are chatbots and virtual agents built using Azure Bot Service. These robo-representatives use AI to provide more natural and human-like conversation, balancing superior efficiency with a friendlier, more personalized user experience. The new Bing is a prime example of this implementation, integrating AI to give users a knowledgeable copilot for web searches.

Similarly, OpenAI’s ChatGPT allows users access to its state-of-the-art language models available as Microsoft’s Azure OpenAI Service. It leverages the power of the highly sophisticated GPT-3 LLM to perform tasks like drafting emails, writing code, answering general knowledge questions, translating between languages, and tutoring in numerous subjects.

These apps don’t just use AI to provide outputs or improve user experiences: They push the boundaries of what’s possible in app development by integrating AI/ML components.

info

Register for the live Q&A Ask The Expert session on September 26 with the Azure Functions product engineering team.

Breaking Down the Role of AI in Intelligent Applications

Intelligent apps reflect the merging of traditional software design with the dynamic capabilities of AI. Let’s explore how AI integrates and amplifies these applications.

At its core, AI augments applications with the capacity to self-learn and self-improve. It allows you to create applications that predict, react, and adapt to a constantly changing environment. In the power grid management system for a major city, an AI-infused application can forecast power usage trends, react to anomalies, and adapt to sudden demand surges, ensuring optimal efficiency and preventing blackouts.

But AI’s role doesn’t end at prediction or decision-making: It also automates tasks. In enterprise solutions, it has moved beyond simple automation to encompass intelligent process automation (IPA). In this arena, AI algorithms understand, learn, and then automate entire digital workflows. For developers, this is a fertile ground for creating applications that can dramatically improve the operational efficiency of businesses.

Even more, AI substantially aids in development tasks. For example, there’s GitHub Copilot, an AI-powered code assistant that offers suggestions based not only on syntax but on development best practices and the intent of the code. AI has evolved from a tool you use to build apps to a partner that helps in creating them. 

Using AI to build intelligent apps isn’t about throwing out what’s worked before. It’s about enhancing the components that have the most impact and achieve the best results. Incorporating AI in apps elevates their predictive abilities, streamlines complex tasks, and even aids in the development and creation process. When you build intelligent apps, you don’t just address current problems. You anticipate future challenges and craft solutions that adapt over time.

Summary

As developers the power of AI is in our hands, and it’s our responsibility to harness its full potential. Each category tells the story of AI's transformative potential, from applications that deliver intelligent outcomes to those with AI capabilities to enrich the user experience and those pushing boundaries with advanced AI/ML technology.

Accessible and robust platforms like Azure AI can simplify the process of developing Intelligent Applications. Whether you’re refining an existing app or building a new one, just entering the world of AI, or already familiar with the landscape, take a moment to appreciate the potential that AI integration offers. You’re embracing this exciting frontier and shaping the future of app development. 

Exercise

  • Complete the Cloud Skills Challenge to build on your apps and AI skills.
  • Watch Episode 01 of the Serverless Edition Learn Live session to learn how to build an end-to-end intelligent app solution.
- - +
Skip to main content

11 posts tagged with "hack-together"

View All Tags

· 9 min read
It's 30DaysOfIA

In the part of our series on intelligent apps, we’ll explore how AI is transforming application development, from design and architecture to building.

What We'll Cover:

  • Revolutionizing application development using AI
  • Infusing AI in application design architecture
  • AI Assisted Pair Programming
  • Future of App Development

A decorative image of application development with AI

Reimagining Application Development With AI: A New Paradigm

Artificial intelligence (AI) is impacting every industry. AI-powered development tools offer new functionalities and assist software developers throughout the development process, revolutionizing app development. From initial design and architecture to coding and debugging, AI reshapes how we approach application conception, creation, and iteration.

In part one of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” we explored the new breed of intelligent apps and how they use AI. In this article, we’ll see how AI technologies enhance developer toolkits and shape the future of app development, particularly through AI-enabled coding assistance and debugging.

The Dawn of Intelligent Apps: Revolutionizing Application Development

AI is more than just another addition to the development toolbox—it represents a revolution bringing unprecedented efficiency, innovation, and user engagement to our applications. 

What sets intelligent apps apart is how they can adapt and learn, helping us deliver unique, real-time user experiences.  

One example is an AI-driven health application that can track biometric data, predict potential health issues, provide personalized health advice, and even guide emergency services during a medical crisis. An AI-enabled language learning app that adapts to our learning speeds, recognizes our problem areas, and customizes its content to help us improve our language proficiencies is another example.  

Intelligent apps like these don’t just use data for recommendations—they can make critical decisions and provide sophisticated services that were previously impossible. 

For developers, intelligent apps mean we’re no longer just coding—we’re teaching applications how to think, adapt, and make decisions. AI is reshaping every step of the development journey, helping us to build more intuitive and user-focused products. 

Next, we’ll examine the impact of AI on application design and architecture and how AI-assisted “pair programming” changes software development.

info

Register for the FREE webinar on Intelligent Apps with Microsoft and Forrester on September 26 to explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries.

There will be a showcase of real-world use cases that demonstrate how AI can be seamlessly integrated into cloud-native environments driving tangible business value.

AI in Design and Architecture: Fueling Creativity and Efficiency

AI technologies help developers automate and streamline their processes and provide new ways to think about design.  

In traditional application development, the design and architecture phase can take significant time, effort, and expertise. For instance, adopting a monolithic architecture or a microservices-based design can significantly increase coding, testing, and debugging complexity. The app may even behave differently when hosted locally than in a deployed environment. It may take hours to track down an issue manually—time developers could spend elsewhere. 

Conversely, we can generate numerous design iterations in seconds with AI, each optimized to fulfill specific user needs and business goals. AI tools can quickly model various scenarios' impacts and software design patterns in the architectural design phase. It provides insights into performance and potential issues and suggests improvements. AI can also generate skeleton code, allowing developers to prototype different solutions or test specific components and technologies rapidly. By reviewing thousands of lines of code in seconds, AI allows us to focus on creating apps that serve users through feature development instead of searching for bugs.  

Integrating AI into the design and architecture phase can even extend our creative capabilities. By leveraging the power of generative AI—like large language models (LLMs)—and their broad knowledge and context going beyond software, we can better brainstorm apps and feature ideas. We can go beyond code and software architecture and get ideas about user experience, logo and branding, or marketing concepts. And as AI tools deliver insights and ideas we would have never considered; it encourages more innovation.

info

Watch Episode 01 of the Learn Live session to learn how to build an end-to-end intelligent app solution architecture.

The Power of AI-Assisted Pair Programming in Building Applications

AI-assisted pair programming is a collaborative coding approach where an AI system—like GitHub Copilot or TestPilot—assists developers during coding. It’s an increasingly common approach that significantly impacts developer productivity. In fact, GitHub Copilot is now behind an average of 46 percent of developers’ code and users are seeing 55 percent faster task completion on average. 

For new software developers, or those interested in learning new skills, AI-assisted pair programming are training wheels for coding. With the benefits of code snippet suggestions, developers can avoid struggling with beginner pitfalls like language syntax. Tools like ChatGPT can act as a personal, on-demand tutor—answering questions, generating code samples, and explaining complex code syntax and logic. These tools dramatically speed the learning process and help developers gain confidence in their coding abilities. 

Building applications with AI tools hastens development and provides more robust code. For example, GitHub Copilot helps us write new code accurately. It also quickly makes sense of existing code, suggests whole new lines or blocks of code, and even detects errors and proposes fixes. It’s like having a second coder working alongside us—one who never tires and is trained on billions of lines of code. 

However, it’s also important to acknowledge the limitations of AI. While generative AI-assistance technologies like GitHub Copilot are impressive, they’re still imperfect. They can sometimes produce erroneous, non-functional, or vulnerable code. They can also lack nuanced, domain-specific knowledge.  

So, although these tools are helpful, we can’t rely on them fully—these are programming aids rather than definitive solutions. AI-assisted pair programming is about augmenting the human developer’s skills, not replacing them.

Debugging and Improving Code With AI: Enhancing Application Quality

Identifying and correcting code errors is often time-consuming and challenging for developers. AI streamlines the debugging process by automatically detecting anomalies, suggesting fixes, and learning from previous bugs to prevent them from recurring. 

We can use generative AI to create unit tests from code snippets, from simple parameter checking and input validation to complex mocks of database and network services. Automated AI testing improves code coverage and robustness. It saves significant time compared to manually crafting unit tests. 

AI also revolutionizes code reviews by scanning and highlighting potential issues—like code patterns leading to a bug or a coding standards violation—and suggesting ways to simplify or refactor the code for readability and performance. This reduces human error and makes the code review process more efficient. Its benefits extend to small teams, where the availability of developers is crucial to deadlines, and large teams, where effective collaboration depends on readability and code consistency. AI further saves time by triaging issues so developers can prioritize the most mission-critical ones. 

Aside from improving code quality, AI is critical to understanding system behavior. Intelligent AI-assisted software development tools can monitor and audit system behavior, giving developers a better insight into how their code performs in different scenarios. AI can track patterns, identify inefficiencies, and provide alerts when they detect unusual behavior—enabling developers to enhance their code further. 

By predicting and identifying potential vulnerabilities, intelligent AI-assisted coding apps like Microsoft Security Copilot are pivotal in ensuring application functionality, reliability, and security. These AI tools are continually trained and upgraded with large codebases and user feedback that refines their responses. This ability to learn from past mistakes makes AI an incredibly powerful tool that will continue to expand its capabilities in the future.

info

Complete the AI Cloud Skills Challenge to build on you AI application development skills with paired programming using generative AI and GitHub Copilot.

AI and the Future of Application Development

The rapid advancements in AI and machine learning technologies point to an exciting future for application development. 

In “The Future of Applications,” software developer Mike Cann illustrates how LLMs might revolutionize application development. Cann that suggests fully-autonomous coding—AI-generated applications created entirely from prompts—may supersede traditional hand-crafted applications. Dynamically generated user interfaces could offer personalized and customizable experiences while we can design APIs for machine consumption and efficiency rather than human readability. Furthermore, we can store data in less structured, more adaptable, and flexible forms. 

AI is already making application development more accessible. With AI handling the complex, technical aspects of development, less-technical individuals can take a greater role in building applications. Consequently, AI can potentially democratize software engineering. 

Intelligent apps will continue to evolve, becoming more efficient, adaptable, and capable of learning from user interactions. Similarly, AI will inevitably become integral in every stage of app development.

Summary

AI has already created a new development paradigm—one in which machine intelligence and human creativity come together to produce innovative, efficient, and robust applications. AI already assists us at every stage of app development, from design and architecture to coding and debugging. 

Developers who embrace AI tools and integrate them into their processes can enhance their productivity, improve the quality of their applications, and expand the possibilities of their application development.  

The future of development is automated and AI-driven, and it’s here today. Getting started is easy—try an AI-powered development tool like GitHub Copilot to see how AI-assisted pair programming can enhance your productivity and code quality.

Exercise

+ + \ No newline at end of file diff --git a/30daysofIA/tags/hack-together/page/7/index.html b/30daysofIA/tags/hack-together/page/7/index.html index b8fcc9ef2b..a4cf0cee61 100644 --- a/30daysofIA/tags/hack-together/page/7/index.html +++ b/30daysofIA/tags/hack-together/page/7/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "hack-together" | Build Intelligent Apps On Azure +11 posts tagged with "hack-together" | Build Intelligent Apps On Azure @@ -14,14 +14,13 @@ - - + +
-

9 posts tagged with "hack-together"

View All Tags

· 4 min read
It's 30DaysOfIA

This Fall focus on building intelligent apps using AI and cloud-native technologies. #FallForIntelligentApps brings to you a learning journey to build your skills on creating differentiated experiences while modernizing your applications. It’s time to learn it all.

What We'll Cover

image

Get Ready To #FallForIntelligentApps starting September 18!

Today, we kick off the Fall season with content and activities to skill you up on all things Intelligent Apps or AI Apps on Azure with content, events, and community interactions! Read on to learn about what is coming!

Explore Our Initiatives

We have a number of initiatives planned for the month to help you learn and skill up on relevant technologies. Click on the links to visit the relevant pages for each

image

Register for the events!

What are 4 things you can do today, to jumpstart your learning journey?

#30Days Of Intelligent Apps

#30DaysOfIA is a series of daily blog posts grouped into themed weeks - taking you from core concepts to end-to-end solution examples in 30 days. Each article will provide conceptual lessons paired with exercises and resources to help you reinforce learnings and take next steps.

This series takes you through learning journey in six stages, each building on the previous week to help you skill up in a beginner-friendly way:

We will start with defining intelligent apps and then expand on how to build with cloud-native technologies like Azure Kubernetes Service, Azure Container Apps and Azure Functions, and take you through end-to-end scenarios for real world application development. Before we dive deep on intelligent apps, here is a high-level overview of the Intelligent Apps landscape on Azure:

image

Containers on Azure services offer you a wide range of capabilities, from simplicity to control to suit your different needs.

image -image

To start with the basics for developing Kubernetes applications, explore #30DaysOfCloudNative.

Cloud-native development when paired with serverless computing enhances your solution architecture for building cost optimized, resilient applications.

image

To start with the basics for serverless computing, explore #30DaysOfServerless.

Let’s Get Started

Now you know everything! We hope you are as excited as we are to dive into a full month of active learning and doing! Don't forget to subscribe for updates in your favorite feed reader! And look out for our first Intelligent Apps post Monday!

- - +
Skip to main content

11 posts tagged with "hack-together"

View All Tags

· 10 min read
It's 30DaysOfIA

Explore real-world examples of how Intelligent Apps revolutionize logistics, retail, finance, manufacturing, tech, and medicine.

What We'll Cover:

  • Real-world businesses revolutionizing operations with Intelligent Apps
  • High level architecture for industry scenarios
  • Implementation architecture overview across industries

A decorative header image for the blog

Real-world Success Stories

According to IBM’s 2022 Global AI Adoption Index, 35% of surveyed companies already use AI in their business, and 42% are exploring the idea. Organizations lagging behind the AI curve will quickly fall behind early adopters, who are already enjoying its benefits and boosting their investment in this technology.

In the first part of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” we explored how organizations can use AI-powered apps, or intelligent apps, to transform their operations, decision-making, and customer experiences, leaping ahead of the competition.

Intelligent apps significantly improve operational efficiency and customer engagement across industries like logistics, retail, finance, manufacturing, tech, and healthcare. In this article, we’ll spotlight real-world success stories of businesses revolutionizing operations with intelligent apps using Microsoft Azure’s app, data, and AI services.

info

Register for the Learn Live on September 21 for a live guided experience with SMEs on how to build end-to-end solution architecture for real world implementation of Intelligent Apps.

Streamlining Operations: Intelligent Apps in the Airline Industry

Aerospace pioneer Airbus leveraged intelligent apps to streamline its operations and innovate its services with two ground-breaking modernizations that use Azure apps, data, and AI services to reimagine pilot training and predict aircraft maintenance issues. 

Aviation technology’s rapid evolution compelled Airbus to reconceive their approach to training pilots. The aircraft manufacturer created a pilot training chatbot by combining Azure AI Services with Speech to Text and Text to Speech. The bot helps trainee pilots navigate over 6,000 pages of technical information. Pilots can ask questions verbally for quick answers, aiding their understanding of dense information and speeding up training. And when Airbus needed to accommodate its military consumers, deploying its Kubernetes cluster to a disconnected, “air-gapped” environment met the strict security requirements of military aircraft training.

A chatbot could look like the following diagram as Azure services work together to create a conversational experience. Its components could include security and governance, bot logic and user experience (UX), bot cognition and intelligence, data extract, transfer, and load (ETL) operations, quality assurance and enhancements, logging, and monitoring and reporting.

A diagram of a conversational experience enabled by Azure services

Airbus also used AI to improve its aircraft maintenance and safety. They deployed AI Anomaly Detector to gather and analyze telemetry data from multiple flights, enabling more profound insights into wear and tear on military aircraft operating in harsh conditions. This AI application helps Airbus better understand each airplane’s health. Airbus can predict and fix potential problems before they occur, improving the aircraft’s safety and operational readiness.

AI and Azure are changing how organizations like Airbus operate, even in strict, highly regulated markets with stringent security requirements. And by streamlining pilot training and staying ahead of aircraft maintenance, Airbus helps its military and commercial clients reliably deliver their goods across the globe, maintaining their critical supply chains.

Boosting Customer Service: Intelligent Apps in the Retail Industry

The retail industry has been among the first to adopt and benefit from intelligent apps. For example, the leading American used car retailer, CarMax, uses intelligent apps to transform the car shopping experience.

Before embracing AI, CarMax faced a significant challenge: Manually creating text summaries was time-consuming and labor-intensive. CarMax decided to leverage Azure OpenAI Service to automatically generate car research pages, offering customers valuable insights while enhancing the website’s search engine rankings.

This AI-driven approach has significantly streamlined the process, achieving in a few months what would have taken years manually. Also, the AI system condenses thousands of customer reviews into a few readable sentences, offering potential buyers a quick overview of others’ experiences.

CarMax has improved their customer experience while boosting operational efficiency through AI adoption. CarMax’s editorial staff focuses on creating strategic, longer-form content with the time saved, enhancing productivity, and driving more website traffic.

CarMax’s success story serves as a testament to the potential of intelligent apps in the retail industry.

Enhancing Cybersecurity: Intelligent Apps in Finance

Intelligent apps also impact the highly regulated financial services sector. Swift, a leading infrastructure provider for financial messaging services, uses intelligent apps to enhance decision-making and combat financial crime.

The massive growth of cross-border transactions and instant payment networks led to a surge in financial fraud that can cost the industry hundreds of billions of dollars every year. So, Swift and Microsoft partnered to build an anomaly detection model using federated learning techniques with Azure Machine Learning and Azure confidential computing.

Microsoft Purview helped govern data, as the federated learning method partially trained each model in a silo before aggregating them. This approach enabled Swift and Microsoft to develop a highly accurate model without copying or moving data from Swift members’ secure locations, ensuring the highest level of security and privacy.

Financial organizations can now deploy this trained ML model to run a real-time fraud detection system, lowering the cost of financial fraud.

Organizations can deploy a fraud detection system like the following example on Microsoft Azure. The system ingests data from streams or storage, runs analytics, and uses ML to detect fraud and store results. It then alerts users and other systems to the security issue.

A diagram of an example fraud detection system

Swift provides the innovative model to its banking partners, who can share data to further train the model. Azure confidential computing securely runs new models on current data to accumulate insights, generating an ongoing learning cycle. This iterative approach strengthens the global detection of fraudulent financial transactions through an increasingly faster and more accurate model. 

The collaboration between Swift and Microsoft demonstrates how combining various Azure services creates Intelligent Apps to enhance decision-making in finance. These apps solve real problems at an unprecedented global scale. 

Enhancing Efficiency: Intelligent Apps in Manufacturing

3M, known for Post-it Notes and other innovative products, wielded the power of Intelligent Apps to standardize, automate, and accelerate its sales forecasting. The company needed a unified, automated approach to replace its multiple manual methods.

It adopted Azure Machine Learning and other Azure AI Services to develop, train, and implement 1,500 custom models for each region and division, replacing manual methods. This solution delivers timely finance, sales, and marketing insights while freeing teams from time-consuming manual data manipulation tasks.

3M used Azure Machine Learning, automated machine learning (AutoML), and the Many Models Solution Accelerator to train and score numerous machine learning models in parallel. This approach significantly shortened the company’s development cycle time.

Then, 3M integrated Microsoft Power BI into their solution to compile and visualize results more effectively. Executives now have an unbiased and unfiltered view of data for better decision-making. With its improved ability to forecast sales, 3M can focus on manufacturing the products it needs efficiently.

3M’s success demonstrates how combining various Azure services can enhance efficiency in the manufacturing industry. These apps improve operational processes while enabling employees to focus on higher-level tasks. Strategic decisions are more targeted, and business conversations are more meaningful.

Driving Innovation: Intelligent Apps in the Technology Industry

As expected, the tech industry is at the forefront of leveraging Intelligent Apps to drive innovation. Elastic saw an opportunity to use intelligent apps to optimize its search services and meet customer needs more effectively. The company built a managed, secure, and scalable solution for its customers, Elastic Cloud, by leveraging Microsoft Azure.

Elastic Cloud uses Azure Machine Learning and other Azure AI Services to automate many basic management tasks, such as backing up data, upgrading, and scaling deployments. This approach enables Elastic’s customers to focus less on managing the software and more on serving their own clients. Customers can quickly deploy and manage Elastic Cloud on Kubernetes (ECK) using Azure Kubernetes Service.

As a result, Elastic’s cloud business is growing twice as fast as its on-premises solutions. Enterprise search users have reported significant improvements in system availability and employee satisfaction.

Additionally, Elastic incorporated Azure OpenAI Service into its Elasticsearch Relevance Engine (ESRE), providing more relevant answers to search queries. ESRE enables text, vector, and hybrid search, offering a more effective and accurate response to queries.

Elastic’s success story demonstrates how combining various Azure services can create intelligent Apps to drive innovation in the tech industry. These apps help improve operational processes and deliver better results quickly.

Revolutionizing Healthcare: Intelligent Apps in Medicine

Intelligent apps also drive transformative changes in the healthcare sector. Cambridgeshire and Peterborough NHS Foundation Trust, a healthcare provider in the United Kingdom, gradually shifted their on-premises systems to the cloud using Azure, improving flexibility, and lowering costs. They needed a solution for their electronic patient record (EPR) system that integrates 23 years of data across multiple systems and formats.

The Trust used Microsoft Azure and Azure Cognitive Search, including semantic search for understanding intent and contexts and cognitive capabilities for extracting insights from data, to standardize and automate data management. This approach made legacy, archive, and live data more discoverable, saving clinicians’ time. The Trust’s solution searches through millions of clinical documents in seconds, even recognizing handwritten notes without needing a separate optical character recognition (OCR) solution to extract text from images (Azure Cognitive Search automatically does it). Clinicians can now uncover previously inaccessible insights, enabling them to offer more personalized care.

The Trust also used Integration Services tools such as Azure Logic Apps to create workflows without writing code and Event Grid to connect Azure and third-party services through a publisher-subscriber model. They switched on a live feed from their new EPR system to Azure to feed real-time information into other applications. Researchers can now access anonymized data to fuel medical innovations beyond the NHS.

The healthcare provider also invested heavily in Power BI’s data visualization and analytical functions to sift through massive amounts of information. This level of data accessibility was previously impossible with their on-premises system. The new insights have significantly improved clinicians’ efficiency for better patient care.

Data accessibility and security are critical in the healthcare industry. Intelligent Apps empower organizations like the Trust to revolutionize medical care. AI capabilities helped the Trust improve its operational efficiency and user experiences, setting a new standard for healthcare in the digital age.

Summary

AI is driving transformation and real-world successes across industries. Intelligent apps are reshaping our world by streamlining operations in manufacturing and logistics, enhancing customer experience in the retail sector, improving decision-making in finance, driving innovation in tech, and revolutionizing healthcare.

As organizations adopt AI, they’re innovating in ways that were once too difficult, expensive, or outright impossible. Companies that wait to embrace intelligent apps risk the chance of falling behind. By embracing intelligent apps and leveraging the capabilities of Azure, you can harness the transformative potential of AI to remain on the cutting edge of technology.

Exercise

+ + \ No newline at end of file diff --git a/30daysofIA/tags/hack-together/page/8/index.html b/30daysofIA/tags/hack-together/page/8/index.html index afc18bafd4..bbf7e9b8f3 100644 --- a/30daysofIA/tags/hack-together/page/8/index.html +++ b/30daysofIA/tags/hack-together/page/8/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "hack-together" | Build Intelligent Apps On Azure +11 posts tagged with "hack-together" | Build Intelligent Apps On Azure @@ -14,14 +14,13 @@ - - + +
-

9 posts tagged with "hack-together"

View All Tags

· 4 min read
It's 30DaysOfIA

Continue The Learning Journey through Fall For Intelligent Apps! 🍂

What We'll Cover

Thank you! ♥️

image

It's hard to believe that JavaScript on Azure hack-together is ending! It seems like just yesterday that we launched this initiative, and yet here we are, 15 days later, with an incredible amount of learning and growth behind us. So... it's time for a wrap!

From the bottom of our hearts, we want to thank each and every one of you for your participation, engagement, and enthusiasm. It's been truly inspiring to see the passion and dedication from this strong community, and we're honored to be a part of it. ✨

Recap of The JavaScript on Azure Global Hack-Together

As we wrap up this exciting event, we wanted to take a moment to reflect on all that we've accomplished together. Over the last 15 days, we've covered a lot of ground, from the basics of contributing to Open source to the exploration of the Contoso Real Estate project from its Frontend to its Backend and future AI implementation.

Now that the hack-together is ending, we want to make sure that you have all the resources you need to continue honing your skills in the future. Whether you're looking to make your fist contribution to open source, become an open source maintainers, collaborate with others, or simply keep learning, there are plenty of resources out there to help you achieve your goals. So, let's dive in and explore all the ways you can continue to grow your JavaScript skills on Azure!

JSonAzure Hack-together Roadmap 📍:

hack-together-roadmap (2)

Recap on past Livestreams🌟:

Day 1️⃣: Opening Keynote (Hack-together Launch): Introduction to the Contoso Real Estate Open-source project!, managing complex and complex enterprise architecture, new announcements for JavaScript developers on Azure

Day 2️⃣: GitHub Copilot & Codespaces: Introduction to your AI pair programmer (GitHub Copilot) and your virtual developer environment on the cloud (GitHub Codespaces)

Day 6️⃣: Build your Frontend using Static Web Apps as part of a complex, modern composable frontends (or micro-frontends) and cloud-native applications.

Day 9️⃣: Build a Serverless Backend using Azure Functions

Day 1️⃣3️⃣: Easily connect to an Azure Cosmos DB, exploring its benefits and how to get started

Day 1️⃣5️⃣: Being in the AI Era, we dive into the Azure OpenAI Service and how you can start to build intelligent JavaScript applications

📖 Self-Learning Resources

  1. JavaScript on Azure Global Hack Together Module collection
  2. Lets #HackTogether: Javascript On Azure Keynote
  3. Step by Step Guide: Migrating v3 to v4 programming model for Azure Functions for Node.Js Application

Continue your journey with #FallForIntelligentApps

Join us this Fall on a learning journey to explore building intelligent apps. Combine the power of AI, cloud-native app development, and cloud-scale data to build highly differentiated digital experiences. Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure. Engage in a self-paced learning adventure by following along the #30Days of Intelligent Apps series, completing the Intelligent Apps Skills Challenge, joining the Product Group for a live Ask The Expert series or building an end to end solution architecture with a live guided experience through the Learn Live series. Discover more here.

Hands-on practice: Make your first contribution to open source!

Join our GitHUb Discussion Forum to connect with developers from every part of world, see contributions from other, find collaborators and make your first contribution to a real-world project! -Don't forget to give the repo a star ⭐

Resources

All resources are accessible on our landing page

- - +
Skip to main content

11 posts tagged with "hack-together"

View All Tags

· 8 min read
It's 30DaysOfIA

Let’s start with understanding the power of intelligent applications.

What We'll Cover:

  • What are intelligent applications?
  • Categories of Intelligent Apps
  • Breaking down the role of AI in intelligent apps

A decorative header image for the blog

Demystifying Intelligent Applications: Leveraging AI in App Development

Until recent years, when you heard the term “artificial intelligence” (AI), you might have pictured autonomous robots from sci-fi films. Now, perhaps your mind jumps to big data and machine learning (ML). Maybe you’re even thinking about the latest ChatGPT plugins and transformer architectures. Today, AI is more than these imaginations: it’s a reality increasingly woven into our day-to-day routines through intelligent applications.

Intelligent apps combine cloud-scale compute, data, and predictive or generative AI. These applications help you automate tasks and make data-driven decisions, aiding in leveraging business intelligence to gain meaningful, practical outcomes. 

For developers, the AI landscape isn’t an abstract complexity. It’s a real, tangible opportunity for evolving traditional applications, making them “intelligent,” and delivering more value to users. And with tools like Azure Kubernetes Service, Azure Cosmos DB, and Azure AI Services, stepping into the world of intelligent apps isn’t a steep climb but a steady trek across a manageable learning curve.

The Three Categories of Intelligent Applications

Before we start building, it’s critical to understand what makes an app “intelligent.” It’s not a universal definition so much as an identifier for apps that fall into one or more of the three primary categories: 

  • Outcome-based — This designation represents the broadest category of intelligent apps, whose principal value for users is the intelligent outcomes they provide. These apps process data to provide insightful results that enhance decision-making or help with tasks like trend prediction, resource management, and customer relationship management.

  • Functionality-based — This type of intelligent app takes AI integration a degree further, integrating AI as a function of the app to create or process its outputs, enhancing the app’s overall interactivity and usefulness.

  • Feature-based — This is the narrowest category, in which the app’s AI/ML component is one of its core features and primary selling points

Let’s dive more deeply into each category.

Outcome-Based Apps: Intelligent Outcomes Drive Success

Outcome-based applications are all about the results provided to the user. They function beyond merely processing data to offer insightful outcomes that drive success.

Think of a personal fitness tracker app that uses AI to analyze the user’s activity and provides actionable recommendations for achieving a goal. This type of app not only tracks your steps, sleep patterns, and heart rate but uses that data to provide you with personalized health advice. It helps you make informed lifestyle choices — the “intelligent” outcome.

Another example is a social media marketing app that uses AI for time-saving data analysis, task automation, and trend prediction. AI-based sentiment analysis can gauge customer perception while natural language processing (NLP) accurately interprets customer inquiries. Task automation can schedule posts and automate responses to common customer queries, and trend forecasting helps model and predict customer behavior to further personalize targeted promotional efforts. Building this type of intelligence into your apps provides cost-effective, actionable insights that guide sales and marketing strategies.

Functionality-Based Apps: AI-Driven Features Enhance User Experiences

As the name suggests, functionality-based applications integrate AI into how the app functions, but the end user drives the action and outcome. In this category of apps, AI takes a more active role in the user’s interaction by allowing them to use AI-created or processed output. These apps leverage AI technologies like Azure AI Services for NLP, image recognition, and pattern recognition to improve user experience.

Consider a music app that generates personalized playlists based on your listening habits or your smartphone’s camera app that uses AI to recognize faces and optimize photo settings. Even more advanced are the language translation apps that translate text from one language to another and can even understand and maintain the context and subtleties of the conversation. They use NLP to accomplish these feats.

Similarly, consider the latest e-commerce apps, whose AI-driven functionalities enable customers to try on clothes virtually, significantly enhancing the online shopping experience.

Feature-Based Apps: Integrating Advanced AI/ML Components

Finally, there are feature-based intelligent apps. These apps incorporate advanced AI/ML components like neural networks, internal large language models (LLMs), and sophisticated algorithms as core features for the user. In this type of application, AI is the primary feature for users to engage with and the primary selling point for the app.

Some examples of feature-based applications are chatbots and virtual agents built using Azure Bot Service. These robo-representatives use AI to provide more natural and human-like conversation, balancing superior efficiency with a friendlier, more personalized user experience. The new Bing is a prime example of this implementation, integrating AI to give users a knowledgeable copilot for web searches.

Similarly, OpenAI’s ChatGPT allows users access to its state-of-the-art language models available as Microsoft’s Azure OpenAI Service. It leverages the power of the highly sophisticated GPT-3 LLM to perform tasks like drafting emails, writing code, answering general knowledge questions, translating between languages, and tutoring in numerous subjects.

These apps don’t just use AI to provide outputs or improve user experiences: They push the boundaries of what’s possible in app development by integrating AI/ML components.

info

Register for the live Q&A Ask The Expert session on September 26 with the Azure Functions product engineering team.

Breaking Down the Role of AI in Intelligent Applications

Intelligent apps reflect the merging of traditional software design with the dynamic capabilities of AI. Let’s explore how AI integrates and amplifies these applications.

At its core, AI augments applications with the capacity to self-learn and self-improve. It allows you to create applications that predict, react, and adapt to a constantly changing environment. In the power grid management system for a major city, an AI-infused application can forecast power usage trends, react to anomalies, and adapt to sudden demand surges, ensuring optimal efficiency and preventing blackouts.

But AI’s role doesn’t end at prediction or decision-making: It also automates tasks. In enterprise solutions, it has moved beyond simple automation to encompass intelligent process automation (IPA). In this arena, AI algorithms understand, learn, and then automate entire digital workflows. For developers, this is a fertile ground for creating applications that can dramatically improve the operational efficiency of businesses.

Even more, AI substantially aids in development tasks. For example, there’s GitHub Copilot, an AI-powered code assistant that offers suggestions based not only on syntax but on development best practices and the intent of the code. AI has evolved from a tool you use to build apps to a partner that helps in creating them. 

Using AI to build intelligent apps isn’t about throwing out what’s worked before. It’s about enhancing the components that have the most impact and achieve the best results. Incorporating AI in apps elevates their predictive abilities, streamlines complex tasks, and even aids in the development and creation process. When you build intelligent apps, you don’t just address current problems. You anticipate future challenges and craft solutions that adapt over time.

Summary

As developers the power of AI is in our hands, and it’s our responsibility to harness its full potential. Each category tells the story of AI's transformative potential, from applications that deliver intelligent outcomes to those with AI capabilities to enrich the user experience and those pushing boundaries with advanced AI/ML technology.

Accessible and robust platforms like Azure AI can simplify the process of developing Intelligent Applications. Whether you’re refining an existing app or building a new one, just entering the world of AI, or already familiar with the landscape, take a moment to appreciate the potential that AI integration offers. You’re embracing this exciting frontier and shaping the future of app development. 

Exercise

  • Complete the Cloud Skills Challenge to build on your apps and AI skills.
  • Watch Episode 01 of the Serverless Edition Learn Live session to learn how to build an end-to-end intelligent app solution.
+ + \ No newline at end of file diff --git a/30daysofIA/tags/hack-together/page/9/index.html b/30daysofIA/tags/hack-together/page/9/index.html index 31b22e95c2..97a9b4124c 100644 --- a/30daysofIA/tags/hack-together/page/9/index.html +++ b/30daysofIA/tags/hack-together/page/9/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "hack-together" | Build Intelligent Apps On Azure +11 posts tagged with "hack-together" | Build Intelligent Apps On Azure @@ -14,13 +14,14 @@ - - + +
-

9 posts tagged with "hack-together"

View All Tags

· 2 min read
It's 30DaysOfIA

September is almost here - and that can only mean one thing!! It's time to 🍂 Fall for something new and exciting and spend a few weeks skilling up on relevant tools, techologies and solutions!!

Last year, we focused on #ServerlessSeptember. This year, we're building on that theme with the addition of cloud-scale Data, cloud-native Technologies and cloud-based AI integrations to help you modernize and build intelligent apps for the enterprise!

Watch this space - and join us in September to learn more!

- - +
Skip to main content

11 posts tagged with "hack-together"

View All Tags

· 4 min read
It's 30DaysOfIA

This Fall focus on building intelligent apps using AI and cloud-native technologies. #FallForIntelligentApps brings to you a learning journey to build your skills on creating differentiated experiences while modernizing your applications. It’s time to learn it all.

What We'll Cover

image

Get Ready To #FallForIntelligentApps starting September 18!

Today, we kick off the Fall season with content and activities to skill you up on all things Intelligent Apps or AI Apps on Azure with content, events, and community interactions! Read on to learn about what is coming!

Explore Our Initiatives

We have a number of initiatives planned for the month to help you learn and skill up on relevant technologies. Click on the links to visit the relevant pages for each

image

Register for the events!

What are 4 things you can do today, to jumpstart your learning journey?

#30Days Of Intelligent Apps

#30DaysOfIA is a series of daily blog posts grouped into themed weeks - taking you from core concepts to end-to-end solution examples in 30 days. Each article will provide conceptual lessons paired with exercises and resources to help you reinforce learnings and take next steps.

This series takes you through learning journey in six stages, each building on the previous week to help you skill up in a beginner-friendly way:

We will start with defining intelligent apps and then expand on how to build with cloud-native technologies like Azure Kubernetes Service, Azure Container Apps and Azure Functions, and take you through end-to-end scenarios for real world application development. Before we dive deep on intelligent apps, here is a high-level overview of the Intelligent Apps landscape on Azure:

image

Containers on Azure services offer you a wide range of capabilities, from simplicity to control to suit your different needs.

image +image

To start with the basics for developing Kubernetes applications, explore #30DaysOfCloudNative.

Cloud-native development when paired with serverless computing enhances your solution architecture for building cost optimized, resilient applications.

image

To start with the basics for serverless computing, explore #30DaysOfServerless.

Let’s Get Started

Now you know everything! We hope you are as excited as we are to dive into a full month of active learning and doing! Don't forget to subscribe for updates in your favorite feed reader! And look out for our first Intelligent Apps post Monday!

+ + \ No newline at end of file diff --git a/30daysofIA/tags/index.html b/30daysofIA/tags/index.html index 9195681288..327fa3afaa 100644 --- a/30daysofIA/tags/index.html +++ b/30daysofIA/tags/index.html @@ -14,13 +14,13 @@ - - + + - - +
Skip to main content
+ + \ No newline at end of file diff --git a/30daysofIA/tags/learn-live/index.html b/30daysofIA/tags/learn-live/index.html index 3d0f77b15c..ee799224a4 100644 --- a/30daysofIA/tags/learn-live/index.html +++ b/30daysofIA/tags/learn-live/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "learn-live" | Build Intelligent Apps On Azure +11 posts tagged with "learn-live" | Build Intelligent Apps On Azure @@ -14,13 +14,13 @@ - - + +
-

9 posts tagged with "learn-live"

View All Tags

· 10 min read
It's 30DaysOfIA

Prepare your organization to adopt intelligent apps by taking a change management approach. Assess your readiness, upskill teams, create a culture of change and overcome resistance, and prepare for future evolution to pave the way for a smooth transition into an AI-centric company.

What We'll Cover:

  • Role of change management in modernizing to intelligent apps
  • Skills needed for building intelligent apps in your organization
  • How to build a culture of change to embrace AI
  • How to prepare for the future of intelligent apps and continuous evolution

image of modernizing AI solutions for intelligent apps

Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management

As we’ve discussed throughout this series, intelligent apps and generative AI are rewriting the rules of business and application development. The first article of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” explored how intelligent apps can enhance user experiences and drive businesses growth. These advancements are opening new opportunities and markets for companies ready to embrace them.

However, embracing this digital transformation is more complex than flipping a switch. It requires adjustments to the technical side, which we explored in the series’ fifth article, “Preparing the Path for Intelligent Apps: Transitioning from On-Premises/IaaS to Cloud Native Applications.” It also demands a shift in how organizations adapt to change and approach app design and development. Let’s explore that mindset transition with tips to help you make it happen.

The Role of Change Management in Transitioning to Intelligent Apps

Transitioning to intelligent apps isn’t merely a technological change. It’s a profound shift in how businesses perceive and operate. This move toward artificial intelligence (AI)-centric approach isn’t meant to replace humans with machines or radically overhaul all business operations.

Instead, intelligent apps should augment human capabilities, streamline processes, and foster an environment where innovation flourishes. They expand toolkits and techniques to include AI-assisted development tools like GitHub Copilot and AI-enabled workflows, helping us design and deliver AI-enhanced experiences.

Effective change management strategies are crucial in helping employees adapt to these new ways of working with intelligent apps, ensuring that the transition doesn’t disrupt productivity or morale.

Team and organization strategies should: 

  • Assess the impact of any proposed changes.  
  • Communicate the benefits and reasons behind the change to all affected team members.  
  • Provide the necessary training and support.  
  • Integrate the changes into the culture and daily operations. 

By incorporating this robust change management approach, you can identify and mitigate potential transition risks early. Moreover, this strategy ensures that employees are engaged throughout the process, fostering a sense of involvement and ownership. This, in turn, cultivates a culture suited for innovation through intelligent apps. 

These organizational changes focus on the following aspects: organizational readiness, skillset, culture of change and overcoming resistance, and adaptability. Let’s explore them below.

info

Register for the intelligent apps webinar on Driving Business Value by Modernizing with Cloud-Native & AI with Microsoft and Forrester on September 26. 

Explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries. 

Identifying Organizational Readiness for Intelligent Apps

The first step in preparing your organization for intelligent apps is to assess your current readiness for change. Analyze various elements of your organization, from infrastructure and workforce skills to strategic vision and overall culture.

Can your apps and systems integrate with AI platforms like Microsoft Azure AI? Is your workforce comfortable working alongside AI, or are they fearful and resistant? Does your strategic vision incorporate AI’s transformative potential, or does it fail to acknowledge AI as a critical driver of future growth?

These questions identify the challenges of integrating AI into an organization and determine the company’s readiness for intelligent apps.

Embracing intelligent apps involves more than being ready with your technological infrastructure or having the right technology or tools. You need flexible organizational structures and processes driving your business to accommodate new ideas and capabilities. Your teams must feel empowered to own and drive them. Much like Microsoft’s shift to AI-powered services—like AI-powered Bing and Edge and Microsoft 365 Copilot—embracing AI organization-wide requires a holistic vision and willingness to innovate, with teams capable of delivering on the plan.

After accessing these critical areas for organizational readiness, you can start planning a strategy for this transition to an AI-ready organization. Identify skill gaps and work to bridge them while nurturing an environment that appreciates and harnesses the potential of AI and intelligent apps.

Nurturing the Indispensable Skills for Intelligent Apps

Tasking your AI specialists with preparing your organization to use and build intelligent apps isn’t enough. Accomplishing this goal requires a cross-functional effort and a diverse skill set.

Developing intelligent apps involves data scientists managing and interpreting vast amounts of data, machine learning engineers building and training the models, and traditional developers integrating these models into usable applications. However, the impact of generative AI spans beyond creating apps. AI fluency across the entire organization—from sales and marketing to accounting and legal departments—offers substantial benefits, sparking new ideas and solutions from sometimes unexpected sources.

Teams' AI skills might roughly fall into three levels: 

  • Fundamental skills — Being familiar and efficient with using AI tools, for example, knowing how to write effective prompts for large language models (LLMs).
  • Visionary skills — Recognizing AI’s strengths and weaknesses, for example, its creativity and hallucinations, that is, unexpected output.
  • Expert skills — Managing data and training models and integrating AI technology into apps, such as incorporating the OpenAI application programming interface (API) into an Intelligent App.

Based on these levels, you can gauge your team's skill readiness to pinpoint the most significant gaps, whether in general AI knowledge across your organization or within technical development teams.

As intelligent apps become increasingly prevalent, upskilling your workforce is crucial. This transition opens a new realm of skills and capabilities for your team to master. You can offer machine learning, data analysis, and cloud computing training programs—such as Microsoft’s Azure AI Fundamentals certification—to empower your team to confidently navigate the intelligent apps landscape.

Nurturing these skills within your organization may require hiring new talent, reskilling current employees, and creating an environment that promotes learning and experimentation. Specifically, if your team understands tools like the Microsoft Azure AI platform, they could contribute significantly to developing and maintaining intelligent apps.

An upskilled team is more than a functional unit. It’s a think tank to drive innovation and improve your organization’s competitive standing in the digital marketplace.

Building a Culture of Change to Embrace AI

Even with the right people and skills in place, change remains an uphill battle for organizations committed to traditional processes. 

Transitioning to intelligent apps is as much about mindset as technology and skills. It requires fostering a culture open to change and innovation that understands AI’s transformative potential and embraces it to enhance rather than replace human capabilities. 

Leaders play a pivotal role in cultivating this mindset. As they model a positive attitude toward AI, emphasize its benefits, and address any concerns transparently, leaders significantly contribute to the organization’s readiness to adopt intelligent apps. 

The most critical factor in this transition is enabling an AI-friendly culture. It’s about encouraging curiosity, rewarding innovative thinking, and creating an environment where people thrive on learning and experimentation. 

Empowering teams to leverage practical AI tools and AI-driven data insights to aid decision-making helps motivate employees to use and explore AI’s potential, improve their skills, and contribute to the successful integration of intelligent apps. Such an environment is fertile ground to grow breakthrough ideas and innovative solutions. 

Overcoming Resistance to Change and Adopting Intelligent Apps

Despite AI’s potential, change isn’t easy. It takes effort and is especially challenging for organizations used to existing processes. This resistance to change is a significant roadblock when transitioning to intelligent apps. 

Overcoming this resistance involves:  

  • Understanding employees’ concerns. 
  • Challenging underlying assumptions about traditional systems and outdated processes that hinder change. 
  • Providing comprehensive training for workers to use AI effectively.
  • Demonstrating the benefits and opportunities of intelligent apps. 
  • Clear and continuous communication to dispel fears and misconceptions about AI.

These steps will foster greater acceptance and enthusiasm for intelligent apps in your teams. 

Preparing for the Future of Intelligent Apps and Continuous Evolution

Finally, it’s essential to understand that generative AI is just beginning to gain momentum. Adjusting to a world of intelligent apps isn’t a one-time change—it’s a continuous evolution. 

As AI technologies mature and evolve and more businesses realize their benefits, we can expect the landscape of intelligent apps to expand further. Organizations can cultivate a mindset of continuous learning, flexibility, and adaptation to prepare for this future. Staying ahead in this dynamic field requires organizations to keep up with the latest AI advancements, continually reassess their strategies, and pivot when necessary. 

The domain of intelligent apps is rapidly advancing. Organizations must prepare to navigate this dynamic landscape to stay ahead of the curve.

Summary

Preparing for AI and intelligent apps is a strategic decision. It requires thoughtful preparation and a culture that embraces change.

With the proper preparation and mindset, adopting AI can become a smoother, collaborative path to innovation for the whole organization. Integrating AI into your organization does not discard what has worked in the past. It simply enhances and augments those processes for a better future.

Consider the points above to assess your organization’s readiness and prepare to welcome intelligent apps.

In the first week of this series, you learned about intelligent apps, how organizations today find success through intelligent apps, what AI-assisted application development looks like, ways to leverage the power of generative AI, the technical changes needed to prepare for intelligent apps, and, in this article, the organizational changes required to harness the power of AI.

The future is intelligent. Use this series as a guideline to navigate challenges while preparing for and embracing AI's opportunities for an intelligent tomorrow.

Exercise

- - +
Skip to main content

11 posts tagged with "learn-live"

View All Tags

· 10 min read
It's 30DaysOfIA

Prepare your organization to adopt intelligent apps by taking a change management approach. Assess your readiness, upskill teams, create a culture of change and overcome resistance, and prepare for future evolution to pave the way for a smooth transition into an AI-centric company.

What We'll Cover:

  • Role of change management in modernizing to intelligent apps
  • Skills needed for building intelligent apps in your organization
  • How to build a culture of change to embrace AI
  • How to prepare for the future of intelligent apps and continuous evolution

image of modernizing AI solutions for intelligent apps

Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management

As we’ve discussed throughout this series, intelligent apps and generative AI are rewriting the rules of business and application development. The first article of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” explored how intelligent apps can enhance user experiences and drive businesses growth. These advancements are opening new opportunities and markets for companies ready to embrace them.

However, embracing this digital transformation is more complex than flipping a switch. It requires adjustments to the technical side, which we explored in the series’ fifth article, “Preparing the Path for Intelligent Apps: Transitioning from On-Premises/IaaS to Cloud Native Applications.” It also demands a shift in how organizations adapt to change and approach app design and development. Let’s explore that mindset transition with tips to help you make it happen.

The Role of Change Management in Transitioning to Intelligent Apps

Transitioning to intelligent apps isn’t merely a technological change. It’s a profound shift in how businesses perceive and operate. This move toward artificial intelligence (AI)-centric approach isn’t meant to replace humans with machines or radically overhaul all business operations.

Instead, intelligent apps should augment human capabilities, streamline processes, and foster an environment where innovation flourishes. They expand toolkits and techniques to include AI-assisted development tools like GitHub Copilot and AI-enabled workflows, helping us design and deliver AI-enhanced experiences.

Effective change management strategies are crucial in helping employees adapt to these new ways of working with intelligent apps, ensuring that the transition doesn’t disrupt productivity or morale.

Team and organization strategies should: 

  • Assess the impact of any proposed changes.  
  • Communicate the benefits and reasons behind the change to all affected team members.  
  • Provide the necessary training and support.  
  • Integrate the changes into the culture and daily operations. 

By incorporating this robust change management approach, you can identify and mitigate potential transition risks early. Moreover, this strategy ensures that employees are engaged throughout the process, fostering a sense of involvement and ownership. This, in turn, cultivates a culture suited for innovation through intelligent apps. 

These organizational changes focus on the following aspects: organizational readiness, skillset, culture of change and overcoming resistance, and adaptability. Let’s explore them below.

info

Register for the intelligent apps webinar on Driving Business Value by Modernizing with Cloud-Native & AI with Microsoft and Forrester on September 26. 

Explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries. 

Identifying Organizational Readiness for Intelligent Apps

The first step in preparing your organization for intelligent apps is to assess your current readiness for change. Analyze various elements of your organization, from infrastructure and workforce skills to strategic vision and overall culture.

Can your apps and systems integrate with AI platforms like Microsoft Azure AI? Is your workforce comfortable working alongside AI, or are they fearful and resistant? Does your strategic vision incorporate AI’s transformative potential, or does it fail to acknowledge AI as a critical driver of future growth?

These questions identify the challenges of integrating AI into an organization and determine the company’s readiness for intelligent apps.

Embracing intelligent apps involves more than being ready with your technological infrastructure or having the right technology or tools. You need flexible organizational structures and processes driving your business to accommodate new ideas and capabilities. Your teams must feel empowered to own and drive them. Much like Microsoft’s shift to AI-powered services—like AI-powered Bing and Edge and Microsoft 365 Copilot—embracing AI organization-wide requires a holistic vision and willingness to innovate, with teams capable of delivering on the plan.

After accessing these critical areas for organizational readiness, you can start planning a strategy for this transition to an AI-ready organization. Identify skill gaps and work to bridge them while nurturing an environment that appreciates and harnesses the potential of AI and intelligent apps.

Nurturing the Indispensable Skills for Intelligent Apps

Tasking your AI specialists with preparing your organization to use and build intelligent apps isn’t enough. Accomplishing this goal requires a cross-functional effort and a diverse skill set.

Developing intelligent apps involves data scientists managing and interpreting vast amounts of data, machine learning engineers building and training the models, and traditional developers integrating these models into usable applications. However, the impact of generative AI spans beyond creating apps. AI fluency across the entire organization—from sales and marketing to accounting and legal departments—offers substantial benefits, sparking new ideas and solutions from sometimes unexpected sources.

Teams' AI skills might roughly fall into three levels: 

  • Fundamental skills — Being familiar and efficient with using AI tools, for example, knowing how to write effective prompts for large language models (LLMs).
  • Visionary skills — Recognizing AI’s strengths and weaknesses, for example, its creativity and hallucinations, that is, unexpected output.
  • Expert skills — Managing data and training models and integrating AI technology into apps, such as incorporating the OpenAI application programming interface (API) into an Intelligent App.

Based on these levels, you can gauge your team's skill readiness to pinpoint the most significant gaps, whether in general AI knowledge across your organization or within technical development teams.

As intelligent apps become increasingly prevalent, upskilling your workforce is crucial. This transition opens a new realm of skills and capabilities for your team to master. You can offer machine learning, data analysis, and cloud computing training programs—such as Microsoft’s Azure AI Fundamentals certification—to empower your team to confidently navigate the intelligent apps landscape.

Nurturing these skills within your organization may require hiring new talent, reskilling current employees, and creating an environment that promotes learning and experimentation. Specifically, if your team understands tools like the Microsoft Azure AI platform, they could contribute significantly to developing and maintaining intelligent apps.

An upskilled team is more than a functional unit. It’s a think tank to drive innovation and improve your organization’s competitive standing in the digital marketplace.

Building a Culture of Change to Embrace AI

Even with the right people and skills in place, change remains an uphill battle for organizations committed to traditional processes. 

Transitioning to intelligent apps is as much about mindset as technology and skills. It requires fostering a culture open to change and innovation that understands AI’s transformative potential and embraces it to enhance rather than replace human capabilities. 

Leaders play a pivotal role in cultivating this mindset. As they model a positive attitude toward AI, emphasize its benefits, and address any concerns transparently, leaders significantly contribute to the organization’s readiness to adopt intelligent apps. 

The most critical factor in this transition is enabling an AI-friendly culture. It’s about encouraging curiosity, rewarding innovative thinking, and creating an environment where people thrive on learning and experimentation. 

Empowering teams to leverage practical AI tools and AI-driven data insights to aid decision-making helps motivate employees to use and explore AI’s potential, improve their skills, and contribute to the successful integration of intelligent apps. Such an environment is fertile ground to grow breakthrough ideas and innovative solutions. 

Overcoming Resistance to Change and Adopting Intelligent Apps

Despite AI’s potential, change isn’t easy. It takes effort and is especially challenging for organizations used to existing processes. This resistance to change is a significant roadblock when transitioning to intelligent apps. 

Overcoming this resistance involves:  

  • Understanding employees’ concerns. 
  • Challenging underlying assumptions about traditional systems and outdated processes that hinder change. 
  • Providing comprehensive training for workers to use AI effectively.
  • Demonstrating the benefits and opportunities of intelligent apps. 
  • Clear and continuous communication to dispel fears and misconceptions about AI.

These steps will foster greater acceptance and enthusiasm for intelligent apps in your teams. 

Preparing for the Future of Intelligent Apps and Continuous Evolution

Finally, it’s essential to understand that generative AI is just beginning to gain momentum. Adjusting to a world of intelligent apps isn’t a one-time change—it’s a continuous evolution. 

As AI technologies mature and evolve and more businesses realize their benefits, we can expect the landscape of intelligent apps to expand further. Organizations can cultivate a mindset of continuous learning, flexibility, and adaptation to prepare for this future. Staying ahead in this dynamic field requires organizations to keep up with the latest AI advancements, continually reassess their strategies, and pivot when necessary. 

The domain of intelligent apps is rapidly advancing. Organizations must prepare to navigate this dynamic landscape to stay ahead of the curve.

Summary

Preparing for AI and intelligent apps is a strategic decision. It requires thoughtful preparation and a culture that embraces change.

With the proper preparation and mindset, adopting AI can become a smoother, collaborative path to innovation for the whole organization. Integrating AI into your organization does not discard what has worked in the past. It simply enhances and augments those processes for a better future.

Consider the points above to assess your organization’s readiness and prepare to welcome intelligent apps.

In the first week of this series, you learned about intelligent apps, how organizations today find success through intelligent apps, what AI-assisted application development looks like, ways to leverage the power of generative AI, the technical changes needed to prepare for intelligent apps, and, in this article, the organizational changes required to harness the power of AI.

The future is intelligent. Use this series as a guideline to navigate challenges while preparing for and embracing AI's opportunities for an intelligent tomorrow.

Exercise

+ + \ No newline at end of file diff --git a/30daysofIA/tags/learn-live/page/10/index.html b/30daysofIA/tags/learn-live/page/10/index.html new file mode 100644 index 0000000000..4b72c5781c --- /dev/null +++ b/30daysofIA/tags/learn-live/page/10/index.html @@ -0,0 +1,27 @@ + + + + + +11 posts tagged with "learn-live" | Build Intelligent Apps On Azure + + + + + + + + + + + + + + +
+

11 posts tagged with "learn-live"

View All Tags

· 4 min read
It's 30DaysOfIA

Continue The Learning Journey through Fall For Intelligent Apps! 🍂

What We'll Cover

Thank you! ♥️

image

It's hard to believe that JavaScript on Azure hack-together is ending! It seems like just yesterday that we launched this initiative, and yet here we are, 15 days later, with an incredible amount of learning and growth behind us. So... it's time for a wrap!

From the bottom of our hearts, we want to thank each and every one of you for your participation, engagement, and enthusiasm. It's been truly inspiring to see the passion and dedication from this strong community, and we're honored to be a part of it. ✨

Recap of The JavaScript on Azure Global Hack-Together

As we wrap up this exciting event, we wanted to take a moment to reflect on all that we've accomplished together. Over the last 15 days, we've covered a lot of ground, from the basics of contributing to Open source to the exploration of the Contoso Real Estate project from its Frontend to its Backend and future AI implementation.

Now that the hack-together is ending, we want to make sure that you have all the resources you need to continue honing your skills in the future. Whether you're looking to make your fist contribution to open source, become an open source maintainers, collaborate with others, or simply keep learning, there are plenty of resources out there to help you achieve your goals. So, let's dive in and explore all the ways you can continue to grow your JavaScript skills on Azure!

JSonAzure Hack-together Roadmap 📍:

hack-together-roadmap (2)

Recap on past Livestreams🌟:

Day 1️⃣: Opening Keynote (Hack-together Launch): Introduction to the Contoso Real Estate Open-source project!, managing complex and complex enterprise architecture, new announcements for JavaScript developers on Azure

Day 2️⃣: GitHub Copilot & Codespaces: Introduction to your AI pair programmer (GitHub Copilot) and your virtual developer environment on the cloud (GitHub Codespaces)

Day 6️⃣: Build your Frontend using Static Web Apps as part of a complex, modern composable frontends (or micro-frontends) and cloud-native applications.

Day 9️⃣: Build a Serverless Backend using Azure Functions

Day 1️⃣3️⃣: Easily connect to an Azure Cosmos DB, exploring its benefits and how to get started

Day 1️⃣5️⃣: Being in the AI Era, we dive into the Azure OpenAI Service and how you can start to build intelligent JavaScript applications

📖 Self-Learning Resources

  1. JavaScript on Azure Global Hack Together Module collection
  2. Lets #HackTogether: Javascript On Azure Keynote
  3. Step by Step Guide: Migrating v3 to v4 programming model for Azure Functions for Node.Js Application

Continue your journey with #FallForIntelligentApps

Join us this Fall on a learning journey to explore building intelligent apps. Combine the power of AI, cloud-native app development, and cloud-scale data to build highly differentiated digital experiences. Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure. Engage in a self-paced learning adventure by following along the #30Days of Intelligent Apps series, completing the Intelligent Apps Skills Challenge, joining the Product Group for a live Ask The Expert series or building an end to end solution architecture with a live guided experience through the Learn Live series. Discover more here.

Hands-on practice: Make your first contribution to open source!

Join our GitHUb Discussion Forum to connect with developers from every part of world, see contributions from other, find collaborators and make your first contribution to a real-world project! +Don't forget to give the repo a star ⭐

Resources

All resources are accessible on our landing page

+ + + + \ No newline at end of file diff --git a/30daysofIA/tags/learn-live/page/11/index.html b/30daysofIA/tags/learn-live/page/11/index.html new file mode 100644 index 0000000000..fc95341fdc --- /dev/null +++ b/30daysofIA/tags/learn-live/page/11/index.html @@ -0,0 +1,26 @@ + + + + + +11 posts tagged with "learn-live" | Build Intelligent Apps On Azure + + + + + + + + + + + + + + +
+

11 posts tagged with "learn-live"

View All Tags

· 2 min read
It's 30DaysOfIA

September is almost here - and that can only mean one thing!! It's time to 🍂 Fall for something new and exciting and spend a few weeks skilling up on relevant tools, techologies and solutions!!

Last year, we focused on #ServerlessSeptember. This year, we're building on that theme with the addition of cloud-scale Data, cloud-native Technologies and cloud-based AI integrations to help you modernize and build intelligent apps for the enterprise!

Watch this space - and join us in September to learn more!

+ + + + \ No newline at end of file diff --git a/30daysofIA/tags/learn-live/page/2/index.html b/30daysofIA/tags/learn-live/page/2/index.html index 9760b0b7a2..2ab59531f7 100644 --- a/30daysofIA/tags/learn-live/page/2/index.html +++ b/30daysofIA/tags/learn-live/page/2/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "learn-live" | Build Intelligent Apps On Azure +11 posts tagged with "learn-live" | Build Intelligent Apps On Azure @@ -14,13 +14,13 @@ - - + +
-

9 posts tagged with "learn-live"

View All Tags

· 9 min read
It's 30DaysOfIA

Learn best practices for modernizing your on-premises or IaaS solutions to intelligent apps in the cloud.

What We'll Cover:

  • Traditional vs Intelligent Apps
  • On-Premises/IaaS vs. Cloud-Native Platforms
  • Modernization strategy for building Intelligent Apps

image of modernizing AI solutions for intelligent apps

Preparing the Path for Intelligent Apps: Transitioning from On-Premises/IaaS to Cloud-Native Applications

The proliferation of generative AI is paving the way for intelligent applications, software that leverages AI capabilities to deliver unparalleled functionality and user experience. As we covered in a previous article, “Demystifying Intelligent Applications: Leveraging AI in App Development,” intelligent Apps aren’t just products of advanced coding. They interact, learn, and evolve thanks to modern AI and machine learning (ML) breakthroughs. 

The paradigm shift from traditional to intelligent apps demands that we change our approach to the technical challenges of software and architecture design. It also necessitates adopting new ways of thinking and operating within an organization, which we explore in greater depth in “Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management.” 

This article explores the technical infrastructure powering intelligent apps, providing a roadmap to transition traditional on-premises or Infrastructure as a Service (IaaS) solutions to intelligent apps deployed to cloud native platforms and services. We’ll discover the differences between conventional and intelligent apps, options for re-architecting existing applications, and critical strategic considerations integral to modernizing the application-building approach. 

Understanding the Shift: Traditional vs. Intelligent Applications

Intelligent apps are more than just an incremental evolution from traditional apps. They involve a fundamental shift in software solution design, development, and deployment. A clear understanding of how each paradigm differs is crucial for comprehending the impact of this transition. 

Functionality and User Experience

Traditional applications, often rule-based and rigid, rely on preprogrammed operations. A traditional weather app, for example, typically presents structured, location-based forecasts using a predetermined data source. Meanwhile, an intelligent app leverages AI for data-driven decision-making and personalization. Rather than merely displaying the forecast, it integrates ML with additional data sources—like a user’s calendar and fitness data—to determine their favorite outdoor activities, provide relevant clothing suggestions, and even automatically design ideal vacations and times based on the user’s optimal weather preferences. 

Similarly, while traditional applications tend to offer a one-size-fits-all UI, intelligent apps use AI to create tailored interactions. For instance, an intelligent banking app might use generative AI to provide a voice-enabled UI that lets customers use natural language to ask about transactions and receive personalized financial advice.

Infrastructure

Intelligent apps integrating these sophisticated features require highly scalable, flexible infrastructure optimized for AI functionality and able to manage large volumes of real-time data. Unlike traditional applications deployed on-premises or in infrastructure as a service (IaaS) environments, intelligent apps demand the flexibility and scalability of cloud native architectures.

An on-premises or IaaS-based e-commerce application may struggle with connecting its data to AI services or scaling to accommodate voluminous real-time data and requests. But an intelligent app built with a cloud native approach and scalability at its core can handle variable demand much more efficiently. It also maintains its infrastructure components on demand, whether for collecting data for AI/ML consumption or user-facing features like AI-enabled customer support and personalization.

Consider an intelligent app built on Azure that creates ChatGPT-like experiences with custom data. It might connect Azure Open AI and Azure Cognitive Search to an app running on Azure Container Apps or Azure Kubernetes Service. 

The architecture might look like the following diagram based on this demo app:

diagram of a demo app architecture

While the cloud is well-equipped to handle the challenges associated with shifting to AI-centric infrastructure—handling real-time data processing and storage, integrating and managing AI services, configuring event-driven and microservices architectures, and ensuring security across the distributed environment—re-architecting a traditional app to an intelligent app requires careful planning and execution to ensure it benefits fully from the capabilities of AI and cloud computing.

On-Premises/IaaS vs. Cloud-Native Platforms

While on-premises and IaaS solutions provide substantial control over data and processes, this fine-grained control comes with setup and maintenance costs and may lack the scalability necessary for AI-driven Intelligent Apps. On-premises hosting involves purchasing and maintaining hardware, including servers and storage systems. IaaS involves leasing virtual hardware and then hiring and training IT personnel for the infrastructure’s operation, maintenance, and security.

Conversely, infrastructure built on cloud native app services with Microsoft Azure offers virtually unlimited scalability without ongoing hardware and maintenance costs. This feature is ideal for intelligent apps that need to adapt and scale to accommodate fluctuating workloads and data streams. And because these cloud infrastructures typically operate on a pay-as-you-go model, traditionally substantial upfront costs turn into predictable operating expenses, lowering both the cost and risk of launching applications.

Moreover, cloud service providers continually update their offerings, so building on sophisticated AI tools and services through the cloud, like the Azure AI Platform and orchestration capabilities with Azure Kubernetes Service, ensures that you have access to the latest AI and most advanced AI models and technologies.

Strategic Considerations for Transitioning to Intelligent Apps

When developing a strategy for transitioning to intelligent apps, you must consider costs, personnel skill development, and data management.

Monolithic architectures are unlikely to support the scale and ease of iteration required by intelligent apps, so you’ll likely need to implement code changes to support the transition. Your chosen hosting solution and the amount of data your app will handle affect cost and performance, in addition to determining privacy and compliance requirements.

An app that manages or stores terabytes or petabytes of data will require storage-optimized virtual machines or a managed database service hosting. Meanwhile, an app that processes video content or performs resource-intensive calculations should prioritize high-performance computing. Maximizing storage and performance can become extraordinarily costly.

Finally, existing applications shifting towards intelligent apps may encounter compatibility conflicts, depending on database type or the existing system’s underlying programming language. As you might imagine, a traditional app running on-premises and using a shared local database may need significant restructuring to support a cloud native architecture. You need plans for data migration, potential downtime, and training.

Next, let’s look at this transformation process.

Re-Architecting Monolithic Apps into Microservices

Shifting from monolithic applications to microservices is pivotal for transitioning to Intelligent Apps, and you can use this shift as an opportunity to transform your legacy software into intelligent apps that leverage the flexibility and power of microservices-enabled AI solutions.

Traditionally, a monolithic application (let’s use an online marketplace as an example) might handle all activities—from serving webpages to processing payments—within an on-premises server or a cloud server. However, this approach is challenging to scale and remains vulnerable to network failures or cyberattacks.

Breaking these applications into multiple services with specific responsibilities, such as authentication or notifications, can overcome these limitations. These services are typically deployed as microservices within container environments like Azure Kubernetes Service or serverless platforms like Azure Container Apps or Azure Functions using automated processes. This approach offers scalability, robustness, and improved performance, as each microservice can run independently and use its own database.

Shifting towards smaller, independent microservices in app architectures allows us to reap the benefits of AI-powered intelligent apps, for example, by using microservices to handle AI tasks such as processing natural language input, analyzing behavioral data, or personalizing content.

info

Watch Episode 1 and Episode 2 of the Learn Live series on how to build, test and deploy an end-to-end intelligent app solution using the Contoso Real Estate Sample.

Event-Driven Architectures: Knowing When to Use Them

Event-Driven Architectures (EDAs) can significantly enhance the performance and cost-effectiveness of intelligent apps. EDAs, typically implemented with microservices, respond in real time to events or state changes ranging from user interactions to real-time analytics.

Consider an intelligent app designed for personalized customer engagement. Instead of designing the application to check continuously for changes in user data, an EDA can react to such changes immediately, triggering appropriate microservices. For example, if users modify their preferences, an event could trigger an AI-driven recommendation microservice to update its suggestions immediately. This real-time responsiveness, enabled by services like Azure Event Grid, can improve user experiences and make intelligent apps more adaptive and proactive.

However, it’s worth noting that while EDAs provide significant benefits, they can introduce unique complexities, such as maintaining event consistency and debugging issues in event chains across multiple microservices. Therefore, it’s important to assess your application’s needs so you can understand where it can benefit from an EDA.

Conclusion

Transitioning to intelligent apps represents a shift in strategy, architecture, and infrastructure. By designing and re-architecting for cloud-based infrastructure, your apps gain scalability, cost-effectiveness, robust data management, access to sophisticated AI tools, enhanced security, and reduced operational overhead.  

But this transition extends well beyond its technical components. Organizations and the people working within them need equal attention. You can explore these facets in greater depth in “Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management.” 

The power of generative AI is here, and preparing for the move toward cloud-based intelligent applications will ensure that your apps remain performative and scalable and—most importantly—that your business is ready for the newest era of app innovation.

Exercise

- - +
Skip to main content

11 posts tagged with "learn-live"

View All Tags

· 9 min read
It's 30DaysOfIA

Learn best practices for modernizing your on-premises or IaaS solutions to intelligent apps in the cloud.

What We'll Cover:

  • Traditional vs Intelligent Apps
  • On-Premises/IaaS vs. Cloud-Native Platforms
  • Modernization strategy for building Intelligent Apps

image of modernizing AI solutions for intelligent apps

Preparing the Path for Intelligent Apps: Transitioning from On-Premises/IaaS to Cloud-Native Applications

The proliferation of generative AI is paving the way for intelligent applications, software that leverages AI capabilities to deliver unparalleled functionality and user experience. As we covered in a previous article, “Demystifying Intelligent Applications: Leveraging AI in App Development,” intelligent Apps aren’t just products of advanced coding. They interact, learn, and evolve thanks to modern AI and machine learning (ML) breakthroughs. 

The paradigm shift from traditional to intelligent apps demands that we change our approach to the technical challenges of software and architecture design. It also necessitates adopting new ways of thinking and operating within an organization, which we explore in greater depth in “Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management.” 

This article explores the technical infrastructure powering intelligent apps, providing a roadmap to transition traditional on-premises or Infrastructure as a Service (IaaS) solutions to intelligent apps deployed to cloud native platforms and services. We’ll discover the differences between conventional and intelligent apps, options for re-architecting existing applications, and critical strategic considerations integral to modernizing the application-building approach. 

Understanding the Shift: Traditional vs. Intelligent Applications

Intelligent apps are more than just an incremental evolution from traditional apps. They involve a fundamental shift in software solution design, development, and deployment. A clear understanding of how each paradigm differs is crucial for comprehending the impact of this transition. 

Functionality and User Experience

Traditional applications, often rule-based and rigid, rely on preprogrammed operations. A traditional weather app, for example, typically presents structured, location-based forecasts using a predetermined data source. Meanwhile, an intelligent app leverages AI for data-driven decision-making and personalization. Rather than merely displaying the forecast, it integrates ML with additional data sources—like a user’s calendar and fitness data—to determine their favorite outdoor activities, provide relevant clothing suggestions, and even automatically design ideal vacations and times based on the user’s optimal weather preferences. 

Similarly, while traditional applications tend to offer a one-size-fits-all UI, intelligent apps use AI to create tailored interactions. For instance, an intelligent banking app might use generative AI to provide a voice-enabled UI that lets customers use natural language to ask about transactions and receive personalized financial advice.

Infrastructure

Intelligent apps integrating these sophisticated features require highly scalable, flexible infrastructure optimized for AI functionality and able to manage large volumes of real-time data. Unlike traditional applications deployed on-premises or in infrastructure as a service (IaaS) environments, intelligent apps demand the flexibility and scalability of cloud native architectures.

An on-premises or IaaS-based e-commerce application may struggle with connecting its data to AI services or scaling to accommodate voluminous real-time data and requests. But an intelligent app built with a cloud native approach and scalability at its core can handle variable demand much more efficiently. It also maintains its infrastructure components on demand, whether for collecting data for AI/ML consumption or user-facing features like AI-enabled customer support and personalization.

Consider an intelligent app built on Azure that creates ChatGPT-like experiences with custom data. It might connect Azure Open AI and Azure Cognitive Search to an app running on Azure Container Apps or Azure Kubernetes Service. 

The architecture might look like the following diagram based on this demo app:

diagram of a demo app architecture

While the cloud is well-equipped to handle the challenges associated with shifting to AI-centric infrastructure—handling real-time data processing and storage, integrating and managing AI services, configuring event-driven and microservices architectures, and ensuring security across the distributed environment—re-architecting a traditional app to an intelligent app requires careful planning and execution to ensure it benefits fully from the capabilities of AI and cloud computing.

On-Premises/IaaS vs. Cloud-Native Platforms

While on-premises and IaaS solutions provide substantial control over data and processes, this fine-grained control comes with setup and maintenance costs and may lack the scalability necessary for AI-driven Intelligent Apps. On-premises hosting involves purchasing and maintaining hardware, including servers and storage systems. IaaS involves leasing virtual hardware and then hiring and training IT personnel for the infrastructure’s operation, maintenance, and security.

Conversely, infrastructure built on cloud native app services with Microsoft Azure offers virtually unlimited scalability without ongoing hardware and maintenance costs. This feature is ideal for intelligent apps that need to adapt and scale to accommodate fluctuating workloads and data streams. And because these cloud infrastructures typically operate on a pay-as-you-go model, traditionally substantial upfront costs turn into predictable operating expenses, lowering both the cost and risk of launching applications.

Moreover, cloud service providers continually update their offerings, so building on sophisticated AI tools and services through the cloud, like the Azure AI Platform and orchestration capabilities with Azure Kubernetes Service, ensures that you have access to the latest AI and most advanced AI models and technologies.

Strategic Considerations for Transitioning to Intelligent Apps

When developing a strategy for transitioning to intelligent apps, you must consider costs, personnel skill development, and data management.

Monolithic architectures are unlikely to support the scale and ease of iteration required by intelligent apps, so you’ll likely need to implement code changes to support the transition. Your chosen hosting solution and the amount of data your app will handle affect cost and performance, in addition to determining privacy and compliance requirements.

An app that manages or stores terabytes or petabytes of data will require storage-optimized virtual machines or a managed database service hosting. Meanwhile, an app that processes video content or performs resource-intensive calculations should prioritize high-performance computing. Maximizing storage and performance can become extraordinarily costly.

Finally, existing applications shifting towards intelligent apps may encounter compatibility conflicts, depending on database type or the existing system’s underlying programming language. As you might imagine, a traditional app running on-premises and using a shared local database may need significant restructuring to support a cloud native architecture. You need plans for data migration, potential downtime, and training.

Next, let’s look at this transformation process.

Re-Architecting Monolithic Apps into Microservices

Shifting from monolithic applications to microservices is pivotal for transitioning to Intelligent Apps, and you can use this shift as an opportunity to transform your legacy software into intelligent apps that leverage the flexibility and power of microservices-enabled AI solutions.

Traditionally, a monolithic application (let’s use an online marketplace as an example) might handle all activities—from serving webpages to processing payments—within an on-premises server or a cloud server. However, this approach is challenging to scale and remains vulnerable to network failures or cyberattacks.

Breaking these applications into multiple services with specific responsibilities, such as authentication or notifications, can overcome these limitations. These services are typically deployed as microservices within container environments like Azure Kubernetes Service or serverless platforms like Azure Container Apps or Azure Functions using automated processes. This approach offers scalability, robustness, and improved performance, as each microservice can run independently and use its own database.

Shifting towards smaller, independent microservices in app architectures allows us to reap the benefits of AI-powered intelligent apps, for example, by using microservices to handle AI tasks such as processing natural language input, analyzing behavioral data, or personalizing content.

info

Watch Episode 1 and Episode 2 of the Learn Live series on how to build, test and deploy an end-to-end intelligent app solution using the Contoso Real Estate Sample.

Event-Driven Architectures: Knowing When to Use Them

Event-Driven Architectures (EDAs) can significantly enhance the performance and cost-effectiveness of intelligent apps. EDAs, typically implemented with microservices, respond in real time to events or state changes ranging from user interactions to real-time analytics.

Consider an intelligent app designed for personalized customer engagement. Instead of designing the application to check continuously for changes in user data, an EDA can react to such changes immediately, triggering appropriate microservices. For example, if users modify their preferences, an event could trigger an AI-driven recommendation microservice to update its suggestions immediately. This real-time responsiveness, enabled by services like Azure Event Grid, can improve user experiences and make intelligent apps more adaptive and proactive.

However, it’s worth noting that while EDAs provide significant benefits, they can introduce unique complexities, such as maintaining event consistency and debugging issues in event chains across multiple microservices. Therefore, it’s important to assess your application’s needs so you can understand where it can benefit from an EDA.

Conclusion

Transitioning to intelligent apps represents a shift in strategy, architecture, and infrastructure. By designing and re-architecting for cloud-based infrastructure, your apps gain scalability, cost-effectiveness, robust data management, access to sophisticated AI tools, enhanced security, and reduced operational overhead.  

But this transition extends well beyond its technical components. Organizations and the people working within them need equal attention. You can explore these facets in greater depth in “Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management.” 

The power of generative AI is here, and preparing for the move toward cloud-based intelligent applications will ensure that your apps remain performative and scalable and—most importantly—that your business is ready for the newest era of app innovation.

Exercise

+ + \ No newline at end of file diff --git a/30daysofIA/tags/learn-live/page/3/index.html b/30daysofIA/tags/learn-live/page/3/index.html index 3e9424e5c3..526b9f7871 100644 --- a/30daysofIA/tags/learn-live/page/3/index.html +++ b/30daysofIA/tags/learn-live/page/3/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "learn-live" | Build Intelligent Apps On Azure +11 posts tagged with "learn-live" | Build Intelligent Apps On Azure @@ -14,13 +14,13 @@ - - + +
-

9 posts tagged with "learn-live"

View All Tags

· 10 min read
It's 30DaysOfIA

Explore how digital natives and Independent Software Vendors (ISVs) can transform their business operations and generate higher value for customers with generative AI and intelligent apps.

What We'll Cover:

  • Potential of Generative AI in application development
  • Challenges with Generative AI
  • Integrating Generative AI with business operations

image of generative AI and intelligent apps used in business

Unleashing Business Transformation: How Digital Natives Can Leverage Generative AI

Generative AI is no longer a technological flight of fancy. It’s here, and it’s rewriting the rules of business innovation. 

This type of AI leverages machine learning algorithms to create new data with similar characteristics to a given dataset. It goes beyond text generation using generative pretrained transformer (GPT) models, covering an array of applications that span from image synthesis to translations and more. It also powers a new class of intelligent applications, which combine AI capabilities with traditional software functions for enhanced user experiences and advanced business solutions. 

Generative AI is transforming how digital natives and independent software vendors (ISVs) operate. Generative AI offers new possibilities for creating unique business value, enhancing customer experiences, and driving growth. 

It plays a critical role in applications, improving functionality, aiding decision-making, and automating complex tasks to deliver dynamic, uniquely personalized user experiences. Generative AI also enables and automates data-driven outcomes, functions, and features.

Integrating AI capabilities into customer-facing apps and turning them into intelligent apps benefits both the customer and the business, as we discussed in Demystifying Intelligent Applications: Leveraging AI in App Development. You get faster product development workflows and better business intelligence for sales and marketing, while your users get more natural and context-aware features and interactions. 

That said, to realize generative AI’s potential, you need some strategy and knowledge of how and where it fits into your current business and operations. We’ll examine this technology’s potential to transform business operations, its risks, and rewards, and how to integrate it into your existing operations. 

Unpacking the Potential of Generative AI

Generative AI is creating new opportunities in the digital playing field. It empowers businesses to generate insights, design products, and automate processes in ways and at speeds that were previously unimaginable. You can now predict customer behavior more accurately, offer highly personalized user experiences, and prototype innovative ideas rapidly. 

For example, Microsoft used generative AI to reinvent web search in the new AI-powered Bing and Edge. The search experience has evolved from a solitary user flow of typing in keywords and scrolling through results. It’s now a conversational experience where Bing uses context to offer better quality and more personalized search queries and results. 

Similarly, Microsoft harnessed generative AI to transform its productivity suite into intelligent apps. Through Copilot for Microsoft 365, PowerPoint now empowers users to tell their stories by using DALL-E to generate images effortlessly. It also helps rephrase text-heavy slides to be more straightforward. Outlook now assists users to compose more effective emails, enabling better communication.

These are just some examples of how AI powers day-to-day work and operations. 

Businesses that ignore generative AI miss out on untapped market opportunities, letting competitors gain an edge in the digital race. By seeking ways to rethink assumptions and reinvent existing approaches, you can effectively use generative AI to create new opportunities, open products to new use cases and markets, unlock new growth and customer satisfaction levels, and increase your revenue.

info

Register for the intelligent apps webinar on Driving Business Value by Modernizing with Cloud-Native & AI with Microsoft and Forrester on September 26.

Explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries. 

The Crucial Role of Generative AI in Business Operations

Generative AI can be a powerful catalyst for business transformation. Let’s quickly tour some ways it can redefine business operations. 

Product development is traditionally a long and painstaking process. Now, tools like GitHub Copilot and TestPilot use generative AI to make coding and testing software easier, faster, and more robust. 

Similarly, machine learning as a service technology, such as Azure Machine Learning, democratizes the availability of machine learning. It helps collect, clean, and train models across datasets, making engineering and analyzing data more efficient and accessible.  

Generative AI also simplifies the process of creating intelligent apps. Previously, simulating thousands of prototypes, predicting their performance, and narrowing down to the most viable options could take weeks, months, or even years of iteration and data analysis. With generative AI, this process can be as quick as days, hours, or perhaps minutes. 

Customer experience is also ripe for transformation. Personalized recommendations drive engagement and sales, and generative AI can scale that personalization to every customer interaction using comprehensive and continually updated data. For instance, Microsoft leverages AI to enable personalization in Dynamics 365 Customer Insights. Using this tool, you can ask questions about your customers in natural language and gain valuable and actionable insights into your customer base. 

Lastly, marketing and sales can benefit from generative AI. For example, identifying current and future key markets and emerging trends helps companies stay ahead of the curve. Tools like Copilot in Power BI help you quickly understand and uncover insights about your business, enabling your team to craft targeted campaigns, create engaging content, and optimize your business strategy. 

Leveraging this full generative AI potential requires the right tools. GPT-4, for instance, can be instrumental in realizing effective business transformation. It excels in understanding context, generating human-like text, and learning from minimal data input, making it a formidable tool in any digital native's arsenal. Platforms like Azure OpenAI Service bring GPT-4’s power to your fingertips.

However, as with any powerful technology, it’s unwise to treat generative AI as a perfect solution. You must know the challenges and risks of not leveraging it effectively, including technical and human-factor considerations. 

Without generative AI, your company risks missing the opportunity to please customers via personalization, as discussed previously. You’ll miss sales opportunities via business intelligence insights, limiting your ability to grow revenue and keep up with the competition. 

One challenge of leveraging generative AI’s full potential is finding the right tools. Fortunately, you can implement generative AI in your organization without investing in infrastructure, computational power, and expertise. Platforms like Azure OpenAI Service and Azure AI Services make AI easily accessible. They also work with Azure compute and data services like Azure Functions and Azure Kubernetes Service (AKS), enabling you to build intelligent apps easily and quickly. 

However, generative AI can’t do everything. It may need human expertise and intervention to use it fully. 

For example, generative AI can create unique data. However, the data may have limited diversity and complexity. The AI may need help recognizing additional considerations or requirements specific to a problem. It may also lack context due to various factors, such as the information’s freshness. Also, since it’s an emerging field that’s potentially worth $4.4 trillion, we’re still learning about the nuances and potential regulatory impact of generative AI. 

Perhaps the most crucial and challenging aspect of adopting generative AI is an organization’s required cultural shift. Your teams must learn how to use AI as your company fosters an understanding of AI’s potential and limitations. 

Begin integrating AI literacy into your company’s DNA. Knowledge of its potential and the readily available tools can inspire your teams to incorporate generative AI into their daily workflows and business operations quickly, improve productivity, and deliver superior customer experiences. 

No company wants to be outpaced by its competitors. You can balance generative AI’s potential with your business operations by staying aware of and adapting to the dynamic AI landscape.

Achieving Harmony: Integrating Generative AI with Existing Business Operations

Integrating generative AI into existing business operations doesn’t have to be disruptive. With the correct strategy, you can do so seamlessly. Your goal should be to augment and enhance your operations, not disrupt and replace. 

For example, Microsoft Cloud technologies leveraged AI to relieve healthcare organizations’ challenges, such as talent shortage and workforce burnout. Providers automated their workflows, invested in specific applications and connectors, and enhanced support for data and operations. 

Azure AI Services, such as text analytics for health, enable healthcare organizations to understand their largely unstructured and untapped medical data. Project Health Insights leans on AI to help healthcare professionals gain actionable insights and inferences from patient data. 

Azure Health Bot, an Intelligent App, empowers organizations to build and deploy legally compliant, powerful conversational healthcare experiences to patients. 

A successful AI integration like this begins with understanding. Knowing what generative AI can do and setting realistic expectations can help you identify where and how AI can best serve your operations. 

Next, you can foster a culture of AI literacy across teams, speeding up adoption by helping them understand how it can aid their work. The more familiar your team is with AI and its possibilities, the smoother the transition. Training and education programs play a pivotal role here. 

Finally, integration requires a systematic approach. It’s not about overhauling everything at once — it's about identifying areas where AI can add the most value, then gradually experimenting and scaling its use. Successful integration stories often feature a phased approach, starting small and growing steadily. 

As we move toward a future where generative AI is a staple in business operations, remember that every journey is unique. Although there’s no one-size-fits-all solution, a strategic approach and a keen understanding of your business needs will help you navigate this new landscape successfully.

Conclusion

Generative AI offers plenty of opportunities for businesses that embrace it. It’s fueling the next generation of intelligent applications, transforming everything from product development to customer engagement to marketing. This technology is the key to unlocking unprecedented growth and innovation. 

While the journey may have challenges, the potential rewards make generative AI worth exploring. As generative AI advances, Azure AI’s broad range of tools and services will help you navigate this fascinating field.

Exercise

  • Watch Episode 02 of the serverless edition Learn Live session to learn how to build, test and deploy an end-to-end intelligent app solution.
  • Complete the Cloud Skills Challenge to build on your AI skills.
  • Register for Ask the Expert: Azure Functions on September 26 for a live Q&A with the Product Team on how to build AI apps using serverless computing.
- - +
Skip to main content

11 posts tagged with "learn-live"

View All Tags

· 12 min read
It's 30DaysOfIA

Delve into creating an Intelligent App that leverages Azure AI Vision to analyze images and extract data. Develop an API to perform optical character recognition (OCR) on uploaded images and testing this API locally.

What We'll Cover:

  • Understanding Azure AI Vision and Azure Kubernetes Service
  • Build a Python Web API to perform OCR
  • Test the API locally

image depicting an Intelligent App using AI

Jumpstart Your AI Journey: Building Your First Intelligent App with Azure AI and AKS (1)

An Intelligent App is a software application that uses artificial intelligence (AI), such as machine learning (ML), to extract meaningful insights, enabling users to make informed decisions based on the extracted text, data, sound, images, and more.

Intelligent Apps can employ advanced algorithms and models to recognize objects, patterns, and features within images, allowing them to perform tasks like object identification, image classification, facial recognition, and image segmentation.

To explore the power of Intelligent Apps, let’s build a Python app that performs optical character recognition (OCR) on images containing numbers and performs aggregate functions on the extracted numbers, like sum and average. We’ll leverage Azure AI Services for the OCR functionality and Azure Kubernetes Service (AKS) to develop, publish, and maintain a cloud-based, scalable app in Azure.

Let’s get started!

Understanding Azure AI Vision and Azure Kubernetes Service

Azure AI Vision is a cloud-based service that offers a range of advanced computer vision capabilities enabling applications to interpret content, such as images and videos, using ML and other AI algorithms. Azure computer vision lets users integrate powerful vision features into their projects without ML experience.

OCR is one of the critical functionalities within Azure vision service, allowing applications to extract text from images. Within Azure AI Vision service, OCR offers several features, including text extraction, multilingual support, layout analysis of documents, and integration with Azure services, such as Azure Functions and Azure Machine Learning.

info

Watch the intelligent apps webinar on Driving Business Value by Modernizing with Cloud-Native & AI with Microsoft and Forrester on September 26.

AKS is Microsoft Azure’s container orchestration platform that enables us to quickly and efficiently deploy, manage, and scale containerized applications, such as our Intelligent App.

Let’s explore how to leverage these tools to build an Intelligent App that uses Azure AI Vision to analyze images and extract data.

Prerequisites

To follow this tutorial, ensure you have the following:

For a preview of this final project, take a look at the complete project code.

Building the API with Azure AI Vision Service

First, log in to your Azure account and navigate to the Azure Portal.

Click Create a resource and search for “resource group.” Create a new resource group named computer-vision.

image in Azure Portal of creating a new resource group

Return to the Azure Portal home page and click Create a resource.

image in Azure Portal of creating a new resource

Search for “computer vision” and select it from the results. Click Create.

image of searching for resource group in Azure Portal

Clicking Create displays the Create Computer Vision wizard. Select the resource group you just created as the Resource group from the dropdown. Enter a resource name similar to what is seen in the screenshot below. Note that this must be a unique name so choose one that is not currently in use. Select Standard S1 as the pricing tier and check the Responsible use of AI box in the field at the bottom.

Image of selecting the Responsible use of AI box

Image of project details

Click Review + Create, then click Create.

Next, get the API key and endpoint from the Azure AI services multi-service account you created. Once the resource is created, navigate to the resource details page. Under Resource Management, select Keys and Endpoint. Once on the Keys and Endpoint page, you’ll find the Key 1 and Endpoint values. These credentials are necessary to access the Azure AI APIs.

image of selecting Keys and Endpoint

second image of selecting Keys and Endpoint

Configuring the Local Environment Variables

Next, we need to define a local environment variable file. This convenient way to store our API keys in a simple text file enables us to manage sensitive information and separate configuration from our API code.

If you haven’t done so already, clone the starter project template from GitHub into your local computer. Open the starter project template in a code editor and create a new .env file in the root folder.

Note: Root folder in this case is the “/Microsoft_Series17-18_Code/intelligent-app-before" folder.

Add the following key-value pairs to the file, remembering to replace the placeholder values with those you obtained from the previous step.

VISION_KEY=<THE-KEY-1-VALUE-FROM-YOUR-AZURE-AI-SERVICE>
VISION_ENDPOINT=<THE-ENDPOINT-VALUE-FROM-YOUR-AZURE-AI-SERVICE>

Reviewing the Quickstart Code

Let’s review the app.py file in the starter project template.

The app.py file works as the entry point for the Flask application. It is responsible for initializing the web server, listening for incoming requests, dispatching them to the appropriate route handlers, and generating responses.

Below is the code contained in the app.py file:

import os
import json

from flask import Flask, request
from flask_restful import Resource, Api
from werkzeug.utils import secure_filename

app = Flask(__name__,
static_url_path='',
static_folder='static/files')

api = Api(app)

app = Flask(__name__)

app.config['UPLOAD_FOLDER'] = 'files'

api = Api(app)

class UploadHandler(Resource):

def allowed_file(self, filename):
return '.' in filename and \
filename.rsplit('.', 1)[1].lower() in {'png'}

def post(self):
form = request.form.to_dict()

if 'file' not in request.files:
return json.dumps({ "success": False, "error": "No file part"})

file = request.files.get("file")
if file and self.allowed_file(file.filename):
filename = secure_filename(file.filename)
upload_folder = "static/files"
if not os.path.exists(upload_folder):
os.makedirs(upload_folder)
local_file_path = os.path.join(upload_folder, filename)
file.save(local_file_path)

return f"File {filename} uploaded successfully to folder: {upload_folder}"

api.add_resource(UploadHandler, "/")

if __name__ == '__main__':
app.run(debug=True)

The UploadHandler class above is defined as a Flask RESTful resource to handle file uploads. The class contains two methods:

  • The allowed_file method checks whether the file extension is allowed for upload. In this case, only .png files are allowed.

  • The post method handles HTTP POST requests for file uploads. It saves the uploaded .png file to the static/files folder.

Finally, the application returns a text response informing us that the file has been uploaded successfully.

Implementing the REST API in Python

To implement image analysis in your REST API, open the starter project template in your terminal, create a virtual environment, and activate it.

Next, install the Azure AI Vision SDK:

pip install azure-ai-vision

Then, add the following line to the requirements.txt file to include the azure-ai-vision package:

azure-ai-vision==0.13.0b1

Now, create an ocr_helper.py file in the project’s root folder. This file is a Python module that provides functions for processing images using OCR. Add the following code to the file:

import os
from statistics import median
from decimal import Decimal
import azure.ai.vision as sdk

def process_ocr(source_image):
service_options = sdk.VisionServiceOptions(os.environ["VISION_ENDPOINT"],
os.environ["VISION_KEY"])

vision_source = sdk.VisionSource(filename=source_image)

analysis_options = sdk.ImageAnalysisOptions()

analysis_options.features = (
sdk.ImageAnalysisFeature.CAPTION |
sdk.ImageAnalysisFeature.TEXT
)

analysis_options.language = "en"

analysis_options.gender_neutral_caption = True

image_analyzer = sdk.ImageAnalyzer(service_options, vision_source, analysis_options)

result = image_analyzer.analyze()

ocr_result = get_ocr_result(result)

return ocr_result

def get_ocr_result(result):
string_list = []

if result.reason != sdk.ImageAnalysisResultReason.ANALYZED:
return sdk.ImageAnalysisErrorDetails.from_result(result)
else:
if result.text is not None:
for line in result.text.lines:
for word in line.words:
string_list.append(word.content)

number_list = convert_to_decimal_list(string_list)

aggregate_result = aggregate_operations(number_list)

return {
"aggregate_result": aggregate_result,
"numbers_read": string_list
}

def convert_to_decimal_list(string_list):
return list(map(Decimal, string_list))

def aggregate_operations(numbers):
result = {
'sum': sum(numbers),
'average': sum(numbers) / len(numbers),
'median': median(numbers),
'min': min(numbers),
'max': max(numbers)
`}`
`return result`

This module uses the azure-ai-vision package to analyze images, including capturing captions and extracting text from the image. The process_ocr function inputs an image file and performs the OCR analysis. The module is a convenient tool for analyzing text-based data in pictures and performing numerical computations based on that data.

Let’s review the different components of the ocr_helper.py module listed above and explore how its functions help our Intelligent App process images and perform aggregate operations:

  • The process_ocr function takes the parameter source_image, which is the path of the image to be processed. The function then initializes the VisionServiceOptions using environment variables VISION_ENDPOINT and VISION_KEY to connect to the Azure AI Vision API.
  • The process_ocr function creates a VisionSource object with the specified source_image file name. ImageAnalysisOptions specify the features to be analyzed, including CAPTION and TEXT. The language is set to English ("en"), and gender-neutral captions are enabled.
  • Finally, an ImageAnalyzer object is created with the service options, vision source, and analysis options. The image is then analyzed using the image_analyzer.analyze method to retrieve OCR results. The recognized text from the OCR result is extracted, with numerical values stored in the string_list variable.
  • The convert_to_decimal_list function converts the list of strings to a list of decimal numbers, which helps process numeric values extracted from the text.
  • The aggregate_operations function calculates various aggregate statistics (sum, average, median, minimum, maximum) from a list of numbers and returns the results in a dictionary.

Note that you must have the appropriate credentials (VISION_KEY) and endpoint (VISION_ENDPOINT) configured for the Azure AI Vision API to use this module.

Finally, we must modify the app.py file so our code can use the process_ocr function of the ocr_helper.py file.

Add the following import statement to the app.py file:

from ocr_helper import process_ocr

Then, replace this line:

return f"File {filename} uploaded successfully to folder: {upload_folder}"

With these two lines:

aggregates = process_ocr(local_file_path)
return json.dumps(aggregates, default=str)

Doing so allows our application to perform aggregate operations (sum, average, median, minimum, maximum) of any numeric values found in the text and return the operation’s result as a JSON response.

Running the Intelligent App Locally

Our final goal is to deploy the sample app to AKS, which will run the app in a container. However, as a good practice, let’s run the app in a container locally before moving it to AKS. Doing so ensures that our application and its dependencies are encapsulated and can be run consistently across different environments.

Deploying to Docker Desktop

For our application to run on Docker, it needs two additional files: a Dockerfile and docker-compose.yml. A Dockerfile creates a single container image by specifying the steps to build it, and a docker-compose.yml file manages multiple containers as a cohesive application, defining their configurations and relationships. Dockerfile builds the image, while docker-compose.yml orchestrates multi-container applications.

In the project root folder, add a file named Dockerfile with the content below:

# syntax=docker/dockerfile:1

FROM python:3.8-slim-buster

WORKDIR /intelligentapp

COPY requirements.txt requirements.txt
RUN pip3 install -r requirements.txt

RUN pip install debugpy

COPY . .

CMD [ "python3", "-m" , "flask", "run", "--host=0.0.0.0"]

Now create a file named docker-compose.yml file in the project’s root folder with the following code, replacing the VISION_KEY and the VISION_ENDPOINT according to the environment variables you configured earlier:

version: '3.8'
services:
intelligentapp:
build:
context: .
dockerfile: Dockerfile
image: intelligent-app
ports:
- 5000:5000
container_name: intelligent-app
environment:
- VISION_KEY=<THE-KEY-1-VALUE-FROM-YOUR-AZURE-AI-SERVICE>
- VISION_ENDPOINT=<THE-ENDPOINT-VALUE-FROM-YOUR-AZURE-AI-SERVICE>

Then, run the following command in the terminal to build the image and start a container for the Intelligent App services defined in the docker-compose.yml file:

docker-compose up --build --force-recreate

Next, open a new terminal and run the command below to list the image deployed to your local Docker:

docker images

docker images

Testing the Intelligent App Locally

Now, we’ll test the Intelligent App’s functionality using the following test image:

test image to test functionality

This image is provided as sample1.png in the sample app source files, so you can easily use it in the next step.

The Intelligent App will read the image above using OCR to extract numbers in the image and perform five aggregate functions:

  • Sum
  • Average
  • Median
  • Min
  • Max

To test the API, open Postman and fill out the fields as follows:

  • URL: http://localhost:5000/
  • Method: POST
  • Body:
    • Form-data
      • Key: File — Click the right end of the Key field and select File from the dropdown list.
      • Value: Click Select Files, then select the sample1.png file provided in the sample code.

image of selecting the sample file

Now click the Send button and review the result body:

"{\"aggregate_result\": {\"sum\": \"25821\", \"average\": \"5164.2\", \"median\": \"5622\", \"min\": \"1447\", \"max\": \"9802\"}, \"numbers_read\": [\"3049\", \"5901\", \"5622\", \"1447\", \"9802\"]}"

As we can see, the app running on a local container returned the correct results based on the sample image. So, we’re ready to prepare our Azure resources to run the Intelligent App on AKS.

Exercise

In this article, we explored the creation of an Intelligent App that leverages Azure AI Vision to analyze images and extract data. We learned how to build a Python Web API to perform OCR on uploaded images and subsequently test this API locally.

Head to the next part, to deploy this API via Azure Kubernetes Service.

+ + \ No newline at end of file diff --git a/30daysofIA/tags/learn-live/page/4/index.html b/30daysofIA/tags/learn-live/page/4/index.html index 9add440089..5eb8408c3d 100644 --- a/30daysofIA/tags/learn-live/page/4/index.html +++ b/30daysofIA/tags/learn-live/page/4/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "learn-live" | Build Intelligent Apps On Azure +11 posts tagged with "learn-live" | Build Intelligent Apps On Azure @@ -14,13 +14,13 @@ - - + +
-

9 posts tagged with "learn-live"

View All Tags

· 9 min read
It's 30DaysOfIA

In the part of our series on intelligent apps, we’ll explore how AI is transforming application development, from design and architecture to building.

What We'll Cover:

  • Revolutionizing application development using AI
  • Infusing AI in application design architecture
  • AI Assisted Pair Programming
  • Future of App Development

A decorative image of application development with AI

Reimagining Application Development With AI: A New Paradigm

Artificial intelligence (AI) is impacting every industry. AI-powered development tools offer new functionalities and assist software developers throughout the development process, revolutionizing app development. From initial design and architecture to coding and debugging, AI reshapes how we approach application conception, creation, and iteration.

In part one of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” we explored the new breed of intelligent apps and how they use AI. In this article, we’ll see how AI technologies enhance developer toolkits and shape the future of app development, particularly through AI-enabled coding assistance and debugging.

The Dawn of Intelligent Apps: Revolutionizing Application Development

AI is more than just another addition to the development toolbox—it represents a revolution bringing unprecedented efficiency, innovation, and user engagement to our applications. 

What sets intelligent apps apart is how they can adapt and learn, helping us deliver unique, real-time user experiences.  

One example is an AI-driven health application that can track biometric data, predict potential health issues, provide personalized health advice, and even guide emergency services during a medical crisis. An AI-enabled language learning app that adapts to our learning speeds, recognizes our problem areas, and customizes its content to help us improve our language proficiencies is another example.  

Intelligent apps like these don’t just use data for recommendations—they can make critical decisions and provide sophisticated services that were previously impossible. 

For developers, intelligent apps mean we’re no longer just coding—we’re teaching applications how to think, adapt, and make decisions. AI is reshaping every step of the development journey, helping us to build more intuitive and user-focused products. 

Next, we’ll examine the impact of AI on application design and architecture and how AI-assisted “pair programming” changes software development.

info

Register for the FREE webinar on Intelligent Apps with Microsoft and Forrester on September 26 to explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries.

There will be a showcase of real-world use cases that demonstrate how AI can be seamlessly integrated into cloud-native environments driving tangible business value.

AI in Design and Architecture: Fueling Creativity and Efficiency

AI technologies help developers automate and streamline their processes and provide new ways to think about design.  

In traditional application development, the design and architecture phase can take significant time, effort, and expertise. For instance, adopting a monolithic architecture or a microservices-based design can significantly increase coding, testing, and debugging complexity. The app may even behave differently when hosted locally than in a deployed environment. It may take hours to track down an issue manually—time developers could spend elsewhere. 

Conversely, we can generate numerous design iterations in seconds with AI, each optimized to fulfill specific user needs and business goals. AI tools can quickly model various scenarios' impacts and software design patterns in the architectural design phase. It provides insights into performance and potential issues and suggests improvements. AI can also generate skeleton code, allowing developers to prototype different solutions or test specific components and technologies rapidly. By reviewing thousands of lines of code in seconds, AI allows us to focus on creating apps that serve users through feature development instead of searching for bugs.  

Integrating AI into the design and architecture phase can even extend our creative capabilities. By leveraging the power of generative AI—like large language models (LLMs)—and their broad knowledge and context going beyond software, we can better brainstorm apps and feature ideas. We can go beyond code and software architecture and get ideas about user experience, logo and branding, or marketing concepts. And as AI tools deliver insights and ideas we would have never considered; it encourages more innovation.

info

Watch Episode 01 of the Learn Live session to learn how to build an end-to-end intelligent app solution architecture.

The Power of AI-Assisted Pair Programming in Building Applications

AI-assisted pair programming is a collaborative coding approach where an AI system—like GitHub Copilot or TestPilot—assists developers during coding. It’s an increasingly common approach that significantly impacts developer productivity. In fact, GitHub Copilot is now behind an average of 46 percent of developers’ code and users are seeing 55 percent faster task completion on average. 

For new software developers, or those interested in learning new skills, AI-assisted pair programming are training wheels for coding. With the benefits of code snippet suggestions, developers can avoid struggling with beginner pitfalls like language syntax. Tools like ChatGPT can act as a personal, on-demand tutor—answering questions, generating code samples, and explaining complex code syntax and logic. These tools dramatically speed the learning process and help developers gain confidence in their coding abilities. 

Building applications with AI tools hastens development and provides more robust code. For example, GitHub Copilot helps us write new code accurately. It also quickly makes sense of existing code, suggests whole new lines or blocks of code, and even detects errors and proposes fixes. It’s like having a second coder working alongside us—one who never tires and is trained on billions of lines of code. 

However, it’s also important to acknowledge the limitations of AI. While generative AI-assistance technologies like GitHub Copilot are impressive, they’re still imperfect. They can sometimes produce erroneous, non-functional, or vulnerable code. They can also lack nuanced, domain-specific knowledge.  

So, although these tools are helpful, we can’t rely on them fully—these are programming aids rather than definitive solutions. AI-assisted pair programming is about augmenting the human developer’s skills, not replacing them.

Debugging and Improving Code With AI: Enhancing Application Quality

Identifying and correcting code errors is often time-consuming and challenging for developers. AI streamlines the debugging process by automatically detecting anomalies, suggesting fixes, and learning from previous bugs to prevent them from recurring. 

We can use generative AI to create unit tests from code snippets, from simple parameter checking and input validation to complex mocks of database and network services. Automated AI testing improves code coverage and robustness. It saves significant time compared to manually crafting unit tests. 

AI also revolutionizes code reviews by scanning and highlighting potential issues—like code patterns leading to a bug or a coding standards violation—and suggesting ways to simplify or refactor the code for readability and performance. This reduces human error and makes the code review process more efficient. Its benefits extend to small teams, where the availability of developers is crucial to deadlines, and large teams, where effective collaboration depends on readability and code consistency. AI further saves time by triaging issues so developers can prioritize the most mission-critical ones. 

Aside from improving code quality, AI is critical to understanding system behavior. Intelligent AI-assisted software development tools can monitor and audit system behavior, giving developers a better insight into how their code performs in different scenarios. AI can track patterns, identify inefficiencies, and provide alerts when they detect unusual behavior—enabling developers to enhance their code further. 

By predicting and identifying potential vulnerabilities, intelligent AI-assisted coding apps like Microsoft Security Copilot are pivotal in ensuring application functionality, reliability, and security. These AI tools are continually trained and upgraded with large codebases and user feedback that refines their responses. This ability to learn from past mistakes makes AI an incredibly powerful tool that will continue to expand its capabilities in the future.

info

Complete the AI Cloud Skills Challenge to build on you AI application development skills with paired programming using generative AI and GitHub Copilot.

AI and the Future of Application Development

The rapid advancements in AI and machine learning technologies point to an exciting future for application development. 

In “The Future of Applications,” software developer Mike Cann illustrates how LLMs might revolutionize application development. Cann that suggests fully-autonomous coding—AI-generated applications created entirely from prompts—may supersede traditional hand-crafted applications. Dynamically generated user interfaces could offer personalized and customizable experiences while we can design APIs for machine consumption and efficiency rather than human readability. Furthermore, we can store data in less structured, more adaptable, and flexible forms. 

AI is already making application development more accessible. With AI handling the complex, technical aspects of development, less-technical individuals can take a greater role in building applications. Consequently, AI can potentially democratize software engineering. 

Intelligent apps will continue to evolve, becoming more efficient, adaptable, and capable of learning from user interactions. Similarly, AI will inevitably become integral in every stage of app development.

Summary

AI has already created a new development paradigm—one in which machine intelligence and human creativity come together to produce innovative, efficient, and robust applications. AI already assists us at every stage of app development, from design and architecture to coding and debugging. 

Developers who embrace AI tools and integrate them into their processes can enhance their productivity, improve the quality of their applications, and expand the possibilities of their application development.  

The future of development is automated and AI-driven, and it’s here today. Getting started is easy—try an AI-powered development tool like GitHub Copilot to see how AI-assisted pair programming can enhance your productivity and code quality.

Exercise

- - +
Skip to main content

11 posts tagged with "learn-live"

View All Tags

· 9 min read
It's 30DaysOfIA

In the last article, we created an Intelligent App that leverages Azure AI Vision to analyze images and extract data. Develop an API to perform optical character recognition (OCR) on uploaded images and testing this API locally.

In this article, we will deploy the web API to the cloud platform using Azure Kubernetes Service.

What We'll Cover:

  • Understanding Azure AI Vision and Azure Kubernetes Service
  • Build a Python Web API to perform OCR
  • Test the API locally

image depicting an Intelligent App using AI

Jumpstart Your AI Journey: Building Your First Intelligent App with Azure AI and AKS (2)

In the previous article we explored the creation of an Intelligent App that leverages Azure AI Vision to analyze images and extract data. We learned how to build a Python Web API to perform OCR on uploaded images and subsequently test this API locally.

In this article we will use Azure Kubernetes Service (AKS) to develop, publish, and maintain our app in the cloud on Azure.

Let’s get started!

Prerequisites

To follow this tutorial, ensure you have completed Jumpstart Your AI Journey: Building Your First Intelligent App with Azure AI and AKS (1).

Pushing a Container Image to Azure Container Registry (ACR)

To start, open your CLI or terminal and type the following command:

az login

Follow the instructions displayed on your browser to enter your Azure credentials.

Once authenticated, you’ll initiate a secure connection between your local environment and Azure. This process grants you access to cloud services and resources.

Next, type the command below in your terminal to set up a new Azure Container Registry (ACR) to store your container images:

az acr create --resource-group computer-vision --name <name-of-azure-container-registry> --sku Basic

Remember to replace <name-of-azure-container-registry> with your container registry name. The name must be unique within Azure and comply with these rules.

The command above creates an Azure Container Registry (ACR) in the computer-vision resource group under the Basic SKU. This ACR is your secure and private repository for storing container images within Azure.

Next, log in to the registry with the following command:

az acr login -n <name-of-azure-container-registry>

The az acr login command above lets you securely authenticate and access the specified ACR without providing their Azure credentials each time.

Now, run the following command in your terminal. It will display the endpoint URL to log in to and interact with the ACR for pushing and pulling container images.

az acr show --name <name-of-azure-container-registry> --query loginServer --output table

This returns an endpoint URL as follows:

Result
----------------------------------
<name-of-azure-container-registry>.azurecr.io

Now, run the following command to show all container images, their repository, tags, and size:

docker images
REPOSITORYTAGIMAGE IDCREATEDSIZE
intelligent-applatesta7bf9f75361716 hours ago197MB

Tags are necessary to push Docker images to a remote registry like Azure Container Registry. They also let you differentiate between different versions of the same image and upload or download the one you want.

Run the following command to tag your Docker image:

docker tag intelligent-app <name-of-azure-container-registry>.azurecr.io/intelligent-app:v1

Then, run the docker images command again to check your tagged image:

docker images
REPOSITORYTAGIMAGE IDCREATEDSIZE
intelligent-applatestc52168039265About a minute ago197MB
<name-of-azure-container-registry>.azurecr.io/intelligent-appv1c52168039265About a minute ago197MB

Now run the following command so Docker can securely upload the image to your Azure Container Registry:

docker push <name-of-azure-container-registry>.azurecr.io/intelligent-app:v1

Once we've deployed the image to the container registry, AKS can access it during deployment.

info

Watch Episode 03 for the Learn Live session to experience a guided session on how to build, test and deploy an end-to-end intelligent app solution.

Deploying the Intelligent App on Azure Kubernetes Service (AKS)

Before we deploy our Intelligent App to AKS, we need to provision an AKS cluster and define Kubernetes manifests.

To provision an AKS cluster to host our application, we specify the desired configurations for the cluster, such as the number of nodes, node size, and networking options. But first, download and install the Kubernetes command-line tools (kubectl), a client credential plugin implementing Azure authentication:

az aks install-cli

If you’re using Linux, review this tutorial. Then run the following:

sudo az aks install-cli

Next, run the following command in your terminal to enable access to the networking-related resources and services provided by the `Microsoft.Network`` namespace in Azure:

az provider register --namespace Microsoft.Network

Now, we must create an AKS cluster. Run the following command to create an AKS cluster named aks-intelligent-app in the computer-vision resource group.

az aks create --resource-group computer-vision --name aks-intelligent-app --node-count 1 --generate-ssh-keys

The command above specifies the target resource group: computer-vision. The node pool is configured with one virtual machine (VM), and the secure shell (SSH) keys for secure node access are generated automatically.

Next, run the following command to update the AKS cluster you created by attaching it to your ACR. Doing so allows the AKS cluster to pull container images from the specified ACR when deploying workloads to the cluster.

az aks update -n aks-intelligent-app -g computer-vision --attach-acr <name-of-azure-container-registry>

Then, run the command below to configure kubectl to work with our AKS cluster in the computer-vision resource group.

az aks get-credentials --resource-group computer-vision --name aks-intelligent-app

The command above retrieves the necessary credentials and context information required for kubectl to communicate with the AKS cluster.

We still have to define the Kubernetes manifests written in YAML, describing the desired state of our application deployment, including containers, networking, and scaling rules. We’ll prepare these manifests, including Deployment and Service configurations, to define how our application should be deployed and exposed.

Create a folder named Deployment in the project root directory. Next, create two files in the deployment folder: deployment.yml and service.yml.

Add the following configuration to the deployment.yml file, replacing the <name-of-azure-container-registry> placeholder with your registry’s name:

apiVersion: apps/v1
kind: Deployment
metadata:
name: intelligent-app
spec:
replicas: 1
selector:
matchLabels:
app: intelligent-app
template:
metadata:
labels:
app: intelligent-app
spec:
nodeSelector:
kubernetes.io/os: linux
containers:
- name: intelligent-app
image: <name-of-azure-container-registry>.azurecr.io/intelligent-app:v1
resources:
limits:
memory: 512Mi
cpu: "1"
requests:
memory: 256Mi
cpu: "0.2"
ports:
- containerPort: 5000
env:
- name: FLASK_DEBUG
value: "1"
- name: VISION_KEY
value: <THE-KEY-1-VALUE-FROM-YOUR-AZURE-AI-SERVICE>
- name: VISION_ENDPOINT
value: <THE-ENDPOINT-VALUE-FROM-YOUR-AZURE-AI>

Additionally, edit the VISION_KEY and VISION_ENDPOINT environment variables above according to the API key and endpoint of your Azure AI Services instance.

Then, add the following configuration to the service.yml file:

apiVersion: v1
kind: Service
metadata:
name: intelligent-app-service
spec:
type: LoadBalancer
ports:
- protocol: TCP
port: 80
targetPort: 5000
name: port5000
selector:
app: intelligent-app

Now, we’ll deploy our application using kubectl. Applying the Kubernetes manifests creates the necessary resources and deploys our containerized application to the AKS cluster.

First, change the terminal to the deployment folder:

cd Deployment 

Then, run the following command to create or update Kubernetes resources defined in the deployment.yml file:

kubectl apply -f deployment.yml

Create a Kubernetes Service resource in a Kubernetes cluster based on the configuration defined in the service.yml file using the code below:

kubectl apply -f service.yml

Once you’ve applied the resource definition and the service configuration contained in the deployment.yml and the service.yml files, open the aks-intelligent-app Kubernetes Service in the Azure Portal, select Workloads under Kubernetes resources on the sidebar, and find the deployment named intelligent-app. It must have the status “Ready 1/1”. If you encounter an issue with this status, check out these troubleshooting resources.

image of selecting the deployment in Azure Portal

Testing the Intelligent App on AKS

To test the app on AKS, first, run the command below:

kubectl get services

This command lists the Services and their corresponding details, including the Service name, cluster IP address, external IP, and ports.

NAMETYPECLUSTER-IPEXTERNAL-IPPORT(S)AGE
intelligent-app-serviceLoadBalancer10.0.77.6020.121.76.15380:30936/TCP47s
kubernetesClusterIP10.0.0.1<none>443/TCP14m

The output above shows a Kubernetes Service named intelligent-app-service with a type set to LoadBalancer. It’s reachable from within the cluster using the cluster IP 10.0.77.60 and accessible externally via the external IP 20.121.76.153 on port 80 (mapped to port 30936).

Note: Your set of IP addresses will differ. Remember to use your unique external IP address when testing with Postman.

To test the deployed app, go to Postman, replace the URL with the external IP of the Kubernetes Service you just deployed, and click Send:

mage of sending the app in Postman

As we can see, our Intelligent App has successfully deployed to AKS and is functioning on the cloud as expected.

Exercise

Next Steps

In this two-part article, we explored the creation of an Intelligent App that leverages Azure AI Vision to analyze images and extract data. We learned how to build a Python Web API to perform OCR on uploaded images and subsequently deploy this API via Azure Kubernetes Service.

Besides OCR and Image Analysis, you can continue exploring Azure’s vast array of services and experiment further with Azure AI and AKS by applying various practical uses to your Intelligent Apps, including natural language processing, speech recognition and synthesis, sentiment analysis for customer feedback, and automated content moderation.

+ + \ No newline at end of file diff --git a/30daysofIA/tags/learn-live/page/5/index.html b/30daysofIA/tags/learn-live/page/5/index.html index b87013fc46..28b7adbb42 100644 --- a/30daysofIA/tags/learn-live/page/5/index.html +++ b/30daysofIA/tags/learn-live/page/5/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "learn-live" | Build Intelligent Apps On Azure +11 posts tagged with "learn-live" | Build Intelligent Apps On Azure @@ -14,13 +14,13 @@ - - + +
-

9 posts tagged with "learn-live"

View All Tags

· 10 min read
It's 30DaysOfIA

Explore real-world examples of how Intelligent Apps revolutionize logistics, retail, finance, manufacturing, tech, and medicine.

What We'll Cover:

  • Real-world businesses revolutionizing operations with Intelligent Apps
  • High level architecture for industry scenarios
  • Implementation architecture overview across industries

A decorative header image for the blog

Real-world Success Stories

According to IBM’s 2022 Global AI Adoption Index, 35% of surveyed companies already use AI in their business, and 42% are exploring the idea. Organizations lagging behind the AI curve will quickly fall behind early adopters, who are already enjoying its benefits and boosting their investment in this technology.

In the first part of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” we explored how organizations can use AI-powered apps, or intelligent apps, to transform their operations, decision-making, and customer experiences, leaping ahead of the competition.

Intelligent apps significantly improve operational efficiency and customer engagement across industries like logistics, retail, finance, manufacturing, tech, and healthcare. In this article, we’ll spotlight real-world success stories of businesses revolutionizing operations with intelligent apps using Microsoft Azure’s app, data, and AI services.

info

Register for the Learn Live on September 21 for a live guided experience with SMEs on how to build end-to-end solution architecture for real world implementation of Intelligent Apps.

Streamlining Operations: Intelligent Apps in the Airline Industry

Aerospace pioneer Airbus leveraged intelligent apps to streamline its operations and innovate its services with two ground-breaking modernizations that use Azure apps, data, and AI services to reimagine pilot training and predict aircraft maintenance issues. 

Aviation technology’s rapid evolution compelled Airbus to reconceive their approach to training pilots. The aircraft manufacturer created a pilot training chatbot by combining Azure AI Services with Speech to Text and Text to Speech. The bot helps trainee pilots navigate over 6,000 pages of technical information. Pilots can ask questions verbally for quick answers, aiding their understanding of dense information and speeding up training. And when Airbus needed to accommodate its military consumers, deploying its Kubernetes cluster to a disconnected, “air-gapped” environment met the strict security requirements of military aircraft training.

A chatbot could look like the following diagram as Azure services work together to create a conversational experience. Its components could include security and governance, bot logic and user experience (UX), bot cognition and intelligence, data extract, transfer, and load (ETL) operations, quality assurance and enhancements, logging, and monitoring and reporting.

A diagram of a conversational experience enabled by Azure services

Airbus also used AI to improve its aircraft maintenance and safety. They deployed AI Anomaly Detector to gather and analyze telemetry data from multiple flights, enabling more profound insights into wear and tear on military aircraft operating in harsh conditions. This AI application helps Airbus better understand each airplane’s health. Airbus can predict and fix potential problems before they occur, improving the aircraft’s safety and operational readiness.

AI and Azure are changing how organizations like Airbus operate, even in strict, highly regulated markets with stringent security requirements. And by streamlining pilot training and staying ahead of aircraft maintenance, Airbus helps its military and commercial clients reliably deliver their goods across the globe, maintaining their critical supply chains.

Boosting Customer Service: Intelligent Apps in the Retail Industry

The retail industry has been among the first to adopt and benefit from intelligent apps. For example, the leading American used car retailer, CarMax, uses intelligent apps to transform the car shopping experience.

Before embracing AI, CarMax faced a significant challenge: Manually creating text summaries was time-consuming and labor-intensive. CarMax decided to leverage Azure OpenAI Service to automatically generate car research pages, offering customers valuable insights while enhancing the website’s search engine rankings.

This AI-driven approach has significantly streamlined the process, achieving in a few months what would have taken years manually. Also, the AI system condenses thousands of customer reviews into a few readable sentences, offering potential buyers a quick overview of others’ experiences.

CarMax has improved their customer experience while boosting operational efficiency through AI adoption. CarMax’s editorial staff focuses on creating strategic, longer-form content with the time saved, enhancing productivity, and driving more website traffic.

CarMax’s success story serves as a testament to the potential of intelligent apps in the retail industry.

Enhancing Cybersecurity: Intelligent Apps in Finance

Intelligent apps also impact the highly regulated financial services sector. Swift, a leading infrastructure provider for financial messaging services, uses intelligent apps to enhance decision-making and combat financial crime.

The massive growth of cross-border transactions and instant payment networks led to a surge in financial fraud that can cost the industry hundreds of billions of dollars every year. So, Swift and Microsoft partnered to build an anomaly detection model using federated learning techniques with Azure Machine Learning and Azure confidential computing.

Microsoft Purview helped govern data, as the federated learning method partially trained each model in a silo before aggregating them. This approach enabled Swift and Microsoft to develop a highly accurate model without copying or moving data from Swift members’ secure locations, ensuring the highest level of security and privacy.

Financial organizations can now deploy this trained ML model to run a real-time fraud detection system, lowering the cost of financial fraud.

Organizations can deploy a fraud detection system like the following example on Microsoft Azure. The system ingests data from streams or storage, runs analytics, and uses ML to detect fraud and store results. It then alerts users and other systems to the security issue.

A diagram of an example fraud detection system

Swift provides the innovative model to its banking partners, who can share data to further train the model. Azure confidential computing securely runs new models on current data to accumulate insights, generating an ongoing learning cycle. This iterative approach strengthens the global detection of fraudulent financial transactions through an increasingly faster and more accurate model. 

The collaboration between Swift and Microsoft demonstrates how combining various Azure services creates Intelligent Apps to enhance decision-making in finance. These apps solve real problems at an unprecedented global scale. 

Enhancing Efficiency: Intelligent Apps in Manufacturing

3M, known for Post-it Notes and other innovative products, wielded the power of Intelligent Apps to standardize, automate, and accelerate its sales forecasting. The company needed a unified, automated approach to replace its multiple manual methods.

It adopted Azure Machine Learning and other Azure AI Services to develop, train, and implement 1,500 custom models for each region and division, replacing manual methods. This solution delivers timely finance, sales, and marketing insights while freeing teams from time-consuming manual data manipulation tasks.

3M used Azure Machine Learning, automated machine learning (AutoML), and the Many Models Solution Accelerator to train and score numerous machine learning models in parallel. This approach significantly shortened the company’s development cycle time.

Then, 3M integrated Microsoft Power BI into their solution to compile and visualize results more effectively. Executives now have an unbiased and unfiltered view of data for better decision-making. With its improved ability to forecast sales, 3M can focus on manufacturing the products it needs efficiently.

3M’s success demonstrates how combining various Azure services can enhance efficiency in the manufacturing industry. These apps improve operational processes while enabling employees to focus on higher-level tasks. Strategic decisions are more targeted, and business conversations are more meaningful.

Driving Innovation: Intelligent Apps in the Technology Industry

As expected, the tech industry is at the forefront of leveraging Intelligent Apps to drive innovation. Elastic saw an opportunity to use intelligent apps to optimize its search services and meet customer needs more effectively. The company built a managed, secure, and scalable solution for its customers, Elastic Cloud, by leveraging Microsoft Azure.

Elastic Cloud uses Azure Machine Learning and other Azure AI Services to automate many basic management tasks, such as backing up data, upgrading, and scaling deployments. This approach enables Elastic’s customers to focus less on managing the software and more on serving their own clients. Customers can quickly deploy and manage Elastic Cloud on Kubernetes (ECK) using Azure Kubernetes Service.

As a result, Elastic’s cloud business is growing twice as fast as its on-premises solutions. Enterprise search users have reported significant improvements in system availability and employee satisfaction.

Additionally, Elastic incorporated Azure OpenAI Service into its Elasticsearch Relevance Engine (ESRE), providing more relevant answers to search queries. ESRE enables text, vector, and hybrid search, offering a more effective and accurate response to queries.

Elastic’s success story demonstrates how combining various Azure services can create intelligent Apps to drive innovation in the tech industry. These apps help improve operational processes and deliver better results quickly.

Revolutionizing Healthcare: Intelligent Apps in Medicine

Intelligent apps also drive transformative changes in the healthcare sector. Cambridgeshire and Peterborough NHS Foundation Trust, a healthcare provider in the United Kingdom, gradually shifted their on-premises systems to the cloud using Azure, improving flexibility, and lowering costs. They needed a solution for their electronic patient record (EPR) system that integrates 23 years of data across multiple systems and formats.

The Trust used Microsoft Azure and Azure Cognitive Search, including semantic search for understanding intent and contexts and cognitive capabilities for extracting insights from data, to standardize and automate data management. This approach made legacy, archive, and live data more discoverable, saving clinicians’ time. The Trust’s solution searches through millions of clinical documents in seconds, even recognizing handwritten notes without needing a separate optical character recognition (OCR) solution to extract text from images (Azure Cognitive Search automatically does it). Clinicians can now uncover previously inaccessible insights, enabling them to offer more personalized care.

The Trust also used Integration Services tools such as Azure Logic Apps to create workflows without writing code and Event Grid to connect Azure and third-party services through a publisher-subscriber model. They switched on a live feed from their new EPR system to Azure to feed real-time information into other applications. Researchers can now access anonymized data to fuel medical innovations beyond the NHS.

The healthcare provider also invested heavily in Power BI’s data visualization and analytical functions to sift through massive amounts of information. This level of data accessibility was previously impossible with their on-premises system. The new insights have significantly improved clinicians’ efficiency for better patient care.

Data accessibility and security are critical in the healthcare industry. Intelligent Apps empower organizations like the Trust to revolutionize medical care. AI capabilities helped the Trust improve its operational efficiency and user experiences, setting a new standard for healthcare in the digital age.

Summary

AI is driving transformation and real-world successes across industries. Intelligent apps are reshaping our world by streamlining operations in manufacturing and logistics, enhancing customer experience in the retail sector, improving decision-making in finance, driving innovation in tech, and revolutionizing healthcare.

As organizations adopt AI, they’re innovating in ways that were once too difficult, expensive, or outright impossible. Companies that wait to embrace intelligent apps risk the chance of falling behind. By embracing intelligent apps and leveraging the capabilities of Azure, you can harness the transformative potential of AI to remain on the cutting edge of technology.

Exercise

- - +
Skip to main content

11 posts tagged with "learn-live"

View All Tags

· 10 min read
It's 30DaysOfIA

Explore how digital natives and Independent Software Vendors (ISVs) can transform their business operations and generate higher value for customers with generative AI and intelligent apps.

What We'll Cover:

  • Potential of Generative AI in application development
  • Challenges with Generative AI
  • Integrating Generative AI with business operations

image of generative AI and intelligent apps used in business

Unleashing Business Transformation: How Digital Natives Can Leverage Generative AI

Generative AI is no longer a technological flight of fancy. It’s here, and it’s rewriting the rules of business innovation. 

This type of AI leverages machine learning algorithms to create new data with similar characteristics to a given dataset. It goes beyond text generation using generative pretrained transformer (GPT) models, covering an array of applications that span from image synthesis to translations and more. It also powers a new class of intelligent applications, which combine AI capabilities with traditional software functions for enhanced user experiences and advanced business solutions. 

Generative AI is transforming how digital natives and independent software vendors (ISVs) operate. Generative AI offers new possibilities for creating unique business value, enhancing customer experiences, and driving growth. 

It plays a critical role in applications, improving functionality, aiding decision-making, and automating complex tasks to deliver dynamic, uniquely personalized user experiences. Generative AI also enables and automates data-driven outcomes, functions, and features.

Integrating AI capabilities into customer-facing apps and turning them into intelligent apps benefits both the customer and the business, as we discussed in Demystifying Intelligent Applications: Leveraging AI in App Development. You get faster product development workflows and better business intelligence for sales and marketing, while your users get more natural and context-aware features and interactions. 

That said, to realize generative AI’s potential, you need some strategy and knowledge of how and where it fits into your current business and operations. We’ll examine this technology’s potential to transform business operations, its risks, and rewards, and how to integrate it into your existing operations. 

Unpacking the Potential of Generative AI

Generative AI is creating new opportunities in the digital playing field. It empowers businesses to generate insights, design products, and automate processes in ways and at speeds that were previously unimaginable. You can now predict customer behavior more accurately, offer highly personalized user experiences, and prototype innovative ideas rapidly. 

For example, Microsoft used generative AI to reinvent web search in the new AI-powered Bing and Edge. The search experience has evolved from a solitary user flow of typing in keywords and scrolling through results. It’s now a conversational experience where Bing uses context to offer better quality and more personalized search queries and results. 

Similarly, Microsoft harnessed generative AI to transform its productivity suite into intelligent apps. Through Copilot for Microsoft 365, PowerPoint now empowers users to tell their stories by using DALL-E to generate images effortlessly. It also helps rephrase text-heavy slides to be more straightforward. Outlook now assists users to compose more effective emails, enabling better communication.

These are just some examples of how AI powers day-to-day work and operations. 

Businesses that ignore generative AI miss out on untapped market opportunities, letting competitors gain an edge in the digital race. By seeking ways to rethink assumptions and reinvent existing approaches, you can effectively use generative AI to create new opportunities, open products to new use cases and markets, unlock new growth and customer satisfaction levels, and increase your revenue.

info

Register for the intelligent apps webinar on Driving Business Value by Modernizing with Cloud-Native & AI with Microsoft and Forrester on September 26.

Explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries. 

The Crucial Role of Generative AI in Business Operations

Generative AI can be a powerful catalyst for business transformation. Let’s quickly tour some ways it can redefine business operations. 

Product development is traditionally a long and painstaking process. Now, tools like GitHub Copilot and TestPilot use generative AI to make coding and testing software easier, faster, and more robust. 

Similarly, machine learning as a service technology, such as Azure Machine Learning, democratizes the availability of machine learning. It helps collect, clean, and train models across datasets, making engineering and analyzing data more efficient and accessible.  

Generative AI also simplifies the process of creating intelligent apps. Previously, simulating thousands of prototypes, predicting their performance, and narrowing down to the most viable options could take weeks, months, or even years of iteration and data analysis. With generative AI, this process can be as quick as days, hours, or perhaps minutes. 

Customer experience is also ripe for transformation. Personalized recommendations drive engagement and sales, and generative AI can scale that personalization to every customer interaction using comprehensive and continually updated data. For instance, Microsoft leverages AI to enable personalization in Dynamics 365 Customer Insights. Using this tool, you can ask questions about your customers in natural language and gain valuable and actionable insights into your customer base. 

Lastly, marketing and sales can benefit from generative AI. For example, identifying current and future key markets and emerging trends helps companies stay ahead of the curve. Tools like Copilot in Power BI help you quickly understand and uncover insights about your business, enabling your team to craft targeted campaigns, create engaging content, and optimize your business strategy. 

Leveraging this full generative AI potential requires the right tools. GPT-4, for instance, can be instrumental in realizing effective business transformation. It excels in understanding context, generating human-like text, and learning from minimal data input, making it a formidable tool in any digital native's arsenal. Platforms like Azure OpenAI Service bring GPT-4’s power to your fingertips.

However, as with any powerful technology, it’s unwise to treat generative AI as a perfect solution. You must know the challenges and risks of not leveraging it effectively, including technical and human-factor considerations. 

Without generative AI, your company risks missing the opportunity to please customers via personalization, as discussed previously. You’ll miss sales opportunities via business intelligence insights, limiting your ability to grow revenue and keep up with the competition. 

One challenge of leveraging generative AI’s full potential is finding the right tools. Fortunately, you can implement generative AI in your organization without investing in infrastructure, computational power, and expertise. Platforms like Azure OpenAI Service and Azure AI Services make AI easily accessible. They also work with Azure compute and data services like Azure Functions and Azure Kubernetes Service (AKS), enabling you to build intelligent apps easily and quickly. 

However, generative AI can’t do everything. It may need human expertise and intervention to use it fully. 

For example, generative AI can create unique data. However, the data may have limited diversity and complexity. The AI may need help recognizing additional considerations or requirements specific to a problem. It may also lack context due to various factors, such as the information’s freshness. Also, since it’s an emerging field that’s potentially worth $4.4 trillion, we’re still learning about the nuances and potential regulatory impact of generative AI. 

Perhaps the most crucial and challenging aspect of adopting generative AI is an organization’s required cultural shift. Your teams must learn how to use AI as your company fosters an understanding of AI’s potential and limitations. 

Begin integrating AI literacy into your company’s DNA. Knowledge of its potential and the readily available tools can inspire your teams to incorporate generative AI into their daily workflows and business operations quickly, improve productivity, and deliver superior customer experiences. 

No company wants to be outpaced by its competitors. You can balance generative AI’s potential with your business operations by staying aware of and adapting to the dynamic AI landscape.

Achieving Harmony: Integrating Generative AI with Existing Business Operations

Integrating generative AI into existing business operations doesn’t have to be disruptive. With the correct strategy, you can do so seamlessly. Your goal should be to augment and enhance your operations, not disrupt and replace. 

For example, Microsoft Cloud technologies leveraged AI to relieve healthcare organizations’ challenges, such as talent shortage and workforce burnout. Providers automated their workflows, invested in specific applications and connectors, and enhanced support for data and operations. 

Azure AI Services, such as text analytics for health, enable healthcare organizations to understand their largely unstructured and untapped medical data. Project Health Insights leans on AI to help healthcare professionals gain actionable insights and inferences from patient data. 

Azure Health Bot, an Intelligent App, empowers organizations to build and deploy legally compliant, powerful conversational healthcare experiences to patients. 

A successful AI integration like this begins with understanding. Knowing what generative AI can do and setting realistic expectations can help you identify where and how AI can best serve your operations. 

Next, you can foster a culture of AI literacy across teams, speeding up adoption by helping them understand how it can aid their work. The more familiar your team is with AI and its possibilities, the smoother the transition. Training and education programs play a pivotal role here. 

Finally, integration requires a systematic approach. It’s not about overhauling everything at once — it's about identifying areas where AI can add the most value, then gradually experimenting and scaling its use. Successful integration stories often feature a phased approach, starting small and growing steadily. 

As we move toward a future where generative AI is a staple in business operations, remember that every journey is unique. Although there’s no one-size-fits-all solution, a strategic approach and a keen understanding of your business needs will help you navigate this new landscape successfully.

Conclusion

Generative AI offers plenty of opportunities for businesses that embrace it. It’s fueling the next generation of intelligent applications, transforming everything from product development to customer engagement to marketing. This technology is the key to unlocking unprecedented growth and innovation. 

While the journey may have challenges, the potential rewards make generative AI worth exploring. As generative AI advances, Azure AI’s broad range of tools and services will help you navigate this fascinating field.

Exercise

  • Watch Episode 02 of the serverless edition Learn Live session to learn how to build, test and deploy an end-to-end intelligent app solution.
  • Complete the Cloud Skills Challenge to build on your AI skills.
  • Register for Ask the Expert: Azure Functions on September 26 for a live Q&A with the Product Team on how to build AI apps using serverless computing.
+ + \ No newline at end of file diff --git a/30daysofIA/tags/learn-live/page/6/index.html b/30daysofIA/tags/learn-live/page/6/index.html index 7a8fc31b33..8ebc23acbf 100644 --- a/30daysofIA/tags/learn-live/page/6/index.html +++ b/30daysofIA/tags/learn-live/page/6/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "learn-live" | Build Intelligent Apps On Azure +11 posts tagged with "learn-live" | Build Intelligent Apps On Azure @@ -14,13 +14,13 @@ - - + +
-

9 posts tagged with "learn-live"

View All Tags

· 8 min read
It's 30DaysOfIA

Let’s start with understanding the power of intelligent applications.

What We'll Cover:

  • What are intelligent applications?
  • Categories of Intelligent Apps
  • Breaking down the role of AI in intelligent apps

A decorative header image for the blog

Demystifying Intelligent Applications: Leveraging AI in App Development

Until recent years, when you heard the term “artificial intelligence” (AI), you might have pictured autonomous robots from sci-fi films. Now, perhaps your mind jumps to big data and machine learning (ML). Maybe you’re even thinking about the latest ChatGPT plugins and transformer architectures. Today, AI is more than these imaginations: it’s a reality increasingly woven into our day-to-day routines through intelligent applications.

Intelligent apps combine cloud-scale compute, data, and predictive or generative AI. These applications help you automate tasks and make data-driven decisions, aiding in leveraging business intelligence to gain meaningful, practical outcomes. 

For developers, the AI landscape isn’t an abstract complexity. It’s a real, tangible opportunity for evolving traditional applications, making them “intelligent,” and delivering more value to users. And with tools like Azure Kubernetes Service, Azure Cosmos DB, and Azure AI Services, stepping into the world of intelligent apps isn’t a steep climb but a steady trek across a manageable learning curve.

The Three Categories of Intelligent Applications

Before we start building, it’s critical to understand what makes an app “intelligent.” It’s not a universal definition so much as an identifier for apps that fall into one or more of the three primary categories: 

  • Outcome-based — This designation represents the broadest category of intelligent apps, whose principal value for users is the intelligent outcomes they provide. These apps process data to provide insightful results that enhance decision-making or help with tasks like trend prediction, resource management, and customer relationship management.

  • Functionality-based — This type of intelligent app takes AI integration a degree further, integrating AI as a function of the app to create or process its outputs, enhancing the app’s overall interactivity and usefulness.

  • Feature-based — This is the narrowest category, in which the app’s AI/ML component is one of its core features and primary selling points

Let’s dive more deeply into each category.

Outcome-Based Apps: Intelligent Outcomes Drive Success

Outcome-based applications are all about the results provided to the user. They function beyond merely processing data to offer insightful outcomes that drive success.

Think of a personal fitness tracker app that uses AI to analyze the user’s activity and provides actionable recommendations for achieving a goal. This type of app not only tracks your steps, sleep patterns, and heart rate but uses that data to provide you with personalized health advice. It helps you make informed lifestyle choices — the “intelligent” outcome.

Another example is a social media marketing app that uses AI for time-saving data analysis, task automation, and trend prediction. AI-based sentiment analysis can gauge customer perception while natural language processing (NLP) accurately interprets customer inquiries. Task automation can schedule posts and automate responses to common customer queries, and trend forecasting helps model and predict customer behavior to further personalize targeted promotional efforts. Building this type of intelligence into your apps provides cost-effective, actionable insights that guide sales and marketing strategies.

Functionality-Based Apps: AI-Driven Features Enhance User Experiences

As the name suggests, functionality-based applications integrate AI into how the app functions, but the end user drives the action and outcome. In this category of apps, AI takes a more active role in the user’s interaction by allowing them to use AI-created or processed output. These apps leverage AI technologies like Azure AI Services for NLP, image recognition, and pattern recognition to improve user experience.

Consider a music app that generates personalized playlists based on your listening habits or your smartphone’s camera app that uses AI to recognize faces and optimize photo settings. Even more advanced are the language translation apps that translate text from one language to another and can even understand and maintain the context and subtleties of the conversation. They use NLP to accomplish these feats.

Similarly, consider the latest e-commerce apps, whose AI-driven functionalities enable customers to try on clothes virtually, significantly enhancing the online shopping experience.

Feature-Based Apps: Integrating Advanced AI/ML Components

Finally, there are feature-based intelligent apps. These apps incorporate advanced AI/ML components like neural networks, internal large language models (LLMs), and sophisticated algorithms as core features for the user. In this type of application, AI is the primary feature for users to engage with and the primary selling point for the app.

Some examples of feature-based applications are chatbots and virtual agents built using Azure Bot Service. These robo-representatives use AI to provide more natural and human-like conversation, balancing superior efficiency with a friendlier, more personalized user experience. The new Bing is a prime example of this implementation, integrating AI to give users a knowledgeable copilot for web searches.

Similarly, OpenAI’s ChatGPT allows users access to its state-of-the-art language models available as Microsoft’s Azure OpenAI Service. It leverages the power of the highly sophisticated GPT-3 LLM to perform tasks like drafting emails, writing code, answering general knowledge questions, translating between languages, and tutoring in numerous subjects.

These apps don’t just use AI to provide outputs or improve user experiences: They push the boundaries of what’s possible in app development by integrating AI/ML components.

info

Register for the live Q&A Ask The Expert session on September 26 with the Azure Functions product engineering team.

Breaking Down the Role of AI in Intelligent Applications

Intelligent apps reflect the merging of traditional software design with the dynamic capabilities of AI. Let’s explore how AI integrates and amplifies these applications.

At its core, AI augments applications with the capacity to self-learn and self-improve. It allows you to create applications that predict, react, and adapt to a constantly changing environment. In the power grid management system for a major city, an AI-infused application can forecast power usage trends, react to anomalies, and adapt to sudden demand surges, ensuring optimal efficiency and preventing blackouts.

But AI’s role doesn’t end at prediction or decision-making: It also automates tasks. In enterprise solutions, it has moved beyond simple automation to encompass intelligent process automation (IPA). In this arena, AI algorithms understand, learn, and then automate entire digital workflows. For developers, this is a fertile ground for creating applications that can dramatically improve the operational efficiency of businesses.

Even more, AI substantially aids in development tasks. For example, there’s GitHub Copilot, an AI-powered code assistant that offers suggestions based not only on syntax but on development best practices and the intent of the code. AI has evolved from a tool you use to build apps to a partner that helps in creating them. 

Using AI to build intelligent apps isn’t about throwing out what’s worked before. It’s about enhancing the components that have the most impact and achieve the best results. Incorporating AI in apps elevates their predictive abilities, streamlines complex tasks, and even aids in the development and creation process. When you build intelligent apps, you don’t just address current problems. You anticipate future challenges and craft solutions that adapt over time.

Summary

As developers the power of AI is in our hands, and it’s our responsibility to harness its full potential. Each category tells the story of AI's transformative potential, from applications that deliver intelligent outcomes to those with AI capabilities to enrich the user experience and those pushing boundaries with advanced AI/ML technology.

Accessible and robust platforms like Azure AI can simplify the process of developing Intelligent Applications. Whether you’re refining an existing app or building a new one, just entering the world of AI, or already familiar with the landscape, take a moment to appreciate the potential that AI integration offers. You’re embracing this exciting frontier and shaping the future of app development. 

Exercise

  • Complete the Cloud Skills Challenge to build on your apps and AI skills.
  • Watch Episode 01 of the Serverless Edition Learn Live session to learn how to build an end-to-end intelligent app solution.
- - +
Skip to main content

11 posts tagged with "learn-live"

View All Tags

· 9 min read
It's 30DaysOfIA

In the part of our series on intelligent apps, we’ll explore how AI is transforming application development, from design and architecture to building.

What We'll Cover:

  • Revolutionizing application development using AI
  • Infusing AI in application design architecture
  • AI Assisted Pair Programming
  • Future of App Development

A decorative image of application development with AI

Reimagining Application Development With AI: A New Paradigm

Artificial intelligence (AI) is impacting every industry. AI-powered development tools offer new functionalities and assist software developers throughout the development process, revolutionizing app development. From initial design and architecture to coding and debugging, AI reshapes how we approach application conception, creation, and iteration.

In part one of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” we explored the new breed of intelligent apps and how they use AI. In this article, we’ll see how AI technologies enhance developer toolkits and shape the future of app development, particularly through AI-enabled coding assistance and debugging.

The Dawn of Intelligent Apps: Revolutionizing Application Development

AI is more than just another addition to the development toolbox—it represents a revolution bringing unprecedented efficiency, innovation, and user engagement to our applications. 

What sets intelligent apps apart is how they can adapt and learn, helping us deliver unique, real-time user experiences.  

One example is an AI-driven health application that can track biometric data, predict potential health issues, provide personalized health advice, and even guide emergency services during a medical crisis. An AI-enabled language learning app that adapts to our learning speeds, recognizes our problem areas, and customizes its content to help us improve our language proficiencies is another example.  

Intelligent apps like these don’t just use data for recommendations—they can make critical decisions and provide sophisticated services that were previously impossible. 

For developers, intelligent apps mean we’re no longer just coding—we’re teaching applications how to think, adapt, and make decisions. AI is reshaping every step of the development journey, helping us to build more intuitive and user-focused products. 

Next, we’ll examine the impact of AI on application design and architecture and how AI-assisted “pair programming” changes software development.

info

Register for the FREE webinar on Intelligent Apps with Microsoft and Forrester on September 26 to explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries.

There will be a showcase of real-world use cases that demonstrate how AI can be seamlessly integrated into cloud-native environments driving tangible business value.

AI in Design and Architecture: Fueling Creativity and Efficiency

AI technologies help developers automate and streamline their processes and provide new ways to think about design.  

In traditional application development, the design and architecture phase can take significant time, effort, and expertise. For instance, adopting a monolithic architecture or a microservices-based design can significantly increase coding, testing, and debugging complexity. The app may even behave differently when hosted locally than in a deployed environment. It may take hours to track down an issue manually—time developers could spend elsewhere. 

Conversely, we can generate numerous design iterations in seconds with AI, each optimized to fulfill specific user needs and business goals. AI tools can quickly model various scenarios' impacts and software design patterns in the architectural design phase. It provides insights into performance and potential issues and suggests improvements. AI can also generate skeleton code, allowing developers to prototype different solutions or test specific components and technologies rapidly. By reviewing thousands of lines of code in seconds, AI allows us to focus on creating apps that serve users through feature development instead of searching for bugs.  

Integrating AI into the design and architecture phase can even extend our creative capabilities. By leveraging the power of generative AI—like large language models (LLMs)—and their broad knowledge and context going beyond software, we can better brainstorm apps and feature ideas. We can go beyond code and software architecture and get ideas about user experience, logo and branding, or marketing concepts. And as AI tools deliver insights and ideas we would have never considered; it encourages more innovation.

info

Watch Episode 01 of the Learn Live session to learn how to build an end-to-end intelligent app solution architecture.

The Power of AI-Assisted Pair Programming in Building Applications

AI-assisted pair programming is a collaborative coding approach where an AI system—like GitHub Copilot or TestPilot—assists developers during coding. It’s an increasingly common approach that significantly impacts developer productivity. In fact, GitHub Copilot is now behind an average of 46 percent of developers’ code and users are seeing 55 percent faster task completion on average. 

For new software developers, or those interested in learning new skills, AI-assisted pair programming are training wheels for coding. With the benefits of code snippet suggestions, developers can avoid struggling with beginner pitfalls like language syntax. Tools like ChatGPT can act as a personal, on-demand tutor—answering questions, generating code samples, and explaining complex code syntax and logic. These tools dramatically speed the learning process and help developers gain confidence in their coding abilities. 

Building applications with AI tools hastens development and provides more robust code. For example, GitHub Copilot helps us write new code accurately. It also quickly makes sense of existing code, suggests whole new lines or blocks of code, and even detects errors and proposes fixes. It’s like having a second coder working alongside us—one who never tires and is trained on billions of lines of code. 

However, it’s also important to acknowledge the limitations of AI. While generative AI-assistance technologies like GitHub Copilot are impressive, they’re still imperfect. They can sometimes produce erroneous, non-functional, or vulnerable code. They can also lack nuanced, domain-specific knowledge.  

So, although these tools are helpful, we can’t rely on them fully—these are programming aids rather than definitive solutions. AI-assisted pair programming is about augmenting the human developer’s skills, not replacing them.

Debugging and Improving Code With AI: Enhancing Application Quality

Identifying and correcting code errors is often time-consuming and challenging for developers. AI streamlines the debugging process by automatically detecting anomalies, suggesting fixes, and learning from previous bugs to prevent them from recurring. 

We can use generative AI to create unit tests from code snippets, from simple parameter checking and input validation to complex mocks of database and network services. Automated AI testing improves code coverage and robustness. It saves significant time compared to manually crafting unit tests. 

AI also revolutionizes code reviews by scanning and highlighting potential issues—like code patterns leading to a bug or a coding standards violation—and suggesting ways to simplify or refactor the code for readability and performance. This reduces human error and makes the code review process more efficient. Its benefits extend to small teams, where the availability of developers is crucial to deadlines, and large teams, where effective collaboration depends on readability and code consistency. AI further saves time by triaging issues so developers can prioritize the most mission-critical ones. 

Aside from improving code quality, AI is critical to understanding system behavior. Intelligent AI-assisted software development tools can monitor and audit system behavior, giving developers a better insight into how their code performs in different scenarios. AI can track patterns, identify inefficiencies, and provide alerts when they detect unusual behavior—enabling developers to enhance their code further. 

By predicting and identifying potential vulnerabilities, intelligent AI-assisted coding apps like Microsoft Security Copilot are pivotal in ensuring application functionality, reliability, and security. These AI tools are continually trained and upgraded with large codebases and user feedback that refines their responses. This ability to learn from past mistakes makes AI an incredibly powerful tool that will continue to expand its capabilities in the future.

info

Complete the AI Cloud Skills Challenge to build on you AI application development skills with paired programming using generative AI and GitHub Copilot.

AI and the Future of Application Development

The rapid advancements in AI and machine learning technologies point to an exciting future for application development. 

In “The Future of Applications,” software developer Mike Cann illustrates how LLMs might revolutionize application development. Cann that suggests fully-autonomous coding—AI-generated applications created entirely from prompts—may supersede traditional hand-crafted applications. Dynamically generated user interfaces could offer personalized and customizable experiences while we can design APIs for machine consumption and efficiency rather than human readability. Furthermore, we can store data in less structured, more adaptable, and flexible forms. 

AI is already making application development more accessible. With AI handling the complex, technical aspects of development, less-technical individuals can take a greater role in building applications. Consequently, AI can potentially democratize software engineering. 

Intelligent apps will continue to evolve, becoming more efficient, adaptable, and capable of learning from user interactions. Similarly, AI will inevitably become integral in every stage of app development.

Summary

AI has already created a new development paradigm—one in which machine intelligence and human creativity come together to produce innovative, efficient, and robust applications. AI already assists us at every stage of app development, from design and architecture to coding and debugging. 

Developers who embrace AI tools and integrate them into their processes can enhance their productivity, improve the quality of their applications, and expand the possibilities of their application development.  

The future of development is automated and AI-driven, and it’s here today. Getting started is easy—try an AI-powered development tool like GitHub Copilot to see how AI-assisted pair programming can enhance your productivity and code quality.

Exercise

+ + \ No newline at end of file diff --git a/30daysofIA/tags/learn-live/page/7/index.html b/30daysofIA/tags/learn-live/page/7/index.html index 13c100eef1..e8c43bd2a6 100644 --- a/30daysofIA/tags/learn-live/page/7/index.html +++ b/30daysofIA/tags/learn-live/page/7/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "learn-live" | Build Intelligent Apps On Azure +11 posts tagged with "learn-live" | Build Intelligent Apps On Azure @@ -14,14 +14,13 @@ - - + +
-

9 posts tagged with "learn-live"

View All Tags

· 4 min read
It's 30DaysOfIA

This Fall focus on building intelligent apps using AI and cloud-native technologies. #FallForIntelligentApps brings to you a learning journey to build your skills on creating differentiated experiences while modernizing your applications. It’s time to learn it all.

What We'll Cover

image

Get Ready To #FallForIntelligentApps starting September 18!

Today, we kick off the Fall season with content and activities to skill you up on all things Intelligent Apps or AI Apps on Azure with content, events, and community interactions! Read on to learn about what is coming!

Explore Our Initiatives

We have a number of initiatives planned for the month to help you learn and skill up on relevant technologies. Click on the links to visit the relevant pages for each

image

Register for the events!

What are 4 things you can do today, to jumpstart your learning journey?

#30Days Of Intelligent Apps

#30DaysOfIA is a series of daily blog posts grouped into themed weeks - taking you from core concepts to end-to-end solution examples in 30 days. Each article will provide conceptual lessons paired with exercises and resources to help you reinforce learnings and take next steps.

This series takes you through learning journey in six stages, each building on the previous week to help you skill up in a beginner-friendly way:

We will start with defining intelligent apps and then expand on how to build with cloud-native technologies like Azure Kubernetes Service, Azure Container Apps and Azure Functions, and take you through end-to-end scenarios for real world application development. Before we dive deep on intelligent apps, here is a high-level overview of the Intelligent Apps landscape on Azure:

image

Containers on Azure services offer you a wide range of capabilities, from simplicity to control to suit your different needs.

image -image

To start with the basics for developing Kubernetes applications, explore #30DaysOfCloudNative.

Cloud-native development when paired with serverless computing enhances your solution architecture for building cost optimized, resilient applications.

image

To start with the basics for serverless computing, explore #30DaysOfServerless.

Let’s Get Started

Now you know everything! We hope you are as excited as we are to dive into a full month of active learning and doing! Don't forget to subscribe for updates in your favorite feed reader! And look out for our first Intelligent Apps post Monday!

- - +
Skip to main content

11 posts tagged with "learn-live"

View All Tags

· 10 min read
It's 30DaysOfIA

Explore real-world examples of how Intelligent Apps revolutionize logistics, retail, finance, manufacturing, tech, and medicine.

What We'll Cover:

  • Real-world businesses revolutionizing operations with Intelligent Apps
  • High level architecture for industry scenarios
  • Implementation architecture overview across industries

A decorative header image for the blog

Real-world Success Stories

According to IBM’s 2022 Global AI Adoption Index, 35% of surveyed companies already use AI in their business, and 42% are exploring the idea. Organizations lagging behind the AI curve will quickly fall behind early adopters, who are already enjoying its benefits and boosting their investment in this technology.

In the first part of this series, “Demystifying Intelligent Applications: Leveraging AI in App Development,” we explored how organizations can use AI-powered apps, or intelligent apps, to transform their operations, decision-making, and customer experiences, leaping ahead of the competition.

Intelligent apps significantly improve operational efficiency and customer engagement across industries like logistics, retail, finance, manufacturing, tech, and healthcare. In this article, we’ll spotlight real-world success stories of businesses revolutionizing operations with intelligent apps using Microsoft Azure’s app, data, and AI services.

info

Register for the Learn Live on September 21 for a live guided experience with SMEs on how to build end-to-end solution architecture for real world implementation of Intelligent Apps.

Streamlining Operations: Intelligent Apps in the Airline Industry

Aerospace pioneer Airbus leveraged intelligent apps to streamline its operations and innovate its services with two ground-breaking modernizations that use Azure apps, data, and AI services to reimagine pilot training and predict aircraft maintenance issues. 

Aviation technology’s rapid evolution compelled Airbus to reconceive their approach to training pilots. The aircraft manufacturer created a pilot training chatbot by combining Azure AI Services with Speech to Text and Text to Speech. The bot helps trainee pilots navigate over 6,000 pages of technical information. Pilots can ask questions verbally for quick answers, aiding their understanding of dense information and speeding up training. And when Airbus needed to accommodate its military consumers, deploying its Kubernetes cluster to a disconnected, “air-gapped” environment met the strict security requirements of military aircraft training.

A chatbot could look like the following diagram as Azure services work together to create a conversational experience. Its components could include security and governance, bot logic and user experience (UX), bot cognition and intelligence, data extract, transfer, and load (ETL) operations, quality assurance and enhancements, logging, and monitoring and reporting.

A diagram of a conversational experience enabled by Azure services

Airbus also used AI to improve its aircraft maintenance and safety. They deployed AI Anomaly Detector to gather and analyze telemetry data from multiple flights, enabling more profound insights into wear and tear on military aircraft operating in harsh conditions. This AI application helps Airbus better understand each airplane’s health. Airbus can predict and fix potential problems before they occur, improving the aircraft’s safety and operational readiness.

AI and Azure are changing how organizations like Airbus operate, even in strict, highly regulated markets with stringent security requirements. And by streamlining pilot training and staying ahead of aircraft maintenance, Airbus helps its military and commercial clients reliably deliver their goods across the globe, maintaining their critical supply chains.

Boosting Customer Service: Intelligent Apps in the Retail Industry

The retail industry has been among the first to adopt and benefit from intelligent apps. For example, the leading American used car retailer, CarMax, uses intelligent apps to transform the car shopping experience.

Before embracing AI, CarMax faced a significant challenge: Manually creating text summaries was time-consuming and labor-intensive. CarMax decided to leverage Azure OpenAI Service to automatically generate car research pages, offering customers valuable insights while enhancing the website’s search engine rankings.

This AI-driven approach has significantly streamlined the process, achieving in a few months what would have taken years manually. Also, the AI system condenses thousands of customer reviews into a few readable sentences, offering potential buyers a quick overview of others’ experiences.

CarMax has improved their customer experience while boosting operational efficiency through AI adoption. CarMax’s editorial staff focuses on creating strategic, longer-form content with the time saved, enhancing productivity, and driving more website traffic.

CarMax’s success story serves as a testament to the potential of intelligent apps in the retail industry.

Enhancing Cybersecurity: Intelligent Apps in Finance

Intelligent apps also impact the highly regulated financial services sector. Swift, a leading infrastructure provider for financial messaging services, uses intelligent apps to enhance decision-making and combat financial crime.

The massive growth of cross-border transactions and instant payment networks led to a surge in financial fraud that can cost the industry hundreds of billions of dollars every year. So, Swift and Microsoft partnered to build an anomaly detection model using federated learning techniques with Azure Machine Learning and Azure confidential computing.

Microsoft Purview helped govern data, as the federated learning method partially trained each model in a silo before aggregating them. This approach enabled Swift and Microsoft to develop a highly accurate model without copying or moving data from Swift members’ secure locations, ensuring the highest level of security and privacy.

Financial organizations can now deploy this trained ML model to run a real-time fraud detection system, lowering the cost of financial fraud.

Organizations can deploy a fraud detection system like the following example on Microsoft Azure. The system ingests data from streams or storage, runs analytics, and uses ML to detect fraud and store results. It then alerts users and other systems to the security issue.

A diagram of an example fraud detection system

Swift provides the innovative model to its banking partners, who can share data to further train the model. Azure confidential computing securely runs new models on current data to accumulate insights, generating an ongoing learning cycle. This iterative approach strengthens the global detection of fraudulent financial transactions through an increasingly faster and more accurate model. 

The collaboration between Swift and Microsoft demonstrates how combining various Azure services creates Intelligent Apps to enhance decision-making in finance. These apps solve real problems at an unprecedented global scale. 

Enhancing Efficiency: Intelligent Apps in Manufacturing

3M, known for Post-it Notes and other innovative products, wielded the power of Intelligent Apps to standardize, automate, and accelerate its sales forecasting. The company needed a unified, automated approach to replace its multiple manual methods.

It adopted Azure Machine Learning and other Azure AI Services to develop, train, and implement 1,500 custom models for each region and division, replacing manual methods. This solution delivers timely finance, sales, and marketing insights while freeing teams from time-consuming manual data manipulation tasks.

3M used Azure Machine Learning, automated machine learning (AutoML), and the Many Models Solution Accelerator to train and score numerous machine learning models in parallel. This approach significantly shortened the company’s development cycle time.

Then, 3M integrated Microsoft Power BI into their solution to compile and visualize results more effectively. Executives now have an unbiased and unfiltered view of data for better decision-making. With its improved ability to forecast sales, 3M can focus on manufacturing the products it needs efficiently.

3M’s success demonstrates how combining various Azure services can enhance efficiency in the manufacturing industry. These apps improve operational processes while enabling employees to focus on higher-level tasks. Strategic decisions are more targeted, and business conversations are more meaningful.

Driving Innovation: Intelligent Apps in the Technology Industry

As expected, the tech industry is at the forefront of leveraging Intelligent Apps to drive innovation. Elastic saw an opportunity to use intelligent apps to optimize its search services and meet customer needs more effectively. The company built a managed, secure, and scalable solution for its customers, Elastic Cloud, by leveraging Microsoft Azure.

Elastic Cloud uses Azure Machine Learning and other Azure AI Services to automate many basic management tasks, such as backing up data, upgrading, and scaling deployments. This approach enables Elastic’s customers to focus less on managing the software and more on serving their own clients. Customers can quickly deploy and manage Elastic Cloud on Kubernetes (ECK) using Azure Kubernetes Service.

As a result, Elastic’s cloud business is growing twice as fast as its on-premises solutions. Enterprise search users have reported significant improvements in system availability and employee satisfaction.

Additionally, Elastic incorporated Azure OpenAI Service into its Elasticsearch Relevance Engine (ESRE), providing more relevant answers to search queries. ESRE enables text, vector, and hybrid search, offering a more effective and accurate response to queries.

Elastic’s success story demonstrates how combining various Azure services can create intelligent Apps to drive innovation in the tech industry. These apps help improve operational processes and deliver better results quickly.

Revolutionizing Healthcare: Intelligent Apps in Medicine

Intelligent apps also drive transformative changes in the healthcare sector. Cambridgeshire and Peterborough NHS Foundation Trust, a healthcare provider in the United Kingdom, gradually shifted their on-premises systems to the cloud using Azure, improving flexibility, and lowering costs. They needed a solution for their electronic patient record (EPR) system that integrates 23 years of data across multiple systems and formats.

The Trust used Microsoft Azure and Azure Cognitive Search, including semantic search for understanding intent and contexts and cognitive capabilities for extracting insights from data, to standardize and automate data management. This approach made legacy, archive, and live data more discoverable, saving clinicians’ time. The Trust’s solution searches through millions of clinical documents in seconds, even recognizing handwritten notes without needing a separate optical character recognition (OCR) solution to extract text from images (Azure Cognitive Search automatically does it). Clinicians can now uncover previously inaccessible insights, enabling them to offer more personalized care.

The Trust also used Integration Services tools such as Azure Logic Apps to create workflows without writing code and Event Grid to connect Azure and third-party services through a publisher-subscriber model. They switched on a live feed from their new EPR system to Azure to feed real-time information into other applications. Researchers can now access anonymized data to fuel medical innovations beyond the NHS.

The healthcare provider also invested heavily in Power BI’s data visualization and analytical functions to sift through massive amounts of information. This level of data accessibility was previously impossible with their on-premises system. The new insights have significantly improved clinicians’ efficiency for better patient care.

Data accessibility and security are critical in the healthcare industry. Intelligent Apps empower organizations like the Trust to revolutionize medical care. AI capabilities helped the Trust improve its operational efficiency and user experiences, setting a new standard for healthcare in the digital age.

Summary

AI is driving transformation and real-world successes across industries. Intelligent apps are reshaping our world by streamlining operations in manufacturing and logistics, enhancing customer experience in the retail sector, improving decision-making in finance, driving innovation in tech, and revolutionizing healthcare.

As organizations adopt AI, they’re innovating in ways that were once too difficult, expensive, or outright impossible. Companies that wait to embrace intelligent apps risk the chance of falling behind. By embracing intelligent apps and leveraging the capabilities of Azure, you can harness the transformative potential of AI to remain on the cutting edge of technology.

Exercise

+ + \ No newline at end of file diff --git a/30daysofIA/tags/learn-live/page/8/index.html b/30daysofIA/tags/learn-live/page/8/index.html index 9e89093e8a..8ffc1100a9 100644 --- a/30daysofIA/tags/learn-live/page/8/index.html +++ b/30daysofIA/tags/learn-live/page/8/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "learn-live" | Build Intelligent Apps On Azure +11 posts tagged with "learn-live" | Build Intelligent Apps On Azure @@ -14,14 +14,13 @@ - - + +
-

9 posts tagged with "learn-live"

View All Tags

· 4 min read
It's 30DaysOfIA

Continue The Learning Journey through Fall For Intelligent Apps! 🍂

What We'll Cover

Thank you! ♥️

image

It's hard to believe that JavaScript on Azure hack-together is ending! It seems like just yesterday that we launched this initiative, and yet here we are, 15 days later, with an incredible amount of learning and growth behind us. So... it's time for a wrap!

From the bottom of our hearts, we want to thank each and every one of you for your participation, engagement, and enthusiasm. It's been truly inspiring to see the passion and dedication from this strong community, and we're honored to be a part of it. ✨

Recap of The JavaScript on Azure Global Hack-Together

As we wrap up this exciting event, we wanted to take a moment to reflect on all that we've accomplished together. Over the last 15 days, we've covered a lot of ground, from the basics of contributing to Open source to the exploration of the Contoso Real Estate project from its Frontend to its Backend and future AI implementation.

Now that the hack-together is ending, we want to make sure that you have all the resources you need to continue honing your skills in the future. Whether you're looking to make your fist contribution to open source, become an open source maintainers, collaborate with others, or simply keep learning, there are plenty of resources out there to help you achieve your goals. So, let's dive in and explore all the ways you can continue to grow your JavaScript skills on Azure!

JSonAzure Hack-together Roadmap 📍:

hack-together-roadmap (2)

Recap on past Livestreams🌟:

Day 1️⃣: Opening Keynote (Hack-together Launch): Introduction to the Contoso Real Estate Open-source project!, managing complex and complex enterprise architecture, new announcements for JavaScript developers on Azure

Day 2️⃣: GitHub Copilot & Codespaces: Introduction to your AI pair programmer (GitHub Copilot) and your virtual developer environment on the cloud (GitHub Codespaces)

Day 6️⃣: Build your Frontend using Static Web Apps as part of a complex, modern composable frontends (or micro-frontends) and cloud-native applications.

Day 9️⃣: Build a Serverless Backend using Azure Functions

Day 1️⃣3️⃣: Easily connect to an Azure Cosmos DB, exploring its benefits and how to get started

Day 1️⃣5️⃣: Being in the AI Era, we dive into the Azure OpenAI Service and how you can start to build intelligent JavaScript applications

📖 Self-Learning Resources

  1. JavaScript on Azure Global Hack Together Module collection
  2. Lets #HackTogether: Javascript On Azure Keynote
  3. Step by Step Guide: Migrating v3 to v4 programming model for Azure Functions for Node.Js Application

Continue your journey with #FallForIntelligentApps

Join us this Fall on a learning journey to explore building intelligent apps. Combine the power of AI, cloud-native app development, and cloud-scale data to build highly differentiated digital experiences. Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure. Engage in a self-paced learning adventure by following along the #30Days of Intelligent Apps series, completing the Intelligent Apps Skills Challenge, joining the Product Group for a live Ask The Expert series or building an end to end solution architecture with a live guided experience through the Learn Live series. Discover more here.

Hands-on practice: Make your first contribution to open source!

Join our GitHUb Discussion Forum to connect with developers from every part of world, see contributions from other, find collaborators and make your first contribution to a real-world project! -Don't forget to give the repo a star ⭐

Resources

All resources are accessible on our landing page

- - +
Skip to main content

11 posts tagged with "learn-live"

View All Tags

· 8 min read
It's 30DaysOfIA

Let’s start with understanding the power of intelligent applications.

What We'll Cover:

  • What are intelligent applications?
  • Categories of Intelligent Apps
  • Breaking down the role of AI in intelligent apps

A decorative header image for the blog

Demystifying Intelligent Applications: Leveraging AI in App Development

Until recent years, when you heard the term “artificial intelligence” (AI), you might have pictured autonomous robots from sci-fi films. Now, perhaps your mind jumps to big data and machine learning (ML). Maybe you’re even thinking about the latest ChatGPT plugins and transformer architectures. Today, AI is more than these imaginations: it’s a reality increasingly woven into our day-to-day routines through intelligent applications.

Intelligent apps combine cloud-scale compute, data, and predictive or generative AI. These applications help you automate tasks and make data-driven decisions, aiding in leveraging business intelligence to gain meaningful, practical outcomes. 

For developers, the AI landscape isn’t an abstract complexity. It’s a real, tangible opportunity for evolving traditional applications, making them “intelligent,” and delivering more value to users. And with tools like Azure Kubernetes Service, Azure Cosmos DB, and Azure AI Services, stepping into the world of intelligent apps isn’t a steep climb but a steady trek across a manageable learning curve.

The Three Categories of Intelligent Applications

Before we start building, it’s critical to understand what makes an app “intelligent.” It’s not a universal definition so much as an identifier for apps that fall into one or more of the three primary categories: 

  • Outcome-based — This designation represents the broadest category of intelligent apps, whose principal value for users is the intelligent outcomes they provide. These apps process data to provide insightful results that enhance decision-making or help with tasks like trend prediction, resource management, and customer relationship management.

  • Functionality-based — This type of intelligent app takes AI integration a degree further, integrating AI as a function of the app to create or process its outputs, enhancing the app’s overall interactivity and usefulness.

  • Feature-based — This is the narrowest category, in which the app’s AI/ML component is one of its core features and primary selling points

Let’s dive more deeply into each category.

Outcome-Based Apps: Intelligent Outcomes Drive Success

Outcome-based applications are all about the results provided to the user. They function beyond merely processing data to offer insightful outcomes that drive success.

Think of a personal fitness tracker app that uses AI to analyze the user’s activity and provides actionable recommendations for achieving a goal. This type of app not only tracks your steps, sleep patterns, and heart rate but uses that data to provide you with personalized health advice. It helps you make informed lifestyle choices — the “intelligent” outcome.

Another example is a social media marketing app that uses AI for time-saving data analysis, task automation, and trend prediction. AI-based sentiment analysis can gauge customer perception while natural language processing (NLP) accurately interprets customer inquiries. Task automation can schedule posts and automate responses to common customer queries, and trend forecasting helps model and predict customer behavior to further personalize targeted promotional efforts. Building this type of intelligence into your apps provides cost-effective, actionable insights that guide sales and marketing strategies.

Functionality-Based Apps: AI-Driven Features Enhance User Experiences

As the name suggests, functionality-based applications integrate AI into how the app functions, but the end user drives the action and outcome. In this category of apps, AI takes a more active role in the user’s interaction by allowing them to use AI-created or processed output. These apps leverage AI technologies like Azure AI Services for NLP, image recognition, and pattern recognition to improve user experience.

Consider a music app that generates personalized playlists based on your listening habits or your smartphone’s camera app that uses AI to recognize faces and optimize photo settings. Even more advanced are the language translation apps that translate text from one language to another and can even understand and maintain the context and subtleties of the conversation. They use NLP to accomplish these feats.

Similarly, consider the latest e-commerce apps, whose AI-driven functionalities enable customers to try on clothes virtually, significantly enhancing the online shopping experience.

Feature-Based Apps: Integrating Advanced AI/ML Components

Finally, there are feature-based intelligent apps. These apps incorporate advanced AI/ML components like neural networks, internal large language models (LLMs), and sophisticated algorithms as core features for the user. In this type of application, AI is the primary feature for users to engage with and the primary selling point for the app.

Some examples of feature-based applications are chatbots and virtual agents built using Azure Bot Service. These robo-representatives use AI to provide more natural and human-like conversation, balancing superior efficiency with a friendlier, more personalized user experience. The new Bing is a prime example of this implementation, integrating AI to give users a knowledgeable copilot for web searches.

Similarly, OpenAI’s ChatGPT allows users access to its state-of-the-art language models available as Microsoft’s Azure OpenAI Service. It leverages the power of the highly sophisticated GPT-3 LLM to perform tasks like drafting emails, writing code, answering general knowledge questions, translating between languages, and tutoring in numerous subjects.

These apps don’t just use AI to provide outputs or improve user experiences: They push the boundaries of what’s possible in app development by integrating AI/ML components.

info

Register for the live Q&A Ask The Expert session on September 26 with the Azure Functions product engineering team.

Breaking Down the Role of AI in Intelligent Applications

Intelligent apps reflect the merging of traditional software design with the dynamic capabilities of AI. Let’s explore how AI integrates and amplifies these applications.

At its core, AI augments applications with the capacity to self-learn and self-improve. It allows you to create applications that predict, react, and adapt to a constantly changing environment. In the power grid management system for a major city, an AI-infused application can forecast power usage trends, react to anomalies, and adapt to sudden demand surges, ensuring optimal efficiency and preventing blackouts.

But AI’s role doesn’t end at prediction or decision-making: It also automates tasks. In enterprise solutions, it has moved beyond simple automation to encompass intelligent process automation (IPA). In this arena, AI algorithms understand, learn, and then automate entire digital workflows. For developers, this is a fertile ground for creating applications that can dramatically improve the operational efficiency of businesses.

Even more, AI substantially aids in development tasks. For example, there’s GitHub Copilot, an AI-powered code assistant that offers suggestions based not only on syntax but on development best practices and the intent of the code. AI has evolved from a tool you use to build apps to a partner that helps in creating them. 

Using AI to build intelligent apps isn’t about throwing out what’s worked before. It’s about enhancing the components that have the most impact and achieve the best results. Incorporating AI in apps elevates their predictive abilities, streamlines complex tasks, and even aids in the development and creation process. When you build intelligent apps, you don’t just address current problems. You anticipate future challenges and craft solutions that adapt over time.

Summary

As developers the power of AI is in our hands, and it’s our responsibility to harness its full potential. Each category tells the story of AI's transformative potential, from applications that deliver intelligent outcomes to those with AI capabilities to enrich the user experience and those pushing boundaries with advanced AI/ML technology.

Accessible and robust platforms like Azure AI can simplify the process of developing Intelligent Applications. Whether you’re refining an existing app or building a new one, just entering the world of AI, or already familiar with the landscape, take a moment to appreciate the potential that AI integration offers. You’re embracing this exciting frontier and shaping the future of app development. 

Exercise

  • Complete the Cloud Skills Challenge to build on your apps and AI skills.
  • Watch Episode 01 of the Serverless Edition Learn Live session to learn how to build an end-to-end intelligent app solution.
+ + \ No newline at end of file diff --git a/30daysofIA/tags/learn-live/page/9/index.html b/30daysofIA/tags/learn-live/page/9/index.html index 5d430b5dba..b34513b6f1 100644 --- a/30daysofIA/tags/learn-live/page/9/index.html +++ b/30daysofIA/tags/learn-live/page/9/index.html @@ -3,7 +3,7 @@ -9 posts tagged with "learn-live" | Build Intelligent Apps On Azure +11 posts tagged with "learn-live" | Build Intelligent Apps On Azure @@ -14,13 +14,14 @@ - - + +
-

9 posts tagged with "learn-live"

View All Tags

· 2 min read
It's 30DaysOfIA

September is almost here - and that can only mean one thing!! It's time to 🍂 Fall for something new and exciting and spend a few weeks skilling up on relevant tools, techologies and solutions!!

Last year, we focused on #ServerlessSeptember. This year, we're building on that theme with the addition of cloud-scale Data, cloud-native Technologies and cloud-based AI integrations to help you modernize and build intelligent apps for the enterprise!

Watch this space - and join us in September to learn more!

- - +
Skip to main content

11 posts tagged with "learn-live"

View All Tags

· 4 min read
It's 30DaysOfIA

This Fall focus on building intelligent apps using AI and cloud-native technologies. #FallForIntelligentApps brings to you a learning journey to build your skills on creating differentiated experiences while modernizing your applications. It’s time to learn it all.

What We'll Cover

image

Get Ready To #FallForIntelligentApps starting September 18!

Today, we kick off the Fall season with content and activities to skill you up on all things Intelligent Apps or AI Apps on Azure with content, events, and community interactions! Read on to learn about what is coming!

Explore Our Initiatives

We have a number of initiatives planned for the month to help you learn and skill up on relevant technologies. Click on the links to visit the relevant pages for each

image

Register for the events!

What are 4 things you can do today, to jumpstart your learning journey?

#30Days Of Intelligent Apps

#30DaysOfIA is a series of daily blog posts grouped into themed weeks - taking you from core concepts to end-to-end solution examples in 30 days. Each article will provide conceptual lessons paired with exercises and resources to help you reinforce learnings and take next steps.

This series takes you through learning journey in six stages, each building on the previous week to help you skill up in a beginner-friendly way:

We will start with defining intelligent apps and then expand on how to build with cloud-native technologies like Azure Kubernetes Service, Azure Container Apps and Azure Functions, and take you through end-to-end scenarios for real world application development. Before we dive deep on intelligent apps, here is a high-level overview of the Intelligent Apps landscape on Azure:

image

Containers on Azure services offer you a wide range of capabilities, from simplicity to control to suit your different needs.

image +image

To start with the basics for developing Kubernetes applications, explore #30DaysOfCloudNative.

Cloud-native development when paired with serverless computing enhances your solution architecture for building cost optimized, resilient applications.

image

To start with the basics for serverless computing, explore #30DaysOfServerless.

Let’s Get Started

Now you know everything! We hope you are as excited as we are to dive into a full month of active learning and doing! Don't forget to subscribe for updates in your favorite feed reader! And look out for our first Intelligent Apps post Monday!

+ + \ No newline at end of file diff --git a/404.html b/404.html index 62e2a9c2c3..9494cfad30 100644 --- a/404.html +++ b/404.html @@ -14,13 +14,13 @@ - - + + - - + + \ No newline at end of file diff --git a/Fall-For-IA/AskTheExpert/index.html b/Fall-For-IA/AskTheExpert/index.html index d968ac35ba..2bce6e196d 100644 --- a/Fall-For-IA/AskTheExpert/index.html +++ b/Fall-For-IA/AskTheExpert/index.html @@ -14,13 +14,13 @@ - - + +

Ask The Expert

  1. Open a New Issue on the repo.
  2. Click Get Started on the 🎤 Ask the Expert! template.
  3. Fill in the details and submit!

Our team will review all submitted questions and prioritize them for the live ATE session. Questions that don't get answered live (due to time constraints) will be responded to here, in response to your submitted issue.


What is it?

Ask the Expert is a series of scheduled 30-minute LIVE broadcasts where you can connect with experts to get your questions answered! You an also visit the site later, to view sessions on demand - and view answers to questions you may have submitted ahead of time.

Ask the Expert


How does it work?

The live broadcast will have a moderated chat session where you can submit questions in real time. We also have a custom 🎤 Ask The Expert issue you can use to submit questions ahead of time as mentioned earlier.

  • We strongly encourage you to submit questions early using that issue
  • Browse previously posted questions to reduce duplication.
  • Upvote (👍🏽) existing questions of interest to help us prioritize them for the live show.

Doing this will help us all in a few ways:

  • We can ensure that all questions get answered here, even if we run out of time on the live broadcast.
  • Others can vote (👍🏽) on your question - helping us prioritize them live based on popularity
  • We can update them with responses post-event for future readers.

When is it?

Visit the ATE : Fall for Intelligent Apps page to see the latest schedule and registration links! For convenience, we've replicated some information here. Please click the REGISTER TO ATTEND links to save the date and get notified of key details like links to the livestream (pre-event) and recording (post-event.)

DateDescription
Fall for Intelligent Apps with Azure Functions Option 1September 26, 2023 : Fall for Intelligent Apps with Azure Functions (Option 1)

Join the Azure Functions Product Group this fall to learn about FaaS or Functions-as-a-Service in Azure serverless computing. It is time to focus on the pieces of code that matter most to you while Azure Functions handles the rest. Discuss with the experts on how to combine the power of AI, cloud-scale data, and serverless app development to create highly differentiated digital experiences. Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure Functions.

REGISTER TO ATTEND

Fall for Intelligent apps with Azure Functions Option 2September 26, 2023 : Fall for Intelligent apps with Azure Functions (Option 2)

Join the Azure Functions Product Group this fall to learn about FaaS or Functions-as-a-Service in Azure serverless computing. It is time to focus on the pieces of code that matter most to you while Azure Functions handles the rest. Discuss with the experts on how to combine the power of AI, cloud-scale data, and serverless app development to create highly differentiated digital experiences. Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure Functions.

REGISTER TO ATTEND

Fall for Intelligent Apps with Azure App Service Option 1October 11, 2023 : Fall for Intelligent Apps with Azure App Service (Option 1)

Join the Azure App Service Product Group this Fall to learn about PaaS or Platform-as-a-Service app development on Azure. Quickly build, deploy, and scale web apps and APIs on your terms. Discuss with the experts on how to combine the power of AI, web app development, and cloud-scale data to create highly differentiated digital experiences. Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure App Service.

REGISTER TO ATTEND

Fall for Intelligent Apps with Azure App Service Option 2October 11, 2023 : Fall for Intelligent Apps with Azure App Service (Option 2)

Join the Azure App Service Product Group this Fall to learn about PaaS or Platform-as-a-Service app development on Azure. Quickly build, deploy, and scale web apps and APIs on your terms. Discuss with the experts on how to combine the power of AI, web app development, and cloud-scale data to create highly differentiated digital experiences. Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure App Service.

REGISTER TO ATTEND

Fall for Intelligent Apps with Azure Container Apps Option 1October 18, 2023 : Fall for Intelligent Apps with Azure Container Apps (Option 1)

Join the Azure Container Apps Product Group this Fall to learn about combining the power of AI, cloud-scale data, and cloud-native app development to create highly differentiated digital experiences with microservices. Azure Container Apps is an app-centric service, empowering developers to focus on the differentiating business logic of their apps rather than on cloud infrastructure management. Discuss with the experts on how to develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure Container Apps.

REGISTER TO ATTEND

Fall for Intelligent apps with Azure Functions Option 2October 18, 2023 : Fall for Intelligent Apps with Azure Container Apps (Option 2)

Join the Azure Container Apps Product Group this Fall to learn about combining the power of AI, cloud-scale data, and cloud-native app development to create highly differentiated digital experiences with microservices. Azure Container Apps is an app-centric service, empowering developers to focus on the differentiating business logic of their apps rather than on cloud infrastructure management. Discuss with the experts on how to develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure Container Apps.

REGISTER TO ATTEND

Fall for Intelligent Apps with Azure Kubernetes Service Option 1October 25, 2023 : Fall for Intelligent Apps with Azure Kubernetes Service (Option 1)

Join the Azure Kubernetes Service Product Group this Fall to learn about developing cloud-native apps for high scale and resilience with Azure Kubernetes Service clusters. Discuss with the experts on how to combine the power of AI with Kubernetes app development to create highly differentiated digital experiences. Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure Kubernetes Service (AKS).

REGISTER TO ATTEND

Fall for Intelligent Apps with Azure Kubernetes Service Option 2October 25, 2023 : Fall for Intelligent Apps with Azure Kubernetes Service (Option 2)

Join the Azure Kubernetes Service Product Group this Fall to learn about developing cloud-native apps for high scale and resilience with Azure Kubernetes Service clusters. Discuss with the experts on how to combine the power of AI with Kubernetes app development to create highly differentiated digital experiences. Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure Kubernetes Service (AKS).

REGISTER TO ATTEND

- - + + \ No newline at end of file diff --git a/Fall-For-IA/CloudSkills/index.html b/Fall-For-IA/CloudSkills/index.html index 864706b4a2..d516632344 100644 --- a/Fall-For-IA/CloudSkills/index.html +++ b/Fall-For-IA/CloudSkills/index.html @@ -14,13 +14,13 @@ - - + +

Cloud Skills Challenge

Use the link above to start the Cloud Skills Challenge today! The challenge runs till Oct 31, so an early start helps!


Fall for Intelligent Apps Skills Challenge

Join us on a learning journey this fall to skill up on your core skills for developing intelligent apps. Explore how to combine the power of AI, cloud-scale data, and cloud-native app development to create highly differentiated digital experiences.

Fall for intelligent apps

  • Intelligent Apps Skills Challenge - Applications are at the core of intelligent solution development. Cloud-native app development empowers you to create modern containerized and serverless apps to build innovative solutions. Explore how to get started with building intelligent apps using Azure Kubernetes Service, Azure Functions and GitHub.

  • Data Skills Challenge - It is time to activate our enormous data stores for building data driven intelligent solutions. Explore the capabilities of cloud-scale data with Microsoft Fabric in this Cloud Skills Challenge! Follow along with the Fabric Community @ https://aka.ms/fabriccommunity.

  • AI Skills Challenge - The world of generative AI is rapidly evolving. Learn how to create intelligent solutions that extract semantic meaning from text and support common computer vision scenarios. Explore how to take advantage of large-scale, generative AI models with deep understandings of language and code to enable new reasoning and comprehension capabilities for building cutting-edge applications responsibly.


About Cloud Skills

The Cloud Skills Challenge is a fun way to skill up on Azure serverless technologies while competing with other members of the community for a chance to win fun swag!

About Cloud Skills

You'll work your way through learning modules that skill you up on relevant technologies - while collecting points that place you on a Leaderboard.

  1. 🎯 Compete - Benchmark your progress against friends and coworkers.
  2. 🎓 Learn - Increase your understanding by completing learning modules.
  3. 🏆 Skill Up - Gain useful technical skills and prep for certifications.

About Microsoft Learn

Completed the Cloud Skills Challenge, and want to keep going on your learning journey? Or, perhaps there are other Cloud+AI topics you want to skill up in? Check out these three resources for building your professional profile!

1️⃣ - LEARNING PATHS2️⃣ - CERTIFICATIONS3️⃣ - LEARNING EVENTS
Skill up on a topic with guided paths for self-study!Showcase your expertise with industry-recognized credentials!Learn from subject matter experts in live & recorded events
- - + + \ No newline at end of file diff --git a/Fall-For-IA/CommunityGallery/index.html b/Fall-For-IA/CommunityGallery/index.html index 4ffdd4d2d8..df83f4a4a3 100644 --- a/Fall-For-IA/CommunityGallery/index.html +++ b/Fall-For-IA/CommunityGallery/index.html @@ -14,13 +14,13 @@ - - + +

Community Gallery

Explore the Community Showcase for videos, blog posts and open-source projects from the community.

Filters

21 posts

Featured Posts


  • Build a Serverless Backend with Azure Functions

    In this session, we'll give you a gentle introduction to serverless backends and how Azure Functions can help you build them quickly and easily. We'll start by discussing the benefits of using serverless backends for your web projects and how Azure Functions can help you get started quickly. Then, we'll dive into a demo of the Contoso Real Estate project, showing you how it uses Azure Functions to power its backend.

  • Build and connect to a Database using Azure Cosmos DB

    In this session, we'll give you a gentle introduction to Azure Cosmos DB and how it can help you store and manage your data in the cloud. We'll start by discussing the benefits of using Azure Cosmos DB for your data storage needs, including its global distribution and scalability. Then, we'll dive into a demo of the Contoso Real Estate project, showing you how it uses Azure Cosmos DB to store its data.

  • Build your Frontend with Azure Static Web Apps

    In this session, we'll give you a gentle introduction to Static Web Apps and the SWA CLI. We'll start by discussing the benefits of using Static Web Apps for your web projects and how the SWA CLI can help you get started quickly. Then, we'll dive into a demo of the Contoso Real Estate project, showing you how it uses Static Web Apps to deploy changes quickly and easily.

  • Hack Together Launch – Opening Keynote

    Join us for an in-depth walkthrough of the Contoso Real Estate project, with a focus on the portal app architecture (Full stack application). During this session, we'll guide you through the key components of the architecture and show you how to set up your own environment for the project. We'll also provide detailed contribution instructions to help you get involved in the project and make a meaningful impact. Whether you're a seasoned developer or just getting started, this session is a must-attend for anyone interested in building scalable, modern web applications.

  • Introduction to Azure OpenAI Service

    Join us for an exciting introduction to the world of AI with Azure OpenAI. In this session, you'll learn how to harness the power of OpenAI to build intelligent applications that can learn, reason, and adapt. We'll cover the basics of Azure OpenAI, including how to set up and configure your environment, and walk you through a series of hands-on exercises to help you get started. Whether you're a seasoned developer or just starting out, this session is the perfect way to unlock the full potential of AI and take your applications to the next level.

  • Introduction to GitHub Copilot

    Join us for an exciting introduction to GitHub Copilot, the revolutionary AI-powered coding assistant. In this session, you'll learn how to harness the power of Copilot to write code faster and more efficiently than ever before. We'll cover the basics of Copilot, including how to install and configure it, and walk you through a series of hands-on exercises to help you get started. Whether you're a seasoned developer or just starting out, this session is the perfect way to take your coding skills to the next level.

All Posts


  • Ask the Expert: Serverless September | Azure Container Apps

    Join the Azure Container Apps Product Group this Serverless September to learn about serverless containers purpose-built for microservices. Azure Container Apps is an app-centric service, empowering developers to focus on the differentiating business logic of their apps rather than on cloud infrastructure management. Discuss with the experts on how to build and deploy modern apps and microservices using serverless containers with Azure Container Apps.

  • Ask the Expert: Serverless September | Azure Functions

    Join the Azure Functions Product Group this Serverless September to learn about FaaS or Functions-as-a-Service in Azure serverless computing. It is time to focus on the pieces of code that matter most to you while Azure Functions handles the rest. Discuss with the experts on how to execute event-driven serverless code functions with an end-to-end development experience using Azure Functions.

  • Azure Samples / Azure Container Apps That Use OpenAI

    This sample demonstrates how to quickly build chat applications using Python and leveraging powerful technologies such as OpenAI ChatGPT models, Embedding models, LangChain framework, ChromaDB vector database, and Chainlit, an open-source Python package that is specifically designed to create user interfaces (UIs) for AI applications. These applications are hosted on Azure Container Apps, a fully managed environment that enables you to run microservices and containerized applications on a serverless platform.

  • Azure Samples / Contoso Real Estate

    This repository contains the reference architecture and components for building enterprise-grade modern composable frontends (or micro-frontends) and cloud-native applications. It is a collection of best practices, architecture patterns, and functional components that can be used to build and deploy modern JavaScript applications to Azure.

  • Build a Serverless Backend with Azure Functions

    In this session, we'll give you a gentle introduction to serverless backends and how Azure Functions can help you build them quickly and easily. We'll start by discussing the benefits of using serverless backends for your web projects and how Azure Functions can help you get started quickly. Then, we'll dive into a demo of the Contoso Real Estate project, showing you how it uses Azure Functions to power its backend.

  • Build an intelligent application fast and flexibly using Open Source on Azure

    Watch this end-to-end demo of an intelligent app that was built using a combination of open source technologies developed by Microsoft and the community. Highlights of the demo include announcements and key technologies.

  • Build and connect to a Database using Azure Cosmos DB

    In this session, we'll give you a gentle introduction to Azure Cosmos DB and how it can help you store and manage your data in the cloud. We'll start by discussing the benefits of using Azure Cosmos DB for your data storage needs, including its global distribution and scalability. Then, we'll dive into a demo of the Contoso Real Estate project, showing you how it uses Azure Cosmos DB to store its data.

  • Build Intelligent Microservices with Azure Container Apps

    Azure Container Apps (ACA) is a great place to run intelligent microservices, APIs, event-driven apps, and more. Infuse AI with Azure Container Apps jobs, leverage adaptable design patterns with Dapr, and explore flexible containerized compute for microservices across serverless or dedicated options.

  • Build scalable, cloud-native apps with AKS and Azure Cosmos DB

    Develop, deploy, and scale cloud-native applications that are high-performance, fast, and can handle traffic bursts with ease. Explore the latest news and capabilities for Azure Kubernetes Service (AKS) and Azure Cosmos DB, and hear from Rockwell Automation about how they've used Azure's cloud-scale app and data services to create global applications.

  • Build your Frontend with Azure Static Web Apps

    In this session, we'll give you a gentle introduction to Static Web Apps and the SWA CLI. We'll start by discussing the benefits of using Static Web Apps for your web projects and how the SWA CLI can help you get started quickly. Then, we'll dive into a demo of the Contoso Real Estate project, showing you how it uses Static Web Apps to deploy changes quickly and easily.

  • Building and scaling cloud-native intelligent applications on Azure

    Learn how to run cloud-native serverless and container applications in Azure using Azure Kubernetes Service and Azure Container Apps. We help you choose the right service for your apps. We show you how Azure is the best platform for hosting cloud native and intelligent apps, and an app using Azure OpenAI Service and Azure Data. Learn all the new capabilities of our container platforms including how to deploy, test for scale, monitor, and much more.

  • Cloud-Native New Year - Azure Kubernetes Service

    Join the Azure Kubernetes Service Product Group this New Year to learn about cloud-native development using Kubernetes on Azure computing. It is time to accelerate your cloud-native application development leveraging the de-facto container platform, Kubernetes. Discuss with the experts on how to develop, manage, scale and secure managed Kubernetes clusters on Azure with an end-to-end development and management experience using Azure Kubernetes Service and Azure Fleet Manager.

  • Deliver apps from code to cloud with Azure Kubernetes Service

    Do you want to build and run cloud-native apps in Microsoft Azure with ease and confidence? Do you want to leverage the power and flexibility of Kubernetes, without the hassle and complexity of managing it yourself? Or maybe you want to learn about the latest and greatest features and integrations that Azure Kubernetes Service (AKS) has to offer? If you answered yes to any of these questions, then this session is for you!

  • Focus on code not infra with Azure Functions Azure Spring Apps Dapr

    Explore an easy on-ramp to build your cloud-native APIs with containers in the cloud. Build an application using Azure Spring APIs to send messages to Dapr enabled message broker, triggering optimized processing with Azure Functions, all hosted in the same Azure Container Apps environment. This unified experience for microservices hosts multitype apps that interact with each other using Dapr, scale dynamically with KEDA, and focus on code, offering a true high productivity developer experience.

  • Hack Together Launch – Opening Keynote

    Join us for an in-depth walkthrough of the Contoso Real Estate project, with a focus on the portal app architecture (Full stack application). During this session, we'll guide you through the key components of the architecture and show you how to set up your own environment for the project. We'll also provide detailed contribution instructions to help you get involved in the project and make a meaningful impact. Whether you're a seasoned developer or just getting started, this session is a must-attend for anyone interested in building scalable, modern web applications.

  • Integrating Azure AI and Azure Kubernetes Service to build intelligent apps

    Build intelligent apps that leverage Azure AI services for natural language processing, machine learning, Azure OpenAI Service with Azure Kubernetes Service (AKS) and other Azure application platform services. Learn best practices to help you achieve optimal scalability, reliability and automation with CI/CD using GitHub. By the end of this session, you will have a better understanding of how to build and deploy intelligent applications on Azure that deliver measurable impact.

  • Introduction to Azure OpenAI Service

    Join us for an exciting introduction to the world of AI with Azure OpenAI. In this session, you'll learn how to harness the power of OpenAI to build intelligent applications that can learn, reason, and adapt. We'll cover the basics of Azure OpenAI, including how to set up and configure your environment, and walk you through a series of hands-on exercises to help you get started. Whether you're a seasoned developer or just starting out, this session is the perfect way to unlock the full potential of AI and take your applications to the next level.

  • Introduction to GitHub Copilot

    Join us for an exciting introduction to GitHub Copilot, the revolutionary AI-powered coding assistant. In this session, you'll learn how to harness the power of Copilot to write code faster and more efficiently than ever before. We'll cover the basics of Copilot, including how to install and configure it, and walk you through a series of hands-on exercises to help you get started. Whether you're a seasoned developer or just starting out, this session is the perfect way to take your coding skills to the next level.

  • Modernizing with containers and serverless Q&A

    Join the Azure cloud-native team to dive deeper into developing modern apps on cloud with containers and serverless technologies. Explore how to leverage the latest product advancements in Azure Kubernetes Service, Azure Container Apps and Azure Functions for scenarios that work best for cloud-native development. The experts cover best practices on how to develop with in-built open-source components like Kubernetes, KEDA, and Dapr to achieve high performance along with dynamic scaling.

  • Modernizing your applications with containers and serverless

    Dive into how cloud-native architectures and technologies can be applied to help build resilient and modern applications. Learn how to use technologies like containers, Kubernetes and serverless integrated with other application ecosystem services to build and deploy microservices architecture on Microsoft Azure. This discussion is ideal for developers, architects, and IT pros who want to learn how to effectively leverage Azure services to build, run and scale modern cloud-native applications.

  • What the Hack: Serverless walkthrough

    The Azure Serverless What The Hack will take you through architecting a serverless solution on Azure for the use case of a Tollbooth Application that needs to meet demand for event driven scale. This is a challenge-based hack. It’s NOT step-by-step. Don’t worry, you will do great whatever your level of experience!

- - + + \ No newline at end of file diff --git a/Fall-For-IA/HackTogether/index.html b/Fall-For-IA/HackTogether/index.html index 2fda346fdd..46a8cccbe6 100644 --- a/Fall-For-IA/HackTogether/index.html +++ b/Fall-For-IA/HackTogether/index.html @@ -14,13 +14,13 @@ - - + +

Hack Together: JS on Azure

Learn about the core Application and AI technologies behind the Contoso Real Estate Sample. Then make your first open-source contribution!

Thumbnail Image forHello, Contoso Real Estate!

Hello, Contoso Real Estate!

Get an overview of the Contoso Real estate app and architecture.

Thumbnail Image forIntroduction to GitHub Copilot

Introduction to GitHub Copilot

Learn how to harness the power of Copilot from installation to usage.

Thumbnail Image forBuild Your Frontend With Azure Static Web Apps

Build Your Frontend With Azure Static Web Apps

Learn about Azure Static Web Apps, the SWA CLI - and usage.

Thumbnail Image forBuild a Serverless Backend with Functions

Build a Serverless Backend with Functions

Show how Azure Functions powers the serverless backend for the app.

Thumbnail Image forBuild & Connect Your Database with Azure Cosmos DB

Build & Connect Your Database with Azure Cosmos DB

Show how you can manage your data in CosmosDB, and usage within the Contoso app.

Thumbnail Image forIntroduction to Azure Open AI Service

Introduction to Azure Open AI Service

Learn the basics of Azure Open AI and explore how you can use it.

- - + + \ No newline at end of file diff --git a/Fall-For-IA/LearnLive/index.html b/Fall-For-IA/LearnLive/index.html index fbe25757f6..bfe93d4353 100644 --- a/Fall-For-IA/LearnLive/index.html +++ b/Fall-For-IA/LearnLive/index.html @@ -14,13 +14,13 @@ - - + +

Learn Live: Serverless Edition

Learn to build an enterprise-grade serverless solution on Azure by deconstructing an open-source reference sample.

Thumbnail Image forGet Started With Contoso Real Estate

Get Started With Contoso Real Estate

Learn about the Contoso Real Estate sample, fork the repo, launch GitHub Codespaces - and build/preview the application to validate environment.

Thumbnail Image forDevelop The Portal Application

Develop The Portal Application

Learn about micro-frontends and API-first design. Deconstruct the portal app, blog app, and serverless API.

Thumbnail Image forIntegrate Auth, Payments, Search

Integrate Auth, Payments, Search

Integrate authentication to support user profiles. Integrate payments and search features using 3rd party API.

Thumbnail Image forAutomate, Test & Deploy to Azure

Automate, Test & Deploy to Azure

Learn to design and run end-to-end tests with Playwright. Provision and deploy solution to Azure with AZD.

- - + + \ No newline at end of file diff --git a/Fall-For-IA/calendar/index.html b/Fall-For-IA/calendar/index.html index eddcd95505..8a4aad3700 100644 --- a/Fall-For-IA/calendar/index.html +++ b/Fall-For-IA/calendar/index.html @@ -14,13 +14,13 @@ - - + +

Fall For Intelligent Apps 🍂

#FallForIntelligentApps kicked off mid-September with initiatives to help you learn the tools, technologies and skills you need to modernize your applications and build differentiated experiences with AI! Look for these signature events & more:

  • 🎙 Ask The Expert - (free, online) with the Azure Product Group across two time zones–Americas + EMEA and APAC + ANZ
  • 👩🏽‍💻 Learn Live - live training series on building intelligent apps end-to-end on Azure with AI.
  • ✍🏽 #30DaysOfIA - series of daily blog posts organized in 4 themed weeks focused on intelligent apps.
  • 🎯 Cloud Skills Challenge - curated collection of Learn modules in Apps, Data & AI - for self-skilling!
  • 🐝 Community Buzz - activities to showcase your projects and contributions - including a gallery!

Read on to learn where you can tune into livestreams, catch up on replays, and participate by making your first open-source contributions!


Sep 2023

🍂#FallForIntelligentApps

  • 👩🏽‍💻 Sep 14 | #LearnLive Serverless - Deconstruct Contoso Real Estate (Architecture)

Oct 2023

🍂#FallForIntelligentApps

  • 👩🏽‍💻 Oct 05 | #LearnLive Serverless - Ep 04: Deconstruct Contoso Real Estate (Testing & Deployment)
  • 🎙 Oct 11 | #AskTheExpert - Azure App Service
  • 👩🏽‍💻 Oct 12 | #LearnLive Kubernetes – Ep 01: Deploying Intelligent Apps with OpenAI on Azure Kubernetes Service 
  • 🎙 Oct 18 | #AskTheExpert - Azure Container Apps
  • 👩🏽‍💻 Oct 19 | #LearnLive Kubernetes – Ep 02: Securing Access to Azure Open AI Services with AKS Workload Identity 
  • 🎙 Oct 25 | #AskTheExpert – Azure Kubernetes Service
  • 👩🏽‍💻 Oct 26 | #LearnLive Kubernetes – Ep 02: Designing Intelligent Application Deployment for Scale, Resilience, and Observability.

Nov 2023

🍂#FallForIntelligentApps

  • Nov 2 | #LearnLive Kubernetes – Ep 04: Network Security and Access for Intelligent Applications on Azure Kubernetes Service

🔥#MSIgnite

Experience the latest innovations around AI, learn from product and partner experts to advance your skills, and connect with your community. Join the community in-person at Seattle, or online from anywhere in the world!

- - + + \ No newline at end of file diff --git a/Fall-For-IA/index.html b/Fall-For-IA/index.html index 45331ad0b1..1277fa9122 100644 --- a/Fall-For-IA/index.html +++ b/Fall-For-IA/index.html @@ -14,13 +14,13 @@ - - + +

🍂 Fall For Intelligent Apps!

Join us this fall on a learning journey to explore building intelligent apps. Combine the power of AI, cloud-scale data, and cloud-native app development to create highly differentiated digital experiences. Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure for your users.

#30DaysOfIA

Join us on a #30Day journey that starts by demystifying Intelligent Apps and ends with you Building a Copilot!

Learn Live

Deconstruct an enterprise-grade end to end reference sample for a serverless or Kubernetes application.

Ask The Expert

Join us for online conversations with the product teams - submit questions ahead of time or ask them live!

Hack Together

Explore this 6-part from Microsoft Reactor on JS & AI on Azure and make an open-source contribution!

Cloud Skills

Skill up on key cloud technologies with these free, self-guided learning courses - and make the leaderboard!

🆕 Community Gallery

Explore the Community Showcase for videos, blog posts and open-source projects for the community!

- - + + \ No newline at end of file diff --git a/New-Year/ate/index.html b/New-Year/ate/index.html index abda001deb..e6e96002ca 100644 --- a/New-Year/ate/index.html +++ b/New-Year/ate/index.html @@ -14,13 +14,13 @@ - - + +

Ask The Expert

Ask the Expert is a series of scheduled 30-minute LIVE broadcasts where you can connect with experts to get your questions answered! You an also visit the site later, to view sessions on demand - and view answers to questions you may have submitted ahead of time.


How does it work?

The live broadcast will have a moderated chat session where you can submit questions in real time. We will also provide guidance on where you can submit questions ahead of time, and recap the questions and responses on this site later - along with links to video recaps where available.


Ask the Experts: Azure Kubernetes Service

Join the Azure Kubernetes Service Product Group this New Year to learn about cloud-native development using Kubernetes on Azure computing. It is time to accelerate your cloud-native application development leveraging the de-facto container platform, Kubernetes. Discuss with the experts on how to develop, manage, scale and secure managed Kubernetes clusters on Azure with an end-to-end development and management experience using Azure Kubernetes Service and Azure Fleet Manager.


When are the sessions?

Visit the Ask The Experts page to Register:

DateDescription
Feb 9th, 2023 @ 9am PSTAsk the Experts: Azure Kubernetes Service
Feb 10th, 2023 @ 12:00pm SGTAsk the Experts: Azure Kubernetes Service (APAC)
- - + + \ No newline at end of file diff --git a/New-Year/calendar/index.html b/New-Year/calendar/index.html index d396a5247c..16d7588051 100644 --- a/New-Year/calendar/index.html +++ b/New-Year/calendar/index.html @@ -14,13 +14,13 @@ - - + +

#CNNY Calendar

#SaveTheDate

#CloudNativeNewYear runs Jan 23 - Feb 23. Check this page for key activities scheduled all month. Use this icon key to scan quickly for dates related to activities of interest.

  • 🎤 Ask The Expert - live Q&A with product teams
  • ✍🏽 #30DaysOfCloudNative - daily content posts from experts
  • 🎯 Cloud Skills Challenge - self-guided learning (with leaderboards)
  • 🎙 Webinars - learn from experts (registration required)
  • ⚛ Reactor - community meetups in-person & online (registration required)
WhenWhatWhere
Jan 23🎯 Cloud Skills Challenge StartsRegister Now
Jan 23✍🏽 #30DaysOfCloudNative KickoffWebsite
Jan 24#TechEspresso: Container Offerings in AzureRegister Now
Jan 26🎙 Webinar: Quickstart Guide to KubernetesRegister Now
Feb 03#AzureHappyHours: How DAPR Bindings simplify 3rd party service integrationsRegister Now
Feb 10#SamosaChaiDotNET Microservices with DAPR+.NETRegister Now
Feb 14#TechEspresso: Azure Kubernetes Service for StartupsRegister Now
Feb 17#AzureHappyHour: DAPR Config Building Block for Microservices SetupRegister Now
Feb 23🎯 Cloud Skills Challenge EndsLast Day!!
Feb 28#TechEspresso: KEDA & DAPR extension introduction in AKSRegister Now
- - + + \ No newline at end of file diff --git a/New-Year/index.html b/New-Year/index.html index 161391e5be..776dfae5ce 100644 --- a/New-Year/index.html +++ b/New-Year/index.html @@ -14,13 +14,13 @@ - - + +


Join us for a month-long celebration of Cloud-Native Computing - from core concepts and developer tools, to usage scenarios and best practices. Bookmark this page, then head over to the blog every week day as we kickstart multiple community-driven and self-guided learning initiatives for jumpstarting your Cloud-Native developer journey.

#30DaysOfCloudNative

Join us on a #30Day journey into Cloud-Native fundamentals.

Ask The Experts

Join us for online conversations with the product teams - submit questions ahead of time or ask them live!

Cloud Skills

Skill up on key cloud technologies with these free, self-guided learning courses - and make the leaderboard!

- - + + \ No newline at end of file diff --git a/assets/images/blog-image-1-1-e8cf7713343a8855e8f9b05434148f9d.png b/assets/images/blog-image-1-1-e8cf7713343a8855e8f9b05434148f9d.png new file mode 100644 index 0000000000000000000000000000000000000000..e01bea0f6a22d1a182bc2ca259378f99d2c22b8f GIT binary patch literal 268714 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 z
  • Lastly, we need to restart the container app revision, to do so run the command below:

     ##Get revision name and assign it to a variable
    $REVISION_NAME = (az containerapp revision list `
    --name $BACKEND_SVC_NAME `
    --resource-group $RESOURCE_GROUP `
    --query [0].name)

    ##Restart revision by name
    az containerapp revision restart `
    --resource-group $RESOURCE_GROUP `
    --name $BACKEND_SVC_NAME `
    --revision $REVISION_NAME
  • Run end-to-end Test on Azure

    From the Azure Portal, select the Azure Container App orders-processor and navigate to Log stream under Monitoring tab, leave the stream connected and opened. From the Azure Portal, select the Azure Service Bus Namespace ordersservices, select the topic orderreceivedtopic, select the subscription named orders-processor-subscription, then click on Service Bus Explorer (preview). From there we need to publish/send a message. Use the JSON payload below

    ```json
    {
    "data": {
    "reference": "Order 150",
    "quantity": 150,
    "createdOn": "2022-05-10T12:45:22.0983978Z"
    }
    }
    ```

    If all is configured correctly, you should start seeing the information logs in Container Apps Log stream, similar to the images below Image showing publishing messages from Azure Service

    Information logs on the Log stream of the deployed Azure Container App Image showing ACA Log Stream

    🎉 CONGRATULATIONS

    You have successfully deployed to the cloud an Azure Container App and configured Dapr Pub/Sub API with Azure Service Bus.

    9. Clean up

    If you are done with the tutorial, use the following command to delete the resource group and all its contained resources to avoid incurring further costs.

    az group delete --name $RESOURCE_GROUP

    Exercise

    I left for you the configuration of the Dapr State Store API with Azure Cosmos DB :)

    When you look at the action method OrderReceived in controller ExternalOrdersController, you will see that I left a line with ToDo: note, this line is responsible to save the received message (OrderModel) into Azure Cosmos DB.

    There is no need to change anything on the code base (other than removing this commented line), that's the beauty of Dapr Building Blocks and how easy it allows us to plug components to our microservice application without any plumping and brining external SDKs.

    For sure you need to work on the configuration part of Dapr State Store by creating a new component file like what we have done with the Pub/Sub API, things that you need to work on are:

    • Provision Azure Cosmos DB Account and obtain its masterKey.
    • Create a Dapr Component file adhering to Dapr Specs.
    • Create an Azure Container Apps component file adhering to ACA component specs.
    • Test locally on your dev machine using Dapr Component file.
    • Register the new Dapr State Store component with Azure Container Apps Environment and set the Cosmos Db masterKey from the Azure Portal. If you want to challenge yourself more, use the Managed Identity approach as done in this post! The right way to protect your keys and you will not worry about managing CosmosDb keys anymore!
    • Build a new image of the application and push it to Azure Container Registry.
    • Update Azure Container Apps and create a new revision which contains the updated code.
    • Verify the results by checking Azure Cosmos DB, you should see the Order Model stored in Cosmos DB.

    If you need help, you can always refer to my blog post Azure Container Apps State Store With Dapr State Management API which contains exactly what you need to implement here, so I'm very confident you will be able to complete this exercise with no issues, happy coding :)

    What's Next?

    If you enjoyed working with Dapr and Azure Container Apps, and you want to have a deep dive with more complex scenarios (Dapr bindings, service discovery, auto scaling with KEDA, sync services communication, distributed tracing, health probes, etc...) where multiple services deployed to a single Container App Environment; I have created a detailed tutorial which should walk you through step by step with through details to build the application.

    So far, the published posts below, and I'm publishing more posts on weekly basis, so stay tuned :)

    Resources

    - - + + \ No newline at end of file diff --git a/blog/15-microservices-azure/index.html b/blog/15-microservices-azure/index.html index 5685e56f77..61f02d8a8c 100644 --- a/blog/15-microservices-azure/index.html +++ b/blog/15-microservices-azure/index.html @@ -14,13 +14,13 @@ - - + +

    15. ACA + Serverless On Azure

    · 4 min read
    Nitya Narasimhan
    Devanshi Joshi

    Welcome to Day 15 of #30DaysOfServerless!

    This post marks the midpoint of our Serverless on Azure journey! Our Week 2 Roadmap showcased two key technologies - Azure Container Apps (ACA) and Dapr - for building serverless microservices. We'll also look at what happened elsewhere in #ServerlessSeptember, then set the stage for our next week's focus: Serverless Integrations.

    Ready? Let's Go!


    What We'll Cover

    • ICYMI: This Week on #ServerlessSeptember
    • Recap: Microservices, Azure Container Apps & Dapr
    • Coming Next: Serverless Integrations
    • Exercise: Take the Cloud Skills Challenge
    • Resources: For self-study!

    This Week In Events

    We had a number of activities happen this week - here's a quick summary:

    This Week in #30Days

    In our #30Days series we focused on Azure Container Apps and Dapr.

    • In Hello Container Apps we learned how Azure Container Apps helps you run microservices and containerized apps on serverless platforms. And we build and deployed our first ACA.
    • In Microservices Communication we explored concepts like environments and virtual networking, with a hands-on example to show how two microservices communicate in a deployed ACA.
    • In Scaling Your Container Apps we learned about KEDA (Kubernetes Event-Driven Autoscaler) and how to configure autoscaling for your ACA based on KEDA-supported triggers.
    • In Build with Dapr we introduced the Distributed Application Runtime (Dapr) and learned how its Building Block APIs and sidecar architecture make it easier to develop microservices with ACA.
    • In Secure ACA Access we learned how to secure ACA access to external services with - and without - Dapr, covering Secret Stores and Managed Identity.
    • Finally, Build ACA with Dapr tied it all together with a enterprise app scenario where an orders processor (ACA) uses Dapr APIs (PubSub, State Management) to receive and store order messages from Azure Service Bus.

    Here's a visual recap:

    Self Study: Code Samples & Tutorials

    There's no better way to get familiar with the concepts, than to dive in and play with code samples and hands-on tutorials. Here are 4 resources to bookmark and try out:

    1. Dapr Quickstarts - these walk you through samples showcasing individual Building Block APIs - with multiple language options available.
    2. Dapr Tutorials provides more complex examples of microservices applications and tools usage, including a Distributed Calculator polyglot app.
    3. Next, try to Deploy a Dapr application to Azure Container Apps to get familiar with the process of setting up the environment, then deploying the app.
    4. Or, explore the many Azure Container Apps samples showcasing various features and more complex architectures tied to real world scenarios.

    What's Next: Serverless Integrations!

    So far we've talked about core technologies (Azure Functions, Azure Container Apps, Dapr) that provide foundational support for your serverless solution. Next, we'll look at Serverless Integrations - specifically at technologies like Azure Logic Apps and Azure Event Grid that automate workflows and create seamless end-to-end solutions that integrate other Azure services in serverless-friendly ways.

    Take the Challenge!

    The Cloud Skills Challenge is still going on, and we've already had hundreds of participants join and complete the learning modules to skill up on Serverless.

    There's still time to join and get yourself on the leaderboard. Get familiar with Azure Functions, SignalR, Logic Apps, Azure SQL and more - in serverless contexts!!


    - - + + \ No newline at end of file diff --git a/blog/17-integrate-cosmosdb/index.html b/blog/17-integrate-cosmosdb/index.html index 7299ff801e..1c887d27a6 100644 --- a/blog/17-integrate-cosmosdb/index.html +++ b/blog/17-integrate-cosmosdb/index.html @@ -14,14 +14,14 @@ - - + +

    17. Logic Apps + Cosmos DB

    · 6 min read
    Brian Benz

    Welcome to Day 17 of #30DaysOfServerless!

    In past weeks, we've covered serverless technologies that provide core capabilities (functions, containers, microservices) for building serverless solutions. This week we're looking at technologies that make service integrations more seamless, starting with Logic Apps. Let's look at one usage example today!

    Ready? Let's Go!


    What We'll Cover

    • Introduction to Logic Apps
    • Settng up Cosmos DB for Logic Apps
    • Setting up a Logic App connection and event
    • Writing data to Cosmos DB from a Logic app
    • Resources: For self-study!


    Introduction to Logic Apps

    Previously in Serverless September, we've covered Azure Functions, where the event triggers code. In Logic Apps, the event triggers a workflow that you design. Logic Apps enable serverless applications to connect to external sources for data then automate business processes via workflows.

    In this post I'll walk you through setting up a Logic App that works with Cosmos DB. For this example, we'll connect to the MSN weather service, an design a logic app workflow that collects data when weather changes, and writes the data to Cosmos DB.

    PREREQUISITES

    Setup Cosmos DB for Logic Apps

    Cosmos DB has many APIs to choose from, but to use the default Logic App connection, we need to choose the a Cosmos DB SQL API. We'll set this up via the Azure Portal.

    To get started with Cosmos DB, you create an account, then a database, then a container to store JSON documents. To create a new Cosmos DB account from the portal dashboard, Select Create a resource > Azure Cosmos DB > Create. Choose core SQL for the API.

    Select your subscription, then create a new resource group called CosmosWeather. Enter an account name and choose a location, select provisioned throughput capacity mode and apply the free tier discount. From here you can select Review and Create, then Create

    Azure Cosmos DB is available in two different capacity modes: provisioned throughput and serverless. You can perform the same database operations in both modes, but the way you get billed for these operations is different. We wil be using provisioned throughput and the free tier for this example.

    Setup the CosmosDB account

    Next, create a new database and container. Go to the Data Explorer in your new Cosmos DB account, and choose New Container. Name the database, and keep all the orher defaults except:

    SettingAction
    Container IDid
    Container partition/id

    Press OK to create a database and container

    A database is analogous to a traditional DBMS namespace. It's used to organize one or more containers.

    Setup the CosmosDB Container

    Now we're ready to set up our logic app an write to Cosmos DB!

    Setup Logic App connection + event

    Once the Cosmos DB SQL API account is created, we can set up our Logic App. From the portal dashboard, Select Create a resource > Integration > Logic App > Create. Name your Logic App and select a location, the rest fo the settings can be left at their defaults. Once you new Logic App is created, select Create a workflow from designer to get started.

    A workflow is a series of steps that defines a task or process. Each workflow starts with a single trigger, after which you must add one or more actions.

    When in designer, search for weather on the right under Add a trigger. Choose MSN Weather. Choose When the current conditions change as the trigger.

    A trigger is always the first step in any workflow and specifies the condition for running any further steps in that workflow.

    Add a location. Valid locations are City, Region, State, Country, Landmark, Postal Code, latitude and longitude. This triggers a new workflow when the conditions change for a location.

    Write data from Logic App to Cosmos DB

    Now we are ready to set up the action to write data to Cosmos DB. Choose add an action and choose Cosmos DB.

    An action is each step in a workflow after the trigger. Every action runs some operation in a workflow.

    In this case, we will be writing a JSON document to the Cosmos DB container we created earlier. Choose Create or Update Document from the actions. At this point you should have a workflow in designer that looks something like this:

    Logic App workflow with trigger

    Start wth the connection for set up the Cosmos DB action. Select Access Key, and provide the primary read-write key (found under keys in Cosmos DB), and the Cosmos DB account ID (without 'documents.azure.com').

    Next, fill in your Cosmos DB Database ID and Collection ID. Create a JSON document bt selecting dynamic content elements and wrapping JSON formatting around them.

    You will need a unique ID for each document that you write to Cosmos DB, for that you can use an expression. Because we declared id to be our unique ID in Cosmos DB, we will use use that for the name. Under expressions, type guid() and press enter to add a unique ID to the JSON document. When complete, you should have a workflow in designer that looks something like this:

    Logic App workflow with trigger and action

    Save the workflow and test the connections by clicking Run Trigger > Run. If connections are working, you should see documents flowing into Cosmos DB over the next few minutes.

    Check the data in Cosmos Db by opening the Data explorer, then choosing the container you created and selecting items. You should see documents similar to this:

    Logic App workflow with trigger and action

    Resources: For self-study!

    Once you've grasped the basics in ths post, there is so much more to learn!

    Thanks for stopping by!

    - - + + \ No newline at end of file diff --git a/blog/18-cloudmail/index.html b/blog/18-cloudmail/index.html index fc90e5dafc..665321cec3 100644 --- a/blog/18-cloudmail/index.html +++ b/blog/18-cloudmail/index.html @@ -14,14 +14,14 @@ - - + +

    18. Logic Apps + Computer Vision

    · 10 min read
    Brian Benz

    Welcome to Day 18 of #30DaysOfServerless!

    Yesterday my Serverless September post introduced you to making Azure Logic Apps and Azure Cosmos DB work together with a sample application that collects weather data. Today I'm sharing a more robust solution that actually reads my mail. Let's learn about Teaching the cloud to read your mail!

    Ready? Let's go!


    What We'll Cover

    • Introduction to the ReadMail solution
    • Setting up Azure storage, Cosmos DB and Computer Vision
    • Connecting it all together with a Logic App
    • Resources: For self-study!


    Introducing the ReadMail solution

    The US Postal system offers a subscription service that sends you images of mail it will be delivering to your home. I decided it would be cool to try getting Azure to collect data based on these images, so that I could categorize my mail and track the types of mail that I received.

    To do this, I used Azure storage, Cosmos DB, Logic Apps, and computer vision. When a new email comes in from the US Postal service (USPS), it triggers a logic app that:

    • Posts attachments to Azure storage
    • Triggers Azure Computer vision to perform an OCR function on attachments
    • Extracts any results into a JSON document
    • Writes the JSON document to Cosmos DB

    workflow for the readmail solution

    In this post I'll walk you through setting up the solution for yourself.

    Prerequisites

    Setup Azure Services

    First, we'll create all of the target environments we need to be used by our Logic App, then we;ll create the Logic App.

    1. Azure Storage

    We'll be using Azure storage to collect attached images from emails as they arrive. Adding images to Azure storage will also trigger a workflow that performs OCR on new attached images and stores the OCR data in Cosmos DB.

    To create a new Azure storage account from the portal dashboard, Select Create a resource > Storage account > Create.

    The Basics tab covers all of the features and information that we will need for this solution:

    SectionFieldRequired or optionalDescription
    Project detailsSubscriptionRequiredSelect the subscription for the new storage account.
    Project detailsResource groupRequiredCreate a new resource group that you will use for storage, Cosmos DB, Computer Vision and the Logic App.
    Instance detailsStorage account nameRequiredChoose a unique name for your storage account. Storage account names must be between 3 and 24 characters in length and may contain numbers and lowercase letters only.
    Instance detailsRegionRequiredSelect the appropriate region for your storage account.
    Instance detailsPerformanceRequiredSelect Standard performance for general-purpose v2 storage accounts (default).
    Instance detailsRedundancyRequiredSelect locally-redundant Storage (LRS) for this example.

    Select Review + create to accept the remaining default options, then validate and create the account.

    2. Azure CosmosDB

    CosmosDB will be used to store the JSON documents returned by the COmputer Vision OCR process.

    See more details and screen shots for setting up CosmosDB in yesterday's Serverless September post - Using Logic Apps with Cosmos DB

    To get started with Cosmos DB, you create an account, then a database, then a container to store JSON documents. To create a new Cosmos DB account from the portal dashboard, Select Create a resource > Azure Cosmos DB > Create. Choose core SQL for the API.

    Select your subscription, then for simplicity use the same resource group you created when you set up storage. Enter an account name and choose a location, select provisioned throughput capacity mode and apply the free tier discount. From here you can select Review and Create, then Create

    Next, create a new database and container. Go to the Data Explorer in your new Cosmos DB account, and choose New Container. Name the database, and keep all the other defaults except:

    SettingAction
    Container IDid
    Container partition/id

    Press OK to create a database and container

    3. Azure Computer Vision

    Azure Cognitive Services' Computer Vision will perform an OCR process on each image attachment that is stored in Azure storage.

    From the portal dashboard, Select Create a resource > AI + Machine Learning > Computer Vision > Create.

    The Basics and Identity tabs cover all of the features and information that we will need for this solution:

    Basics Tab

    SectionFieldRequired or optionalDescription
    Project detailsSubscriptionRequiredSelect the subscription for the new service.
    Project detailsResource groupRequiredUse the same resource group that you used for Azure storage and Cosmos DB.
    Instance detailsRegionRequiredSelect the appropriate region for your Computer Vision service.
    Instance detailsNameRequiredChoose a unique name for your Computer Vision service.
    Instance detailsPricingRequiredSelect the free tier for this example.

    Identity Tab

    SectionFieldRequired or optionalDescription
    System assigned managed identityStatusRequiredEnable system assigned identity to grant the resource access to other existing resources.

    Select Review + create to accept the remaining default options, then validate and create the account.


    Connect it all with a Logic App

    Now we're ready to put this all together in a Logic App workflow!

    1. Create Logic App

    From the portal dashboard, Select Create a resource > Integration > Logic App > Create. Name your Logic App and select a location, the rest of the settings can be left at their defaults.

    2. Create Workflow: Add Trigger

    Once the Logic App is created, select Create a workflow from designer.

    A workflow is a series of steps that defines a task or process. Each workflow starts with a single trigger, after which you must add one or more actions.

    When in designer, search for outlook.com on the right under Add a trigger. Choose outlook.com. Choose When a new email arrives as the trigger.

    A trigger is always the first step in any workflow and specifies the condition for running any further steps in that workflow.

    Set the following values:

    ParameterValue
    FolderInbox
    ImportanceAny
    Only With AttachmentsYes
    Include AttachmentsYes

    Then add a new parameter:

    ParameterValue
    FromAdd the email address that sends you the email with attachments
    3. Create Workflow: Add Action (for Trigger)

    Choose add an action and choose control > for-each.

    logic app for each

    Inside the for-each action, in Select an output from previous steps, choose attachments. Then, again inside the for-each action, add the create blob action:

    Set the following values:

    ParameterValue
    Folder Path/mailreaderinbox
    Blob NameAttachments Name
    Blob ContentAttachments Content

    This extracts attachments from the email and created a new blob for each attachment.

    Next, inside the same for-each action, add the get blob content action.

    Set the following values:

    ParameterValue
    Blobid
    Infer content typeYes

    We create and read from a blob for each attachment because Computer Vision needs a non-virtual source to read from when performing an OCR process. Because we enabled system assigned identity to grant Computer Vision to other existing resources, it can access the blob but not the outlook.com attachment. Also, we pass the ID of the blob to use as a unique ID when writing to Cosmos DB.

    create blob from attachments

    Next, inside the same for-each action, choose add an action and choose control > condition. Set the value to Media Type > is equal to > image/JPEG

    The USPS sends attachments of multiple types, but we only want to scan attachments that have images of our mail, which are always JPEG images. If the condition is true, we will process the image with Computer Vision OCR and write the results to a JSON document in CosmosDB.

    In the True section of the condition, add an action and choose Computer Vision API > Optical Character Recognition (OCR) to JSON.

    Set the following values:

    ParameterValue
    Image SourceImage Content
    Image contentFile Content

    In the same True section of the condition, choose add an action and choose Cosmos DB. Choose Create or Update Document from the actions. Select Access Key, and provide the primary read-write key (found under keys in Cosmos DB), and the Cosmos DB account ID (without 'documents.azure.com').

    Next, fill in your Cosmos DB Database ID and Collection ID. Create a JSON document by selecting dynamic content elements and wrapping JSON formatting around them.

    Be sure to use the ID passed from blob storage as your unique ID for CosmosDB. That way you can troubleshoot and JSON or OCR issues by tracing back the JSON document in Cosmos Db to the blob in Azure storage. Also, include the Computer Vision JSON response, as it contains the results of the Computer Vision OCR scan. all other elements are optional.

    4. TEST WORKFLOW

    When complete, you should have an action the Logic App designer that looks something like this:

    Logic App workflow create or update document in cosmosdb

    Save the workflow and test the connections by clicking Run Trigger > Run. If connections are working, you should see documents flowing into Cosmos DB each time that an email arrives with image attachments.

    Check the data in Cosmos Db by opening the Data explorer, then choosing the container you created and selecting items. You should see documents similar to this:

    Logic App workflow with trigger and action

    1. Congratulations

    You just built your personal ReadMail solution with Logic Apps! 🎉


    Resources: For self-study!

    Once you have an understanding of the basics in ths post, there is so much more to learn!

    Thanks for stopping by!

    - - + + \ No newline at end of file diff --git a/blog/20-events-graph/index.html b/blog/20-events-graph/index.html index 1c5f2dfd2d..13ad365452 100644 --- a/blog/20-events-graph/index.html +++ b/blog/20-events-graph/index.html @@ -14,15 +14,15 @@ - - + +

    20. Integrate with Microsoft Graph

    · 10 min read
    Ayca Bas

    Welcome to Day 20 of #30DaysOfServerless!

    Every day millions of people spend their precious time in productivity tools. What if you use data and intelligence behind the Microsoft applications (Microsoft Teams, Outlook, and many other Office apps) to build seamless automations and custom apps to boost productivity?

    In this post, we'll learn how to build a seamless onboarding experience for new employees joining a company with the power of Microsoft Graph, integrated with Event Hubs and Logic Apps!


    What We'll Cover

    • ✨ The power of Microsoft Graph
    • 🖇️ How do Microsoft Graph and Event Hubs work together?
    • 🛠 Let's Build an Onboarding Workflow!
      • 1️⃣ Setup Azure Event Hubs + Key Vault
      • 2️⃣ Subscribe to users, receive change notifications from Logic Apps
      • 3️⃣ Create Onboarding workflow in the Logic Apps
    • 🚀 Debug: Your onboarding experience
    • ✋ Exercise: Try this tutorial out yourself!
    • 📚 Resources: For Self-Study


    ✨ The Power of Microsoft Graph

    Microsoft Graph is the gateway to data and intelligence in Microsoft 365 platform. Microsoft Graph exploses Rest APIs and client libraries to access data across Microsoft 365 core services such as Calendar, Teams, To Do, Outlook, People, Planner, OneDrive, OneNote and more.

    Overview of Microsoft Graph

    You can build custom experiences by using Microsoft Graph such as automating the onboarding process for new employees. When new employees are created in the Azure Active Directory, they will be automatically added in the Onboarding team on Microsoft Teams.

    Solution architecture


    🖇️ Microsoft Graph with Event Hubs

    Microsoft Graph uses a webhook mechanism to track changes in resources and deliver change notifications to the clients. For example, with Microsoft Graph Change Notifications, you can receive change notifications when:

    • a new task is added in the to-do list
    • a user changes the presence status from busy to available
    • an event is deleted/cancelled from the calendar

    If you'd like to track a large set of resources at a high frequency, use Azure Events Hubs instead of traditional webhooks to receive change notifications. Azure Event Hubs is a popular real-time events ingestion and distribution service built for scale.

    EVENT GRID - PARTNER EVENTS

    Microsoft Graph Change Notifications can be also received by using Azure Event Grid -- currently available for Microsoft Partners! Read the Partner Events Overview documentation for details.

    Setup Azure Event Hubs + Key Vault.

    To get Microsoft Graph Change Notifications delivered to Azure Event Hubs, we'll have to setup Azure Event Hubs and Azure Key Vault. We'll use Azure Key Vault to access to Event Hubs connection string.

    1️⃣ Create Azure Event Hubs

    1. Go to Azure Portal and select Create a resource, type Event Hubs and select click Create.
    2. Fill in the Event Hubs namespace creation details, and then click Create.
    3. Go to the newly created Event Hubs namespace page, select Event Hubs tab from the left pane and + Event Hub:
      • Name your Event Hub as Event Hub
      • Click Create.
    4. Click the name of the Event Hub, and then select Shared access policies and + Add to add a new policy:
      • Give a name to the policy
      • Check Send and Listen
      • Click Create.
    5. After the policy has been created, click the name of the policy to open the details panel, and then copy the Connection string-primary key value. Write it down; you'll need it for the next step.
    6. Go to Consumer groups tab in the left pane and select + Consumer group, give a name for your consumer group as onboarding and select Create.

    2️⃣ Create Azure Key Vault

    1. Go to Azure Portal and select Create a resource, type Key Vault and select Create.
    2. Fill in the Key Vault creation details, and then click Review + Create.
    3. Go to newly created Key Vault and select Secrets tab from the left pane and click + Generate/Import:
      • Give a name to the secret
      • For the value, paste in the connection string you generated at the Event Hubs step
      • Click Create
      • Copy the name of the secret.
    4. Select Access Policies from the left pane and + Add Access Policy:
      • For Secret permissions, select Get
      • For Principal, select Microsoft Graph Change Tracking
      • Click Add.
    5. Select Overview tab from the left pane and copy the Vault URI.

    Subscribe for Logic Apps change notifications

    To start receiving Microsoft Graph Change Notifications, we'll need to create subscription to the resource that we'd like to track - here, 'users'. We'll use Azure Logic Apps to create subscription.

    To create subscription for Microsoft Graph Change Notifications, we'll need to make a http post request to https://graph.microsoft.com/v1.0/subscriptions. Microsoft Graph requires Azure Active Directory authentication make API calls. First, we'll need to register an app to Azure Active Directory, and then we will make the Microsoft Graph Subscription API call with Azure Logic Apps.

    1️⃣ Create an app in Azure Active Directory

    1. In the Azure Portal, go to Azure Active Directory and select App registrations from the left pane and select + New registration. Fill in the details for the new App registration form as below:
      • Name: Graph Subscription Flow Auth
      • Supported account types: Accounts in any organizational directory (Any Azure AD directory - Multitenant) and personal Microsoft accounts (e.g. Skype, Xbox)
      • Select Register.
    2. Go to newly registered app in Azure Active Directory, select API permissions:
      • Select + Add a permission and Microsoft Graph
      • Select Application permissions and add User.Read.All and Directory.Read.All.
      • Select Grant admin consent for the organization
    3. Select Certificates & secrets tab from the left pane, select + New client secret:
      • Choose desired expiry duration
      • Select Add
      • Copy the value of the secret.
    4. Go to Overview from the left pane, copy Application (client) ID and Directory (tenant) ID.

    2️⃣ Create subscription with Azure Logic Apps

    1. Go to Azure Portal and select Create a resource, type Logic apps and select click Create.

    2. Fill in the Logic Apps creation details, and then click Create.

    3. Go to the newly created Logic Apps page, select Workflows tab from the left pane and select + Add:

      • Give a name to the new workflow as graph-subscription-flow
      • Select Stateful as a state type
      • Click Create.
    4. Go to graph-subscription-flow, and then select Designer tab.

    5. In the Choose an operation section, search for Schedule and select Recurrence as a trigger. Fill in the parameters as below:

      • Interval: 61
      • Frequency: Minute
      • Time zone: Select your own time zone
      • Start time: Set a start time
    6. Select + button in the flow and select add an action. Search for HTTP and select HTTP as an action. Fill in the parameters as below:

      • Method: POST
      • URI: https://graph.microsoft.com/v1.0/subscriptions
      • Headers:
        • Key: Content-type
        • Value: application/json
      • Body:
      {
      "changeType": "created, updated",
      "clientState": "secretClientValue",
      "expirationDateTime": "@{addHours(utcNow(), 1)}",
      "notificationUrl": "EventHub:https://<YOUR-VAULT-URI>/secrets/<YOUR-KEY-VAULT-SECRET-NAME>?tenantId=72f988bf-86f1-41af-91ab-2d7cd011db47",
      "resource": "users"
      }

      In notificationUrl, make sure to replace <YOUR-VAULT-URI> with the vault uri and <YOUR-KEY-VAULT-SECRET-NAME> with the secret name that you copied from the Key Vault.

      In resource, define the resource type you'd like to track changes. For our example, we will track changes for users resource.

      • Authentication:
        • Authentication type: Active Directory OAuth
        • Authority: https://login.microsoft.com
        • Tenant: Directory (tenant) ID copied from AAD app
        • Audience: https://graph.microsoft.com
        • Client ID: Application (client) ID copied from AAD app
        • Credential Type: Secret
        • Secret: value of the secret copied from AAD app
    7. Select Save and run your workflow from the Overview tab.

      Check your subscription in Graph Explorer: If you'd like to make sure that your subscription is created successfully by Logic Apps, you can go to Graph Explorer, login with your Microsoft 365 account and make GET request to https://graph.microsoft.com/v1.0/subscriptions. Your subscription should appear in the response after it's created successfully.

    Subscription workflow success

    After subscription is created successfully by Logic Apps, Azure Event Hubs will receive notifications whenever there is a new user created in Azure Active Directory.


    Create Onboarding workflow in Logic Apps

    We'll create a second workflow in the Logic Apps to receive change notifications from Event Hubs when there is a new user created in the Azure Active Directory and add new user in Onboarding team on Microsoft Teams.

    1. Go to the Logic Apps you created in the previous steps, select Workflows tab and create a new workflow by selecting + Add:
      • Give a name to the new workflow as teams-onboarding-flow
      • Select Stateful as a state type
      • Click Create.
    2. Go to teams-onboarding-flow, and then select Designer tab.
    3. In the Choose an operation section, search for Event Hub, select When events are available in Event Hub as a trigger. Setup Event Hub connection as below:
      • Create Connection:
        • Connection name: Connection
        • Authentication Type: Connection String
        • Connection String: Go to Event Hubs > Shared Access Policies > RootManageSharedAccessKey and copy Connection string–primary key
        • Select Create.
      • Parameters:
        • Event Hub Name: Event Hub
        • Consumer Group Name: onboarding
    4. Select + in the flow and add an action, search for Control and add For each as an action. Fill in For each action as below:
      • Select output from previous steps: Events
    5. Inside For each, select + in the flow and add an action, search for Data operations and select Parse JSON. Fill in Parse JSON action as below:
      • Content: Events Content
      • Schema: Copy the json content from schema-parse.json and paste as a schema
    6. Select + in the flow and add an action, search for Control and add For each as an action. Fill in For each action as below:
      • Select output from previous steps: value
      1. Inside For each, select + in the flow and add an action, search for Microsoft Teams and select Add a member to a team. Login with your Microsoft 365 account to create a connection and fill in Add a member to a team action as below:
      • Team: Create an Onboarding team on Microsoft Teams and select
      • A user AAD ID for the user to add to a team: id
    7. Select Save.

    🚀 Debug your onboarding experience

    To debug our onboarding experience, we'll need to create a new user in Azure Active Directory and see if it's added in Microsoft Teams Onboarding team automatically.

    1. Go to Azure Portal and select Azure Active Directory from the left pane and go to Users. Select + New user and Create new user. Fill in the details as below:

      • User name: JaneDoe
      • Name: Jane Doe

      new user in Azure Active Directory

    2. When you added Jane Doe as a new user, it should trigger the teams-onboarding-flow to run. teams onboarding flow success

    3. Once the teams-onboarding-flow runs successfully, you should be able to see Jane Doe as a member of the Onboarding team on Microsoft Teams! 🥳 new member in Onboarding team on Microsoft Teams

    Congratulations! 🎉

    You just built an onboarding experience using Azure Logic Apps, Azure Event Hubs and Azure Key Vault.


    📚 Resources

    - - + + \ No newline at end of file diff --git a/blog/21-cloudevents-via-event-grid/index.html b/blog/21-cloudevents-via-event-grid/index.html index d46b30c7fd..9d29e4be6c 100644 --- a/blog/21-cloudevents-via-event-grid/index.html +++ b/blog/21-cloudevents-via-event-grid/index.html @@ -14,13 +14,13 @@ - - + +

    21. CloudEvents with Event Grid

    · 9 min read
    Justin Yoo

    Welcome to Day 21 of #30DaysOfServerless!

    We've so far walked through what Azure Event Grid is and how it generally works. Today, let's discuss how Azure Event Grid deals with CloudEvents.


    What We'll Cover


    OK. Let's get started!

    What is CloudEvents?

    Needless to say, events are everywhere. Events come not only from event-driven systems but also from many different systems and devices, including IoT ones like Raspberry PI.

    But the problem is that every event publisher (system/device that creates events) describes their events differently, meaning there is no standard way of describing events. It has caused many issues between systems, mainly from the interoperability perspective.

    1. Consistency: No standard way of describing events resulted in developers having to write their own event handling logic for each event source.
    2. Accessibility: There were no common libraries, tooling and infrastructure to deliver events across systems.
    3. Productivity: The overall productivity decreases because of the lack of the standard format of events.

    Cloud Events Logo

    Therefore, CNCF (Cloud-Native Computing Foundation) has brought up the concept, called CloudEvents. CloudEvents is a specification that commonly describes event data. Conforming any event data to this spec will simplify the event declaration and delivery across systems and platforms and more, resulting in a huge productivity increase.

    How Azure Event Grid brokers CloudEvents

    Before CloudEvents, Azure Event Grid described events in their own way. Therefore, if you want to use Azure Event Grid, you should follow the event format/schema that Azure Event Grid declares. However, not every system/service/application follows the Azure Event Grid schema. Therefore, Azure Event Grid now supports CloudEvents spec as input and output formats.

    Azure Event Grid for Azure

    Take a look at the simple diagram below, which describes how Azure Event Grid captures events raised from various Azure services. In this diagram, Azure Key Vault takes the role of the event source or event publisher, and Azure Logic Apps takes the role of the event handler (I'll discuss Azure Logic Apps as the event handler later in this post). We use Azure Event Grid System Topic for Azure.

    Azure Event Grid for Azure

    Therefore, let's create an Azure Event Grid System Topic that captures events raised from Azure Key Vault when a new version of a secret is added.

    Azure Event Grid System Topic for Key Vault

    As Azure Event Grid makes use of the pub/sub pattern, you need to create the Azure Event Grid Subscription to consume the events. Here's the subscription that uses the Event Grid data format:

    ![Azure Event Grid System Subscription for Key Vault in Event Grid Format][./img/21-cloudevents-via-event-grid-03.png]

    Once you create the subscription, create a new version of the secret on Azure Key Vault. Then, Azure Key Vault raises an event, which is captured in the Event Grid format:

    [
    {
    "id": "6f44b9c0-d37e-40e7-89be-f70a6da291cc",
    "topic": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/rg-aegce-krc/providers/Microsoft.KeyVault/vaults/kv-xxxxxxxx",
    "subject": "hello",
    "eventType": "Microsoft.KeyVault.SecretNewVersionCreated",
    "data": {
    "Id": "https://kv-xxxxxxxx.vault.azure.net/secrets/hello/064dfc082fec463f8d4610ed6118811d",
    "VaultName": "kv-xxxxxxxx",
    "ObjectType": "Secret",
    "ObjectName": "hello",
    "Version": "064dfc082fec463f8d4610ed6118811d",
    "NBF": null,
    "EXP": null
    },
    "dataVersion": "1",
    "metadataVersion": "1",
    "eventTime": "2022-09-21T07:08:09.1234567Z"
    }
    ]

    So, how is it different from the CloudEvents format? Let's take a look. According to the spec, the JSON data in CloudEvents might look like this:

    {
    "id" : "C234-1234-1234",
    "source" : "/mycontext",
    "specversion" : "1.0",
    "type" : "com.example.someevent",
    "comexampleextension1" : "value",
    "time" : "2018-04-05T17:31:00Z",
    "datacontenttype" : "application/cloudevents+json",
    "data" : {
    "appinfoA" : "abc",
    "appinfoB" : 123,
    "appinfoC" : true
    }
    }

    This time, let's create another subscription using the CloudEvents schema. Here's how to create the subscription against the system topic:

    Azure Event Grid System Subscription for Key Vault in CloudEvents Format

    Therefore, Azure Key Vault emits the event data in the CloudEvents format:

    {
    "id": "6f44b9c0-d37e-40e7-89be-f70a6da291cc",
    "source": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/rg-aegce-krc/providers/Microsoft.KeyVault/vaults/kv-xxxxxxxx",
    "specversion": "1.0",
    "type": "Microsoft.KeyVault.SecretNewVersionCreated",
    "subject": "hello",
    "time": "2022-09-21T07:08:09.1234567Z",
    "data": {
    "Id": "https://kv-xxxxxxxx.vault.azure.net/secrets/hello/064dfc082fec463f8d4610ed6118811d",
    "VaultName": "kv-xxxxxxxx",
    "ObjectType": "Secret",
    "ObjectName": "hello",
    "Version": "064dfc082fec463f8d4610ed6118811d",
    "NBF": null,
    "EXP": null
    }
    }

    Can you identify some differences between the Event Grid format and the CloudEvents format? Fortunately, both Event Grid schema and CloudEvents schema look similar to each other. But they might be significantly different if you use a different event source outside Azure.

    Azure Event Grid for Systems outside Azure

    As mentioned above, the event data described outside Azure or your own applications within Azure might not be understandable by Azure Event Grid. In this case, we need to use Azure Event Grid Custom Topic. Here's the diagram for it:

    Azure Event Grid for Applications outside Azure

    Let's create the Azure Event Grid Custom Topic. When you create the topic, make sure that you use the CloudEvent schema during the provisioning process:

    Azure Event Grid Custom Topic

    If your application needs to publish events to Azure Event Grid Custom Topic, your application should build the event data in the CloudEvents format. If you use a .NET application, add the NuGet package first.

    dotnet add package Azure.Messaging.EventGrid

    Then, create the publisher instance. You've already got the topic endpoint URL and the access key.

    var topicEndpoint = new Uri("<Azure Event Grid Custom Topic Endpoint URL>");
    var credential = new AzureKeyCredential("<Azure Event Grid Custom Topic Access Key>");
    var publisher = new EventGridPublisherClient(topicEndpoint, credential);

    Now, build the event data like below. Make sure that you follow the CloudEvents schema that requires additional metadata like event source, event type and content type.

    var source = "/your/event/source";
    var type = "com.source.event.your/OnEventOccurs";

    var data = new MyEventData() { Hello = "World" };

    var @event = new CloudEvent(source, type, data);

    And finally, send the event to Azure Event Grid Custom Topic.

    await publisher.SendEventAsync(@event);

    The captured event data looks like the following:

    {
    "id": "cc2b2775-52b8-43b8-a7cc-c1c33c2b2e59",
    "source": "/your/event/source",
    "type": "com.source.event.my/OnEventOccurs",
    "data": {
    "Hello": "World"
    },
    "time": "2022-09-21T07:08:09.1234567+00:00",
    "specversion": "1.0"
    }

    However, due to limitations, someone might insist that their existing application doesn't or can't emit the event data in the CloudEvents format. In this case, what should we do? There's no standard way of sending the event data in the CloudEvents format to Azure Event Grid Custom Topic. One of the approaches we may be able to apply is to put a converter between the existing application and Azure Event Grid Custom Topic like below:

    Azure Event Grid for Applications outside Azure with Converter

    Once the Function app (or any converter app) receives legacy event data, it internally converts the CloudEvents format and publishes it to Azure Event Grid.

    var data = default(MyRequestData);
    using (var reader = new StreamReader(req.Body))
    {
    var serialised = await reader.ReadToEndAsync();
    data = JsonConvert.DeserializeObject<MyRequestData>(serialised);
    }

    var converted = new MyEventData() { Hello = data.Lorem };
    var @event = new CloudEvent(source, type, converted);

    The converted event data is captured like this:

    {
    "id": "df296da3-77cd-4da2-8122-91f631941610",
    "source": "/your/event/source",
    "type": "com.source.event.my/OnEventOccurs",
    "data": {
    "Hello": "ipsum"
    },
    "time": "2022-09-21T07:08:09.1234567+00:00",
    "specversion": "1.0"
    }

    This approach is beneficial in many integration scenarios to make all the event data canonicalised.

    How Azure Logic Apps consumes CloudEvents

    I put Azure Logic Apps as the event handler in the previous diagrams. According to the CloudEvents spec, each event handler must implement request validation to avoid abuse. One good thing about using Azure Logic Apps is that it has already implemented this request validation feature. It implies that we just subscribe to the topic and consume the event data.

    Create a new Logic Apps instance and add the HTTP Request trigger. Once it saves, you will get the endpoint URL.

    Azure Logic Apps with HTTP Request Trigger

    Then, create the Azure Event Grid Subscription with:

    • Endpoint type: Webhook
    • Endpoint URL: The Logic Apps URL from above.

    Azure Logic Apps with HTTP Request Trigger

    Once the subscription is ready, this Logic Apps works well as the event handler. Here's how it receives the CloudEvents data from the subscription.

    Azure Logic Apps that Received CloudEvents data

    Now you've got the CloudEvents data. It's entirely up to you to handle that event data however you want!

    Exercise: Try this yourself!

    You can fork this GitHub repository to your account and play around with it to see how Azure Event Grid with CloudEvents works. Alternatively, the "Deploy to Azure" button below will provision all necessary Azure resources and deploy an Azure Functions app to mimic the event publisher.

    Deploy To Azure

    Resources: For self-study!

    Want to know more about CloudEvents in real-life examples? Here are several resources you can take a look at:

    - - + + \ No newline at end of file diff --git a/blog/24-aca-dotnet/index.html b/blog/24-aca-dotnet/index.html index 7a8fa1a076..2731b598d8 100644 --- a/blog/24-aca-dotnet/index.html +++ b/blog/24-aca-dotnet/index.html @@ -14,13 +14,13 @@ - - + +

    24. Deploy ASP.NET app to ACA

    · 19 min read
    Alex Wolf

    Welcome to Day 24 of #30DaysOfServerless!

    We continue exploring E2E scenarios with this tutorial where you'll deploy a containerized ASP.NET Core 6.0 application to Azure Container Apps.

    The application consists of a front-end web app built using Blazor Server, as well as two Web API projects to manage data. These projects will exist as three separate containers inside of a shared container apps environment.


    What We'll Cover

    • Deploy ASP.NET Core 6.0 app to Azure Container Apps
    • Automate deployment workflows using GitHub Actions
    • Provision and deploy resources using Azure Bicep
    • Exercise: Try this yourself!
    • Resources: For self-study!


    Introduction

    Azure Container Apps enables you to run microservices and containerized applications on a serverless platform. With Container Apps, you enjoy the benefits of running containers while leaving behind the concerns of manually configuring cloud infrastructure and complex container orchestrators.

    In this tutorial, you'll deploy a containerized ASP.NET Core 6.0 application to Azure Container Apps. The application consists of a front-end web app built using Blazor Server, as well as two Web API projects to manage data. These projects will exist as three separate containers inside of a shared container apps environment.

    You will use GitHub Actions in combination with Bicep to deploy the application. These tools provide an approachable and sustainable solution for building CI/CD pipelines and working with Container Apps.

    PRE-REQUISITES

    Architecture

    In this tutorial, we'll setup a container app environment with a separate container for each project in the sample store app. The major components of the sample project include:

    • A Blazor Server front-end web app to display product information
    • A products API to list available products
    • An inventory API to determine how many products are in stock
    • GitHub Actions and Bicep templates to provision Azure resources and then build and deploy the sample app.

    You will explore these templates later in the tutorial.

    Public internet traffic should be proxied to the Blazor app. The back-end APIs should only be reachable via requests from the Blazor app inside the container apps environment. This setup can be achieved using container apps environment ingress configurations during deployment.

    An architecture diagram of the shopping app


    Project Sources

    Want to follow along? Fork the sample below. The tutorial can be completed with or without Dapr integration. Pick the path you feel comfortable in. Dapr provides various benefits that make working with Microservices easier - you can learn more in the docs. For this tutorial you will need GitHub and Azure CLI.

    PICK YOUR PATH

    To follow along with this tutorial, fork the relevant sample project below.

    You can run the app locally from Visual Studio:

    • Right click on the Blazor Store project and select Set as Startup Project.
    • Press the start button at the top of Visual Studio to run the app.
    • (Once running) start each API in the background by
    • right-clicking on the project node
    • selecting Debug --> Start without debugging.

    Once the Blazor app is running, you should see something like this:

    An architecture diagram of the shopping app


    Configuring Azure credentials

    In order to deploy the application to Azure through GitHub Actions, you first need to create a service principal. The service principal will allow the GitHub Actions process to authenticate to your Azure subscription to create resources and deploy code. You can learn more about Service Principals in the Azure CLI documentation. For this step you'll need to be logged into the Azure CLI.

    1) If you have not done so already, make sure to fork the sample project to your own GitHub account or organization.

    1) Once you have completed this step, create a service principal using the Azure CLI command below:

    ```azurecli
    $subscriptionId=$(az account show --query id --output tsv)
    az ad sp create-for-rbac --sdk-auth --name WebAndApiSample --role Contributor --scopes /subscriptions/$subscriptionId
    ```

    1) Copy the JSON output of the CLI command to your clipboard

    1) Under the settings tab of your forked GitHub repo, create a new secret named AzureSPN. The name is important to match the Bicep templates included in the project, which we'll review later. Paste the copied service principal values on your clipboard into the secret and save your changes. This new secret will be used by the GitHub Actions workflow to authenticate to Azure.

    :::image type="content" source="./img/dotnet/github-secrets.png" alt-text="A screenshot of adding GitHub secrets.":::

    Deploy using Github Actions

    You are now ready to deploy the application to Azure Container Apps using GitHub Actions. The sample application includes a GitHub Actions template that is configured to build and deploy any changes to a branch named deploy. The deploy branch does not exist in your forked repository by default, but you can easily create it through the GitHub user interface.

    1) Switch to the Actions tab along the top navigation of your GitHub repository. If you have not done so already, ensure that workflows are enabled by clicking the button in the center of the page.

    A screenshot showing how to enable GitHub actions

    1) Navigate to the main Code tab of your repository and select the main dropdown. Enter deploy into the branch input box, and then select Create branch: deploy from 'main'.

    A screenshot showing how to create the deploy branch

    1) On the new deploy branch, navigate down into the .github/workflows folder. You should see a file called deploy.yml, which contains the main GitHub Actions workflow script. Click on the file to view its content. You'll learn more about this file later in the tutorial.

    1) Click the pencil icon in the upper right to edit the document.

    1) Change the RESOURCE_GROUP_NAME: value to msdocswebappapis or another valid resource group name of your choosing.

    1) In the upper right of the screen, select Start commit and then Commit changes to commit your edit. This will persist the change to the file and trigger the GitHub Actions workflow to build and deploy the app.

    A screenshot showing how to commit changes

    1) Switch to the Actions tab along the top navigation again. You should see the workflow running to create the necessary resources and deploy the app. The workflow may take several minutes to run. When it completes successfully, all of the jobs should have a green checkmark icon next to them.

    The completed GitHub workflow.

    Explore the Azure resources

    Once the GitHub Actions workflow has completed successfully you can browse the created resources in the Azure portal.

    1) On the left navigation, select Resource Groups. Next,choose the msdocswebappapis resource group that was created by the GitHub Actions workflow.

    2) You should see seven resources available that match the screenshot and table descriptions below.

    The resources created in Azure.

    Resource nameTypeDescription
    inventoryContainer appThe containerized inventory API.
    msdocswebappapisacrContainer registryA registry that stores the built Container images for your apps.
    msdocswebappapisaiApplication insightsApplication insights provides advanced monitoring, logging and metrics for your apps.
    msdocswebappapisenvContainer apps environmentA container environment that manages networking, security and resource concerns. All of your containers live in this environment.
    msdocswebappapislogsLog Analytics workspaceA workspace environment for managing logging and analytics for the container apps environment
    productsContainer appThe containerized products API.
    storeContainer appThe Blazor front-end web app.

    3) You can view your running app in the browser by clicking on the store container app. On the overview page, click the Application Url link on the upper right of the screen.

    :::image type="content" source="./img/dotnet/application-url.png" alt-text="The link to browse the app.":::

    Understanding the GitHub Actions workflow

    The GitHub Actions workflow created and deployed resources to Azure using the deploy.yml file in the .github folder at the root of the project. The primary purpose of this file is to respond to events - such as commits to a branch - and run jobs to accomplish tasks. The deploy.yml file in the sample project has three main jobs:

    • Provision: Create the necessary resources in Azure, such as the container apps environment. This step leverages Bicep templates to create the Azure resources, which you'll explore in a moment.
    • Build: Create the container images for the three apps in the project and store them in the container registry.
    • Deploy: Deploy the container images to the different container apps created during the provisioning job.

    The deploy.yml file also accepts parameters to make the workflow more dynamic, such as setting the resource group name or the Azure region resources will be provisioned to.

    Below is a commented version of the deploy.yml file that highlights the essential steps.

    name: Build and deploy .NET application to Container Apps

    # Trigger the workflow on pushes to the deploy branch
    on:
    push:
    branches:
    - deploy

    env:
    # Set workflow variables
    RESOURCE_GROUP_NAME: msdocswebappapis

    REGION: eastus

    STORE_DOCKER: Store/Dockerfile
    STORE_IMAGE: store

    INVENTORY_DOCKER: Store.InventoryApi/Dockerfile
    INVENTORY_IMAGE: inventory

    PRODUCTS_DOCKER: Store.ProductApi/Dockerfile
    PRODUCTS_IMAGE: products

    jobs:
    # Create the required Azure resources
    provision:
    runs-on: ubuntu-latest

    steps:

    - name: Checkout to the branch
    uses: actions/checkout@v2

    - name: Azure Login
    uses: azure/login@v1
    with:
    creds: ${{ secrets.AzureSPN }}

    - name: Create resource group
    uses: azure/CLI@v1
    with:
    inlineScript: >
    echo "Creating resource group in Azure"
    echo "Executing 'az group create -l ${{ env.REGION }} -n ${{ env.RESOURCE_GROUP_NAME }}'"
    az group create -l ${{ env.REGION }} -n ${{ env.RESOURCE_GROUP_NAME }}

    # Use Bicep templates to create the resources in Azure
    - name: Creating resources
    uses: azure/CLI@v1
    with:
    inlineScript: >
    echo "Creating resources"
    az deployment group create --resource-group ${{ env.RESOURCE_GROUP_NAME }} --template-file '/github/workspace/Azure/main.bicep' --debug

    # Build the three app container images
    build:
    runs-on: ubuntu-latest
    needs: provision

    steps:

    - name: Checkout to the branch
    uses: actions/checkout@v2

    - name: Azure Login
    uses: azure/login@v1
    with:
    creds: ${{ secrets.AzureSPN }}

    - name: Set up Docker Buildx
    uses: docker/setup-buildx-action@v1

    - name: Login to ACR
    run: |
    set -euo pipefail
    access_token=$(az account get-access-token --query accessToken -o tsv)
    refresh_token=$(curl https://${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io/oauth2/exchange -v -d "grant_type=access_token&service=${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io&access_token=$access_token" | jq -r .refresh_token)
    docker login -u 00000000-0000-0000-0000-000000000000 --password-stdin ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io <<< "$refresh_token"

    - name: Build the products api image and push it to ACR
    uses: docker/build-push-action@v2
    with:
    push: true
    tags: ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io/${{ env.PRODUCTS_IMAGE }}:${{ github.sha }}
    file: ${{ env.PRODUCTS_DOCKER }}

    - name: Build the inventory api image and push it to ACR
    uses: docker/build-push-action@v2
    with:
    push: true
    tags: ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io/${{ env.INVENTORY_IMAGE }}:${{ github.sha }}
    file: ${{ env.INVENTORY_DOCKER }}

    - name: Build the frontend image and push it to ACR
    uses: docker/build-push-action@v2
    with:
    push: true
    tags: ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io/${{ env.STORE_IMAGE }}:${{ github.sha }}
    file: ${{ env.STORE_DOCKER }}

    # Deploy the three container images
    deploy:
    runs-on: ubuntu-latest
    needs: build

    steps:

    - name: Checkout to the branch
    uses: actions/checkout@v2

    - name: Azure Login
    uses: azure/login@v1
    with:
    creds: ${{ secrets.AzureSPN }}

    - name: Installing Container Apps extension
    uses: azure/CLI@v1
    with:
    inlineScript: >
    az config set extension.use_dynamic_install=yes_without_prompt

    az extension add --name containerapp --yes

    - name: Login to ACR
    run: |
    set -euo pipefail
    access_token=$(az account get-access-token --query accessToken -o tsv)
    refresh_token=$(curl https://${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io/oauth2/exchange -v -d "grant_type=access_token&service=${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io&access_token=$access_token" | jq -r .refresh_token)
    docker login -u 00000000-0000-0000-0000-000000000000 --password-stdin ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io <<< "$refresh_token"

    - name: Deploy Container Apps
    uses: azure/CLI@v1
    with:
    inlineScript: >
    az containerapp registry set -n products -g ${{ env.RESOURCE_GROUP_NAME }} --server ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io

    az containerapp update -n products -g ${{ env.RESOURCE_GROUP_NAME }} -i ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io/${{ env.PRODUCTS_IMAGE }}:${{ github.sha }}

    az containerapp registry set -n inventory -g ${{ env.RESOURCE_GROUP_NAME }} --server ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io

    az containerapp update -n inventory -g ${{ env.RESOURCE_GROUP_NAME }} -i ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io/${{ env.INVENTORY_IMAGE }}:${{ github.sha }}

    az containerapp registry set -n store -g ${{ env.RESOURCE_GROUP_NAME }} --server ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io

    az containerapp update -n store -g ${{ env.RESOURCE_GROUP_NAME }} -i ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io/${{ env.STORE_IMAGE }}:${{ github.sha }}

    - name: logout
    run: >
    az logout

    Understanding the Bicep templates

    During the provisioning stage of the GitHub Actions workflow, the main.bicep file is processed. Bicep files provide a declarative way of generating resources in Azure and are ideal for managing infrastructure as code. You can learn more about Bicep in the related documentation. The main.bicep file in the sample project creates the following resources:

    • The container registry to store images of the containerized apps.
    • The container apps environment, which handles networking and resource management for the container apps.
    • Three container apps - one for the Blazor front-end and two for the back-end product and inventory APIs.
    • Configuration values to connect these services together

    main.bicep without Dapr

    param location string = resourceGroup().location

    # create the azure container registry
    resource acr 'Microsoft.ContainerRegistry/registries@2021-09-01' = {
    name: toLower('${resourceGroup().name}acr')
    location: location
    sku: {
    name: 'Basic'
    }
    properties: {
    adminUserEnabled: true
    }
    }

    # create the aca environment
    module env 'environment.bicep' = {
    name: 'containerAppEnvironment'
    params: {
    location: location
    }
    }

    # create the various configuration pairs
    var shared_config = [
    {
    name: 'ASPNETCORE_ENVIRONMENT'
    value: 'Development'
    }
    {
    name: 'APPINSIGHTS_INSTRUMENTATIONKEY'
    value: env.outputs.appInsightsInstrumentationKey
    }
    {
    name: 'APPLICATIONINSIGHTS_CONNECTION_STRING'
    value: env.outputs.appInsightsConnectionString
    }
    ]

    # create the products api container app
    module products 'container_app.bicep' = {
    name: 'products'
    params: {
    name: 'products'
    location: location
    registryPassword: acr.listCredentials().passwords[0].value
    registryUsername: acr.listCredentials().username
    containerAppEnvironmentId: env.outputs.id
    registry: acr.name
    envVars: shared_config
    externalIngress: false
    }
    }

    # create the inventory api container app
    module inventory 'container_app.bicep' = {
    name: 'inventory'
    params: {
    name: 'inventory'
    location: location
    registryPassword: acr.listCredentials().passwords[0].value
    registryUsername: acr.listCredentials().username
    containerAppEnvironmentId: env.outputs.id
    registry: acr.name
    envVars: shared_config
    externalIngress: false
    }
    }

    # create the store api container app
    var frontend_config = [
    {
    name: 'ProductsApi'
    value: 'http://${products.outputs.fqdn}'
    }
    {
    name: 'InventoryApi'
    value: 'http://${inventory.outputs.fqdn}'
    }
    ]

    module store 'container_app.bicep' = {
    name: 'store'
    params: {
    name: 'store'
    location: location
    registryPassword: acr.listCredentials().passwords[0].value
    registryUsername: acr.listCredentials().username
    containerAppEnvironmentId: env.outputs.id
    registry: acr.name
    envVars: union(shared_config, frontend_config)
    externalIngress: true
    }
    }

    main.bicep with Dapr


    param location string = resourceGroup().location

    # create the azure container registry
    resource acr 'Microsoft.ContainerRegistry/registries@2021-09-01' = {
    name: toLower('${resourceGroup().name}acr')
    location: location
    sku: {
    name: 'Basic'
    }
    properties: {
    adminUserEnabled: true
    }
    }

    # create the aca environment
    module env 'environment.bicep' = {
    name: 'containerAppEnvironment'
    params: {
    location: location
    }
    }

    # create the various config pairs
    var shared_config = [
    {
    name: 'ASPNETCORE_ENVIRONMENT'
    value: 'Development'
    }
    {
    name: 'APPINSIGHTS_INSTRUMENTATIONKEY'
    value: env.outputs.appInsightsInstrumentationKey
    }
    {
    name: 'APPLICATIONINSIGHTS_CONNECTION_STRING'
    value: env.outputs.appInsightsConnectionString
    }
    ]

    # create the products api container app
    module products 'container_app.bicep' = {
    name: 'products'
    params: {
    name: 'products'
    location: location
    registryPassword: acr.listCredentials().passwords[0].value
    registryUsername: acr.listCredentials().username
    containerAppEnvironmentId: env.outputs.id
    registry: acr.name
    envVars: shared_config
    externalIngress: false
    }
    }

    # create the inventory api container app
    module inventory 'container_app.bicep' = {
    name: 'inventory'
    params: {
    name: 'inventory'
    location: location
    registryPassword: acr.listCredentials().passwords[0].value
    registryUsername: acr.listCredentials().username
    containerAppEnvironmentId: env.outputs.id
    registry: acr.name
    envVars: shared_config
    externalIngress: false
    }
    }

    # create the store api container app
    module store 'container_app.bicep' = {
    name: 'store'
    params: {
    name: 'store'
    location: location
    registryPassword: acr.listCredentials().passwords[0].value
    registryUsername: acr.listCredentials().username
    containerAppEnvironmentId: env.outputs.id
    registry: acr.name
    envVars: shared_config
    externalIngress: true
    }
    }


    Bicep Modules

    The main.bicep file references modules to create resources, such as module products. Modules are a feature of Bicep templates that enable you to abstract resource declarations into their own files or sub-templates. As the main.bicep file is processed, the defined modules are also evaluated. Modules allow you to create resources in a more organized and reusable way. They can also define input and output parameters that are passed to and from the parent template, such as the name of a resource.

    For example, the environment.bicep module extracts the details of creating a container apps environment into a reusable template. The module defines necessary resource dependencies such as Log Analytics Workspaces and an Application Insights instance.

    environment.bicep without Dapr

    param baseName string = resourceGroup().name
    param location string = resourceGroup().location

    resource logs 'Microsoft.OperationalInsights/workspaces@2021-06-01' = {
    name: '${baseName}logs'
    location: location
    properties: any({
    retentionInDays: 30
    features: {
    searchVersion: 1
    }
    sku: {
    name: 'PerGB2018'
    }
    })
    }

    resource appInsights 'Microsoft.Insights/components@2020-02-02' = {
    name: '${baseName}ai'
    location: location
    kind: 'web'
    properties: {
    Application_Type: 'web'
    WorkspaceResourceId: logs.id
    }
    }

    resource env 'Microsoft.App/managedEnvironments@2022-01-01-preview' = {
    name: '${baseName}env'
    location: location
    properties: {
    appLogsConfiguration: {
    destination: 'log-analytics'
    logAnalyticsConfiguration: {
    customerId: logs.properties.customerId
    sharedKey: logs.listKeys().primarySharedKey
    }
    }
    }
    }

    output id string = env.id
    output appInsightsInstrumentationKey string = appInsights.properties.InstrumentationKey
    output appInsightsConnectionString string = appInsights.properties.ConnectionString

    environment.bicep with Dapr


    param baseName string = resourceGroup().name
    param location string = resourceGroup().location

    resource logs 'Microsoft.OperationalInsights/workspaces@2021-06-01' = {
    name: '${baseName}logs'
    location: location
    properties: any({
    retentionInDays: 30
    features: {
    searchVersion: 1
    }
    sku: {
    name: 'PerGB2018'
    }
    })
    }

    resource appInsights 'Microsoft.Insights/components@2020-02-02' = {
    name: '${baseName}ai'
    location: location
    kind: 'web'
    properties: {
    Application_Type: 'web'
    WorkspaceResourceId: logs.id
    }
    }

    resource env 'Microsoft.App/managedEnvironments@2022-01-01-preview' = {
    name: '${baseName}env'
    location: location
    properties: {
    appLogsConfiguration: {
    destination: 'log-analytics'
    logAnalyticsConfiguration: {
    customerId: logs.properties.customerId
    sharedKey: logs.listKeys().primarySharedKey
    }
    }
    }
    }

    output id string = env.id
    output appInsightsInstrumentationKey string = appInsights.properties.InstrumentationKey
    output appInsightsConnectionString string = appInsights.properties.ConnectionString


    The container_apps.bicep template defines numerous parameters to provide a reusable template for creating container apps. This allows the module to be used in other CI/CD pipelines as well.

    container_app.bicep without Dapr

    param name string
    param location string = resourceGroup().location
    param containerAppEnvironmentId string
    param repositoryImage string = 'mcr.microsoft.com/azuredocs/containerapps-helloworld:latest'
    param envVars array = []
    param registry string
    param minReplicas int = 1
    param maxReplicas int = 1
    param port int = 80
    param externalIngress bool = false
    param allowInsecure bool = true
    param transport string = 'http'
    param registryUsername string
    @secure()
    param registryPassword string

    resource containerApp 'Microsoft.App/containerApps@2022-01-01-preview' ={
    name: name
    location: location
    properties:{
    managedEnvironmentId: containerAppEnvironmentId
    configuration: {
    activeRevisionsMode: 'single'
    secrets: [
    {
    name: 'container-registry-password'
    value: registryPassword
    }
    ]
    registries: [
    {
    server: registry
    username: registryUsername
    passwordSecretRef: 'container-registry-password'
    }
    ]
    ingress: {
    external: externalIngress
    targetPort: port
    transport: transport
    allowInsecure: allowInsecure
    }
    }
    template: {
    containers: [
    {
    image: repositoryImage
    name: name
    env: envVars
    }
    ]
    scale: {
    minReplicas: minReplicas
    maxReplicas: maxReplicas
    }
    }
    }
    }

    output fqdn string = containerApp.properties.configuration.ingress.fqdn

    container_app.bicep with Dapr


    param name string
    param location string = resourceGroup().location
    param containerAppEnvironmentId string
    param repositoryImage string = 'mcr.microsoft.com/azuredocs/containerapps-helloworld:latest'
    param envVars array = []
    param registry string
    param minReplicas int = 1
    param maxReplicas int = 1
    param port int = 80
    param externalIngress bool = false
    param allowInsecure bool = true
    param transport string = 'http'
    param appProtocol string = 'http'
    param registryUsername string
    @secure()
    param registryPassword string

    resource containerApp 'Microsoft.App/containerApps@2022-01-01-preview' ={
    name: name
    location: location
    properties:{
    managedEnvironmentId: containerAppEnvironmentId
    configuration: {
    dapr: {
    enabled: true
    appId: name
    appPort: port
    appProtocol: appProtocol
    }
    activeRevisionsMode: 'single'
    secrets: [
    {
    name: 'container-registry-password'
    value: registryPassword
    }
    ]
    registries: [
    {
    server: registry
    username: registryUsername
    passwordSecretRef: 'container-registry-password'
    }
    ]
    ingress: {
    external: externalIngress
    targetPort: port
    transport: transport
    allowInsecure: allowInsecure
    }
    }
    template: {
    containers: [
    {
    image: repositoryImage
    name: name
    env: envVars
    }
    ]
    scale: {
    minReplicas: minReplicas
    maxReplicas: maxReplicas
    }
    }
    }
    }

    output fqdn string = containerApp.properties.configuration.ingress.fqdn


    Understanding configuration differences with Dapr

    The code for this specific sample application is largely the same whether or not Dapr is integrated. However, even with this simple app, there are a few benefits and configuration differences when using Dapr that are worth exploring.

    In this scenario most of the changes are related to communication between the container apps. However, you can explore the full range of Dapr benefits by reading the Dapr integration with Azure Container Apps article in the conceptual documentation.

    Without Dapr

    Without Dapr the main.bicep template handles wiring up the front-end store app to communicate with the back-end apis by manually managing environment variables. The bicep template retrieves the fully qualified domains (fqdn) of the API apps as output parameters when they are created. Those configurations are then set as environment variables on the store container app.


    # Retrieve environment variables from API container creation
    var frontend_config = [
    {
    name: 'ProductsApi'
    value: 'http://${products.outputs.fqdn}'
    }
    {
    name: 'InventoryApi'
    value: 'http://${inventory.outputs.fqdn}'
    }
    ]

    # create the store api container app, passing in config
    module store 'container_app.bicep' = {
    name: 'store'
    params: {
    name: 'store'
    location: location
    registryPassword: acr.listCredentials().passwords[0].value
    registryUsername: acr.listCredentials().username
    containerAppEnvironmentId: env.outputs.id
    registry: acr.name
    envVars: union(shared_config, frontend_config)
    externalIngress: true
    }
    }

    The environment variables are then retrieved inside of the program class and used to configure the base URLs of the corresponding HTTP clients.


    builder.Services.AddHttpClient("Products", (httpClient) => httpClient.BaseAddress = new Uri(builder.Configuration.GetValue<string>("ProductsApi")));
    builder.Services.AddHttpClient("Inventory", (httpClient) => httpClient.BaseAddress = new Uri(builder.Configuration.GetValue<string>("InventoryApi")));

    With Dapr

    Dapr can be enabled on a container app when it is created, as seen below. This configuration adds a Dapr sidecar to the app to streamline discovery and communication features between the different container apps in your environment.


    # Create the container app with Dapr enabled
    resource containerApp 'Microsoft.App/containerApps@2022-01-01-preview' ={
    name: name
    location: location
    properties:{
    managedEnvironmentId: containerAppEnvironmentId
    configuration: {
    dapr: {
    enabled: true
    appId: name
    appPort: port
    appProtocol: appProtocol
    }
    activeRevisionsMode: 'single'
    secrets: [
    {
    name: 'container-registry-password'
    value: registryPassword
    }
    ]

    # Rest of template omitted for brevity...
    }
    }

    Some of these Dapr features can be surfaced through the program file. You can configure your HttpClient to leverage Dapr configurations when communicating with other apps in your environment.


    // reconfigure code to make requests to Dapr sidecar
    var baseURL = (Environment.GetEnvironmentVariable("BASE_URL") ?? "http://localhost") + ":" + (Environment.GetEnvironmentVariable("DAPR_HTTP_PORT") ?? "3500");
    builder.Services.AddHttpClient("Products", (httpClient) =>
    {
    httpClient.BaseAddress = new Uri(baseURL);
    httpClient.DefaultRequestHeaders.Add("dapr-app-id", "Products");
    });

    builder.Services.AddHttpClient("Inventory", (httpClient) =>
    {
    httpClient.BaseAddress = new Uri(baseURL);
    httpClient.DefaultRequestHeaders.Add("dapr-app-id", "Inventory");
    });


    Clean up resources

    If you're not going to continue to use this application, you can delete the Azure Container Apps and all the associated services by removing the resource group.

    Follow these steps in the Azure portal to remove the resources you created:

    1. In the Azure portal, navigate to the msdocswebappsapi resource group using the left navigation or search bar.
    2. Select the Delete resource group button at the top of the resource group Overview.
    3. Enter the resource group name msdocswebappsapi in the Are you sure you want to delete "msdocswebappsapi" confirmation dialog.
    4. Select Delete.
      The process to delete the resource group may take a few minutes to complete.
    - - + + \ No newline at end of file diff --git a/blog/25-aca-java/index.html b/blog/25-aca-java/index.html index 155e20d9a9..7c5d76a16c 100644 --- a/blog/25-aca-java/index.html +++ b/blog/25-aca-java/index.html @@ -14,13 +14,13 @@ - - + +

    25. Deploy Spring Boot App to ACA

    · 7 min read
    Brian Benz

    Welcome to Day 25 of #30DaysOfServerless!

    Azure Container Apps enable application code packaged in containers to run and scale without the overhead of managing cloud infrastructure and container orchestration. In this post I'll show you how to deploy a Java application running on Spring Boot in a container to Azure Container Registry and Azure Container Apps.


    What We'll Cover

    • Introduction to Deploying Java containers in the cloud
    • Step-by-step: Deploying to Azure Container Registry
    • Step-by-step: Deploying and running on Azure Container Apps
    • Resources: For self-study!


    Deploy Java containers to cloud

    We'll deploy a Java application running on Spring Boot in a container to Azure Container Registry and Azure Container Apps. Here are the main steps:

    • Create Azure Container Registry (ACR) on Azure portal
    • Create Azure Container App (ACA) on Azure portal.
    • Deploy code to Azure Container Registry from the Azure CLI.
    • Deploy container from ACR to ACA using the Azure portal.
    PRE-REQUISITES

    Sign in to Azure from the CLI using the az login command, and follow the prompts in your browser to complete the authentication process. Also, ensure you're running the latest version of the CLI by using the az upgrade command.

    1. Get Sample Code

    Fork and clone the sample GitHub repo to your local machine. Navigate to the and click Fork in the top-right corner of the page.

    The example code that we're using is a very basic containerized Spring Boot example. There are a lot more details to learn about Spring boot apps in docker, for a deep dive check out this Spring Boot Guide

    2. Run Sample Locally (Optional)

    If you have docker installed locally, you can optionally test the code on your local machine. Navigate to the root directory of the forked repository and run the following commands:

    docker build -t spring-boot-docker-aca .
    docker run -p 8080:8080 spring-boot-docker-aca

    Open a browser and go to https://localhost:8080. You should see this message:

    Hello Docker World

    That indicates the the Spring Boot app is successfully running locally in a docker container.

    Next, let's set up an Azure Container Registry an an Azure Container App and deploy this container to the cloud!


    3. Step-by-step: Deploy to ACR

    To create a container registry from the portal dashboard, Select Create a resource > Containers > Container Registry.

    Navigate to container registry in portal

    In the Basics tab, enter values for Resource group and Registry name. The registry name must be unique within Azure, and contain 5-50 alphanumeric characters. Create a new resource group in the West US location named spring-boot-docker-aca. Select the 'Basic' SKU.

    Keep the default values for the remaining settings. Then select Review + create, then Create. When the Deployment succeeded message appears, select the container registry in the portal.

    Note the registry server name ending with azurecr.io. You will use this in the following steps when you push and pull images with Docker.

    3.1 Log into registry using the Azure CLI

    Before pushing and pulling container images, you must log in to the registry instance. Sign into the Azure CLI on your local machine, then run the az acr login command. For this step, use the registry name, not the server name ending with azurecr.io.

    From the command line, type:

    az acr login --name myregistryname

    The command returns Login Succeeded once completed.

    3.2 Build & deploy with az acr build

    Next, we're going to deploy the docker container we created earlier using the AZ ACR Build command. AZ ACR Build creates a docker build from local code and pushes the container to Azure Container Registry if the build is successful.

    Go to your local clone of the spring-boot-docker-aca repo in the command line, type:

    az acr build --registry myregistryname --image spring-boot-docker-aca:v1 .

    3.3 List container images

    Once the AZ ACR Build command is complete, you should be able to view the container as a repository in the registry. In the portal, open your registry and select Repositories, then select the spring-boot-docker-aca repository you created with docker push. You should also see the v1 image under Tags.

    4. Deploy on ACA

    Now that we have an image in the Azure Container Registry, we can deploy it to Azure Container Apps. For the first deployment, we'll pull the container from our ACR as part of the ACA setup.

    4.1 Create a container app

    We'll create the container app at the same place that we created the container registry in the Azure portal. From the portal, select Create a resource > Containers > Container App. In the Basics tab, set these values:

    4.2 Enter project details

    SettingAction
    SubscriptionYour Azure subscription.
    Resource groupUse the spring-boot-docker-aca resource group
    Container app nameEnter spring-boot-docker-aca.

    4.3 Create an environment

    1. In the Create Container App environment field, select Create new.

    2. In the Create Container App Environment page on the Basics tab, enter the following values:

      SettingValue
      Environment nameEnter my-environment.
      RegionSelect westus3.
    3. Select OK.

    4. Select the Create button at the bottom of the Create Container App Environment page.

    5. Select the Next: App settings button at the bottom of the page.

    5. App settings tab

    The App settings tab is where you connect to the ACR and pull the repository image:

    SettingAction
    Use quickstart imageUncheck the checkbox.
    NameEnter spring-boot-docker-aca.
    Image sourceSelect Azure Container Registry
    RegistrySelect your ACR from the list.
    ImageSelect spring-boot-docker-aca from the list.
    Image TagSelect v1 from the list.

    5.1 Application ingress settings

    SettingAction
    IngressSelect Enabled.
    Ingress visibilitySelect External to publicly expose your container app.
    Target portEnter 8080.

    5.2 Deploy the container app

    1. Select the Review and create button at the bottom of the page.
    2. Select Create.

    Once the deployment is successfully completed, you'll see the message: Your deployment is complete.

    5.3 Verify deployment

    In the portal, go to the Overview of your spring-boot-docker-aca Azure Container App, and click on the Application Url. You should see this message in the browser:

    Hello Docker World

    That indicates the the Spring Boot app is running in a docker container in your spring-boot-docker-aca Azure Container App.

    Resources: For self-study!

    Once you have an understanding of the basics in ths post, there is so much more to learn!

    Thanks for stopping by!

    - - + + \ No newline at end of file diff --git a/blog/28-where-am-i/index.html b/blog/28-where-am-i/index.html index fddc741a4b..ed38f7736f 100644 --- a/blog/28-where-am-i/index.html +++ b/blog/28-where-am-i/index.html @@ -14,13 +14,13 @@ - - + +

    28. Serverless + Power Platforms

    · 14 min read
    Justin Yoo

    Welcome to Day 28 of #30DaysOfServerless!

    Since it's the serverless end-to-end week, I'm going to discuss how to use a serverless application Azure Functions with OpenAPI extension to be seamlessly integrated with Power Platform custom connector through Azure API Management - in a post I call "Where am I? My GPS Location with Serverless Power Platform Custom Connector"

    OK. Are you ready? Let's get started!


    What We'll Cover

    • What is Power Platform custom connector?
    • Proxy app to Google Maps and Naver Map API
    • API Management integration
    • Two ways of building custom connector
    • Where am I? Power Apps app
    • Exercise: Try this yourself!
    • Resources: For self-study!


    SAMPLE REPO

    Want to follow along? Check out the sample app on GitHub repository used in this post.

    What is Power Platform custom connector?

    Power Platform is a low-code/no-code application development tool for fusion teams that consist of a group of people. Those people come from various disciplines, including field experts (domain experts), IT professionals and professional developers, to draw business values successfully. Within the fusion team, the domain experts become citizen developers or low-code developers by Power Platform. In addition, Making Power Platform more powerful is that it offers hundreds of connectors to other Microsoft 365 and third-party services like SAP, ServiceNow, Salesforce, Google, etc.

    However, what if you want to use your internal APIs or APIs not yet offering their official connectors? Here's an example. If your company has an inventory management system, and you want to use it within your Power Apps or Power Automate. That point is exactly where Power Platform custom connectors is necessary.

    Inventory Management System for Power Apps

    Therefore, Power Platform custom connectors enrich those citizen developers' capabilities because those connectors can connect any API applications for the citizen developers to use.

    In this post, let's build a custom connector that provides a static map image generated by Google Maps API and Naver Map API using your GPS location.

    Proxy app to Google Maps and Naver Map API

    First, let's build an Azure Functions app that connects to Google Maps and Naver Map. Suppose that you've already got the API keys for both services. If you haven't yet, get the keys first by visiting here for Google and here for Naver. Then, store them to local.settings.json within your Azure Functions app.

    {
    "Values": {
    ...
    "Maps__Google__ApiKey": "<GOOGLE_MAPS_API_KEY>",
    "Maps__Naver__ClientId": "<NAVER_MAP_API_CLIENT_ID>",
    "Maps__Naver__ClientSecret": "<NAVER_MAP_API_CLIENT_SECRET>"
    }
    }

    Here's the sample logic to get the static image from Google Maps API. It takes the latitude and longitude of your current location and image zoom level, then returns the static map image. There are a few hard-coded assumptions, though:

    • The image size should be 400x400.
    • The image should be in .png format.
    • The marker should show be red and show my location.
    public class GoogleMapService : IMapService
    {
    public async Task<byte[]> GetMapAsync(HttpRequest req)
    {
    var latitude = req.Query["lat"];
    var longitude = req.Query["long"];
    var zoom = (string)req.Query["zoom"] ?? "14";

    var sb = new StringBuilder();
    sb.Append("https://maps.googleapis.com/maps/api/staticmap")
    .Append($"?center={latitude},{longitude}")
    .Append("&size=400x400")
    .Append($"&zoom={zoom}")
    .Append($"&markers=color:red|{latitude},{longitude}")
    .Append("&format=png32")
    .Append($"&key={this._settings.Google.ApiKey}");
    var requestUri = new Uri(sb.ToString());

    var bytes = await this._http.GetByteArrayAsync(requestUri).ConfigureAwait(false);

    return bytes;
    }
    }

    The NaverMapService class has a similar logic with the same input and assumptions. Here's the code:

    public class NaverMapService : IMapService
    {
    public async Task<byte[]> GetMapAsync(HttpRequest req)
    {
    var latitude = req.Query["lat"];
    var longitude = req.Query["long"];
    var zoom = (string)req.Query["zoom"] ?? "13";

    var sb = new StringBuilder();
    sb.Append("https://naveropenapi.apigw.ntruss.com/map-static/v2/raster")
    .Append($"?center={longitude},{latitude}")
    .Append("&w=400")
    .Append("&h=400")
    .Append($"&level={zoom}")
    .Append($"&markers=color:blue|pos:{longitude}%20{latitude}")
    .Append("&format=png")
    .Append("&lang=en");
    var requestUri = new Uri(sb.ToString());

    this._http.DefaultRequestHeaders.Clear();
    this._http.DefaultRequestHeaders.Add("X-NCP-APIGW-API-KEY-ID", this._settings.Naver.ClientId);
    this._http.DefaultRequestHeaders.Add("X-NCP-APIGW-API-KEY", this._settings.Naver.ClientSecret);

    var bytes = await this._http.GetByteArrayAsync(requestUri).ConfigureAwait(false);

    return bytes;
    }
    }

    Let's take a look at the function endpoints. Here's for the Google Maps and Naver Map. As the GetMapAsync(req) method returns a byte array value, you need to transform it as FileContentResult, with the content type of image/png.

    // Google Maps
    public class GoogleMapsTrigger
    {
    [FunctionName(nameof(GoogleMapsTrigger.GetGoogleMapImage))]
    public async Task<IActionResult> GetGoogleMapImage(
    [HttpTrigger(AuthorizationLevel.Anonymous, "GET", Route = "google/image")] HttpRequest req)
    {
    this._logger.LogInformation("C# HTTP trigger function processed a request.");

    var bytes = await this._service.GetMapAsync(req).ConfigureAwait(false);

    return new FileContentResult(bytes, "image/png");
    }
    }

    // Naver Map
    public class NaverMapsTrigger
    {
    [FunctionName(nameof(NaverMapsTrigger.GetNaverMapImage))]
    public async Task<IActionResult> GetNaverMapImage(
    [HttpTrigger(AuthorizationLevel.Anonymous, "GET", Route = "naver/image")] HttpRequest req)
    {
    this._logger.LogInformation("C# HTTP trigger function processed a request.");

    var bytes = await this._service.GetMapAsync(req).ConfigureAwait(false);

    return new FileContentResult(bytes, "image/png");
    }
    }

    Then, add the OpenAPI capability to each function endpoint. Here's the example:

    // Google Maps
    public class GoogleMapsTrigger
    {
    [FunctionName(nameof(GoogleMapsTrigger.GetGoogleMapImage))]
    // ⬇️⬇️⬇️ Add decorators provided by the OpenAPI extension ⬇️⬇️⬇️
    [OpenApiOperation(operationId: nameof(GoogleMapsTrigger.GetGoogleMapImage), tags: new[] { "google" })]
    [OpenApiParameter(name: "lat", In = ParameterLocation.Query, Required = true, Type = typeof(string), Description = "The **latitude** parameter")]
    [OpenApiParameter(name: "long", In = ParameterLocation.Query, Required = true, Type = typeof(string), Description = "The **longitude** parameter")]
    [OpenApiParameter(name: "zoom", In = ParameterLocation.Query, Required = false, Type = typeof(string), Description = "The **zoom level** parameter &ndash; Default value is `14`")]
    [OpenApiResponseWithBody(statusCode: HttpStatusCode.OK, contentType: "image/png", bodyType: typeof(byte[]), Description = "The map image as an OK response")]
    // ⬆️⬆️⬆️ Add decorators provided by the OpenAPI extension ⬆️⬆️⬆️
    public async Task<IActionResult> GetGoogleMapImage(
    [HttpTrigger(AuthorizationLevel.Anonymous, "GET", Route = "google/image")] HttpRequest req)
    {
    ...
    }
    }

    // Naver Map
    public class NaverMapsTrigger
    {
    [FunctionName(nameof(NaverMapsTrigger.GetNaverMapImage))]
    // ⬇️⬇️⬇️ Add decorators provided by the OpenAPI extension ⬇️⬇️⬇️
    [OpenApiOperation(operationId: nameof(NaverMapsTrigger.GetNaverMapImage), tags: new[] { "naver" })]
    [OpenApiParameter(name: "lat", In = ParameterLocation.Query, Required = true, Type = typeof(string), Description = "The **latitude** parameter")]
    [OpenApiParameter(name: "long", In = ParameterLocation.Query, Required = true, Type = typeof(string), Description = "The **longitude** parameter")]
    [OpenApiParameter(name: "zoom", In = ParameterLocation.Query, Required = false, Type = typeof(string), Description = "The **zoom level** parameter &ndash; Default value is `13`")]
    [OpenApiResponseWithBody(statusCode: HttpStatusCode.OK, contentType: "image/png", bodyType: typeof(byte[]), Description = "The map image as an OK response")]
    // ⬆️⬆️⬆️ Add decorators provided by the OpenAPI extension ⬆️⬆️⬆️
    public async Task<IActionResult> GetNaverMapImage(
    [HttpTrigger(AuthorizationLevel.Anonymous, "GET", Route = "naver/image")] HttpRequest req)
    {
    ...
    }
    }

    Run the function app in the local. Here are the latitude and longitude values for Seoul, Korea.

    • latitude: 37.574703
    • longitude: 126.978519

    Google Map for Seoul

    It seems to be working! Let's deploy it to Azure.

    API Management integration

    Visual Studio 2022 provides a built-in deployment tool for Azure Functions app onto Azure. In addition, the deployment tool supports seamless integration with Azure API Management as long as your Azure Functions app enables the OpenAPI capability. In this post, I'm going to use this feature. Right-mouse click on the Azure Functions project and select the "Publish" menu.

    Visual Studio context menu for publish

    Then, you will see the publish screen. Click the "➕ New" button to create a new publish profile.

    Create a new publish profile

    Choose "Azure" and click the "Next" button.

    Choose the target platform for publish

    Select the app instance. This time simply pick up the "Azure Function App (Windows)" option, then click "Next".

    Choose the target OS for publish

    If you already provision an Azure Function app instance, you will see it on the screen. Otherwise, create a new one. Then, click "Next".

    Choose the target instance for publish

    In the next step, you are asked to choose the Azure API Management instance for integration. Choose one, or create a new one. Then, click "Next".

    Choose the APIM instance for integration

    Finally, select the publish method either local publish or GitHub Actions workflow. Let's pick up the local publish method for now. Then, click "Finish".

    Choose the deployment type

    The publish profile has been created. Click "Close" to move on.

    Publish profile created

    Now the function app is ready for deployment. Click the "Publish" button and see how it goes.

    Publish function app

    The Azure function app has been deployed and integrated with the Azure API Management instance.

    Function app published

    Go to the published function app site, and everything looks OK.

    Function app on Azure

    And API Management shows the function app integrated perfectly.

    Function app integrated with APIM

    Now, you are ready to create a custom connector. Let's move on.

    Two ways of building custom connector

    There are two ways to create a custom connector.

    Export custom connector from API Management

    First, you can directly use the built-in API Management feature. Then, click the ellipsis icon and select the "Create Power Connector" menu.

    Create Power Connector menu

    Then, you are redirected to this screen. While the "API" and "API display name" fields are pre-populated, you need to choose the Power Platform environment tied to your tenant. Choose an environment, click "Authenticate", and click "Create".

    Create custom connector screen

    Check your custom connector on Power Apps or Power Automate side.

    Custom connector created on Power Apps

    However, there's a caveat to this approach. Because it's tied to your tenant, you should use the second approach if you want to use this custom connector on the other tenant.

    Import custom connector from OpenAPI document or URL

    Click the ellipsis icon again and select the "Export" menu.

    Export menu

    On the Export API screen, choose the "OpenAPI v2 (JSON)" panel because Power Platform custom connector currently accepts version 2 of the OpenAPI document.

    Select OpenAPI v2

    Download the OpenAPI document to your local computer and move to your Power Apps or Power Automate page under your desired environment. I'm going to use the Power Automate page. First, go to the "Data" ➡️ "Custom connectors" page. Then, click the "➕ New custom connector" ➡️ "Import an OpenAPI file" at the top right corner.

    New custom connector

    When a modal pops up, give the custom connector name and import the OpenAPI document exported above. Then, click "Continue".

    Import custom connector

    Actually, that's it! Next, click the "✔️ Create connector" button to create the connector.

    Create custom connector

    Go back to the custom connector page, and you will see the "Maps API" custom connector you just created.

    Custom connector imported

    So, you are ready to create a Power Apps app to display your location on Google Maps or Naver Map! Let's move on.

    Where am I? Power Apps app

    Open the Power Apps Studio, and create an empty canvas app, named Who am I with a phone layout.

    Custom connector integration

    To use the custom connector created above, you need to add it to the Power App. Click the cylinder icon on the left and click the "Add data" button.

    Add custom connector to data pane

    Search the custom connector name, "Maps API", and click the custom connector to add.

    Search custom connector

    To use the custom connector, you also need to create a connection to it. Click the "Connect" button and move on.

    Create connection to custom connector

    Now, you've got the connection to the custom connector.

    Connection to custom connector ready

    Controls

    Let's build the Power Apps app. First of all, put three controls Image, Slider and Button onto the canvas.

    Power Apps control added

    Click the "Screen1" control and change the value on the property "OnVisible" to the formula below. The formula stores the current slider value in the zoomlevel collection.

    ClearCollect(
    zoomlevel,
    Slider1.Value
    )

    Click the "Botton1" control and change the value on the property "OnSelected" to the formula below. It passes the current latitude, longitude and zoom level to the custom connector and receives the image data. The received image data is stored in the result collection.

    ClearCollect(
    result,
    MAPS.GetGoogleMapImage(
    Location.Latitude,
    Location.Longitude,
    { zoom: First(zoomlevel).Value }
    )
    )

    Click the "Image1" control and change the value on the property "Image" to the formula below. It gets the image data from the result collection.

    First(result).Url

    Click the "Slider1" control and change the value on the property "OnChange" to the formula below. It stores the current slider value to the zoomlevel collection, followed by calling the custom connector to get the image data against the current location.

    ClearCollect(
    zoomlevel,
    Slider1.Value
    );
    ClearCollect(
    result,
    MAPS.GetGoogleMapImage(
    Location.Latitude,
    Location.Longitude,
    { zoom: First(zoomlevel).Value }
    )
    )

    That seems to be OK. Let's click the "Where am I?" button. But it doesn't show the image. The First(result).Url value is actually similar to this:

    appres://blobmanager/1090a86393a843adbfcf428f0b90e91b/1

    It's the image reference value somewhere you can't get there.

    Workaround Power Automate workflow

    Therefore, you need a workaround using a Power Automate workflow to sort out this issue. Open the Power Automate Studio, create an instant cloud flow with the Power App trigger, and give it the "Where am I" name. Then add input parameters of lat, long and zoom.

    Power Apps trigger on Power Automate workflow

    Add custom connector action to get the map image.

    Select action to get the Google Maps image

    In the action, pass the appropriate parameters to the action.

    Pass parameters to the custom connector action

    Add a "Response" action and put the following values into each field.

    • "Body" field:

      {
      "base64Image": <power_automate_expression>
      }

      The <power_automate_expression> should be concat('data:', body('GetGoogleMapImage')?['$content-type'], ';base64,', body('GetGoogleMapImage')?['$content']).

    • "Response Body JSON Schema" field:

      {
      "type": "object",
      "properties": {
      "base64Image": {
      "type": "string"
      }
      }
      }

    Format the Response action

    Let's return to the Power Apps Studio and add the Power Automate workflow you created.

    Add Power Automate workflow

    Select "Button1" and change the value on the property "OnSelect" below. It replaces the direct call to the custom connector with the Power Automate workflow.

    ClearCollect(
    result,
    WhereamI.Run(
    Location.Latitude,
    Location.Longitude,
    First(zoomlevel).Value
    )
    )

    Also, change the value on the property "OnChange" of the "Slider1" control below, replacing the custom connector call with the Power Automate workflow call.

    ClearCollect(
    zoomlevel,
    Slider1.Value
    );
    ClearCollect(
    result,
    WhereamI.Run(
    Location.Latitude,
    Location.Longitude,
    First(zoomlevel).Value
    )
    )

    And finally, change the "Image1" control's "Image" property value below.

    First(result).base64Image

    The workaround has been applied. Click the "Where am I?" button to see your current location from Google Maps.

    Run Power Apps app #1

    If you change the slider left or right, you will see either the zoomed-in image or the zoomed-out image.

    Run Power Apps app #2

    Now, you've created a Power Apps app to show your current location using:

    • Google Maps API through the custom connector, and
    • Custom connector written in Azure Functions with OpenAPI extension!

    Exercise: Try this yourself!

    You can fork this GitHub repository to your account and play around with it to see how the custom connector works. After forking the repository, make sure that you create all the necessary secrets to your repository documented in the README file.

    Then, click the "Deploy to Azure" button, and it will provision all necessary Azure resources and deploy an Azure Functions app for a custom connector.

    Deploy To Azure

    Once everything is deployed successfully, try to create a Power Apps app and Power Automate workflow to see your current location in real-time!

    Resources: For self-study!

    Want to know more about Power Platform custom connector and Azure Functions OpenAPI extension? Here are several resources you can take a look at:

    - - + + \ No newline at end of file diff --git a/blog/29-awesome-azd/index.html b/blog/29-awesome-azd/index.html index a7c9e342d8..9770f966e7 100644 --- a/blog/29-awesome-azd/index.html +++ b/blog/29-awesome-azd/index.html @@ -14,13 +14,13 @@ - - + +

    Oct | `awesome-azd` Templates

    · 5 min read
    Savannah Ostrowski

    Welcome to Beyond #30DaysOfServerless! in October!

    Yes, it's October!! And since we ended #ServerlessSeptember with a focus on End-to-End Development for Serverless on Azure, we thought it would be good to share updates in October that can help you skill up even further.

    Today, we're following up on the Code to Cloud with azd blog post (Day #29) where we introduced the Azure Developer CLI (azd), an open-source tool for streamlining your end-to-end developer experience going from local development environment to Azure cloud. In today's post, we celebrate the October 2022 release of the tool, with three cool new features.

    And if it's October, it must be #Hacktoberfest!! Read on to learn about how you can take advantage of one of the new features, to contribute to the azd open-source community and ecosystem!

    Ready? Let's go!


    What We'll Cover

    • Azure Friday: Introducing the Azure Developer CLI (Video)
    • October 2022 Release: What's New in the Azure Developer CLI?
      • Azure Pipelines for CI/CD: Learn more
      • Improved Infrastructure as Code structure via Bicep modules: Learn more
      • A new azd template gallery: The new azd-templates gallery for community use! Learn more
    • Awesome-Azd: The new azd-templates gallery for Community use
      • Features: discover, create, contribute, request - templates
      • Hacktoberfest: opportunities to contribute in October - and beyond!


    Azure Friday

    This post is a follow-up to our #ServerlessSeptember post on Code to Cloud with Azure Developer CLI where we introduced azd, a new open-source tool that makes it quick and simple for you to move your application from a local development environment to Azure, streamlining your end-to-end developer workflow in the process.

    Prefer to watch a video overview? I have you covered! Check out my recent conversation with Scott Hanselman on Azure Friday where we:

    • talked about the code-to-cloud developer journey
    • walkthrough the ins and outs of an azd template
    • explored Azure Developer CLI commands in the terminal and VS Code, and
    • (probably most importantly) got a web app up and running on Azure with a database, Key Vault and monitoring all in a couple of minutes

    October Release

    We're pleased to announce the October 2022 release of the Azure Developer CLI (currently 0.3.0-beta.2). Read the release announcement for more details. Here are the highlights:

    • Azure Pipelines for CI/CD: This addresses azure-dev#101, adding support for Azure Pipelines (alongside GitHub Actions) as a CI/CD provider. Learn more about usage and related documentation.
    • Improved Infrastructure as Code structure via Bicep modules: This addresses azure-dev#543, which recognized the complexity of using a single resources.bicep file for all resources. With this release, azd templates now come with Bicep modules organized by purpose making it easier to edit and understand. Learn more about this structure, and how to use it.
    • New Templates Gallery - awesome-azd: This addresses azure-dev#398, which aimed to make templates more discoverable and easier to contribute. Learn more about how the new gallery improves the template discovery experience.

    In the next section, we'll dive briefly into the last feature, introducing the new awesome-azd site and resource for templates discovery and contribution. And, since it's #Hacktoberfest season, we'll talk about the Contributor Guide and the many ways you can contribute to this project - with, or without, code.


    It's awesome-azd

    Welcome to awesome-azd a new template gallery hosted on GitHub Pages, and meant to be a destination site for discovering, requesting, and contributing azd-templates for community use!

    In addition, it's README reflects the awesome-list resource format, providing a location for the community to share "best of" resources for Azure Developer CLI - from blog posts and videos, to full-scale tutorials and templates.

    The Gallery is organized into three main areas:

    Take a minute to explore the Gallery and note the features:

    • Search for templates by name
    • Requested Templates - indicating asks from the community
    • Featured Templates - highlighting high-quality templates
    • Filters - to discover templates by and/or query combinations

    Check back often to see the latest contributed templates and requests!


    Hacktoberfest

    So, why is this a good time to talk about the Gallery? Because October means it's time for #Hacktoberfest - a month-long celebration of open-source projects and their maintainers, and an opportunity for first-time contributors to get support and guidance making their first pull-requests! Check out the #Hacktoberfest topic on GitHub for projects you can contribute to.

    And we hope you think of awesome-azd as another possible project to contribute to.

    Check out the FAQ section to learn how to create, discover, and contribute templates. Or take a couple of minutes to watch this video walkthrough from Jon Gallant:

    And don't hesitate to reach out to us - either via Issues on the repo, or in the Discussions section of this site, to give us feedback!

    Happy Hacking! 🎃


    - - + + \ No newline at end of file diff --git a/blog/29-azure-developer-cli/index.html b/blog/29-azure-developer-cli/index.html index a40210ee7e..973a375cd5 100644 --- a/blog/29-azure-developer-cli/index.html +++ b/blog/29-azure-developer-cli/index.html @@ -14,8 +14,8 @@ - - + +
    @@ -26,7 +26,7 @@

    ...and that's it! We've successfully deployed our application on Azure!

    But there's more!

    Best practices: Monitoring and CI/CD!

    In my opinion, it's not enough to just set up the application on Azure! I want to know that my web app is performant and serving my users reliably! I also want to make sure that I'm not inadvertently breaking my application as I continue to make changes to it. Thankfully, the Azure Developer CLI also handles all of this via two additional commands - azd monitor and azd pipeline config.

    Application Monitoring

    When we provisioned all of our infrastructure, we also set up application monitoring via a Bicep file in our .infra/ directory that spec'd out an Application Insights dashboard. By running azd monitor we can see the dashboard with live metrics that was configured for the application.

    We can also navigate to the Application Dashboard by clicking on the resource group name, where you can set a specific refresh rate for the dashboard, and see usage, reliability, and performance metrics over time.

    I don't know about everyone else but I have spent a ton of time building out similar dashboards. It can be super time-consuming to write all the queries and create the visualizations so this feels like a real time saver.

    CI/CD

    Finally let's talk about setting up CI/CD! This might be my favorite azd feature. As I mentioned before, the Azure Developer CLI has a command, azd pipeline config, which uses the files in the .github/ directory to set up a GitHub Action. More than that, if there is no upstream repo, the Developer CLI will actually help you create one. But what does this mean exactly? Because our GitHub Action is using the same commands you'd run in the CLI under the hood, we're actually going to have CI/CD set up to run on every commit into the repo, against real Azure resources. What a sweet collaboration feature!

    That's it! We've gone end-to-end with the Azure Developer CLI - initialized a project, provisioned the resources on Azure, deployed our code on Azure, set up monitoring logs and dashboards, and set up a CI/CD pipeline with GitHub Actions to run on every commit into the repo (on real Azure resources!).

    Exercise: Try it yourself or create your own template!

    As an exercise, try out the workflow above with any template on GitHub!

    Or, try turning your own project into an Azure Developer CLI-enabled template by following this guidance. If you create your own template, don't forget to tag the repo with the azd-templates topic on GitHub to help others find it (unfamiliar with GitHub topics? Learn how to add topics to your repo)! We'd also love to chat with you about your experience creating an azd template - if you're open to providing feedback around this, please fill out this form!

    Resources

    - - + + \ No newline at end of file diff --git a/blog/archive/index.html b/blog/archive/index.html index 2d03bb2d46..1136368f97 100644 --- a/blog/archive/index.html +++ b/blog/archive/index.html @@ -14,13 +14,13 @@ - - + +

    Archive

    Archive

    2022

    - - + + \ No newline at end of file diff --git a/blog/index.html b/blog/index.html index 7d85a12674..a682414b9b 100644 --- a/blog/index.html +++ b/blog/index.html @@ -14,13 +14,13 @@ - - + +

    · 5 min read
    Savannah Ostrowski

    Welcome to Beyond #30DaysOfServerless! in October!

    Yes, it's October!! And since we ended #ServerlessSeptember with a focus on End-to-End Development for Serverless on Azure, we thought it would be good to share updates in October that can help you skill up even further.

    Today, we're following up on the Code to Cloud with azd blog post (Day #29) where we introduced the Azure Developer CLI (azd), an open-source tool for streamlining your end-to-end developer experience going from local development environment to Azure cloud. In today's post, we celebrate the October 2022 release of the tool, with three cool new features.

    And if it's October, it must be #Hacktoberfest!! Read on to learn about how you can take advantage of one of the new features, to contribute to the azd open-source community and ecosystem!

    Ready? Let's go!


    What We'll Cover

    • Azure Friday: Introducing the Azure Developer CLI (Video)
    • October 2022 Release: What's New in the Azure Developer CLI?
      • Azure Pipelines for CI/CD: Learn more
      • Improved Infrastructure as Code structure via Bicep modules: Learn more
      • A new azd template gallery: The new azd-templates gallery for community use! Learn more
    • Awesome-Azd: The new azd-templates gallery for Community use
      • Features: discover, create, contribute, request - templates
      • Hacktoberfest: opportunities to contribute in October - and beyond!


    Azure Friday

    This post is a follow-up to our #ServerlessSeptember post on Code to Cloud with Azure Developer CLI where we introduced azd, a new open-source tool that makes it quick and simple for you to move your application from a local development environment to Azure, streamlining your end-to-end developer workflow in the process.

    Prefer to watch a video overview? I have you covered! Check out my recent conversation with Scott Hanselman on Azure Friday where we:

    • talked about the code-to-cloud developer journey
    • walkthrough the ins and outs of an azd template
    • explored Azure Developer CLI commands in the terminal and VS Code, and
    • (probably most importantly) got a web app up and running on Azure with a database, Key Vault and monitoring all in a couple of minutes

    October Release

    We're pleased to announce the October 2022 release of the Azure Developer CLI (currently 0.3.0-beta.2). Read the release announcement for more details. Here are the highlights:

    • Azure Pipelines for CI/CD: This addresses azure-dev#101, adding support for Azure Pipelines (alongside GitHub Actions) as a CI/CD provider. Learn more about usage and related documentation.
    • Improved Infrastructure as Code structure via Bicep modules: This addresses azure-dev#543, which recognized the complexity of using a single resources.bicep file for all resources. With this release, azd templates now come with Bicep modules organized by purpose making it easier to edit and understand. Learn more about this structure, and how to use it.
    • New Templates Gallery - awesome-azd: This addresses azure-dev#398, which aimed to make templates more discoverable and easier to contribute. Learn more about how the new gallery improves the template discovery experience.

    In the next section, we'll dive briefly into the last feature, introducing the new awesome-azd site and resource for templates discovery and contribution. And, since it's #Hacktoberfest season, we'll talk about the Contributor Guide and the many ways you can contribute to this project - with, or without, code.


    It's awesome-azd

    Welcome to awesome-azd a new template gallery hosted on GitHub Pages, and meant to be a destination site for discovering, requesting, and contributing azd-templates for community use!

    In addition, it's README reflects the awesome-list resource format, providing a location for the community to share "best of" resources for Azure Developer CLI - from blog posts and videos, to full-scale tutorials and templates.

    The Gallery is organized into three main areas:

    Take a minute to explore the Gallery and note the features:

    • Search for templates by name
    • Requested Templates - indicating asks from the community
    • Featured Templates - highlighting high-quality templates
    • Filters - to discover templates by and/or query combinations

    Check back often to see the latest contributed templates and requests!


    Hacktoberfest

    So, why is this a good time to talk about the Gallery? Because October means it's time for #Hacktoberfest - a month-long celebration of open-source projects and their maintainers, and an opportunity for first-time contributors to get support and guidance making their first pull-requests! Check out the #Hacktoberfest topic on GitHub for projects you can contribute to.

    And we hope you think of awesome-azd as another possible project to contribute to.

    Check out the FAQ section to learn how to create, discover, and contribute templates. Or take a couple of minutes to watch this video walkthrough from Jon Gallant:

    And don't hesitate to reach out to us - either via Issues on the repo, or in the Discussions section of this site, to give us feedback!

    Happy Hacking! 🎃


    - - + + \ No newline at end of file diff --git a/blog/microservices-10/index.html b/blog/microservices-10/index.html index 769d90b92f..07efe30a84 100644 --- a/blog/microservices-10/index.html +++ b/blog/microservices-10/index.html @@ -14,13 +14,13 @@ - - + +

    10. Microservices Communication

    · 8 min read
    Paul Yu

    Welcome to Day 10 of #30DaysOfServerless!

    We continue our exploraton into Azure Container Apps, with today's focus being communication between microservices, and how to configure your Azure Container Apps environment in the context of a deployment example.


    What We'll Cover

    • ACA Environments & Virtual Networking
    • Basic Microservices Communications
    • Walkthrough: ACA Deployment Example
    • Summary and Next Steps
    • Exercise: Try this yourself!
    • Resources: For self-study!


    Introduction

    In yesterday's post, we learned what the Azure Container Apps (ACA) service is and the problems it aims to solve. It is considered to be a Container-as-a-Service platform since much of the complex implementation details of running a Kubernetes cluster is managed for you.

    Some of the use cases for ACA include event-driven processing jobs and background tasks, but this article will focus on hosting microservices, and how they can communicate with each other within the ACA service. At the end of this article, you will have a solid understanding of how networking and communication is handled and will leave you with a few tutorials to try.

    Environments and virtual networking in ACA

    Before we jump into microservices communication, we should review how networking works within ACA. With ACA being a managed service, Azure will take care of most of your underlying infrastructure concerns. As you provision an ACA resource, Azure provisions an Environment to deploy Container Apps into. This environment is your isolation boundary.

    Azure Container Apps Environment

    By default, Azure creates and manages a new Virtual Network (VNET) for you and the VNET is associated with the environment. As you deploy container apps, they are deployed into the same VNET and the environment is assigned a static public IP address which allows your apps to be accessible over the internet. This VNET is not visible or manageable.

    If you need control of the networking flows within the VNET, you can pre-provision one and tell Azure to deploy an environment within it. This "bring-your-own" VNET model allows you to deploy an environment in either External or Internal modes. Deploying an environment in External mode gives you the flexibility of managing your own VNET, while still allowing your containers to be accessible from outside the environment; a static public IP address is assigned to the environment. When deploying in Internal mode, your containers are accessible within the environment and/or VNET but not accessible from the internet.

    Bringing your own VNET will require some planning and you will need dedicate an empty subnet which will be used exclusively by the ACA environment. The size of your subnet will be dependant on how many containers you plan on deploying and your scaling requirements and one requirement to know is that the subnet address range must have have a /23 CIDR prefix at minimum. You will also need to think about your deployment strategy since ACA has the concept of Revisions which will also consume IPs from your subnet.

    Some additional restrictions to consider when planning your subnet address space is listed in the Resources section below and can be addressed in future posts, so be sure to follow us on dev.to and bookmark the ServerlessSeptember site.

    Basic microservices communication in ACA

    When it comes to communications between containers, ACA addresses this concern with its Ingress capabilities. With HTTP Ingress enabled on your container app, you can expose your app on a HTTPS endpoint.

    If your environment is deployed using default networking and your containers needs to be accessible from outside the environment, you will need to set the Ingress traffic option to Accepting traffic from anywhere. This will generate a Full-Qualified Domain Name (FQDN) which you can use to access your app right away. The ingress feature also generates and assigns a Secure Socket Layer (SSL) certificate for the FQDN.

    External ingress on Container App

    If your environment is deployed using default networking and your containers only need to communicate with other containers in the environment, you'll need to set the Ingress traffic option to Limited to Container Apps Environment. You get a FQDN here as well, but in the section below we'll see how that changes.

    Internal ingress on Container App

    As mentioned in the networking section above, if you deploy your ACA environment into a VNET in internal mode, your options will be Limited to Container Apps Environment or Limited to VNet.

    Ingress on internal virtual network

    Note how the Accepting traffic from anywhere option is greyed out. If your VNET is deployed in external mode, then the option will be available.

    Let's walk though an example ACA deployment

    The diagram below illustrates a simple microservices application that I deployed to ACA. The three container apps all have ingress enabled. The greeting-service app calls two backend services; a hello-service that returns the text Hello (in random casing) and a world-service that returns the text World (in a few random languages). The greeting-service concatenates the two strings together and returns Hello World to the browser. The greeting-service is the only service accessible via external ingress while two backend services are only accessible via internal ingress.

    Greeting Service overview

    With ingress enabled, let's take a quick look at the FQDN structures. Here is the FQDN of the external greeting-service.

    https://greeting-service.victoriouswave-3749d046.eastus.azurecontainerapps.io

    We can break it down into these components:

    https://[YOUR-CONTAINER-APP-NAME].[RANDOM-NAME]-[RANDOM-CHARACTERS].[AZURE-REGION].containerapps.io

    And here is the FQDN of the internal hello-service.

    https://hello-service.internal.victoriouswave-3749d046.eastus.azurecontainerapps.io

    Can you spot the difference between FQDNs?

    That was too easy 😉... the word internal is added as a subdomain in the FQDN between your container app name and the random name for all internal ingress endpoints.

    https://[YOUR-CONTAINER-APP-NAME].internal.[RANDOM-NAME]-[RANDOM-CHARACTERS].[AZURE-REGION].containerapps.io

    Now that we know the internal service FQDNs, we use them in the greeting-service app to achieve basic service-to-service communications.

    So we can inject FQDNs of downstream APIs to upstream apps using environment variables, but the downside to this approach is that need to deploy downstream containers ahead of time and this dependency will need to be planned for during your deployment process. There are ways around this and one option is to leverage the auto-injected environment variables within your app code.

    If I use the Console blade for the hello-service container app and run the env command, you will see environment variables named CONTAINER_APP_NAME and CONTAINER_APP_ENV_DNS_SUFFIX. You can use these values to determine FQDNs within your upstream app.

    hello-service environment variables

    Back in the greeting-service container I can invoke the hello-service container's sayhello method. I know the container app name is hello-service and this service is exposed over an internal ingress, therefore, if I add the internal subdomain to the CONTAINER_APP_ENV_DNS_SUFFIX I can invoke a HTTP request to the hello-service from my greeting-service container.

    Invoke the sayHello method from the greeting-service container

    As you can see, the ingress feature enables communications to other container apps over HTTP/S and ACA will inject environment variables into our container to help determine what the ingress FQDNs would be. All we need now is a little bit of code modification in the greeting-service app and build the FQDNs of our backend APIs by retrieving these environment variables.

    Greeting service code

    ... and now we have a working microservices app on ACA! 🎉

    Hello World

    Summary and next steps

    We've covered Container Apps networking and the basics of how containers communicate with one another. However, there is a better way to address service-to-service invocation using Dapr, which is an open-source framework for building microservices. It is natively integrated into the ACA service and in a future post, you'll learn how to enable it in your Container App to address microservices concerns and more. So stay tuned!

    Exercises

    As a takeaway for today's post, I encourage you to complete this tutorial and if you'd like to deploy the sample app that was presented in this article, my teammate @StevenMurawski is hosting a docker-compose-examples repo which includes samples for deploying to ACA using Docker Compose files. To learn more about the az containerapp compose command, a link to his blog articles are listed in the Resources section below.

    If you have any questions or feedback, please let us know in the comments below or reach out on Twitter @pauldotyu

    Have fun packing and shipping containers! See you in the next post!

    Resources

    The sample app presented here was inspired by services demonstrated in the book Introducing Distributed Application Runtime (Dapr): Simplifying Microservices Applications Development Through Proven and Reusable Patterns and Practices. Go check it out to leran more about Dapr!

    - - + + \ No newline at end of file diff --git a/blog/page/10/index.html b/blog/page/10/index.html index e214e13363..12bb6cbdb5 100644 --- a/blog/page/10/index.html +++ b/blog/page/10/index.html @@ -14,13 +14,13 @@ - - + +

    · 6 min read
    Ramya Oruganti

    Welcome to Day 19 of #30DaysOfServerless!

    Today, we have a special set of posts from our Zero To Hero 🚀 initiative, featuring blog posts authored by our Product Engineering teams for #ServerlessSeptember. Posts were originally published on the Apps on Azure blog on Microsoft Tech Community.


    What We'll Cover

    • Retry Policy Support - in Apache Kafka Extension
    • AutoOffsetReset property - in Apache Kafka Extension
    • Key support for Kafka messages - in Apache Kafka Extension
    • References: Apache Kafka Extension for Azure Functions


    Recently we launched the Apache Kafka extension for Azure functions in GA with some cool new features like deserialization of Avro Generic records and Kafka headers support. We received great responses - so we're back with more updates!

    Retry Policy support

    Handling errors in Azure Functions is important to avoid data loss or miss events or monitor the health of an application. Apache Kafka Extension for Azure Functions supports retry policy which tells the runtime to rerun a failed execution until either successful completion occurs or the maximum number of retries is reached.

    A retry policy is evaluated when a trigger function raises an uncaught exception. As a best practice, you should catch all exceptions in your code and rethrow any errors that you want to result in a retry.

    There are two retry strategies supported by policy that you can configure :- fixed delay and exponential backoff

    1. Fixed Delay - A specified amount of time is allowed to elapse between each retry.
    2. Exponential Backoff - The first retry waits for the minimum delay. On subsequent retries, time is added exponentially to the initial duration for each retry, until the maximum delay is reached. Exponential back-off adds some small randomization to delays to stagger retries in high-throughput scenarios.
    Please Note

    Retry Policy for Kafka extension is NOT supported for C# (in proc and out proc) trigger and output binding. This is supported for languages like Java, Node (JS , TypeScript), PowerShell and Python trigger and output bindings.

    Here is the sample code view of exponential backoff retry strategy

    Error Handling with Apache Kafka extension for Azure Functions

    AutoOffsetReset property

    AutoOffsetReset property enables customers to configure the behaviour in the absence of an initial offset. Imagine a scenario when there is a need to change consumer group name. The consumer connected using a new consumer group had to reprocess all events starting from the oldest (earliest) one, as this was the default one and this setting wasn’t exposed as configurable option in the Apache Kafka extension for Azure Functions(previously). With the help of this kafka setting you can configure on how to start processing events for newly created consumer groups.

    Due to lack of the ability to configure this setting, offset commit errors were causing topics to restart from earliest offset· Users were looking to be able to set offset setting to either latest or earliest based on their requirements.

    We are happy to share that we have enabled the AutoOffsetReset setting as a configurable one to either - Earliest(Default) and Latest. Setting the value to Earliest configures the consumption of the messages from the the earliest/smallest offset or beginning of the topic partition. Setting the property to Latest configures the consumption of the messages from the latest/largest offset or from the end of the topic partition. This is supported for all the Azure Functions supported languages (C# (in & out), Java, Node (JS and TypeScript), PowerShell and python) and can be used for both triggers and output binding

    Error Handling with Apache Kafka extension for Azure Functions

    Key support for Kafka messages

    With keys the producer/output binding can be mapped to broker and partition to write based on the message. So alongside the message value, we can choose to send a message key and that key can be whatever you want it could be a string, it could be a number . In case you don’t send the key, the key is set to null then the data will be sent in a Round Robin fashion to make it very simple. But in case you send a key with your message, all the messages that share the same key will always go to the same partition and thus you can enable grouping of similar messages into partitions

    Previously while consuming a Kafka event message using the Azure Function kafka extension, the event key was always none although the key was present in the event message.

    Key support was implemented in the extension which enables customers to set/view key in the Kafka event messages coming in to the kafka trigger and set keys to the messages going in to kafka topics (with keys set) through output binding. Therefore key support was enabled in the extension to support both trigger and output binding for all Azure Functions supported languages ( (C# (in & out), Java, Node (JS and TypeScript), PowerShell and python)

    Here is the view of an output binding producer code where Kafka messages are being set with key

    Error Handling with Apache Kafka extension for Azure Functions

    Conclusion:

    In this article you have learnt about the latest additions to the Apache Kafka extension for Azure Functions. Incase you have been waiting for these features to get released or need them you are all set and please go head and try them out!! They are available in the latest extension bundles

    Want to learn more?

    Please refer to Apache Kafka bindings for Azure Functions | Microsoft Docs for detail documentation, samples on the Azure function supported languages and more!

    References

    FEEDBACK WELCOME

    Keep in touch with us on Twitter via @AzureFunctions.

    - - + + \ No newline at end of file diff --git a/blog/page/11/index.html b/blog/page/11/index.html index 85a0589dd6..2c18e18a6c 100644 --- a/blog/page/11/index.html +++ b/blog/page/11/index.html @@ -14,14 +14,14 @@ - - + +

    · 5 min read
    Mike Morton

    Welcome to Day 19 of #30DaysOfServerless!

    Today, we have a special set of posts from our Zero To Hero 🚀 initiative, featuring blog posts authored by our Product Engineering teams for #ServerlessSeptember. Posts were originally published on the Apps on Azure blog on Microsoft Tech Community.


    What We'll Cover

    • Log Streaming - in Azure Portal
    • Console Connect - in Azure Portal
    • Metrics - using Azure Monitor
    • Log Analytics - using Azure Monitor
    • Metric Alerts and Log Alerts - using Azure Monitor


    In past weeks, @kendallroden wrote about what it means to be Cloud-Native and @Anthony Chu the various ways to get your apps running on Azure Container Apps. Today, we will talk about the observability tools you can use to observe, debug, and diagnose your Azure Container Apps.

    Azure Container Apps provides several observability features to help you debug and diagnose your apps. There are both Azure portal and CLI options you can use to help understand the health of your apps and help identify when issues arise.

    While these features are helpful throughout your container app’s lifetime, there are two that are especially helpful. Log streaming and console connect can be a huge help in the initial stages when issues often rear their ugly head. Let's dig into both of these a little.

    Log Streaming

    Log streaming allows you to use the Azure portal to view the streaming logs from your app. You’ll see the logs written from the app to the container’s console (stderr and stdout). If your app is running multiple revisions, you can choose from which revision to view logs. You can also select a specific replica if your app is configured to scale. Lastly, you can choose from which container to view the log output. This is useful when you are running a custom or Dapr sidecar container. view streaming logs

    Here’s an example CLI command to view the logs of a container app.

    az containerapp logs show -n MyContainerapp -g MyResourceGroup

    You can find more information about the different options in our CLI docs.

    Console Connect

    In the Azure portal, you can connect to the console of a container in your app. Like log streaming, you can select the revision, replica, and container if applicable. After connecting to the console of the container, you can execute shell commands and utilities that you have installed in your container. You can view files and their contents, monitor processes, and perform other debugging tasks.

    This can be great for checking configuration files or even modifying a setting or library your container is using. Of course, updating a container in this fashion is not something you should do to a production app, but tweaking and re-testing an app in a non-production environment can speed up development.

    Here’s an example CLI command to connect to the console of a container app.

    az containerapp exec -n MyContainerapp -g MyResourceGroup

    You can find more information about the different options in our CLI docs.

    Metrics

    Azure Monitor collects metric data from your container app at regular intervals to help you gain insights into the performance and health of your container app. Container apps provide these metrics:

    • CPU usage
    • Memory working set bytes
    • Network in bytes
    • Network out bytes
    • Requests
    • Replica count
    • Replica restart count

    Here you can see the metrics explorer showing the replica count for an app as it scaled from one replica to fifteen, and then back down to one.

    You can also retrieve metric data through the Azure CLI.

    Log Analytics

    Azure Monitor Log Analytics is great for viewing your historical logs emitted from your container apps. There are two custom tables of interest, the ContainerAppConsoleLogs_CL which contains all the log messages written by your app (stdout and stderr), and the ContainerAppSystemLogs_CL which contain the system messages from the Azure Container Apps service.

    You can also query Log Analytics through the Azure CLI.

    Alerts

    Azure Monitor alerts notify you so that you can respond quickly to critical issues. There are two types of alerts that you can define:

    You can create alert rules from metric charts in the metric explorer and from queries in Log Analytics. You can also define and manage alerts from the Monitor|Alerts page.

    Here is what creating an alert looks like in the Azure portal. In this case we are setting an alert rule from the metric explorer to trigger an alert if the replica restart count for a specific container app is greater than two within the last fifteen minutes.

    To learn more about alerts, refer to Overview of alerts in Microsoft Azure.

    Conclusion

    In this article, we looked at the several ways to observe, debug, and diagnose your Azure Container Apps. As you can see there are rich portal tools and a complete set of CLI commands to use. All the tools are helpful throughout the lifecycle of your app, be sure to take advantage of them when having an issue and/or to prevent issues.

    To learn more, visit Azure Container Apps | Microsoft Azure today!

    ASK THE EXPERT: LIVE Q&A

    The Azure Container Apps team will answer questions live on September 29.

    - - + + \ No newline at end of file diff --git a/blog/page/12/index.html b/blog/page/12/index.html index a628368708..d6955a4c3e 100644 --- a/blog/page/12/index.html +++ b/blog/page/12/index.html @@ -14,14 +14,14 @@ - - + +

    · 10 min read
    Brian Benz

    Welcome to Day 18 of #30DaysOfServerless!

    Yesterday my Serverless September post introduced you to making Azure Logic Apps and Azure Cosmos DB work together with a sample application that collects weather data. Today I'm sharing a more robust solution that actually reads my mail. Let's learn about Teaching the cloud to read your mail!

    Ready? Let's go!


    What We'll Cover

    • Introduction to the ReadMail solution
    • Setting up Azure storage, Cosmos DB and Computer Vision
    • Connecting it all together with a Logic App
    • Resources: For self-study!


    Introducing the ReadMail solution

    The US Postal system offers a subscription service that sends you images of mail it will be delivering to your home. I decided it would be cool to try getting Azure to collect data based on these images, so that I could categorize my mail and track the types of mail that I received.

    To do this, I used Azure storage, Cosmos DB, Logic Apps, and computer vision. When a new email comes in from the US Postal service (USPS), it triggers a logic app that:

    • Posts attachments to Azure storage
    • Triggers Azure Computer vision to perform an OCR function on attachments
    • Extracts any results into a JSON document
    • Writes the JSON document to Cosmos DB

    workflow for the readmail solution

    In this post I'll walk you through setting up the solution for yourself.

    Prerequisites

    Setup Azure Services

    First, we'll create all of the target environments we need to be used by our Logic App, then we;ll create the Logic App.

    1. Azure Storage

    We'll be using Azure storage to collect attached images from emails as they arrive. Adding images to Azure storage will also trigger a workflow that performs OCR on new attached images and stores the OCR data in Cosmos DB.

    To create a new Azure storage account from the portal dashboard, Select Create a resource > Storage account > Create.

    The Basics tab covers all of the features and information that we will need for this solution:

    SectionFieldRequired or optionalDescription
    Project detailsSubscriptionRequiredSelect the subscription for the new storage account.
    Project detailsResource groupRequiredCreate a new resource group that you will use for storage, Cosmos DB, Computer Vision and the Logic App.
    Instance detailsStorage account nameRequiredChoose a unique name for your storage account. Storage account names must be between 3 and 24 characters in length and may contain numbers and lowercase letters only.
    Instance detailsRegionRequiredSelect the appropriate region for your storage account.
    Instance detailsPerformanceRequiredSelect Standard performance for general-purpose v2 storage accounts (default).
    Instance detailsRedundancyRequiredSelect locally-redundant Storage (LRS) for this example.

    Select Review + create to accept the remaining default options, then validate and create the account.

    2. Azure CosmosDB

    CosmosDB will be used to store the JSON documents returned by the COmputer Vision OCR process.

    See more details and screen shots for setting up CosmosDB in yesterday's Serverless September post - Using Logic Apps with Cosmos DB

    To get started with Cosmos DB, you create an account, then a database, then a container to store JSON documents. To create a new Cosmos DB account from the portal dashboard, Select Create a resource > Azure Cosmos DB > Create. Choose core SQL for the API.

    Select your subscription, then for simplicity use the same resource group you created when you set up storage. Enter an account name and choose a location, select provisioned throughput capacity mode and apply the free tier discount. From here you can select Review and Create, then Create

    Next, create a new database and container. Go to the Data Explorer in your new Cosmos DB account, and choose New Container. Name the database, and keep all the other defaults except:

    SettingAction
    Container IDid
    Container partition/id

    Press OK to create a database and container

    3. Azure Computer Vision

    Azure Cognitive Services' Computer Vision will perform an OCR process on each image attachment that is stored in Azure storage.

    From the portal dashboard, Select Create a resource > AI + Machine Learning > Computer Vision > Create.

    The Basics and Identity tabs cover all of the features and information that we will need for this solution:

    Basics Tab

    SectionFieldRequired or optionalDescription
    Project detailsSubscriptionRequiredSelect the subscription for the new service.
    Project detailsResource groupRequiredUse the same resource group that you used for Azure storage and Cosmos DB.
    Instance detailsRegionRequiredSelect the appropriate region for your Computer Vision service.
    Instance detailsNameRequiredChoose a unique name for your Computer Vision service.
    Instance detailsPricingRequiredSelect the free tier for this example.

    Identity Tab

    SectionFieldRequired or optionalDescription
    System assigned managed identityStatusRequiredEnable system assigned identity to grant the resource access to other existing resources.

    Select Review + create to accept the remaining default options, then validate and create the account.


    Connect it all with a Logic App

    Now we're ready to put this all together in a Logic App workflow!

    1. Create Logic App

    From the portal dashboard, Select Create a resource > Integration > Logic App > Create. Name your Logic App and select a location, the rest of the settings can be left at their defaults.

    2. Create Workflow: Add Trigger

    Once the Logic App is created, select Create a workflow from designer.

    A workflow is a series of steps that defines a task or process. Each workflow starts with a single trigger, after which you must add one or more actions.

    When in designer, search for outlook.com on the right under Add a trigger. Choose outlook.com. Choose When a new email arrives as the trigger.

    A trigger is always the first step in any workflow and specifies the condition for running any further steps in that workflow.

    Set the following values:

    ParameterValue
    FolderInbox
    ImportanceAny
    Only With AttachmentsYes
    Include AttachmentsYes

    Then add a new parameter:

    ParameterValue
    FromAdd the email address that sends you the email with attachments
    3. Create Workflow: Add Action (for Trigger)

    Choose add an action and choose control > for-each.

    logic app for each

    Inside the for-each action, in Select an output from previous steps, choose attachments. Then, again inside the for-each action, add the create blob action:

    Set the following values:

    ParameterValue
    Folder Path/mailreaderinbox
    Blob NameAttachments Name
    Blob ContentAttachments Content

    This extracts attachments from the email and created a new blob for each attachment.

    Next, inside the same for-each action, add the get blob content action.

    Set the following values:

    ParameterValue
    Blobid
    Infer content typeYes

    We create and read from a blob for each attachment because Computer Vision needs a non-virtual source to read from when performing an OCR process. Because we enabled system assigned identity to grant Computer Vision to other existing resources, it can access the blob but not the outlook.com attachment. Also, we pass the ID of the blob to use as a unique ID when writing to Cosmos DB.

    create blob from attachments

    Next, inside the same for-each action, choose add an action and choose control > condition. Set the value to Media Type > is equal to > image/JPEG

    The USPS sends attachments of multiple types, but we only want to scan attachments that have images of our mail, which are always JPEG images. If the condition is true, we will process the image with Computer Vision OCR and write the results to a JSON document in CosmosDB.

    In the True section of the condition, add an action and choose Computer Vision API > Optical Character Recognition (OCR) to JSON.

    Set the following values:

    ParameterValue
    Image SourceImage Content
    Image contentFile Content

    In the same True section of the condition, choose add an action and choose Cosmos DB. Choose Create or Update Document from the actions. Select Access Key, and provide the primary read-write key (found under keys in Cosmos DB), and the Cosmos DB account ID (without 'documents.azure.com').

    Next, fill in your Cosmos DB Database ID and Collection ID. Create a JSON document by selecting dynamic content elements and wrapping JSON formatting around them.

    Be sure to use the ID passed from blob storage as your unique ID for CosmosDB. That way you can troubleshoot and JSON or OCR issues by tracing back the JSON document in Cosmos Db to the blob in Azure storage. Also, include the Computer Vision JSON response, as it contains the results of the Computer Vision OCR scan. all other elements are optional.

    4. TEST WORKFLOW

    When complete, you should have an action the Logic App designer that looks something like this:

    Logic App workflow create or update document in cosmosdb

    Save the workflow and test the connections by clicking Run Trigger > Run. If connections are working, you should see documents flowing into Cosmos DB each time that an email arrives with image attachments.

    Check the data in Cosmos Db by opening the Data explorer, then choosing the container you created and selecting items. You should see documents similar to this:

    Logic App workflow with trigger and action

    1. Congratulations

    You just built your personal ReadMail solution with Logic Apps! 🎉


    Resources: For self-study!

    Once you have an understanding of the basics in ths post, there is so much more to learn!

    Thanks for stopping by!

    - - + + \ No newline at end of file diff --git a/blog/page/13/index.html b/blog/page/13/index.html index 93987aa566..43522a323a 100644 --- a/blog/page/13/index.html +++ b/blog/page/13/index.html @@ -14,14 +14,14 @@ - - + +

    · 6 min read
    Brian Benz

    Welcome to Day 17 of #30DaysOfServerless!

    In past weeks, we've covered serverless technologies that provide core capabilities (functions, containers, microservices) for building serverless solutions. This week we're looking at technologies that make service integrations more seamless, starting with Logic Apps. Let's look at one usage example today!

    Ready? Let's Go!


    What We'll Cover

    • Introduction to Logic Apps
    • Settng up Cosmos DB for Logic Apps
    • Setting up a Logic App connection and event
    • Writing data to Cosmos DB from a Logic app
    • Resources: For self-study!


    Introduction to Logic Apps

    Previously in Serverless September, we've covered Azure Functions, where the event triggers code. In Logic Apps, the event triggers a workflow that you design. Logic Apps enable serverless applications to connect to external sources for data then automate business processes via workflows.

    In this post I'll walk you through setting up a Logic App that works with Cosmos DB. For this example, we'll connect to the MSN weather service, an design a logic app workflow that collects data when weather changes, and writes the data to Cosmos DB.

    PREREQUISITES

    Setup Cosmos DB for Logic Apps

    Cosmos DB has many APIs to choose from, but to use the default Logic App connection, we need to choose the a Cosmos DB SQL API. We'll set this up via the Azure Portal.

    To get started with Cosmos DB, you create an account, then a database, then a container to store JSON documents. To create a new Cosmos DB account from the portal dashboard, Select Create a resource > Azure Cosmos DB > Create. Choose core SQL for the API.

    Select your subscription, then create a new resource group called CosmosWeather. Enter an account name and choose a location, select provisioned throughput capacity mode and apply the free tier discount. From here you can select Review and Create, then Create

    Azure Cosmos DB is available in two different capacity modes: provisioned throughput and serverless. You can perform the same database operations in both modes, but the way you get billed for these operations is different. We wil be using provisioned throughput and the free tier for this example.

    Setup the CosmosDB account

    Next, create a new database and container. Go to the Data Explorer in your new Cosmos DB account, and choose New Container. Name the database, and keep all the orher defaults except:

    SettingAction
    Container IDid
    Container partition/id

    Press OK to create a database and container

    A database is analogous to a traditional DBMS namespace. It's used to organize one or more containers.

    Setup the CosmosDB Container

    Now we're ready to set up our logic app an write to Cosmos DB!

    Setup Logic App connection + event

    Once the Cosmos DB SQL API account is created, we can set up our Logic App. From the portal dashboard, Select Create a resource > Integration > Logic App > Create. Name your Logic App and select a location, the rest fo the settings can be left at their defaults. Once you new Logic App is created, select Create a workflow from designer to get started.

    A workflow is a series of steps that defines a task or process. Each workflow starts with a single trigger, after which you must add one or more actions.

    When in designer, search for weather on the right under Add a trigger. Choose MSN Weather. Choose When the current conditions change as the trigger.

    A trigger is always the first step in any workflow and specifies the condition for running any further steps in that workflow.

    Add a location. Valid locations are City, Region, State, Country, Landmark, Postal Code, latitude and longitude. This triggers a new workflow when the conditions change for a location.

    Write data from Logic App to Cosmos DB

    Now we are ready to set up the action to write data to Cosmos DB. Choose add an action and choose Cosmos DB.

    An action is each step in a workflow after the trigger. Every action runs some operation in a workflow.

    In this case, we will be writing a JSON document to the Cosmos DB container we created earlier. Choose Create or Update Document from the actions. At this point you should have a workflow in designer that looks something like this:

    Logic App workflow with trigger

    Start wth the connection for set up the Cosmos DB action. Select Access Key, and provide the primary read-write key (found under keys in Cosmos DB), and the Cosmos DB account ID (without 'documents.azure.com').

    Next, fill in your Cosmos DB Database ID and Collection ID. Create a JSON document bt selecting dynamic content elements and wrapping JSON formatting around them.

    You will need a unique ID for each document that you write to Cosmos DB, for that you can use an expression. Because we declared id to be our unique ID in Cosmos DB, we will use use that for the name. Under expressions, type guid() and press enter to add a unique ID to the JSON document. When complete, you should have a workflow in designer that looks something like this:

    Logic App workflow with trigger and action

    Save the workflow and test the connections by clicking Run Trigger > Run. If connections are working, you should see documents flowing into Cosmos DB over the next few minutes.

    Check the data in Cosmos Db by opening the Data explorer, then choosing the container you created and selecting items. You should see documents similar to this:

    Logic App workflow with trigger and action

    Resources: For self-study!

    Once you've grasped the basics in ths post, there is so much more to learn!

    Thanks for stopping by!

    - - + + \ No newline at end of file diff --git a/blog/page/14/index.html b/blog/page/14/index.html index 4f5726ec9f..41645c76dc 100644 --- a/blog/page/14/index.html +++ b/blog/page/14/index.html @@ -14,13 +14,13 @@ - - + +

    · 4 min read
    Nitya Narasimhan
    Devanshi Joshi

    Welcome to Day 15 of #30DaysOfServerless!

    This post marks the midpoint of our Serverless on Azure journey! Our Week 2 Roadmap showcased two key technologies - Azure Container Apps (ACA) and Dapr - for building serverless microservices. We'll also look at what happened elsewhere in #ServerlessSeptember, then set the stage for our next week's focus: Serverless Integrations.

    Ready? Let's Go!


    What We'll Cover

    • ICYMI: This Week on #ServerlessSeptember
    • Recap: Microservices, Azure Container Apps & Dapr
    • Coming Next: Serverless Integrations
    • Exercise: Take the Cloud Skills Challenge
    • Resources: For self-study!

    This Week In Events

    We had a number of activities happen this week - here's a quick summary:

    This Week in #30Days

    In our #30Days series we focused on Azure Container Apps and Dapr.

    • In Hello Container Apps we learned how Azure Container Apps helps you run microservices and containerized apps on serverless platforms. And we build and deployed our first ACA.
    • In Microservices Communication we explored concepts like environments and virtual networking, with a hands-on example to show how two microservices communicate in a deployed ACA.
    • In Scaling Your Container Apps we learned about KEDA (Kubernetes Event-Driven Autoscaler) and how to configure autoscaling for your ACA based on KEDA-supported triggers.
    • In Build with Dapr we introduced the Distributed Application Runtime (Dapr) and learned how its Building Block APIs and sidecar architecture make it easier to develop microservices with ACA.
    • In Secure ACA Access we learned how to secure ACA access to external services with - and without - Dapr, covering Secret Stores and Managed Identity.
    • Finally, Build ACA with Dapr tied it all together with a enterprise app scenario where an orders processor (ACA) uses Dapr APIs (PubSub, State Management) to receive and store order messages from Azure Service Bus.

    Here's a visual recap:

    Self Study: Code Samples & Tutorials

    There's no better way to get familiar with the concepts, than to dive in and play with code samples and hands-on tutorials. Here are 4 resources to bookmark and try out:

    1. Dapr Quickstarts - these walk you through samples showcasing individual Building Block APIs - with multiple language options available.
    2. Dapr Tutorials provides more complex examples of microservices applications and tools usage, including a Distributed Calculator polyglot app.
    3. Next, try to Deploy a Dapr application to Azure Container Apps to get familiar with the process of setting up the environment, then deploying the app.
    4. Or, explore the many Azure Container Apps samples showcasing various features and more complex architectures tied to real world scenarios.

    What's Next: Serverless Integrations!

    So far we've talked about core technologies (Azure Functions, Azure Container Apps, Dapr) that provide foundational support for your serverless solution. Next, we'll look at Serverless Integrations - specifically at technologies like Azure Logic Apps and Azure Event Grid that automate workflows and create seamless end-to-end solutions that integrate other Azure services in serverless-friendly ways.

    Take the Challenge!

    The Cloud Skills Challenge is still going on, and we've already had hundreds of participants join and complete the learning modules to skill up on Serverless.

    There's still time to join and get yourself on the leaderboard. Get familiar with Azure Functions, SignalR, Logic Apps, Azure SQL and more - in serverless contexts!!


    - - + + \ No newline at end of file diff --git a/blog/page/15/index.html b/blog/page/15/index.html index edc9605144..cc094d337b 100644 --- a/blog/page/15/index.html +++ b/blog/page/15/index.html @@ -14,8 +14,8 @@ - - + +
    @@ -24,7 +24,7 @@ Image showing container apps role assignment

  • Lastly, we need to restart the container app revision, to do so run the command below:

     ##Get revision name and assign it to a variable
    $REVISION_NAME = (az containerapp revision list `
    --name $BACKEND_SVC_NAME `
    --resource-group $RESOURCE_GROUP `
    --query [0].name)

    ##Restart revision by name
    az containerapp revision restart `
    --resource-group $RESOURCE_GROUP `
    --name $BACKEND_SVC_NAME `
    --revision $REVISION_NAME
  • Run end-to-end Test on Azure

    From the Azure Portal, select the Azure Container App orders-processor and navigate to Log stream under Monitoring tab, leave the stream connected and opened. From the Azure Portal, select the Azure Service Bus Namespace ordersservices, select the topic orderreceivedtopic, select the subscription named orders-processor-subscription, then click on Service Bus Explorer (preview). From there we need to publish/send a message. Use the JSON payload below

    ```json
    {
    "data": {
    "reference": "Order 150",
    "quantity": 150,
    "createdOn": "2022-05-10T12:45:22.0983978Z"
    }
    }
    ```

    If all is configured correctly, you should start seeing the information logs in Container Apps Log stream, similar to the images below Image showing publishing messages from Azure Service

    Information logs on the Log stream of the deployed Azure Container App Image showing ACA Log Stream

    🎉 CONGRATULATIONS

    You have successfully deployed to the cloud an Azure Container App and configured Dapr Pub/Sub API with Azure Service Bus.

    9. Clean up

    If you are done with the tutorial, use the following command to delete the resource group and all its contained resources to avoid incurring further costs.

    az group delete --name $RESOURCE_GROUP

    Exercise

    I left for you the configuration of the Dapr State Store API with Azure Cosmos DB :)

    When you look at the action method OrderReceived in controller ExternalOrdersController, you will see that I left a line with ToDo: note, this line is responsible to save the received message (OrderModel) into Azure Cosmos DB.

    There is no need to change anything on the code base (other than removing this commented line), that's the beauty of Dapr Building Blocks and how easy it allows us to plug components to our microservice application without any plumping and brining external SDKs.

    For sure you need to work on the configuration part of Dapr State Store by creating a new component file like what we have done with the Pub/Sub API, things that you need to work on are:

    • Provision Azure Cosmos DB Account and obtain its masterKey.
    • Create a Dapr Component file adhering to Dapr Specs.
    • Create an Azure Container Apps component file adhering to ACA component specs.
    • Test locally on your dev machine using Dapr Component file.
    • Register the new Dapr State Store component with Azure Container Apps Environment and set the Cosmos Db masterKey from the Azure Portal. If you want to challenge yourself more, use the Managed Identity approach as done in this post! The right way to protect your keys and you will not worry about managing CosmosDb keys anymore!
    • Build a new image of the application and push it to Azure Container Registry.
    • Update Azure Container Apps and create a new revision which contains the updated code.
    • Verify the results by checking Azure Cosmos DB, you should see the Order Model stored in Cosmos DB.

    If you need help, you can always refer to my blog post Azure Container Apps State Store With Dapr State Management API which contains exactly what you need to implement here, so I'm very confident you will be able to complete this exercise with no issues, happy coding :)

    What's Next?

    If you enjoyed working with Dapr and Azure Container Apps, and you want to have a deep dive with more complex scenarios (Dapr bindings, service discovery, auto scaling with KEDA, sync services communication, distributed tracing, health probes, etc...) where multiple services deployed to a single Container App Environment; I have created a detailed tutorial which should walk you through step by step with through details to build the application.

    So far, the published posts below, and I'm publishing more posts on weekly basis, so stay tuned :)

    Resources

    - - + + \ No newline at end of file diff --git a/blog/page/16/index.html b/blog/page/16/index.html index 1e6d402671..213ca0ef76 100644 --- a/blog/page/16/index.html +++ b/blog/page/16/index.html @@ -14,14 +14,14 @@ - - + +

    · 11 min read
    Kendall Roden

    Welcome to Day 13 of #30DaysOfServerless!

    In the previous post, we learned about all things Distributed Application Runtime (Dapr) and highlighted the capabilities you can unlock through managed Dapr in Azure Container Apps! Today, we'll dive into how we can make use of Container Apps secrets and managed identities to securely access cloud-hosted resources that your Container Apps depend on!

    Ready? Let's go.


    What We'll Cover

    • Secure access to external services overview
    • Using Container Apps Secrets
    • Using Managed Identity for connecting to Azure resources
    • Using Dapr secret store component references (Dapr-only)
    • Conclusion
    • Resources: For self-study!


    Securing access to external services

    In most, if not all, microservice-based applications, one or more services in the system will rely on other cloud-hosted resources; Think external services like databases, secret stores, message brokers, event sources, etc. To interact with these services, an application must have the ability to establish a secure connection. Traditionally, an application will authenticate to these backing resources using some type of connection string or password.

    I'm not sure if it was just me, but one of the first things I learned as a developer was to ensure credentials and other sensitive information were never checked into the codebase. The ability to inject these values at runtime is a non-negotiable.

    In Azure Container Apps, applications can securely leverage connection information via Container Apps Secrets. If the resource is Azure-based, a more ideal solution that removes the dependence on secrets altogether is using Managed Identity.

    Specifically for Dapr-enabled container apps, users can now tap into the power of the Dapr secrets API! With this new capability unlocked in Container Apps, users can call the Dapr secrets API from application code to securely access secrets from Key Vault or other backing secret stores. In addition, customers can also make use of a secret store component reference when wiring up Dapr state store components and more!

    ALSO, I'm excited to share that support for Dapr + Managed Identity is now available!!. What does this mean? It means that you can enable Managed Identity for your container app - and when establishing connections via Dapr, the Dapr sidecar can use this identity! This means simplified components without the need for secrets when connecting to Azure services!

    Let's dive a bit deeper into the following three topics:

    1. Using Container Apps secrets in your container apps
    2. Using Managed Identity to connect to Azure services
    3. Connecting to services securely for Dapr-enabled apps

    Secure access to external services without Dapr

    Leveraging Container Apps secrets at runtime

    Users can leverage this approach for any values which need to be securely stored, however, it is recommended to use Managed Identity where possible when connecting to Azure-specific resources.

    First, let's establish a few important points regarding secrets in container apps:

    • Secrets are scoped at the container app level, meaning secrets cannot be shared across container apps today
    • When running in multiple-revision mode,
      • changes to secrets do not generate a new revision
      • running revisions will not be automatically restarted to reflect changes. If you want to force-update existing container app revisions to reflect the changed secrets values, you will need to perform revision restarts.
    STEP 1

    Provide the secure value as a secret parameter when creating your container app using the syntax "SECRET_NAME=SECRET_VALUE"

    az containerapp create \
    --resource-group "my-resource-group" \
    --name queuereader \
    --environment "my-environment-name" \
    --image demos/queuereader:v1 \
    --secrets "queue-connection-string=$CONNECTION_STRING"
    STEP 2

    Create an environment variable which references the value of the secret created in step 1 using the syntax "ENV_VARIABLE_NAME=secretref:SECRET_NAME"

    az containerapp create \
    --resource-group "my-resource-group" \
    --name myQueueApp \
    --environment "my-environment-name" \
    --image demos/myQueueApp:v1 \
    --secrets "queue-connection-string=$CONNECTIONSTRING" \
    --env-vars "QueueName=myqueue" "ConnectionString=secretref:queue-connection-string"

    This ConnectionString environment variable can be used within your application code to securely access the connection string value at runtime.

    Using Managed Identity to connect to Azure services

    A managed identity from Azure Active Directory (Azure AD) allows your container app to access other Azure AD-protected resources. This approach is recommended where possible as it eliminates the need for managing secret credentials in your container apps and allows you to properly scope the permissions needed for a given container app using role-based access control. Both system-assigned and user-assigned identities are available in container apps. For more background on managed identities in Azure AD, see Managed identities for Azure resources.

    To configure your app with a system-assigned managed identity you will follow similar steps to the following:

    STEP 1

    Run the following command to create a system-assigned identity for your container app

    az containerapp identity assign \
    --name "myQueueApp" \
    --resource-group "my-resource-group" \
    --system-assigned
    STEP 2

    Retrieve the identity details for your container app and store the Principal ID for the identity in a variable "PRINCIPAL_ID"

    az containerapp identity show \
    --name "myQueueApp" \
    --resource-group "my-resource-group"
    STEP 3

    Assign the appropriate roles and permissions to your container app's managed identity using the Principal ID in step 2 based on the resources you need to access (example below)

    az role assignment create \
    --role "Storage Queue Data Contributor" \
    --assignee $PRINCIPAL_ID \
    --scope "/subscriptions/<subscription>/resourceGroups/<resource-group>/providers/Microsoft.Storage/storageAccounts/<storage-account>/queueServices/default/queues/<queue>"

    After running the above commands, your container app will be able to access your Azure Store Queue because it's managed identity has been assigned the "Store Queue Data Contributor" role. The role assignments you create will be contingent solely on the resources your container app needs to access. To instrument your code to use this managed identity, see more details here.

    In addition to using managed identity to access services from your container app, you can also use managed identity to pull your container images from Azure Container Registry.

    Secure access to external services with Dapr

    For Dapr-enabled apps, there are a few ways to connect to the resources your solutions depend on. In this section, we will discuss when to use each approach.

    1. Using Container Apps secrets in your Dapr components
    2. Using Managed Identity with Dapr Components
    3. Using Dapr Secret Stores for runtime secrets and component references

    Using Container Apps secrets in Dapr components

    Prior to providing support for the Dapr Secret's Management building block, this was the only approach available for securely storing sensitive values for use in Dapr components.

    In Dapr OSS, when no secret store reference is provided in a Dapr component file, the default secret store is set to "Kubernetes secrets". In Container Apps, we do not expose the ability to use this default store. Rather, Container Apps secrets can be used in it's place.

    With the introduction of the Secrets API and the ability to use Dapr + Managed Identity, this approach is useful for a limited number of scenarios:

    • Quick demos and dev/test scenarios using the Container Apps CLI
    • Securing values when a secret store is not configured or available for use
    • Using service principal credentials to configure an Azure Key Vault secret store component (Using Managed Identity is recommend)
    • Securing access credentials which may be required when creating a non-Azure secret store component
    STEP 1

    Create a Dapr component which can be used by one or more services in the container apps environment. In the below example, you will create a secret to store the storage account key and reference this secret from the appropriate Dapr metadata property.

       componentType: state.azure.blobstorage
    version: v1
    metadata:
    - name: accountName
    value: testStorage
    - name: accountKey
    secretRef: account-key
    - name: containerName
    value: myContainer
    secrets:
    - name: account-key
    value: "<STORAGE_ACCOUNT_KEY>"
    scopes:
    - myApp
    STEP 2

    Deploy the Dapr component using the below command with the appropriate arguments.

     az containerapp env dapr-component set \
    --name "my-environment" \
    --resource-group "my-resource-group" \
    --dapr-component-name statestore \
    --yaml "./statestore.yaml"

    Using Managed Identity with Dapr Components

    Dapr-enabled container apps can now make use of managed identities within Dapr components. This is the most ideal path for connecting to Azure services securely, and allows for the removal of sensitive values in the component itself.

    The Dapr sidecar makes use of the existing identities available within a given container app; Dapr itself does not have it's own identity. Therefore, the steps to enable Dapr + MI are similar to those in the section regarding managed identity for non-Dapr apps. See example steps below specifically for using a system-assigned identity:

    1. Create a system-assigned identity for your container app

    2. Retrieve the identity details for your container app and store the Principal ID for the identity in a variable "PRINCIPAL_ID"

    3. Assign the appropriate roles and permissions (for accessing resources backing your Dapr components) to your ACA's managed identity using the Principal ID

    4. Create a simplified Dapr component without any secrets required

          componentType: state.azure.blobstorage
      version: v1
      metadata:
      - name: accountName
      value: testStorage
      - name: containerName
      value: myContainer
      scopes:
      - myApp
    5. Deploy the component to test the connection from your container app via Dapr!

    Keep in mind, all Dapr components will be loaded by each Dapr-enabled container app in an environment by default. In order to avoid apps without the appropriate permissions from loading a component unsuccessfully, use scopes. This will ensure that only applications with the appropriate identities to access the backing resource load the component.

    Using Dapr Secret Stores for runtime secrets and component references

    Dapr integrates with secret stores to provide apps and other components with secure storage and access to secrets such as access keys and passwords. The Dapr Secrets API is now available for use in Container Apps.

    Using Dapr’s secret store building block typically involves the following:

    • Setting up a component for a specific secret store solution.
    • Retrieving secrets using the Dapr secrets API in the application code.
    • Optionally, referencing secrets in Dapr component files.

    Let's walk through a couple sample workflows involving the use of Dapr's Secrets Management capabilities!

    Setting up a component for a specific secret store solution

    1. Create an Azure Key Vault instance for hosting the secrets required by your application.

      az keyvault create --name "<your-unique-keyvault-name>" --resource-group "my-resource-group" --location "<your-location>"
    2. Create an Azure Key Vault component in your environment without the secrets values, as the connection will be established to Azure Key Vault via Managed Identity.

          componentType: secretstores.azure.keyvault
      version: v1
      metadata:
      - name: vaultName
      value: "[your_keyvault_name]"
      scopes:
      - myApp
      az containerapp env dapr-component set \
      --name "my-environment" \
      --resource-group "my-resource-group" \
      --dapr-component-name secretstore \
      --yaml "./secretstore.yaml"
    3. Run the following command to create a system-assigned identity for your container app

      az containerapp identity assign \
      --name "myApp" \
      --resource-group "my-resource-group" \
      --system-assigned
    4. Retrieve the identity details for your container app and store the Principal ID for the identity in a variable "PRINCIPAL_ID"

      az containerapp identity show \
      --name "myApp" \
      --resource-group "my-resource-group"
    5. Assign the appropriate roles and permissions to your container app's managed identity to access Azure Key Vault

      az role assignment create \
      --role "Key Vault Secrets Officer" \
      --assignee $PRINCIPAL_ID \
      --scope /subscriptions/{subscriptionid}/resourcegroups/{resource-group-name}/providers/Microsoft.KeyVault/vaults/{key-vault-name}
    6. Begin using the Dapr Secrets API in your application code to retrieve secrets! See additional details here.

    Referencing secrets in Dapr component files

    Once a Dapr secret store component is available in the environment, it can be used to retrieve secrets for use in other components. For example, when creating a state store component, you can add a reference to the Dapr secret store from which you would like to source connection information. You will no longer use secrets directly in the component spec, but rather will instruct the Dapr sidecar to retrieve the secrets from the specified store.

          componentType: state.azure.blobstorage
    version: v1
    metadata:
    - name: accountName
    value: testStorage
    - name: accountKey
    secretRef: account-key
    - name: containerName
    value: myContainer
    secretStoreComponent: "<SECRET_STORE_COMPONENT_NAME>"
    scopes:
    - myApp

    Summary

    In this post, we have covered the high-level details on how to work with secret values in Azure Container Apps for both Dapr and Non-Dapr apps. In the next article, we will walk through a complex Dapr example from end-to-end which makes use of the new support for Dapr + Managed Identity. Stayed tuned for additional documentation around Dapr secrets as it will be release in the next two weeks!

    Resources

    Here are the main resources to explore for self-study:

    - - + + \ No newline at end of file diff --git a/blog/page/17/index.html b/blog/page/17/index.html index f3524b9a2a..4747702d52 100644 --- a/blog/page/17/index.html +++ b/blog/page/17/index.html @@ -14,13 +14,13 @@ - - + +

    · 8 min read
    Nitya Narasimhan

    Welcome to Day 12 of #30DaysOfServerless!

    So far we've looked at Azure Container Apps - what it is, how it enables microservices communication, and how it enables auto-scaling with KEDA compliant scalers. Today we'll shift gears and talk about Dapr - the Distributed Application Runtime - and how it makes microservices development with ACA easier with core building blocks and a sidecar architecture!

    Ready? Let's go!


    What We'll Cover

    • What is Dapr and why use it?
    • Building Block APIs
    • Dapr Quickstart and Tutorials
    • Dapr-enabled ACA: A Sidecar Approach
    • Exercise: Build & Deploy a Dapr-enabled ACA.
    • Resources: For self-study!


    Hello, Dapr!

    Building distributed applications is hard. Building reliable and portable microservces means having middleware that deals with challenges like service discovery, sync and async communications, state management, secure information sharing and more. Integrating these support services into your application can be challenging from both development and maintenance perspectives, adding complexity that is independent of the core application logic you want to focus on.

    This is where Dapr (Distributed Application Runtime) shines - it's defined as::

    a portable, event-driven runtime that makes it easy for any developer to build resilient, stateless and stateful applications that run on the cloud and edge and embraces the diversity of languages and developer frameworks.

    But what does this actually mean to me as an app developer?


    Dapr + Apps: A Sidecar Approach

    The strength of Dapr lies in its ability to:

    • abstract complexities of distributed systems middleware - with Building Block APIs that implement components using best practices to tackle key challenges.
    • implement a Sidecar Pattern with interactions via APIs - allowing applications to keep their codebase clean and focus on app logic.
    • be Incrementally Adoptable - allowing developers to start by integrating one API, then evolving to use more as and when needed.
    • be Platform Agnostic - allowing applications to be developed in a preferred language or framework without impacting integration capabilities.

    The application-dapr sidecar interaction is illustrated below. The API abstraction allows applications to get the desired functionality without having to know how it was implemented, or without having to integrate Dapr-specific code into their codebase. Note how the sidecar process listens on port 3500 and the API provides clear routes for the specific building blocks supported by Dapr (e.g, /secrets, /state etc.)


    Dapr Building Blocks: API Interactions

    Dapr Building Blocks refers to HTTP and gRPC endpoints exposed by Dapr API endpoints exposed by the Dapr sidecar, providing key capabilities like state management, observability, service-to-service invocation, pub/sub messaging and more to the associated application.

    Building Blocks: Under the Hood
    The Dapr API is implemented by modular components that codify best practices for tackling the specific challenge that they represent. The API abstraction allows component implementations to evolve, or alternatives to be used , without requiring changes to the application codebase.

    The latest Dapr release has the building blocks shown in the above figure. Not all capabilities are available to Azure Container Apps by default - check the documentation for the latest updates on this. For now, Azure Container Apps + Dapr integration provides the following capabilities to the application:

    In the next section, we'll dive into Dapr-enabled Azure Container Apps. Before we do that, here are a couple of resources to help you explore the Dapr platform by itself, and get more hands-on experience with the concepts and capabilities:

    • Dapr Quickstarts - build your first Dapr app, then explore quickstarts for a core APIs including service-to-service invocation, pub/sub, state mangement, bindings and secrets management.
    • Dapr Tutorials - go beyond the basic quickstart and explore more realistic service integrations and usage scenarios. Try the distributed calculator example!

    Integrate Dapr & Azure Container Apps

    Dapr currently has a v1.9 (preview) version, but Azure Container Apps supports Dapr v1.8. In this section, we'll look at what it takes to enable, configure, and use, Dapr integration with Azure Container Apps. It involves 3 steps: enabling Dapr using settings, configuring Dapr components (API) for use, then invoking the APIs.

    Here's a simple a publisher-subscriber scenario from the documentation. We have two Container apps identified as publisher-app and subscriber-app deployed in a single environment. Each ACA has an activated daprd sidecar, allowing them to use the Pub/Sub API to communicate asynchronously with each other - without having to write the underlying pub/sub implementation themselves. Rather, we can see that the Dapr API uses a pubsub,azure.servicebus component to implement that capability.

    Pub/sub example

    Let's look at how this is setup.

    1. Enable Dapr in ACA: Settings

    We can enable Dapr integration in the Azure Container App during creation by specifying settings in one of two ways, based on your development preference:

    • Using Azure CLI: use custom commandline options for each setting
    • Using Infrastructure-as-Code (IaC): using properties for Bicep, ARM templates

    Once enabled, Dapr will run in the same environment as the Azure Container App, and listen on port 3500 for API requests. The Dapr sidecar can be shared my multiple Container Apps deployed in the same environment.

    There are four main settings we will focus on for this demo - the example below shows the ARM template properties, but you can find the equivalent CLI parameters here for comparison.

    • dapr.enabled - enable Dapr for Azure Container App
    • dapr.appPort - specify port on which app is listening
    • dapr.appProtocol - specify if using http (default) or gRPC for API
    • dapr.appId - specify unique application ID for service discovery, usage

    These are defined under the properties.configuration section for your resource. Changing Dapr settings does not update the revision but it will restart ACA revisions and replicas. Here is what the relevant section of the ARM template looks like for the publisher-app ACA in the scenario shown above.

    "dapr": {
    "enabled": true,
    "appId": "publisher-app",
    "appProcotol": "http",
    "appPort": 80
    }

    2. Configure Dapr in ACA: Components

    The next step after activating the Dapr sidecar, is to define the APIs that you want to use and potentially specify the Dapr components (specific implementations of that API) that you prefer. These components are created at environment-level and by default, Dapr-enabled containers apps in an environment will load the complete set of deployed components -- use the scopes property to ensure only components needed by a given app are loaded at runtime. Here's what the ARM template resources section looks like for the example above. This tells us that the environment has a dapr-pubsub component of type pubsub.azure.servicebus deployed - where that component is loaded by container apps with dapr ids (publisher-app, subscriber-app).

    USING MANAGED IDENTITY + DAPR

    The secrets approach used here is idea for demo purposes. However, we recommend using Managed Identity with Dapr in production. For more details on secrets, check out tomorrow's post on Secrets and Managed Identity in Azure Container Apps

    {
    "resources": [
    {
    "type": "daprComponents",
    "name": "dapr-pubsub",
    "properties": {
    "componentType": "pubsub.azure.servicebus",
    "version": "v1",
    "secrets": [
    {
    "name": "sb-root-connectionstring",
    "value": "value"
    }
    ],
    "metadata": [
    {
    "name": "connectionString",
    "secretRef": "sb-root-connectionstring"
    }
    ],
    // Application scopes
    "scopes": ["publisher-app", "subscriber-app"]

    }
    }
    ]
    }

    With this configuration, the ACA is now set to use pub/sub capabilities from the Dapr sidecar, using standard HTTP requests to the exposed API endpoint for this service.

    Exercise: Deploy Dapr-enabled ACA

    In the next couple posts in this series, we'll be discussing how you can use the Dapr secrets API and doing a walkthrough of a more complex example, to show how Dapr-enabled Azure Container Apps are created and deployed.

    However, you can get hands-on experience with these concepts by walking through one of these two tutorials, each providing an alternative approach to configure and setup the application describe in the scenario below:

    Resources

    Here are the main resources to explore for self-study:

    - - + + \ No newline at end of file diff --git a/blog/page/18/index.html b/blog/page/18/index.html index f6450d0187..7808a97f32 100644 --- a/blog/page/18/index.html +++ b/blog/page/18/index.html @@ -14,13 +14,13 @@ - - + +

    · 6 min read
    Melony Qin

    Welcome to Day 12 of #30DaysOfServerless!

    Today, we have a special set of posts from our Zero To Hero 🚀 initiative, featuring blog posts authored by our Product Engineering teams for #ServerlessSeptember. Posts were originally published on the Apps on Azure blog on Microsoft Tech Community.


    What We'll Cover

    • What are Custom Handlers, and why use them?
    • How Custom Handler Works
    • Message Processing With Azure Custom Handler
    • Azure Portal Monitoring


    If you have been working with Azure Functions for a while, you may know Azure Functions is a serverless FaaS (Function as a Service) offered provided by Microsoft Azure, which is built for your key scenarios, including building web APIs, processing file uploads, responding to database changes, processing IoT data streams, managing message queues, and more.

    Custom Handlers: What and Why

    Azure functions support multiple programming languages including C#, F#, Java, JavaScript, Python, typescript, and PowerShell. If you want to get extended language support with Azure functions for other languages such as Go, and Rust, that’s where custom handler comes in.

    An Azure function custom handler allows the use of any language that supports HTTP primitives and author Azure functions. With custom handlers, you can use triggers and input and output bindings via extension bundles, hence it supports all the triggers and bindings you're used to with Azure functions.

    How a Custom Handler Works

    Let’s take a look at custom handlers and how it works.

    • A request is sent to the function host when an event is triggered. It’s up to the function host to issue a request payload to the custom handler, which holds the trigger and inputs binding data as well as other metadata for the function.
    • The custom handler is a local HTTP web server. It executes the function code and returns a response payload to the Functions host.
    • The Functions host passes data from the response to the function's output bindings which will be passed to the downstream stream services for data processing.

    Check out this article to know more about Azure functions custom handlers.


    Message processing with Custom Handlers

    Message processing is one of the key scenarios that Azure functions are trying to address. In the message-processing scenario, events are often collected in queues. These events can trigger Azure functions to execute a piece of business logic.

    You can use the Service Bus trigger to respond to messages from an Azure Service Bus queue - it's then up to the Azure functions custom handlers to take further actions to process the messages. The process is described in the following diagram:

    Building Serverless Go Applications with Azure functions custom handlers

    In Azure function, the function.json defines the function's trigger, input and output bindings, and other configuration settings. Note that every function can have multiple bindings, but it can only have one trigger. The following is an example of setting up the Service Bus queue trigger in the function.json file :

    {
    "bindings": [
    {
    "name": "queueItem",
    "type": "serviceBusTrigger",
    "direction": "in",
    "queueName": "functionqueue",
    "connection": "ServiceBusConnection"
    }
    ]
    }

    You can add a binding definition in the function.json to write the output to a database or other locations of your desire. Supported bindings can be found here.

    As we’re programming in Go, so we need to set the value of defaultExecutablePath to handler in the customHandler.description section in the host.json file.

    Assume we’re programming in Windows OS, and we have named our go application as server.go, after we executed go build server.go command, it produces an executable called server.exe. So here we set server.exe in the host.json as the following example :

      "customHandler": {
    "description": {
    "defaultExecutablePath": "./server.exe",
    "workingDirectory": "",
    "arguments": []
    }
    }

    We’re showcasing a simple Go application here with Azure functions custom handlers where we print out the messages received from the function host. The following is the full code of server.go application :

    package main

    import (
    "encoding/json"
    "fmt"
    "log"
    "net/http"
    "os"
    )

    type InvokeRequest struct {
    Data map[string]json.RawMessage
    Metadata map[string]interface{}
    }

    func queueHandler(w http.ResponseWriter, r *http.Request) {
    var invokeRequest InvokeRequest

    d := json.NewDecoder(r.Body)
    d.Decode(&invokeRequest)

    var parsedMessage string
    json.Unmarshal(invokeRequest.Data["queueItem"], &parsedMessage)

    fmt.Println(parsedMessage)
    }

    func main() {
    customHandlerPort, exists := os.LookupEnv("FUNCTIONS_CUSTOMHANDLER_PORT")
    if !exists {
    customHandlerPort = "8080"
    }
    mux := http.NewServeMux()
    mux.HandleFunc("/MessageProcessorFunction", queueHandler)
    fmt.Println("Go server Listening on: ", customHandlerPort)
    log.Fatal(http.ListenAndServe(":"+customHandlerPort, mux))

    }

    Ensure you have Azure functions core tools installed, then we can use func start command to start our function. Then we’ll have have a C#-based Message Sender application on Github to send out 3000 messages to the Azure service bus queue. You’ll see Azure functions instantly start to process the messages and print out the message as the following:

    Monitoring Serverless Go Applications with Azure functions custom handlers


    Azure portal monitoring

    Let’s go back to Azure portal portal the events see how those messages in Azure Service Bus queue were being processed. There was 3000 messages were queued in the Service Bus queue ( the Blue line stands for incoming Messages ). The outgoing messages (the red line in smaller wave shape ) showing there are progressively being read by Azure functions as the following :

    Monitoring Serverless Go Applications with Azure functions custom handlers

    Check out this article about monitoring Azure Service bus for further information.

    Next steps

    Thanks for following along, we’re looking forward to hearing your feedback. Also, if you discover potential issues, please record them on Azure Functions host GitHub repository or tag us @AzureFunctions on Twitter.

    RESOURCES

    Start to build your serverless applications with custom handlers, check out the official documentation:

    Life is a journey of learning. Let’s stay tuned!

    - - + + \ No newline at end of file diff --git a/blog/page/19/index.html b/blog/page/19/index.html index 7ae3cf4149..edfdb2bb05 100644 --- a/blog/page/19/index.html +++ b/blog/page/19/index.html @@ -14,13 +14,13 @@ - - + +

    · 5 min read
    Anthony Chu

    Welcome to Day 12 of #30DaysOfServerless!

    Today, we have a special set of posts from our Zero To Hero 🚀 initiative, featuring blog posts authored by our Product Engineering teams for #ServerlessSeptember. Posts were originally published on the Apps on Azure blog on Microsoft Tech Community.


    What We'll Cover

    • Using Visual Studio
    • Using Visual Studio Code: Docker, ACA extensions
    • Using Azure CLI
    • Using CI/CD Pipelines


    Last week, @kendallroden wrote about what it means to be Cloud-Native and how Azure Container Apps provides a serverless containers platform for hosting all of your Cloud-Native applications. Today, we’ll walk through a few ways to get your apps up and running on Azure Container Apps.

    Depending on where you are in your Cloud-Native app development journey, you might choose to use different tools to deploy your apps.

    • “Right-click, publish” – Deploying an app directly from an IDE or code editor is often seen as a bad practice, but it’s one of the quickest ways to test out an app in a cloud environment.
    • Command line interface – CLIs are useful for deploying apps from a terminal. Commands can be run manually or in a script.
    • Continuous integration/deployment – To deploy production apps, the recommended approach is to automate the process in a robust CI/CD pipeline.

    Let's explore some of these options in more depth.

    Visual Studio

    Visual Studio 2022 has built-in support for deploying .NET applications to Azure Container Apps. You can use the familiar publish dialog to provision Container Apps resources and deploy to them directly. This helps you prototype an app and see it run in Azure Container Apps with the least amount of effort.

    Journey to the cloud with Azure Container Apps

    Once you’re happy with the app and it’s ready for production, Visual Studio allows you to push your code to GitHub and set up a GitHub Actions workflow to build and deploy your app every time you push changes. You can do this by checking a box.

    Journey to the cloud with Azure Container Apps

    Visual Studio Code

    There are a couple of valuable extensions that you’ll want to install if you’re working in VS Code.

    Docker extension

    The Docker extension provides commands for building a container image for your app and pushing it to a container registry. It can even do this without requiring Docker Desktop on your local machine --- the “Build image in Azure” command remotely builds and pushes a container image to Azure Container Registry.

    Journey to the cloud with Azure Container Apps

    And if your app doesn’t have a dockerfile, the extension will generate one for you.

    Journey to the cloud with Azure Container Apps

    Azure Container Apps extension

    Once you’ve built your container image and pushed it to a registry, the Azure Container Apps VS Code extension provides commands for creating a container app and deploying revisions using the image you’ve built.

    Journey to the cloud with Azure Container Apps

    Azure CLI

    The Azure CLI can be used to manage pretty much anything in Azure. For Azure Container Apps, you’ll find commands for creating, updating, and managing your Container Apps resources.

    Just like in VS Code, with a few commands in the Azure CLI, you can create your Azure resources, build and push your container image, and then deploy it to a container app.

    To make things as simple as possible, the Azure CLI also has an “az containerapp up” command. This single command takes care of everything that’s needed to turn your source code from your local machine to a cloud-hosted application in Azure Container Apps.

    az containerapp up --name myapp --source ./src

    We saw earlier that Visual Studio can generate a GitHub Actions workflow to automatically build and deploy your app on every commit. “az containerapp up” can do this too. The following adds a workflow to a repo.

    az containerapp up --name myapp --repo https://github.com/myorg/myproject

    CI/CD pipelines

    When it’s time to take your app to production, it’s strongly recommended to set up a CI/CD pipeline to automatically and repeatably build, test, and deploy it. We’ve already seen that tools such as Visual Studio and Azure CLI can automatically generate a workflow for GitHub Actions. You can set up a pipeline in Azure DevOps too. This is an example Azure DevOps pipeline.

    trigger:
    branches:
    include:
    - main

    pool:
    vmImage: ubuntu-latest

    stages:

    - stage: Build

    jobs:
    - job: build
    displayName: Build app

    steps:
    - task: Docker@2
    inputs:
    containerRegistry: 'myregistry'
    repository: 'hello-aca'
    command: 'buildAndPush'
    Dockerfile: 'hello-container-apps/Dockerfile'
    tags: '$(Build.BuildId)'

    - stage: Deploy

    jobs:
    - job: deploy
    displayName: Deploy app

    steps:
    - task: AzureCLI@2
    inputs:
    azureSubscription: 'my-subscription(5361b9d6-46ea-43c3-a898-15f14afb0db6)'
    scriptType: 'bash'
    scriptLocation: 'inlineScript'
    inlineScript: |
    # automatically install Container Apps CLI extension
    az config set extension.use_dynamic_install=yes_without_prompt

    # ensure registry is configured in container app
    az containerapp registry set \
    --name hello-aca \
    --resource-group mygroup \
    --server myregistry.azurecr.io \
    --identity system

    # update container app
    az containerapp update \
    --name hello-aca \
    --resource-group mygroup \
    --image myregistry.azurecr.io/hello-aca:$(Build.BuildId)

    Conclusion

    In this article, we looked at a few ways to deploy your Cloud-Native applications to Azure Container Apps and how to decide which one to use based on where you are in your app’s journey to the cloud.

    To learn more, visit Azure Container Apps | Microsoft Azure today!

    ASK THE EXPERT: LIVE Q&A

    The Azure Container Apps team will answer questions live on September 29.

    - - + + \ No newline at end of file diff --git a/blog/page/2/index.html b/blog/page/2/index.html index 925b0a856d..ca79c6aa4c 100644 --- a/blog/page/2/index.html +++ b/blog/page/2/index.html @@ -14,14 +14,14 @@ - - + +

    · 7 min read
    Devanshi Joshi

    It's Serverless September in a Nutshell! Join us as we unpack our month-long learning journey exploring the core technology pillars for Serverless architectures on Azure. Then end with a look at next steps to build your Cloud-native applications on Azure.


    What We'll Cover

    • Functions-as-a-Service (FaaS)
    • Microservices and Containers
    • Serverless Integrations
    • End-to-End Solutions
    • Developer Tools & #Hacktoberfest

    Banner for Serverless September


    Building Cloud-native Apps

    By definition, cloud-native technologies empower organizations to build and run scalable applications in modern, dynamic environments such as public, private, and hybrid clouds. You can learn more about cloud-native in Kendall Roden's #ServerlessSeptember post on Going Cloud-native with Azure Container Apps.

    Serveless technologies accelerate productivity and minimize costs for deploying applications at cloud scale. So, what can we build with serverless technologies in cloud-native on Azure? Anything that is event-driven - examples include:

    • Microservices - scaled by KEDA-compliant triggers
    • Public API Endpoints - scaled by #concurrent HTTP requests
    • Event-Driven Applications - scaled by length of message queue
    • Web Applications - scaled by #concurrent HTTP requests
    • Background Process - scaled by CPU and Memory usage

    Great - but as developers, we really want to know how we can get started building and deploying serverless solutions on Azure. That was the focus of our #ServerlessSeptember journey. Let's take a quick look at the four key themes.

    Functions-as-a-Service (FaaS)

    Functions-as-a-Service (FaaS) is the epitome of developer productivity for full-stack modern apps. As developers, you don't manage infrastructure and focus only on business logic and application code. And, with Serverless Compute you only pay for when your code runs - making this the simplest first step to begin migrating your application to cloud-native.

    In Azure, FaaS is provided by Azure Functions. Check out our Functions + Serverless on Azure to go from learning core concepts, to building your first Functions app in your programming language of choice. Azure functions support multiple programming languages including C#, F#, Java, JavaScript, Python, Typescript, and PowerShell.

    Want to get extended language support for languages like Go, and Rust? You can Use Custom Handlers to make this happen! But what if you want to have long-running functions, or create complex workflows involving more than one function? Read our post on Durable Entities to learn how you can orchestrate this with Azure Functions.

    Check out this recent AskTheExpert Q&A session with the Azure Functions team to get answers to popular community questions on Azure Functions features and usage.

    Microservices and Containers

    Functions-as-a-Service is an ideal first step towards serverless development. But Functions are just one of the 5 pillars of cloud-native. This week we'll look at two of the other pillars: microservices and containers - with specific focus on two core technologies: Azure Container Apps and Dapr (Distributed Application Runtime).

    In this 6-part series of posts, we walk through each technology independently, before looking at the value of building Azure Container Apps with Dapr.

    • In Hello Container Apps we learned core concepts & deployed our first ACA.
    • In Microservices Communication we learned about ACA environments and virtual networks, and how microservices communicate in ACA with a hands-on tutorial.
    • In Scaling Your Container Apps we learned about KEDA (Kubernetes Event-Driven Autoscaler) and configuring ACA for autoscaling with KEDA-compliant triggers.
    • In Build with Dapr we introduced the Distributed Application Runtime (Dapr), exploring its Building Block APIs and sidecar architecture for working with ACA.
    • In Secure ACA Access we learned how to secure ACA access to external services with - and without - Dapr, covering Secret Stores and Managed Identity.
    • Finally, Build ACA with Dapr tied it all together with a enterprise app scenario where an orders processor (ACA) uses Dapr APIs (PubSub, State Management) to receive and store order messages from Azure Service Bus.

    Build ACA with Dapr

    Check out this recent AskTheExpert Q&A session with the Azure Container Apps team for answers to popular community questions on core features and usage.

    Serverless Integrations

    In the first half of the month we looked at compute resources for building and deploying serverless applications. In the second half, we look at integration tools and resources that automate developer workflows to streamline the end-to-end developer experience.

    In Azure, this is enabled by services like Azure Logic Apps and Azure Event Grid. Azure Logic Apps provides a visual designer to create and automate workflows with little or no code involved. Azure Event Grid provides a highly-scable event broker with support for pub/sub communications to drive async event-driven architectures.

    • In Tracking Weather Data Changes With Logic Apps we look at how you can use Logic Apps to integrate the MSN weather service with Azure CosmosDB, allowing automated collection of weather data on changes.

    • In Teach the Cloud to Read & Categorize Mail we take it a step further, using Logic Apps to automate a workflow that includes a Computer Vision service to "read" images and store the results to CosmosDB.

    • In Integrate with Microsoft Graph we explore a multi-cloud scenario (Azure + M365) where change notifications from Microsoft Graph can be integrated using Logic Apps and Event Hubs to power an onboarding workflow.

    • In Cloud Events with Event Grid we learn about the CloudEvents specification (for consistently describing event data) - and learn how Event Grid brokers events in this format. Azure Logic Apps can be an Event handler (subscriber) that uses the event to trigger an automated workflow on receipt.

      Azure Event Grid And Logic Apps

    Want to explore other such integrations? Browse Azure Architectures and filter by selected Azure services for more real-world scenarios.


    End-to-End Solutions

    We've covered serverless compute solutions (for building your serverless applications) and serverless integration services to automate end-to-end workflows in synchronous or asynchronous event-driven architectures. In this final week, we want to leave you with a sense of end-to-end development tools and use cases that can be enabled by Serverless on Azure. Here are some key examples:

    ArticleDescription
    In this tutorial, you'll learn to deploy a containerized ASP.NET Core 6.0 application to Azure Container Apps - with a Blazor front-end and two Web API projects
    Deploy Java containers to cloudIn this tutorial you learn to build and deploy a Java application running on Spring Boot, by publishing it in a container to Azure Container Registry, then deploying to Azure Container Apps,, from ACR, via the Azure Portal.
    **Where am I? My GPS Location with Serverless Power Platform Custom Connector**In this step-by-step tutorial you learn to integrate a serverless application (built on Azure Functions and OpenAPI) with Power Platforms custom connectors via Azure API Management (API-M).This pattern can empower a new ecosystem of fusion apps for cases like inventory management.
    And in our Serverless Hacks initiative, we walked through an 8-step hack to build a serverless tollbooth. Check out this 12-part video walkthrough of a reference solution using .NET.

    Developer Tools

    But wait - there's more. Those are a sample of the end-to-end application scenarios that are built on serverless on Azure. But what about the developer experience? In this article, we say hello to the Azure Developer CLI - an open-source tool that streamlines your develop-deploy workflow, with simple commands that map to core stages of your development journey. Go from code to cloud with one CLI

    And watch this space for more such tutorials and content through October, including a special #Hacktoberfest focused initiative to encourage and support first-time contributors to open-source. Here's a sneak peek at the project we plan to share - the new awesome-azd templates gallery.


    Join us at Microsoft Ignite!

    Want to continue your learning journey, and learn about what's next for Serverless on Azure? Microsoft Ignite happens Oct 12-14 this year and has multiple sessions on relevant technologies and tools. Check out the Session Catalog and register here to attend online.

    - - + + \ No newline at end of file diff --git a/blog/page/20/index.html b/blog/page/20/index.html index 100537e7e1..f0336e4024 100644 --- a/blog/page/20/index.html +++ b/blog/page/20/index.html @@ -14,13 +14,13 @@ - - + +

    · 7 min read
    Paul Yu

    Welcome to Day 11 of #30DaysOfServerless!

    Yesterday we explored Azure Container Concepts related to environments, networking and microservices communication - and illustrated these with a deployment example. Today, we turn our attention to scaling your container apps with demand.


    What We'll Cover

    • What makes ACA Serverless?
    • What is Keda?
    • Scaling Your ACA
    • ACA Scaling In Action
    • Exercise: Explore azure-opensource-labs examples
    • Resources: For self-study!


    So, what makes Azure Container Apps "serverless"?

    Today we are going to focus on what makes Azure Container Apps (ACA) a "serverless" offering. But what does the term "serverless" really mean? As much as we'd like to think there aren't any servers involved, that is certainly not the case. In general, "serverless" means that most (if not all) server maintenance has been abstracted away from you.

    With serverless, you don't spend any time managing and patching servers. This concern is offloaded to Azure and you simply focus on adding business value through application delivery. In addition to operational efficiency, cost efficiency can be achieved with serverless on-demand pricing models. Your workload horizontally scales out based on need and you only pay for what you use. To me, this is serverless, and my teammate @StevenMurawski said it best... "being able to scale to zero is what gives ACA it's serverless magic."

    Scaling your Container Apps

    If you don't know by now, ACA is built on a solid open-source foundation. Behind the scenes, it runs on a managed Kubernetes cluster and includes several open-source components out-of-the box including Dapr to help you build and run microservices, Envoy Proxy for ingress capabilities, and KEDA for event-driven autoscaling. Again, you do not need to install these components yourself. All you need to be concerned with is enabling and/or configuring your container app to leverage these components.

    Let's take a closer look at autoscaling in ACA to help you optimize your container app.

    What is KEDA?

    KEDA stands for Kubernetes Event-Driven Autoscaler. It is an open-source project initially started by Microsoft and Red Hat and has been donated to the Cloud-Native Computing Foundation (CNCF). It is being maintained by a community of 200+ contributors and adopted by many large organizations. In terms of its status as a CNCF project it is currently in the Incubating Stage which means the project has gone through significant due diligence and on its way towards the Graduation Stage.

    Prior to KEDA, horizontally scaling your Kubernetes deployment was achieved through the Horizontal Pod Autoscaler (HPA) which relies on resource metrics such as CPU and memory to determine when additional replicas should be deployed. Being limited to CPU and memory falls a bit short for certain workloads. This is especially true for apps that need to processes messages from a queue or HTTP-based apps that can handle a specific amount of incoming HTTP requests at a time. KEDA aims to fill that gap and provides a much more robust framework for scaling by working in conjunction with HPA. It offers many scalers for you to implement and even allows your deployments to scale to zero! 🥳

    KEDA architecture

    Configuring ACA scale rules

    As I mentioned above, ACA's autoscaling feature leverages KEDA and gives you the ability to configure the number of replicas to deploy based on rules (event triggers). The number of replicas can be configured as a static number or a range (minimum and maximum). So if you need your containers to run 24/7, set the min and max to be the same value. By default, when you deploy a container app, it is set to scale from 0 to 10 replicas. The default scaling rule uses HTTP scaling and defaults to a minimum of 10 concurrent requests per second. Once the threshold of 10 concurrent request per second is met, another replica will be deployed until it reaches the maximum number of replicas.

    At the time of this writing, a container app can have up to 30 replicas.

    Default autoscaler

    As a best practice, if you have a Min / max replicas range configured, you should configure a scaling rule even if it is just explicitly setting the default values.

    Adding HTTP scaling rule

    In addition to HTTP scaling, you can also configure an Azure queue rule, which allows you to use Azure Storage Queues as an event data source.

    Adding Azure Queue scaling rule

    The most flexibility comes with the Custom rule type. This opens up a LOT more options for scaling. All of KEDA's event-based scalers are supported with this option 🚀

    Adding Custom scaling rule

    Translating KEDA templates to Azure templates

    When you implement Custom rules, you need to become familiar with translating KEDA templates to Azure Resource Manager templates or ACA YAML manifests. The KEDA scaler documentation is great and it should be simple to translate KEDA template metadata to an ACA rule metadata.

    The images below shows how to translated a scaling rule which uses Azure Service Bus as an event data source. The custom rule type is set to azure-servicebus and details of the service bus is added to the Metadata section. One important thing to note here is that the connection string to the service bus was added as a secret on the container app and the trigger parameter must be set to connection.

    Azure Container App custom rule metadata

    Azure Container App custom rule metadata

    Additional examples of KEDA scaler conversion can be found in the resources section and example video below.

    See Container App scaling in action

    Now that we've built up some foundational knowledge on how ACA autoscaling is implemented and configured, let's look at a few examples.

    Autoscaling based on HTTP traffic load

    Autoscaling based on Azure Service Bus message queues

    Summary

    ACA brings you a true serverless experience and gives you the ability to configure autoscaling rules based on KEDA scaler templates. This gives you flexibility to scale based on a wide variety of data sources in an event-driven manner. With the amount built-in scalers currently available, there is probably a scaler out there for all your use cases. If not, I encourage you to get involved with the KEDA community and help make it better!

    Exercise

    By now, you've probably read and seen enough and now ready to give this autoscaling thing a try. The example I walked through in the videos above can be found at the azure-opensource-labs repo. I highly encourage you to head over to the containerapps-terraform folder and try the lab out. There you'll find instructions which will cover all the steps and tools you'll need implement autoscaling container apps within your own Azure subscription.

    If you have any questions or feedback, please let us know in the comments below or reach out on Twitter @pauldotyu

    Have fun scaling your containers!

    Resources

    - - + + \ No newline at end of file diff --git a/blog/page/21/index.html b/blog/page/21/index.html index 0eb45febc7..aa3f9c0f85 100644 --- a/blog/page/21/index.html +++ b/blog/page/21/index.html @@ -14,13 +14,13 @@ - - + +

    · 8 min read
    Paul Yu

    Welcome to Day 10 of #30DaysOfServerless!

    We continue our exploraton into Azure Container Apps, with today's focus being communication between microservices, and how to configure your Azure Container Apps environment in the context of a deployment example.


    What We'll Cover

    • ACA Environments & Virtual Networking
    • Basic Microservices Communications
    • Walkthrough: ACA Deployment Example
    • Summary and Next Steps
    • Exercise: Try this yourself!
    • Resources: For self-study!


    Introduction

    In yesterday's post, we learned what the Azure Container Apps (ACA) service is and the problems it aims to solve. It is considered to be a Container-as-a-Service platform since much of the complex implementation details of running a Kubernetes cluster is managed for you.

    Some of the use cases for ACA include event-driven processing jobs and background tasks, but this article will focus on hosting microservices, and how they can communicate with each other within the ACA service. At the end of this article, you will have a solid understanding of how networking and communication is handled and will leave you with a few tutorials to try.

    Environments and virtual networking in ACA

    Before we jump into microservices communication, we should review how networking works within ACA. With ACA being a managed service, Azure will take care of most of your underlying infrastructure concerns. As you provision an ACA resource, Azure provisions an Environment to deploy Container Apps into. This environment is your isolation boundary.

    Azure Container Apps Environment

    By default, Azure creates and manages a new Virtual Network (VNET) for you and the VNET is associated with the environment. As you deploy container apps, they are deployed into the same VNET and the environment is assigned a static public IP address which allows your apps to be accessible over the internet. This VNET is not visible or manageable.

    If you need control of the networking flows within the VNET, you can pre-provision one and tell Azure to deploy an environment within it. This "bring-your-own" VNET model allows you to deploy an environment in either External or Internal modes. Deploying an environment in External mode gives you the flexibility of managing your own VNET, while still allowing your containers to be accessible from outside the environment; a static public IP address is assigned to the environment. When deploying in Internal mode, your containers are accessible within the environment and/or VNET but not accessible from the internet.

    Bringing your own VNET will require some planning and you will need dedicate an empty subnet which will be used exclusively by the ACA environment. The size of your subnet will be dependant on how many containers you plan on deploying and your scaling requirements and one requirement to know is that the subnet address range must have have a /23 CIDR prefix at minimum. You will also need to think about your deployment strategy since ACA has the concept of Revisions which will also consume IPs from your subnet.

    Some additional restrictions to consider when planning your subnet address space is listed in the Resources section below and can be addressed in future posts, so be sure to follow us on dev.to and bookmark the ServerlessSeptember site.

    Basic microservices communication in ACA

    When it comes to communications between containers, ACA addresses this concern with its Ingress capabilities. With HTTP Ingress enabled on your container app, you can expose your app on a HTTPS endpoint.

    If your environment is deployed using default networking and your containers needs to be accessible from outside the environment, you will need to set the Ingress traffic option to Accepting traffic from anywhere. This will generate a Full-Qualified Domain Name (FQDN) which you can use to access your app right away. The ingress feature also generates and assigns a Secure Socket Layer (SSL) certificate for the FQDN.

    External ingress on Container App

    If your environment is deployed using default networking and your containers only need to communicate with other containers in the environment, you'll need to set the Ingress traffic option to Limited to Container Apps Environment. You get a FQDN here as well, but in the section below we'll see how that changes.

    Internal ingress on Container App

    As mentioned in the networking section above, if you deploy your ACA environment into a VNET in internal mode, your options will be Limited to Container Apps Environment or Limited to VNet.

    Ingress on internal virtual network

    Note how the Accepting traffic from anywhere option is greyed out. If your VNET is deployed in external mode, then the option will be available.

    Let's walk though an example ACA deployment

    The diagram below illustrates a simple microservices application that I deployed to ACA. The three container apps all have ingress enabled. The greeting-service app calls two backend services; a hello-service that returns the text Hello (in random casing) and a world-service that returns the text World (in a few random languages). The greeting-service concatenates the two strings together and returns Hello World to the browser. The greeting-service is the only service accessible via external ingress while two backend services are only accessible via internal ingress.

    Greeting Service overview

    With ingress enabled, let's take a quick look at the FQDN structures. Here is the FQDN of the external greeting-service.

    https://greeting-service.victoriouswave-3749d046.eastus.azurecontainerapps.io

    We can break it down into these components:

    https://[YOUR-CONTAINER-APP-NAME].[RANDOM-NAME]-[RANDOM-CHARACTERS].[AZURE-REGION].containerapps.io

    And here is the FQDN of the internal hello-service.

    https://hello-service.internal.victoriouswave-3749d046.eastus.azurecontainerapps.io

    Can you spot the difference between FQDNs?

    That was too easy 😉... the word internal is added as a subdomain in the FQDN between your container app name and the random name for all internal ingress endpoints.

    https://[YOUR-CONTAINER-APP-NAME].internal.[RANDOM-NAME]-[RANDOM-CHARACTERS].[AZURE-REGION].containerapps.io

    Now that we know the internal service FQDNs, we use them in the greeting-service app to achieve basic service-to-service communications.

    So we can inject FQDNs of downstream APIs to upstream apps using environment variables, but the downside to this approach is that need to deploy downstream containers ahead of time and this dependency will need to be planned for during your deployment process. There are ways around this and one option is to leverage the auto-injected environment variables within your app code.

    If I use the Console blade for the hello-service container app and run the env command, you will see environment variables named CONTAINER_APP_NAME and CONTAINER_APP_ENV_DNS_SUFFIX. You can use these values to determine FQDNs within your upstream app.

    hello-service environment variables

    Back in the greeting-service container I can invoke the hello-service container's sayhello method. I know the container app name is hello-service and this service is exposed over an internal ingress, therefore, if I add the internal subdomain to the CONTAINER_APP_ENV_DNS_SUFFIX I can invoke a HTTP request to the hello-service from my greeting-service container.

    Invoke the sayHello method from the greeting-service container

    As you can see, the ingress feature enables communications to other container apps over HTTP/S and ACA will inject environment variables into our container to help determine what the ingress FQDNs would be. All we need now is a little bit of code modification in the greeting-service app and build the FQDNs of our backend APIs by retrieving these environment variables.

    Greeting service code

    ... and now we have a working microservices app on ACA! 🎉

    Hello World

    Summary and next steps

    We've covered Container Apps networking and the basics of how containers communicate with one another. However, there is a better way to address service-to-service invocation using Dapr, which is an open-source framework for building microservices. It is natively integrated into the ACA service and in a future post, you'll learn how to enable it in your Container App to address microservices concerns and more. So stay tuned!

    Exercises

    As a takeaway for today's post, I encourage you to complete this tutorial and if you'd like to deploy the sample app that was presented in this article, my teammate @StevenMurawski is hosting a docker-compose-examples repo which includes samples for deploying to ACA using Docker Compose files. To learn more about the az containerapp compose command, a link to his blog articles are listed in the Resources section below.

    If you have any questions or feedback, please let us know in the comments below or reach out on Twitter @pauldotyu

    Have fun packing and shipping containers! See you in the next post!

    Resources

    The sample app presented here was inspired by services demonstrated in the book Introducing Distributed Application Runtime (Dapr): Simplifying Microservices Applications Development Through Proven and Reusable Patterns and Practices. Go check it out to leran more about Dapr!

    - - + + \ No newline at end of file diff --git a/blog/page/22/index.html b/blog/page/22/index.html index 6e106b508c..7bf8b5dd9b 100644 --- a/blog/page/22/index.html +++ b/blog/page/22/index.html @@ -14,13 +14,13 @@ - - + +

    · 12 min read
    Nitya Narasimhan

    Welcome to Day 9 of #30DaysOfServerless!


    What We'll Cover

    • The Week Ahead
    • Hello, Container Apps!
    • Quickstart: Build Your First ACA!
    • Under The Hood: Core ACA Concepts
    • Exercise: Try this yourself!
    • Resources: For self-study!


    The Week Ahead

    Welcome to Week 2 of #ServerlessSeptember, where we put the focus on Microservices and building Cloud-Native applications that are optimized for serverless solutions on Azure. One week is not enough to do this complex topic justice so consider this a 7-part jumpstart to the longer journey.

    1. Hello, Container Apps (ACA) - Learn about Azure Container Apps, a key service that helps you run microservices and containerized apps on a serverless platform. Know the core concepts. (Tutorial 1: First ACA)
    2. Communication with Microservices - Dive deeper into two key concepts: environments and virtual networking. Learn how microservices communicate in ACA, and walkthrough an example. (Tutorial 2: ACA with 3 Microservices)
    3. Scaling Your Container Apps - Learn about KEDA. Understand how to configure your ACA for auto-scaling with KEDA-supported triggers. Put this into action by walking through a tutorial. (Tutorial 3: Configure Autoscaling)
    4. Hello, Distributed Application Runtime (Dapr) - Learn about Dapr and how its Building Block APIs simplify microservices development with ACA. Know how the sidecar pattern enables incremental adoption of Dapr APIs without requiring any Dapr code integration in app. (Tutorial 4: Setup & Explore Dapr)
    5. Building ACA with Dapr - See how Dapr works with ACA by building a Dapr-enabled Azure Container App. Walk through a .NET tutorial using Pub/Sub and State Management APIs in an enterprise scenario. (Tutorial 5: Build ACA with Dapr)
    6. Managing Secrets With Dapr - We'll look at the Secrets API (a key Building Block of Dapr) and learn how it simplifies management of sensitive information in ACA.
    7. Microservices + Serverless On Azure - We recap Week 2 (Microservices) and set the stage for Week 3 ( Integrations) of Serverless September. Plus, self-study resources including ACA development tutorials in different languages.

    Ready? Let's go!


    Azure Container Apps!

    When building your application, your first decision is about where you host your application. The Azure Architecture Center has a handy chart to help you decide between choices like Azure Functions, Azure App Service, Azure Container Instances, Azure Container Apps and more. But if you are new to this space, you'll need a good understanding of the terms and concepts behind the services Today, we'll focus on Azure Container Apps (ACA) - so let's start with the fundamentals.

    Containerized App Defined

    A containerized app is one where the application components, dependencies, and configuration, are packaged into a single file (container image), which can be instantiated in an isolated runtime environment (container) that is portable across hosts (OS). This makes containers lightweight and scalable - and ensures that applications behave consistently on different host platforms.

    Container images can be shared via container registries (public or private) helping developers discover and deploy related apps with less effort. Scaling a containerized app can be as simple as activating more instances of its container image. However, this requires container orchestrators to automate the management of container apps for efficiency. Orchestrators use technologies like Kubernetes to support capabilities like workload scheduling, self-healing and auto-scaling on demand.

    Cloud-Native & Microservices

    Containers are seen as one of the 5 pillars of Cloud-Native app development, an approach where applications are designed explicitly to take advantage of the unique benefits of modern dynamic environments (involving public, private and hybrid clouds). Containers are particularly suited to serverless solutions based on microservices.

    • With serverless - developers use managed services instead of managing their own infrastructure. Services are typically event-driven and can be configured for autoscaling with rules tied to event triggers. Serverless is cost-effective, with developers paying only for the compute cycles and resources they use.
    • With microservices - developers compose their applications from independent components. Each component can be deployed in its own container, and scaled at that granularity. This simplifies component reuse (across apps) and maintainability (over time) - with developers evolving functionality at microservice (vs. app) levels.

    Hello, Azure Container Apps!

    Azure Container Apps is the managed service that helps you run containerized apps and microservices as a serverless compute solution, on Azure. You can:

    • deploy serverless API endpoints - autoscaled by HTTP request traffic
    • host background processing apps - autoscaled by CPU or memory load
    • handle event-driven processing - autoscaled by #messages in queue
    • run microservices - autoscaled by any KEDA-supported scaler.

    Want a quick intro to the topic? Start by watching the short video below - then read these two posts from our ZeroToHero series:


    Deploy Your First ACA

    Dev Options

    We typically have three options for development:

    • Use the Azure Portal - provision and deploy from a browser.
    • Use Visual Studio Code (with relevant extensions) - if you prefer an IDE
    • Using Azure CLI - if you prefer to build and deploy from command line.

    The documentation site has quickstarts for three contexts:

    For this quickstart, we'll go with the first option (sample image) so we can move quickly to core concepts. We'll leave the others as an exercise for you to explore.

    1. Setup Resources

    PRE-REQUISITES

    You need:

    • An Azure account with an active subscription
    • An installed Azure CLI

    Start by logging into Azure from the CLI. The command should launch a browser to complete the auth flow (or give you an option to take an alternative path).

    $ az login

    Successful authentication will result in extensive command-line output detailing the status of your subscription.

    Next, install the Azure Container Apps extension for the CLI

    $ az extension add --name containerapp --upgrade
    ...
    The installed extension 'containerapp' is in preview.

    Once successfully installed, register the Microsoft.App namespace.

    $ az provider register --namespace Microsoft.App

    Then set local environment variables in that terminal - and verify they are set correctly:

    $ RESOURCE_GROUP="my-container-apps"
    $ LOCATION="canadacentral"
    $ CONTAINERAPPS_ENVIRONMENT="my-environment"

    $ echo $LOCATION $RESOURCE_GROUP $CONTAINERAPPS_ENVIRONMENT
    canadacentral my-container-apps my-environment

    Now you can use Azure CLI to provision a resource group for this tutorial. Creating a resource group also makes it easier for us to delete/reclaim all resources used at the end of this tutorial.

    az group create \
    --name $RESOURCE_GROUP \
    --location $LOCATION
    Congratulations

    You completed the Setup step!

    On completion, the console should print out the details of the newly created resource group. You should also be able to visit the Azure Portal and find the newly-active my-container-apps resource group under your active subscription.

    2. Create Environment

    An environment is like the picket fence around your property. It creates a secure boundary that contains a group of container apps - such that all apps deployed to it share the same virtual network and logging resources.

    $ az containerapp env create \
    --name $CONTAINERAPPS_ENVIRONMENT \
    --resource-group $RESOURCE_GROUP \
    --location $LOCATION

    No Log Analytics workspace provided.
    Generating a Log Analytics workspace with name ...

    This can take a few minutes. When done, you will see the terminal display more details. You can also check the resource group in the portal and see that a Container Apps Environment and a Log Analytics Workspace are created for you as part of this step.

    You've got the fence set up. Now it's time to build your home - er, container app!

    3. Create Container App

    Here's the command we'll use to create our first Azure Container App. Note that the --image argument provides the link to a pre-existing containerapps-helloworld image.

    az containerapp create \
    --name my-container-app \
    --resource-group $RESOURCE_GROUP \
    --environment $CONTAINERAPPS_ENVIRONMENT \
    --image mcr.microsoft.com/azuredocs/containerapps-helloworld:latest \
    --target-port 80 \
    --ingress 'external' \
    --query properties.configuration.ingress.fqdn
    ...
    ...

    Container app created. Access your app at <URL>

    The --ingress property shows that the app is open to external requests; in other words, it is publicly visible at the <URL> that is printed out on the terminal on successsful completion of this step.

    4. Verify Deployment

    Let's see if this works. You can verify that your container app by visitng the URL returned above in your browser. You should see something like this!

    Container App Hello World

    You can also visit the Azure Portal and look under the created Resource Group. You should see a new Container App type of resource was created after this step.

    Congratulations

    You just created and deployed your first "Hello World" Azure Container App! This validates your local development environment setup and existence of a valid Azure subscription.

    5. Clean Up Your Resources

    It's good practice to clean up resources once you are done with a tutorial.

    THIS ACTION IS IRREVERSIBLE

    This command deletes the resource group we created above - and all resources in it. So make sure you specified the right name, then confirm deletion.

    $ az group delete --name $RESOURCE_GROUP
    Are you sure you want to perform this operation? (y/n):

    Note that you can also delete the resource group from the Azure Portal interface if that feels more comfortable. For now, we'll just use the Portal to verify that deletion occurred. If you had previously opened the Resource Group page for the created resource, just refresh it. You should see something like this:

    Resource Not Found


    Core Concepts

    COMING SOON

    An illustrated guide summarizing these concepts in a single sketchnote.

    We covered a lot today - we'll stop with a quick overview of core concepts behind Azure Container Apps, each linked to documentation for self-study. We'll dive into more details on some of these concepts in upcoming articles:

    • Environments - are the secure boundary around a group of container apps that are deployed in the same virtual network. They write logs to a shared Log Analytics workspace and can communicate seamlessly using Dapr, if used.
    • Containers refer to the container image deployed in the Azure Container App. They can use any runtime, programming language, or development stack - and be discovered using any public or private container registry. A container app can support multiple containers.
    • Revisions are immutable snapshots of an Azure Container App. The first revision is created when the ACA is first deployed, with new revisions created when redeployment occurs with revision-scope changes. Multiple revisions can run concurrently in an environment.
    • Application Lifecycle Management revolves around these revisions, with a container app having three phases: deployment, update and deactivation.
    • Microservices are independent units of functionality in Cloud-Native architectures. A single container app typically represents a single microservice, and can be composed from one or more containers. Microservices can now be scaled and upgraded indepedently, giving your application more flexbility and control.
    • Networking architecture consist of a virtual network (VNET) associated with the environment. Unless you provide a custom VNET at environment creation time, a default VNET is automatically created. The VNET configuration determines access (ingress, internal vs. external) and can influence auto-scaling choices (e.g., use HTTP Edge Proxy and scale based on number of HTTP requests).
    • Observability is about monitoring the health of your application and diagnosing it to improve reliability or performance. Azure Container Apps has a number of features - from Log streaming and Container console to integration with Azure Monitor - to provide a holistic view of application status over time.
    • Easy Auth is possible with built-in support for authentication and authorization including support for popular identity providers like Facebook, Google, Twitter and GitHub - alongside the Microsoft Identity Platform.

    Keep these terms in mind as we walk through more tutorials this week, to see how they find application in real examples. Finally, a note on Dapr, the Distributed Application Runtime that abstracts away many of the challenges posed by distributed systems - and lets you focus on your application logic.

    DAPR INTEGRATION MADE EASY

    Dapr uses a sidecar architecture, allowing Azure Container Apps to communicate with Dapr Building Block APIs over either gRPC or HTTP. Your ACA can be built to run with or without Dapr - giving you the flexibility to incrementally adopt specific APIs and unlock related capabilities as the need arises.

    In later articles this week, we'll do a deeper dive into Dapr and build our first Dapr-enable Azure Container App to get a better understanding of this integration.

    Exercise

    Congratulations! You made it! By now you should have a good idea of what Cloud-Native development means, why Microservices and Containers are important to that vision - and how Azure Container Apps helps simplify the building and deployment of microservices based applications using serverless architectures on Azure.

    Now it's your turn to reinforce learning by doing.

    Resources

    Three key resources to bookmark and explore:

    - - + + \ No newline at end of file diff --git a/blog/page/23/index.html b/blog/page/23/index.html index c91dc2ba5b..89a516c09a 100644 --- a/blog/page/23/index.html +++ b/blog/page/23/index.html @@ -14,13 +14,13 @@ - - + +

    · 5 min read
    Nitya Narasimhan
    Devanshi Joshi

    SEP 08: CHANGE IN PUBLISHING SCHEDULE

    Starting from Week 2 (Sep 8), we'll be publishing blog posts in batches rather than on a daily basis, so you can read a series of related posts together. Don't want to miss updates? Just subscribe to the feed


    Welcome to Day 8 of #30DaysOfServerless!

    This marks the end of our Week 1 Roadmap focused on Azure Functions!! Today, we'll do a quick recap of all #ServerlessSeptember activities in Week 1, set the stage for Week 2 - and leave you with some excellent tutorials you should explore to build more advanced scenarios with Azure Functions.

    Ready? Let's go.


    What We'll Cover

    • Azure Functions: Week 1 Recap
    • Advanced Functions: Explore Samples
    • End-to-End: Serverless Hacks & Cloud Skills
    • What's Next: Hello, Containers & Microservices
    • Challenge: Complete the Learning Path


    Week 1 Recap: #30Days & Functions

    Congratulations!! We made it to the end of Week 1 of #ServerlessSeptember. Let's recap what we learned so far:

    • In Core Concepts we looked at where Azure Functions fits into the serverless options available on Azure. And we learned about key concepts like Triggers, Bindings, Custom Handlers and Durable Functions.
    • In Build Your First Function we looked at the tooling options for creating Functions apps, testing them locally, and deploying them to Azure - as we built and deployed our first Functions app.
    • In the next 4 posts, we explored new Triggers, Integrations, and Scenarios - as we looked at building Functions Apps in Java, JavaScript, .NET and Python.
    • And in the Zero-To-Hero series, we learned about Durable Entities - and how we can use them to create stateful serverless solutions using a Chirper Sample as an example scenario.

    The illustrated roadmap below summarizes what we covered each day this week, as we bring our Functions-as-a-Service exploration to a close.


    Advanced Functions: Code Samples

    So, now that we've got our first Functions app under our belt, and validated our local development setup for tooling, where can we go next? A good next step is to explore different triggers and bindings, that drive richer end-to-end scenarios. For example:

    • Integrate Functions with Azure Logic Apps - we'll discuss Azure Logic Apps in Week 3. For now, think of it as a workflow automation tool that lets you integrate seamlessly with other supported Azure services to drive an end-to-end scenario. In this tutorial, we set up a workflow connecting Twitter (get tweet) to Azure Cognitive Services (analyze sentiment) - and use that to trigger an Azure Functions app to send email about the result.
    • Integrate Functions with Event Grid - we'll discuss Azure Event Grid in Week 3. For now, think of it as an eventing service connecting event sources (publishers) to event handlers (subscribers) at cloud scale. In this tutorial, we handle a common use case - a workflow where loading an image to Blob Storage triggers an Azure Functions app that implements a resize function, helping automatically generate thumbnails for the uploaded image.
    • Integrate Functions with CosmosDB and SignalR to bring real-time push-based notifications to your web app. It achieves this by using a Functions app that is triggered by changes in a CosmosDB backend, causing it to broadcast that update (push notification to connected web clients over SignalR, in real time.

    Want more ideas? Check out the Azure Samples for Functions for implementations, and browse the Azure Architecture Center for reference architectures from real-world scenarios that involve Azure Functions usage.


    E2E Scenarios: Hacks & Cloud Skills

    Want to systematically work your way through a single End-to-End scenario involving Azure Functions alongside other serverless support technologies? Check out the Serverless Hacks activity happening during #ServerlessSeptember, and learn to build this "Serverless Tollbooth Application" in a series of 10 challenges. Check out the video series for a reference solution in .NET and sign up for weekly office hours to join peers and discuss your solutions or challenges.

    Or perhaps you prefer to learn core concepts with code in a structured learning path? We have that covered. Check out the 12-module "Create Serverless Applications" course from Microsoft Learn which walks your through concepts, one at a time, with code. Even better - sign up for the free Cloud Skills Challenge and complete the same path (in under 30 days) but this time, with the added fun of competing against your peers for a spot on a leaderboard, and swag.


    What's Next? Hello, Cloud-Native!

    So where to next? In Week 2 we turn our attention from Functions-as-a-Service to building more complex backends using Containers and Microservices. We'll focus on two core technologies - Azure Container Apps and Dapr (Distributed Application Runtime) - both key components of a broader vision around Building Cloud-Native Applications in Azure.

    What is Cloud-Native you ask?

    Fortunately for you, we have an excellent introduction in our Zero-to-Hero article on Go Cloud-Native with Azure Container Apps - that explains the 5 pillars of Cloud-Native and highlights the value of Azure Container Apps (scenarios) and Dapr (sidecar architecture) for simplified microservices-based solution with auto-scale capability. Prefer a visual summary? Here's an illustrate guide to that article for convenience.

    Go Cloud-Native Download a higher resolution version of the image


    Take The Challenge

    We typically end each post with an exercise or activity to reinforce what you learned. For Week 1, we encourage you to take the Cloud Skills Challenge and work your way through at least a subset of the modules, for hands-on experience with the different Azure Functions concepts, integrations, and usage.

    See you in Week 2!

    - - + + \ No newline at end of file diff --git a/blog/page/24/index.html b/blog/page/24/index.html index 128f796412..428bae6765 100644 --- a/blog/page/24/index.html +++ b/blog/page/24/index.html @@ -14,15 +14,15 @@ - - + +

    · 7 min read
    Jay Miller

    Welcome to Day 7 of #30DaysOfServerless!

    Over the past couple of days, we've explored Azure Functions from the perspective of specific programming languages. Today we'll continue that trend by looking at Python - exploring the Timer Trigger and CosmosDB binding, and showcasing integration with a FastAPI-implemented web app.

    Ready? Let's go.


    What We'll Cover

    • Developer Guidance: Azure Functions On Python
    • Build & Deploy: Wildfire Detection Apps with Timer Trigger + CosmosDB
    • Demo: My Fire Map App: Using FastAPI and Azure Maps to visualize data
    • Next Steps: Explore Azure Samples
    • Exercise: Try this yourself!
    • Resources: For self-study!


    Developer Guidance

    If you're a Python developer new to serverless on Azure, start with the Azure Functions Python Developer Guide. It covers:

    • Quickstarts with Visual Studio Code and Azure CLI
    • Adopting best practices for hosting, reliability and efficiency.
    • Tutorials showcasing Azure automation, image classification and more
    • Samples showcasing Azure Functions features for Python developers

    Now let's dive in and build our first Python-based Azure Functions app.


    Detecting Wildfires Around the World?

    I live in California which is known for lots of wildfires. I wanted to create a proof of concept for developing an application that could let me know if there was a wildfire detected near my home.

    NASA has a few satelites orbiting the Earth that can detect wildfires. These satelites take scans of the radiative heat in and use that to determine the likelihood of a wildfire. NASA updates their information about every 30 minutes and it can take about four hours for to scan and process information.

    Fire Point Near Austin, TX

    I want to get the information but I don't want to ping NASA or another service every time I check.

    What if I occaisionally download all the data I need? Then I can ping that as much as I like.

    I can create a script that does just that. Any time I say I can create a script that is a verbal queue for me to consider using an Azure function. With the function being ran in the cloud, I can ensure the script runs even when I'm not at my computer.

    How the Timer Trigger Works

    This function will utilize the Timer Trigger. This means Azure will call this function to run at a scheduled interval. This isn't the only way to keep the data in sync, but we know that arcgis, the service that we're using says that data is only updated every 30 minutes or so.

    To learn more about the TimerTrigger as a concept, check out the Azure Functions documentation around Timers.

    When we create the function we tell it a few things like where the script will live (in our case in __init__.py) the type and direction and notably often it should run. We specify the timer using schedule": <The CRON INTERVAL>. For us we're using 0 0,30 * * * which means every 30 minutes at the hour and half-hour.

    {
    "scriptFile": "__init__.py",
    "bindings": [
    {
    "name": "reqTimer",
    "type": "timerTrigger",
    "direction": "in",
    "schedule": "0 0,30 * * * *"
    }
    ]
    }

    Next, we create the code that runs when the function is called.

    Connecting to the Database and our Source

    Disclaimer: The data that we're pulling is for educational purposes only. This is not meant to be a production level application. You're welcome play with this project but ensure that you're using the data in compliance with Esri.

    Our function does two important things.

    1. It pulls data from ArcGIS that meets the parameters
    2. It stores that pulled data into our database

    If you want to check out the code in its entirety, check out the GitHub repository.

    Pulling the data from ArcGIS is easy. We can use the ArcGIS Python API. Then, we need to load the service layer. Finally we query that layer for the specific data.

    def write_new_file_data(gis_id:str, layer:int=0) -> FeatureSet:
    """Returns a JSON String of the Dataframe"""
    fire_data = g.content.get(gis_id)
    feature = fire_data.layers[layer] # Loading Featured Layer from ArcGIS
    q = feature.query(
    where="confidence >= 65 AND hours_old <= 4", #The filter for the query
    return_distince_values=True,
    out_fields="confidence, hours_old", # The data we want to store with our points
    out_sr=4326, # The spatial reference of the data
    )
    return q

    Then we need to store the data in our database.

    We're using Cosmos DB for this. COSMOSDB is a NoSQL database, which means that the data looks a lot like a python dictionary as it's JSON. This means that we don't need to worry about converting the data into a format that can be stored in a relational database.

    The second reason is that Cosmos DB is tied into the Azure ecosystem so that if we want to create functions Azure events around it, we can.

    Our script grabs the information that we pulled from ArcGIS and stores it in our database.

    async with CosmosClient.from_connection_string(COSMOS_CONNECTION_STRING) as client:
    container = database.get_container_client(container=CONTAINER)
    for record in data:
    await container.create_item(
    record,
    enable_automatic_id_generation=True,
    )

    In our code each of these functions live in their own space. So in the main function we focus solely on what azure functions will be doing. The script that gets called is __init__.py. There we'll have the function call the other functions running.

    We created another function called load_and_write that does all the work outlined above. __init__.py will call that.

    async def main(reqTimer: func.TimerRequest) -> None:
    database=database
    container=container
    await update_db.load_and_write(gis_id=GIS_LAYER_ID, database=database, container=container)

    Then we deploy the function to Azure. I like to use VS Code's Azure Extension but you can also deploy it a few other ways.

    Deploying the function via VS Code

    Once the function is deployed we can load the Azure portal and see a ping whenever the function is called. The pings correspond to the Function being ran

    We can also see the data now living in the datastore. Document in Cosmos DB

    It's in the Database, Now What?

    Now the real fun begins. We just loaded the last bit of fire data into a database. We can now query that data and serve it to others.

    As I mentioned before, our Cosmos DB data is also stored in Azure, which means that we can deploy Azure Functions to trigger when new data is added. Perhaps you can use this to check for fires near you and use a Logic App to send an alert to your phone or email.

    Another option is to create a web application that talks to the database and displays the data. I've created an example of this using FastAPI – https://jm-func-us-fire-notify.azurewebsites.net.

    Website that Checks for Fires


    Next Steps

    This article showcased the Timer Trigger and the HTTP Trigger for Azure Functions in Python. Now try exploring other triggers and bindings by browsing Bindings code samples for Python and Azure Functions samples for Python

    Once you've tried out the samples, you may want to explore more advanced integrations or extensions for serverless Python scenarios. Here are some suggestions:

    And check out the resources for more tutorials to build up your Azure Functions skills.

    Exercise

    I encourage you to fork the repository and try building and deploying it yourself! You can see the TimerTrigger and a HTTPTrigger building the website.

    Then try extending it. Perhaps if wildfires are a big thing in your area, you can use some of the data available in Planetary Computer to check out some other datasets.

    Resources

    - - + + \ No newline at end of file diff --git a/blog/page/25/index.html b/blog/page/25/index.html index e5fc5cb4aa..f030d6b75d 100644 --- a/blog/page/25/index.html +++ b/blog/page/25/index.html @@ -14,13 +14,13 @@ - - + +

    · 10 min read
    Mike James
    Matt Soucoup

    Welcome to Day 6 of #30DaysOfServerless!

    The theme for this week is Azure Functions. Today we're going to talk about why Azure Functions are a great fit for .NET developers.


    What We'll Cover

    • What is serverless computing?
    • How does Azure Functions fit in?
    • Let's build a simple Azure Function in .NET
    • Developer Guide, Samples & Scenarios
    • Exercise: Explore the Create Serverless Applications path.
    • Resources: For self-study!

    A banner image that has the title of this article with the author&#39;s photo and a drawing that summarizes the demo application.


    The leaves are changing colors and there's a chill in the air, or for those lucky folks in the Southern Hemisphere, the leaves are budding and a warmth is in the air. Either way, that can only mean one thing - it's Serverless September!🍂 So today, we're going to take a look at Azure Functions - what they are, and why they're a great fit for .NET developers.

    What is serverless computing?

    For developers, serverless computing means you write highly compact individual functions that do one thing - and run in the cloud. These functions are triggered by some external event. That event could be a record being inserted into a database, a file uploaded into BLOB storage, a timer interval elapsed, or even a simple HTTP request.

    But... servers are still definitely involved! What has changed from other types of cloud computing is that the idea and ownership of the server has been abstracted away.

    A lot of the time you'll hear folks refer to this as Functions as a Service or FaaS. The defining characteristic is all you need to do is put together your application logic. Your code is going to be invoked in response to events - and the cloud provider takes care of everything else. You literally get to focus on only the business logic you need to run in response to something of interest - no worries about hosting.

    You do not need to worry about wiring up the plumbing between the service that originates the event and the serverless runtime environment. The cloud provider will handle the mechanism to call your function in response to whatever event you chose to have the function react to. And it passes along any data that is relevant to the event to your code.

    And here's a really neat thing. You only pay for the time the serverless function is running. So, if you have a function that is triggered by an HTTP request, and you rarely get requests to your function, you would rarely pay.

    How does Azure Functions fit in?

    Microsoft's Azure Functions is a modern serverless architecture, offering event-driven cloud computing that is easy for developers to use. It provides a way to run small pieces of code or Functions in the cloud without developers having to worry themselves about the infrastructure or platform the Function is running on.

    That means we're only concerned about writing the logic of the Function. And we can write that logic in our choice of languages... like C#. We are also able to add packages from NuGet to Azure Functions—this way, we don't have to reinvent the wheel and can use well-tested libraries.

    And the Azure Functions runtime takes care of a ton of neat stuff for us, like passing in information about the event that caused it to kick off - in a strongly typed variable. It also "binds" to other services, like Azure Storage, we can easily access those services from our code without having to worry about new'ing them up.

    Let's build an Azure Function!

    Scaffold the Function

    Don't worry about having an Azure subscription or even being connected to the internet—we can develop and debug Azure Functions locally using either Visual Studio or Visual Studio Code!

    For this example, I'm going to use Visual Studio Code to build up a Function that responds to an HTTP trigger and then writes a message to an Azure Storage Queue.

    Diagram of the how the Azure Function will use the HTTP trigger and the Azure Storage Queue Binding

    The incoming HTTP call is the trigger and the message queue the Function writes to is an output binding. Let's have at it!

    info

    You do need to have some tools downloaded and installed to get started. First and foremost, you'll need Visual Studio Code. Then you'll need the Azure Functions extension for VS Code to do the development with. Finally, you'll need the Azurite Emulator installed as well—this will allow us to write to a message queue locally.

    Oh! And of course, .NET 6!

    Now with all of the tooling out of the way, let's write a Function!

    1. Fire up Visual Studio Code. Then, from the command palette, type: Azure Functions: Create New Project

      Screenshot of create a new function dialog in VS Code

    2. Follow the steps as to which directory you want to create the project in and which .NET runtime and language you want to use.

      Screenshot of VS Code prompting which directory and language to use

    3. Pick .NET 6 and C#.

      It will then prompt you to pick the folder in which your Function app resides and then select a template.

      Screenshot of VS Code prompting you to pick the Function trigger template

      Pick the HTTP trigger template. When prompted for a name, call it: PostToAQueue.

    Execute the Function Locally

    1. After giving it a namespace, it prompts for an authorization level—pick Anonymous. Now we have a Function! Let's go ahead and hit F5 and see it run!
    info

    After the templates have finished installing, you may get a prompt to download additional components—these are NuGet packages. Go ahead and do that.

    When it runs, you'll see the Azure Functions logo appear in the Terminal window with the URL the Function is located at. Copy that link.

    Screenshot of the Azure Functions local runtime starting up

    1. Type the link into a browser, adding a name parameter as shown in this example: http://localhost:7071/api/PostToAQueue?name=Matt. The Function will respond with a message. You can even set breakpoints in Visual Studio Code and step through the code!

    Write To Azure Storage Queue

    Next, we'll get this HTTP trigger Function to write to a local Azure Storage Queue. First we need to add the Storage NuGet package to our project. In the terminal, type:

    dotnet add package Microsoft.Azure.WebJobs.Extensions.Storage

    Then set a configuration setting to tell the Function runtime where to find the Storage. Open up local.settings.json and set "AzureWebJobsStorage" to "UseDevelopmentStorage=true". The full file will look like:

    {
    "IsEncrypted": false,
    "Values": {
    "AzureWebJobsStorage": "UseDevelopmentStorage=true",
    "AzureWebJobsDashboard": ""
    }
    }

    Then create a new class within your project. This class will hold nothing but properties. Call it whatever you want and add whatever properties you want to it. I called mine TheMessage and added an Id and Name properties to it.

    public class TheMessage
    {
    public string Id { get; set; }
    public string Name { get; set; }
    }

    Finally, change your PostToAQueue Function, so it looks like the following:


    public static class PostToAQueue
    {
    [FunctionName("PostToAQueue")]
    public static async Task<IActionResult> Run(
    [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req,
    [Queue("demoqueue", Connection = "AzureWebJobsStorage")] IAsyncCollector<TheMessage> messages,
    ILogger log)
    {
    string name = req.Query["name"];

    await messages.AddAsync(new TheMessage { Id = System.Guid.NewGuid().ToString(), Name = name });

    return new OkResult();
    }
    }

    Note the addition of the messages variable. This is telling the Function to use the storage connection we specified before via the Connection property. And it is also specifying which queue to use in that storage account, in this case demoqueue.

    All the code is doing is pulling out the name from the query string, new'ing up a new TheMessage class and adding that to the IAsyncCollector variable.

    That will add the new message to the queue!

    Make sure Azurite is started within VS Code (both the queue and blob emulators). Run the app and send the same GET request as before: http://localhost:7071/api/PostToAQueue?name=Matt.

    If you have the Azure Storage Explorer installed, you can browse your local Queue and see the new message in there!

    Screenshot of Azure Storage Explorer with the new message in the queue

    Summing Up

    We had a quick look at what Microsoft's serverless offering, Azure Functions, is comprised of. It's a full-featured FaaS offering that enables you to write functions in your language of choice, including reusing packages such as those from NuGet.

    A highlight of Azure Functions is the way they are triggered and bound. The triggers define how a Function starts, and bindings are akin to input and output parameters on it that correspond to external services. The best part is that the Azure Function runtime takes care of maintaining the connection to the external services so you don't have to worry about new'ing up or disposing of the connections yourself.

    We then wrote a quick Function that gets triggered off an HTTP request and then writes a query string parameters from that request into a local Azure Storage Queue.

    What's Next

    So, where can you go from here?

    Think about how you can build real-world scenarios by integrating other Azure services. For example, you could use serverless integrations to build a workflow where the input payload received using an HTTP Trigger, is now stored in Blob Storage (output binding), which in turn triggers another service (e.g., Cognitive Services) that processes the blob and returns an enhanced result.

    Keep an eye out for an update to this post where we walk through a scenario like this with code. Check out the resources below to help you get started on your own.

    Exercise

    This brings us close to the end of Week 1 with Azure Functions. We've learned core concepts, built and deployed our first Functions app, and explored quickstarts and scenarios for different programming languages. So, what can you do to explore this topic on your own?

    • Explore the Create Serverless Applications learning path which has several modules that explore Azure Functions integrations with various services.
    • Take up the Cloud Skills Challenge and complete those modules in a fun setting where you compete with peers for a spot on the leaderboard!

    Then come back tomorrow as we wrap up the week with a discussion on end-to-end scenarios, a recap of what we covered this week, and a look at what's ahead next week.

    Resources

    Start here for developer guidance in getting started with Azure Functions as a .NET/C# developer:

    Then learn about supported Triggers and Bindings for C#, with code snippets to show how they are used.

    Finally, explore Azure Functions samples for C# and learn to implement serverless solutions. Examples include:

    - - + + \ No newline at end of file diff --git a/blog/page/26/index.html b/blog/page/26/index.html index 1074471d07..7ff162201a 100644 --- a/blog/page/26/index.html +++ b/blog/page/26/index.html @@ -14,15 +14,15 @@ - - + +

    · 8 min read
    David Justo

    Welcome to Day 6 of #30DaysOfServerless!

    Today, we have a special set of posts from our Zero To Hero 🚀 initiative, featuring blog posts authored by our Product Engineering teams for #ServerlessSeptember. Posts were originally published on the Apps on Azure blog on Microsoft Tech Community.


    What We'll Cover

    • What are Durable Entities
    • Some Background
    • A Programming Model
    • Entities for a Micro-Blogging Platform


    Durable Entities are a special type of Azure Function that allow you to implement stateful objects in a serverless environment. They make it easy to introduce stateful components to your app without needing to manually persist data to external storage, so you can focus on your business logic. We’ll demonstrate their power with a real-life example in the last section.

    Entities 101: Some Background

    Programming Durable Entities feels a lot like object-oriented programming, except that these “objects” exist in a distributed system. Like objects, each Entity instance has a unique identifier, i.e. an entity ID that can be used to read and manipulate their internal state. Entities define a list of operations that constrain how their internal state is managed, like an object interface.

    Some experienced readers may realize that Entities sound a lot like an implementation of the Actor Pattern. For a discussion of the relationship between Entities and Actors, please refer to this documentation.

    Entities are a part of the Durable Functions Extension, an extension of Azure Functions that empowers programmers with stateful abstractions for serverless, such as Orchestrations (i.e. workflows).

    Durable Functions is available in most Azure Functions runtime environments: .NET, Node.js, Python, PowerShell, and Java (preview). For this article, we’ll focus on the C# experience, but note that Entities are also available in Node.js and Python; their availability in other languages is underway.

    Entities 102: The programming model

    Imagine you want to implement a simple Entity that just counts things. Its interface allows you to get the current count, add to the current count, and to reset the count to zero.

    If you implement this in an object-oriented way, you’d probably define a class (say “Counter”) with a method to get the current count (say “Counter.Get”), another to add to the count (say “Counter.Add”), and another to reset the count (say “Counter.Reset”). Well, the implementation of an Entity in C# is not that different from this sketch:

    [JsonObject(MemberSerialization.OptIn)] 
    public class Counter
    {
    [JsonProperty("value")]
    public int Value { get; set; }

    public void Add(int amount)
    {
    this.Value += amount;
    }

    public Task Reset()
    {
    this.Value = 0;
    return Task.CompletedTask;
    }

    public Task<int> Get()
    {
    return Task.FromResult(this.Value);
    }
    [FunctionName(nameof(Counter))]
    public static Task Run([EntityTrigger] IDurableEntityContext ctx)
    => ctx.DispatchAsync<Counter>();

    }

    We’ve defined a class named Counter, with an internal count stored in the variable “Value” which is manipulated through the “Add” and “Reset” methods, and which can be read via “Get”.

    The “Run” method is simply boilerplate required for the Azure Functions framework to interact with the object we’ve defined – it’s the method that the framework calls internally when it needs to load the Entity object. When DispatchAsync is called, the Entity and its corresponded state (the last count in “Value”) is loaded from storage. Again, this is mostly just boilerplate: your Entity’s business logic lies in the rest of the class.

    Finally, the Json annotation on top of the class and the Value field tells the Durable Functions framework that the “Value” field is to be durably persisted as part of the durable state on each Entity invocation. If you were to annotate other class variables with JsonProperty, they would also become part of the managed state.

    Entities for a micro-blogging platform

    We’ll try to implement a simple micro-blogging platform, a la Twitter. Let’s call it “Chirper”. In Chirper, users write chirps (i.e tweets), they can follow, and unfollow other users, and they can read the chirps of users they follow.

    Defining Entity

    Just like in OOP, it’s useful to begin by identifying what are the stateful agents of this scenario. In this case, users have state (who they follow and their chirps), and chirps have state in the form of their content. So, we could model these stateful agents as Entities!

    Below is a potential way to implement a User for Chirper as an Entity:

      [JsonObject(MemberSerialization = MemberSerialization.OptIn)] 
    public class User: IUser
    {
    [JsonProperty]
    public List<string> FollowedUsers { get; set; } = new List<string>();

    public void Add(string user)
    {
    FollowedUsers.Add(user);
    }

    public void Remove(string user)
    {
    FollowedUsers.Remove(user);
    }

    public Task<List<string>> Get()
    {
    return Task.FromResult(FollowedUsers);
    }
    // note: removed boilerplate “Run” method, for conciseness.
    }

    In this case, our Entity’s internal state is stored in “FollowedUsers” which is an array of accounts followed by this user. The operations exposed by this entity allow clients to read and modify this data: it can be read by “Get”, a new follower can be added via “Add”, and a user can be unfollowed via “Remove”.

    With that, we’ve modeled a Chirper’s user as an Entity! Recall that Entity instances each has a unique ID, so we can consider that unique ID to correspond to a specific user account.

    What about chirps? Should we represent them as Entities as well? That would certainly be valid. However, we would then need to create a mapping between an entity ID and every chirp entity ID that this user wrote.

    For demonstration purposes, a simpler approach would be to create an Entity that stores the list of all chirps authored by a given user; call it UserChirps. Then, we could fix each User Entity to share the same entity ID as its corresponding UserChirps Entity, making client operations easier.

    Below is a simple implementation of UserChirps:

      [JsonObject(MemberSerialization = MemberSerialization.OptIn)] 
    public class UserChirps : IUserChirps
    {
    [JsonProperty]
    public List<Chirp> Chirps { get; set; } = new List<Chirp>();

    public void Add(Chirp chirp)
    {
    Chirps.Add(chirp);
    }

    public void Remove(DateTime timestamp)
    {
    Chirps.RemoveAll(chirp => chirp.Timestamp == timestamp);
    }

    public Task<List<Chirp>> Get()
    {
    return Task.FromResult(Chirps);
    }

    // Omitted boilerplate “Run” function
    }

    Here, our state is stored in Chirps, a list of user posts. Our operations are the same as before: Get, Read, and Add. It’s the same pattern as before, but we’re representing different data.

    To put it all together, let’s set up Entity clients to generate and manipulate these Entities according to some REST API.

    Interacting with Entity

    Before going there, let’s talk briefly about how you can interact with an Entity. Entity interactions take one of two forms -- calls and signals:

    Calling an entity is a two-way communication. You send an operation message to the entity and then wait for the response message before you continue. The response can be a result value or an error. Signaling an entity is a one-way (fire-and-forget) communication. You send an operation message but don’t wait for a response. You have the reassurance that the message will be delivered eventually, but you don’t know when and don’t know what the response is. For example, when you read the state of an Entity, you are performing a “call” interaction. When you record that a user has followed another, you may choose to simply signal it.

    Now say user with a given userId (say “durableFan99” ) wants to post a chirp. For this, you can write an HTTP endpoint to signal the UserChips entity to record that chirp. We can leverage the HTTP Trigger functionality from Azure Functions and pair it with an entity client binding that signals the Add operation of our Chirp Entity:

    [FunctionName("UserChirpsPost")] 
    public static async Task<HttpResponseMessage> UserChirpsPost(
    [HttpTrigger(AuthorizationLevel.Function, "post", Route = "user/{userId}/chirps")]
    HttpRequestMessage req,
    DurableClient] IDurableClient client,
    ILogger log,
    string userId)
    {
    Authenticate(req, userId);
    var chirp = new Chirp()
    {
    UserId = userId,
    Timestamp = DateTime.UtcNow,
    Content = await req.Content.ReadAsStringAsync(),
    };
    await client.SignalEntityAsync<IUserChirps>(userId, x => x.Add(chirp));
    return req.CreateResponse(HttpStatusCode.Accepted, chirp);
    }

    Following the same pattern as above, to get all the chirps from a user, you could read the status of your Entity via ReadEntityStateAsync, which follows the call-interaction pattern as your client expects a response:

    [FunctionName("UserChirpsGet")] 
    public static async Task<HttpResponseMessage> UserChirpsGet(
    [HttpTrigger(AuthorizationLevel.Function, "get", Route = "user/{userId}/chirps")] HttpRequestMessage req,
    [DurableClient] IDurableClient client,
    ILogger log,
    string userId)
    {

    Authenticate(req, userId);
    var target = new EntityId(nameof(UserChirps), userId);
    var chirps = await client.ReadEntityStateAsync<UserChirps>(target);
    return chirps.EntityExists
    ? req.CreateResponse(HttpStatusCode.OK, chirps.EntityState.Chirps)
    : req.CreateResponse(HttpStatusCode.NotFound);
    }

    And there you have it! To play with a complete implementation of Chirper, you can try out our sample in the Durable Functions extension repo.

    Thank you!

    info

    Thanks for following along, and we hope you find Entities as useful as we do! If you have questions or feedback, please file issues in the repo above or tag us @AzureFunctions on Twitter

    - - + + \ No newline at end of file diff --git a/blog/page/27/index.html b/blog/page/27/index.html index fa86759881..9add0f1ef1 100644 --- a/blog/page/27/index.html +++ b/blog/page/27/index.html @@ -14,13 +14,13 @@ - - + +

    · 8 min read
    Kendall Roden

    Welcome to Day 6 of #30DaysOfServerless!

    Today, we have a special set of posts from our Zero To Hero 🚀 initiative, featuring blog posts authored by our Product Engineering teams for #ServerlessSeptember. Posts were originally published on the Apps on Azure blog on Microsoft Tech Community.


    What We'll Cover

    • Defining Cloud-Native
    • Introduction to Azure Container Apps
    • Dapr In Azure Container Apps
    • Conclusion


    Defining Cloud-Native

    While I’m positive I’m not the first person to ask this, I think it’s an appropriate way for us to kick off this article: “How many developers does it take to define Cloud-Native?” I hope you aren’t waiting for a punch line because I seriously want to know your thoughts (drop your perspectives in the comments..) but if you ask me, the limit does not exist!

    A quick online search of the topic returns a laundry list of articles, e-books, twitter threads, etc. all trying to nail down the one true definition. While diving into the rabbit hole of Cloud-Native, you will inevitably find yourself on the Cloud-Native Computing Foundation (CNCF) site. The CNCF is part of the Linux Foundation and aims to make "Cloud-Native computing ubiquitous" through deep open source project and community involvement. The CNCF has also published arguably the most popularized definition of Cloud-Native which begins with the following statement:

    “Cloud-Native technologies empower organizations to build and run scalable applications in modern, dynamic environments such as public, private, and hybrid clouds."

    Over the past four years, my day-to-day work has been driven primarily by the surging demand for application containerization and the drastic adoption of Kubernetes as the de-facto container orchestrator. Customers are eager to learn and leverage patterns, practices and technologies that enable building "loosely coupled systems that are resilient, manageable, and observable". Enterprise developers at these organizations are being tasked with rapidly deploying event-driven, horizontally-scalable, polyglot services via repeatable, code-to-cloud pipelines.

    While building Cloud-Native solutions can enable rapid innovation, the transition to adopting a Cloud-Native architectural approach comes with a steep learning curve and a new set of considerations. In a document published by Microsoft called What is Cloud-Native?, there are a few key areas highlighted to aid customers in the adoption of best practices for building modern, portable applications which I will summarize below:

    Cloud infrastructure

    • Cloud-Native applications leverage cloud infrastructure and make use of Platform-as-a-service offerings
    • Cloud-Native applications depend on highly-elastic infrastructure with automatic scaling, self-healing, and monitoring capabilities

    Modern application design

    • Cloud-Native applications should be constructed using principles outlined in the 12 factor methodology

    Microservices

    • Cloud-Native applications are typically composed of microservices where each core function, or service, is built and deployed independently

    Containers

    • Cloud-Native applications are typically deployed using containers as a packaging mechanism where an application's code and dependencies are bundled together for consistency of deployment
    • Cloud-Native applications leverage container orchestration technologies- primarily Kubernetes- for achieving capabilities such as workload scheduling, self-healing, auto-scale, etc.

    Backing services

    • Cloud-Native applications are ideally stateless workloads which retrieve and store data in data stores external to the application hosting infrastructure. Cloud providers like Azure provide an array of backing data services which can be securely accessed from application code and provide capabilities for ensuring application data is highly-available

    Automation

    • Cloud-Native solutions should use deployment automation for backing cloud infrastructure via versioned, parameterized Infrastructure as Code (IaC) templates which provide a consistent, repeatable process for provisioning cloud resources.
    • Cloud-Native solutions should make use of modern CI/CD practices and pipelines to ensure successful, reliable infrastructure and application deployment.

    Azure Container Apps

    In many of the conversations I've had with customers that involve talk of Kubernetes and containers, the topics of cost-optimization, security, networking, and reducing infrastructure and operations inevitably arise. I personally have yet to meet with any customers eager to have their developers get more involved with infrastructure concerns.

    One of my former colleagues, Jeff Hollan, made a statement while appearing on a 2019 episode of The Cloud-Native Show where he shared his perspective on Cloud-Native:

    "When I think about Cloud-Native... it's writing applications in a way where you are specifically thinking about the benefits the cloud can provide... to me, serverless is the perfect realization of that because the only reason you can write serverless applications is because the cloud exists."

    I must say that I agree with Jeff's perspective. In addition to optimizing development practices for the Cloud-Native world, reducing infrastructure exposure and operations is equally as important to many organizations and can be achieved as a result of cloud platform innovation.

    In May of 2022, Microsoft announced the general availability of Azure Container Apps. Azure Container Apps provides customers with the ability to run microservices and containerized applications on a serverless, consumption-based platform.

    For those interested in taking advantage of the open source ecosystem while reaping the benefits of a managed platform experience, Container Apps run on Kubernetes and provides a set of managed open source projects embedded directly into the platform including the Kubernetes Event Driven Autoscaler (KEDA), the Distributed Application Runtime (Dapr) and Envoy.

    Azure Kubernetes Service vs. Azure Container Apps

    Container apps provides other Cloud-Native features and capabilities in addition to those above including, but not limited to:

    The ability to dynamically scale and support growing numbers of users, events, and requests is one of the core requirements for most Cloud-Native, distributed applications. Azure Container Apps is purpose-built with this and other Cloud-Native tenants in mind.

    What can you build with Azure Container Apps?

    Dapr in Azure Container Apps

    As a quick personal note before we dive into this section I will say I am a bit bias about Dapr. When Dapr was first released, I had an opportunity to immediately get involved and became an early advocate for the project. It is created by developers for developers, and solves tangible problems customers architecting distributed systems face:

    HOW DO I
    • integrate with external systems that my app has to react and respond to?
    • create event driven apps which reliably send events from one service to another?
    • observe the calls and events between my services to diagnose issues in production?
    • access secrets securely from within my application?
    • discover other services and call methods on them?
    • prevent committing to a technology early and have the flexibility to swap out an alternative based on project or environment changes?

    While existing solutions were in the market which could be used to address some of the concerns above, there was not a lightweight, CNCF-backed project which could provide a unified approach to solve the more fundamental ask from customers: "How do I make it easy for developers to build microservices based on Cloud-Native best practices?"

    Enter Dapr!

    The Distributed Application Runtime (Dapr) provides APIs that simplify microservice connectivity. Whether your communication pattern is service to service invocation or pub/sub messaging, Dapr helps you write resilient and secured microservices. By letting Dapr’s sidecar take care of the complex challenges such as service discovery, message broker integration, encryption, observability, and secret management, you can focus on business logic and keep your code simple."

    The Container Apps platform provides a managed and supported Dapr integration which eliminates the need for deploying and managing the Dapr OSS project. In addition to providing managed upgrades, the platform also exposes a simplified Dapr interaction model to increase developer productivity and reduce the friction required to leverage Dapr capabilities. While the Dapr integration makes it easier for customers to adopt Cloud-Native best practices in container apps it is not required to make use of the container apps platform.

    Image on Dapr

    For additional insight into the dapr integration visit aka.ms/aca-dapr.

    Conclusion

    Backed by and integrated with powerful Cloud-Native technologies, Azure Container Apps strives to make developers productive, while reducing the operational overhead and learning curve that typically accompanies adopting a cloud-native strategy.

    If you are interested in building resilient, portable and highly-scalable apps visit Azure Container Apps | Microsoft Azure today!

    ASK THE EXPERT: LIVE Q&A

    The Azure Container Apps team will answer questions live on September 29.

    - - + + \ No newline at end of file diff --git a/blog/page/28/index.html b/blog/page/28/index.html index e751de8a0b..b6b21fa16e 100644 --- a/blog/page/28/index.html +++ b/blog/page/28/index.html @@ -14,13 +14,13 @@ - - + +

    · 7 min read
    Aaron Powell

    Welcome to Day 5 of #30DaysOfServerless!

    Yesterday we looked at Azure Functions from the perspective of a Java developer. Today, we'll do a similar walkthrough from the perspective of a JavaScript developer.

    And, we'll use this to explore another popular usage scenario for Azure Functions: building a serverless HTTP API using JavaScript.

    Ready? Let's go.


    What We'll Cover

    • Developer Guidance
    • Create Azure Function with CLI
    • Calling an external API
    • Azure Samples & Scenarios for JS
    • Exercise: Support searching
    • Resources: For self-study!


    Developer Guidance

    If you're a JavaScript developer new to serverless on Azure, start by exploring the Azure Functions JavaScript Developers Guide. It covers:

    • Quickstarts for Node.js - using Visual Code, CLI or Azure Portal
    • Guidance on hosting options and performance considerations
    • Azure Functions bindings and (code samples) for JavaScript
    • Scenario examples - integrations with other Azure Services

    Node.js 18 Support

    Node.js 18 Support (Public Preview)

    Azure Functions support for Node.js 18 entered Public Preview on Aug 31, 2022 and is supported by the Azure Functions v.4.x runtime!

    As we continue to explore how we can use Azure Functions, today we're going to look at using JavaScript to create one, and we're going to be using the newly released Node.js 18 support for Azure Functions to make the most out of the platform.

    Ensure you have Node.js 18 and Azure Functions v4.x versions installed, along with a text editor (I'll use VS Code in this post), and a terminal, then we're ready to go.

    Scenario: Calling The GitHub API

    The application we're going to be building today will use the GitHub API to return a random commit message, so that we don't need to come up with one ourselves! After all, naming things can be really hard! 🤣

    Creating the Azure Function

    To create our Azure Function, we're going to use the Azure Functions CLI, which we can install using npm:

    npm install --global azure-function-core-tools

    Once that's installed, we can use the new func command to initalise our project:

    func init --worker-runtime node --language javascript

    When running func init we can either provide the worker-runtime and language as arguments, or use the menu system that the tool will provide us. For brevity's stake, I've used the arguments here, specifying that we want node as the runtime and javascript as the language, but you could change that to typescript if you'd prefer to use TypeScript.

    Once the init command is completed, you should have a .vscode folder, and the files .gitignore, host.json, local.settings.json, and package.json.

    Files generated by func initFiles generated by func init

    Adding a HTTP Trigger

    We have an empty Functions app so far, what we need to do next is create a Function that it will run, and we're going to make a HTTP Trigger Function, which is a Function that responds to HTTP requests. We'll use the func new command to create that:

    func new --template "HTTP Trigger" --name "get-commit-message"

    When this completes, we'll have a folder for the Function, using the name we provided, that contains the filesfunction.json and index.js. Let's open the function.json to understand it a little bit:

    {
    "bindings": [
    {
    "authLevel": "function",
    "type": "httpTrigger",
    "direction": "in",
    "name": "req",
    "methods": [
    "get",
    "post"
    ]
    },
    {
    "type": "http",
    "direction": "out",
    "name": "res"
    }
    ]
    }

    This file is used to tell Functions about the Function that we've created and what it does, so it knows to handle the appropriate events. We have a bindings node which contains the event bindings for our Azure Function. The first binding is using the type httpTrigger, which indicates that it'll be executed, or triggered, by a HTTP event, and the methods indicates that it's listening to both GET and POST (you can change this for the right HTTP methods that you want to support). The HTTP request information will be bound to a property in the Functions context called req, so we can access query strings, the request body, etc.

    The other binding we have has the direction of out, meaning that it's something that the Function will return to the called, and since this is a HTTP API, the type is http, indicating that we'll return a HTTP response, and that response will be on a property called res that we add to the Functions context.

    Let's go ahead and start the Function and call it:

    func start

    Starting the FunctionStarting the Function

    With the Function started, access the endpoint http://localhost:7071/api/get-commit-message via a browser or using cURL:

    curl http://localhost:7071/api/get-commit-message\?name\=ServerlessSeptember

    Hello from Azure FunctionsHello from Azure Functions

    🎉 CONGRATULATIONS

    You created and ran a JavaScript function app locally!

    Calling an external API

    It's time to update the Function to do what we want to do - call the GitHub Search API and get some commit messages. The endpoint that we'll be calling is https://api.github.com/search/commits?q=language:javascript.

    Note: The GitHub API is rate limited and this sample will call it unauthenticated, so be aware of that in your own testing.

    To call this API, we'll leverage the newly released fetch support in Node 18 and async/await, to make for a very clean Function.

    Open up the index.js file, and delete the contents of the existing Function, so we have a empty one:

    module.exports = async function (context, req) {

    }

    The default template uses CommonJS, but you can use ES Modules with Azure Functions if you prefer.

    Now we'll use fetch to call the API, and unpack the JSON response:

    module.exports = async function (context, req) {
    const res = await fetch("https://api.github.com/search/commits?q=language:javascript");
    const json = await res.json();
    const messages = json.items.map(item => item.commit.message);
    context.res = {
    body: {
    messages
    }
    };
    }

    To send a response to the client, we're setting the context.res property, where res is the name of the output binding in our function.json, and giving it a body that contains the commit messages.

    Run func start again, and call the endpoint:

    curl http://localhost:7071/api/get-commit-message

    The you'll get some commit messages:

    A series of commit messages from the GitHub Search APIA series of commit messages from the GitHub Search API

    🎉 CONGRATULATIONS

    There we go, we've created an Azure Function which is used as a proxy to another API, that we call (using native fetch in Node.js 18) and from which we return a subset of the JSON payload.

    Next Steps

    Other Triggers, Bindings

    This article focused on using the HTTPTrigger and relevant bindings, to build a serverless API using Azure Functions. How can you explore other supported bindings, with code samples to illustrate usage?

    Scenarios with Integrations

    Once you've tried out the samples, try building an end-to-end scenario by using these triggers to integrate seamlessly with other services. Here are some suggestions:

    Exercise: Support searching

    The GitHub Search API allows you to provide search parameters via the q query string. In this sample, we hard-coded it to be language:javascript, but as a follow-on exercise, expand the Function to allow the caller to provide the search terms as a query string to the Azure Function, which is passed to the GitHub Search API. Hint - have a look at the req argument.

    Resources

    - - + + \ No newline at end of file diff --git a/blog/page/29/index.html b/blog/page/29/index.html index 09fd2bb736..5fb20a89a9 100644 --- a/blog/page/29/index.html +++ b/blog/page/29/index.html @@ -14,13 +14,13 @@ - - + +

    · 8 min read
    Rory Preddy

    Welcome to Day 4 of #30DaysOfServerless!

    Yesterday we walked through an Azure Functions Quickstart with JavaScript, and used it to understand the general Functions App structure, tooling and developer experience.

    Today we'll look at developing Functions app with a different programming language - namely, Java - and explore developer guidance, tools and resources to build serverless Java solutions on Azure.


    What We'll Cover


    Developer Guidance

    If you're a Java developer new to serverless on Azure, start by exploring the Azure Functions Java Developer Guide. It covers:

    In this blog post, we'll dive into one quickstart, and discuss other resources briefly, for awareness! Do check out the recommended exercises and resources for self-study!


    My First Java Functions App

    In today's post, we'll walk through the Quickstart: Azure Functions tutorial using Visual Studio Code. In the process, we'll setup our development environment with the relevant command-line tools and VS Code extensions to make building Functions app simpler.

    Note: Completing this exercise may incur a a cost of a few USD cents based on your Azure subscription. Explore pricing details to learn more.

    First, make sure you have your development environment setup and configured.

    PRE-REQUISITES
    1. An Azure account with an active subscription - Create an account for free
    2. The Java Development Kit, version 11 or 8. - Install
    3. Apache Maven, version 3.0 or above. - Install
    4. Visual Studio Code. - Install
    5. The Java extension pack - Install
    6. The Azure Functions extension for Visual Studio Code - Install

    VS Code Setup

    NEW TO VISUAL STUDIO CODE?

    Start with the Java in Visual Studio Code tutorial to jumpstart your learning!

    Install the Extension Pack for Java (shown below) to install 6 popular extensions to help development workflow from creation to testing, debugging, and deployment.

    Extension Pack for Java

    Now, it's time to get started on our first Java-based Functions app.

    1. Create App

    1. Open a command-line terminal and create a folder for your project. Use the code command to launch Visual Studio Code from that directory as shown:

      $ mkdir java-function-resource-group-api
      $ cd java-function-resource-group-api
      $ code .
    2. Open the Visual Studio Command Palette (Ctrl + Shift + p) and select Azure Functions: create new project to kickstart the create workflow. Alternatively, you can click the Azure icon (on activity sidebar), to get the Workspace window, click "+" and pick the "Create Function" option as shown below.

      Screenshot of creating function in Azure from Visual Studio Code.

    3. This triggers a multi-step workflow. Fill in the information for each step as shown in the following prompts. Important: Start this process from an empty folder - the workflow will populate it with the scaffold for your Java-based Functions app.

      PromptValue
      Choose the directory location.You should either create a new folder or choose an empty folder for the project workspace. Don't choose a project folder that is already part of a workspace.
      Select a languageChoose Java.
      Select a version of JavaChoose Java 11 or Java 8, the Java version on which your functions run in Azure. Choose a Java version that you've verified locally.
      Provide a group IDChoose com.function.
      Provide an artifact IDEnter myFunction.
      Provide a versionChoose 1.0-SNAPSHOT.
      Provide a package nameChoose com.function.
      Provide an app nameEnter HttpExample.
      Select the build tool for Java projectChoose Maven.

    Visual Studio Code uses the provided information and generates an Azure Functions project. You can view the local project files in the Explorer - it should look like this:

    Azure Functions Scaffold For Java

    2. Preview App

    Visual Studio Code integrates with the Azure Functions Core tools to let you run this project on your local development computer before you publish to Azure.

    1. To build and run the application, use the following Maven command. You should see output similar to that shown below.

      $ mvn clean package azure-functions:run
      ..
      ..
      Now listening on: http://0.0.0.0:7071
      Application started. Press Ctrl+C to shut down.

      Http Functions:

      HttpExample: [GET,POST] http://localhost:7071/api/HttpExample
      ...
    2. Copy the URL of your HttpExample function from this output to a browser and append the query string ?name=<YOUR_NAME>, making the full URL something like http://localhost:7071/api/HttpExample?name=Functions. The browser should display a message that echoes back your query string value. The terminal in which you started your project also shows log output as you make requests.

    🎉 CONGRATULATIONS

    You created and ran a function app locally!

    With the Terminal panel focused, press Ctrl + C to stop Core Tools and disconnect the debugger. After you've verified that the function runs correctly on your local computer, it's time to use Visual Studio Code and Maven to publish and test the project on Azure.

    3. Sign into Azure

    Before you can deploy, sign in to your Azure subscription.

    az login

    The az login command signs you into your Azure account.

    Use the following command to deploy your project to a new function app.

    mvn clean package azure-functions:deploy

    When the creation is complete, the following Azure resources are created in your subscription:

    • Resource group. Named as java-functions-group.
    • Storage account. Required by Functions. The name is generated randomly based on Storage account name requirements.
    • Hosting plan. Serverless hosting for your function app.The name is java-functions-app-service-plan.
    • Function app. A function app is the deployment and execution unit for your functions. The name is randomly generated based on your artifactId, appended with a randomly generated number.

    4. Deploy App

    1. Back in the Resources area in the side bar, expand your subscription, your new function app, and Functions. Right-click (Windows) or Ctrl - click (macOS) the HttpExample function and choose Execute Function Now....

      Screenshot of executing function in Azure from Visual Studio Code.

    2. In Enter request body you see the request message body value of { "name": "Azure" }. Press Enter to send this request message to your function.

    3. When the function executes in Azure and returns a response, a notification is raised in Visual Studio Code.

    You can also copy the complete Invoke URL shown in the output of the publish command into a browser address bar, appending the query parameter ?name=Functions. The browser should display similar output as when you ran the function locally.

    🎉 CONGRATULATIONS

    You deployed your function app to Azure, and invoked it!

    5. Clean up

    Use the following command to delete the resource group and all its contained resources to avoid incurring further costs.

    az group delete --name java-functions-group

    Next Steps

    So, where can you go from here? The example above used a familiar HTTP Trigger scenario with a single Azure service (Azure Functions). Now, think about how you can build richer workflows by using other triggers and integrating with other Azure or third-party services.

    Other Triggers, Bindings

    Check out Azure Functions Samples In Java for samples (and short use cases) that highlight other triggers - with code! This includes triggers to integrate with CosmosDB, Blob Storage, Event Grid, Event Hub, Kafka and more.

    Scenario with Integrations

    Once you've tried out the samples, try building an end-to-end scenario by using these triggers to integrate seamlessly with other Services. Here are a couple of useful tutorials:

    Exercise

    Time to put this into action and validate your development workflow:

    Resources

    - - + + \ No newline at end of file diff --git a/blog/page/3/index.html b/blog/page/3/index.html index c6475f7bd7..747b192b78 100644 --- a/blog/page/3/index.html +++ b/blog/page/3/index.html @@ -14,8 +14,8 @@ - - + +
    @@ -26,7 +26,7 @@

    ...and that's it! We've successfully deployed our application on Azure!

    But there's more!

    Best practices: Monitoring and CI/CD!

    In my opinion, it's not enough to just set up the application on Azure! I want to know that my web app is performant and serving my users reliably! I also want to make sure that I'm not inadvertently breaking my application as I continue to make changes to it. Thankfully, the Azure Developer CLI also handles all of this via two additional commands - azd monitor and azd pipeline config.

    Application Monitoring

    When we provisioned all of our infrastructure, we also set up application monitoring via a Bicep file in our .infra/ directory that spec'd out an Application Insights dashboard. By running azd monitor we can see the dashboard with live metrics that was configured for the application.

    We can also navigate to the Application Dashboard by clicking on the resource group name, where you can set a specific refresh rate for the dashboard, and see usage, reliability, and performance metrics over time.

    I don't know about everyone else but I have spent a ton of time building out similar dashboards. It can be super time-consuming to write all the queries and create the visualizations so this feels like a real time saver.

    CI/CD

    Finally let's talk about setting up CI/CD! This might be my favorite azd feature. As I mentioned before, the Azure Developer CLI has a command, azd pipeline config, which uses the files in the .github/ directory to set up a GitHub Action. More than that, if there is no upstream repo, the Developer CLI will actually help you create one. But what does this mean exactly? Because our GitHub Action is using the same commands you'd run in the CLI under the hood, we're actually going to have CI/CD set up to run on every commit into the repo, against real Azure resources. What a sweet collaboration feature!

    That's it! We've gone end-to-end with the Azure Developer CLI - initialized a project, provisioned the resources on Azure, deployed our code on Azure, set up monitoring logs and dashboards, and set up a CI/CD pipeline with GitHub Actions to run on every commit into the repo (on real Azure resources!).

    Exercise: Try it yourself or create your own template!

    As an exercise, try out the workflow above with any template on GitHub!

    Or, try turning your own project into an Azure Developer CLI-enabled template by following this guidance. If you create your own template, don't forget to tag the repo with the azd-templates topic on GitHub to help others find it (unfamiliar with GitHub topics? Learn how to add topics to your repo)! We'd also love to chat with you about your experience creating an azd template - if you're open to providing feedback around this, please fill out this form!

    Resources

    - - + + \ No newline at end of file diff --git a/blog/page/30/index.html b/blog/page/30/index.html index 827f629164..6fbaa8ae3c 100644 --- a/blog/page/30/index.html +++ b/blog/page/30/index.html @@ -14,13 +14,13 @@ - - + +

    · 9 min read
    Nitya Narasimhan

    Welcome to Day 3 of #30DaysOfServerless!

    Yesterday we learned core concepts and terminology for Azure Functions, the signature Functions-as-a-Service option on Azure. Today we take our first steps into building and deploying an Azure Functions app, and validate local development setup.

    Ready? Let's go.


    What We'll Cover


    Developer Guidance

    Before we jump into development, let's familiarize ourselves with language-specific guidance from the Azure Functions Developer Guide. We'll review the JavaScript version but guides for F#, Java, Python, C# and PowerShell are also available.

    1. A function is defined by two things: code (written in a supported programming language) and configuration (specified in a functions.json file, declaring the triggers, bindings and other context for execution).

    2. A function app is the unit of deployment for your functions, and is associated with a single execution context or runtime. It can contain multiple functions, but they must be in the same language.

    3. A host configuration is runtime-specific configuration that affects all functions running in a given function app instance. It is defined in a host.json file.

    4. A recommended folder structure is defined for the function app, but may vary based on the programming language used. Check the documentation on folder structures to learn the default for your preferred language.

    Here's an example of the JavaScript folder structure for a function app containing two functions with some shared dependencies. Note that host.json (runtime configuration) is defined once, in the root directory. And function.json is defined separately for each function.

    FunctionsProject
    | - MyFirstFunction
    | | - index.js
    | | - function.json
    | - MySecondFunction
    | | - index.js
    | | - function.json
    | - SharedCode
    | | - myFirstHelperFunction.js
    | | - mySecondHelperFunction.js
    | - node_modules
    | - host.json
    | - package.json
    | - local.settings.json

    We'll dive into what the contents of these files look like, when we build and deploy the first function. We'll cover local.settings.json in the About Local Testing section at the end.


    My First Function App

    The documentation provides quickstart options for all supported languages. We'll walk through the JavaScript versions in this article. You have two options for development:

    I'm a huge fan of VS Code - so I'll be working through that tutorial today.

    PRE-REQUISITES

    Don't forget to validate your setup by checking the versions of installed software.

    Install VSCode Extension

    Installing the Visual Studio Code extension should automatically open this page in your IDE with similar quickstart instructions, but potentially more recent screenshots.

    Visual Studio Code Extension for VS Code

    Note that it may make sense to install the Azure tools for Visual Studio Code extensions pack if you plan on working through the many projects in Serverless September. This includes the Azure Functions extension by default.

    Create First Function App

    Walk through the Create local [project] steps of the quickstart. The process is quick and painless and scaffolds out this folder structure and files. Note the existence (and locations) of functions.json and host.json files.

    Final screenshot for VS Code workflow

    Explore the Code

    Check out the functions.json configuration file. It shows that the function is activated by an httpTrigger with an input binding (tied to req payload) and an output binding (tied to res payload). And it supports both GET and POST requests on the exposed URL.

    {
    "bindings": [
    {
    "authLevel": "anonymous",
    "type": "httpTrigger",
    "direction": "in",
    "name": "req",
    "methods": [
    "get",
    "post"
    ]
    },
    {
    "type": "http",
    "direction": "out",
    "name": "res"
    }
    ]
    }

    Check out index.js - the function implementation. We see it logs a message to the console when invoked. It then extracts a name value from the input payload (req) and crafts a different responseMessage based on the presence/absence of a valid name. It returns this response in the output payload (res).

    module.exports = async function (context, req) {
    context.log('JavaScript HTTP trigger function processed a request.');

    const name = (req.query.name || (req.body && req.body.name));
    const responseMessage = name
    ? "Hello, " + name + ". This HTTP triggered function executed successfully."
    : "This HTTP triggered function executed successfully. Pass a name in the query string or in the request body for a personalized response.";

    context.res = {
    // status: 200, /* Defaults to 200 */
    body: responseMessage
    };
    }

    Preview Function App Locally

    You can now run this function app locally using Azure Functions Core Tools. VS Code integrates seamlessly with this CLI-based tool, making it possible for you to exploit all its capabilities without leaving the IDE. In fact, the workflow will even prompt you to install those tools if they didn't already exist in your local dev environment.

    Now run the function app locally by clicking on the "Run and Debug" icon in the activity bar (highlighted, left) and pressing the "▶️" (Attach to Node Functions) to start execution. On success, your console output should show something like this.

    Final screenshot for VS Code workflow

    You can test the function locally by visiting the Function Url shown (http://localhost:7071/api/HttpTrigger1) or by opening the Workspace region of the Azure extension, and selecting the Execute Function now menu item as shown.

    Final screenshot for VS Code workflow

    In the latter case, the Enter request body popup will show a pre-populated request of {"name":"Azure"} that you can submit.

    Final screenshot for VS Code workflow

    On successful execution, your VS Code window will show a notification as follows. Take note of the console output - it shows the message encoded in index.js.

    Final screenshot for VS Code workflow

    You can also visit the deployed function URL directly in a local browser - testing the case for a request made with no name payload attached. Note how the response in the browser now shows the non-personalized version of the message!

    Final screenshot for VS Code workflow

    🎉 Congratulations

    You created and ran a function app locally!

    (Re)Deploy to Azure

    Now, just follow the creating a function app in Azure steps to deploy it to Azure, using an active subscription! The deployed app resource should now show up under the Function App Resources where you can click Execute Function Now to test the Azure-deployed version instead. You can also look up the function URL in the portal and visit that link in your local browser to trigger the function without the name context.

    🎉 Congratulations

    You have an Azure-hosted serverless function app!

    Challenge yourself and try to change the code and redeploy to Azure to return something different. You have effectively created a serverless API endpoint!


    About Core Tools

    That was a lot to cover! In the next few days we'll have more examples for Azure Functions app development - focused on different programming languages. So let's wrap today's post by reviewing two helpful resources.

    First, let's talk about Azure Functions Core Tools - the command-line tool that lets you develop, manage, and deploy, Azure Functions projects from your local development environment. It is used transparently by the VS Code extension - but you can use it directly from a terminal for a powerful command-line end-to-end developer experience! The Core Tools commands are organized into the following contexts:

    Learn how to work with Azure Functions Core Tools. Not only can it help with quick command execution, it can also be invaluable for debugging issues that may not always be visible or understandable in an IDE.

    About Local Testing

    You might have noticed that the scaffold also produced a local.settings.json file. What is that and why is it useful? By definition, the local.settings.json file "stores app settings and settings used by local development tools. Settings in the local.settings.json file are used only when you're running your project locally."

    Read the guidance on Code and test Azure Functions Locally to learn more about how to configure development environments locally, for your preferred programming language, to support testing and debugging on the local Functions runtime.

    Exercise

    We made it! Now it's your turn!! Here are a few things you can try to apply what you learned and reinforce your understanding:

    Resources

    Bookmark and visit the #30DaysOfServerless Collection. It's the one-stop collection of resources we will keep updated with links to relevant documentation and learning resources.

    - - + + \ No newline at end of file diff --git a/blog/page/31/index.html b/blog/page/31/index.html index b407220988..a63f7c6b3d 100644 --- a/blog/page/31/index.html +++ b/blog/page/31/index.html @@ -14,15 +14,15 @@ - - + +

    · 9 min read
    Nitya Narasimhan

    Welcome to Day 2️⃣ of #30DaysOfServerless!

    Today, we kickstart our journey into serveless on Azure with a look at Functions As a Service. We'll explore Azure Functions - from core concepts to usage patterns.

    Ready? Let's Go!


    What We'll Cover

    • What is Functions-as-a-Service? (FaaS)
    • What is Azure Functions?
    • Triggers, Bindings and Custom Handlers
    • What is Durable Functions?
    • Orchestrators, Entity Functions and Application Patterns
    • Exercise: Take the Cloud Skills Challenge!
    • Resources: #30DaysOfServerless Collection.


    1. What is FaaS?

    Faas stands for Functions As a Service (FaaS). But what does that mean for us as application developers? We know that building and deploying modern applications at scale can get complicated and it starts with us needing to take decisions on Compute. In other words, we need to answer this question: "where should I host my application given my resource dependencies and scaling requirements?"

    this useful flowchart

    Azure has this useful flowchart (shown below) to guide your decision-making. You'll see that hosting options generally fall into three categories:

    • Infrastructure as a Service (IaaS) - where you provision and manage Virtual Machines yourself (cloud provider manages infra).
    • Platform as a Service (PaaS) - where you use a provider-managed hosting environment like Azure Container Apps.
    • Functions as a Service (FaaS) - where you forget about hosting environments and simply deploy your code for the provider to run.

    Here, "serverless" compute refers to hosting options where we (as developers) can focus on building apps without having to manage the infrastructure. See serverless compute options on Azure for more information.


    2. Azure Functions

    Azure Functions is the Functions-as-a-Service (FaaS) option on Azure. It is the ideal serverless solution if your application is event-driven with short-lived workloads. With Azure Functions, we develop applications as modular blocks of code (functions) that are executed on demand, in response to configured events (triggers). This approach brings us two advantages:

    • It saves us money. We only pay for the time the function runs.
    • It scales with demand. We have 3 hosting plans for flexible scaling behaviors.

    Azure Functions can be programmed in many popular languages (C#, F#, Java, JavaScript, TypeScript, PowerShell or Python), with Azure providing language-specific handlers and default runtimes to execute them.

    Concept: Custom Handlers
    • What if we wanted to program in a non-supported language?
    • Or we wanted to use a different runtime for a supported language?

    Custom Handlers have you covered! These are lightweight webservers that can receive and process input events from the Functions host - and return responses that can be delivered to any output targets. By this definition, custom handlers can be implemented by any language that supports receiving HTTP events. Check out the quickstart for writing a custom handler in Rust or Go.

    Custom Handlers

    Concept: Trigger and Bindings

    We talked about what functions are (code blocks). But when are they invoked or executed? And how do we provide inputs (arguments) and retrieve outputs (results) from this execution?

    This is where triggers and bindings come in.

    • Triggers define how a function is invoked and what associated data it will provide. A function must have exactly one trigger.
    • Bindings declaratively define how a resource is connected to the function. The resource or binding can be of type input, output, or both. Bindings are optional. A Function can have multiple input, output bindings.

    Azure Functions comes with a number of supported bindings that can be used to integrate relevant services to power a specific scenario. For instance:

    • HTTP Triggers - invokes the function in response to an HTTP request. Use this to implement serverless APIs for your application.
    • Event Grid Triggers invokes the function on receiving events from an Event Grid. Use this to process events reactively, and potentially publish responses back to custom Event Grid topics.
    • SignalR Service Trigger invokes the function in response to messages from Azure SignalR, allowing your application to take actions with real-time contexts.

    Triggers and bindings help you abstract your function's interfaces to other components it interacts with, eliminating hardcoded integrations. They are configured differently based on the programming language you use. For example - JavaScript functions are configured in the functions.json file. Here's an example of what that looks like.

    {
    "disabled":false,
    "bindings":[
    // ... bindings here
    {
    "type": "bindingType",
    "direction": "in",
    "name": "myParamName",
    // ... more depending on binding
    }
    ]
    }

    The key thing to remember is that triggers and bindings have a direction property - triggers are always in, input bindings are in and output bindings are out. Some bindings can support a special inout direction.

    The documentation has code examples for bindings to popular Azure services. Here's an example of the bindings and trigger configuration for a BlobStorage use case.

    // function.json configuration

    {
    "bindings": [
    {
    "queueName": "myqueue-items",
    "connection": "MyStorageConnectionAppSetting",
    "name": "myQueueItem",
    "type": "queueTrigger",
    "direction": "in"
    },
    {
    "name": "myInputBlob",
    "type": "blob",
    "path": "samples-workitems/{queueTrigger}",
    "connection": "MyStorageConnectionAppSetting",
    "direction": "in"
    },
    {
    "name": "myOutputBlob",
    "type": "blob",
    "path": "samples-workitems/{queueTrigger}-Copy",
    "connection": "MyStorageConnectionAppSetting",
    "direction": "out"
    }
    ],
    "disabled": false
    }

    The code below shows the function implementation. In this scenario, the function is triggered by a queue message carrying an input payload with a blob name. In response, it copies that data to the resource associated with the output binding.

    // function implementation

    module.exports = async function(context) {
    context.log('Node.js Queue trigger function processed', context.bindings.myQueueItem);
    context.bindings.myOutputBlob = context.bindings.myInputBlob;
    };
    Concept: Custom Bindings

    What if we have a more complex scenario that requires bindings for non-supported resources?

    There is an option create custom bindings if necessary. We don't have time to dive into details here but definitely check out the documentation


    3. Durable Functions

    This sounds great, right?. But now, let's talk about one challenge for Azure Functions. In the use cases so far, the functions are stateless - they take inputs at runtime if necessary, and return output results if required. But they are otherwise self-contained, which is great for scalability!

    But what if I needed to build more complex workflows that need to store and transfer state, and complete operations in a reliable manner? Durable Functions are an extension of Azure Functions that makes stateful workflows possible.

    Concept: Orchestrator Functions

    How can I create workflows that coordinate functions?

    Durable Functions use orchestrator functions to coordinate execution of other Durable functions within a given Functions app. These functions are durable and reliable. Later in this post, we'll talk briefly about some application patterns that showcase popular orchestration scenarios.

    Concept: Entity Functions

    How do I persist and manage state across workflows?

    Entity Functions provide explicit state mangement for Durable Functions, defining operations to read and write state to durable entities. They are associated with a special entity trigger for invocation. These are currently available only for a subset of programming languages so check to see if they are supported for your programming language of choice.

    USAGE: Application Patterns

    Durable Functions are a fascinating topic that would require a separate, longer post, to do justice. For now, let's look at some application patterns that showcase the value of these starting with the simplest one - Function Chaining as shown below:

    Function Chaining

    Here, we want to execute a sequence of named functions in a specific order. As shown in the snippet below, the orchestrator function coordinates invocations on the given functions in the desired sequence - "chaining" inputs and outputs to establish the workflow. Take note of the yield keyword. This triggers a checkpoint, preserving the current state of the function for reliable operation.

    const df = require("durable-functions");

    module.exports = df.orchestrator(function*(context) {
    try {
    const x = yield context.df.callActivity("F1");
    const y = yield context.df.callActivity("F2", x);
    const z = yield context.df.callActivity("F3", y);
    return yield context.df.callActivity("F4", z);
    } catch (error) {
    // Error handling or compensation goes here.
    }
    });

    Other application patterns for durable functions include:

    There's a lot more to explore but we won't have time to do that today. Definitely check the documentation and take a minute to read the comparison with Azure Logic Apps to understand what each technology provides for serverless workflow automation.


    4. Exercise

    That was a lot of information to absorb! Thankfully, there are a lot of examples in the documentation that can help put these in context. Here are a couple of exercises you can do, to reinforce your understanding of these concepts.


    5. What's Next?

    The goal for today was to give you a quick tour of key terminology and concepts related to Azure Functions. Tomorrow, we dive into the developer experience, starting with core tools for local development and ending by deploying our first Functions app.

    Want to do some prep work? Here are a few useful links:


    6. Resources


    - - + + \ No newline at end of file diff --git a/blog/page/32/index.html b/blog/page/32/index.html index d0273786f1..bace32e9b9 100644 --- a/blog/page/32/index.html +++ b/blog/page/32/index.html @@ -14,13 +14,13 @@ - - + +

    · 5 min read
    Nitya Narasimhan
    Devanshi Joshi

    What We'll Cover

    • What is Serverless September? (6 initiatives)
    • How can I participate? (3 actions)
    • How can I skill up (30 days)
    • Who is behind this? (Team Contributors)
    • How can you contribute? (Custom Issues)
    • Exercise: Take the Cloud Skills Challenge!
    • Resources: #30DaysOfServerless Collection.

    Serverless September

    Welcome to Day 01 of 🍂 #ServerlessSeptember! Today, we kick off a full month of content and activities to skill you up on all things Serverless on Azure with content, events, and community interactions! Read on to learn about what we have planned!


    Explore our initiatives

    We have a number of initiatives planned for the month to help you learn and skill up on relevant technologies. Click on the links to visit the relevant pages for each.

    We'll go into more details about #30DaysOfServerless in this post - don't forget to subscribe to the blog to get daily posts delivered directly to your preferred feed reader!


    Register for events!

    What are 3 things you can do today, to jumpstart your learning journey?

    Serverless Hacks


    #30DaysOfServerless

    #30DaysOfServerless is a month-long series of daily blog posts grouped into 4 themed weeks - taking you from core concepts to end-to-end solution examples in 30 days. Each article will be short (5-8 mins reading time) and provide exercises and resources to help you reinforce learnings and take next steps.

    This series focuses on the Serverless On Azure learning journey in four stages, each building on the previous week to help you skill up in a beginner-friendly way:

    We have a tentative roadmap for the topics we hope to cover and will keep this updated as we go with links to actual articles as they get published.

    Week 1: FOCUS ON FUNCTIONS ⚡️

    Here's a sneak peek at what we have planned for week 1. We'll start with a broad look at fundamentals, walkthrough examples for each targeted programming language, then wrap with a post that showcases the role of Azure Functions in powering different serverless scenarios.

    • Sep 02: Learn Core Concepts for Azure Functions
    • Sep 03: Build and deploy your first Function
    • Sep 04: Azure Functions - for Java Developers!
    • Sep 05: Azure Functions - for JavaScript Developers!
    • Sep 06: Azure Functions - for .NET Developers!
    • Sep 07: Azure Functions - for Python Developers!
    • Sep 08: Wrap: Azure Functions + Serverless on Azure

    Ways to Participate..

    We hope you are as excited as we are, to jumpstart this journey. We want to make this a useful, beginner-friendly journey and we need your help!

    Here are the many ways you can participate:

    • Follow Azure on dev.to - we'll republish posts under this series page and welcome comments and feedback there!
    • Discussions on GitHub - Use this if you have feedback for us (on how we can improve these resources), or want to chat with your peers about serverless topics.
    • Custom Issues - just pick a template, create a new issue by filling in the requested details, and submit. You can use these to:
      • submit questions for AskTheExpert (live Q&A) ahead of time
      • submit your own articles or projects for community to learn from
      • share your ServerlessHack and get listed in our Hall Of Fame!
      • report bugs or share ideas for improvements

    Here's the list of custom issues currently defined.

    Community Buzz

    Let's Get Started!

    Now you know everything! We hope you are as excited as we are to dive into a full month of active learning and doing! Don't forget to subscribe for updates in your favorite feed reader! And look out for our first Azure Functions post tomorrow!


    - - + + \ No newline at end of file diff --git a/blog/page/33/index.html b/blog/page/33/index.html index e34430f8c4..34bece4a88 100644 --- a/blog/page/33/index.html +++ b/blog/page/33/index.html @@ -14,13 +14,13 @@ - - + +

    · 3 min read
    Sara Gibbons

    ✨ Serverless September For Students

    My love for the tech industry grows as it evolves. Not just for the new technologies to play with, but seeing how paths into a tech career continue to expand. Allowing so many new voices, ideas and perspectives to our industry. With serverless computing removing barriers of entry for so many.

    It's a reason I enjoy working with universities and students. I get to hear the excitement of learning, fresh ideas and perspectives from our student community. All you students are incredible! How you view serverless, and what it can do, so cool!

    This year for Serverless September we want to hear all the amazing ways our student community is learning and working with Azure Serverless, and have all new ways for you to participate.

    Getting Started

    If you don't already have an Azure for Students account you can easily get your FREE account created at Azure for Students Sign up.

    If you are new to serverless, here are a couple links to get you started:

    No Experience, No problem

    For Serverless September we have planned beginner friendly content all month long. Covering such services as:

    You can follow #30DaysOfServerles here on the blog for daily posts covering concepts, scenarios, and how to create end-to-end solutions.

    Join the Cloud Skills Challenge where we have selected a list of Learn Modules for you to go through at your own pace, including deploying a full stack application with Azure Static Web Apps.

    Have A Question

    We want to hear it! All month long we will have Ask The Expert sessions. Submit your questions at any time and will be be sure to get one of our Azure Serverless experts to get you an answer.

    Share What You've Created

    If you have written a blog post, recorded a video, have an open source Azure Serverless project, we'd love to see it! Here is some links for you to share your creations

    🧭 Explore Student Resources

    ⚡️ Join us!

    Multiple teams across Microsoft are working to create Serverless September! They all want to hear from our incredible student community. We can't wait to share all the Serverless September resources and hear what you have learned and created. Here are some ways to keep up to date on all Serverless September activity:

    - - + + \ No newline at end of file diff --git a/blog/page/34/index.html b/blog/page/34/index.html index 0b7cead56c..d5e8acaf28 100644 --- a/blog/page/34/index.html +++ b/blog/page/34/index.html @@ -14,13 +14,13 @@ - - + +

    · 3 min read
    Nitya Narasimhan
    Devanshi Joshi

    🍂 It's September?

    Well, almost! September 1 is a few days away and I'm excited! Why? Because it's the perfect time to revisit #Serverless September, a month of

    ".. content-driven learning where experts and practitioners share their insights and tutorials on how to use serverless technologies effectively in today's ecosystems"

    If the words look familiar, it's because I actually wrote them 2 years ago when we launched the 2020 edition of this series. You might even recall this whimsical image I drew to capture the concept of September (fall) and Serverless (event-driven on-demand compute). Since then, a lot has happened in the serverless ecosystem!

    You can still browse the 2020 Content Collection to find great talks, articles and code samples to get started using Serverless on Azure. But read on to learn what's new!

    🧐 What's New?

    Well - quite a few things actually. This year, Devanshi Joshi and I expanded the original concept in a number of ways. Here's just a few of them that come to mind.

    New Website

    This year, we created this website (shortcut: https://aka.ms/serverless-september) to serve as a permanent home for content in 2022 and beyond - making it a canonical source for the #serverless posts we publish to tech communities like dev.to, Azure Developer Community and Apps On Azure. We hope this also makes it easier for you to search for, or discover, current and past articles that support your learning journey!

    Start by bookmarking these two sites:

    More Options

    Previous years focused on curating and sharing content authored by Microsoft and community contributors, showcasing serverless examples and best practices. This was perfect for those who already had experience with the core devtools and concepts.

    This year, we wanted to combine beginner-friendly options (for those just starting their serverless journey) with more advanced insights (for those looking to skill up further). Here's a sneak peek at some of the initiatives we've got planned!

    We'll also explore the full spectrum of serverless - from Functions-as-a-Service (for granularity) to Containerization (for deployment) and Microservices (for scalability). Here are a few services and technologies you'll get to learn more about:

    ⚡️ Join us!

    This has been a labor of love from multiple teams at Microsoft! We can't wait to share all the resources that we hope will help you skill up on all things Serverless this September! Here are a couple of ways to participate:

    - - + + \ No newline at end of file diff --git a/blog/page/4/index.html b/blog/page/4/index.html index 91602be2d0..3b5fb1028d 100644 --- a/blog/page/4/index.html +++ b/blog/page/4/index.html @@ -14,13 +14,13 @@ - - + +

    · 14 min read
    Justin Yoo

    Welcome to Day 28 of #30DaysOfServerless!

    Since it's the serverless end-to-end week, I'm going to discuss how to use a serverless application Azure Functions with OpenAPI extension to be seamlessly integrated with Power Platform custom connector through Azure API Management - in a post I call "Where am I? My GPS Location with Serverless Power Platform Custom Connector"

    OK. Are you ready? Let's get started!


    What We'll Cover

    • What is Power Platform custom connector?
    • Proxy app to Google Maps and Naver Map API
    • API Management integration
    • Two ways of building custom connector
    • Where am I? Power Apps app
    • Exercise: Try this yourself!
    • Resources: For self-study!


    SAMPLE REPO

    Want to follow along? Check out the sample app on GitHub repository used in this post.

    What is Power Platform custom connector?

    Power Platform is a low-code/no-code application development tool for fusion teams that consist of a group of people. Those people come from various disciplines, including field experts (domain experts), IT professionals and professional developers, to draw business values successfully. Within the fusion team, the domain experts become citizen developers or low-code developers by Power Platform. In addition, Making Power Platform more powerful is that it offers hundreds of connectors to other Microsoft 365 and third-party services like SAP, ServiceNow, Salesforce, Google, etc.

    However, what if you want to use your internal APIs or APIs not yet offering their official connectors? Here's an example. If your company has an inventory management system, and you want to use it within your Power Apps or Power Automate. That point is exactly where Power Platform custom connectors is necessary.

    Inventory Management System for Power Apps

    Therefore, Power Platform custom connectors enrich those citizen developers' capabilities because those connectors can connect any API applications for the citizen developers to use.

    In this post, let's build a custom connector that provides a static map image generated by Google Maps API and Naver Map API using your GPS location.

    Proxy app to Google Maps and Naver Map API

    First, let's build an Azure Functions app that connects to Google Maps and Naver Map. Suppose that you've already got the API keys for both services. If you haven't yet, get the keys first by visiting here for Google and here for Naver. Then, store them to local.settings.json within your Azure Functions app.

    {
    "Values": {
    ...
    "Maps__Google__ApiKey": "<GOOGLE_MAPS_API_KEY>",
    "Maps__Naver__ClientId": "<NAVER_MAP_API_CLIENT_ID>",
    "Maps__Naver__ClientSecret": "<NAVER_MAP_API_CLIENT_SECRET>"
    }
    }

    Here's the sample logic to get the static image from Google Maps API. It takes the latitude and longitude of your current location and image zoom level, then returns the static map image. There are a few hard-coded assumptions, though:

    • The image size should be 400x400.
    • The image should be in .png format.
    • The marker should show be red and show my location.
    public class GoogleMapService : IMapService
    {
    public async Task<byte[]> GetMapAsync(HttpRequest req)
    {
    var latitude = req.Query["lat"];
    var longitude = req.Query["long"];
    var zoom = (string)req.Query["zoom"] ?? "14";

    var sb = new StringBuilder();
    sb.Append("https://maps.googleapis.com/maps/api/staticmap")
    .Append($"?center={latitude},{longitude}")
    .Append("&size=400x400")
    .Append($"&zoom={zoom}")
    .Append($"&markers=color:red|{latitude},{longitude}")
    .Append("&format=png32")
    .Append($"&key={this._settings.Google.ApiKey}");
    var requestUri = new Uri(sb.ToString());

    var bytes = await this._http.GetByteArrayAsync(requestUri).ConfigureAwait(false);

    return bytes;
    }
    }

    The NaverMapService class has a similar logic with the same input and assumptions. Here's the code:

    public class NaverMapService : IMapService
    {
    public async Task<byte[]> GetMapAsync(HttpRequest req)
    {
    var latitude = req.Query["lat"];
    var longitude = req.Query["long"];
    var zoom = (string)req.Query["zoom"] ?? "13";

    var sb = new StringBuilder();
    sb.Append("https://naveropenapi.apigw.ntruss.com/map-static/v2/raster")
    .Append($"?center={longitude},{latitude}")
    .Append("&w=400")
    .Append("&h=400")
    .Append($"&level={zoom}")
    .Append($"&markers=color:blue|pos:{longitude}%20{latitude}")
    .Append("&format=png")
    .Append("&lang=en");
    var requestUri = new Uri(sb.ToString());

    this._http.DefaultRequestHeaders.Clear();
    this._http.DefaultRequestHeaders.Add("X-NCP-APIGW-API-KEY-ID", this._settings.Naver.ClientId);
    this._http.DefaultRequestHeaders.Add("X-NCP-APIGW-API-KEY", this._settings.Naver.ClientSecret);

    var bytes = await this._http.GetByteArrayAsync(requestUri).ConfigureAwait(false);

    return bytes;
    }
    }

    Let's take a look at the function endpoints. Here's for the Google Maps and Naver Map. As the GetMapAsync(req) method returns a byte array value, you need to transform it as FileContentResult, with the content type of image/png.

    // Google Maps
    public class GoogleMapsTrigger
    {
    [FunctionName(nameof(GoogleMapsTrigger.GetGoogleMapImage))]
    public async Task<IActionResult> GetGoogleMapImage(
    [HttpTrigger(AuthorizationLevel.Anonymous, "GET", Route = "google/image")] HttpRequest req)
    {
    this._logger.LogInformation("C# HTTP trigger function processed a request.");

    var bytes = await this._service.GetMapAsync(req).ConfigureAwait(false);

    return new FileContentResult(bytes, "image/png");
    }
    }

    // Naver Map
    public class NaverMapsTrigger
    {
    [FunctionName(nameof(NaverMapsTrigger.GetNaverMapImage))]
    public async Task<IActionResult> GetNaverMapImage(
    [HttpTrigger(AuthorizationLevel.Anonymous, "GET", Route = "naver/image")] HttpRequest req)
    {
    this._logger.LogInformation("C# HTTP trigger function processed a request.");

    var bytes = await this._service.GetMapAsync(req).ConfigureAwait(false);

    return new FileContentResult(bytes, "image/png");
    }
    }

    Then, add the OpenAPI capability to each function endpoint. Here's the example:

    // Google Maps
    public class GoogleMapsTrigger
    {
    [FunctionName(nameof(GoogleMapsTrigger.GetGoogleMapImage))]
    // ⬇️⬇️⬇️ Add decorators provided by the OpenAPI extension ⬇️⬇️⬇️
    [OpenApiOperation(operationId: nameof(GoogleMapsTrigger.GetGoogleMapImage), tags: new[] { "google" })]
    [OpenApiParameter(name: "lat", In = ParameterLocation.Query, Required = true, Type = typeof(string), Description = "The **latitude** parameter")]
    [OpenApiParameter(name: "long", In = ParameterLocation.Query, Required = true, Type = typeof(string), Description = "The **longitude** parameter")]
    [OpenApiParameter(name: "zoom", In = ParameterLocation.Query, Required = false, Type = typeof(string), Description = "The **zoom level** parameter &ndash; Default value is `14`")]
    [OpenApiResponseWithBody(statusCode: HttpStatusCode.OK, contentType: "image/png", bodyType: typeof(byte[]), Description = "The map image as an OK response")]
    // ⬆️⬆️⬆️ Add decorators provided by the OpenAPI extension ⬆️⬆️⬆️
    public async Task<IActionResult> GetGoogleMapImage(
    [HttpTrigger(AuthorizationLevel.Anonymous, "GET", Route = "google/image")] HttpRequest req)
    {
    ...
    }
    }

    // Naver Map
    public class NaverMapsTrigger
    {
    [FunctionName(nameof(NaverMapsTrigger.GetNaverMapImage))]
    // ⬇️⬇️⬇️ Add decorators provided by the OpenAPI extension ⬇️⬇️⬇️
    [OpenApiOperation(operationId: nameof(NaverMapsTrigger.GetNaverMapImage), tags: new[] { "naver" })]
    [OpenApiParameter(name: "lat", In = ParameterLocation.Query, Required = true, Type = typeof(string), Description = "The **latitude** parameter")]
    [OpenApiParameter(name: "long", In = ParameterLocation.Query, Required = true, Type = typeof(string), Description = "The **longitude** parameter")]
    [OpenApiParameter(name: "zoom", In = ParameterLocation.Query, Required = false, Type = typeof(string), Description = "The **zoom level** parameter &ndash; Default value is `13`")]
    [OpenApiResponseWithBody(statusCode: HttpStatusCode.OK, contentType: "image/png", bodyType: typeof(byte[]), Description = "The map image as an OK response")]
    // ⬆️⬆️⬆️ Add decorators provided by the OpenAPI extension ⬆️⬆️⬆️
    public async Task<IActionResult> GetNaverMapImage(
    [HttpTrigger(AuthorizationLevel.Anonymous, "GET", Route = "naver/image")] HttpRequest req)
    {
    ...
    }
    }

    Run the function app in the local. Here are the latitude and longitude values for Seoul, Korea.

    • latitude: 37.574703
    • longitude: 126.978519

    Google Map for Seoul

    It seems to be working! Let's deploy it to Azure.

    API Management integration

    Visual Studio 2022 provides a built-in deployment tool for Azure Functions app onto Azure. In addition, the deployment tool supports seamless integration with Azure API Management as long as your Azure Functions app enables the OpenAPI capability. In this post, I'm going to use this feature. Right-mouse click on the Azure Functions project and select the "Publish" menu.

    Visual Studio context menu for publish

    Then, you will see the publish screen. Click the "➕ New" button to create a new publish profile.

    Create a new publish profile

    Choose "Azure" and click the "Next" button.

    Choose the target platform for publish

    Select the app instance. This time simply pick up the "Azure Function App (Windows)" option, then click "Next".

    Choose the target OS for publish

    If you already provision an Azure Function app instance, you will see it on the screen. Otherwise, create a new one. Then, click "Next".

    Choose the target instance for publish

    In the next step, you are asked to choose the Azure API Management instance for integration. Choose one, or create a new one. Then, click "Next".

    Choose the APIM instance for integration

    Finally, select the publish method either local publish or GitHub Actions workflow. Let's pick up the local publish method for now. Then, click "Finish".

    Choose the deployment type

    The publish profile has been created. Click "Close" to move on.

    Publish profile created

    Now the function app is ready for deployment. Click the "Publish" button and see how it goes.

    Publish function app

    The Azure function app has been deployed and integrated with the Azure API Management instance.

    Function app published

    Go to the published function app site, and everything looks OK.

    Function app on Azure

    And API Management shows the function app integrated perfectly.

    Function app integrated with APIM

    Now, you are ready to create a custom connector. Let's move on.

    Two ways of building custom connector

    There are two ways to create a custom connector.

    Export custom connector from API Management

    First, you can directly use the built-in API Management feature. Then, click the ellipsis icon and select the "Create Power Connector" menu.

    Create Power Connector menu

    Then, you are redirected to this screen. While the "API" and "API display name" fields are pre-populated, you need to choose the Power Platform environment tied to your tenant. Choose an environment, click "Authenticate", and click "Create".

    Create custom connector screen

    Check your custom connector on Power Apps or Power Automate side.

    Custom connector created on Power Apps

    However, there's a caveat to this approach. Because it's tied to your tenant, you should use the second approach if you want to use this custom connector on the other tenant.

    Import custom connector from OpenAPI document or URL

    Click the ellipsis icon again and select the "Export" menu.

    Export menu

    On the Export API screen, choose the "OpenAPI v2 (JSON)" panel because Power Platform custom connector currently accepts version 2 of the OpenAPI document.

    Select OpenAPI v2

    Download the OpenAPI document to your local computer and move to your Power Apps or Power Automate page under your desired environment. I'm going to use the Power Automate page. First, go to the "Data" ➡️ "Custom connectors" page. Then, click the "➕ New custom connector" ➡️ "Import an OpenAPI file" at the top right corner.

    New custom connector

    When a modal pops up, give the custom connector name and import the OpenAPI document exported above. Then, click "Continue".

    Import custom connector

    Actually, that's it! Next, click the "✔️ Create connector" button to create the connector.

    Create custom connector

    Go back to the custom connector page, and you will see the "Maps API" custom connector you just created.

    Custom connector imported

    So, you are ready to create a Power Apps app to display your location on Google Maps or Naver Map! Let's move on.

    Where am I? Power Apps app

    Open the Power Apps Studio, and create an empty canvas app, named Who am I with a phone layout.

    Custom connector integration

    To use the custom connector created above, you need to add it to the Power App. Click the cylinder icon on the left and click the "Add data" button.

    Add custom connector to data pane

    Search the custom connector name, "Maps API", and click the custom connector to add.

    Search custom connector

    To use the custom connector, you also need to create a connection to it. Click the "Connect" button and move on.

    Create connection to custom connector

    Now, you've got the connection to the custom connector.

    Connection to custom connector ready

    Controls

    Let's build the Power Apps app. First of all, put three controls Image, Slider and Button onto the canvas.

    Power Apps control added

    Click the "Screen1" control and change the value on the property "OnVisible" to the formula below. The formula stores the current slider value in the zoomlevel collection.

    ClearCollect(
    zoomlevel,
    Slider1.Value
    )

    Click the "Botton1" control and change the value on the property "OnSelected" to the formula below. It passes the current latitude, longitude and zoom level to the custom connector and receives the image data. The received image data is stored in the result collection.

    ClearCollect(
    result,
    MAPS.GetGoogleMapImage(
    Location.Latitude,
    Location.Longitude,
    { zoom: First(zoomlevel).Value }
    )
    )

    Click the "Image1" control and change the value on the property "Image" to the formula below. It gets the image data from the result collection.

    First(result).Url

    Click the "Slider1" control and change the value on the property "OnChange" to the formula below. It stores the current slider value to the zoomlevel collection, followed by calling the custom connector to get the image data against the current location.

    ClearCollect(
    zoomlevel,
    Slider1.Value
    );
    ClearCollect(
    result,
    MAPS.GetGoogleMapImage(
    Location.Latitude,
    Location.Longitude,
    { zoom: First(zoomlevel).Value }
    )
    )

    That seems to be OK. Let's click the "Where am I?" button. But it doesn't show the image. The First(result).Url value is actually similar to this:

    appres://blobmanager/1090a86393a843adbfcf428f0b90e91b/1

    It's the image reference value somewhere you can't get there.

    Workaround Power Automate workflow

    Therefore, you need a workaround using a Power Automate workflow to sort out this issue. Open the Power Automate Studio, create an instant cloud flow with the Power App trigger, and give it the "Where am I" name. Then add input parameters of lat, long and zoom.

    Power Apps trigger on Power Automate workflow

    Add custom connector action to get the map image.

    Select action to get the Google Maps image

    In the action, pass the appropriate parameters to the action.

    Pass parameters to the custom connector action

    Add a "Response" action and put the following values into each field.

    • "Body" field:

      {
      "base64Image": <power_automate_expression>
      }

      The <power_automate_expression> should be concat('data:', body('GetGoogleMapImage')?['$content-type'], ';base64,', body('GetGoogleMapImage')?['$content']).

    • "Response Body JSON Schema" field:

      {
      "type": "object",
      "properties": {
      "base64Image": {
      "type": "string"
      }
      }
      }

    Format the Response action

    Let's return to the Power Apps Studio and add the Power Automate workflow you created.

    Add Power Automate workflow

    Select "Button1" and change the value on the property "OnSelect" below. It replaces the direct call to the custom connector with the Power Automate workflow.

    ClearCollect(
    result,
    WhereamI.Run(
    Location.Latitude,
    Location.Longitude,
    First(zoomlevel).Value
    )
    )

    Also, change the value on the property "OnChange" of the "Slider1" control below, replacing the custom connector call with the Power Automate workflow call.

    ClearCollect(
    zoomlevel,
    Slider1.Value
    );
    ClearCollect(
    result,
    WhereamI.Run(
    Location.Latitude,
    Location.Longitude,
    First(zoomlevel).Value
    )
    )

    And finally, change the "Image1" control's "Image" property value below.

    First(result).base64Image

    The workaround has been applied. Click the "Where am I?" button to see your current location from Google Maps.

    Run Power Apps app #1

    If you change the slider left or right, you will see either the zoomed-in image or the zoomed-out image.

    Run Power Apps app #2

    Now, you've created a Power Apps app to show your current location using:

    • Google Maps API through the custom connector, and
    • Custom connector written in Azure Functions with OpenAPI extension!

    Exercise: Try this yourself!

    You can fork this GitHub repository to your account and play around with it to see how the custom connector works. After forking the repository, make sure that you create all the necessary secrets to your repository documented in the README file.

    Then, click the "Deploy to Azure" button, and it will provision all necessary Azure resources and deploy an Azure Functions app for a custom connector.

    Deploy To Azure

    Once everything is deployed successfully, try to create a Power Apps app and Power Automate workflow to see your current location in real-time!

    Resources: For self-study!

    Want to know more about Power Platform custom connector and Azure Functions OpenAPI extension? Here are several resources you can take a look at:

    - - + + \ No newline at end of file diff --git a/blog/page/5/index.html b/blog/page/5/index.html index 8b1e8871aa..b605abc838 100644 --- a/blog/page/5/index.html +++ b/blog/page/5/index.html @@ -14,14 +14,14 @@ - - + +

    · 5 min read
    Madhura Bharadwaj

    Welcome to Day 26 of #30DaysOfServerless!

    Today, we have a special set of posts from our Zero To Hero 🚀 initiative, featuring blog posts authored by our Product Engineering teams for #ServerlessSeptember. Posts were originally published on the Apps on Azure blog on Microsoft Tech Community.


    What We'll Cover

    • Monitoring your Azure Functions
    • Built-in log streaming
    • Live Metrics stream
    • Troubleshooting Azure Functions


    Monitoring your Azure Functions:

    Azure Functions uses Application Insights to collect and analyze log data from individual function executions in your function app.

    Using Application Insights

    Application Insights collects log, performance, and error data. By automatically detecting performance anomalies and featuring powerful analytics tools, you can more easily diagnose issues and better understand how your functions are used. These tools are designed to help you continuously improve performance and usability of your functions. You can even use Application Insights during local function app project development.

    Typically, you create an Application Insights instance when you create your function app. In this case, the instrumentation key required for the integration is already set as an application setting named APPINSIGHTS_INSTRUMENTATIONKEY. With Application Insights integration enabled, telemetry data is sent to your connected Application Insights instance. This data includes logs generated by the Functions host, traces written from your functions code, and performance data. In addition to data from your functions and the Functions host, you can also collect data from the Functions scale controller.

    By default, the data collected from your function app is stored in Application Insights. In the Azure portal, Application Insights provides an extensive set of visualizations of your telemetry data. You can drill into error logs and query events and metrics. To learn more, including basic examples of how to view and query your collected data, see Analyze Azure Functions telemetry in Application Insights.

    Using Log Streaming

    In addition to this, you can have a smoother debugging experience through log streaming. There are two ways to view a stream of log files being generated by your function executions.

    • Built-in log streaming: the App Service platform lets you view a stream of your application log files. This is equivalent to the output seen when you debug your functions during local development and when you use the Test tab in the portal. All log-based information is displayed. For more information, see Stream logs. This streaming method supports only a single instance and can't be used with an app running on Linux in a Consumption plan.
    • Live Metrics Stream: when your function app is connected to Application Insights, you can view log data and other metrics in near real-time in the Azure portal using Live Metrics Stream. Use this method when monitoring functions running on multiple-instances or on Linux in a Consumption plan. This method uses sampled data. Log streams can be viewed both in the portal and in most local development environments.
    Monitoring Azure Functions

    Learn how to configure monitoring for your Azure Functions. See Monitoring Azure Functions data reference for detailed information on the metrics and logs metrics created by Azure Functions.

    In addition to this, Azure Functions uses Azure Monitor to monitor the health of your function apps. Azure Functions collects the same kinds of monitoring data as other Azure resources that are described in Azure Monitor data collection. See Monitoring Azure Functions data reference for detailed information on the metrics and logs metrics created by Azure Functions.

    Troubleshooting your Azure Functions:

    When you do run into issues with your function app, Azure Functions diagnostics points out what’s wrong. It guides you to the right information to troubleshoot and resolve the issue more easily and quickly.

    Let’s explore how to use Azure Functions diagnostics to diagnose and solve common function app issues.

    1. Navigate to your function app in the Azure portal.
    2. Select Diagnose and solve problems to open Azure Functions diagnostics.
    3. Once you’re here, there are multiple ways to retrieve the information you’re looking for. Choose a category that best describes the issue of your function app by using the keywords in the homepage tile. You can also type a keyword that best describes your issue in the search bar. There’s also a section at the bottom of the page that will directly take you to some of the more popular troubleshooting tools. For example, you could type execution to see a list of diagnostic reports related to your function app execution and open them directly from the homepage.

    Monitoring and troubleshooting apps in Azure Functions

    1. For example, click on the Function App Down or Reporting Errors link under Popular troubleshooting tools section. You will find detailed analysis, insights and next steps for the issues that were detected. On the left you’ll see a list of detectors. Click on them to explore more, or if there’s a particular keyword you want to look for, type it Into the search bar on the top.

    Monitoring and troubleshooting apps in Azure Functions

    TROUBLESHOOTING TIP

    Here are some general troubleshooting tips that you can follow if you find your Function App throwing Azure Functions Runtime unreachable error.

    Also be sure to check out the recommended best practices to ensure your Azure Functions are highly reliable. This article details some best practices for designing and deploying efficient function apps that remain healthy and perform well in a cloud-based environment.

    Bonus tip:

    - - + + \ No newline at end of file diff --git a/blog/page/6/index.html b/blog/page/6/index.html index 68adce9ded..25711ddfa2 100644 --- a/blog/page/6/index.html +++ b/blog/page/6/index.html @@ -14,13 +14,13 @@ - - + +

    · 7 min read
    Brian Benz

    Welcome to Day 25 of #30DaysOfServerless!

    Azure Container Apps enable application code packaged in containers to run and scale without the overhead of managing cloud infrastructure and container orchestration. In this post I'll show you how to deploy a Java application running on Spring Boot in a container to Azure Container Registry and Azure Container Apps.


    What We'll Cover

    • Introduction to Deploying Java containers in the cloud
    • Step-by-step: Deploying to Azure Container Registry
    • Step-by-step: Deploying and running on Azure Container Apps
    • Resources: For self-study!


    Deploy Java containers to cloud

    We'll deploy a Java application running on Spring Boot in a container to Azure Container Registry and Azure Container Apps. Here are the main steps:

    • Create Azure Container Registry (ACR) on Azure portal
    • Create Azure Container App (ACA) on Azure portal.
    • Deploy code to Azure Container Registry from the Azure CLI.
    • Deploy container from ACR to ACA using the Azure portal.
    PRE-REQUISITES

    Sign in to Azure from the CLI using the az login command, and follow the prompts in your browser to complete the authentication process. Also, ensure you're running the latest version of the CLI by using the az upgrade command.

    1. Get Sample Code

    Fork and clone the sample GitHub repo to your local machine. Navigate to the and click Fork in the top-right corner of the page.

    The example code that we're using is a very basic containerized Spring Boot example. There are a lot more details to learn about Spring boot apps in docker, for a deep dive check out this Spring Boot Guide

    2. Run Sample Locally (Optional)

    If you have docker installed locally, you can optionally test the code on your local machine. Navigate to the root directory of the forked repository and run the following commands:

    docker build -t spring-boot-docker-aca .
    docker run -p 8080:8080 spring-boot-docker-aca

    Open a browser and go to https://localhost:8080. You should see this message:

    Hello Docker World

    That indicates the the Spring Boot app is successfully running locally in a docker container.

    Next, let's set up an Azure Container Registry an an Azure Container App and deploy this container to the cloud!


    3. Step-by-step: Deploy to ACR

    To create a container registry from the portal dashboard, Select Create a resource > Containers > Container Registry.

    Navigate to container registry in portal

    In the Basics tab, enter values for Resource group and Registry name. The registry name must be unique within Azure, and contain 5-50 alphanumeric characters. Create a new resource group in the West US location named spring-boot-docker-aca. Select the 'Basic' SKU.

    Keep the default values for the remaining settings. Then select Review + create, then Create. When the Deployment succeeded message appears, select the container registry in the portal.

    Note the registry server name ending with azurecr.io. You will use this in the following steps when you push and pull images with Docker.

    3.1 Log into registry using the Azure CLI

    Before pushing and pulling container images, you must log in to the registry instance. Sign into the Azure CLI on your local machine, then run the az acr login command. For this step, use the registry name, not the server name ending with azurecr.io.

    From the command line, type:

    az acr login --name myregistryname

    The command returns Login Succeeded once completed.

    3.2 Build & deploy with az acr build

    Next, we're going to deploy the docker container we created earlier using the AZ ACR Build command. AZ ACR Build creates a docker build from local code and pushes the container to Azure Container Registry if the build is successful.

    Go to your local clone of the spring-boot-docker-aca repo in the command line, type:

    az acr build --registry myregistryname --image spring-boot-docker-aca:v1 .

    3.3 List container images

    Once the AZ ACR Build command is complete, you should be able to view the container as a repository in the registry. In the portal, open your registry and select Repositories, then select the spring-boot-docker-aca repository you created with docker push. You should also see the v1 image under Tags.

    4. Deploy on ACA

    Now that we have an image in the Azure Container Registry, we can deploy it to Azure Container Apps. For the first deployment, we'll pull the container from our ACR as part of the ACA setup.

    4.1 Create a container app

    We'll create the container app at the same place that we created the container registry in the Azure portal. From the portal, select Create a resource > Containers > Container App. In the Basics tab, set these values:

    4.2 Enter project details

    SettingAction
    SubscriptionYour Azure subscription.
    Resource groupUse the spring-boot-docker-aca resource group
    Container app nameEnter spring-boot-docker-aca.

    4.3 Create an environment

    1. In the Create Container App environment field, select Create new.

    2. In the Create Container App Environment page on the Basics tab, enter the following values:

      SettingValue
      Environment nameEnter my-environment.
      RegionSelect westus3.
    3. Select OK.

    4. Select the Create button at the bottom of the Create Container App Environment page.

    5. Select the Next: App settings button at the bottom of the page.

    5. App settings tab

    The App settings tab is where you connect to the ACR and pull the repository image:

    SettingAction
    Use quickstart imageUncheck the checkbox.
    NameEnter spring-boot-docker-aca.
    Image sourceSelect Azure Container Registry
    RegistrySelect your ACR from the list.
    ImageSelect spring-boot-docker-aca from the list.
    Image TagSelect v1 from the list.

    5.1 Application ingress settings

    SettingAction
    IngressSelect Enabled.
    Ingress visibilitySelect External to publicly expose your container app.
    Target portEnter 8080.

    5.2 Deploy the container app

    1. Select the Review and create button at the bottom of the page.
    2. Select Create.

    Once the deployment is successfully completed, you'll see the message: Your deployment is complete.

    5.3 Verify deployment

    In the portal, go to the Overview of your spring-boot-docker-aca Azure Container App, and click on the Application Url. You should see this message in the browser:

    Hello Docker World

    That indicates the the Spring Boot app is running in a docker container in your spring-boot-docker-aca Azure Container App.

    Resources: For self-study!

    Once you have an understanding of the basics in ths post, there is so much more to learn!

    Thanks for stopping by!

    - - + + \ No newline at end of file diff --git a/blog/page/7/index.html b/blog/page/7/index.html index 20d585323b..515352303c 100644 --- a/blog/page/7/index.html +++ b/blog/page/7/index.html @@ -14,13 +14,13 @@ - - + +

    · 19 min read
    Alex Wolf

    Welcome to Day 24 of #30DaysOfServerless!

    We continue exploring E2E scenarios with this tutorial where you'll deploy a containerized ASP.NET Core 6.0 application to Azure Container Apps.

    The application consists of a front-end web app built using Blazor Server, as well as two Web API projects to manage data. These projects will exist as three separate containers inside of a shared container apps environment.


    What We'll Cover

    • Deploy ASP.NET Core 6.0 app to Azure Container Apps
    • Automate deployment workflows using GitHub Actions
    • Provision and deploy resources using Azure Bicep
    • Exercise: Try this yourself!
    • Resources: For self-study!


    Introduction

    Azure Container Apps enables you to run microservices and containerized applications on a serverless platform. With Container Apps, you enjoy the benefits of running containers while leaving behind the concerns of manually configuring cloud infrastructure and complex container orchestrators.

    In this tutorial, you'll deploy a containerized ASP.NET Core 6.0 application to Azure Container Apps. The application consists of a front-end web app built using Blazor Server, as well as two Web API projects to manage data. These projects will exist as three separate containers inside of a shared container apps environment.

    You will use GitHub Actions in combination with Bicep to deploy the application. These tools provide an approachable and sustainable solution for building CI/CD pipelines and working with Container Apps.

    PRE-REQUISITES

    Architecture

    In this tutorial, we'll setup a container app environment with a separate container for each project in the sample store app. The major components of the sample project include:

    • A Blazor Server front-end web app to display product information
    • A products API to list available products
    • An inventory API to determine how many products are in stock
    • GitHub Actions and Bicep templates to provision Azure resources and then build and deploy the sample app.

    You will explore these templates later in the tutorial.

    Public internet traffic should be proxied to the Blazor app. The back-end APIs should only be reachable via requests from the Blazor app inside the container apps environment. This setup can be achieved using container apps environment ingress configurations during deployment.

    An architecture diagram of the shopping app


    Project Sources

    Want to follow along? Fork the sample below. The tutorial can be completed with or without Dapr integration. Pick the path you feel comfortable in. Dapr provides various benefits that make working with Microservices easier - you can learn more in the docs. For this tutorial you will need GitHub and Azure CLI.

    PICK YOUR PATH

    To follow along with this tutorial, fork the relevant sample project below.

    You can run the app locally from Visual Studio:

    • Right click on the Blazor Store project and select Set as Startup Project.
    • Press the start button at the top of Visual Studio to run the app.
    • (Once running) start each API in the background by
    • right-clicking on the project node
    • selecting Debug --> Start without debugging.

    Once the Blazor app is running, you should see something like this:

    An architecture diagram of the shopping app


    Configuring Azure credentials

    In order to deploy the application to Azure through GitHub Actions, you first need to create a service principal. The service principal will allow the GitHub Actions process to authenticate to your Azure subscription to create resources and deploy code. You can learn more about Service Principals in the Azure CLI documentation. For this step you'll need to be logged into the Azure CLI.

    1) If you have not done so already, make sure to fork the sample project to your own GitHub account or organization.

    1) Once you have completed this step, create a service principal using the Azure CLI command below:

    ```azurecli
    $subscriptionId=$(az account show --query id --output tsv)
    az ad sp create-for-rbac --sdk-auth --name WebAndApiSample --role Contributor --scopes /subscriptions/$subscriptionId
    ```

    1) Copy the JSON output of the CLI command to your clipboard

    1) Under the settings tab of your forked GitHub repo, create a new secret named AzureSPN. The name is important to match the Bicep templates included in the project, which we'll review later. Paste the copied service principal values on your clipboard into the secret and save your changes. This new secret will be used by the GitHub Actions workflow to authenticate to Azure.

    :::image type="content" source="./img/dotnet/github-secrets.png" alt-text="A screenshot of adding GitHub secrets.":::

    Deploy using Github Actions

    You are now ready to deploy the application to Azure Container Apps using GitHub Actions. The sample application includes a GitHub Actions template that is configured to build and deploy any changes to a branch named deploy. The deploy branch does not exist in your forked repository by default, but you can easily create it through the GitHub user interface.

    1) Switch to the Actions tab along the top navigation of your GitHub repository. If you have not done so already, ensure that workflows are enabled by clicking the button in the center of the page.

    A screenshot showing how to enable GitHub actions

    1) Navigate to the main Code tab of your repository and select the main dropdown. Enter deploy into the branch input box, and then select Create branch: deploy from 'main'.

    A screenshot showing how to create the deploy branch

    1) On the new deploy branch, navigate down into the .github/workflows folder. You should see a file called deploy.yml, which contains the main GitHub Actions workflow script. Click on the file to view its content. You'll learn more about this file later in the tutorial.

    1) Click the pencil icon in the upper right to edit the document.

    1) Change the RESOURCE_GROUP_NAME: value to msdocswebappapis or another valid resource group name of your choosing.

    1) In the upper right of the screen, select Start commit and then Commit changes to commit your edit. This will persist the change to the file and trigger the GitHub Actions workflow to build and deploy the app.

    A screenshot showing how to commit changes

    1) Switch to the Actions tab along the top navigation again. You should see the workflow running to create the necessary resources and deploy the app. The workflow may take several minutes to run. When it completes successfully, all of the jobs should have a green checkmark icon next to them.

    The completed GitHub workflow.

    Explore the Azure resources

    Once the GitHub Actions workflow has completed successfully you can browse the created resources in the Azure portal.

    1) On the left navigation, select Resource Groups. Next,choose the msdocswebappapis resource group that was created by the GitHub Actions workflow.

    2) You should see seven resources available that match the screenshot and table descriptions below.

    The resources created in Azure.

    Resource nameTypeDescription
    inventoryContainer appThe containerized inventory API.
    msdocswebappapisacrContainer registryA registry that stores the built Container images for your apps.
    msdocswebappapisaiApplication insightsApplication insights provides advanced monitoring, logging and metrics for your apps.
    msdocswebappapisenvContainer apps environmentA container environment that manages networking, security and resource concerns. All of your containers live in this environment.
    msdocswebappapislogsLog Analytics workspaceA workspace environment for managing logging and analytics for the container apps environment
    productsContainer appThe containerized products API.
    storeContainer appThe Blazor front-end web app.

    3) You can view your running app in the browser by clicking on the store container app. On the overview page, click the Application Url link on the upper right of the screen.

    :::image type="content" source="./img/dotnet/application-url.png" alt-text="The link to browse the app.":::

    Understanding the GitHub Actions workflow

    The GitHub Actions workflow created and deployed resources to Azure using the deploy.yml file in the .github folder at the root of the project. The primary purpose of this file is to respond to events - such as commits to a branch - and run jobs to accomplish tasks. The deploy.yml file in the sample project has three main jobs:

    • Provision: Create the necessary resources in Azure, such as the container apps environment. This step leverages Bicep templates to create the Azure resources, which you'll explore in a moment.
    • Build: Create the container images for the three apps in the project and store them in the container registry.
    • Deploy: Deploy the container images to the different container apps created during the provisioning job.

    The deploy.yml file also accepts parameters to make the workflow more dynamic, such as setting the resource group name or the Azure region resources will be provisioned to.

    Below is a commented version of the deploy.yml file that highlights the essential steps.

    name: Build and deploy .NET application to Container Apps

    # Trigger the workflow on pushes to the deploy branch
    on:
    push:
    branches:
    - deploy

    env:
    # Set workflow variables
    RESOURCE_GROUP_NAME: msdocswebappapis

    REGION: eastus

    STORE_DOCKER: Store/Dockerfile
    STORE_IMAGE: store

    INVENTORY_DOCKER: Store.InventoryApi/Dockerfile
    INVENTORY_IMAGE: inventory

    PRODUCTS_DOCKER: Store.ProductApi/Dockerfile
    PRODUCTS_IMAGE: products

    jobs:
    # Create the required Azure resources
    provision:
    runs-on: ubuntu-latest

    steps:

    - name: Checkout to the branch
    uses: actions/checkout@v2

    - name: Azure Login
    uses: azure/login@v1
    with:
    creds: ${{ secrets.AzureSPN }}

    - name: Create resource group
    uses: azure/CLI@v1
    with:
    inlineScript: >
    echo "Creating resource group in Azure"
    echo "Executing 'az group create -l ${{ env.REGION }} -n ${{ env.RESOURCE_GROUP_NAME }}'"
    az group create -l ${{ env.REGION }} -n ${{ env.RESOURCE_GROUP_NAME }}

    # Use Bicep templates to create the resources in Azure
    - name: Creating resources
    uses: azure/CLI@v1
    with:
    inlineScript: >
    echo "Creating resources"
    az deployment group create --resource-group ${{ env.RESOURCE_GROUP_NAME }} --template-file '/github/workspace/Azure/main.bicep' --debug

    # Build the three app container images
    build:
    runs-on: ubuntu-latest
    needs: provision

    steps:

    - name: Checkout to the branch
    uses: actions/checkout@v2

    - name: Azure Login
    uses: azure/login@v1
    with:
    creds: ${{ secrets.AzureSPN }}

    - name: Set up Docker Buildx
    uses: docker/setup-buildx-action@v1

    - name: Login to ACR
    run: |
    set -euo pipefail
    access_token=$(az account get-access-token --query accessToken -o tsv)
    refresh_token=$(curl https://${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io/oauth2/exchange -v -d "grant_type=access_token&service=${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io&access_token=$access_token" | jq -r .refresh_token)
    docker login -u 00000000-0000-0000-0000-000000000000 --password-stdin ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io <<< "$refresh_token"

    - name: Build the products api image and push it to ACR
    uses: docker/build-push-action@v2
    with:
    push: true
    tags: ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io/${{ env.PRODUCTS_IMAGE }}:${{ github.sha }}
    file: ${{ env.PRODUCTS_DOCKER }}

    - name: Build the inventory api image and push it to ACR
    uses: docker/build-push-action@v2
    with:
    push: true
    tags: ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io/${{ env.INVENTORY_IMAGE }}:${{ github.sha }}
    file: ${{ env.INVENTORY_DOCKER }}

    - name: Build the frontend image and push it to ACR
    uses: docker/build-push-action@v2
    with:
    push: true
    tags: ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io/${{ env.STORE_IMAGE }}:${{ github.sha }}
    file: ${{ env.STORE_DOCKER }}

    # Deploy the three container images
    deploy:
    runs-on: ubuntu-latest
    needs: build

    steps:

    - name: Checkout to the branch
    uses: actions/checkout@v2

    - name: Azure Login
    uses: azure/login@v1
    with:
    creds: ${{ secrets.AzureSPN }}

    - name: Installing Container Apps extension
    uses: azure/CLI@v1
    with:
    inlineScript: >
    az config set extension.use_dynamic_install=yes_without_prompt

    az extension add --name containerapp --yes

    - name: Login to ACR
    run: |
    set -euo pipefail
    access_token=$(az account get-access-token --query accessToken -o tsv)
    refresh_token=$(curl https://${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io/oauth2/exchange -v -d "grant_type=access_token&service=${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io&access_token=$access_token" | jq -r .refresh_token)
    docker login -u 00000000-0000-0000-0000-000000000000 --password-stdin ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io <<< "$refresh_token"

    - name: Deploy Container Apps
    uses: azure/CLI@v1
    with:
    inlineScript: >
    az containerapp registry set -n products -g ${{ env.RESOURCE_GROUP_NAME }} --server ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io

    az containerapp update -n products -g ${{ env.RESOURCE_GROUP_NAME }} -i ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io/${{ env.PRODUCTS_IMAGE }}:${{ github.sha }}

    az containerapp registry set -n inventory -g ${{ env.RESOURCE_GROUP_NAME }} --server ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io

    az containerapp update -n inventory -g ${{ env.RESOURCE_GROUP_NAME }} -i ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io/${{ env.INVENTORY_IMAGE }}:${{ github.sha }}

    az containerapp registry set -n store -g ${{ env.RESOURCE_GROUP_NAME }} --server ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io

    az containerapp update -n store -g ${{ env.RESOURCE_GROUP_NAME }} -i ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io/${{ env.STORE_IMAGE }}:${{ github.sha }}

    - name: logout
    run: >
    az logout

    Understanding the Bicep templates

    During the provisioning stage of the GitHub Actions workflow, the main.bicep file is processed. Bicep files provide a declarative way of generating resources in Azure and are ideal for managing infrastructure as code. You can learn more about Bicep in the related documentation. The main.bicep file in the sample project creates the following resources:

    • The container registry to store images of the containerized apps.
    • The container apps environment, which handles networking and resource management for the container apps.
    • Three container apps - one for the Blazor front-end and two for the back-end product and inventory APIs.
    • Configuration values to connect these services together

    main.bicep without Dapr

    param location string = resourceGroup().location

    # create the azure container registry
    resource acr 'Microsoft.ContainerRegistry/registries@2021-09-01' = {
    name: toLower('${resourceGroup().name}acr')
    location: location
    sku: {
    name: 'Basic'
    }
    properties: {
    adminUserEnabled: true
    }
    }

    # create the aca environment
    module env 'environment.bicep' = {
    name: 'containerAppEnvironment'
    params: {
    location: location
    }
    }

    # create the various configuration pairs
    var shared_config = [
    {
    name: 'ASPNETCORE_ENVIRONMENT'
    value: 'Development'
    }
    {
    name: 'APPINSIGHTS_INSTRUMENTATIONKEY'
    value: env.outputs.appInsightsInstrumentationKey
    }
    {
    name: 'APPLICATIONINSIGHTS_CONNECTION_STRING'
    value: env.outputs.appInsightsConnectionString
    }
    ]

    # create the products api container app
    module products 'container_app.bicep' = {
    name: 'products'
    params: {
    name: 'products'
    location: location
    registryPassword: acr.listCredentials().passwords[0].value
    registryUsername: acr.listCredentials().username
    containerAppEnvironmentId: env.outputs.id
    registry: acr.name
    envVars: shared_config
    externalIngress: false
    }
    }

    # create the inventory api container app
    module inventory 'container_app.bicep' = {
    name: 'inventory'
    params: {
    name: 'inventory'
    location: location
    registryPassword: acr.listCredentials().passwords[0].value
    registryUsername: acr.listCredentials().username
    containerAppEnvironmentId: env.outputs.id
    registry: acr.name
    envVars: shared_config
    externalIngress: false
    }
    }

    # create the store api container app
    var frontend_config = [
    {
    name: 'ProductsApi'
    value: 'http://${products.outputs.fqdn}'
    }
    {
    name: 'InventoryApi'
    value: 'http://${inventory.outputs.fqdn}'
    }
    ]

    module store 'container_app.bicep' = {
    name: 'store'
    params: {
    name: 'store'
    location: location
    registryPassword: acr.listCredentials().passwords[0].value
    registryUsername: acr.listCredentials().username
    containerAppEnvironmentId: env.outputs.id
    registry: acr.name
    envVars: union(shared_config, frontend_config)
    externalIngress: true
    }
    }

    main.bicep with Dapr


    param location string = resourceGroup().location

    # create the azure container registry
    resource acr 'Microsoft.ContainerRegistry/registries@2021-09-01' = {
    name: toLower('${resourceGroup().name}acr')
    location: location
    sku: {
    name: 'Basic'
    }
    properties: {
    adminUserEnabled: true
    }
    }

    # create the aca environment
    module env 'environment.bicep' = {
    name: 'containerAppEnvironment'
    params: {
    location: location
    }
    }

    # create the various config pairs
    var shared_config = [
    {
    name: 'ASPNETCORE_ENVIRONMENT'
    value: 'Development'
    }
    {
    name: 'APPINSIGHTS_INSTRUMENTATIONKEY'
    value: env.outputs.appInsightsInstrumentationKey
    }
    {
    name: 'APPLICATIONINSIGHTS_CONNECTION_STRING'
    value: env.outputs.appInsightsConnectionString
    }
    ]

    # create the products api container app
    module products 'container_app.bicep' = {
    name: 'products'
    params: {
    name: 'products'
    location: location
    registryPassword: acr.listCredentials().passwords[0].value
    registryUsername: acr.listCredentials().username
    containerAppEnvironmentId: env.outputs.id
    registry: acr.name
    envVars: shared_config
    externalIngress: false
    }
    }

    # create the inventory api container app
    module inventory 'container_app.bicep' = {
    name: 'inventory'
    params: {
    name: 'inventory'
    location: location
    registryPassword: acr.listCredentials().passwords[0].value
    registryUsername: acr.listCredentials().username
    containerAppEnvironmentId: env.outputs.id
    registry: acr.name
    envVars: shared_config
    externalIngress: false
    }
    }

    # create the store api container app
    module store 'container_app.bicep' = {
    name: 'store'
    params: {
    name: 'store'
    location: location
    registryPassword: acr.listCredentials().passwords[0].value
    registryUsername: acr.listCredentials().username
    containerAppEnvironmentId: env.outputs.id
    registry: acr.name
    envVars: shared_config
    externalIngress: true
    }
    }


    Bicep Modules

    The main.bicep file references modules to create resources, such as module products. Modules are a feature of Bicep templates that enable you to abstract resource declarations into their own files or sub-templates. As the main.bicep file is processed, the defined modules are also evaluated. Modules allow you to create resources in a more organized and reusable way. They can also define input and output parameters that are passed to and from the parent template, such as the name of a resource.

    For example, the environment.bicep module extracts the details of creating a container apps environment into a reusable template. The module defines necessary resource dependencies such as Log Analytics Workspaces and an Application Insights instance.

    environment.bicep without Dapr

    param baseName string = resourceGroup().name
    param location string = resourceGroup().location

    resource logs 'Microsoft.OperationalInsights/workspaces@2021-06-01' = {
    name: '${baseName}logs'
    location: location
    properties: any({
    retentionInDays: 30
    features: {
    searchVersion: 1
    }
    sku: {
    name: 'PerGB2018'
    }
    })
    }

    resource appInsights 'Microsoft.Insights/components@2020-02-02' = {
    name: '${baseName}ai'
    location: location
    kind: 'web'
    properties: {
    Application_Type: 'web'
    WorkspaceResourceId: logs.id
    }
    }

    resource env 'Microsoft.App/managedEnvironments@2022-01-01-preview' = {
    name: '${baseName}env'
    location: location
    properties: {
    appLogsConfiguration: {
    destination: 'log-analytics'
    logAnalyticsConfiguration: {
    customerId: logs.properties.customerId
    sharedKey: logs.listKeys().primarySharedKey
    }
    }
    }
    }

    output id string = env.id
    output appInsightsInstrumentationKey string = appInsights.properties.InstrumentationKey
    output appInsightsConnectionString string = appInsights.properties.ConnectionString

    environment.bicep with Dapr


    param baseName string = resourceGroup().name
    param location string = resourceGroup().location

    resource logs 'Microsoft.OperationalInsights/workspaces@2021-06-01' = {
    name: '${baseName}logs'
    location: location
    properties: any({
    retentionInDays: 30
    features: {
    searchVersion: 1
    }
    sku: {
    name: 'PerGB2018'
    }
    })
    }

    resource appInsights 'Microsoft.Insights/components@2020-02-02' = {
    name: '${baseName}ai'
    location: location
    kind: 'web'
    properties: {
    Application_Type: 'web'
    WorkspaceResourceId: logs.id
    }
    }

    resource env 'Microsoft.App/managedEnvironments@2022-01-01-preview' = {
    name: '${baseName}env'
    location: location
    properties: {
    appLogsConfiguration: {
    destination: 'log-analytics'
    logAnalyticsConfiguration: {
    customerId: logs.properties.customerId
    sharedKey: logs.listKeys().primarySharedKey
    }
    }
    }
    }

    output id string = env.id
    output appInsightsInstrumentationKey string = appInsights.properties.InstrumentationKey
    output appInsightsConnectionString string = appInsights.properties.ConnectionString


    The container_apps.bicep template defines numerous parameters to provide a reusable template for creating container apps. This allows the module to be used in other CI/CD pipelines as well.

    container_app.bicep without Dapr

    param name string
    param location string = resourceGroup().location
    param containerAppEnvironmentId string
    param repositoryImage string = 'mcr.microsoft.com/azuredocs/containerapps-helloworld:latest'
    param envVars array = []
    param registry string
    param minReplicas int = 1
    param maxReplicas int = 1
    param port int = 80
    param externalIngress bool = false
    param allowInsecure bool = true
    param transport string = 'http'
    param registryUsername string
    @secure()
    param registryPassword string

    resource containerApp 'Microsoft.App/containerApps@2022-01-01-preview' ={
    name: name
    location: location
    properties:{
    managedEnvironmentId: containerAppEnvironmentId
    configuration: {
    activeRevisionsMode: 'single'
    secrets: [
    {
    name: 'container-registry-password'
    value: registryPassword
    }
    ]
    registries: [
    {
    server: registry
    username: registryUsername
    passwordSecretRef: 'container-registry-password'
    }
    ]
    ingress: {
    external: externalIngress
    targetPort: port
    transport: transport
    allowInsecure: allowInsecure
    }
    }
    template: {
    containers: [
    {
    image: repositoryImage
    name: name
    env: envVars
    }
    ]
    scale: {
    minReplicas: minReplicas
    maxReplicas: maxReplicas
    }
    }
    }
    }

    output fqdn string = containerApp.properties.configuration.ingress.fqdn

    container_app.bicep with Dapr


    param name string
    param location string = resourceGroup().location
    param containerAppEnvironmentId string
    param repositoryImage string = 'mcr.microsoft.com/azuredocs/containerapps-helloworld:latest'
    param envVars array = []
    param registry string
    param minReplicas int = 1
    param maxReplicas int = 1
    param port int = 80
    param externalIngress bool = false
    param allowInsecure bool = true
    param transport string = 'http'
    param appProtocol string = 'http'
    param registryUsername string
    @secure()
    param registryPassword string

    resource containerApp 'Microsoft.App/containerApps@2022-01-01-preview' ={
    name: name
    location: location
    properties:{
    managedEnvironmentId: containerAppEnvironmentId
    configuration: {
    dapr: {
    enabled: true
    appId: name
    appPort: port
    appProtocol: appProtocol
    }
    activeRevisionsMode: 'single'
    secrets: [
    {
    name: 'container-registry-password'
    value: registryPassword
    }
    ]
    registries: [
    {
    server: registry
    username: registryUsername
    passwordSecretRef: 'container-registry-password'
    }
    ]
    ingress: {
    external: externalIngress
    targetPort: port
    transport: transport
    allowInsecure: allowInsecure
    }
    }
    template: {
    containers: [
    {
    image: repositoryImage
    name: name
    env: envVars
    }
    ]
    scale: {
    minReplicas: minReplicas
    maxReplicas: maxReplicas
    }
    }
    }
    }

    output fqdn string = containerApp.properties.configuration.ingress.fqdn


    Understanding configuration differences with Dapr

    The code for this specific sample application is largely the same whether or not Dapr is integrated. However, even with this simple app, there are a few benefits and configuration differences when using Dapr that are worth exploring.

    In this scenario most of the changes are related to communication between the container apps. However, you can explore the full range of Dapr benefits by reading the Dapr integration with Azure Container Apps article in the conceptual documentation.

    Without Dapr

    Without Dapr the main.bicep template handles wiring up the front-end store app to communicate with the back-end apis by manually managing environment variables. The bicep template retrieves the fully qualified domains (fqdn) of the API apps as output parameters when they are created. Those configurations are then set as environment variables on the store container app.


    # Retrieve environment variables from API container creation
    var frontend_config = [
    {
    name: 'ProductsApi'
    value: 'http://${products.outputs.fqdn}'
    }
    {
    name: 'InventoryApi'
    value: 'http://${inventory.outputs.fqdn}'
    }
    ]

    # create the store api container app, passing in config
    module store 'container_app.bicep' = {
    name: 'store'
    params: {
    name: 'store'
    location: location
    registryPassword: acr.listCredentials().passwords[0].value
    registryUsername: acr.listCredentials().username
    containerAppEnvironmentId: env.outputs.id
    registry: acr.name
    envVars: union(shared_config, frontend_config)
    externalIngress: true
    }
    }

    The environment variables are then retrieved inside of the program class and used to configure the base URLs of the corresponding HTTP clients.


    builder.Services.AddHttpClient("Products", (httpClient) => httpClient.BaseAddress = new Uri(builder.Configuration.GetValue<string>("ProductsApi")));
    builder.Services.AddHttpClient("Inventory", (httpClient) => httpClient.BaseAddress = new Uri(builder.Configuration.GetValue<string>("InventoryApi")));

    With Dapr

    Dapr can be enabled on a container app when it is created, as seen below. This configuration adds a Dapr sidecar to the app to streamline discovery and communication features between the different container apps in your environment.


    # Create the container app with Dapr enabled
    resource containerApp 'Microsoft.App/containerApps@2022-01-01-preview' ={
    name: name
    location: location
    properties:{
    managedEnvironmentId: containerAppEnvironmentId
    configuration: {
    dapr: {
    enabled: true
    appId: name
    appPort: port
    appProtocol: appProtocol
    }
    activeRevisionsMode: 'single'
    secrets: [
    {
    name: 'container-registry-password'
    value: registryPassword
    }
    ]

    # Rest of template omitted for brevity...
    }
    }

    Some of these Dapr features can be surfaced through the program file. You can configure your HttpClient to leverage Dapr configurations when communicating with other apps in your environment.


    // reconfigure code to make requests to Dapr sidecar
    var baseURL = (Environment.GetEnvironmentVariable("BASE_URL") ?? "http://localhost") + ":" + (Environment.GetEnvironmentVariable("DAPR_HTTP_PORT") ?? "3500");
    builder.Services.AddHttpClient("Products", (httpClient) =>
    {
    httpClient.BaseAddress = new Uri(baseURL);
    httpClient.DefaultRequestHeaders.Add("dapr-app-id", "Products");
    });

    builder.Services.AddHttpClient("Inventory", (httpClient) =>
    {
    httpClient.BaseAddress = new Uri(baseURL);
    httpClient.DefaultRequestHeaders.Add("dapr-app-id", "Inventory");
    });


    Clean up resources

    If you're not going to continue to use this application, you can delete the Azure Container Apps and all the associated services by removing the resource group.

    Follow these steps in the Azure portal to remove the resources you created:

    1. In the Azure portal, navigate to the msdocswebappsapi resource group using the left navigation or search bar.
    2. Select the Delete resource group button at the top of the resource group Overview.
    3. Enter the resource group name msdocswebappsapi in the Are you sure you want to delete "msdocswebappsapi" confirmation dialog.
    4. Select Delete.
      The process to delete the resource group may take a few minutes to complete.
    - - + + \ No newline at end of file diff --git a/blog/page/8/index.html b/blog/page/8/index.html index 8f89432061..978a9c7c57 100644 --- a/blog/page/8/index.html +++ b/blog/page/8/index.html @@ -14,13 +14,13 @@ - - + +

    · 9 min read
    Justin Yoo

    Welcome to Day 21 of #30DaysOfServerless!

    We've so far walked through what Azure Event Grid is and how it generally works. Today, let's discuss how Azure Event Grid deals with CloudEvents.


    What We'll Cover


    OK. Let's get started!

    What is CloudEvents?

    Needless to say, events are everywhere. Events come not only from event-driven systems but also from many different systems and devices, including IoT ones like Raspberry PI.

    But the problem is that every event publisher (system/device that creates events) describes their events differently, meaning there is no standard way of describing events. It has caused many issues between systems, mainly from the interoperability perspective.

    1. Consistency: No standard way of describing events resulted in developers having to write their own event handling logic for each event source.
    2. Accessibility: There were no common libraries, tooling and infrastructure to deliver events across systems.
    3. Productivity: The overall productivity decreases because of the lack of the standard format of events.

    Cloud Events Logo

    Therefore, CNCF (Cloud-Native Computing Foundation) has brought up the concept, called CloudEvents. CloudEvents is a specification that commonly describes event data. Conforming any event data to this spec will simplify the event declaration and delivery across systems and platforms and more, resulting in a huge productivity increase.

    How Azure Event Grid brokers CloudEvents

    Before CloudEvents, Azure Event Grid described events in their own way. Therefore, if you want to use Azure Event Grid, you should follow the event format/schema that Azure Event Grid declares. However, not every system/service/application follows the Azure Event Grid schema. Therefore, Azure Event Grid now supports CloudEvents spec as input and output formats.

    Azure Event Grid for Azure

    Take a look at the simple diagram below, which describes how Azure Event Grid captures events raised from various Azure services. In this diagram, Azure Key Vault takes the role of the event source or event publisher, and Azure Logic Apps takes the role of the event handler (I'll discuss Azure Logic Apps as the event handler later in this post). We use Azure Event Grid System Topic for Azure.

    Azure Event Grid for Azure

    Therefore, let's create an Azure Event Grid System Topic that captures events raised from Azure Key Vault when a new version of a secret is added.

    Azure Event Grid System Topic for Key Vault

    As Azure Event Grid makes use of the pub/sub pattern, you need to create the Azure Event Grid Subscription to consume the events. Here's the subscription that uses the Event Grid data format:

    ![Azure Event Grid System Subscription for Key Vault in Event Grid Format][./img/21-cloudevents-via-event-grid-03.png]

    Once you create the subscription, create a new version of the secret on Azure Key Vault. Then, Azure Key Vault raises an event, which is captured in the Event Grid format:

    [
    {
    "id": "6f44b9c0-d37e-40e7-89be-f70a6da291cc",
    "topic": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/rg-aegce-krc/providers/Microsoft.KeyVault/vaults/kv-xxxxxxxx",
    "subject": "hello",
    "eventType": "Microsoft.KeyVault.SecretNewVersionCreated",
    "data": {
    "Id": "https://kv-xxxxxxxx.vault.azure.net/secrets/hello/064dfc082fec463f8d4610ed6118811d",
    "VaultName": "kv-xxxxxxxx",
    "ObjectType": "Secret",
    "ObjectName": "hello",
    "Version": "064dfc082fec463f8d4610ed6118811d",
    "NBF": null,
    "EXP": null
    },
    "dataVersion": "1",
    "metadataVersion": "1",
    "eventTime": "2022-09-21T07:08:09.1234567Z"
    }
    ]

    So, how is it different from the CloudEvents format? Let's take a look. According to the spec, the JSON data in CloudEvents might look like this:

    {
    "id" : "C234-1234-1234",
    "source" : "/mycontext",
    "specversion" : "1.0",
    "type" : "com.example.someevent",
    "comexampleextension1" : "value",
    "time" : "2018-04-05T17:31:00Z",
    "datacontenttype" : "application/cloudevents+json",
    "data" : {
    "appinfoA" : "abc",
    "appinfoB" : 123,
    "appinfoC" : true
    }
    }

    This time, let's create another subscription using the CloudEvents schema. Here's how to create the subscription against the system topic:

    Azure Event Grid System Subscription for Key Vault in CloudEvents Format

    Therefore, Azure Key Vault emits the event data in the CloudEvents format:

    {
    "id": "6f44b9c0-d37e-40e7-89be-f70a6da291cc",
    "source": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/rg-aegce-krc/providers/Microsoft.KeyVault/vaults/kv-xxxxxxxx",
    "specversion": "1.0",
    "type": "Microsoft.KeyVault.SecretNewVersionCreated",
    "subject": "hello",
    "time": "2022-09-21T07:08:09.1234567Z",
    "data": {
    "Id": "https://kv-xxxxxxxx.vault.azure.net/secrets/hello/064dfc082fec463f8d4610ed6118811d",
    "VaultName": "kv-xxxxxxxx",
    "ObjectType": "Secret",
    "ObjectName": "hello",
    "Version": "064dfc082fec463f8d4610ed6118811d",
    "NBF": null,
    "EXP": null
    }
    }

    Can you identify some differences between the Event Grid format and the CloudEvents format? Fortunately, both Event Grid schema and CloudEvents schema look similar to each other. But they might be significantly different if you use a different event source outside Azure.

    Azure Event Grid for Systems outside Azure

    As mentioned above, the event data described outside Azure or your own applications within Azure might not be understandable by Azure Event Grid. In this case, we need to use Azure Event Grid Custom Topic. Here's the diagram for it:

    Azure Event Grid for Applications outside Azure

    Let's create the Azure Event Grid Custom Topic. When you create the topic, make sure that you use the CloudEvent schema during the provisioning process:

    Azure Event Grid Custom Topic

    If your application needs to publish events to Azure Event Grid Custom Topic, your application should build the event data in the CloudEvents format. If you use a .NET application, add the NuGet package first.

    dotnet add package Azure.Messaging.EventGrid

    Then, create the publisher instance. You've already got the topic endpoint URL and the access key.

    var topicEndpoint = new Uri("<Azure Event Grid Custom Topic Endpoint URL>");
    var credential = new AzureKeyCredential("<Azure Event Grid Custom Topic Access Key>");
    var publisher = new EventGridPublisherClient(topicEndpoint, credential);

    Now, build the event data like below. Make sure that you follow the CloudEvents schema that requires additional metadata like event source, event type and content type.

    var source = "/your/event/source";
    var type = "com.source.event.your/OnEventOccurs";

    var data = new MyEventData() { Hello = "World" };

    var @event = new CloudEvent(source, type, data);

    And finally, send the event to Azure Event Grid Custom Topic.

    await publisher.SendEventAsync(@event);

    The captured event data looks like the following:

    {
    "id": "cc2b2775-52b8-43b8-a7cc-c1c33c2b2e59",
    "source": "/your/event/source",
    "type": "com.source.event.my/OnEventOccurs",
    "data": {
    "Hello": "World"
    },
    "time": "2022-09-21T07:08:09.1234567+00:00",
    "specversion": "1.0"
    }

    However, due to limitations, someone might insist that their existing application doesn't or can't emit the event data in the CloudEvents format. In this case, what should we do? There's no standard way of sending the event data in the CloudEvents format to Azure Event Grid Custom Topic. One of the approaches we may be able to apply is to put a converter between the existing application and Azure Event Grid Custom Topic like below:

    Azure Event Grid for Applications outside Azure with Converter

    Once the Function app (or any converter app) receives legacy event data, it internally converts the CloudEvents format and publishes it to Azure Event Grid.

    var data = default(MyRequestData);
    using (var reader = new StreamReader(req.Body))
    {
    var serialised = await reader.ReadToEndAsync();
    data = JsonConvert.DeserializeObject<MyRequestData>(serialised);
    }

    var converted = new MyEventData() { Hello = data.Lorem };
    var @event = new CloudEvent(source, type, converted);

    The converted event data is captured like this:

    {
    "id": "df296da3-77cd-4da2-8122-91f631941610",
    "source": "/your/event/source",
    "type": "com.source.event.my/OnEventOccurs",
    "data": {
    "Hello": "ipsum"
    },
    "time": "2022-09-21T07:08:09.1234567+00:00",
    "specversion": "1.0"
    }

    This approach is beneficial in many integration scenarios to make all the event data canonicalised.

    How Azure Logic Apps consumes CloudEvents

    I put Azure Logic Apps as the event handler in the previous diagrams. According to the CloudEvents spec, each event handler must implement request validation to avoid abuse. One good thing about using Azure Logic Apps is that it has already implemented this request validation feature. It implies that we just subscribe to the topic and consume the event data.

    Create a new Logic Apps instance and add the HTTP Request trigger. Once it saves, you will get the endpoint URL.

    Azure Logic Apps with HTTP Request Trigger

    Then, create the Azure Event Grid Subscription with:

    • Endpoint type: Webhook
    • Endpoint URL: The Logic Apps URL from above.

    Azure Logic Apps with HTTP Request Trigger

    Once the subscription is ready, this Logic Apps works well as the event handler. Here's how it receives the CloudEvents data from the subscription.

    Azure Logic Apps that Received CloudEvents data

    Now you've got the CloudEvents data. It's entirely up to you to handle that event data however you want!

    Exercise: Try this yourself!

    You can fork this GitHub repository to your account and play around with it to see how Azure Event Grid with CloudEvents works. Alternatively, the "Deploy to Azure" button below will provision all necessary Azure resources and deploy an Azure Functions app to mimic the event publisher.

    Deploy To Azure

    Resources: For self-study!

    Want to know more about CloudEvents in real-life examples? Here are several resources you can take a look at:

    - - + + \ No newline at end of file diff --git a/blog/page/9/index.html b/blog/page/9/index.html index 2f7fc63a9c..150adae243 100644 --- a/blog/page/9/index.html +++ b/blog/page/9/index.html @@ -14,15 +14,15 @@ - - + +

    · 10 min read
    Ayca Bas

    Welcome to Day 20 of #30DaysOfServerless!

    Every day millions of people spend their precious time in productivity tools. What if you use data and intelligence behind the Microsoft applications (Microsoft Teams, Outlook, and many other Office apps) to build seamless automations and custom apps to boost productivity?

    In this post, we'll learn how to build a seamless onboarding experience for new employees joining a company with the power of Microsoft Graph, integrated with Event Hubs and Logic Apps!


    What We'll Cover

    • ✨ The power of Microsoft Graph
    • 🖇️ How do Microsoft Graph and Event Hubs work together?
    • 🛠 Let's Build an Onboarding Workflow!
      • 1️⃣ Setup Azure Event Hubs + Key Vault
      • 2️⃣ Subscribe to users, receive change notifications from Logic Apps
      • 3️⃣ Create Onboarding workflow in the Logic Apps
    • 🚀 Debug: Your onboarding experience
    • ✋ Exercise: Try this tutorial out yourself!
    • 📚 Resources: For Self-Study


    ✨ The Power of Microsoft Graph

    Microsoft Graph is the gateway to data and intelligence in Microsoft 365 platform. Microsoft Graph exploses Rest APIs and client libraries to access data across Microsoft 365 core services such as Calendar, Teams, To Do, Outlook, People, Planner, OneDrive, OneNote and more.

    Overview of Microsoft Graph

    You can build custom experiences by using Microsoft Graph such as automating the onboarding process for new employees. When new employees are created in the Azure Active Directory, they will be automatically added in the Onboarding team on Microsoft Teams.

    Solution architecture


    🖇️ Microsoft Graph with Event Hubs

    Microsoft Graph uses a webhook mechanism to track changes in resources and deliver change notifications to the clients. For example, with Microsoft Graph Change Notifications, you can receive change notifications when:

    • a new task is added in the to-do list
    • a user changes the presence status from busy to available
    • an event is deleted/cancelled from the calendar

    If you'd like to track a large set of resources at a high frequency, use Azure Events Hubs instead of traditional webhooks to receive change notifications. Azure Event Hubs is a popular real-time events ingestion and distribution service built for scale.

    EVENT GRID - PARTNER EVENTS

    Microsoft Graph Change Notifications can be also received by using Azure Event Grid -- currently available for Microsoft Partners! Read the Partner Events Overview documentation for details.

    Setup Azure Event Hubs + Key Vault.

    To get Microsoft Graph Change Notifications delivered to Azure Event Hubs, we'll have to setup Azure Event Hubs and Azure Key Vault. We'll use Azure Key Vault to access to Event Hubs connection string.

    1️⃣ Create Azure Event Hubs

    1. Go to Azure Portal and select Create a resource, type Event Hubs and select click Create.
    2. Fill in the Event Hubs namespace creation details, and then click Create.
    3. Go to the newly created Event Hubs namespace page, select Event Hubs tab from the left pane and + Event Hub:
      • Name your Event Hub as Event Hub
      • Click Create.
    4. Click the name of the Event Hub, and then select Shared access policies and + Add to add a new policy:
      • Give a name to the policy
      • Check Send and Listen
      • Click Create.
    5. After the policy has been created, click the name of the policy to open the details panel, and then copy the Connection string-primary key value. Write it down; you'll need it for the next step.
    6. Go to Consumer groups tab in the left pane and select + Consumer group, give a name for your consumer group as onboarding and select Create.

    2️⃣ Create Azure Key Vault

    1. Go to Azure Portal and select Create a resource, type Key Vault and select Create.
    2. Fill in the Key Vault creation details, and then click Review + Create.
    3. Go to newly created Key Vault and select Secrets tab from the left pane and click + Generate/Import:
      • Give a name to the secret
      • For the value, paste in the connection string you generated at the Event Hubs step
      • Click Create
      • Copy the name of the secret.
    4. Select Access Policies from the left pane and + Add Access Policy:
      • For Secret permissions, select Get
      • For Principal, select Microsoft Graph Change Tracking
      • Click Add.
    5. Select Overview tab from the left pane and copy the Vault URI.

    Subscribe for Logic Apps change notifications

    To start receiving Microsoft Graph Change Notifications, we'll need to create subscription to the resource that we'd like to track - here, 'users'. We'll use Azure Logic Apps to create subscription.

    To create subscription for Microsoft Graph Change Notifications, we'll need to make a http post request to https://graph.microsoft.com/v1.0/subscriptions. Microsoft Graph requires Azure Active Directory authentication make API calls. First, we'll need to register an app to Azure Active Directory, and then we will make the Microsoft Graph Subscription API call with Azure Logic Apps.

    1️⃣ Create an app in Azure Active Directory

    1. In the Azure Portal, go to Azure Active Directory and select App registrations from the left pane and select + New registration. Fill in the details for the new App registration form as below:
      • Name: Graph Subscription Flow Auth
      • Supported account types: Accounts in any organizational directory (Any Azure AD directory - Multitenant) and personal Microsoft accounts (e.g. Skype, Xbox)
      • Select Register.
    2. Go to newly registered app in Azure Active Directory, select API permissions:
      • Select + Add a permission and Microsoft Graph
      • Select Application permissions and add User.Read.All and Directory.Read.All.
      • Select Grant admin consent for the organization
    3. Select Certificates & secrets tab from the left pane, select + New client secret:
      • Choose desired expiry duration
      • Select Add
      • Copy the value of the secret.
    4. Go to Overview from the left pane, copy Application (client) ID and Directory (tenant) ID.

    2️⃣ Create subscription with Azure Logic Apps

    1. Go to Azure Portal and select Create a resource, type Logic apps and select click Create.

    2. Fill in the Logic Apps creation details, and then click Create.

    3. Go to the newly created Logic Apps page, select Workflows tab from the left pane and select + Add:

      • Give a name to the new workflow as graph-subscription-flow
      • Select Stateful as a state type
      • Click Create.
    4. Go to graph-subscription-flow, and then select Designer tab.

    5. In the Choose an operation section, search for Schedule and select Recurrence as a trigger. Fill in the parameters as below:

      • Interval: 61
      • Frequency: Minute
      • Time zone: Select your own time zone
      • Start time: Set a start time
    6. Select + button in the flow and select add an action. Search for HTTP and select HTTP as an action. Fill in the parameters as below:

      • Method: POST
      • URI: https://graph.microsoft.com/v1.0/subscriptions
      • Headers:
        • Key: Content-type
        • Value: application/json
      • Body:
      {
      "changeType": "created, updated",
      "clientState": "secretClientValue",
      "expirationDateTime": "@{addHours(utcNow(), 1)}",
      "notificationUrl": "EventHub:https://<YOUR-VAULT-URI>/secrets/<YOUR-KEY-VAULT-SECRET-NAME>?tenantId=72f988bf-86f1-41af-91ab-2d7cd011db47",
      "resource": "users"
      }

      In notificationUrl, make sure to replace <YOUR-VAULT-URI> with the vault uri and <YOUR-KEY-VAULT-SECRET-NAME> with the secret name that you copied from the Key Vault.

      In resource, define the resource type you'd like to track changes. For our example, we will track changes for users resource.

      • Authentication:
        • Authentication type: Active Directory OAuth
        • Authority: https://login.microsoft.com
        • Tenant: Directory (tenant) ID copied from AAD app
        • Audience: https://graph.microsoft.com
        • Client ID: Application (client) ID copied from AAD app
        • Credential Type: Secret
        • Secret: value of the secret copied from AAD app
    7. Select Save and run your workflow from the Overview tab.

      Check your subscription in Graph Explorer: If you'd like to make sure that your subscription is created successfully by Logic Apps, you can go to Graph Explorer, login with your Microsoft 365 account and make GET request to https://graph.microsoft.com/v1.0/subscriptions. Your subscription should appear in the response after it's created successfully.

    Subscription workflow success

    After subscription is created successfully by Logic Apps, Azure Event Hubs will receive notifications whenever there is a new user created in Azure Active Directory.


    Create Onboarding workflow in Logic Apps

    We'll create a second workflow in the Logic Apps to receive change notifications from Event Hubs when there is a new user created in the Azure Active Directory and add new user in Onboarding team on Microsoft Teams.

    1. Go to the Logic Apps you created in the previous steps, select Workflows tab and create a new workflow by selecting + Add:
      • Give a name to the new workflow as teams-onboarding-flow
      • Select Stateful as a state type
      • Click Create.
    2. Go to teams-onboarding-flow, and then select Designer tab.
    3. In the Choose an operation section, search for Event Hub, select When events are available in Event Hub as a trigger. Setup Event Hub connection as below:
      • Create Connection:
        • Connection name: Connection
        • Authentication Type: Connection String
        • Connection String: Go to Event Hubs > Shared Access Policies > RootManageSharedAccessKey and copy Connection string–primary key
        • Select Create.
      • Parameters:
        • Event Hub Name: Event Hub
        • Consumer Group Name: onboarding
    4. Select + in the flow and add an action, search for Control and add For each as an action. Fill in For each action as below:
      • Select output from previous steps: Events
    5. Inside For each, select + in the flow and add an action, search for Data operations and select Parse JSON. Fill in Parse JSON action as below:
      • Content: Events Content
      • Schema: Copy the json content from schema-parse.json and paste as a schema
    6. Select + in the flow and add an action, search for Control and add For each as an action. Fill in For each action as below:
      • Select output from previous steps: value
      1. Inside For each, select + in the flow and add an action, search for Microsoft Teams and select Add a member to a team. Login with your Microsoft 365 account to create a connection and fill in Add a member to a team action as below:
      • Team: Create an Onboarding team on Microsoft Teams and select
      • A user AAD ID for the user to add to a team: id
    7. Select Save.

    🚀 Debug your onboarding experience

    To debug our onboarding experience, we'll need to create a new user in Azure Active Directory and see if it's added in Microsoft Teams Onboarding team automatically.

    1. Go to Azure Portal and select Azure Active Directory from the left pane and go to Users. Select + New user and Create new user. Fill in the details as below:

      • User name: JaneDoe
      • Name: Jane Doe

      new user in Azure Active Directory

    2. When you added Jane Doe as a new user, it should trigger the teams-onboarding-flow to run. teams onboarding flow success

    3. Once the teams-onboarding-flow runs successfully, you should be able to see Jane Doe as a member of the Onboarding team on Microsoft Teams! 🥳 new member in Onboarding team on Microsoft Teams

    Congratulations! 🎉

    You just built an onboarding experience using Azure Logic Apps, Azure Event Hubs and Azure Key Vault.


    📚 Resources

    - - + + \ No newline at end of file diff --git a/blog/serverless-status-post/index.html b/blog/serverless-status-post/index.html index 92fca79785..40c1e17951 100644 --- a/blog/serverless-status-post/index.html +++ b/blog/serverless-status-post/index.html @@ -14,14 +14,14 @@ - - + +

    Serverless September - In a Nutshell

    · 7 min read
    Devanshi Joshi

    It's Serverless September in a Nutshell! Join us as we unpack our month-long learning journey exploring the core technology pillars for Serverless architectures on Azure. Then end with a look at next steps to build your Cloud-native applications on Azure.


    What We'll Cover

    • Functions-as-a-Service (FaaS)
    • Microservices and Containers
    • Serverless Integrations
    • End-to-End Solutions
    • Developer Tools & #Hacktoberfest

    Banner for Serverless September


    Building Cloud-native Apps

    By definition, cloud-native technologies empower organizations to build and run scalable applications in modern, dynamic environments such as public, private, and hybrid clouds. You can learn more about cloud-native in Kendall Roden's #ServerlessSeptember post on Going Cloud-native with Azure Container Apps.

    Serveless technologies accelerate productivity and minimize costs for deploying applications at cloud scale. So, what can we build with serverless technologies in cloud-native on Azure? Anything that is event-driven - examples include:

    • Microservices - scaled by KEDA-compliant triggers
    • Public API Endpoints - scaled by #concurrent HTTP requests
    • Event-Driven Applications - scaled by length of message queue
    • Web Applications - scaled by #concurrent HTTP requests
    • Background Process - scaled by CPU and Memory usage

    Great - but as developers, we really want to know how we can get started building and deploying serverless solutions on Azure. That was the focus of our #ServerlessSeptember journey. Let's take a quick look at the four key themes.

    Functions-as-a-Service (FaaS)

    Functions-as-a-Service (FaaS) is the epitome of developer productivity for full-stack modern apps. As developers, you don't manage infrastructure and focus only on business logic and application code. And, with Serverless Compute you only pay for when your code runs - making this the simplest first step to begin migrating your application to cloud-native.

    In Azure, FaaS is provided by Azure Functions. Check out our Functions + Serverless on Azure to go from learning core concepts, to building your first Functions app in your programming language of choice. Azure functions support multiple programming languages including C#, F#, Java, JavaScript, Python, Typescript, and PowerShell.

    Want to get extended language support for languages like Go, and Rust? You can Use Custom Handlers to make this happen! But what if you want to have long-running functions, or create complex workflows involving more than one function? Read our post on Durable Entities to learn how you can orchestrate this with Azure Functions.

    Check out this recent AskTheExpert Q&A session with the Azure Functions team to get answers to popular community questions on Azure Functions features and usage.

    Microservices and Containers

    Functions-as-a-Service is an ideal first step towards serverless development. But Functions are just one of the 5 pillars of cloud-native. This week we'll look at two of the other pillars: microservices and containers - with specific focus on two core technologies: Azure Container Apps and Dapr (Distributed Application Runtime).

    In this 6-part series of posts, we walk through each technology independently, before looking at the value of building Azure Container Apps with Dapr.

    • In Hello Container Apps we learned core concepts & deployed our first ACA.
    • In Microservices Communication we learned about ACA environments and virtual networks, and how microservices communicate in ACA with a hands-on tutorial.
    • In Scaling Your Container Apps we learned about KEDA (Kubernetes Event-Driven Autoscaler) and configuring ACA for autoscaling with KEDA-compliant triggers.
    • In Build with Dapr we introduced the Distributed Application Runtime (Dapr), exploring its Building Block APIs and sidecar architecture for working with ACA.
    • In Secure ACA Access we learned how to secure ACA access to external services with - and without - Dapr, covering Secret Stores and Managed Identity.
    • Finally, Build ACA with Dapr tied it all together with a enterprise app scenario where an orders processor (ACA) uses Dapr APIs (PubSub, State Management) to receive and store order messages from Azure Service Bus.

    Build ACA with Dapr

    Check out this recent AskTheExpert Q&A session with the Azure Container Apps team for answers to popular community questions on core features and usage.

    Serverless Integrations

    In the first half of the month we looked at compute resources for building and deploying serverless applications. In the second half, we look at integration tools and resources that automate developer workflows to streamline the end-to-end developer experience.

    In Azure, this is enabled by services like Azure Logic Apps and Azure Event Grid. Azure Logic Apps provides a visual designer to create and automate workflows with little or no code involved. Azure Event Grid provides a highly-scable event broker with support for pub/sub communications to drive async event-driven architectures.

    • In Tracking Weather Data Changes With Logic Apps we look at how you can use Logic Apps to integrate the MSN weather service with Azure CosmosDB, allowing automated collection of weather data on changes.

    • In Teach the Cloud to Read & Categorize Mail we take it a step further, using Logic Apps to automate a workflow that includes a Computer Vision service to "read" images and store the results to CosmosDB.

    • In Integrate with Microsoft Graph we explore a multi-cloud scenario (Azure + M365) where change notifications from Microsoft Graph can be integrated using Logic Apps and Event Hubs to power an onboarding workflow.

    • In Cloud Events with Event Grid we learn about the CloudEvents specification (for consistently describing event data) - and learn how Event Grid brokers events in this format. Azure Logic Apps can be an Event handler (subscriber) that uses the event to trigger an automated workflow on receipt.

      Azure Event Grid And Logic Apps

    Want to explore other such integrations? Browse Azure Architectures and filter by selected Azure services for more real-world scenarios.


    End-to-End Solutions

    We've covered serverless compute solutions (for building your serverless applications) and serverless integration services to automate end-to-end workflows in synchronous or asynchronous event-driven architectures. In this final week, we want to leave you with a sense of end-to-end development tools and use cases that can be enabled by Serverless on Azure. Here are some key examples:

    ArticleDescription
    In this tutorial, you'll learn to deploy a containerized ASP.NET Core 6.0 application to Azure Container Apps - with a Blazor front-end and two Web API projects
    Deploy Java containers to cloudIn this tutorial you learn to build and deploy a Java application running on Spring Boot, by publishing it in a container to Azure Container Registry, then deploying to Azure Container Apps,, from ACR, via the Azure Portal.
    **Where am I? My GPS Location with Serverless Power Platform Custom Connector**In this step-by-step tutorial you learn to integrate a serverless application (built on Azure Functions and OpenAPI) with Power Platforms custom connectors via Azure API Management (API-M).This pattern can empower a new ecosystem of fusion apps for cases like inventory management.
    And in our Serverless Hacks initiative, we walked through an 8-step hack to build a serverless tollbooth. Check out this 12-part video walkthrough of a reference solution using .NET.

    Developer Tools

    But wait - there's more. Those are a sample of the end-to-end application scenarios that are built on serverless on Azure. But what about the developer experience? In this article, we say hello to the Azure Developer CLI - an open-source tool that streamlines your develop-deploy workflow, with simple commands that map to core stages of your development journey. Go from code to cloud with one CLI

    And watch this space for more such tutorials and content through October, including a special #Hacktoberfest focused initiative to encourage and support first-time contributors to open-source. Here's a sneak peek at the project we plan to share - the new awesome-azd templates gallery.


    Join us at Microsoft Ignite!

    Want to continue your learning journey, and learn about what's next for Serverless on Azure? Microsoft Ignite happens Oct 12-14 this year and has multiple sessions on relevant technologies and tools. Check out the Session Catalog and register here to attend online.

    - - + + \ No newline at end of file diff --git a/blog/students/index.html b/blog/students/index.html index 5b58675aa2..104588dc99 100644 --- a/blog/students/index.html +++ b/blog/students/index.html @@ -14,13 +14,13 @@ - - + +

    Welcome Students!

    · 3 min read
    Sara Gibbons

    ✨ Serverless September For Students

    My love for the tech industry grows as it evolves. Not just for the new technologies to play with, but seeing how paths into a tech career continue to expand. Allowing so many new voices, ideas and perspectives to our industry. With serverless computing removing barriers of entry for so many.

    It's a reason I enjoy working with universities and students. I get to hear the excitement of learning, fresh ideas and perspectives from our student community. All you students are incredible! How you view serverless, and what it can do, so cool!

    This year for Serverless September we want to hear all the amazing ways our student community is learning and working with Azure Serverless, and have all new ways for you to participate.

    Getting Started

    If you don't already have an Azure for Students account you can easily get your FREE account created at Azure for Students Sign up.

    If you are new to serverless, here are a couple links to get you started:

    No Experience, No problem

    For Serverless September we have planned beginner friendly content all month long. Covering such services as:

    You can follow #30DaysOfServerles here on the blog for daily posts covering concepts, scenarios, and how to create end-to-end solutions.

    Join the Cloud Skills Challenge where we have selected a list of Learn Modules for you to go through at your own pace, including deploying a full stack application with Azure Static Web Apps.

    Have A Question

    We want to hear it! All month long we will have Ask The Expert sessions. Submit your questions at any time and will be be sure to get one of our Azure Serverless experts to get you an answer.

    Share What You've Created

    If you have written a blog post, recorded a video, have an open source Azure Serverless project, we'd love to see it! Here is some links for you to share your creations

    🧭 Explore Student Resources

    ⚡️ Join us!

    Multiple teams across Microsoft are working to create Serverless September! They all want to hear from our incredible student community. We can't wait to share all the Serverless September resources and hear what you have learned and created. Here are some ways to keep up to date on all Serverless September activity:

    - - + + \ No newline at end of file diff --git a/blog/tags/30-days-of-serverless/index.html b/blog/tags/30-days-of-serverless/index.html index 3ed2b44a86..17b6ef60f4 100644 --- a/blog/tags/30-days-of-serverless/index.html +++ b/blog/tags/30-days-of-serverless/index.html @@ -14,8 +14,8 @@ - - + +
    @@ -26,7 +26,7 @@

    ...and that's it! We've successfully deployed our application on Azure!

    But there's more!

    Best practices: Monitoring and CI/CD!

    In my opinion, it's not enough to just set up the application on Azure! I want to know that my web app is performant and serving my users reliably! I also want to make sure that I'm not inadvertently breaking my application as I continue to make changes to it. Thankfully, the Azure Developer CLI also handles all of this via two additional commands - azd monitor and azd pipeline config.

    Application Monitoring

    When we provisioned all of our infrastructure, we also set up application monitoring via a Bicep file in our .infra/ directory that spec'd out an Application Insights dashboard. By running azd monitor we can see the dashboard with live metrics that was configured for the application.

    We can also navigate to the Application Dashboard by clicking on the resource group name, where you can set a specific refresh rate for the dashboard, and see usage, reliability, and performance metrics over time.

    I don't know about everyone else but I have spent a ton of time building out similar dashboards. It can be super time-consuming to write all the queries and create the visualizations so this feels like a real time saver.

    CI/CD

    Finally let's talk about setting up CI/CD! This might be my favorite azd feature. As I mentioned before, the Azure Developer CLI has a command, azd pipeline config, which uses the files in the .github/ directory to set up a GitHub Action. More than that, if there is no upstream repo, the Developer CLI will actually help you create one. But what does this mean exactly? Because our GitHub Action is using the same commands you'd run in the CLI under the hood, we're actually going to have CI/CD set up to run on every commit into the repo, against real Azure resources. What a sweet collaboration feature!

    That's it! We've gone end-to-end with the Azure Developer CLI - initialized a project, provisioned the resources on Azure, deployed our code on Azure, set up monitoring logs and dashboards, and set up a CI/CD pipeline with GitHub Actions to run on every commit into the repo (on real Azure resources!).

    Exercise: Try it yourself or create your own template!

    As an exercise, try out the workflow above with any template on GitHub!

    Or, try turning your own project into an Azure Developer CLI-enabled template by following this guidance. If you create your own template, don't forget to tag the repo with the azd-templates topic on GitHub to help others find it (unfamiliar with GitHub topics? Learn how to add topics to your repo)! We'd also love to chat with you about your experience creating an azd template - if you're open to providing feedback around this, please fill out this form!

    Resources

    - - + + \ No newline at end of file diff --git a/blog/tags/30-days-of-serverless/page/10/index.html b/blog/tags/30-days-of-serverless/page/10/index.html index b05202e2ce..d09aaffea5 100644 --- a/blog/tags/30-days-of-serverless/page/10/index.html +++ b/blog/tags/30-days-of-serverless/page/10/index.html @@ -14,14 +14,14 @@ - - + +

    20 posts tagged with "30-days-of-serverless"

    View All Tags

    · 11 min read
    Kendall Roden

    Welcome to Day 13 of #30DaysOfServerless!

    In the previous post, we learned about all things Distributed Application Runtime (Dapr) and highlighted the capabilities you can unlock through managed Dapr in Azure Container Apps! Today, we'll dive into how we can make use of Container Apps secrets and managed identities to securely access cloud-hosted resources that your Container Apps depend on!

    Ready? Let's go.


    What We'll Cover

    • Secure access to external services overview
    • Using Container Apps Secrets
    • Using Managed Identity for connecting to Azure resources
    • Using Dapr secret store component references (Dapr-only)
    • Conclusion
    • Resources: For self-study!


    Securing access to external services

    In most, if not all, microservice-based applications, one or more services in the system will rely on other cloud-hosted resources; Think external services like databases, secret stores, message brokers, event sources, etc. To interact with these services, an application must have the ability to establish a secure connection. Traditionally, an application will authenticate to these backing resources using some type of connection string or password.

    I'm not sure if it was just me, but one of the first things I learned as a developer was to ensure credentials and other sensitive information were never checked into the codebase. The ability to inject these values at runtime is a non-negotiable.

    In Azure Container Apps, applications can securely leverage connection information via Container Apps Secrets. If the resource is Azure-based, a more ideal solution that removes the dependence on secrets altogether is using Managed Identity.

    Specifically for Dapr-enabled container apps, users can now tap into the power of the Dapr secrets API! With this new capability unlocked in Container Apps, users can call the Dapr secrets API from application code to securely access secrets from Key Vault or other backing secret stores. In addition, customers can also make use of a secret store component reference when wiring up Dapr state store components and more!

    ALSO, I'm excited to share that support for Dapr + Managed Identity is now available!!. What does this mean? It means that you can enable Managed Identity for your container app - and when establishing connections via Dapr, the Dapr sidecar can use this identity! This means simplified components without the need for secrets when connecting to Azure services!

    Let's dive a bit deeper into the following three topics:

    1. Using Container Apps secrets in your container apps
    2. Using Managed Identity to connect to Azure services
    3. Connecting to services securely for Dapr-enabled apps

    Secure access to external services without Dapr

    Leveraging Container Apps secrets at runtime

    Users can leverage this approach for any values which need to be securely stored, however, it is recommended to use Managed Identity where possible when connecting to Azure-specific resources.

    First, let's establish a few important points regarding secrets in container apps:

    • Secrets are scoped at the container app level, meaning secrets cannot be shared across container apps today
    • When running in multiple-revision mode,
      • changes to secrets do not generate a new revision
      • running revisions will not be automatically restarted to reflect changes. If you want to force-update existing container app revisions to reflect the changed secrets values, you will need to perform revision restarts.
    STEP 1

    Provide the secure value as a secret parameter when creating your container app using the syntax "SECRET_NAME=SECRET_VALUE"

    az containerapp create \
    --resource-group "my-resource-group" \
    --name queuereader \
    --environment "my-environment-name" \
    --image demos/queuereader:v1 \
    --secrets "queue-connection-string=$CONNECTION_STRING"
    STEP 2

    Create an environment variable which references the value of the secret created in step 1 using the syntax "ENV_VARIABLE_NAME=secretref:SECRET_NAME"

    az containerapp create \
    --resource-group "my-resource-group" \
    --name myQueueApp \
    --environment "my-environment-name" \
    --image demos/myQueueApp:v1 \
    --secrets "queue-connection-string=$CONNECTIONSTRING" \
    --env-vars "QueueName=myqueue" "ConnectionString=secretref:queue-connection-string"

    This ConnectionString environment variable can be used within your application code to securely access the connection string value at runtime.

    Using Managed Identity to connect to Azure services

    A managed identity from Azure Active Directory (Azure AD) allows your container app to access other Azure AD-protected resources. This approach is recommended where possible as it eliminates the need for managing secret credentials in your container apps and allows you to properly scope the permissions needed for a given container app using role-based access control. Both system-assigned and user-assigned identities are available in container apps. For more background on managed identities in Azure AD, see Managed identities for Azure resources.

    To configure your app with a system-assigned managed identity you will follow similar steps to the following:

    STEP 1

    Run the following command to create a system-assigned identity for your container app

    az containerapp identity assign \
    --name "myQueueApp" \
    --resource-group "my-resource-group" \
    --system-assigned
    STEP 2

    Retrieve the identity details for your container app and store the Principal ID for the identity in a variable "PRINCIPAL_ID"

    az containerapp identity show \
    --name "myQueueApp" \
    --resource-group "my-resource-group"
    STEP 3

    Assign the appropriate roles and permissions to your container app's managed identity using the Principal ID in step 2 based on the resources you need to access (example below)

    az role assignment create \
    --role "Storage Queue Data Contributor" \
    --assignee $PRINCIPAL_ID \
    --scope "/subscriptions/<subscription>/resourceGroups/<resource-group>/providers/Microsoft.Storage/storageAccounts/<storage-account>/queueServices/default/queues/<queue>"

    After running the above commands, your container app will be able to access your Azure Store Queue because it's managed identity has been assigned the "Store Queue Data Contributor" role. The role assignments you create will be contingent solely on the resources your container app needs to access. To instrument your code to use this managed identity, see more details here.

    In addition to using managed identity to access services from your container app, you can also use managed identity to pull your container images from Azure Container Registry.

    Secure access to external services with Dapr

    For Dapr-enabled apps, there are a few ways to connect to the resources your solutions depend on. In this section, we will discuss when to use each approach.

    1. Using Container Apps secrets in your Dapr components
    2. Using Managed Identity with Dapr Components
    3. Using Dapr Secret Stores for runtime secrets and component references

    Using Container Apps secrets in Dapr components

    Prior to providing support for the Dapr Secret's Management building block, this was the only approach available for securely storing sensitive values for use in Dapr components.

    In Dapr OSS, when no secret store reference is provided in a Dapr component file, the default secret store is set to "Kubernetes secrets". In Container Apps, we do not expose the ability to use this default store. Rather, Container Apps secrets can be used in it's place.

    With the introduction of the Secrets API and the ability to use Dapr + Managed Identity, this approach is useful for a limited number of scenarios:

    • Quick demos and dev/test scenarios using the Container Apps CLI
    • Securing values when a secret store is not configured or available for use
    • Using service principal credentials to configure an Azure Key Vault secret store component (Using Managed Identity is recommend)
    • Securing access credentials which may be required when creating a non-Azure secret store component
    STEP 1

    Create a Dapr component which can be used by one or more services in the container apps environment. In the below example, you will create a secret to store the storage account key and reference this secret from the appropriate Dapr metadata property.

       componentType: state.azure.blobstorage
    version: v1
    metadata:
    - name: accountName
    value: testStorage
    - name: accountKey
    secretRef: account-key
    - name: containerName
    value: myContainer
    secrets:
    - name: account-key
    value: "<STORAGE_ACCOUNT_KEY>"
    scopes:
    - myApp
    STEP 2

    Deploy the Dapr component using the below command with the appropriate arguments.

     az containerapp env dapr-component set \
    --name "my-environment" \
    --resource-group "my-resource-group" \
    --dapr-component-name statestore \
    --yaml "./statestore.yaml"

    Using Managed Identity with Dapr Components

    Dapr-enabled container apps can now make use of managed identities within Dapr components. This is the most ideal path for connecting to Azure services securely, and allows for the removal of sensitive values in the component itself.

    The Dapr sidecar makes use of the existing identities available within a given container app; Dapr itself does not have it's own identity. Therefore, the steps to enable Dapr + MI are similar to those in the section regarding managed identity for non-Dapr apps. See example steps below specifically for using a system-assigned identity:

    1. Create a system-assigned identity for your container app

    2. Retrieve the identity details for your container app and store the Principal ID for the identity in a variable "PRINCIPAL_ID"

    3. Assign the appropriate roles and permissions (for accessing resources backing your Dapr components) to your ACA's managed identity using the Principal ID

    4. Create a simplified Dapr component without any secrets required

          componentType: state.azure.blobstorage
      version: v1
      metadata:
      - name: accountName
      value: testStorage
      - name: containerName
      value: myContainer
      scopes:
      - myApp
    5. Deploy the component to test the connection from your container app via Dapr!

    Keep in mind, all Dapr components will be loaded by each Dapr-enabled container app in an environment by default. In order to avoid apps without the appropriate permissions from loading a component unsuccessfully, use scopes. This will ensure that only applications with the appropriate identities to access the backing resource load the component.

    Using Dapr Secret Stores for runtime secrets and component references

    Dapr integrates with secret stores to provide apps and other components with secure storage and access to secrets such as access keys and passwords. The Dapr Secrets API is now available for use in Container Apps.

    Using Dapr’s secret store building block typically involves the following:

    • Setting up a component for a specific secret store solution.
    • Retrieving secrets using the Dapr secrets API in the application code.
    • Optionally, referencing secrets in Dapr component files.

    Let's walk through a couple sample workflows involving the use of Dapr's Secrets Management capabilities!

    Setting up a component for a specific secret store solution

    1. Create an Azure Key Vault instance for hosting the secrets required by your application.

      az keyvault create --name "<your-unique-keyvault-name>" --resource-group "my-resource-group" --location "<your-location>"
    2. Create an Azure Key Vault component in your environment without the secrets values, as the connection will be established to Azure Key Vault via Managed Identity.

          componentType: secretstores.azure.keyvault
      version: v1
      metadata:
      - name: vaultName
      value: "[your_keyvault_name]"
      scopes:
      - myApp
      az containerapp env dapr-component set \
      --name "my-environment" \
      --resource-group "my-resource-group" \
      --dapr-component-name secretstore \
      --yaml "./secretstore.yaml"
    3. Run the following command to create a system-assigned identity for your container app

      az containerapp identity assign \
      --name "myApp" \
      --resource-group "my-resource-group" \
      --system-assigned
    4. Retrieve the identity details for your container app and store the Principal ID for the identity in a variable "PRINCIPAL_ID"

      az containerapp identity show \
      --name "myApp" \
      --resource-group "my-resource-group"
    5. Assign the appropriate roles and permissions to your container app's managed identity to access Azure Key Vault

      az role assignment create \
      --role "Key Vault Secrets Officer" \
      --assignee $PRINCIPAL_ID \
      --scope /subscriptions/{subscriptionid}/resourcegroups/{resource-group-name}/providers/Microsoft.KeyVault/vaults/{key-vault-name}
    6. Begin using the Dapr Secrets API in your application code to retrieve secrets! See additional details here.

    Referencing secrets in Dapr component files

    Once a Dapr secret store component is available in the environment, it can be used to retrieve secrets for use in other components. For example, when creating a state store component, you can add a reference to the Dapr secret store from which you would like to source connection information. You will no longer use secrets directly in the component spec, but rather will instruct the Dapr sidecar to retrieve the secrets from the specified store.

          componentType: state.azure.blobstorage
    version: v1
    metadata:
    - name: accountName
    value: testStorage
    - name: accountKey
    secretRef: account-key
    - name: containerName
    value: myContainer
    secretStoreComponent: "<SECRET_STORE_COMPONENT_NAME>"
    scopes:
    - myApp

    Summary

    In this post, we have covered the high-level details on how to work with secret values in Azure Container Apps for both Dapr and Non-Dapr apps. In the next article, we will walk through a complex Dapr example from end-to-end which makes use of the new support for Dapr + Managed Identity. Stayed tuned for additional documentation around Dapr secrets as it will be release in the next two weeks!

    Resources

    Here are the main resources to explore for self-study:

    - - + + \ No newline at end of file diff --git a/blog/tags/30-days-of-serverless/page/11/index.html b/blog/tags/30-days-of-serverless/page/11/index.html index de2d64f127..fc4c9c9a73 100644 --- a/blog/tags/30-days-of-serverless/page/11/index.html +++ b/blog/tags/30-days-of-serverless/page/11/index.html @@ -14,13 +14,13 @@ - - + +

    20 posts tagged with "30-days-of-serverless"

    View All Tags

    · 8 min read
    Nitya Narasimhan

    Welcome to Day 12 of #30DaysOfServerless!

    So far we've looked at Azure Container Apps - what it is, how it enables microservices communication, and how it enables auto-scaling with KEDA compliant scalers. Today we'll shift gears and talk about Dapr - the Distributed Application Runtime - and how it makes microservices development with ACA easier with core building blocks and a sidecar architecture!

    Ready? Let's go!


    What We'll Cover

    • What is Dapr and why use it?
    • Building Block APIs
    • Dapr Quickstart and Tutorials
    • Dapr-enabled ACA: A Sidecar Approach
    • Exercise: Build & Deploy a Dapr-enabled ACA.
    • Resources: For self-study!


    Hello, Dapr!

    Building distributed applications is hard. Building reliable and portable microservces means having middleware that deals with challenges like service discovery, sync and async communications, state management, secure information sharing and more. Integrating these support services into your application can be challenging from both development and maintenance perspectives, adding complexity that is independent of the core application logic you want to focus on.

    This is where Dapr (Distributed Application Runtime) shines - it's defined as::

    a portable, event-driven runtime that makes it easy for any developer to build resilient, stateless and stateful applications that run on the cloud and edge and embraces the diversity of languages and developer frameworks.

    But what does this actually mean to me as an app developer?


    Dapr + Apps: A Sidecar Approach

    The strength of Dapr lies in its ability to:

    • abstract complexities of distributed systems middleware - with Building Block APIs that implement components using best practices to tackle key challenges.
    • implement a Sidecar Pattern with interactions via APIs - allowing applications to keep their codebase clean and focus on app logic.
    • be Incrementally Adoptable - allowing developers to start by integrating one API, then evolving to use more as and when needed.
    • be Platform Agnostic - allowing applications to be developed in a preferred language or framework without impacting integration capabilities.

    The application-dapr sidecar interaction is illustrated below. The API abstraction allows applications to get the desired functionality without having to know how it was implemented, or without having to integrate Dapr-specific code into their codebase. Note how the sidecar process listens on port 3500 and the API provides clear routes for the specific building blocks supported by Dapr (e.g, /secrets, /state etc.)


    Dapr Building Blocks: API Interactions

    Dapr Building Blocks refers to HTTP and gRPC endpoints exposed by Dapr API endpoints exposed by the Dapr sidecar, providing key capabilities like state management, observability, service-to-service invocation, pub/sub messaging and more to the associated application.

    Building Blocks: Under the Hood
    The Dapr API is implemented by modular components that codify best practices for tackling the specific challenge that they represent. The API abstraction allows component implementations to evolve, or alternatives to be used , without requiring changes to the application codebase.

    The latest Dapr release has the building blocks shown in the above figure. Not all capabilities are available to Azure Container Apps by default - check the documentation for the latest updates on this. For now, Azure Container Apps + Dapr integration provides the following capabilities to the application:

    In the next section, we'll dive into Dapr-enabled Azure Container Apps. Before we do that, here are a couple of resources to help you explore the Dapr platform by itself, and get more hands-on experience with the concepts and capabilities:

    • Dapr Quickstarts - build your first Dapr app, then explore quickstarts for a core APIs including service-to-service invocation, pub/sub, state mangement, bindings and secrets management.
    • Dapr Tutorials - go beyond the basic quickstart and explore more realistic service integrations and usage scenarios. Try the distributed calculator example!

    Integrate Dapr & Azure Container Apps

    Dapr currently has a v1.9 (preview) version, but Azure Container Apps supports Dapr v1.8. In this section, we'll look at what it takes to enable, configure, and use, Dapr integration with Azure Container Apps. It involves 3 steps: enabling Dapr using settings, configuring Dapr components (API) for use, then invoking the APIs.

    Here's a simple a publisher-subscriber scenario from the documentation. We have two Container apps identified as publisher-app and subscriber-app deployed in a single environment. Each ACA has an activated daprd sidecar, allowing them to use the Pub/Sub API to communicate asynchronously with each other - without having to write the underlying pub/sub implementation themselves. Rather, we can see that the Dapr API uses a pubsub,azure.servicebus component to implement that capability.

    Pub/sub example

    Let's look at how this is setup.

    1. Enable Dapr in ACA: Settings

    We can enable Dapr integration in the Azure Container App during creation by specifying settings in one of two ways, based on your development preference:

    • Using Azure CLI: use custom commandline options for each setting
    • Using Infrastructure-as-Code (IaC): using properties for Bicep, ARM templates

    Once enabled, Dapr will run in the same environment as the Azure Container App, and listen on port 3500 for API requests. The Dapr sidecar can be shared my multiple Container Apps deployed in the same environment.

    There are four main settings we will focus on for this demo - the example below shows the ARM template properties, but you can find the equivalent CLI parameters here for comparison.

    • dapr.enabled - enable Dapr for Azure Container App
    • dapr.appPort - specify port on which app is listening
    • dapr.appProtocol - specify if using http (default) or gRPC for API
    • dapr.appId - specify unique application ID for service discovery, usage

    These are defined under the properties.configuration section for your resource. Changing Dapr settings does not update the revision but it will restart ACA revisions and replicas. Here is what the relevant section of the ARM template looks like for the publisher-app ACA in the scenario shown above.

    "dapr": {
    "enabled": true,
    "appId": "publisher-app",
    "appProcotol": "http",
    "appPort": 80
    }

    2. Configure Dapr in ACA: Components

    The next step after activating the Dapr sidecar, is to define the APIs that you want to use and potentially specify the Dapr components (specific implementations of that API) that you prefer. These components are created at environment-level and by default, Dapr-enabled containers apps in an environment will load the complete set of deployed components -- use the scopes property to ensure only components needed by a given app are loaded at runtime. Here's what the ARM template resources section looks like for the example above. This tells us that the environment has a dapr-pubsub component of type pubsub.azure.servicebus deployed - where that component is loaded by container apps with dapr ids (publisher-app, subscriber-app).

    USING MANAGED IDENTITY + DAPR

    The secrets approach used here is idea for demo purposes. However, we recommend using Managed Identity with Dapr in production. For more details on secrets, check out tomorrow's post on Secrets and Managed Identity in Azure Container Apps

    {
    "resources": [
    {
    "type": "daprComponents",
    "name": "dapr-pubsub",
    "properties": {
    "componentType": "pubsub.azure.servicebus",
    "version": "v1",
    "secrets": [
    {
    "name": "sb-root-connectionstring",
    "value": "value"
    }
    ],
    "metadata": [
    {
    "name": "connectionString",
    "secretRef": "sb-root-connectionstring"
    }
    ],
    // Application scopes
    "scopes": ["publisher-app", "subscriber-app"]

    }
    }
    ]
    }

    With this configuration, the ACA is now set to use pub/sub capabilities from the Dapr sidecar, using standard HTTP requests to the exposed API endpoint for this service.

    Exercise: Deploy Dapr-enabled ACA

    In the next couple posts in this series, we'll be discussing how you can use the Dapr secrets API and doing a walkthrough of a more complex example, to show how Dapr-enabled Azure Container Apps are created and deployed.

    However, you can get hands-on experience with these concepts by walking through one of these two tutorials, each providing an alternative approach to configure and setup the application describe in the scenario below:

    Resources

    Here are the main resources to explore for self-study:

    - - + + \ No newline at end of file diff --git a/blog/tags/30-days-of-serverless/page/12/index.html b/blog/tags/30-days-of-serverless/page/12/index.html index c268d059c8..3714b56263 100644 --- a/blog/tags/30-days-of-serverless/page/12/index.html +++ b/blog/tags/30-days-of-serverless/page/12/index.html @@ -14,13 +14,13 @@ - - + +

    20 posts tagged with "30-days-of-serverless"

    View All Tags

    · 7 min read
    Paul Yu

    Welcome to Day 11 of #30DaysOfServerless!

    Yesterday we explored Azure Container Concepts related to environments, networking and microservices communication - and illustrated these with a deployment example. Today, we turn our attention to scaling your container apps with demand.


    What We'll Cover

    • What makes ACA Serverless?
    • What is Keda?
    • Scaling Your ACA
    • ACA Scaling In Action
    • Exercise: Explore azure-opensource-labs examples
    • Resources: For self-study!


    So, what makes Azure Container Apps "serverless"?

    Today we are going to focus on what makes Azure Container Apps (ACA) a "serverless" offering. But what does the term "serverless" really mean? As much as we'd like to think there aren't any servers involved, that is certainly not the case. In general, "serverless" means that most (if not all) server maintenance has been abstracted away from you.

    With serverless, you don't spend any time managing and patching servers. This concern is offloaded to Azure and you simply focus on adding business value through application delivery. In addition to operational efficiency, cost efficiency can be achieved with serverless on-demand pricing models. Your workload horizontally scales out based on need and you only pay for what you use. To me, this is serverless, and my teammate @StevenMurawski said it best... "being able to scale to zero is what gives ACA it's serverless magic."

    Scaling your Container Apps

    If you don't know by now, ACA is built on a solid open-source foundation. Behind the scenes, it runs on a managed Kubernetes cluster and includes several open-source components out-of-the box including Dapr to help you build and run microservices, Envoy Proxy for ingress capabilities, and KEDA for event-driven autoscaling. Again, you do not need to install these components yourself. All you need to be concerned with is enabling and/or configuring your container app to leverage these components.

    Let's take a closer look at autoscaling in ACA to help you optimize your container app.

    What is KEDA?

    KEDA stands for Kubernetes Event-Driven Autoscaler. It is an open-source project initially started by Microsoft and Red Hat and has been donated to the Cloud-Native Computing Foundation (CNCF). It is being maintained by a community of 200+ contributors and adopted by many large organizations. In terms of its status as a CNCF project it is currently in the Incubating Stage which means the project has gone through significant due diligence and on its way towards the Graduation Stage.

    Prior to KEDA, horizontally scaling your Kubernetes deployment was achieved through the Horizontal Pod Autoscaler (HPA) which relies on resource metrics such as CPU and memory to determine when additional replicas should be deployed. Being limited to CPU and memory falls a bit short for certain workloads. This is especially true for apps that need to processes messages from a queue or HTTP-based apps that can handle a specific amount of incoming HTTP requests at a time. KEDA aims to fill that gap and provides a much more robust framework for scaling by working in conjunction with HPA. It offers many scalers for you to implement and even allows your deployments to scale to zero! 🥳

    KEDA architecture

    Configuring ACA scale rules

    As I mentioned above, ACA's autoscaling feature leverages KEDA and gives you the ability to configure the number of replicas to deploy based on rules (event triggers). The number of replicas can be configured as a static number or a range (minimum and maximum). So if you need your containers to run 24/7, set the min and max to be the same value. By default, when you deploy a container app, it is set to scale from 0 to 10 replicas. The default scaling rule uses HTTP scaling and defaults to a minimum of 10 concurrent requests per second. Once the threshold of 10 concurrent request per second is met, another replica will be deployed until it reaches the maximum number of replicas.

    At the time of this writing, a container app can have up to 30 replicas.

    Default autoscaler

    As a best practice, if you have a Min / max replicas range configured, you should configure a scaling rule even if it is just explicitly setting the default values.

    Adding HTTP scaling rule

    In addition to HTTP scaling, you can also configure an Azure queue rule, which allows you to use Azure Storage Queues as an event data source.

    Adding Azure Queue scaling rule

    The most flexibility comes with the Custom rule type. This opens up a LOT more options for scaling. All of KEDA's event-based scalers are supported with this option 🚀

    Adding Custom scaling rule

    Translating KEDA templates to Azure templates

    When you implement Custom rules, you need to become familiar with translating KEDA templates to Azure Resource Manager templates or ACA YAML manifests. The KEDA scaler documentation is great and it should be simple to translate KEDA template metadata to an ACA rule metadata.

    The images below shows how to translated a scaling rule which uses Azure Service Bus as an event data source. The custom rule type is set to azure-servicebus and details of the service bus is added to the Metadata section. One important thing to note here is that the connection string to the service bus was added as a secret on the container app and the trigger parameter must be set to connection.

    Azure Container App custom rule metadata

    Azure Container App custom rule metadata

    Additional examples of KEDA scaler conversion can be found in the resources section and example video below.

    See Container App scaling in action

    Now that we've built up some foundational knowledge on how ACA autoscaling is implemented and configured, let's look at a few examples.

    Autoscaling based on HTTP traffic load

    Autoscaling based on Azure Service Bus message queues

    Summary

    ACA brings you a true serverless experience and gives you the ability to configure autoscaling rules based on KEDA scaler templates. This gives you flexibility to scale based on a wide variety of data sources in an event-driven manner. With the amount built-in scalers currently available, there is probably a scaler out there for all your use cases. If not, I encourage you to get involved with the KEDA community and help make it better!

    Exercise

    By now, you've probably read and seen enough and now ready to give this autoscaling thing a try. The example I walked through in the videos above can be found at the azure-opensource-labs repo. I highly encourage you to head over to the containerapps-terraform folder and try the lab out. There you'll find instructions which will cover all the steps and tools you'll need implement autoscaling container apps within your own Azure subscription.

    If you have any questions or feedback, please let us know in the comments below or reach out on Twitter @pauldotyu

    Have fun scaling your containers!

    Resources

    - - + + \ No newline at end of file diff --git a/blog/tags/30-days-of-serverless/page/13/index.html b/blog/tags/30-days-of-serverless/page/13/index.html index bae45728c3..faa41dead8 100644 --- a/blog/tags/30-days-of-serverless/page/13/index.html +++ b/blog/tags/30-days-of-serverless/page/13/index.html @@ -14,13 +14,13 @@ - - + +

    20 posts tagged with "30-days-of-serverless"

    View All Tags

    · 8 min read
    Paul Yu

    Welcome to Day 10 of #30DaysOfServerless!

    We continue our exploraton into Azure Container Apps, with today's focus being communication between microservices, and how to configure your Azure Container Apps environment in the context of a deployment example.


    What We'll Cover

    • ACA Environments & Virtual Networking
    • Basic Microservices Communications
    • Walkthrough: ACA Deployment Example
    • Summary and Next Steps
    • Exercise: Try this yourself!
    • Resources: For self-study!


    Introduction

    In yesterday's post, we learned what the Azure Container Apps (ACA) service is and the problems it aims to solve. It is considered to be a Container-as-a-Service platform since much of the complex implementation details of running a Kubernetes cluster is managed for you.

    Some of the use cases for ACA include event-driven processing jobs and background tasks, but this article will focus on hosting microservices, and how they can communicate with each other within the ACA service. At the end of this article, you will have a solid understanding of how networking and communication is handled and will leave you with a few tutorials to try.

    Environments and virtual networking in ACA

    Before we jump into microservices communication, we should review how networking works within ACA. With ACA being a managed service, Azure will take care of most of your underlying infrastructure concerns. As you provision an ACA resource, Azure provisions an Environment to deploy Container Apps into. This environment is your isolation boundary.

    Azure Container Apps Environment

    By default, Azure creates and manages a new Virtual Network (VNET) for you and the VNET is associated with the environment. As you deploy container apps, they are deployed into the same VNET and the environment is assigned a static public IP address which allows your apps to be accessible over the internet. This VNET is not visible or manageable.

    If you need control of the networking flows within the VNET, you can pre-provision one and tell Azure to deploy an environment within it. This "bring-your-own" VNET model allows you to deploy an environment in either External or Internal modes. Deploying an environment in External mode gives you the flexibility of managing your own VNET, while still allowing your containers to be accessible from outside the environment; a static public IP address is assigned to the environment. When deploying in Internal mode, your containers are accessible within the environment and/or VNET but not accessible from the internet.

    Bringing your own VNET will require some planning and you will need dedicate an empty subnet which will be used exclusively by the ACA environment. The size of your subnet will be dependant on how many containers you plan on deploying and your scaling requirements and one requirement to know is that the subnet address range must have have a /23 CIDR prefix at minimum. You will also need to think about your deployment strategy since ACA has the concept of Revisions which will also consume IPs from your subnet.

    Some additional restrictions to consider when planning your subnet address space is listed in the Resources section below and can be addressed in future posts, so be sure to follow us on dev.to and bookmark the ServerlessSeptember site.

    Basic microservices communication in ACA

    When it comes to communications between containers, ACA addresses this concern with its Ingress capabilities. With HTTP Ingress enabled on your container app, you can expose your app on a HTTPS endpoint.

    If your environment is deployed using default networking and your containers needs to be accessible from outside the environment, you will need to set the Ingress traffic option to Accepting traffic from anywhere. This will generate a Full-Qualified Domain Name (FQDN) which you can use to access your app right away. The ingress feature also generates and assigns a Secure Socket Layer (SSL) certificate for the FQDN.

    External ingress on Container App

    If your environment is deployed using default networking and your containers only need to communicate with other containers in the environment, you'll need to set the Ingress traffic option to Limited to Container Apps Environment. You get a FQDN here as well, but in the section below we'll see how that changes.

    Internal ingress on Container App

    As mentioned in the networking section above, if you deploy your ACA environment into a VNET in internal mode, your options will be Limited to Container Apps Environment or Limited to VNet.

    Ingress on internal virtual network

    Note how the Accepting traffic from anywhere option is greyed out. If your VNET is deployed in external mode, then the option will be available.

    Let's walk though an example ACA deployment

    The diagram below illustrates a simple microservices application that I deployed to ACA. The three container apps all have ingress enabled. The greeting-service app calls two backend services; a hello-service that returns the text Hello (in random casing) and a world-service that returns the text World (in a few random languages). The greeting-service concatenates the two strings together and returns Hello World to the browser. The greeting-service is the only service accessible via external ingress while two backend services are only accessible via internal ingress.

    Greeting Service overview

    With ingress enabled, let's take a quick look at the FQDN structures. Here is the FQDN of the external greeting-service.

    https://greeting-service.victoriouswave-3749d046.eastus.azurecontainerapps.io

    We can break it down into these components:

    https://[YOUR-CONTAINER-APP-NAME].[RANDOM-NAME]-[RANDOM-CHARACTERS].[AZURE-REGION].containerapps.io

    And here is the FQDN of the internal hello-service.

    https://hello-service.internal.victoriouswave-3749d046.eastus.azurecontainerapps.io

    Can you spot the difference between FQDNs?

    That was too easy 😉... the word internal is added as a subdomain in the FQDN between your container app name and the random name for all internal ingress endpoints.

    https://[YOUR-CONTAINER-APP-NAME].internal.[RANDOM-NAME]-[RANDOM-CHARACTERS].[AZURE-REGION].containerapps.io

    Now that we know the internal service FQDNs, we use them in the greeting-service app to achieve basic service-to-service communications.

    So we can inject FQDNs of downstream APIs to upstream apps using environment variables, but the downside to this approach is that need to deploy downstream containers ahead of time and this dependency will need to be planned for during your deployment process. There are ways around this and one option is to leverage the auto-injected environment variables within your app code.

    If I use the Console blade for the hello-service container app and run the env command, you will see environment variables named CONTAINER_APP_NAME and CONTAINER_APP_ENV_DNS_SUFFIX. You can use these values to determine FQDNs within your upstream app.

    hello-service environment variables

    Back in the greeting-service container I can invoke the hello-service container's sayhello method. I know the container app name is hello-service and this service is exposed over an internal ingress, therefore, if I add the internal subdomain to the CONTAINER_APP_ENV_DNS_SUFFIX I can invoke a HTTP request to the hello-service from my greeting-service container.

    Invoke the sayHello method from the greeting-service container

    As you can see, the ingress feature enables communications to other container apps over HTTP/S and ACA will inject environment variables into our container to help determine what the ingress FQDNs would be. All we need now is a little bit of code modification in the greeting-service app and build the FQDNs of our backend APIs by retrieving these environment variables.

    Greeting service code

    ... and now we have a working microservices app on ACA! 🎉

    Hello World

    Summary and next steps

    We've covered Container Apps networking and the basics of how containers communicate with one another. However, there is a better way to address service-to-service invocation using Dapr, which is an open-source framework for building microservices. It is natively integrated into the ACA service and in a future post, you'll learn how to enable it in your Container App to address microservices concerns and more. So stay tuned!

    Exercises

    As a takeaway for today's post, I encourage you to complete this tutorial and if you'd like to deploy the sample app that was presented in this article, my teammate @StevenMurawski is hosting a docker-compose-examples repo which includes samples for deploying to ACA using Docker Compose files. To learn more about the az containerapp compose command, a link to his blog articles are listed in the Resources section below.

    If you have any questions or feedback, please let us know in the comments below or reach out on Twitter @pauldotyu

    Have fun packing and shipping containers! See you in the next post!

    Resources

    The sample app presented here was inspired by services demonstrated in the book Introducing Distributed Application Runtime (Dapr): Simplifying Microservices Applications Development Through Proven and Reusable Patterns and Practices. Go check it out to leran more about Dapr!

    - - + + \ No newline at end of file diff --git a/blog/tags/30-days-of-serverless/page/14/index.html b/blog/tags/30-days-of-serverless/page/14/index.html index 8b0c892bf9..d7c3534315 100644 --- a/blog/tags/30-days-of-serverless/page/14/index.html +++ b/blog/tags/30-days-of-serverless/page/14/index.html @@ -14,13 +14,13 @@ - - + +

    20 posts tagged with "30-days-of-serverless"

    View All Tags

    · 12 min read
    Nitya Narasimhan

    Welcome to Day 9 of #30DaysOfServerless!


    What We'll Cover

    • The Week Ahead
    • Hello, Container Apps!
    • Quickstart: Build Your First ACA!
    • Under The Hood: Core ACA Concepts
    • Exercise: Try this yourself!
    • Resources: For self-study!


    The Week Ahead

    Welcome to Week 2 of #ServerlessSeptember, where we put the focus on Microservices and building Cloud-Native applications that are optimized for serverless solutions on Azure. One week is not enough to do this complex topic justice so consider this a 7-part jumpstart to the longer journey.

    1. Hello, Container Apps (ACA) - Learn about Azure Container Apps, a key service that helps you run microservices and containerized apps on a serverless platform. Know the core concepts. (Tutorial 1: First ACA)
    2. Communication with Microservices - Dive deeper into two key concepts: environments and virtual networking. Learn how microservices communicate in ACA, and walkthrough an example. (Tutorial 2: ACA with 3 Microservices)
    3. Scaling Your Container Apps - Learn about KEDA. Understand how to configure your ACA for auto-scaling with KEDA-supported triggers. Put this into action by walking through a tutorial. (Tutorial 3: Configure Autoscaling)
    4. Hello, Distributed Application Runtime (Dapr) - Learn about Dapr and how its Building Block APIs simplify microservices development with ACA. Know how the sidecar pattern enables incremental adoption of Dapr APIs without requiring any Dapr code integration in app. (Tutorial 4: Setup & Explore Dapr)
    5. Building ACA with Dapr - See how Dapr works with ACA by building a Dapr-enabled Azure Container App. Walk through a .NET tutorial using Pub/Sub and State Management APIs in an enterprise scenario. (Tutorial 5: Build ACA with Dapr)
    6. Managing Secrets With Dapr - We'll look at the Secrets API (a key Building Block of Dapr) and learn how it simplifies management of sensitive information in ACA.
    7. Microservices + Serverless On Azure - We recap Week 2 (Microservices) and set the stage for Week 3 ( Integrations) of Serverless September. Plus, self-study resources including ACA development tutorials in different languages.

    Ready? Let's go!


    Azure Container Apps!

    When building your application, your first decision is about where you host your application. The Azure Architecture Center has a handy chart to help you decide between choices like Azure Functions, Azure App Service, Azure Container Instances, Azure Container Apps and more. But if you are new to this space, you'll need a good understanding of the terms and concepts behind the services Today, we'll focus on Azure Container Apps (ACA) - so let's start with the fundamentals.

    Containerized App Defined

    A containerized app is one where the application components, dependencies, and configuration, are packaged into a single file (container image), which can be instantiated in an isolated runtime environment (container) that is portable across hosts (OS). This makes containers lightweight and scalable - and ensures that applications behave consistently on different host platforms.

    Container images can be shared via container registries (public or private) helping developers discover and deploy related apps with less effort. Scaling a containerized app can be as simple as activating more instances of its container image. However, this requires container orchestrators to automate the management of container apps for efficiency. Orchestrators use technologies like Kubernetes to support capabilities like workload scheduling, self-healing and auto-scaling on demand.

    Cloud-Native & Microservices

    Containers are seen as one of the 5 pillars of Cloud-Native app development, an approach where applications are designed explicitly to take advantage of the unique benefits of modern dynamic environments (involving public, private and hybrid clouds). Containers are particularly suited to serverless solutions based on microservices.

    • With serverless - developers use managed services instead of managing their own infrastructure. Services are typically event-driven and can be configured for autoscaling with rules tied to event triggers. Serverless is cost-effective, with developers paying only for the compute cycles and resources they use.
    • With microservices - developers compose their applications from independent components. Each component can be deployed in its own container, and scaled at that granularity. This simplifies component reuse (across apps) and maintainability (over time) - with developers evolving functionality at microservice (vs. app) levels.

    Hello, Azure Container Apps!

    Azure Container Apps is the managed service that helps you run containerized apps and microservices as a serverless compute solution, on Azure. You can:

    • deploy serverless API endpoints - autoscaled by HTTP request traffic
    • host background processing apps - autoscaled by CPU or memory load
    • handle event-driven processing - autoscaled by #messages in queue
    • run microservices - autoscaled by any KEDA-supported scaler.

    Want a quick intro to the topic? Start by watching the short video below - then read these two posts from our ZeroToHero series:


    Deploy Your First ACA

    Dev Options

    We typically have three options for development:

    • Use the Azure Portal - provision and deploy from a browser.
    • Use Visual Studio Code (with relevant extensions) - if you prefer an IDE
    • Using Azure CLI - if you prefer to build and deploy from command line.

    The documentation site has quickstarts for three contexts:

    For this quickstart, we'll go with the first option (sample image) so we can move quickly to core concepts. We'll leave the others as an exercise for you to explore.

    1. Setup Resources

    PRE-REQUISITES

    You need:

    • An Azure account with an active subscription
    • An installed Azure CLI

    Start by logging into Azure from the CLI. The command should launch a browser to complete the auth flow (or give you an option to take an alternative path).

    $ az login

    Successful authentication will result in extensive command-line output detailing the status of your subscription.

    Next, install the Azure Container Apps extension for the CLI

    $ az extension add --name containerapp --upgrade
    ...
    The installed extension 'containerapp' is in preview.

    Once successfully installed, register the Microsoft.App namespace.

    $ az provider register --namespace Microsoft.App

    Then set local environment variables in that terminal - and verify they are set correctly:

    $ RESOURCE_GROUP="my-container-apps"
    $ LOCATION="canadacentral"
    $ CONTAINERAPPS_ENVIRONMENT="my-environment"

    $ echo $LOCATION $RESOURCE_GROUP $CONTAINERAPPS_ENVIRONMENT
    canadacentral my-container-apps my-environment

    Now you can use Azure CLI to provision a resource group for this tutorial. Creating a resource group also makes it easier for us to delete/reclaim all resources used at the end of this tutorial.

    az group create \
    --name $RESOURCE_GROUP \
    --location $LOCATION
    Congratulations

    You completed the Setup step!

    On completion, the console should print out the details of the newly created resource group. You should also be able to visit the Azure Portal and find the newly-active my-container-apps resource group under your active subscription.

    2. Create Environment

    An environment is like the picket fence around your property. It creates a secure boundary that contains a group of container apps - such that all apps deployed to it share the same virtual network and logging resources.

    $ az containerapp env create \
    --name $CONTAINERAPPS_ENVIRONMENT \
    --resource-group $RESOURCE_GROUP \
    --location $LOCATION

    No Log Analytics workspace provided.
    Generating a Log Analytics workspace with name ...

    This can take a few minutes. When done, you will see the terminal display more details. You can also check the resource group in the portal and see that a Container Apps Environment and a Log Analytics Workspace are created for you as part of this step.

    You've got the fence set up. Now it's time to build your home - er, container app!

    3. Create Container App

    Here's the command we'll use to create our first Azure Container App. Note that the --image argument provides the link to a pre-existing containerapps-helloworld image.

    az containerapp create \
    --name my-container-app \
    --resource-group $RESOURCE_GROUP \
    --environment $CONTAINERAPPS_ENVIRONMENT \
    --image mcr.microsoft.com/azuredocs/containerapps-helloworld:latest \
    --target-port 80 \
    --ingress 'external' \
    --query properties.configuration.ingress.fqdn
    ...
    ...

    Container app created. Access your app at <URL>

    The --ingress property shows that the app is open to external requests; in other words, it is publicly visible at the <URL> that is printed out on the terminal on successsful completion of this step.

    4. Verify Deployment

    Let's see if this works. You can verify that your container app by visitng the URL returned above in your browser. You should see something like this!

    Container App Hello World

    You can also visit the Azure Portal and look under the created Resource Group. You should see a new Container App type of resource was created after this step.

    Congratulations

    You just created and deployed your first "Hello World" Azure Container App! This validates your local development environment setup and existence of a valid Azure subscription.

    5. Clean Up Your Resources

    It's good practice to clean up resources once you are done with a tutorial.

    THIS ACTION IS IRREVERSIBLE

    This command deletes the resource group we created above - and all resources in it. So make sure you specified the right name, then confirm deletion.

    $ az group delete --name $RESOURCE_GROUP
    Are you sure you want to perform this operation? (y/n):

    Note that you can also delete the resource group from the Azure Portal interface if that feels more comfortable. For now, we'll just use the Portal to verify that deletion occurred. If you had previously opened the Resource Group page for the created resource, just refresh it. You should see something like this:

    Resource Not Found


    Core Concepts

    COMING SOON

    An illustrated guide summarizing these concepts in a single sketchnote.

    We covered a lot today - we'll stop with a quick overview of core concepts behind Azure Container Apps, each linked to documentation for self-study. We'll dive into more details on some of these concepts in upcoming articles:

    • Environments - are the secure boundary around a group of container apps that are deployed in the same virtual network. They write logs to a shared Log Analytics workspace and can communicate seamlessly using Dapr, if used.
    • Containers refer to the container image deployed in the Azure Container App. They can use any runtime, programming language, or development stack - and be discovered using any public or private container registry. A container app can support multiple containers.
    • Revisions are immutable snapshots of an Azure Container App. The first revision is created when the ACA is first deployed, with new revisions created when redeployment occurs with revision-scope changes. Multiple revisions can run concurrently in an environment.
    • Application Lifecycle Management revolves around these revisions, with a container app having three phases: deployment, update and deactivation.
    • Microservices are independent units of functionality in Cloud-Native architectures. A single container app typically represents a single microservice, and can be composed from one or more containers. Microservices can now be scaled and upgraded indepedently, giving your application more flexbility and control.
    • Networking architecture consist of a virtual network (VNET) associated with the environment. Unless you provide a custom VNET at environment creation time, a default VNET is automatically created. The VNET configuration determines access (ingress, internal vs. external) and can influence auto-scaling choices (e.g., use HTTP Edge Proxy and scale based on number of HTTP requests).
    • Observability is about monitoring the health of your application and diagnosing it to improve reliability or performance. Azure Container Apps has a number of features - from Log streaming and Container console to integration with Azure Monitor - to provide a holistic view of application status over time.
    • Easy Auth is possible with built-in support for authentication and authorization including support for popular identity providers like Facebook, Google, Twitter and GitHub - alongside the Microsoft Identity Platform.

    Keep these terms in mind as we walk through more tutorials this week, to see how they find application in real examples. Finally, a note on Dapr, the Distributed Application Runtime that abstracts away many of the challenges posed by distributed systems - and lets you focus on your application logic.

    DAPR INTEGRATION MADE EASY

    Dapr uses a sidecar architecture, allowing Azure Container Apps to communicate with Dapr Building Block APIs over either gRPC or HTTP. Your ACA can be built to run with or without Dapr - giving you the flexibility to incrementally adopt specific APIs and unlock related capabilities as the need arises.

    In later articles this week, we'll do a deeper dive into Dapr and build our first Dapr-enable Azure Container App to get a better understanding of this integration.

    Exercise

    Congratulations! You made it! By now you should have a good idea of what Cloud-Native development means, why Microservices and Containers are important to that vision - and how Azure Container Apps helps simplify the building and deployment of microservices based applications using serverless architectures on Azure.

    Now it's your turn to reinforce learning by doing.

    Resources

    Three key resources to bookmark and explore:

    - - + + \ No newline at end of file diff --git a/blog/tags/30-days-of-serverless/page/15/index.html b/blog/tags/30-days-of-serverless/page/15/index.html index 7878d67e28..2bbbdc201a 100644 --- a/blog/tags/30-days-of-serverless/page/15/index.html +++ b/blog/tags/30-days-of-serverless/page/15/index.html @@ -14,13 +14,13 @@ - - + +

    20 posts tagged with "30-days-of-serverless"

    View All Tags

    · 5 min read
    Nitya Narasimhan
    Devanshi Joshi

    SEP 08: CHANGE IN PUBLISHING SCHEDULE

    Starting from Week 2 (Sep 8), we'll be publishing blog posts in batches rather than on a daily basis, so you can read a series of related posts together. Don't want to miss updates? Just subscribe to the feed


    Welcome to Day 8 of #30DaysOfServerless!

    This marks the end of our Week 1 Roadmap focused on Azure Functions!! Today, we'll do a quick recap of all #ServerlessSeptember activities in Week 1, set the stage for Week 2 - and leave you with some excellent tutorials you should explore to build more advanced scenarios with Azure Functions.

    Ready? Let's go.


    What We'll Cover

    • Azure Functions: Week 1 Recap
    • Advanced Functions: Explore Samples
    • End-to-End: Serverless Hacks & Cloud Skills
    • What's Next: Hello, Containers & Microservices
    • Challenge: Complete the Learning Path


    Week 1 Recap: #30Days & Functions

    Congratulations!! We made it to the end of Week 1 of #ServerlessSeptember. Let's recap what we learned so far:

    • In Core Concepts we looked at where Azure Functions fits into the serverless options available on Azure. And we learned about key concepts like Triggers, Bindings, Custom Handlers and Durable Functions.
    • In Build Your First Function we looked at the tooling options for creating Functions apps, testing them locally, and deploying them to Azure - as we built and deployed our first Functions app.
    • In the next 4 posts, we explored new Triggers, Integrations, and Scenarios - as we looked at building Functions Apps in Java, JavaScript, .NET and Python.
    • And in the Zero-To-Hero series, we learned about Durable Entities - and how we can use them to create stateful serverless solutions using a Chirper Sample as an example scenario.

    The illustrated roadmap below summarizes what we covered each day this week, as we bring our Functions-as-a-Service exploration to a close.


    Advanced Functions: Code Samples

    So, now that we've got our first Functions app under our belt, and validated our local development setup for tooling, where can we go next? A good next step is to explore different triggers and bindings, that drive richer end-to-end scenarios. For example:

    • Integrate Functions with Azure Logic Apps - we'll discuss Azure Logic Apps in Week 3. For now, think of it as a workflow automation tool that lets you integrate seamlessly with other supported Azure services to drive an end-to-end scenario. In this tutorial, we set up a workflow connecting Twitter (get tweet) to Azure Cognitive Services (analyze sentiment) - and use that to trigger an Azure Functions app to send email about the result.
    • Integrate Functions with Event Grid - we'll discuss Azure Event Grid in Week 3. For now, think of it as an eventing service connecting event sources (publishers) to event handlers (subscribers) at cloud scale. In this tutorial, we handle a common use case - a workflow where loading an image to Blob Storage triggers an Azure Functions app that implements a resize function, helping automatically generate thumbnails for the uploaded image.
    • Integrate Functions with CosmosDB and SignalR to bring real-time push-based notifications to your web app. It achieves this by using a Functions app that is triggered by changes in a CosmosDB backend, causing it to broadcast that update (push notification to connected web clients over SignalR, in real time.

    Want more ideas? Check out the Azure Samples for Functions for implementations, and browse the Azure Architecture Center for reference architectures from real-world scenarios that involve Azure Functions usage.


    E2E Scenarios: Hacks & Cloud Skills

    Want to systematically work your way through a single End-to-End scenario involving Azure Functions alongside other serverless support technologies? Check out the Serverless Hacks activity happening during #ServerlessSeptember, and learn to build this "Serverless Tollbooth Application" in a series of 10 challenges. Check out the video series for a reference solution in .NET and sign up for weekly office hours to join peers and discuss your solutions or challenges.

    Or perhaps you prefer to learn core concepts with code in a structured learning path? We have that covered. Check out the 12-module "Create Serverless Applications" course from Microsoft Learn which walks your through concepts, one at a time, with code. Even better - sign up for the free Cloud Skills Challenge and complete the same path (in under 30 days) but this time, with the added fun of competing against your peers for a spot on a leaderboard, and swag.


    What's Next? Hello, Cloud-Native!

    So where to next? In Week 2 we turn our attention from Functions-as-a-Service to building more complex backends using Containers and Microservices. We'll focus on two core technologies - Azure Container Apps and Dapr (Distributed Application Runtime) - both key components of a broader vision around Building Cloud-Native Applications in Azure.

    What is Cloud-Native you ask?

    Fortunately for you, we have an excellent introduction in our Zero-to-Hero article on Go Cloud-Native with Azure Container Apps - that explains the 5 pillars of Cloud-Native and highlights the value of Azure Container Apps (scenarios) and Dapr (sidecar architecture) for simplified microservices-based solution with auto-scale capability. Prefer a visual summary? Here's an illustrate guide to that article for convenience.

    Go Cloud-Native Download a higher resolution version of the image


    Take The Challenge

    We typically end each post with an exercise or activity to reinforce what you learned. For Week 1, we encourage you to take the Cloud Skills Challenge and work your way through at least a subset of the modules, for hands-on experience with the different Azure Functions concepts, integrations, and usage.

    See you in Week 2!

    - - + + \ No newline at end of file diff --git a/blog/tags/30-days-of-serverless/page/16/index.html b/blog/tags/30-days-of-serverless/page/16/index.html index 048a34dadd..a0e8adaf80 100644 --- a/blog/tags/30-days-of-serverless/page/16/index.html +++ b/blog/tags/30-days-of-serverless/page/16/index.html @@ -14,15 +14,15 @@ - - + +

    20 posts tagged with "30-days-of-serverless"

    View All Tags

    · 7 min read
    Jay Miller

    Welcome to Day 7 of #30DaysOfServerless!

    Over the past couple of days, we've explored Azure Functions from the perspective of specific programming languages. Today we'll continue that trend by looking at Python - exploring the Timer Trigger and CosmosDB binding, and showcasing integration with a FastAPI-implemented web app.

    Ready? Let's go.


    What We'll Cover

    • Developer Guidance: Azure Functions On Python
    • Build & Deploy: Wildfire Detection Apps with Timer Trigger + CosmosDB
    • Demo: My Fire Map App: Using FastAPI and Azure Maps to visualize data
    • Next Steps: Explore Azure Samples
    • Exercise: Try this yourself!
    • Resources: For self-study!


    Developer Guidance

    If you're a Python developer new to serverless on Azure, start with the Azure Functions Python Developer Guide. It covers:

    • Quickstarts with Visual Studio Code and Azure CLI
    • Adopting best practices for hosting, reliability and efficiency.
    • Tutorials showcasing Azure automation, image classification and more
    • Samples showcasing Azure Functions features for Python developers

    Now let's dive in and build our first Python-based Azure Functions app.


    Detecting Wildfires Around the World?

    I live in California which is known for lots of wildfires. I wanted to create a proof of concept for developing an application that could let me know if there was a wildfire detected near my home.

    NASA has a few satelites orbiting the Earth that can detect wildfires. These satelites take scans of the radiative heat in and use that to determine the likelihood of a wildfire. NASA updates their information about every 30 minutes and it can take about four hours for to scan and process information.

    Fire Point Near Austin, TX

    I want to get the information but I don't want to ping NASA or another service every time I check.

    What if I occaisionally download all the data I need? Then I can ping that as much as I like.

    I can create a script that does just that. Any time I say I can create a script that is a verbal queue for me to consider using an Azure function. With the function being ran in the cloud, I can ensure the script runs even when I'm not at my computer.

    How the Timer Trigger Works

    This function will utilize the Timer Trigger. This means Azure will call this function to run at a scheduled interval. This isn't the only way to keep the data in sync, but we know that arcgis, the service that we're using says that data is only updated every 30 minutes or so.

    To learn more about the TimerTrigger as a concept, check out the Azure Functions documentation around Timers.

    When we create the function we tell it a few things like where the script will live (in our case in __init__.py) the type and direction and notably often it should run. We specify the timer using schedule": <The CRON INTERVAL>. For us we're using 0 0,30 * * * which means every 30 minutes at the hour and half-hour.

    {
    "scriptFile": "__init__.py",
    "bindings": [
    {
    "name": "reqTimer",
    "type": "timerTrigger",
    "direction": "in",
    "schedule": "0 0,30 * * * *"
    }
    ]
    }

    Next, we create the code that runs when the function is called.

    Connecting to the Database and our Source

    Disclaimer: The data that we're pulling is for educational purposes only. This is not meant to be a production level application. You're welcome play with this project but ensure that you're using the data in compliance with Esri.

    Our function does two important things.

    1. It pulls data from ArcGIS that meets the parameters
    2. It stores that pulled data into our database

    If you want to check out the code in its entirety, check out the GitHub repository.

    Pulling the data from ArcGIS is easy. We can use the ArcGIS Python API. Then, we need to load the service layer. Finally we query that layer for the specific data.

    def write_new_file_data(gis_id:str, layer:int=0) -> FeatureSet:
    """Returns a JSON String of the Dataframe"""
    fire_data = g.content.get(gis_id)
    feature = fire_data.layers[layer] # Loading Featured Layer from ArcGIS
    q = feature.query(
    where="confidence >= 65 AND hours_old <= 4", #The filter for the query
    return_distince_values=True,
    out_fields="confidence, hours_old", # The data we want to store with our points
    out_sr=4326, # The spatial reference of the data
    )
    return q

    Then we need to store the data in our database.

    We're using Cosmos DB for this. COSMOSDB is a NoSQL database, which means that the data looks a lot like a python dictionary as it's JSON. This means that we don't need to worry about converting the data into a format that can be stored in a relational database.

    The second reason is that Cosmos DB is tied into the Azure ecosystem so that if we want to create functions Azure events around it, we can.

    Our script grabs the information that we pulled from ArcGIS and stores it in our database.

    async with CosmosClient.from_connection_string(COSMOS_CONNECTION_STRING) as client:
    container = database.get_container_client(container=CONTAINER)
    for record in data:
    await container.create_item(
    record,
    enable_automatic_id_generation=True,
    )

    In our code each of these functions live in their own space. So in the main function we focus solely on what azure functions will be doing. The script that gets called is __init__.py. There we'll have the function call the other functions running.

    We created another function called load_and_write that does all the work outlined above. __init__.py will call that.

    async def main(reqTimer: func.TimerRequest) -> None:
    database=database
    container=container
    await update_db.load_and_write(gis_id=GIS_LAYER_ID, database=database, container=container)

    Then we deploy the function to Azure. I like to use VS Code's Azure Extension but you can also deploy it a few other ways.

    Deploying the function via VS Code

    Once the function is deployed we can load the Azure portal and see a ping whenever the function is called. The pings correspond to the Function being ran

    We can also see the data now living in the datastore. Document in Cosmos DB

    It's in the Database, Now What?

    Now the real fun begins. We just loaded the last bit of fire data into a database. We can now query that data and serve it to others.

    As I mentioned before, our Cosmos DB data is also stored in Azure, which means that we can deploy Azure Functions to trigger when new data is added. Perhaps you can use this to check for fires near you and use a Logic App to send an alert to your phone or email.

    Another option is to create a web application that talks to the database and displays the data. I've created an example of this using FastAPI – https://jm-func-us-fire-notify.azurewebsites.net.

    Website that Checks for Fires


    Next Steps

    This article showcased the Timer Trigger and the HTTP Trigger for Azure Functions in Python. Now try exploring other triggers and bindings by browsing Bindings code samples for Python and Azure Functions samples for Python

    Once you've tried out the samples, you may want to explore more advanced integrations or extensions for serverless Python scenarios. Here are some suggestions:

    And check out the resources for more tutorials to build up your Azure Functions skills.

    Exercise

    I encourage you to fork the repository and try building and deploying it yourself! You can see the TimerTrigger and a HTTPTrigger building the website.

    Then try extending it. Perhaps if wildfires are a big thing in your area, you can use some of the data available in Planetary Computer to check out some other datasets.

    Resources

    - - + + \ No newline at end of file diff --git a/blog/tags/30-days-of-serverless/page/17/index.html b/blog/tags/30-days-of-serverless/page/17/index.html index fb615e6df1..5cf71576b7 100644 --- a/blog/tags/30-days-of-serverless/page/17/index.html +++ b/blog/tags/30-days-of-serverless/page/17/index.html @@ -14,13 +14,13 @@ - - + +

    20 posts tagged with "30-days-of-serverless"

    View All Tags

    · 10 min read
    Mike James
    Matt Soucoup

    Welcome to Day 6 of #30DaysOfServerless!

    The theme for this week is Azure Functions. Today we're going to talk about why Azure Functions are a great fit for .NET developers.


    What We'll Cover

    • What is serverless computing?
    • How does Azure Functions fit in?
    • Let's build a simple Azure Function in .NET
    • Developer Guide, Samples & Scenarios
    • Exercise: Explore the Create Serverless Applications path.
    • Resources: For self-study!

    A banner image that has the title of this article with the author&#39;s photo and a drawing that summarizes the demo application.


    The leaves are changing colors and there's a chill in the air, or for those lucky folks in the Southern Hemisphere, the leaves are budding and a warmth is in the air. Either way, that can only mean one thing - it's Serverless September!🍂 So today, we're going to take a look at Azure Functions - what they are, and why they're a great fit for .NET developers.

    What is serverless computing?

    For developers, serverless computing means you write highly compact individual functions that do one thing - and run in the cloud. These functions are triggered by some external event. That event could be a record being inserted into a database, a file uploaded into BLOB storage, a timer interval elapsed, or even a simple HTTP request.

    But... servers are still definitely involved! What has changed from other types of cloud computing is that the idea and ownership of the server has been abstracted away.

    A lot of the time you'll hear folks refer to this as Functions as a Service or FaaS. The defining characteristic is all you need to do is put together your application logic. Your code is going to be invoked in response to events - and the cloud provider takes care of everything else. You literally get to focus on only the business logic you need to run in response to something of interest - no worries about hosting.

    You do not need to worry about wiring up the plumbing between the service that originates the event and the serverless runtime environment. The cloud provider will handle the mechanism to call your function in response to whatever event you chose to have the function react to. And it passes along any data that is relevant to the event to your code.

    And here's a really neat thing. You only pay for the time the serverless function is running. So, if you have a function that is triggered by an HTTP request, and you rarely get requests to your function, you would rarely pay.

    How does Azure Functions fit in?

    Microsoft's Azure Functions is a modern serverless architecture, offering event-driven cloud computing that is easy for developers to use. It provides a way to run small pieces of code or Functions in the cloud without developers having to worry themselves about the infrastructure or platform the Function is running on.

    That means we're only concerned about writing the logic of the Function. And we can write that logic in our choice of languages... like C#. We are also able to add packages from NuGet to Azure Functions—this way, we don't have to reinvent the wheel and can use well-tested libraries.

    And the Azure Functions runtime takes care of a ton of neat stuff for us, like passing in information about the event that caused it to kick off - in a strongly typed variable. It also "binds" to other services, like Azure Storage, we can easily access those services from our code without having to worry about new'ing them up.

    Let's build an Azure Function!

    Scaffold the Function

    Don't worry about having an Azure subscription or even being connected to the internet—we can develop and debug Azure Functions locally using either Visual Studio or Visual Studio Code!

    For this example, I'm going to use Visual Studio Code to build up a Function that responds to an HTTP trigger and then writes a message to an Azure Storage Queue.

    Diagram of the how the Azure Function will use the HTTP trigger and the Azure Storage Queue Binding

    The incoming HTTP call is the trigger and the message queue the Function writes to is an output binding. Let's have at it!

    info

    You do need to have some tools downloaded and installed to get started. First and foremost, you'll need Visual Studio Code. Then you'll need the Azure Functions extension for VS Code to do the development with. Finally, you'll need the Azurite Emulator installed as well—this will allow us to write to a message queue locally.

    Oh! And of course, .NET 6!

    Now with all of the tooling out of the way, let's write a Function!

    1. Fire up Visual Studio Code. Then, from the command palette, type: Azure Functions: Create New Project

      Screenshot of create a new function dialog in VS Code

    2. Follow the steps as to which directory you want to create the project in and which .NET runtime and language you want to use.

      Screenshot of VS Code prompting which directory and language to use

    3. Pick .NET 6 and C#.

      It will then prompt you to pick the folder in which your Function app resides and then select a template.

      Screenshot of VS Code prompting you to pick the Function trigger template

      Pick the HTTP trigger template. When prompted for a name, call it: PostToAQueue.

    Execute the Function Locally

    1. After giving it a namespace, it prompts for an authorization level—pick Anonymous. Now we have a Function! Let's go ahead and hit F5 and see it run!
    info

    After the templates have finished installing, you may get a prompt to download additional components—these are NuGet packages. Go ahead and do that.

    When it runs, you'll see the Azure Functions logo appear in the Terminal window with the URL the Function is located at. Copy that link.

    Screenshot of the Azure Functions local runtime starting up

    1. Type the link into a browser, adding a name parameter as shown in this example: http://localhost:7071/api/PostToAQueue?name=Matt. The Function will respond with a message. You can even set breakpoints in Visual Studio Code and step through the code!

    Write To Azure Storage Queue

    Next, we'll get this HTTP trigger Function to write to a local Azure Storage Queue. First we need to add the Storage NuGet package to our project. In the terminal, type:

    dotnet add package Microsoft.Azure.WebJobs.Extensions.Storage

    Then set a configuration setting to tell the Function runtime where to find the Storage. Open up local.settings.json and set "AzureWebJobsStorage" to "UseDevelopmentStorage=true". The full file will look like:

    {
    "IsEncrypted": false,
    "Values": {
    "AzureWebJobsStorage": "UseDevelopmentStorage=true",
    "AzureWebJobsDashboard": ""
    }
    }

    Then create a new class within your project. This class will hold nothing but properties. Call it whatever you want and add whatever properties you want to it. I called mine TheMessage and added an Id and Name properties to it.

    public class TheMessage
    {
    public string Id { get; set; }
    public string Name { get; set; }
    }

    Finally, change your PostToAQueue Function, so it looks like the following:


    public static class PostToAQueue
    {
    [FunctionName("PostToAQueue")]
    public static async Task<IActionResult> Run(
    [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req,
    [Queue("demoqueue", Connection = "AzureWebJobsStorage")] IAsyncCollector<TheMessage> messages,
    ILogger log)
    {
    string name = req.Query["name"];

    await messages.AddAsync(new TheMessage { Id = System.Guid.NewGuid().ToString(), Name = name });

    return new OkResult();
    }
    }

    Note the addition of the messages variable. This is telling the Function to use the storage connection we specified before via the Connection property. And it is also specifying which queue to use in that storage account, in this case demoqueue.

    All the code is doing is pulling out the name from the query string, new'ing up a new TheMessage class and adding that to the IAsyncCollector variable.

    That will add the new message to the queue!

    Make sure Azurite is started within VS Code (both the queue and blob emulators). Run the app and send the same GET request as before: http://localhost:7071/api/PostToAQueue?name=Matt.

    If you have the Azure Storage Explorer installed, you can browse your local Queue and see the new message in there!

    Screenshot of Azure Storage Explorer with the new message in the queue

    Summing Up

    We had a quick look at what Microsoft's serverless offering, Azure Functions, is comprised of. It's a full-featured FaaS offering that enables you to write functions in your language of choice, including reusing packages such as those from NuGet.

    A highlight of Azure Functions is the way they are triggered and bound. The triggers define how a Function starts, and bindings are akin to input and output parameters on it that correspond to external services. The best part is that the Azure Function runtime takes care of maintaining the connection to the external services so you don't have to worry about new'ing up or disposing of the connections yourself.

    We then wrote a quick Function that gets triggered off an HTTP request and then writes a query string parameters from that request into a local Azure Storage Queue.

    What's Next

    So, where can you go from here?

    Think about how you can build real-world scenarios by integrating other Azure services. For example, you could use serverless integrations to build a workflow where the input payload received using an HTTP Trigger, is now stored in Blob Storage (output binding), which in turn triggers another service (e.g., Cognitive Services) that processes the blob and returns an enhanced result.

    Keep an eye out for an update to this post where we walk through a scenario like this with code. Check out the resources below to help you get started on your own.

    Exercise

    This brings us close to the end of Week 1 with Azure Functions. We've learned core concepts, built and deployed our first Functions app, and explored quickstarts and scenarios for different programming languages. So, what can you do to explore this topic on your own?

    • Explore the Create Serverless Applications learning path which has several modules that explore Azure Functions integrations with various services.
    • Take up the Cloud Skills Challenge and complete those modules in a fun setting where you compete with peers for a spot on the leaderboard!

    Then come back tomorrow as we wrap up the week with a discussion on end-to-end scenarios, a recap of what we covered this week, and a look at what's ahead next week.

    Resources

    Start here for developer guidance in getting started with Azure Functions as a .NET/C# developer:

    Then learn about supported Triggers and Bindings for C#, with code snippets to show how they are used.

    Finally, explore Azure Functions samples for C# and learn to implement serverless solutions. Examples include:

    - - + + \ No newline at end of file diff --git a/blog/tags/30-days-of-serverless/page/18/index.html b/blog/tags/30-days-of-serverless/page/18/index.html index 284b7dc15d..d27441651a 100644 --- a/blog/tags/30-days-of-serverless/page/18/index.html +++ b/blog/tags/30-days-of-serverless/page/18/index.html @@ -14,13 +14,13 @@ - - + +

    20 posts tagged with "30-days-of-serverless"

    View All Tags

    · 7 min read
    Aaron Powell

    Welcome to Day 5 of #30DaysOfServerless!

    Yesterday we looked at Azure Functions from the perspective of a Java developer. Today, we'll do a similar walkthrough from the perspective of a JavaScript developer.

    And, we'll use this to explore another popular usage scenario for Azure Functions: building a serverless HTTP API using JavaScript.

    Ready? Let's go.


    What We'll Cover

    • Developer Guidance
    • Create Azure Function with CLI
    • Calling an external API
    • Azure Samples & Scenarios for JS
    • Exercise: Support searching
    • Resources: For self-study!


    Developer Guidance

    If you're a JavaScript developer new to serverless on Azure, start by exploring the Azure Functions JavaScript Developers Guide. It covers:

    • Quickstarts for Node.js - using Visual Code, CLI or Azure Portal
    • Guidance on hosting options and performance considerations
    • Azure Functions bindings and (code samples) for JavaScript
    • Scenario examples - integrations with other Azure Services

    Node.js 18 Support

    Node.js 18 Support (Public Preview)

    Azure Functions support for Node.js 18 entered Public Preview on Aug 31, 2022 and is supported by the Azure Functions v.4.x runtime!

    As we continue to explore how we can use Azure Functions, today we're going to look at using JavaScript to create one, and we're going to be using the newly released Node.js 18 support for Azure Functions to make the most out of the platform.

    Ensure you have Node.js 18 and Azure Functions v4.x versions installed, along with a text editor (I'll use VS Code in this post), and a terminal, then we're ready to go.

    Scenario: Calling The GitHub API

    The application we're going to be building today will use the GitHub API to return a random commit message, so that we don't need to come up with one ourselves! After all, naming things can be really hard! 🤣

    Creating the Azure Function

    To create our Azure Function, we're going to use the Azure Functions CLI, which we can install using npm:

    npm install --global azure-function-core-tools

    Once that's installed, we can use the new func command to initalise our project:

    func init --worker-runtime node --language javascript

    When running func init we can either provide the worker-runtime and language as arguments, or use the menu system that the tool will provide us. For brevity's stake, I've used the arguments here, specifying that we want node as the runtime and javascript as the language, but you could change that to typescript if you'd prefer to use TypeScript.

    Once the init command is completed, you should have a .vscode folder, and the files .gitignore, host.json, local.settings.json, and package.json.

    Files generated by func initFiles generated by func init

    Adding a HTTP Trigger

    We have an empty Functions app so far, what we need to do next is create a Function that it will run, and we're going to make a HTTP Trigger Function, which is a Function that responds to HTTP requests. We'll use the func new command to create that:

    func new --template "HTTP Trigger" --name "get-commit-message"

    When this completes, we'll have a folder for the Function, using the name we provided, that contains the filesfunction.json and index.js. Let's open the function.json to understand it a little bit:

    {
    "bindings": [
    {
    "authLevel": "function",
    "type": "httpTrigger",
    "direction": "in",
    "name": "req",
    "methods": [
    "get",
    "post"
    ]
    },
    {
    "type": "http",
    "direction": "out",
    "name": "res"
    }
    ]
    }

    This file is used to tell Functions about the Function that we've created and what it does, so it knows to handle the appropriate events. We have a bindings node which contains the event bindings for our Azure Function. The first binding is using the type httpTrigger, which indicates that it'll be executed, or triggered, by a HTTP event, and the methods indicates that it's listening to both GET and POST (you can change this for the right HTTP methods that you want to support). The HTTP request information will be bound to a property in the Functions context called req, so we can access query strings, the request body, etc.

    The other binding we have has the direction of out, meaning that it's something that the Function will return to the called, and since this is a HTTP API, the type is http, indicating that we'll return a HTTP response, and that response will be on a property called res that we add to the Functions context.

    Let's go ahead and start the Function and call it:

    func start

    Starting the FunctionStarting the Function

    With the Function started, access the endpoint http://localhost:7071/api/get-commit-message via a browser or using cURL:

    curl http://localhost:7071/api/get-commit-message\?name\=ServerlessSeptember

    Hello from Azure FunctionsHello from Azure Functions

    🎉 CONGRATULATIONS

    You created and ran a JavaScript function app locally!

    Calling an external API

    It's time to update the Function to do what we want to do - call the GitHub Search API and get some commit messages. The endpoint that we'll be calling is https://api.github.com/search/commits?q=language:javascript.

    Note: The GitHub API is rate limited and this sample will call it unauthenticated, so be aware of that in your own testing.

    To call this API, we'll leverage the newly released fetch support in Node 18 and async/await, to make for a very clean Function.

    Open up the index.js file, and delete the contents of the existing Function, so we have a empty one:

    module.exports = async function (context, req) {

    }

    The default template uses CommonJS, but you can use ES Modules with Azure Functions if you prefer.

    Now we'll use fetch to call the API, and unpack the JSON response:

    module.exports = async function (context, req) {
    const res = await fetch("https://api.github.com/search/commits?q=language:javascript");
    const json = await res.json();
    const messages = json.items.map(item => item.commit.message);
    context.res = {
    body: {
    messages
    }
    };
    }

    To send a response to the client, we're setting the context.res property, where res is the name of the output binding in our function.json, and giving it a body that contains the commit messages.

    Run func start again, and call the endpoint:

    curl http://localhost:7071/api/get-commit-message

    The you'll get some commit messages:

    A series of commit messages from the GitHub Search APIA series of commit messages from the GitHub Search API

    🎉 CONGRATULATIONS

    There we go, we've created an Azure Function which is used as a proxy to another API, that we call (using native fetch in Node.js 18) and from which we return a subset of the JSON payload.

    Next Steps

    Other Triggers, Bindings

    This article focused on using the HTTPTrigger and relevant bindings, to build a serverless API using Azure Functions. How can you explore other supported bindings, with code samples to illustrate usage?

    Scenarios with Integrations

    Once you've tried out the samples, try building an end-to-end scenario by using these triggers to integrate seamlessly with other services. Here are some suggestions:

    Exercise: Support searching

    The GitHub Search API allows you to provide search parameters via the q query string. In this sample, we hard-coded it to be language:javascript, but as a follow-on exercise, expand the Function to allow the caller to provide the search terms as a query string to the Azure Function, which is passed to the GitHub Search API. Hint - have a look at the req argument.

    Resources

    - - + + \ No newline at end of file diff --git a/blog/tags/30-days-of-serverless/page/19/index.html b/blog/tags/30-days-of-serverless/page/19/index.html index 5af1f3ac9c..e04e418c20 100644 --- a/blog/tags/30-days-of-serverless/page/19/index.html +++ b/blog/tags/30-days-of-serverless/page/19/index.html @@ -14,13 +14,13 @@ - - + +

    20 posts tagged with "30-days-of-serverless"

    View All Tags

    · 9 min read
    Nitya Narasimhan

    Welcome to Day 3 of #30DaysOfServerless!

    Yesterday we learned core concepts and terminology for Azure Functions, the signature Functions-as-a-Service option on Azure. Today we take our first steps into building and deploying an Azure Functions app, and validate local development setup.

    Ready? Let's go.


    What We'll Cover


    Developer Guidance

    Before we jump into development, let's familiarize ourselves with language-specific guidance from the Azure Functions Developer Guide. We'll review the JavaScript version but guides for F#, Java, Python, C# and PowerShell are also available.

    1. A function is defined by two things: code (written in a supported programming language) and configuration (specified in a functions.json file, declaring the triggers, bindings and other context for execution).

    2. A function app is the unit of deployment for your functions, and is associated with a single execution context or runtime. It can contain multiple functions, but they must be in the same language.

    3. A host configuration is runtime-specific configuration that affects all functions running in a given function app instance. It is defined in a host.json file.

    4. A recommended folder structure is defined for the function app, but may vary based on the programming language used. Check the documentation on folder structures to learn the default for your preferred language.

    Here's an example of the JavaScript folder structure for a function app containing two functions with some shared dependencies. Note that host.json (runtime configuration) is defined once, in the root directory. And function.json is defined separately for each function.

    FunctionsProject
    | - MyFirstFunction
    | | - index.js
    | | - function.json
    | - MySecondFunction
    | | - index.js
    | | - function.json
    | - SharedCode
    | | - myFirstHelperFunction.js
    | | - mySecondHelperFunction.js
    | - node_modules
    | - host.json
    | - package.json
    | - local.settings.json

    We'll dive into what the contents of these files look like, when we build and deploy the first function. We'll cover local.settings.json in the About Local Testing section at the end.


    My First Function App

    The documentation provides quickstart options for all supported languages. We'll walk through the JavaScript versions in this article. You have two options for development:

    I'm a huge fan of VS Code - so I'll be working through that tutorial today.

    PRE-REQUISITES

    Don't forget to validate your setup by checking the versions of installed software.

    Install VSCode Extension

    Installing the Visual Studio Code extension should automatically open this page in your IDE with similar quickstart instructions, but potentially more recent screenshots.

    Visual Studio Code Extension for VS Code

    Note that it may make sense to install the Azure tools for Visual Studio Code extensions pack if you plan on working through the many projects in Serverless September. This includes the Azure Functions extension by default.

    Create First Function App

    Walk through the Create local [project] steps of the quickstart. The process is quick and painless and scaffolds out this folder structure and files. Note the existence (and locations) of functions.json and host.json files.

    Final screenshot for VS Code workflow

    Explore the Code

    Check out the functions.json configuration file. It shows that the function is activated by an httpTrigger with an input binding (tied to req payload) and an output binding (tied to res payload). And it supports both GET and POST requests on the exposed URL.

    {
    "bindings": [
    {
    "authLevel": "anonymous",
    "type": "httpTrigger",
    "direction": "in",
    "name": "req",
    "methods": [
    "get",
    "post"
    ]
    },
    {
    "type": "http",
    "direction": "out",
    "name": "res"
    }
    ]
    }

    Check out index.js - the function implementation. We see it logs a message to the console when invoked. It then extracts a name value from the input payload (req) and crafts a different responseMessage based on the presence/absence of a valid name. It returns this response in the output payload (res).

    module.exports = async function (context, req) {
    context.log('JavaScript HTTP trigger function processed a request.');

    const name = (req.query.name || (req.body && req.body.name));
    const responseMessage = name
    ? "Hello, " + name + ". This HTTP triggered function executed successfully."
    : "This HTTP triggered function executed successfully. Pass a name in the query string or in the request body for a personalized response.";

    context.res = {
    // status: 200, /* Defaults to 200 */
    body: responseMessage
    };
    }

    Preview Function App Locally

    You can now run this function app locally using Azure Functions Core Tools. VS Code integrates seamlessly with this CLI-based tool, making it possible for you to exploit all its capabilities without leaving the IDE. In fact, the workflow will even prompt you to install those tools if they didn't already exist in your local dev environment.

    Now run the function app locally by clicking on the "Run and Debug" icon in the activity bar (highlighted, left) and pressing the "▶️" (Attach to Node Functions) to start execution. On success, your console output should show something like this.

    Final screenshot for VS Code workflow

    You can test the function locally by visiting the Function Url shown (http://localhost:7071/api/HttpTrigger1) or by opening the Workspace region of the Azure extension, and selecting the Execute Function now menu item as shown.

    Final screenshot for VS Code workflow

    In the latter case, the Enter request body popup will show a pre-populated request of {"name":"Azure"} that you can submit.

    Final screenshot for VS Code workflow

    On successful execution, your VS Code window will show a notification as follows. Take note of the console output - it shows the message encoded in index.js.

    Final screenshot for VS Code workflow

    You can also visit the deployed function URL directly in a local browser - testing the case for a request made with no name payload attached. Note how the response in the browser now shows the non-personalized version of the message!

    Final screenshot for VS Code workflow

    🎉 Congratulations

    You created and ran a function app locally!

    (Re)Deploy to Azure

    Now, just follow the creating a function app in Azure steps to deploy it to Azure, using an active subscription! The deployed app resource should now show up under the Function App Resources where you can click Execute Function Now to test the Azure-deployed version instead. You can also look up the function URL in the portal and visit that link in your local browser to trigger the function without the name context.

    🎉 Congratulations

    You have an Azure-hosted serverless function app!

    Challenge yourself and try to change the code and redeploy to Azure to return something different. You have effectively created a serverless API endpoint!


    About Core Tools

    That was a lot to cover! In the next few days we'll have more examples for Azure Functions app development - focused on different programming languages. So let's wrap today's post by reviewing two helpful resources.

    First, let's talk about Azure Functions Core Tools - the command-line tool that lets you develop, manage, and deploy, Azure Functions projects from your local development environment. It is used transparently by the VS Code extension - but you can use it directly from a terminal for a powerful command-line end-to-end developer experience! The Core Tools commands are organized into the following contexts:

    Learn how to work with Azure Functions Core Tools. Not only can it help with quick command execution, it can also be invaluable for debugging issues that may not always be visible or understandable in an IDE.

    About Local Testing

    You might have noticed that the scaffold also produced a local.settings.json file. What is that and why is it useful? By definition, the local.settings.json file "stores app settings and settings used by local development tools. Settings in the local.settings.json file are used only when you're running your project locally."

    Read the guidance on Code and test Azure Functions Locally to learn more about how to configure development environments locally, for your preferred programming language, to support testing and debugging on the local Functions runtime.

    Exercise

    We made it! Now it's your turn!! Here are a few things you can try to apply what you learned and reinforce your understanding:

    Resources

    Bookmark and visit the #30DaysOfServerless Collection. It's the one-stop collection of resources we will keep updated with links to relevant documentation and learning resources.

    - - + + \ No newline at end of file diff --git a/blog/tags/30-days-of-serverless/page/2/index.html b/blog/tags/30-days-of-serverless/page/2/index.html index 5f241c001f..9874468c38 100644 --- a/blog/tags/30-days-of-serverless/page/2/index.html +++ b/blog/tags/30-days-of-serverless/page/2/index.html @@ -14,13 +14,13 @@ - - + +

    20 posts tagged with "30-days-of-serverless"

    View All Tags

    · 14 min read
    Justin Yoo

    Welcome to Day 28 of #30DaysOfServerless!

    Since it's the serverless end-to-end week, I'm going to discuss how to use a serverless application Azure Functions with OpenAPI extension to be seamlessly integrated with Power Platform custom connector through Azure API Management - in a post I call "Where am I? My GPS Location with Serverless Power Platform Custom Connector"

    OK. Are you ready? Let's get started!


    What We'll Cover

    • What is Power Platform custom connector?
    • Proxy app to Google Maps and Naver Map API
    • API Management integration
    • Two ways of building custom connector
    • Where am I? Power Apps app
    • Exercise: Try this yourself!
    • Resources: For self-study!


    SAMPLE REPO

    Want to follow along? Check out the sample app on GitHub repository used in this post.

    What is Power Platform custom connector?

    Power Platform is a low-code/no-code application development tool for fusion teams that consist of a group of people. Those people come from various disciplines, including field experts (domain experts), IT professionals and professional developers, to draw business values successfully. Within the fusion team, the domain experts become citizen developers or low-code developers by Power Platform. In addition, Making Power Platform more powerful is that it offers hundreds of connectors to other Microsoft 365 and third-party services like SAP, ServiceNow, Salesforce, Google, etc.

    However, what if you want to use your internal APIs or APIs not yet offering their official connectors? Here's an example. If your company has an inventory management system, and you want to use it within your Power Apps or Power Automate. That point is exactly where Power Platform custom connectors is necessary.

    Inventory Management System for Power Apps

    Therefore, Power Platform custom connectors enrich those citizen developers' capabilities because those connectors can connect any API applications for the citizen developers to use.

    In this post, let's build a custom connector that provides a static map image generated by Google Maps API and Naver Map API using your GPS location.

    Proxy app to Google Maps and Naver Map API

    First, let's build an Azure Functions app that connects to Google Maps and Naver Map. Suppose that you've already got the API keys for both services. If you haven't yet, get the keys first by visiting here for Google and here for Naver. Then, store them to local.settings.json within your Azure Functions app.

    {
    "Values": {
    ...
    "Maps__Google__ApiKey": "<GOOGLE_MAPS_API_KEY>",
    "Maps__Naver__ClientId": "<NAVER_MAP_API_CLIENT_ID>",
    "Maps__Naver__ClientSecret": "<NAVER_MAP_API_CLIENT_SECRET>"
    }
    }

    Here's the sample logic to get the static image from Google Maps API. It takes the latitude and longitude of your current location and image zoom level, then returns the static map image. There are a few hard-coded assumptions, though:

    • The image size should be 400x400.
    • The image should be in .png format.
    • The marker should show be red and show my location.
    public class GoogleMapService : IMapService
    {
    public async Task<byte[]> GetMapAsync(HttpRequest req)
    {
    var latitude = req.Query["lat"];
    var longitude = req.Query["long"];
    var zoom = (string)req.Query["zoom"] ?? "14";

    var sb = new StringBuilder();
    sb.Append("https://maps.googleapis.com/maps/api/staticmap")
    .Append($"?center={latitude},{longitude}")
    .Append("&size=400x400")
    .Append($"&zoom={zoom}")
    .Append($"&markers=color:red|{latitude},{longitude}")
    .Append("&format=png32")
    .Append($"&key={this._settings.Google.ApiKey}");
    var requestUri = new Uri(sb.ToString());

    var bytes = await this._http.GetByteArrayAsync(requestUri).ConfigureAwait(false);

    return bytes;
    }
    }

    The NaverMapService class has a similar logic with the same input and assumptions. Here's the code:

    public class NaverMapService : IMapService
    {
    public async Task<byte[]> GetMapAsync(HttpRequest req)
    {
    var latitude = req.Query["lat"];
    var longitude = req.Query["long"];
    var zoom = (string)req.Query["zoom"] ?? "13";

    var sb = new StringBuilder();
    sb.Append("https://naveropenapi.apigw.ntruss.com/map-static/v2/raster")
    .Append($"?center={longitude},{latitude}")
    .Append("&w=400")
    .Append("&h=400")
    .Append($"&level={zoom}")
    .Append($"&markers=color:blue|pos:{longitude}%20{latitude}")
    .Append("&format=png")
    .Append("&lang=en");
    var requestUri = new Uri(sb.ToString());

    this._http.DefaultRequestHeaders.Clear();
    this._http.DefaultRequestHeaders.Add("X-NCP-APIGW-API-KEY-ID", this._settings.Naver.ClientId);
    this._http.DefaultRequestHeaders.Add("X-NCP-APIGW-API-KEY", this._settings.Naver.ClientSecret);

    var bytes = await this._http.GetByteArrayAsync(requestUri).ConfigureAwait(false);

    return bytes;
    }
    }

    Let's take a look at the function endpoints. Here's for the Google Maps and Naver Map. As the GetMapAsync(req) method returns a byte array value, you need to transform it as FileContentResult, with the content type of image/png.

    // Google Maps
    public class GoogleMapsTrigger
    {
    [FunctionName(nameof(GoogleMapsTrigger.GetGoogleMapImage))]
    public async Task<IActionResult> GetGoogleMapImage(
    [HttpTrigger(AuthorizationLevel.Anonymous, "GET", Route = "google/image")] HttpRequest req)
    {
    this._logger.LogInformation("C# HTTP trigger function processed a request.");

    var bytes = await this._service.GetMapAsync(req).ConfigureAwait(false);

    return new FileContentResult(bytes, "image/png");
    }
    }

    // Naver Map
    public class NaverMapsTrigger
    {
    [FunctionName(nameof(NaverMapsTrigger.GetNaverMapImage))]
    public async Task<IActionResult> GetNaverMapImage(
    [HttpTrigger(AuthorizationLevel.Anonymous, "GET", Route = "naver/image")] HttpRequest req)
    {
    this._logger.LogInformation("C# HTTP trigger function processed a request.");

    var bytes = await this._service.GetMapAsync(req).ConfigureAwait(false);

    return new FileContentResult(bytes, "image/png");
    }
    }

    Then, add the OpenAPI capability to each function endpoint. Here's the example:

    // Google Maps
    public class GoogleMapsTrigger
    {
    [FunctionName(nameof(GoogleMapsTrigger.GetGoogleMapImage))]
    // ⬇️⬇️⬇️ Add decorators provided by the OpenAPI extension ⬇️⬇️⬇️
    [OpenApiOperation(operationId: nameof(GoogleMapsTrigger.GetGoogleMapImage), tags: new[] { "google" })]
    [OpenApiParameter(name: "lat", In = ParameterLocation.Query, Required = true, Type = typeof(string), Description = "The **latitude** parameter")]
    [OpenApiParameter(name: "long", In = ParameterLocation.Query, Required = true, Type = typeof(string), Description = "The **longitude** parameter")]
    [OpenApiParameter(name: "zoom", In = ParameterLocation.Query, Required = false, Type = typeof(string), Description = "The **zoom level** parameter &ndash; Default value is `14`")]
    [OpenApiResponseWithBody(statusCode: HttpStatusCode.OK, contentType: "image/png", bodyType: typeof(byte[]), Description = "The map image as an OK response")]
    // ⬆️⬆️⬆️ Add decorators provided by the OpenAPI extension ⬆️⬆️⬆️
    public async Task<IActionResult> GetGoogleMapImage(
    [HttpTrigger(AuthorizationLevel.Anonymous, "GET", Route = "google/image")] HttpRequest req)
    {
    ...
    }
    }

    // Naver Map
    public class NaverMapsTrigger
    {
    [FunctionName(nameof(NaverMapsTrigger.GetNaverMapImage))]
    // ⬇️⬇️⬇️ Add decorators provided by the OpenAPI extension ⬇️⬇️⬇️
    [OpenApiOperation(operationId: nameof(NaverMapsTrigger.GetNaverMapImage), tags: new[] { "naver" })]
    [OpenApiParameter(name: "lat", In = ParameterLocation.Query, Required = true, Type = typeof(string), Description = "The **latitude** parameter")]
    [OpenApiParameter(name: "long", In = ParameterLocation.Query, Required = true, Type = typeof(string), Description = "The **longitude** parameter")]
    [OpenApiParameter(name: "zoom", In = ParameterLocation.Query, Required = false, Type = typeof(string), Description = "The **zoom level** parameter &ndash; Default value is `13`")]
    [OpenApiResponseWithBody(statusCode: HttpStatusCode.OK, contentType: "image/png", bodyType: typeof(byte[]), Description = "The map image as an OK response")]
    // ⬆️⬆️⬆️ Add decorators provided by the OpenAPI extension ⬆️⬆️⬆️
    public async Task<IActionResult> GetNaverMapImage(
    [HttpTrigger(AuthorizationLevel.Anonymous, "GET", Route = "naver/image")] HttpRequest req)
    {
    ...
    }
    }

    Run the function app in the local. Here are the latitude and longitude values for Seoul, Korea.

    • latitude: 37.574703
    • longitude: 126.978519

    Google Map for Seoul

    It seems to be working! Let's deploy it to Azure.

    API Management integration

    Visual Studio 2022 provides a built-in deployment tool for Azure Functions app onto Azure. In addition, the deployment tool supports seamless integration with Azure API Management as long as your Azure Functions app enables the OpenAPI capability. In this post, I'm going to use this feature. Right-mouse click on the Azure Functions project and select the "Publish" menu.

    Visual Studio context menu for publish

    Then, you will see the publish screen. Click the "➕ New" button to create a new publish profile.

    Create a new publish profile

    Choose "Azure" and click the "Next" button.

    Choose the target platform for publish

    Select the app instance. This time simply pick up the "Azure Function App (Windows)" option, then click "Next".

    Choose the target OS for publish

    If you already provision an Azure Function app instance, you will see it on the screen. Otherwise, create a new one. Then, click "Next".

    Choose the target instance for publish

    In the next step, you are asked to choose the Azure API Management instance for integration. Choose one, or create a new one. Then, click "Next".

    Choose the APIM instance for integration

    Finally, select the publish method either local publish or GitHub Actions workflow. Let's pick up the local publish method for now. Then, click "Finish".

    Choose the deployment type

    The publish profile has been created. Click "Close" to move on.

    Publish profile created

    Now the function app is ready for deployment. Click the "Publish" button and see how it goes.

    Publish function app

    The Azure function app has been deployed and integrated with the Azure API Management instance.

    Function app published

    Go to the published function app site, and everything looks OK.

    Function app on Azure

    And API Management shows the function app integrated perfectly.

    Function app integrated with APIM

    Now, you are ready to create a custom connector. Let's move on.

    Two ways of building custom connector

    There are two ways to create a custom connector.

    Export custom connector from API Management

    First, you can directly use the built-in API Management feature. Then, click the ellipsis icon and select the "Create Power Connector" menu.

    Create Power Connector menu

    Then, you are redirected to this screen. While the "API" and "API display name" fields are pre-populated, you need to choose the Power Platform environment tied to your tenant. Choose an environment, click "Authenticate", and click "Create".

    Create custom connector screen

    Check your custom connector on Power Apps or Power Automate side.

    Custom connector created on Power Apps

    However, there's a caveat to this approach. Because it's tied to your tenant, you should use the second approach if you want to use this custom connector on the other tenant.

    Import custom connector from OpenAPI document or URL

    Click the ellipsis icon again and select the "Export" menu.

    Export menu

    On the Export API screen, choose the "OpenAPI v2 (JSON)" panel because Power Platform custom connector currently accepts version 2 of the OpenAPI document.

    Select OpenAPI v2

    Download the OpenAPI document to your local computer and move to your Power Apps or Power Automate page under your desired environment. I'm going to use the Power Automate page. First, go to the "Data" ➡️ "Custom connectors" page. Then, click the "➕ New custom connector" ➡️ "Import an OpenAPI file" at the top right corner.

    New custom connector

    When a modal pops up, give the custom connector name and import the OpenAPI document exported above. Then, click "Continue".

    Import custom connector

    Actually, that's it! Next, click the "✔️ Create connector" button to create the connector.

    Create custom connector

    Go back to the custom connector page, and you will see the "Maps API" custom connector you just created.

    Custom connector imported

    So, you are ready to create a Power Apps app to display your location on Google Maps or Naver Map! Let's move on.

    Where am I? Power Apps app

    Open the Power Apps Studio, and create an empty canvas app, named Who am I with a phone layout.

    Custom connector integration

    To use the custom connector created above, you need to add it to the Power App. Click the cylinder icon on the left and click the "Add data" button.

    Add custom connector to data pane

    Search the custom connector name, "Maps API", and click the custom connector to add.

    Search custom connector

    To use the custom connector, you also need to create a connection to it. Click the "Connect" button and move on.

    Create connection to custom connector

    Now, you've got the connection to the custom connector.

    Connection to custom connector ready

    Controls

    Let's build the Power Apps app. First of all, put three controls Image, Slider and Button onto the canvas.

    Power Apps control added

    Click the "Screen1" control and change the value on the property "OnVisible" to the formula below. The formula stores the current slider value in the zoomlevel collection.

    ClearCollect(
    zoomlevel,
    Slider1.Value
    )

    Click the "Botton1" control and change the value on the property "OnSelected" to the formula below. It passes the current latitude, longitude and zoom level to the custom connector and receives the image data. The received image data is stored in the result collection.

    ClearCollect(
    result,
    MAPS.GetGoogleMapImage(
    Location.Latitude,
    Location.Longitude,
    { zoom: First(zoomlevel).Value }
    )
    )

    Click the "Image1" control and change the value on the property "Image" to the formula below. It gets the image data from the result collection.

    First(result).Url

    Click the "Slider1" control and change the value on the property "OnChange" to the formula below. It stores the current slider value to the zoomlevel collection, followed by calling the custom connector to get the image data against the current location.

    ClearCollect(
    zoomlevel,
    Slider1.Value
    );
    ClearCollect(
    result,
    MAPS.GetGoogleMapImage(
    Location.Latitude,
    Location.Longitude,
    { zoom: First(zoomlevel).Value }
    )
    )

    That seems to be OK. Let's click the "Where am I?" button. But it doesn't show the image. The First(result).Url value is actually similar to this:

    appres://blobmanager/1090a86393a843adbfcf428f0b90e91b/1

    It's the image reference value somewhere you can't get there.

    Workaround Power Automate workflow

    Therefore, you need a workaround using a Power Automate workflow to sort out this issue. Open the Power Automate Studio, create an instant cloud flow with the Power App trigger, and give it the "Where am I" name. Then add input parameters of lat, long and zoom.

    Power Apps trigger on Power Automate workflow

    Add custom connector action to get the map image.

    Select action to get the Google Maps image

    In the action, pass the appropriate parameters to the action.

    Pass parameters to the custom connector action

    Add a "Response" action and put the following values into each field.

    • "Body" field:

      {
      "base64Image": <power_automate_expression>
      }

      The <power_automate_expression> should be concat('data:', body('GetGoogleMapImage')?['$content-type'], ';base64,', body('GetGoogleMapImage')?['$content']).

    • "Response Body JSON Schema" field:

      {
      "type": "object",
      "properties": {
      "base64Image": {
      "type": "string"
      }
      }
      }

    Format the Response action

    Let's return to the Power Apps Studio and add the Power Automate workflow you created.

    Add Power Automate workflow

    Select "Button1" and change the value on the property "OnSelect" below. It replaces the direct call to the custom connector with the Power Automate workflow.

    ClearCollect(
    result,
    WhereamI.Run(
    Location.Latitude,
    Location.Longitude,
    First(zoomlevel).Value
    )
    )

    Also, change the value on the property "OnChange" of the "Slider1" control below, replacing the custom connector call with the Power Automate workflow call.

    ClearCollect(
    zoomlevel,
    Slider1.Value
    );
    ClearCollect(
    result,
    WhereamI.Run(
    Location.Latitude,
    Location.Longitude,
    First(zoomlevel).Value
    )
    )

    And finally, change the "Image1" control's "Image" property value below.

    First(result).base64Image

    The workaround has been applied. Click the "Where am I?" button to see your current location from Google Maps.

    Run Power Apps app #1

    If you change the slider left or right, you will see either the zoomed-in image or the zoomed-out image.

    Run Power Apps app #2

    Now, you've created a Power Apps app to show your current location using:

    • Google Maps API through the custom connector, and
    • Custom connector written in Azure Functions with OpenAPI extension!

    Exercise: Try this yourself!

    You can fork this GitHub repository to your account and play around with it to see how the custom connector works. After forking the repository, make sure that you create all the necessary secrets to your repository documented in the README file.

    Then, click the "Deploy to Azure" button, and it will provision all necessary Azure resources and deploy an Azure Functions app for a custom connector.

    Deploy To Azure

    Once everything is deployed successfully, try to create a Power Apps app and Power Automate workflow to see your current location in real-time!

    Resources: For self-study!

    Want to know more about Power Platform custom connector and Azure Functions OpenAPI extension? Here are several resources you can take a look at:

    - - + + \ No newline at end of file diff --git a/blog/tags/30-days-of-serverless/page/20/index.html b/blog/tags/30-days-of-serverless/page/20/index.html index 62174bf05c..8b60dbd184 100644 --- a/blog/tags/30-days-of-serverless/page/20/index.html +++ b/blog/tags/30-days-of-serverless/page/20/index.html @@ -14,15 +14,15 @@ - - + +

    20 posts tagged with "30-days-of-serverless"

    View All Tags

    · 9 min read
    Nitya Narasimhan

    Welcome to Day 2️⃣ of #30DaysOfServerless!

    Today, we kickstart our journey into serveless on Azure with a look at Functions As a Service. We'll explore Azure Functions - from core concepts to usage patterns.

    Ready? Let's Go!


    What We'll Cover

    • What is Functions-as-a-Service? (FaaS)
    • What is Azure Functions?
    • Triggers, Bindings and Custom Handlers
    • What is Durable Functions?
    • Orchestrators, Entity Functions and Application Patterns
    • Exercise: Take the Cloud Skills Challenge!
    • Resources: #30DaysOfServerless Collection.


    1. What is FaaS?

    Faas stands for Functions As a Service (FaaS). But what does that mean for us as application developers? We know that building and deploying modern applications at scale can get complicated and it starts with us needing to take decisions on Compute. In other words, we need to answer this question: "where should I host my application given my resource dependencies and scaling requirements?"

    this useful flowchart

    Azure has this useful flowchart (shown below) to guide your decision-making. You'll see that hosting options generally fall into three categories:

    • Infrastructure as a Service (IaaS) - where you provision and manage Virtual Machines yourself (cloud provider manages infra).
    • Platform as a Service (PaaS) - where you use a provider-managed hosting environment like Azure Container Apps.
    • Functions as a Service (FaaS) - where you forget about hosting environments and simply deploy your code for the provider to run.

    Here, "serverless" compute refers to hosting options where we (as developers) can focus on building apps without having to manage the infrastructure. See serverless compute options on Azure for more information.


    2. Azure Functions

    Azure Functions is the Functions-as-a-Service (FaaS) option on Azure. It is the ideal serverless solution if your application is event-driven with short-lived workloads. With Azure Functions, we develop applications as modular blocks of code (functions) that are executed on demand, in response to configured events (triggers). This approach brings us two advantages:

    • It saves us money. We only pay for the time the function runs.
    • It scales with demand. We have 3 hosting plans for flexible scaling behaviors.

    Azure Functions can be programmed in many popular languages (C#, F#, Java, JavaScript, TypeScript, PowerShell or Python), with Azure providing language-specific handlers and default runtimes to execute them.

    Concept: Custom Handlers
    • What if we wanted to program in a non-supported language?
    • Or we wanted to use a different runtime for a supported language?

    Custom Handlers have you covered! These are lightweight webservers that can receive and process input events from the Functions host - and return responses that can be delivered to any output targets. By this definition, custom handlers can be implemented by any language that supports receiving HTTP events. Check out the quickstart for writing a custom handler in Rust or Go.

    Custom Handlers

    Concept: Trigger and Bindings

    We talked about what functions are (code blocks). But when are they invoked or executed? And how do we provide inputs (arguments) and retrieve outputs (results) from this execution?

    This is where triggers and bindings come in.

    • Triggers define how a function is invoked and what associated data it will provide. A function must have exactly one trigger.
    • Bindings declaratively define how a resource is connected to the function. The resource or binding can be of type input, output, or both. Bindings are optional. A Function can have multiple input, output bindings.

    Azure Functions comes with a number of supported bindings that can be used to integrate relevant services to power a specific scenario. For instance:

    • HTTP Triggers - invokes the function in response to an HTTP request. Use this to implement serverless APIs for your application.
    • Event Grid Triggers invokes the function on receiving events from an Event Grid. Use this to process events reactively, and potentially publish responses back to custom Event Grid topics.
    • SignalR Service Trigger invokes the function in response to messages from Azure SignalR, allowing your application to take actions with real-time contexts.

    Triggers and bindings help you abstract your function's interfaces to other components it interacts with, eliminating hardcoded integrations. They are configured differently based on the programming language you use. For example - JavaScript functions are configured in the functions.json file. Here's an example of what that looks like.

    {
    "disabled":false,
    "bindings":[
    // ... bindings here
    {
    "type": "bindingType",
    "direction": "in",
    "name": "myParamName",
    // ... more depending on binding
    }
    ]
    }

    The key thing to remember is that triggers and bindings have a direction property - triggers are always in, input bindings are in and output bindings are out. Some bindings can support a special inout direction.

    The documentation has code examples for bindings to popular Azure services. Here's an example of the bindings and trigger configuration for a BlobStorage use case.

    // function.json configuration

    {
    "bindings": [
    {
    "queueName": "myqueue-items",
    "connection": "MyStorageConnectionAppSetting",
    "name": "myQueueItem",
    "type": "queueTrigger",
    "direction": "in"
    },
    {
    "name": "myInputBlob",
    "type": "blob",
    "path": "samples-workitems/{queueTrigger}",
    "connection": "MyStorageConnectionAppSetting",
    "direction": "in"
    },
    {
    "name": "myOutputBlob",
    "type": "blob",
    "path": "samples-workitems/{queueTrigger}-Copy",
    "connection": "MyStorageConnectionAppSetting",
    "direction": "out"
    }
    ],
    "disabled": false
    }

    The code below shows the function implementation. In this scenario, the function is triggered by a queue message carrying an input payload with a blob name. In response, it copies that data to the resource associated with the output binding.

    // function implementation

    module.exports = async function(context) {
    context.log('Node.js Queue trigger function processed', context.bindings.myQueueItem);
    context.bindings.myOutputBlob = context.bindings.myInputBlob;
    };
    Concept: Custom Bindings

    What if we have a more complex scenario that requires bindings for non-supported resources?

    There is an option create custom bindings if necessary. We don't have time to dive into details here but definitely check out the documentation


    3. Durable Functions

    This sounds great, right?. But now, let's talk about one challenge for Azure Functions. In the use cases so far, the functions are stateless - they take inputs at runtime if necessary, and return output results if required. But they are otherwise self-contained, which is great for scalability!

    But what if I needed to build more complex workflows that need to store and transfer state, and complete operations in a reliable manner? Durable Functions are an extension of Azure Functions that makes stateful workflows possible.

    Concept: Orchestrator Functions

    How can I create workflows that coordinate functions?

    Durable Functions use orchestrator functions to coordinate execution of other Durable functions within a given Functions app. These functions are durable and reliable. Later in this post, we'll talk briefly about some application patterns that showcase popular orchestration scenarios.

    Concept: Entity Functions

    How do I persist and manage state across workflows?

    Entity Functions provide explicit state mangement for Durable Functions, defining operations to read and write state to durable entities. They are associated with a special entity trigger for invocation. These are currently available only for a subset of programming languages so check to see if they are supported for your programming language of choice.

    USAGE: Application Patterns

    Durable Functions are a fascinating topic that would require a separate, longer post, to do justice. For now, let's look at some application patterns that showcase the value of these starting with the simplest one - Function Chaining as shown below:

    Function Chaining

    Here, we want to execute a sequence of named functions in a specific order. As shown in the snippet below, the orchestrator function coordinates invocations on the given functions in the desired sequence - "chaining" inputs and outputs to establish the workflow. Take note of the yield keyword. This triggers a checkpoint, preserving the current state of the function for reliable operation.

    const df = require("durable-functions");

    module.exports = df.orchestrator(function*(context) {
    try {
    const x = yield context.df.callActivity("F1");
    const y = yield context.df.callActivity("F2", x);
    const z = yield context.df.callActivity("F3", y);
    return yield context.df.callActivity("F4", z);
    } catch (error) {
    // Error handling or compensation goes here.
    }
    });

    Other application patterns for durable functions include:

    There's a lot more to explore but we won't have time to do that today. Definitely check the documentation and take a minute to read the comparison with Azure Logic Apps to understand what each technology provides for serverless workflow automation.


    4. Exercise

    That was a lot of information to absorb! Thankfully, there are a lot of examples in the documentation that can help put these in context. Here are a couple of exercises you can do, to reinforce your understanding of these concepts.


    5. What's Next?

    The goal for today was to give you a quick tour of key terminology and concepts related to Azure Functions. Tomorrow, we dive into the developer experience, starting with core tools for local development and ending by deploying our first Functions app.

    Want to do some prep work? Here are a few useful links:


    6. Resources


    - - + + \ No newline at end of file diff --git a/blog/tags/30-days-of-serverless/page/3/index.html b/blog/tags/30-days-of-serverless/page/3/index.html index abad29df1f..81fc3d95ac 100644 --- a/blog/tags/30-days-of-serverless/page/3/index.html +++ b/blog/tags/30-days-of-serverless/page/3/index.html @@ -14,13 +14,13 @@ - - + +

    20 posts tagged with "30-days-of-serverless"

    View All Tags

    · 7 min read
    Brian Benz

    Welcome to Day 25 of #30DaysOfServerless!

    Azure Container Apps enable application code packaged in containers to run and scale without the overhead of managing cloud infrastructure and container orchestration. In this post I'll show you how to deploy a Java application running on Spring Boot in a container to Azure Container Registry and Azure Container Apps.


    What We'll Cover

    • Introduction to Deploying Java containers in the cloud
    • Step-by-step: Deploying to Azure Container Registry
    • Step-by-step: Deploying and running on Azure Container Apps
    • Resources: For self-study!


    Deploy Java containers to cloud

    We'll deploy a Java application running on Spring Boot in a container to Azure Container Registry and Azure Container Apps. Here are the main steps:

    • Create Azure Container Registry (ACR) on Azure portal
    • Create Azure Container App (ACA) on Azure portal.
    • Deploy code to Azure Container Registry from the Azure CLI.
    • Deploy container from ACR to ACA using the Azure portal.
    PRE-REQUISITES

    Sign in to Azure from the CLI using the az login command, and follow the prompts in your browser to complete the authentication process. Also, ensure you're running the latest version of the CLI by using the az upgrade command.

    1. Get Sample Code

    Fork and clone the sample GitHub repo to your local machine. Navigate to the and click Fork in the top-right corner of the page.

    The example code that we're using is a very basic containerized Spring Boot example. There are a lot more details to learn about Spring boot apps in docker, for a deep dive check out this Spring Boot Guide

    2. Run Sample Locally (Optional)

    If you have docker installed locally, you can optionally test the code on your local machine. Navigate to the root directory of the forked repository and run the following commands:

    docker build -t spring-boot-docker-aca .
    docker run -p 8080:8080 spring-boot-docker-aca

    Open a browser and go to https://localhost:8080. You should see this message:

    Hello Docker World

    That indicates the the Spring Boot app is successfully running locally in a docker container.

    Next, let's set up an Azure Container Registry an an Azure Container App and deploy this container to the cloud!


    3. Step-by-step: Deploy to ACR

    To create a container registry from the portal dashboard, Select Create a resource > Containers > Container Registry.

    Navigate to container registry in portal

    In the Basics tab, enter values for Resource group and Registry name. The registry name must be unique within Azure, and contain 5-50 alphanumeric characters. Create a new resource group in the West US location named spring-boot-docker-aca. Select the 'Basic' SKU.

    Keep the default values for the remaining settings. Then select Review + create, then Create. When the Deployment succeeded message appears, select the container registry in the portal.

    Note the registry server name ending with azurecr.io. You will use this in the following steps when you push and pull images with Docker.

    3.1 Log into registry using the Azure CLI

    Before pushing and pulling container images, you must log in to the registry instance. Sign into the Azure CLI on your local machine, then run the az acr login command. For this step, use the registry name, not the server name ending with azurecr.io.

    From the command line, type:

    az acr login --name myregistryname

    The command returns Login Succeeded once completed.

    3.2 Build & deploy with az acr build

    Next, we're going to deploy the docker container we created earlier using the AZ ACR Build command. AZ ACR Build creates a docker build from local code and pushes the container to Azure Container Registry if the build is successful.

    Go to your local clone of the spring-boot-docker-aca repo in the command line, type:

    az acr build --registry myregistryname --image spring-boot-docker-aca:v1 .

    3.3 List container images

    Once the AZ ACR Build command is complete, you should be able to view the container as a repository in the registry. In the portal, open your registry and select Repositories, then select the spring-boot-docker-aca repository you created with docker push. You should also see the v1 image under Tags.

    4. Deploy on ACA

    Now that we have an image in the Azure Container Registry, we can deploy it to Azure Container Apps. For the first deployment, we'll pull the container from our ACR as part of the ACA setup.

    4.1 Create a container app

    We'll create the container app at the same place that we created the container registry in the Azure portal. From the portal, select Create a resource > Containers > Container App. In the Basics tab, set these values:

    4.2 Enter project details

    SettingAction
    SubscriptionYour Azure subscription.
    Resource groupUse the spring-boot-docker-aca resource group
    Container app nameEnter spring-boot-docker-aca.

    4.3 Create an environment

    1. In the Create Container App environment field, select Create new.

    2. In the Create Container App Environment page on the Basics tab, enter the following values:

      SettingValue
      Environment nameEnter my-environment.
      RegionSelect westus3.
    3. Select OK.

    4. Select the Create button at the bottom of the Create Container App Environment page.

    5. Select the Next: App settings button at the bottom of the page.

    5. App settings tab

    The App settings tab is where you connect to the ACR and pull the repository image:

    SettingAction
    Use quickstart imageUncheck the checkbox.
    NameEnter spring-boot-docker-aca.
    Image sourceSelect Azure Container Registry
    RegistrySelect your ACR from the list.
    ImageSelect spring-boot-docker-aca from the list.
    Image TagSelect v1 from the list.

    5.1 Application ingress settings

    SettingAction
    IngressSelect Enabled.
    Ingress visibilitySelect External to publicly expose your container app.
    Target portEnter 8080.

    5.2 Deploy the container app

    1. Select the Review and create button at the bottom of the page.
    2. Select Create.

    Once the deployment is successfully completed, you'll see the message: Your deployment is complete.

    5.3 Verify deployment

    In the portal, go to the Overview of your spring-boot-docker-aca Azure Container App, and click on the Application Url. You should see this message in the browser:

    Hello Docker World

    That indicates the the Spring Boot app is running in a docker container in your spring-boot-docker-aca Azure Container App.

    Resources: For self-study!

    Once you have an understanding of the basics in ths post, there is so much more to learn!

    Thanks for stopping by!

    - - + + \ No newline at end of file diff --git a/blog/tags/30-days-of-serverless/page/4/index.html b/blog/tags/30-days-of-serverless/page/4/index.html index cc8e131259..6dab6f2fb8 100644 --- a/blog/tags/30-days-of-serverless/page/4/index.html +++ b/blog/tags/30-days-of-serverless/page/4/index.html @@ -14,13 +14,13 @@ - - + +

    20 posts tagged with "30-days-of-serverless"

    View All Tags

    · 19 min read
    Alex Wolf

    Welcome to Day 24 of #30DaysOfServerless!

    We continue exploring E2E scenarios with this tutorial where you'll deploy a containerized ASP.NET Core 6.0 application to Azure Container Apps.

    The application consists of a front-end web app built using Blazor Server, as well as two Web API projects to manage data. These projects will exist as three separate containers inside of a shared container apps environment.


    What We'll Cover

    • Deploy ASP.NET Core 6.0 app to Azure Container Apps
    • Automate deployment workflows using GitHub Actions
    • Provision and deploy resources using Azure Bicep
    • Exercise: Try this yourself!
    • Resources: For self-study!


    Introduction

    Azure Container Apps enables you to run microservices and containerized applications on a serverless platform. With Container Apps, you enjoy the benefits of running containers while leaving behind the concerns of manually configuring cloud infrastructure and complex container orchestrators.

    In this tutorial, you'll deploy a containerized ASP.NET Core 6.0 application to Azure Container Apps. The application consists of a front-end web app built using Blazor Server, as well as two Web API projects to manage data. These projects will exist as three separate containers inside of a shared container apps environment.

    You will use GitHub Actions in combination with Bicep to deploy the application. These tools provide an approachable and sustainable solution for building CI/CD pipelines and working with Container Apps.

    PRE-REQUISITES

    Architecture

    In this tutorial, we'll setup a container app environment with a separate container for each project in the sample store app. The major components of the sample project include:

    • A Blazor Server front-end web app to display product information
    • A products API to list available products
    • An inventory API to determine how many products are in stock
    • GitHub Actions and Bicep templates to provision Azure resources and then build and deploy the sample app.

    You will explore these templates later in the tutorial.

    Public internet traffic should be proxied to the Blazor app. The back-end APIs should only be reachable via requests from the Blazor app inside the container apps environment. This setup can be achieved using container apps environment ingress configurations during deployment.

    An architecture diagram of the shopping app


    Project Sources

    Want to follow along? Fork the sample below. The tutorial can be completed with or without Dapr integration. Pick the path you feel comfortable in. Dapr provides various benefits that make working with Microservices easier - you can learn more in the docs. For this tutorial you will need GitHub and Azure CLI.

    PICK YOUR PATH

    To follow along with this tutorial, fork the relevant sample project below.

    You can run the app locally from Visual Studio:

    • Right click on the Blazor Store project and select Set as Startup Project.
    • Press the start button at the top of Visual Studio to run the app.
    • (Once running) start each API in the background by
    • right-clicking on the project node
    • selecting Debug --> Start without debugging.

    Once the Blazor app is running, you should see something like this:

    An architecture diagram of the shopping app


    Configuring Azure credentials

    In order to deploy the application to Azure through GitHub Actions, you first need to create a service principal. The service principal will allow the GitHub Actions process to authenticate to your Azure subscription to create resources and deploy code. You can learn more about Service Principals in the Azure CLI documentation. For this step you'll need to be logged into the Azure CLI.

    1) If you have not done so already, make sure to fork the sample project to your own GitHub account or organization.

    1) Once you have completed this step, create a service principal using the Azure CLI command below:

    ```azurecli
    $subscriptionId=$(az account show --query id --output tsv)
    az ad sp create-for-rbac --sdk-auth --name WebAndApiSample --role Contributor --scopes /subscriptions/$subscriptionId
    ```

    1) Copy the JSON output of the CLI command to your clipboard

    1) Under the settings tab of your forked GitHub repo, create a new secret named AzureSPN. The name is important to match the Bicep templates included in the project, which we'll review later. Paste the copied service principal values on your clipboard into the secret and save your changes. This new secret will be used by the GitHub Actions workflow to authenticate to Azure.

    :::image type="content" source="./img/dotnet/github-secrets.png" alt-text="A screenshot of adding GitHub secrets.":::

    Deploy using Github Actions

    You are now ready to deploy the application to Azure Container Apps using GitHub Actions. The sample application includes a GitHub Actions template that is configured to build and deploy any changes to a branch named deploy. The deploy branch does not exist in your forked repository by default, but you can easily create it through the GitHub user interface.

    1) Switch to the Actions tab along the top navigation of your GitHub repository. If you have not done so already, ensure that workflows are enabled by clicking the button in the center of the page.

    A screenshot showing how to enable GitHub actions

    1) Navigate to the main Code tab of your repository and select the main dropdown. Enter deploy into the branch input box, and then select Create branch: deploy from 'main'.

    A screenshot showing how to create the deploy branch

    1) On the new deploy branch, navigate down into the .github/workflows folder. You should see a file called deploy.yml, which contains the main GitHub Actions workflow script. Click on the file to view its content. You'll learn more about this file later in the tutorial.

    1) Click the pencil icon in the upper right to edit the document.

    1) Change the RESOURCE_GROUP_NAME: value to msdocswebappapis or another valid resource group name of your choosing.

    1) In the upper right of the screen, select Start commit and then Commit changes to commit your edit. This will persist the change to the file and trigger the GitHub Actions workflow to build and deploy the app.

    A screenshot showing how to commit changes

    1) Switch to the Actions tab along the top navigation again. You should see the workflow running to create the necessary resources and deploy the app. The workflow may take several minutes to run. When it completes successfully, all of the jobs should have a green checkmark icon next to them.

    The completed GitHub workflow.

    Explore the Azure resources

    Once the GitHub Actions workflow has completed successfully you can browse the created resources in the Azure portal.

    1) On the left navigation, select Resource Groups. Next,choose the msdocswebappapis resource group that was created by the GitHub Actions workflow.

    2) You should see seven resources available that match the screenshot and table descriptions below.

    The resources created in Azure.

    Resource nameTypeDescription
    inventoryContainer appThe containerized inventory API.
    msdocswebappapisacrContainer registryA registry that stores the built Container images for your apps.
    msdocswebappapisaiApplication insightsApplication insights provides advanced monitoring, logging and metrics for your apps.
    msdocswebappapisenvContainer apps environmentA container environment that manages networking, security and resource concerns. All of your containers live in this environment.
    msdocswebappapislogsLog Analytics workspaceA workspace environment for managing logging and analytics for the container apps environment
    productsContainer appThe containerized products API.
    storeContainer appThe Blazor front-end web app.

    3) You can view your running app in the browser by clicking on the store container app. On the overview page, click the Application Url link on the upper right of the screen.

    :::image type="content" source="./img/dotnet/application-url.png" alt-text="The link to browse the app.":::

    Understanding the GitHub Actions workflow

    The GitHub Actions workflow created and deployed resources to Azure using the deploy.yml file in the .github folder at the root of the project. The primary purpose of this file is to respond to events - such as commits to a branch - and run jobs to accomplish tasks. The deploy.yml file in the sample project has three main jobs:

    • Provision: Create the necessary resources in Azure, such as the container apps environment. This step leverages Bicep templates to create the Azure resources, which you'll explore in a moment.
    • Build: Create the container images for the three apps in the project and store them in the container registry.
    • Deploy: Deploy the container images to the different container apps created during the provisioning job.

    The deploy.yml file also accepts parameters to make the workflow more dynamic, such as setting the resource group name or the Azure region resources will be provisioned to.

    Below is a commented version of the deploy.yml file that highlights the essential steps.

    name: Build and deploy .NET application to Container Apps

    # Trigger the workflow on pushes to the deploy branch
    on:
    push:
    branches:
    - deploy

    env:
    # Set workflow variables
    RESOURCE_GROUP_NAME: msdocswebappapis

    REGION: eastus

    STORE_DOCKER: Store/Dockerfile
    STORE_IMAGE: store

    INVENTORY_DOCKER: Store.InventoryApi/Dockerfile
    INVENTORY_IMAGE: inventory

    PRODUCTS_DOCKER: Store.ProductApi/Dockerfile
    PRODUCTS_IMAGE: products

    jobs:
    # Create the required Azure resources
    provision:
    runs-on: ubuntu-latest

    steps:

    - name: Checkout to the branch
    uses: actions/checkout@v2

    - name: Azure Login
    uses: azure/login@v1
    with:
    creds: ${{ secrets.AzureSPN }}

    - name: Create resource group
    uses: azure/CLI@v1
    with:
    inlineScript: >
    echo "Creating resource group in Azure"
    echo "Executing 'az group create -l ${{ env.REGION }} -n ${{ env.RESOURCE_GROUP_NAME }}'"
    az group create -l ${{ env.REGION }} -n ${{ env.RESOURCE_GROUP_NAME }}

    # Use Bicep templates to create the resources in Azure
    - name: Creating resources
    uses: azure/CLI@v1
    with:
    inlineScript: >
    echo "Creating resources"
    az deployment group create --resource-group ${{ env.RESOURCE_GROUP_NAME }} --template-file '/github/workspace/Azure/main.bicep' --debug

    # Build the three app container images
    build:
    runs-on: ubuntu-latest
    needs: provision

    steps:

    - name: Checkout to the branch
    uses: actions/checkout@v2

    - name: Azure Login
    uses: azure/login@v1
    with:
    creds: ${{ secrets.AzureSPN }}

    - name: Set up Docker Buildx
    uses: docker/setup-buildx-action@v1

    - name: Login to ACR
    run: |
    set -euo pipefail
    access_token=$(az account get-access-token --query accessToken -o tsv)
    refresh_token=$(curl https://${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io/oauth2/exchange -v -d "grant_type=access_token&service=${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io&access_token=$access_token" | jq -r .refresh_token)
    docker login -u 00000000-0000-0000-0000-000000000000 --password-stdin ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io <<< "$refresh_token"

    - name: Build the products api image and push it to ACR
    uses: docker/build-push-action@v2
    with:
    push: true
    tags: ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io/${{ env.PRODUCTS_IMAGE }}:${{ github.sha }}
    file: ${{ env.PRODUCTS_DOCKER }}

    - name: Build the inventory api image and push it to ACR
    uses: docker/build-push-action@v2
    with:
    push: true
    tags: ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io/${{ env.INVENTORY_IMAGE }}:${{ github.sha }}
    file: ${{ env.INVENTORY_DOCKER }}

    - name: Build the frontend image and push it to ACR
    uses: docker/build-push-action@v2
    with:
    push: true
    tags: ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io/${{ env.STORE_IMAGE }}:${{ github.sha }}
    file: ${{ env.STORE_DOCKER }}

    # Deploy the three container images
    deploy:
    runs-on: ubuntu-latest
    needs: build

    steps:

    - name: Checkout to the branch
    uses: actions/checkout@v2

    - name: Azure Login
    uses: azure/login@v1
    with:
    creds: ${{ secrets.AzureSPN }}

    - name: Installing Container Apps extension
    uses: azure/CLI@v1
    with:
    inlineScript: >
    az config set extension.use_dynamic_install=yes_without_prompt

    az extension add --name containerapp --yes

    - name: Login to ACR
    run: |
    set -euo pipefail
    access_token=$(az account get-access-token --query accessToken -o tsv)
    refresh_token=$(curl https://${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io/oauth2/exchange -v -d "grant_type=access_token&service=${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io&access_token=$access_token" | jq -r .refresh_token)
    docker login -u 00000000-0000-0000-0000-000000000000 --password-stdin ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io <<< "$refresh_token"

    - name: Deploy Container Apps
    uses: azure/CLI@v1
    with:
    inlineScript: >
    az containerapp registry set -n products -g ${{ env.RESOURCE_GROUP_NAME }} --server ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io

    az containerapp update -n products -g ${{ env.RESOURCE_GROUP_NAME }} -i ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io/${{ env.PRODUCTS_IMAGE }}:${{ github.sha }}

    az containerapp registry set -n inventory -g ${{ env.RESOURCE_GROUP_NAME }} --server ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io

    az containerapp update -n inventory -g ${{ env.RESOURCE_GROUP_NAME }} -i ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io/${{ env.INVENTORY_IMAGE }}:${{ github.sha }}

    az containerapp registry set -n store -g ${{ env.RESOURCE_GROUP_NAME }} --server ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io

    az containerapp update -n store -g ${{ env.RESOURCE_GROUP_NAME }} -i ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io/${{ env.STORE_IMAGE }}:${{ github.sha }}

    - name: logout
    run: >
    az logout

    Understanding the Bicep templates

    During the provisioning stage of the GitHub Actions workflow, the main.bicep file is processed. Bicep files provide a declarative way of generating resources in Azure and are ideal for managing infrastructure as code. You can learn more about Bicep in the related documentation. The main.bicep file in the sample project creates the following resources:

    • The container registry to store images of the containerized apps.
    • The container apps environment, which handles networking and resource management for the container apps.
    • Three container apps - one for the Blazor front-end and two for the back-end product and inventory APIs.
    • Configuration values to connect these services together

    main.bicep without Dapr

    param location string = resourceGroup().location

    # create the azure container registry
    resource acr 'Microsoft.ContainerRegistry/registries@2021-09-01' = {
    name: toLower('${resourceGroup().name}acr')
    location: location
    sku: {
    name: 'Basic'
    }
    properties: {
    adminUserEnabled: true
    }
    }

    # create the aca environment
    module env 'environment.bicep' = {
    name: 'containerAppEnvironment'
    params: {
    location: location
    }
    }

    # create the various configuration pairs
    var shared_config = [
    {
    name: 'ASPNETCORE_ENVIRONMENT'
    value: 'Development'
    }
    {
    name: 'APPINSIGHTS_INSTRUMENTATIONKEY'
    value: env.outputs.appInsightsInstrumentationKey
    }
    {
    name: 'APPLICATIONINSIGHTS_CONNECTION_STRING'
    value: env.outputs.appInsightsConnectionString
    }
    ]

    # create the products api container app
    module products 'container_app.bicep' = {
    name: 'products'
    params: {
    name: 'products'
    location: location
    registryPassword: acr.listCredentials().passwords[0].value
    registryUsername: acr.listCredentials().username
    containerAppEnvironmentId: env.outputs.id
    registry: acr.name
    envVars: shared_config
    externalIngress: false
    }
    }

    # create the inventory api container app
    module inventory 'container_app.bicep' = {
    name: 'inventory'
    params: {
    name: 'inventory'
    location: location
    registryPassword: acr.listCredentials().passwords[0].value
    registryUsername: acr.listCredentials().username
    containerAppEnvironmentId: env.outputs.id
    registry: acr.name
    envVars: shared_config
    externalIngress: false
    }
    }

    # create the store api container app
    var frontend_config = [
    {
    name: 'ProductsApi'
    value: 'http://${products.outputs.fqdn}'
    }
    {
    name: 'InventoryApi'
    value: 'http://${inventory.outputs.fqdn}'
    }
    ]

    module store 'container_app.bicep' = {
    name: 'store'
    params: {
    name: 'store'
    location: location
    registryPassword: acr.listCredentials().passwords[0].value
    registryUsername: acr.listCredentials().username
    containerAppEnvironmentId: env.outputs.id
    registry: acr.name
    envVars: union(shared_config, frontend_config)
    externalIngress: true
    }
    }

    main.bicep with Dapr


    param location string = resourceGroup().location

    # create the azure container registry
    resource acr 'Microsoft.ContainerRegistry/registries@2021-09-01' = {
    name: toLower('${resourceGroup().name}acr')
    location: location
    sku: {
    name: 'Basic'
    }
    properties: {
    adminUserEnabled: true
    }
    }

    # create the aca environment
    module env 'environment.bicep' = {
    name: 'containerAppEnvironment'
    params: {
    location: location
    }
    }

    # create the various config pairs
    var shared_config = [
    {
    name: 'ASPNETCORE_ENVIRONMENT'
    value: 'Development'
    }
    {
    name: 'APPINSIGHTS_INSTRUMENTATIONKEY'
    value: env.outputs.appInsightsInstrumentationKey
    }
    {
    name: 'APPLICATIONINSIGHTS_CONNECTION_STRING'
    value: env.outputs.appInsightsConnectionString
    }
    ]

    # create the products api container app
    module products 'container_app.bicep' = {
    name: 'products'
    params: {
    name: 'products'
    location: location
    registryPassword: acr.listCredentials().passwords[0].value
    registryUsername: acr.listCredentials().username
    containerAppEnvironmentId: env.outputs.id
    registry: acr.name
    envVars: shared_config
    externalIngress: false
    }
    }

    # create the inventory api container app
    module inventory 'container_app.bicep' = {
    name: 'inventory'
    params: {
    name: 'inventory'
    location: location
    registryPassword: acr.listCredentials().passwords[0].value
    registryUsername: acr.listCredentials().username
    containerAppEnvironmentId: env.outputs.id
    registry: acr.name
    envVars: shared_config
    externalIngress: false
    }
    }

    # create the store api container app
    module store 'container_app.bicep' = {
    name: 'store'
    params: {
    name: 'store'
    location: location
    registryPassword: acr.listCredentials().passwords[0].value
    registryUsername: acr.listCredentials().username
    containerAppEnvironmentId: env.outputs.id
    registry: acr.name
    envVars: shared_config
    externalIngress: true
    }
    }


    Bicep Modules

    The main.bicep file references modules to create resources, such as module products. Modules are a feature of Bicep templates that enable you to abstract resource declarations into their own files or sub-templates. As the main.bicep file is processed, the defined modules are also evaluated. Modules allow you to create resources in a more organized and reusable way. They can also define input and output parameters that are passed to and from the parent template, such as the name of a resource.

    For example, the environment.bicep module extracts the details of creating a container apps environment into a reusable template. The module defines necessary resource dependencies such as Log Analytics Workspaces and an Application Insights instance.

    environment.bicep without Dapr

    param baseName string = resourceGroup().name
    param location string = resourceGroup().location

    resource logs 'Microsoft.OperationalInsights/workspaces@2021-06-01' = {
    name: '${baseName}logs'
    location: location
    properties: any({
    retentionInDays: 30
    features: {
    searchVersion: 1
    }
    sku: {
    name: 'PerGB2018'
    }
    })
    }

    resource appInsights 'Microsoft.Insights/components@2020-02-02' = {
    name: '${baseName}ai'
    location: location
    kind: 'web'
    properties: {
    Application_Type: 'web'
    WorkspaceResourceId: logs.id
    }
    }

    resource env 'Microsoft.App/managedEnvironments@2022-01-01-preview' = {
    name: '${baseName}env'
    location: location
    properties: {
    appLogsConfiguration: {
    destination: 'log-analytics'
    logAnalyticsConfiguration: {
    customerId: logs.properties.customerId
    sharedKey: logs.listKeys().primarySharedKey
    }
    }
    }
    }

    output id string = env.id
    output appInsightsInstrumentationKey string = appInsights.properties.InstrumentationKey
    output appInsightsConnectionString string = appInsights.properties.ConnectionString

    environment.bicep with Dapr


    param baseName string = resourceGroup().name
    param location string = resourceGroup().location

    resource logs 'Microsoft.OperationalInsights/workspaces@2021-06-01' = {
    name: '${baseName}logs'
    location: location
    properties: any({
    retentionInDays: 30
    features: {
    searchVersion: 1
    }
    sku: {
    name: 'PerGB2018'
    }
    })
    }

    resource appInsights 'Microsoft.Insights/components@2020-02-02' = {
    name: '${baseName}ai'
    location: location
    kind: 'web'
    properties: {
    Application_Type: 'web'
    WorkspaceResourceId: logs.id
    }
    }

    resource env 'Microsoft.App/managedEnvironments@2022-01-01-preview' = {
    name: '${baseName}env'
    location: location
    properties: {
    appLogsConfiguration: {
    destination: 'log-analytics'
    logAnalyticsConfiguration: {
    customerId: logs.properties.customerId
    sharedKey: logs.listKeys().primarySharedKey
    }
    }
    }
    }

    output id string = env.id
    output appInsightsInstrumentationKey string = appInsights.properties.InstrumentationKey
    output appInsightsConnectionString string = appInsights.properties.ConnectionString


    The container_apps.bicep template defines numerous parameters to provide a reusable template for creating container apps. This allows the module to be used in other CI/CD pipelines as well.

    container_app.bicep without Dapr

    param name string
    param location string = resourceGroup().location
    param containerAppEnvironmentId string
    param repositoryImage string = 'mcr.microsoft.com/azuredocs/containerapps-helloworld:latest'
    param envVars array = []
    param registry string
    param minReplicas int = 1
    param maxReplicas int = 1
    param port int = 80
    param externalIngress bool = false
    param allowInsecure bool = true
    param transport string = 'http'
    param registryUsername string
    @secure()
    param registryPassword string

    resource containerApp 'Microsoft.App/containerApps@2022-01-01-preview' ={
    name: name
    location: location
    properties:{
    managedEnvironmentId: containerAppEnvironmentId
    configuration: {
    activeRevisionsMode: 'single'
    secrets: [
    {
    name: 'container-registry-password'
    value: registryPassword
    }
    ]
    registries: [
    {
    server: registry
    username: registryUsername
    passwordSecretRef: 'container-registry-password'
    }
    ]
    ingress: {
    external: externalIngress
    targetPort: port
    transport: transport
    allowInsecure: allowInsecure
    }
    }
    template: {
    containers: [
    {
    image: repositoryImage
    name: name
    env: envVars
    }
    ]
    scale: {
    minReplicas: minReplicas
    maxReplicas: maxReplicas
    }
    }
    }
    }

    output fqdn string = containerApp.properties.configuration.ingress.fqdn

    container_app.bicep with Dapr


    param name string
    param location string = resourceGroup().location
    param containerAppEnvironmentId string
    param repositoryImage string = 'mcr.microsoft.com/azuredocs/containerapps-helloworld:latest'
    param envVars array = []
    param registry string
    param minReplicas int = 1
    param maxReplicas int = 1
    param port int = 80
    param externalIngress bool = false
    param allowInsecure bool = true
    param transport string = 'http'
    param appProtocol string = 'http'
    param registryUsername string
    @secure()
    param registryPassword string

    resource containerApp 'Microsoft.App/containerApps@2022-01-01-preview' ={
    name: name
    location: location
    properties:{
    managedEnvironmentId: containerAppEnvironmentId
    configuration: {
    dapr: {
    enabled: true
    appId: name
    appPort: port
    appProtocol: appProtocol
    }
    activeRevisionsMode: 'single'
    secrets: [
    {
    name: 'container-registry-password'
    value: registryPassword
    }
    ]
    registries: [
    {
    server: registry
    username: registryUsername
    passwordSecretRef: 'container-registry-password'
    }
    ]
    ingress: {
    external: externalIngress
    targetPort: port
    transport: transport
    allowInsecure: allowInsecure
    }
    }
    template: {
    containers: [
    {
    image: repositoryImage
    name: name
    env: envVars
    }
    ]
    scale: {
    minReplicas: minReplicas
    maxReplicas: maxReplicas
    }
    }
    }
    }

    output fqdn string = containerApp.properties.configuration.ingress.fqdn


    Understanding configuration differences with Dapr

    The code for this specific sample application is largely the same whether or not Dapr is integrated. However, even with this simple app, there are a few benefits and configuration differences when using Dapr that are worth exploring.

    In this scenario most of the changes are related to communication between the container apps. However, you can explore the full range of Dapr benefits by reading the Dapr integration with Azure Container Apps article in the conceptual documentation.

    Without Dapr

    Without Dapr the main.bicep template handles wiring up the front-end store app to communicate with the back-end apis by manually managing environment variables. The bicep template retrieves the fully qualified domains (fqdn) of the API apps as output parameters when they are created. Those configurations are then set as environment variables on the store container app.


    # Retrieve environment variables from API container creation
    var frontend_config = [
    {
    name: 'ProductsApi'
    value: 'http://${products.outputs.fqdn}'
    }
    {
    name: 'InventoryApi'
    value: 'http://${inventory.outputs.fqdn}'
    }
    ]

    # create the store api container app, passing in config
    module store 'container_app.bicep' = {
    name: 'store'
    params: {
    name: 'store'
    location: location
    registryPassword: acr.listCredentials().passwords[0].value
    registryUsername: acr.listCredentials().username
    containerAppEnvironmentId: env.outputs.id
    registry: acr.name
    envVars: union(shared_config, frontend_config)
    externalIngress: true
    }
    }

    The environment variables are then retrieved inside of the program class and used to configure the base URLs of the corresponding HTTP clients.


    builder.Services.AddHttpClient("Products", (httpClient) => httpClient.BaseAddress = new Uri(builder.Configuration.GetValue<string>("ProductsApi")));
    builder.Services.AddHttpClient("Inventory", (httpClient) => httpClient.BaseAddress = new Uri(builder.Configuration.GetValue<string>("InventoryApi")));

    With Dapr

    Dapr can be enabled on a container app when it is created, as seen below. This configuration adds a Dapr sidecar to the app to streamline discovery and communication features between the different container apps in your environment.


    # Create the container app with Dapr enabled
    resource containerApp 'Microsoft.App/containerApps@2022-01-01-preview' ={
    name: name
    location: location
    properties:{
    managedEnvironmentId: containerAppEnvironmentId
    configuration: {
    dapr: {
    enabled: true
    appId: name
    appPort: port
    appProtocol: appProtocol
    }
    activeRevisionsMode: 'single'
    secrets: [
    {
    name: 'container-registry-password'
    value: registryPassword
    }
    ]

    # Rest of template omitted for brevity...
    }
    }

    Some of these Dapr features can be surfaced through the program file. You can configure your HttpClient to leverage Dapr configurations when communicating with other apps in your environment.


    // reconfigure code to make requests to Dapr sidecar
    var baseURL = (Environment.GetEnvironmentVariable("BASE_URL") ?? "http://localhost") + ":" + (Environment.GetEnvironmentVariable("DAPR_HTTP_PORT") ?? "3500");
    builder.Services.AddHttpClient("Products", (httpClient) =>
    {
    httpClient.BaseAddress = new Uri(baseURL);
    httpClient.DefaultRequestHeaders.Add("dapr-app-id", "Products");
    });

    builder.Services.AddHttpClient("Inventory", (httpClient) =>
    {
    httpClient.BaseAddress = new Uri(baseURL);
    httpClient.DefaultRequestHeaders.Add("dapr-app-id", "Inventory");
    });


    Clean up resources

    If you're not going to continue to use this application, you can delete the Azure Container Apps and all the associated services by removing the resource group.

    Follow these steps in the Azure portal to remove the resources you created:

    1. In the Azure portal, navigate to the msdocswebappsapi resource group using the left navigation or search bar.
    2. Select the Delete resource group button at the top of the resource group Overview.
    3. Enter the resource group name msdocswebappsapi in the Are you sure you want to delete "msdocswebappsapi" confirmation dialog.
    4. Select Delete.
      The process to delete the resource group may take a few minutes to complete.
    - - + + \ No newline at end of file diff --git a/blog/tags/30-days-of-serverless/page/5/index.html b/blog/tags/30-days-of-serverless/page/5/index.html index ef5e4b6f3f..777aeac0b0 100644 --- a/blog/tags/30-days-of-serverless/page/5/index.html +++ b/blog/tags/30-days-of-serverless/page/5/index.html @@ -14,15 +14,15 @@ - - + +

    20 posts tagged with "30-days-of-serverless"

    View All Tags

    · 10 min read
    Ayca Bas

    Welcome to Day 20 of #30DaysOfServerless!

    Every day millions of people spend their precious time in productivity tools. What if you use data and intelligence behind the Microsoft applications (Microsoft Teams, Outlook, and many other Office apps) to build seamless automations and custom apps to boost productivity?

    In this post, we'll learn how to build a seamless onboarding experience for new employees joining a company with the power of Microsoft Graph, integrated with Event Hubs and Logic Apps!


    What We'll Cover

    • ✨ The power of Microsoft Graph
    • 🖇️ How do Microsoft Graph and Event Hubs work together?
    • 🛠 Let's Build an Onboarding Workflow!
      • 1️⃣ Setup Azure Event Hubs + Key Vault
      • 2️⃣ Subscribe to users, receive change notifications from Logic Apps
      • 3️⃣ Create Onboarding workflow in the Logic Apps
    • 🚀 Debug: Your onboarding experience
    • ✋ Exercise: Try this tutorial out yourself!
    • 📚 Resources: For Self-Study


    ✨ The Power of Microsoft Graph

    Microsoft Graph is the gateway to data and intelligence in Microsoft 365 platform. Microsoft Graph exploses Rest APIs and client libraries to access data across Microsoft 365 core services such as Calendar, Teams, To Do, Outlook, People, Planner, OneDrive, OneNote and more.

    Overview of Microsoft Graph

    You can build custom experiences by using Microsoft Graph such as automating the onboarding process for new employees. When new employees are created in the Azure Active Directory, they will be automatically added in the Onboarding team on Microsoft Teams.

    Solution architecture


    🖇️ Microsoft Graph with Event Hubs

    Microsoft Graph uses a webhook mechanism to track changes in resources and deliver change notifications to the clients. For example, with Microsoft Graph Change Notifications, you can receive change notifications when:

    • a new task is added in the to-do list
    • a user changes the presence status from busy to available
    • an event is deleted/cancelled from the calendar

    If you'd like to track a large set of resources at a high frequency, use Azure Events Hubs instead of traditional webhooks to receive change notifications. Azure Event Hubs is a popular real-time events ingestion and distribution service built for scale.

    EVENT GRID - PARTNER EVENTS

    Microsoft Graph Change Notifications can be also received by using Azure Event Grid -- currently available for Microsoft Partners! Read the Partner Events Overview documentation for details.

    Setup Azure Event Hubs + Key Vault.

    To get Microsoft Graph Change Notifications delivered to Azure Event Hubs, we'll have to setup Azure Event Hubs and Azure Key Vault. We'll use Azure Key Vault to access to Event Hubs connection string.

    1️⃣ Create Azure Event Hubs

    1. Go to Azure Portal and select Create a resource, type Event Hubs and select click Create.
    2. Fill in the Event Hubs namespace creation details, and then click Create.
    3. Go to the newly created Event Hubs namespace page, select Event Hubs tab from the left pane and + Event Hub:
      • Name your Event Hub as Event Hub
      • Click Create.
    4. Click the name of the Event Hub, and then select Shared access policies and + Add to add a new policy:
      • Give a name to the policy
      • Check Send and Listen
      • Click Create.
    5. After the policy has been created, click the name of the policy to open the details panel, and then copy the Connection string-primary key value. Write it down; you'll need it for the next step.
    6. Go to Consumer groups tab in the left pane and select + Consumer group, give a name for your consumer group as onboarding and select Create.

    2️⃣ Create Azure Key Vault

    1. Go to Azure Portal and select Create a resource, type Key Vault and select Create.
    2. Fill in the Key Vault creation details, and then click Review + Create.
    3. Go to newly created Key Vault and select Secrets tab from the left pane and click + Generate/Import:
      • Give a name to the secret
      • For the value, paste in the connection string you generated at the Event Hubs step
      • Click Create
      • Copy the name of the secret.
    4. Select Access Policies from the left pane and + Add Access Policy:
      • For Secret permissions, select Get
      • For Principal, select Microsoft Graph Change Tracking
      • Click Add.
    5. Select Overview tab from the left pane and copy the Vault URI.

    Subscribe for Logic Apps change notifications

    To start receiving Microsoft Graph Change Notifications, we'll need to create subscription to the resource that we'd like to track - here, 'users'. We'll use Azure Logic Apps to create subscription.

    To create subscription for Microsoft Graph Change Notifications, we'll need to make a http post request to https://graph.microsoft.com/v1.0/subscriptions. Microsoft Graph requires Azure Active Directory authentication make API calls. First, we'll need to register an app to Azure Active Directory, and then we will make the Microsoft Graph Subscription API call with Azure Logic Apps.

    1️⃣ Create an app in Azure Active Directory

    1. In the Azure Portal, go to Azure Active Directory and select App registrations from the left pane and select + New registration. Fill in the details for the new App registration form as below:
      • Name: Graph Subscription Flow Auth
      • Supported account types: Accounts in any organizational directory (Any Azure AD directory - Multitenant) and personal Microsoft accounts (e.g. Skype, Xbox)
      • Select Register.
    2. Go to newly registered app in Azure Active Directory, select API permissions:
      • Select + Add a permission and Microsoft Graph
      • Select Application permissions and add User.Read.All and Directory.Read.All.
      • Select Grant admin consent for the organization
    3. Select Certificates & secrets tab from the left pane, select + New client secret:
      • Choose desired expiry duration
      • Select Add
      • Copy the value of the secret.
    4. Go to Overview from the left pane, copy Application (client) ID and Directory (tenant) ID.

    2️⃣ Create subscription with Azure Logic Apps

    1. Go to Azure Portal and select Create a resource, type Logic apps and select click Create.

    2. Fill in the Logic Apps creation details, and then click Create.

    3. Go to the newly created Logic Apps page, select Workflows tab from the left pane and select + Add:

      • Give a name to the new workflow as graph-subscription-flow
      • Select Stateful as a state type
      • Click Create.
    4. Go to graph-subscription-flow, and then select Designer tab.

    5. In the Choose an operation section, search for Schedule and select Recurrence as a trigger. Fill in the parameters as below:

      • Interval: 61
      • Frequency: Minute
      • Time zone: Select your own time zone
      • Start time: Set a start time
    6. Select + button in the flow and select add an action. Search for HTTP and select HTTP as an action. Fill in the parameters as below:

      • Method: POST
      • URI: https://graph.microsoft.com/v1.0/subscriptions
      • Headers:
        • Key: Content-type
        • Value: application/json
      • Body:
      {
      "changeType": "created, updated",
      "clientState": "secretClientValue",
      "expirationDateTime": "@{addHours(utcNow(), 1)}",
      "notificationUrl": "EventHub:https://<YOUR-VAULT-URI>/secrets/<YOUR-KEY-VAULT-SECRET-NAME>?tenantId=72f988bf-86f1-41af-91ab-2d7cd011db47",
      "resource": "users"
      }

      In notificationUrl, make sure to replace <YOUR-VAULT-URI> with the vault uri and <YOUR-KEY-VAULT-SECRET-NAME> with the secret name that you copied from the Key Vault.

      In resource, define the resource type you'd like to track changes. For our example, we will track changes for users resource.

      • Authentication:
        • Authentication type: Active Directory OAuth
        • Authority: https://login.microsoft.com
        • Tenant: Directory (tenant) ID copied from AAD app
        • Audience: https://graph.microsoft.com
        • Client ID: Application (client) ID copied from AAD app
        • Credential Type: Secret
        • Secret: value of the secret copied from AAD app
    7. Select Save and run your workflow from the Overview tab.

      Check your subscription in Graph Explorer: If you'd like to make sure that your subscription is created successfully by Logic Apps, you can go to Graph Explorer, login with your Microsoft 365 account and make GET request to https://graph.microsoft.com/v1.0/subscriptions. Your subscription should appear in the response after it's created successfully.

    Subscription workflow success

    After subscription is created successfully by Logic Apps, Azure Event Hubs will receive notifications whenever there is a new user created in Azure Active Directory.


    Create Onboarding workflow in Logic Apps

    We'll create a second workflow in the Logic Apps to receive change notifications from Event Hubs when there is a new user created in the Azure Active Directory and add new user in Onboarding team on Microsoft Teams.

    1. Go to the Logic Apps you created in the previous steps, select Workflows tab and create a new workflow by selecting + Add:
      • Give a name to the new workflow as teams-onboarding-flow
      • Select Stateful as a state type
      • Click Create.
    2. Go to teams-onboarding-flow, and then select Designer tab.
    3. In the Choose an operation section, search for Event Hub, select When events are available in Event Hub as a trigger. Setup Event Hub connection as below:
      • Create Connection:
        • Connection name: Connection
        • Authentication Type: Connection String
        • Connection String: Go to Event Hubs > Shared Access Policies > RootManageSharedAccessKey and copy Connection string–primary key
        • Select Create.
      • Parameters:
        • Event Hub Name: Event Hub
        • Consumer Group Name: onboarding
    4. Select + in the flow and add an action, search for Control and add For each as an action. Fill in For each action as below:
      • Select output from previous steps: Events
    5. Inside For each, select + in the flow and add an action, search for Data operations and select Parse JSON. Fill in Parse JSON action as below:
      • Content: Events Content
      • Schema: Copy the json content from schema-parse.json and paste as a schema
    6. Select + in the flow and add an action, search for Control and add For each as an action. Fill in For each action as below:
      • Select output from previous steps: value
      1. Inside For each, select + in the flow and add an action, search for Microsoft Teams and select Add a member to a team. Login with your Microsoft 365 account to create a connection and fill in Add a member to a team action as below:
      • Team: Create an Onboarding team on Microsoft Teams and select
      • A user AAD ID for the user to add to a team: id
    7. Select Save.

    🚀 Debug your onboarding experience

    To debug our onboarding experience, we'll need to create a new user in Azure Active Directory and see if it's added in Microsoft Teams Onboarding team automatically.

    1. Go to Azure Portal and select Azure Active Directory from the left pane and go to Users. Select + New user and Create new user. Fill in the details as below:

      • User name: JaneDoe
      • Name: Jane Doe

      new user in Azure Active Directory

    2. When you added Jane Doe as a new user, it should trigger the teams-onboarding-flow to run. teams onboarding flow success

    3. Once the teams-onboarding-flow runs successfully, you should be able to see Jane Doe as a member of the Onboarding team on Microsoft Teams! 🥳 new member in Onboarding team on Microsoft Teams

    Congratulations! 🎉

    You just built an onboarding experience using Azure Logic Apps, Azure Event Hubs and Azure Key Vault.


    📚 Resources

    - - + + \ No newline at end of file diff --git a/blog/tags/30-days-of-serverless/page/6/index.html b/blog/tags/30-days-of-serverless/page/6/index.html index 4efab00779..a7f168fd30 100644 --- a/blog/tags/30-days-of-serverless/page/6/index.html +++ b/blog/tags/30-days-of-serverless/page/6/index.html @@ -14,14 +14,14 @@ - - + +

    20 posts tagged with "30-days-of-serverless"

    View All Tags

    · 10 min read
    Brian Benz

    Welcome to Day 18 of #30DaysOfServerless!

    Yesterday my Serverless September post introduced you to making Azure Logic Apps and Azure Cosmos DB work together with a sample application that collects weather data. Today I'm sharing a more robust solution that actually reads my mail. Let's learn about Teaching the cloud to read your mail!

    Ready? Let's go!


    What We'll Cover

    • Introduction to the ReadMail solution
    • Setting up Azure storage, Cosmos DB and Computer Vision
    • Connecting it all together with a Logic App
    • Resources: For self-study!


    Introducing the ReadMail solution

    The US Postal system offers a subscription service that sends you images of mail it will be delivering to your home. I decided it would be cool to try getting Azure to collect data based on these images, so that I could categorize my mail and track the types of mail that I received.

    To do this, I used Azure storage, Cosmos DB, Logic Apps, and computer vision. When a new email comes in from the US Postal service (USPS), it triggers a logic app that:

    • Posts attachments to Azure storage
    • Triggers Azure Computer vision to perform an OCR function on attachments
    • Extracts any results into a JSON document
    • Writes the JSON document to Cosmos DB

    workflow for the readmail solution

    In this post I'll walk you through setting up the solution for yourself.

    Prerequisites

    Setup Azure Services

    First, we'll create all of the target environments we need to be used by our Logic App, then we;ll create the Logic App.

    1. Azure Storage

    We'll be using Azure storage to collect attached images from emails as they arrive. Adding images to Azure storage will also trigger a workflow that performs OCR on new attached images and stores the OCR data in Cosmos DB.

    To create a new Azure storage account from the portal dashboard, Select Create a resource > Storage account > Create.

    The Basics tab covers all of the features and information that we will need for this solution:

    SectionFieldRequired or optionalDescription
    Project detailsSubscriptionRequiredSelect the subscription for the new storage account.
    Project detailsResource groupRequiredCreate a new resource group that you will use for storage, Cosmos DB, Computer Vision and the Logic App.
    Instance detailsStorage account nameRequiredChoose a unique name for your storage account. Storage account names must be between 3 and 24 characters in length and may contain numbers and lowercase letters only.
    Instance detailsRegionRequiredSelect the appropriate region for your storage account.
    Instance detailsPerformanceRequiredSelect Standard performance for general-purpose v2 storage accounts (default).
    Instance detailsRedundancyRequiredSelect locally-redundant Storage (LRS) for this example.

    Select Review + create to accept the remaining default options, then validate and create the account.

    2. Azure CosmosDB

    CosmosDB will be used to store the JSON documents returned by the COmputer Vision OCR process.

    See more details and screen shots for setting up CosmosDB in yesterday's Serverless September post - Using Logic Apps with Cosmos DB

    To get started with Cosmos DB, you create an account, then a database, then a container to store JSON documents. To create a new Cosmos DB account from the portal dashboard, Select Create a resource > Azure Cosmos DB > Create. Choose core SQL for the API.

    Select your subscription, then for simplicity use the same resource group you created when you set up storage. Enter an account name and choose a location, select provisioned throughput capacity mode and apply the free tier discount. From here you can select Review and Create, then Create

    Next, create a new database and container. Go to the Data Explorer in your new Cosmos DB account, and choose New Container. Name the database, and keep all the other defaults except:

    SettingAction
    Container IDid
    Container partition/id

    Press OK to create a database and container

    3. Azure Computer Vision

    Azure Cognitive Services' Computer Vision will perform an OCR process on each image attachment that is stored in Azure storage.

    From the portal dashboard, Select Create a resource > AI + Machine Learning > Computer Vision > Create.

    The Basics and Identity tabs cover all of the features and information that we will need for this solution:

    Basics Tab

    SectionFieldRequired or optionalDescription
    Project detailsSubscriptionRequiredSelect the subscription for the new service.
    Project detailsResource groupRequiredUse the same resource group that you used for Azure storage and Cosmos DB.
    Instance detailsRegionRequiredSelect the appropriate region for your Computer Vision service.
    Instance detailsNameRequiredChoose a unique name for your Computer Vision service.
    Instance detailsPricingRequiredSelect the free tier for this example.

    Identity Tab

    SectionFieldRequired or optionalDescription
    System assigned managed identityStatusRequiredEnable system assigned identity to grant the resource access to other existing resources.

    Select Review + create to accept the remaining default options, then validate and create the account.


    Connect it all with a Logic App

    Now we're ready to put this all together in a Logic App workflow!

    1. Create Logic App

    From the portal dashboard, Select Create a resource > Integration > Logic App > Create. Name your Logic App and select a location, the rest of the settings can be left at their defaults.

    2. Create Workflow: Add Trigger

    Once the Logic App is created, select Create a workflow from designer.

    A workflow is a series of steps that defines a task or process. Each workflow starts with a single trigger, after which you must add one or more actions.

    When in designer, search for outlook.com on the right under Add a trigger. Choose outlook.com. Choose When a new email arrives as the trigger.

    A trigger is always the first step in any workflow and specifies the condition for running any further steps in that workflow.

    Set the following values:

    ParameterValue
    FolderInbox
    ImportanceAny
    Only With AttachmentsYes
    Include AttachmentsYes

    Then add a new parameter:

    ParameterValue
    FromAdd the email address that sends you the email with attachments
    3. Create Workflow: Add Action (for Trigger)

    Choose add an action and choose control > for-each.

    logic app for each

    Inside the for-each action, in Select an output from previous steps, choose attachments. Then, again inside the for-each action, add the create blob action:

    Set the following values:

    ParameterValue
    Folder Path/mailreaderinbox
    Blob NameAttachments Name
    Blob ContentAttachments Content

    This extracts attachments from the email and created a new blob for each attachment.

    Next, inside the same for-each action, add the get blob content action.

    Set the following values:

    ParameterValue
    Blobid
    Infer content typeYes

    We create and read from a blob for each attachment because Computer Vision needs a non-virtual source to read from when performing an OCR process. Because we enabled system assigned identity to grant Computer Vision to other existing resources, it can access the blob but not the outlook.com attachment. Also, we pass the ID of the blob to use as a unique ID when writing to Cosmos DB.

    create blob from attachments

    Next, inside the same for-each action, choose add an action and choose control > condition. Set the value to Media Type > is equal to > image/JPEG

    The USPS sends attachments of multiple types, but we only want to scan attachments that have images of our mail, which are always JPEG images. If the condition is true, we will process the image with Computer Vision OCR and write the results to a JSON document in CosmosDB.

    In the True section of the condition, add an action and choose Computer Vision API > Optical Character Recognition (OCR) to JSON.

    Set the following values:

    ParameterValue
    Image SourceImage Content
    Image contentFile Content

    In the same True section of the condition, choose add an action and choose Cosmos DB. Choose Create or Update Document from the actions. Select Access Key, and provide the primary read-write key (found under keys in Cosmos DB), and the Cosmos DB account ID (without 'documents.azure.com').

    Next, fill in your Cosmos DB Database ID and Collection ID. Create a JSON document by selecting dynamic content elements and wrapping JSON formatting around them.

    Be sure to use the ID passed from blob storage as your unique ID for CosmosDB. That way you can troubleshoot and JSON or OCR issues by tracing back the JSON document in Cosmos Db to the blob in Azure storage. Also, include the Computer Vision JSON response, as it contains the results of the Computer Vision OCR scan. all other elements are optional.

    4. TEST WORKFLOW

    When complete, you should have an action the Logic App designer that looks something like this:

    Logic App workflow create or update document in cosmosdb

    Save the workflow and test the connections by clicking Run Trigger > Run. If connections are working, you should see documents flowing into Cosmos DB each time that an email arrives with image attachments.

    Check the data in Cosmos Db by opening the Data explorer, then choosing the container you created and selecting items. You should see documents similar to this:

    Logic App workflow with trigger and action

    1. Congratulations

    You just built your personal ReadMail solution with Logic Apps! 🎉


    Resources: For self-study!

    Once you have an understanding of the basics in ths post, there is so much more to learn!

    Thanks for stopping by!

    - - + + \ No newline at end of file diff --git a/blog/tags/30-days-of-serverless/page/7/index.html b/blog/tags/30-days-of-serverless/page/7/index.html index 0208d6224f..0a4b062e2f 100644 --- a/blog/tags/30-days-of-serverless/page/7/index.html +++ b/blog/tags/30-days-of-serverless/page/7/index.html @@ -14,14 +14,14 @@ - - + +

    20 posts tagged with "30-days-of-serverless"

    View All Tags

    · 6 min read
    Brian Benz

    Welcome to Day 17 of #30DaysOfServerless!

    In past weeks, we've covered serverless technologies that provide core capabilities (functions, containers, microservices) for building serverless solutions. This week we're looking at technologies that make service integrations more seamless, starting with Logic Apps. Let's look at one usage example today!

    Ready? Let's Go!


    What We'll Cover

    • Introduction to Logic Apps
    • Settng up Cosmos DB for Logic Apps
    • Setting up a Logic App connection and event
    • Writing data to Cosmos DB from a Logic app
    • Resources: For self-study!


    Introduction to Logic Apps

    Previously in Serverless September, we've covered Azure Functions, where the event triggers code. In Logic Apps, the event triggers a workflow that you design. Logic Apps enable serverless applications to connect to external sources for data then automate business processes via workflows.

    In this post I'll walk you through setting up a Logic App that works with Cosmos DB. For this example, we'll connect to the MSN weather service, an design a logic app workflow that collects data when weather changes, and writes the data to Cosmos DB.

    PREREQUISITES

    Setup Cosmos DB for Logic Apps

    Cosmos DB has many APIs to choose from, but to use the default Logic App connection, we need to choose the a Cosmos DB SQL API. We'll set this up via the Azure Portal.

    To get started with Cosmos DB, you create an account, then a database, then a container to store JSON documents. To create a new Cosmos DB account from the portal dashboard, Select Create a resource > Azure Cosmos DB > Create. Choose core SQL for the API.

    Select your subscription, then create a new resource group called CosmosWeather. Enter an account name and choose a location, select provisioned throughput capacity mode and apply the free tier discount. From here you can select Review and Create, then Create

    Azure Cosmos DB is available in two different capacity modes: provisioned throughput and serverless. You can perform the same database operations in both modes, but the way you get billed for these operations is different. We wil be using provisioned throughput and the free tier for this example.

    Setup the CosmosDB account

    Next, create a new database and container. Go to the Data Explorer in your new Cosmos DB account, and choose New Container. Name the database, and keep all the orher defaults except:

    SettingAction
    Container IDid
    Container partition/id

    Press OK to create a database and container

    A database is analogous to a traditional DBMS namespace. It's used to organize one or more containers.

    Setup the CosmosDB Container

    Now we're ready to set up our logic app an write to Cosmos DB!

    Setup Logic App connection + event

    Once the Cosmos DB SQL API account is created, we can set up our Logic App. From the portal dashboard, Select Create a resource > Integration > Logic App > Create. Name your Logic App and select a location, the rest fo the settings can be left at their defaults. Once you new Logic App is created, select Create a workflow from designer to get started.

    A workflow is a series of steps that defines a task or process. Each workflow starts with a single trigger, after which you must add one or more actions.

    When in designer, search for weather on the right under Add a trigger. Choose MSN Weather. Choose When the current conditions change as the trigger.

    A trigger is always the first step in any workflow and specifies the condition for running any further steps in that workflow.

    Add a location. Valid locations are City, Region, State, Country, Landmark, Postal Code, latitude and longitude. This triggers a new workflow when the conditions change for a location.

    Write data from Logic App to Cosmos DB

    Now we are ready to set up the action to write data to Cosmos DB. Choose add an action and choose Cosmos DB.

    An action is each step in a workflow after the trigger. Every action runs some operation in a workflow.

    In this case, we will be writing a JSON document to the Cosmos DB container we created earlier. Choose Create or Update Document from the actions. At this point you should have a workflow in designer that looks something like this:

    Logic App workflow with trigger

    Start wth the connection for set up the Cosmos DB action. Select Access Key, and provide the primary read-write key (found under keys in Cosmos DB), and the Cosmos DB account ID (without 'documents.azure.com').

    Next, fill in your Cosmos DB Database ID and Collection ID. Create a JSON document bt selecting dynamic content elements and wrapping JSON formatting around them.

    You will need a unique ID for each document that you write to Cosmos DB, for that you can use an expression. Because we declared id to be our unique ID in Cosmos DB, we will use use that for the name. Under expressions, type guid() and press enter to add a unique ID to the JSON document. When complete, you should have a workflow in designer that looks something like this:

    Logic App workflow with trigger and action

    Save the workflow and test the connections by clicking Run Trigger > Run. If connections are working, you should see documents flowing into Cosmos DB over the next few minutes.

    Check the data in Cosmos Db by opening the Data explorer, then choosing the container you created and selecting items. You should see documents similar to this:

    Logic App workflow with trigger and action

    Resources: For self-study!

    Once you've grasped the basics in ths post, there is so much more to learn!

    Thanks for stopping by!

    - - + + \ No newline at end of file diff --git a/blog/tags/30-days-of-serverless/page/8/index.html b/blog/tags/30-days-of-serverless/page/8/index.html index 4797646959..00fa8f5676 100644 --- a/blog/tags/30-days-of-serverless/page/8/index.html +++ b/blog/tags/30-days-of-serverless/page/8/index.html @@ -14,13 +14,13 @@ - - + +

    20 posts tagged with "30-days-of-serverless"

    View All Tags

    · 4 min read
    Nitya Narasimhan
    Devanshi Joshi

    Welcome to Day 15 of #30DaysOfServerless!

    This post marks the midpoint of our Serverless on Azure journey! Our Week 2 Roadmap showcased two key technologies - Azure Container Apps (ACA) and Dapr - for building serverless microservices. We'll also look at what happened elsewhere in #ServerlessSeptember, then set the stage for our next week's focus: Serverless Integrations.

    Ready? Let's Go!


    What We'll Cover

    • ICYMI: This Week on #ServerlessSeptember
    • Recap: Microservices, Azure Container Apps & Dapr
    • Coming Next: Serverless Integrations
    • Exercise: Take the Cloud Skills Challenge
    • Resources: For self-study!

    This Week In Events

    We had a number of activities happen this week - here's a quick summary:

    This Week in #30Days

    In our #30Days series we focused on Azure Container Apps and Dapr.

    • In Hello Container Apps we learned how Azure Container Apps helps you run microservices and containerized apps on serverless platforms. And we build and deployed our first ACA.
    • In Microservices Communication we explored concepts like environments and virtual networking, with a hands-on example to show how two microservices communicate in a deployed ACA.
    • In Scaling Your Container Apps we learned about KEDA (Kubernetes Event-Driven Autoscaler) and how to configure autoscaling for your ACA based on KEDA-supported triggers.
    • In Build with Dapr we introduced the Distributed Application Runtime (Dapr) and learned how its Building Block APIs and sidecar architecture make it easier to develop microservices with ACA.
    • In Secure ACA Access we learned how to secure ACA access to external services with - and without - Dapr, covering Secret Stores and Managed Identity.
    • Finally, Build ACA with Dapr tied it all together with a enterprise app scenario where an orders processor (ACA) uses Dapr APIs (PubSub, State Management) to receive and store order messages from Azure Service Bus.

    Here's a visual recap:

    Self Study: Code Samples & Tutorials

    There's no better way to get familiar with the concepts, than to dive in and play with code samples and hands-on tutorials. Here are 4 resources to bookmark and try out:

    1. Dapr Quickstarts - these walk you through samples showcasing individual Building Block APIs - with multiple language options available.
    2. Dapr Tutorials provides more complex examples of microservices applications and tools usage, including a Distributed Calculator polyglot app.
    3. Next, try to Deploy a Dapr application to Azure Container Apps to get familiar with the process of setting up the environment, then deploying the app.
    4. Or, explore the many Azure Container Apps samples showcasing various features and more complex architectures tied to real world scenarios.

    What's Next: Serverless Integrations!

    So far we've talked about core technologies (Azure Functions, Azure Container Apps, Dapr) that provide foundational support for your serverless solution. Next, we'll look at Serverless Integrations - specifically at technologies like Azure Logic Apps and Azure Event Grid that automate workflows and create seamless end-to-end solutions that integrate other Azure services in serverless-friendly ways.

    Take the Challenge!

    The Cloud Skills Challenge is still going on, and we've already had hundreds of participants join and complete the learning modules to skill up on Serverless.

    There's still time to join and get yourself on the leaderboard. Get familiar with Azure Functions, SignalR, Logic Apps, Azure SQL and more - in serverless contexts!!


    - - + + \ No newline at end of file diff --git a/blog/tags/30-days-of-serverless/page/9/index.html b/blog/tags/30-days-of-serverless/page/9/index.html index 7ebadd7275..0e41e4dadc 100644 --- a/blog/tags/30-days-of-serverless/page/9/index.html +++ b/blog/tags/30-days-of-serverless/page/9/index.html @@ -14,8 +14,8 @@ - - + +
    @@ -24,7 +24,7 @@ Image showing container apps role assignment

  • Lastly, we need to restart the container app revision, to do so run the command below:

     ##Get revision name and assign it to a variable
    $REVISION_NAME = (az containerapp revision list `
    --name $BACKEND_SVC_NAME `
    --resource-group $RESOURCE_GROUP `
    --query [0].name)

    ##Restart revision by name
    az containerapp revision restart `
    --resource-group $RESOURCE_GROUP `
    --name $BACKEND_SVC_NAME `
    --revision $REVISION_NAME
  • Run end-to-end Test on Azure

    From the Azure Portal, select the Azure Container App orders-processor and navigate to Log stream under Monitoring tab, leave the stream connected and opened. From the Azure Portal, select the Azure Service Bus Namespace ordersservices, select the topic orderreceivedtopic, select the subscription named orders-processor-subscription, then click on Service Bus Explorer (preview). From there we need to publish/send a message. Use the JSON payload below

    ```json
    {
    "data": {
    "reference": "Order 150",
    "quantity": 150,
    "createdOn": "2022-05-10T12:45:22.0983978Z"
    }
    }
    ```

    If all is configured correctly, you should start seeing the information logs in Container Apps Log stream, similar to the images below Image showing publishing messages from Azure Service

    Information logs on the Log stream of the deployed Azure Container App Image showing ACA Log Stream

    🎉 CONGRATULATIONS

    You have successfully deployed to the cloud an Azure Container App and configured Dapr Pub/Sub API with Azure Service Bus.

    9. Clean up

    If you are done with the tutorial, use the following command to delete the resource group and all its contained resources to avoid incurring further costs.

    az group delete --name $RESOURCE_GROUP

    Exercise

    I left for you the configuration of the Dapr State Store API with Azure Cosmos DB :)

    When you look at the action method OrderReceived in controller ExternalOrdersController, you will see that I left a line with ToDo: note, this line is responsible to save the received message (OrderModel) into Azure Cosmos DB.

    There is no need to change anything on the code base (other than removing this commented line), that's the beauty of Dapr Building Blocks and how easy it allows us to plug components to our microservice application without any plumping and brining external SDKs.

    For sure you need to work on the configuration part of Dapr State Store by creating a new component file like what we have done with the Pub/Sub API, things that you need to work on are:

    • Provision Azure Cosmos DB Account and obtain its masterKey.
    • Create a Dapr Component file adhering to Dapr Specs.
    • Create an Azure Container Apps component file adhering to ACA component specs.
    • Test locally on your dev machine using Dapr Component file.
    • Register the new Dapr State Store component with Azure Container Apps Environment and set the Cosmos Db masterKey from the Azure Portal. If you want to challenge yourself more, use the Managed Identity approach as done in this post! The right way to protect your keys and you will not worry about managing CosmosDb keys anymore!
    • Build a new image of the application and push it to Azure Container Registry.
    • Update Azure Container Apps and create a new revision which contains the updated code.
    • Verify the results by checking Azure Cosmos DB, you should see the Order Model stored in Cosmos DB.

    If you need help, you can always refer to my blog post Azure Container Apps State Store With Dapr State Management API which contains exactly what you need to implement here, so I'm very confident you will be able to complete this exercise with no issues, happy coding :)

    What's Next?

    If you enjoyed working with Dapr and Azure Container Apps, and you want to have a deep dive with more complex scenarios (Dapr bindings, service discovery, auto scaling with KEDA, sync services communication, distributed tracing, health probes, etc...) where multiple services deployed to a single Container App Environment; I have created a detailed tutorial which should walk you through step by step with through details to build the application.

    So far, the published posts below, and I'm publishing more posts on weekly basis, so stay tuned :)

    Resources

    - - + + \ No newline at end of file diff --git a/blog/tags/ask-the-expert/index.html b/blog/tags/ask-the-expert/index.html index 09d42857ee..4c0cf0b559 100644 --- a/blog/tags/ask-the-expert/index.html +++ b/blog/tags/ask-the-expert/index.html @@ -14,13 +14,13 @@ - - + +

    One post tagged with "ask-the-expert"

    View All Tags

    · 5 min read
    Nitya Narasimhan
    Devanshi Joshi

    SEP 08: CHANGE IN PUBLISHING SCHEDULE

    Starting from Week 2 (Sep 8), we'll be publishing blog posts in batches rather than on a daily basis, so you can read a series of related posts together. Don't want to miss updates? Just subscribe to the feed


    Welcome to Day 8 of #30DaysOfServerless!

    This marks the end of our Week 1 Roadmap focused on Azure Functions!! Today, we'll do a quick recap of all #ServerlessSeptember activities in Week 1, set the stage for Week 2 - and leave you with some excellent tutorials you should explore to build more advanced scenarios with Azure Functions.

    Ready? Let's go.


    What We'll Cover

    • Azure Functions: Week 1 Recap
    • Advanced Functions: Explore Samples
    • End-to-End: Serverless Hacks & Cloud Skills
    • What's Next: Hello, Containers & Microservices
    • Challenge: Complete the Learning Path


    Week 1 Recap: #30Days & Functions

    Congratulations!! We made it to the end of Week 1 of #ServerlessSeptember. Let's recap what we learned so far:

    • In Core Concepts we looked at where Azure Functions fits into the serverless options available on Azure. And we learned about key concepts like Triggers, Bindings, Custom Handlers and Durable Functions.
    • In Build Your First Function we looked at the tooling options for creating Functions apps, testing them locally, and deploying them to Azure - as we built and deployed our first Functions app.
    • In the next 4 posts, we explored new Triggers, Integrations, and Scenarios - as we looked at building Functions Apps in Java, JavaScript, .NET and Python.
    • And in the Zero-To-Hero series, we learned about Durable Entities - and how we can use them to create stateful serverless solutions using a Chirper Sample as an example scenario.

    The illustrated roadmap below summarizes what we covered each day this week, as we bring our Functions-as-a-Service exploration to a close.


    Advanced Functions: Code Samples

    So, now that we've got our first Functions app under our belt, and validated our local development setup for tooling, where can we go next? A good next step is to explore different triggers and bindings, that drive richer end-to-end scenarios. For example:

    • Integrate Functions with Azure Logic Apps - we'll discuss Azure Logic Apps in Week 3. For now, think of it as a workflow automation tool that lets you integrate seamlessly with other supported Azure services to drive an end-to-end scenario. In this tutorial, we set up a workflow connecting Twitter (get tweet) to Azure Cognitive Services (analyze sentiment) - and use that to trigger an Azure Functions app to send email about the result.
    • Integrate Functions with Event Grid - we'll discuss Azure Event Grid in Week 3. For now, think of it as an eventing service connecting event sources (publishers) to event handlers (subscribers) at cloud scale. In this tutorial, we handle a common use case - a workflow where loading an image to Blob Storage triggers an Azure Functions app that implements a resize function, helping automatically generate thumbnails for the uploaded image.
    • Integrate Functions with CosmosDB and SignalR to bring real-time push-based notifications to your web app. It achieves this by using a Functions app that is triggered by changes in a CosmosDB backend, causing it to broadcast that update (push notification to connected web clients over SignalR, in real time.

    Want more ideas? Check out the Azure Samples for Functions for implementations, and browse the Azure Architecture Center for reference architectures from real-world scenarios that involve Azure Functions usage.


    E2E Scenarios: Hacks & Cloud Skills

    Want to systematically work your way through a single End-to-End scenario involving Azure Functions alongside other serverless support technologies? Check out the Serverless Hacks activity happening during #ServerlessSeptember, and learn to build this "Serverless Tollbooth Application" in a series of 10 challenges. Check out the video series for a reference solution in .NET and sign up for weekly office hours to join peers and discuss your solutions or challenges.

    Or perhaps you prefer to learn core concepts with code in a structured learning path? We have that covered. Check out the 12-module "Create Serverless Applications" course from Microsoft Learn which walks your through concepts, one at a time, with code. Even better - sign up for the free Cloud Skills Challenge and complete the same path (in under 30 days) but this time, with the added fun of competing against your peers for a spot on a leaderboard, and swag.


    What's Next? Hello, Cloud-Native!

    So where to next? In Week 2 we turn our attention from Functions-as-a-Service to building more complex backends using Containers and Microservices. We'll focus on two core technologies - Azure Container Apps and Dapr (Distributed Application Runtime) - both key components of a broader vision around Building Cloud-Native Applications in Azure.

    What is Cloud-Native you ask?

    Fortunately for you, we have an excellent introduction in our Zero-to-Hero article on Go Cloud-Native with Azure Container Apps - that explains the 5 pillars of Cloud-Native and highlights the value of Azure Container Apps (scenarios) and Dapr (sidecar architecture) for simplified microservices-based solution with auto-scale capability. Prefer a visual summary? Here's an illustrate guide to that article for convenience.

    Go Cloud-Native Download a higher resolution version of the image


    Take The Challenge

    We typically end each post with an exercise or activity to reinforce what you learned. For Week 1, we encourage you to take the Cloud Skills Challenge and work your way through at least a subset of the modules, for hands-on experience with the different Azure Functions concepts, integrations, and usage.

    See you in Week 2!

    - - + + \ No newline at end of file diff --git a/blog/tags/asp-net/index.html b/blog/tags/asp-net/index.html index d25a4f45b8..5886f9a147 100644 --- a/blog/tags/asp-net/index.html +++ b/blog/tags/asp-net/index.html @@ -14,13 +14,13 @@ - - + +

    One post tagged with "asp.net"

    View All Tags

    · 19 min read
    Alex Wolf

    Welcome to Day 24 of #30DaysOfServerless!

    We continue exploring E2E scenarios with this tutorial where you'll deploy a containerized ASP.NET Core 6.0 application to Azure Container Apps.

    The application consists of a front-end web app built using Blazor Server, as well as two Web API projects to manage data. These projects will exist as three separate containers inside of a shared container apps environment.


    What We'll Cover

    • Deploy ASP.NET Core 6.0 app to Azure Container Apps
    • Automate deployment workflows using GitHub Actions
    • Provision and deploy resources using Azure Bicep
    • Exercise: Try this yourself!
    • Resources: For self-study!


    Introduction

    Azure Container Apps enables you to run microservices and containerized applications on a serverless platform. With Container Apps, you enjoy the benefits of running containers while leaving behind the concerns of manually configuring cloud infrastructure and complex container orchestrators.

    In this tutorial, you'll deploy a containerized ASP.NET Core 6.0 application to Azure Container Apps. The application consists of a front-end web app built using Blazor Server, as well as two Web API projects to manage data. These projects will exist as three separate containers inside of a shared container apps environment.

    You will use GitHub Actions in combination with Bicep to deploy the application. These tools provide an approachable and sustainable solution for building CI/CD pipelines and working with Container Apps.

    PRE-REQUISITES

    Architecture

    In this tutorial, we'll setup a container app environment with a separate container for each project in the sample store app. The major components of the sample project include:

    • A Blazor Server front-end web app to display product information
    • A products API to list available products
    • An inventory API to determine how many products are in stock
    • GitHub Actions and Bicep templates to provision Azure resources and then build and deploy the sample app.

    You will explore these templates later in the tutorial.

    Public internet traffic should be proxied to the Blazor app. The back-end APIs should only be reachable via requests from the Blazor app inside the container apps environment. This setup can be achieved using container apps environment ingress configurations during deployment.

    An architecture diagram of the shopping app


    Project Sources

    Want to follow along? Fork the sample below. The tutorial can be completed with or without Dapr integration. Pick the path you feel comfortable in. Dapr provides various benefits that make working with Microservices easier - you can learn more in the docs. For this tutorial you will need GitHub and Azure CLI.

    PICK YOUR PATH

    To follow along with this tutorial, fork the relevant sample project below.

    You can run the app locally from Visual Studio:

    • Right click on the Blazor Store project and select Set as Startup Project.
    • Press the start button at the top of Visual Studio to run the app.
    • (Once running) start each API in the background by
    • right-clicking on the project node
    • selecting Debug --> Start without debugging.

    Once the Blazor app is running, you should see something like this:

    An architecture diagram of the shopping app


    Configuring Azure credentials

    In order to deploy the application to Azure through GitHub Actions, you first need to create a service principal. The service principal will allow the GitHub Actions process to authenticate to your Azure subscription to create resources and deploy code. You can learn more about Service Principals in the Azure CLI documentation. For this step you'll need to be logged into the Azure CLI.

    1) If you have not done so already, make sure to fork the sample project to your own GitHub account or organization.

    1) Once you have completed this step, create a service principal using the Azure CLI command below:

    ```azurecli
    $subscriptionId=$(az account show --query id --output tsv)
    az ad sp create-for-rbac --sdk-auth --name WebAndApiSample --role Contributor --scopes /subscriptions/$subscriptionId
    ```

    1) Copy the JSON output of the CLI command to your clipboard

    1) Under the settings tab of your forked GitHub repo, create a new secret named AzureSPN. The name is important to match the Bicep templates included in the project, which we'll review later. Paste the copied service principal values on your clipboard into the secret and save your changes. This new secret will be used by the GitHub Actions workflow to authenticate to Azure.

    :::image type="content" source="./img/dotnet/github-secrets.png" alt-text="A screenshot of adding GitHub secrets.":::

    Deploy using Github Actions

    You are now ready to deploy the application to Azure Container Apps using GitHub Actions. The sample application includes a GitHub Actions template that is configured to build and deploy any changes to a branch named deploy. The deploy branch does not exist in your forked repository by default, but you can easily create it through the GitHub user interface.

    1) Switch to the Actions tab along the top navigation of your GitHub repository. If you have not done so already, ensure that workflows are enabled by clicking the button in the center of the page.

    A screenshot showing how to enable GitHub actions

    1) Navigate to the main Code tab of your repository and select the main dropdown. Enter deploy into the branch input box, and then select Create branch: deploy from 'main'.

    A screenshot showing how to create the deploy branch

    1) On the new deploy branch, navigate down into the .github/workflows folder. You should see a file called deploy.yml, which contains the main GitHub Actions workflow script. Click on the file to view its content. You'll learn more about this file later in the tutorial.

    1) Click the pencil icon in the upper right to edit the document.

    1) Change the RESOURCE_GROUP_NAME: value to msdocswebappapis or another valid resource group name of your choosing.

    1) In the upper right of the screen, select Start commit and then Commit changes to commit your edit. This will persist the change to the file and trigger the GitHub Actions workflow to build and deploy the app.

    A screenshot showing how to commit changes

    1) Switch to the Actions tab along the top navigation again. You should see the workflow running to create the necessary resources and deploy the app. The workflow may take several minutes to run. When it completes successfully, all of the jobs should have a green checkmark icon next to them.

    The completed GitHub workflow.

    Explore the Azure resources

    Once the GitHub Actions workflow has completed successfully you can browse the created resources in the Azure portal.

    1) On the left navigation, select Resource Groups. Next,choose the msdocswebappapis resource group that was created by the GitHub Actions workflow.

    2) You should see seven resources available that match the screenshot and table descriptions below.

    The resources created in Azure.

    Resource nameTypeDescription
    inventoryContainer appThe containerized inventory API.
    msdocswebappapisacrContainer registryA registry that stores the built Container images for your apps.
    msdocswebappapisaiApplication insightsApplication insights provides advanced monitoring, logging and metrics for your apps.
    msdocswebappapisenvContainer apps environmentA container environment that manages networking, security and resource concerns. All of your containers live in this environment.
    msdocswebappapislogsLog Analytics workspaceA workspace environment for managing logging and analytics for the container apps environment
    productsContainer appThe containerized products API.
    storeContainer appThe Blazor front-end web app.

    3) You can view your running app in the browser by clicking on the store container app. On the overview page, click the Application Url link on the upper right of the screen.

    :::image type="content" source="./img/dotnet/application-url.png" alt-text="The link to browse the app.":::

    Understanding the GitHub Actions workflow

    The GitHub Actions workflow created and deployed resources to Azure using the deploy.yml file in the .github folder at the root of the project. The primary purpose of this file is to respond to events - such as commits to a branch - and run jobs to accomplish tasks. The deploy.yml file in the sample project has three main jobs:

    • Provision: Create the necessary resources in Azure, such as the container apps environment. This step leverages Bicep templates to create the Azure resources, which you'll explore in a moment.
    • Build: Create the container images for the three apps in the project and store them in the container registry.
    • Deploy: Deploy the container images to the different container apps created during the provisioning job.

    The deploy.yml file also accepts parameters to make the workflow more dynamic, such as setting the resource group name or the Azure region resources will be provisioned to.

    Below is a commented version of the deploy.yml file that highlights the essential steps.

    name: Build and deploy .NET application to Container Apps

    # Trigger the workflow on pushes to the deploy branch
    on:
    push:
    branches:
    - deploy

    env:
    # Set workflow variables
    RESOURCE_GROUP_NAME: msdocswebappapis

    REGION: eastus

    STORE_DOCKER: Store/Dockerfile
    STORE_IMAGE: store

    INVENTORY_DOCKER: Store.InventoryApi/Dockerfile
    INVENTORY_IMAGE: inventory

    PRODUCTS_DOCKER: Store.ProductApi/Dockerfile
    PRODUCTS_IMAGE: products

    jobs:
    # Create the required Azure resources
    provision:
    runs-on: ubuntu-latest

    steps:

    - name: Checkout to the branch
    uses: actions/checkout@v2

    - name: Azure Login
    uses: azure/login@v1
    with:
    creds: ${{ secrets.AzureSPN }}

    - name: Create resource group
    uses: azure/CLI@v1
    with:
    inlineScript: >
    echo "Creating resource group in Azure"
    echo "Executing 'az group create -l ${{ env.REGION }} -n ${{ env.RESOURCE_GROUP_NAME }}'"
    az group create -l ${{ env.REGION }} -n ${{ env.RESOURCE_GROUP_NAME }}

    # Use Bicep templates to create the resources in Azure
    - name: Creating resources
    uses: azure/CLI@v1
    with:
    inlineScript: >
    echo "Creating resources"
    az deployment group create --resource-group ${{ env.RESOURCE_GROUP_NAME }} --template-file '/github/workspace/Azure/main.bicep' --debug

    # Build the three app container images
    build:
    runs-on: ubuntu-latest
    needs: provision

    steps:

    - name: Checkout to the branch
    uses: actions/checkout@v2

    - name: Azure Login
    uses: azure/login@v1
    with:
    creds: ${{ secrets.AzureSPN }}

    - name: Set up Docker Buildx
    uses: docker/setup-buildx-action@v1

    - name: Login to ACR
    run: |
    set -euo pipefail
    access_token=$(az account get-access-token --query accessToken -o tsv)
    refresh_token=$(curl https://${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io/oauth2/exchange -v -d "grant_type=access_token&service=${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io&access_token=$access_token" | jq -r .refresh_token)
    docker login -u 00000000-0000-0000-0000-000000000000 --password-stdin ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io <<< "$refresh_token"

    - name: Build the products api image and push it to ACR
    uses: docker/build-push-action@v2
    with:
    push: true
    tags: ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io/${{ env.PRODUCTS_IMAGE }}:${{ github.sha }}
    file: ${{ env.PRODUCTS_DOCKER }}

    - name: Build the inventory api image and push it to ACR
    uses: docker/build-push-action@v2
    with:
    push: true
    tags: ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io/${{ env.INVENTORY_IMAGE }}:${{ github.sha }}
    file: ${{ env.INVENTORY_DOCKER }}

    - name: Build the frontend image and push it to ACR
    uses: docker/build-push-action@v2
    with:
    push: true
    tags: ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io/${{ env.STORE_IMAGE }}:${{ github.sha }}
    file: ${{ env.STORE_DOCKER }}

    # Deploy the three container images
    deploy:
    runs-on: ubuntu-latest
    needs: build

    steps:

    - name: Checkout to the branch
    uses: actions/checkout@v2

    - name: Azure Login
    uses: azure/login@v1
    with:
    creds: ${{ secrets.AzureSPN }}

    - name: Installing Container Apps extension
    uses: azure/CLI@v1
    with:
    inlineScript: >
    az config set extension.use_dynamic_install=yes_without_prompt

    az extension add --name containerapp --yes

    - name: Login to ACR
    run: |
    set -euo pipefail
    access_token=$(az account get-access-token --query accessToken -o tsv)
    refresh_token=$(curl https://${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io/oauth2/exchange -v -d "grant_type=access_token&service=${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io&access_token=$access_token" | jq -r .refresh_token)
    docker login -u 00000000-0000-0000-0000-000000000000 --password-stdin ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io <<< "$refresh_token"

    - name: Deploy Container Apps
    uses: azure/CLI@v1
    with:
    inlineScript: >
    az containerapp registry set -n products -g ${{ env.RESOURCE_GROUP_NAME }} --server ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io

    az containerapp update -n products -g ${{ env.RESOURCE_GROUP_NAME }} -i ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io/${{ env.PRODUCTS_IMAGE }}:${{ github.sha }}

    az containerapp registry set -n inventory -g ${{ env.RESOURCE_GROUP_NAME }} --server ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io

    az containerapp update -n inventory -g ${{ env.RESOURCE_GROUP_NAME }} -i ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io/${{ env.INVENTORY_IMAGE }}:${{ github.sha }}

    az containerapp registry set -n store -g ${{ env.RESOURCE_GROUP_NAME }} --server ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io

    az containerapp update -n store -g ${{ env.RESOURCE_GROUP_NAME }} -i ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io/${{ env.STORE_IMAGE }}:${{ github.sha }}

    - name: logout
    run: >
    az logout

    Understanding the Bicep templates

    During the provisioning stage of the GitHub Actions workflow, the main.bicep file is processed. Bicep files provide a declarative way of generating resources in Azure and are ideal for managing infrastructure as code. You can learn more about Bicep in the related documentation. The main.bicep file in the sample project creates the following resources:

    • The container registry to store images of the containerized apps.
    • The container apps environment, which handles networking and resource management for the container apps.
    • Three container apps - one for the Blazor front-end and two for the back-end product and inventory APIs.
    • Configuration values to connect these services together

    main.bicep without Dapr

    param location string = resourceGroup().location

    # create the azure container registry
    resource acr 'Microsoft.ContainerRegistry/registries@2021-09-01' = {
    name: toLower('${resourceGroup().name}acr')
    location: location
    sku: {
    name: 'Basic'
    }
    properties: {
    adminUserEnabled: true
    }
    }

    # create the aca environment
    module env 'environment.bicep' = {
    name: 'containerAppEnvironment'
    params: {
    location: location
    }
    }

    # create the various configuration pairs
    var shared_config = [
    {
    name: 'ASPNETCORE_ENVIRONMENT'
    value: 'Development'
    }
    {
    name: 'APPINSIGHTS_INSTRUMENTATIONKEY'
    value: env.outputs.appInsightsInstrumentationKey
    }
    {
    name: 'APPLICATIONINSIGHTS_CONNECTION_STRING'
    value: env.outputs.appInsightsConnectionString
    }
    ]

    # create the products api container app
    module products 'container_app.bicep' = {
    name: 'products'
    params: {
    name: 'products'
    location: location
    registryPassword: acr.listCredentials().passwords[0].value
    registryUsername: acr.listCredentials().username
    containerAppEnvironmentId: env.outputs.id
    registry: acr.name
    envVars: shared_config
    externalIngress: false
    }
    }

    # create the inventory api container app
    module inventory 'container_app.bicep' = {
    name: 'inventory'
    params: {
    name: 'inventory'
    location: location
    registryPassword: acr.listCredentials().passwords[0].value
    registryUsername: acr.listCredentials().username
    containerAppEnvironmentId: env.outputs.id
    registry: acr.name
    envVars: shared_config
    externalIngress: false
    }
    }

    # create the store api container app
    var frontend_config = [
    {
    name: 'ProductsApi'
    value: 'http://${products.outputs.fqdn}'
    }
    {
    name: 'InventoryApi'
    value: 'http://${inventory.outputs.fqdn}'
    }
    ]

    module store 'container_app.bicep' = {
    name: 'store'
    params: {
    name: 'store'
    location: location
    registryPassword: acr.listCredentials().passwords[0].value
    registryUsername: acr.listCredentials().username
    containerAppEnvironmentId: env.outputs.id
    registry: acr.name
    envVars: union(shared_config, frontend_config)
    externalIngress: true
    }
    }

    main.bicep with Dapr


    param location string = resourceGroup().location

    # create the azure container registry
    resource acr 'Microsoft.ContainerRegistry/registries@2021-09-01' = {
    name: toLower('${resourceGroup().name}acr')
    location: location
    sku: {
    name: 'Basic'
    }
    properties: {
    adminUserEnabled: true
    }
    }

    # create the aca environment
    module env 'environment.bicep' = {
    name: 'containerAppEnvironment'
    params: {
    location: location
    }
    }

    # create the various config pairs
    var shared_config = [
    {
    name: 'ASPNETCORE_ENVIRONMENT'
    value: 'Development'
    }
    {
    name: 'APPINSIGHTS_INSTRUMENTATIONKEY'
    value: env.outputs.appInsightsInstrumentationKey
    }
    {
    name: 'APPLICATIONINSIGHTS_CONNECTION_STRING'
    value: env.outputs.appInsightsConnectionString
    }
    ]

    # create the products api container app
    module products 'container_app.bicep' = {
    name: 'products'
    params: {
    name: 'products'
    location: location
    registryPassword: acr.listCredentials().passwords[0].value
    registryUsername: acr.listCredentials().username
    containerAppEnvironmentId: env.outputs.id
    registry: acr.name
    envVars: shared_config
    externalIngress: false
    }
    }

    # create the inventory api container app
    module inventory 'container_app.bicep' = {
    name: 'inventory'
    params: {
    name: 'inventory'
    location: location
    registryPassword: acr.listCredentials().passwords[0].value
    registryUsername: acr.listCredentials().username
    containerAppEnvironmentId: env.outputs.id
    registry: acr.name
    envVars: shared_config
    externalIngress: false
    }
    }

    # create the store api container app
    module store 'container_app.bicep' = {
    name: 'store'
    params: {
    name: 'store'
    location: location
    registryPassword: acr.listCredentials().passwords[0].value
    registryUsername: acr.listCredentials().username
    containerAppEnvironmentId: env.outputs.id
    registry: acr.name
    envVars: shared_config
    externalIngress: true
    }
    }


    Bicep Modules

    The main.bicep file references modules to create resources, such as module products. Modules are a feature of Bicep templates that enable you to abstract resource declarations into their own files or sub-templates. As the main.bicep file is processed, the defined modules are also evaluated. Modules allow you to create resources in a more organized and reusable way. They can also define input and output parameters that are passed to and from the parent template, such as the name of a resource.

    For example, the environment.bicep module extracts the details of creating a container apps environment into a reusable template. The module defines necessary resource dependencies such as Log Analytics Workspaces and an Application Insights instance.

    environment.bicep without Dapr

    param baseName string = resourceGroup().name
    param location string = resourceGroup().location

    resource logs 'Microsoft.OperationalInsights/workspaces@2021-06-01' = {
    name: '${baseName}logs'
    location: location
    properties: any({
    retentionInDays: 30
    features: {
    searchVersion: 1
    }
    sku: {
    name: 'PerGB2018'
    }
    })
    }

    resource appInsights 'Microsoft.Insights/components@2020-02-02' = {
    name: '${baseName}ai'
    location: location
    kind: 'web'
    properties: {
    Application_Type: 'web'
    WorkspaceResourceId: logs.id
    }
    }

    resource env 'Microsoft.App/managedEnvironments@2022-01-01-preview' = {
    name: '${baseName}env'
    location: location
    properties: {
    appLogsConfiguration: {
    destination: 'log-analytics'
    logAnalyticsConfiguration: {
    customerId: logs.properties.customerId
    sharedKey: logs.listKeys().primarySharedKey
    }
    }
    }
    }

    output id string = env.id
    output appInsightsInstrumentationKey string = appInsights.properties.InstrumentationKey
    output appInsightsConnectionString string = appInsights.properties.ConnectionString

    environment.bicep with Dapr


    param baseName string = resourceGroup().name
    param location string = resourceGroup().location

    resource logs 'Microsoft.OperationalInsights/workspaces@2021-06-01' = {
    name: '${baseName}logs'
    location: location
    properties: any({
    retentionInDays: 30
    features: {
    searchVersion: 1
    }
    sku: {
    name: 'PerGB2018'
    }
    })
    }

    resource appInsights 'Microsoft.Insights/components@2020-02-02' = {
    name: '${baseName}ai'
    location: location
    kind: 'web'
    properties: {
    Application_Type: 'web'
    WorkspaceResourceId: logs.id
    }
    }

    resource env 'Microsoft.App/managedEnvironments@2022-01-01-preview' = {
    name: '${baseName}env'
    location: location
    properties: {
    appLogsConfiguration: {
    destination: 'log-analytics'
    logAnalyticsConfiguration: {
    customerId: logs.properties.customerId
    sharedKey: logs.listKeys().primarySharedKey
    }
    }
    }
    }

    output id string = env.id
    output appInsightsInstrumentationKey string = appInsights.properties.InstrumentationKey
    output appInsightsConnectionString string = appInsights.properties.ConnectionString


    The container_apps.bicep template defines numerous parameters to provide a reusable template for creating container apps. This allows the module to be used in other CI/CD pipelines as well.

    container_app.bicep without Dapr

    param name string
    param location string = resourceGroup().location
    param containerAppEnvironmentId string
    param repositoryImage string = 'mcr.microsoft.com/azuredocs/containerapps-helloworld:latest'
    param envVars array = []
    param registry string
    param minReplicas int = 1
    param maxReplicas int = 1
    param port int = 80
    param externalIngress bool = false
    param allowInsecure bool = true
    param transport string = 'http'
    param registryUsername string
    @secure()
    param registryPassword string

    resource containerApp 'Microsoft.App/containerApps@2022-01-01-preview' ={
    name: name
    location: location
    properties:{
    managedEnvironmentId: containerAppEnvironmentId
    configuration: {
    activeRevisionsMode: 'single'
    secrets: [
    {
    name: 'container-registry-password'
    value: registryPassword
    }
    ]
    registries: [
    {
    server: registry
    username: registryUsername
    passwordSecretRef: 'container-registry-password'
    }
    ]
    ingress: {
    external: externalIngress
    targetPort: port
    transport: transport
    allowInsecure: allowInsecure
    }
    }
    template: {
    containers: [
    {
    image: repositoryImage
    name: name
    env: envVars
    }
    ]
    scale: {
    minReplicas: minReplicas
    maxReplicas: maxReplicas
    }
    }
    }
    }

    output fqdn string = containerApp.properties.configuration.ingress.fqdn

    container_app.bicep with Dapr


    param name string
    param location string = resourceGroup().location
    param containerAppEnvironmentId string
    param repositoryImage string = 'mcr.microsoft.com/azuredocs/containerapps-helloworld:latest'
    param envVars array = []
    param registry string
    param minReplicas int = 1
    param maxReplicas int = 1
    param port int = 80
    param externalIngress bool = false
    param allowInsecure bool = true
    param transport string = 'http'
    param appProtocol string = 'http'
    param registryUsername string
    @secure()
    param registryPassword string

    resource containerApp 'Microsoft.App/containerApps@2022-01-01-preview' ={
    name: name
    location: location
    properties:{
    managedEnvironmentId: containerAppEnvironmentId
    configuration: {
    dapr: {
    enabled: true
    appId: name
    appPort: port
    appProtocol: appProtocol
    }
    activeRevisionsMode: 'single'
    secrets: [
    {
    name: 'container-registry-password'
    value: registryPassword
    }
    ]
    registries: [
    {
    server: registry
    username: registryUsername
    passwordSecretRef: 'container-registry-password'
    }
    ]
    ingress: {
    external: externalIngress
    targetPort: port
    transport: transport
    allowInsecure: allowInsecure
    }
    }
    template: {
    containers: [
    {
    image: repositoryImage
    name: name
    env: envVars
    }
    ]
    scale: {
    minReplicas: minReplicas
    maxReplicas: maxReplicas
    }
    }
    }
    }

    output fqdn string = containerApp.properties.configuration.ingress.fqdn


    Understanding configuration differences with Dapr

    The code for this specific sample application is largely the same whether or not Dapr is integrated. However, even with this simple app, there are a few benefits and configuration differences when using Dapr that are worth exploring.

    In this scenario most of the changes are related to communication between the container apps. However, you can explore the full range of Dapr benefits by reading the Dapr integration with Azure Container Apps article in the conceptual documentation.

    Without Dapr

    Without Dapr the main.bicep template handles wiring up the front-end store app to communicate with the back-end apis by manually managing environment variables. The bicep template retrieves the fully qualified domains (fqdn) of the API apps as output parameters when they are created. Those configurations are then set as environment variables on the store container app.


    # Retrieve environment variables from API container creation
    var frontend_config = [
    {
    name: 'ProductsApi'
    value: 'http://${products.outputs.fqdn}'
    }
    {
    name: 'InventoryApi'
    value: 'http://${inventory.outputs.fqdn}'
    }
    ]

    # create the store api container app, passing in config
    module store 'container_app.bicep' = {
    name: 'store'
    params: {
    name: 'store'
    location: location
    registryPassword: acr.listCredentials().passwords[0].value
    registryUsername: acr.listCredentials().username
    containerAppEnvironmentId: env.outputs.id
    registry: acr.name
    envVars: union(shared_config, frontend_config)
    externalIngress: true
    }
    }

    The environment variables are then retrieved inside of the program class and used to configure the base URLs of the corresponding HTTP clients.


    builder.Services.AddHttpClient("Products", (httpClient) => httpClient.BaseAddress = new Uri(builder.Configuration.GetValue<string>("ProductsApi")));
    builder.Services.AddHttpClient("Inventory", (httpClient) => httpClient.BaseAddress = new Uri(builder.Configuration.GetValue<string>("InventoryApi")));

    With Dapr

    Dapr can be enabled on a container app when it is created, as seen below. This configuration adds a Dapr sidecar to the app to streamline discovery and communication features between the different container apps in your environment.


    # Create the container app with Dapr enabled
    resource containerApp 'Microsoft.App/containerApps@2022-01-01-preview' ={
    name: name
    location: location
    properties:{
    managedEnvironmentId: containerAppEnvironmentId
    configuration: {
    dapr: {
    enabled: true
    appId: name
    appPort: port
    appProtocol: appProtocol
    }
    activeRevisionsMode: 'single'
    secrets: [
    {
    name: 'container-registry-password'
    value: registryPassword
    }
    ]

    # Rest of template omitted for brevity...
    }
    }

    Some of these Dapr features can be surfaced through the program file. You can configure your HttpClient to leverage Dapr configurations when communicating with other apps in your environment.


    // reconfigure code to make requests to Dapr sidecar
    var baseURL = (Environment.GetEnvironmentVariable("BASE_URL") ?? "http://localhost") + ":" + (Environment.GetEnvironmentVariable("DAPR_HTTP_PORT") ?? "3500");
    builder.Services.AddHttpClient("Products", (httpClient) =>
    {
    httpClient.BaseAddress = new Uri(baseURL);
    httpClient.DefaultRequestHeaders.Add("dapr-app-id", "Products");
    });

    builder.Services.AddHttpClient("Inventory", (httpClient) =>
    {
    httpClient.BaseAddress = new Uri(baseURL);
    httpClient.DefaultRequestHeaders.Add("dapr-app-id", "Inventory");
    });


    Clean up resources

    If you're not going to continue to use this application, you can delete the Azure Container Apps and all the associated services by removing the resource group.

    Follow these steps in the Azure portal to remove the resources you created:

    1. In the Azure portal, navigate to the msdocswebappsapi resource group using the left navigation or search bar.
    2. Select the Delete resource group button at the top of the resource group Overview.
    3. Enter the resource group name msdocswebappsapi in the Are you sure you want to delete "msdocswebappsapi" confirmation dialog.
    4. Select Delete.
      The process to delete the resource group may take a few minutes to complete.
    - - + + \ No newline at end of file diff --git a/blog/tags/autoscaling/index.html b/blog/tags/autoscaling/index.html index dec089df52..f1736c8330 100644 --- a/blog/tags/autoscaling/index.html +++ b/blog/tags/autoscaling/index.html @@ -14,13 +14,13 @@ - - + +

    One post tagged with "autoscaling"

    View All Tags

    · 7 min read
    Paul Yu

    Welcome to Day 11 of #30DaysOfServerless!

    Yesterday we explored Azure Container Concepts related to environments, networking and microservices communication - and illustrated these with a deployment example. Today, we turn our attention to scaling your container apps with demand.


    What We'll Cover

    • What makes ACA Serverless?
    • What is Keda?
    • Scaling Your ACA
    • ACA Scaling In Action
    • Exercise: Explore azure-opensource-labs examples
    • Resources: For self-study!


    So, what makes Azure Container Apps "serverless"?

    Today we are going to focus on what makes Azure Container Apps (ACA) a "serverless" offering. But what does the term "serverless" really mean? As much as we'd like to think there aren't any servers involved, that is certainly not the case. In general, "serverless" means that most (if not all) server maintenance has been abstracted away from you.

    With serverless, you don't spend any time managing and patching servers. This concern is offloaded to Azure and you simply focus on adding business value through application delivery. In addition to operational efficiency, cost efficiency can be achieved with serverless on-demand pricing models. Your workload horizontally scales out based on need and you only pay for what you use. To me, this is serverless, and my teammate @StevenMurawski said it best... "being able to scale to zero is what gives ACA it's serverless magic."

    Scaling your Container Apps

    If you don't know by now, ACA is built on a solid open-source foundation. Behind the scenes, it runs on a managed Kubernetes cluster and includes several open-source components out-of-the box including Dapr to help you build and run microservices, Envoy Proxy for ingress capabilities, and KEDA for event-driven autoscaling. Again, you do not need to install these components yourself. All you need to be concerned with is enabling and/or configuring your container app to leverage these components.

    Let's take a closer look at autoscaling in ACA to help you optimize your container app.

    What is KEDA?

    KEDA stands for Kubernetes Event-Driven Autoscaler. It is an open-source project initially started by Microsoft and Red Hat and has been donated to the Cloud-Native Computing Foundation (CNCF). It is being maintained by a community of 200+ contributors and adopted by many large organizations. In terms of its status as a CNCF project it is currently in the Incubating Stage which means the project has gone through significant due diligence and on its way towards the Graduation Stage.

    Prior to KEDA, horizontally scaling your Kubernetes deployment was achieved through the Horizontal Pod Autoscaler (HPA) which relies on resource metrics such as CPU and memory to determine when additional replicas should be deployed. Being limited to CPU and memory falls a bit short for certain workloads. This is especially true for apps that need to processes messages from a queue or HTTP-based apps that can handle a specific amount of incoming HTTP requests at a time. KEDA aims to fill that gap and provides a much more robust framework for scaling by working in conjunction with HPA. It offers many scalers for you to implement and even allows your deployments to scale to zero! 🥳

    KEDA architecture

    Configuring ACA scale rules

    As I mentioned above, ACA's autoscaling feature leverages KEDA and gives you the ability to configure the number of replicas to deploy based on rules (event triggers). The number of replicas can be configured as a static number or a range (minimum and maximum). So if you need your containers to run 24/7, set the min and max to be the same value. By default, when you deploy a container app, it is set to scale from 0 to 10 replicas. The default scaling rule uses HTTP scaling and defaults to a minimum of 10 concurrent requests per second. Once the threshold of 10 concurrent request per second is met, another replica will be deployed until it reaches the maximum number of replicas.

    At the time of this writing, a container app can have up to 30 replicas.

    Default autoscaler

    As a best practice, if you have a Min / max replicas range configured, you should configure a scaling rule even if it is just explicitly setting the default values.

    Adding HTTP scaling rule

    In addition to HTTP scaling, you can also configure an Azure queue rule, which allows you to use Azure Storage Queues as an event data source.

    Adding Azure Queue scaling rule

    The most flexibility comes with the Custom rule type. This opens up a LOT more options for scaling. All of KEDA's event-based scalers are supported with this option 🚀

    Adding Custom scaling rule

    Translating KEDA templates to Azure templates

    When you implement Custom rules, you need to become familiar with translating KEDA templates to Azure Resource Manager templates or ACA YAML manifests. The KEDA scaler documentation is great and it should be simple to translate KEDA template metadata to an ACA rule metadata.

    The images below shows how to translated a scaling rule which uses Azure Service Bus as an event data source. The custom rule type is set to azure-servicebus and details of the service bus is added to the Metadata section. One important thing to note here is that the connection string to the service bus was added as a secret on the container app and the trigger parameter must be set to connection.

    Azure Container App custom rule metadata

    Azure Container App custom rule metadata

    Additional examples of KEDA scaler conversion can be found in the resources section and example video below.

    See Container App scaling in action

    Now that we've built up some foundational knowledge on how ACA autoscaling is implemented and configured, let's look at a few examples.

    Autoscaling based on HTTP traffic load

    Autoscaling based on Azure Service Bus message queues

    Summary

    ACA brings you a true serverless experience and gives you the ability to configure autoscaling rules based on KEDA scaler templates. This gives you flexibility to scale based on a wide variety of data sources in an event-driven manner. With the amount built-in scalers currently available, there is probably a scaler out there for all your use cases. If not, I encourage you to get involved with the KEDA community and help make it better!

    Exercise

    By now, you've probably read and seen enough and now ready to give this autoscaling thing a try. The example I walked through in the videos above can be found at the azure-opensource-labs repo. I highly encourage you to head over to the containerapps-terraform folder and try the lab out. There you'll find instructions which will cover all the steps and tools you'll need implement autoscaling container apps within your own Azure subscription.

    If you have any questions or feedback, please let us know in the comments below or reach out on Twitter @pauldotyu

    Have fun scaling your containers!

    Resources

    - - + + \ No newline at end of file diff --git a/blog/tags/azd/index.html b/blog/tags/azd/index.html index 03c9a9982f..c74aff98a0 100644 --- a/blog/tags/azd/index.html +++ b/blog/tags/azd/index.html @@ -14,8 +14,8 @@ - - + +
    @@ -26,7 +26,7 @@

    ...and that's it! We've successfully deployed our application on Azure!

    But there's more!

    Best practices: Monitoring and CI/CD!

    In my opinion, it's not enough to just set up the application on Azure! I want to know that my web app is performant and serving my users reliably! I also want to make sure that I'm not inadvertently breaking my application as I continue to make changes to it. Thankfully, the Azure Developer CLI also handles all of this via two additional commands - azd monitor and azd pipeline config.

    Application Monitoring

    When we provisioned all of our infrastructure, we also set up application monitoring via a Bicep file in our .infra/ directory that spec'd out an Application Insights dashboard. By running azd monitor we can see the dashboard with live metrics that was configured for the application.

    We can also navigate to the Application Dashboard by clicking on the resource group name, where you can set a specific refresh rate for the dashboard, and see usage, reliability, and performance metrics over time.

    I don't know about everyone else but I have spent a ton of time building out similar dashboards. It can be super time-consuming to write all the queries and create the visualizations so this feels like a real time saver.

    CI/CD

    Finally let's talk about setting up CI/CD! This might be my favorite azd feature. As I mentioned before, the Azure Developer CLI has a command, azd pipeline config, which uses the files in the .github/ directory to set up a GitHub Action. More than that, if there is no upstream repo, the Developer CLI will actually help you create one. But what does this mean exactly? Because our GitHub Action is using the same commands you'd run in the CLI under the hood, we're actually going to have CI/CD set up to run on every commit into the repo, against real Azure resources. What a sweet collaboration feature!

    That's it! We've gone end-to-end with the Azure Developer CLI - initialized a project, provisioned the resources on Azure, deployed our code on Azure, set up monitoring logs and dashboards, and set up a CI/CD pipeline with GitHub Actions to run on every commit into the repo (on real Azure resources!).

    Exercise: Try it yourself or create your own template!

    As an exercise, try out the workflow above with any template on GitHub!

    Or, try turning your own project into an Azure Developer CLI-enabled template by following this guidance. If you create your own template, don't forget to tag the repo with the azd-templates topic on GitHub to help others find it (unfamiliar with GitHub topics? Learn how to add topics to your repo)! We'd also love to chat with you about your experience creating an azd template - if you're open to providing feedback around this, please fill out this form!

    Resources

    - - + + \ No newline at end of file diff --git a/blog/tags/azure-container-apps/index.html b/blog/tags/azure-container-apps/index.html index 9a0275277c..8b9c7fc774 100644 --- a/blog/tags/azure-container-apps/index.html +++ b/blog/tags/azure-container-apps/index.html @@ -14,14 +14,14 @@ - - + +

    20 posts tagged with "azure-container-apps"

    View All Tags

    · 7 min read
    Devanshi Joshi

    It's Serverless September in a Nutshell! Join us as we unpack our month-long learning journey exploring the core technology pillars for Serverless architectures on Azure. Then end with a look at next steps to build your Cloud-native applications on Azure.


    What We'll Cover

    • Functions-as-a-Service (FaaS)
    • Microservices and Containers
    • Serverless Integrations
    • End-to-End Solutions
    • Developer Tools & #Hacktoberfest

    Banner for Serverless September


    Building Cloud-native Apps

    By definition, cloud-native technologies empower organizations to build and run scalable applications in modern, dynamic environments such as public, private, and hybrid clouds. You can learn more about cloud-native in Kendall Roden's #ServerlessSeptember post on Going Cloud-native with Azure Container Apps.

    Serveless technologies accelerate productivity and minimize costs for deploying applications at cloud scale. So, what can we build with serverless technologies in cloud-native on Azure? Anything that is event-driven - examples include:

    • Microservices - scaled by KEDA-compliant triggers
    • Public API Endpoints - scaled by #concurrent HTTP requests
    • Event-Driven Applications - scaled by length of message queue
    • Web Applications - scaled by #concurrent HTTP requests
    • Background Process - scaled by CPU and Memory usage

    Great - but as developers, we really want to know how we can get started building and deploying serverless solutions on Azure. That was the focus of our #ServerlessSeptember journey. Let's take a quick look at the four key themes.

    Functions-as-a-Service (FaaS)

    Functions-as-a-Service (FaaS) is the epitome of developer productivity for full-stack modern apps. As developers, you don't manage infrastructure and focus only on business logic and application code. And, with Serverless Compute you only pay for when your code runs - making this the simplest first step to begin migrating your application to cloud-native.

    In Azure, FaaS is provided by Azure Functions. Check out our Functions + Serverless on Azure to go from learning core concepts, to building your first Functions app in your programming language of choice. Azure functions support multiple programming languages including C#, F#, Java, JavaScript, Python, Typescript, and PowerShell.

    Want to get extended language support for languages like Go, and Rust? You can Use Custom Handlers to make this happen! But what if you want to have long-running functions, or create complex workflows involving more than one function? Read our post on Durable Entities to learn how you can orchestrate this with Azure Functions.

    Check out this recent AskTheExpert Q&A session with the Azure Functions team to get answers to popular community questions on Azure Functions features and usage.

    Microservices and Containers

    Functions-as-a-Service is an ideal first step towards serverless development. But Functions are just one of the 5 pillars of cloud-native. This week we'll look at two of the other pillars: microservices and containers - with specific focus on two core technologies: Azure Container Apps and Dapr (Distributed Application Runtime).

    In this 6-part series of posts, we walk through each technology independently, before looking at the value of building Azure Container Apps with Dapr.

    • In Hello Container Apps we learned core concepts & deployed our first ACA.
    • In Microservices Communication we learned about ACA environments and virtual networks, and how microservices communicate in ACA with a hands-on tutorial.
    • In Scaling Your Container Apps we learned about KEDA (Kubernetes Event-Driven Autoscaler) and configuring ACA for autoscaling with KEDA-compliant triggers.
    • In Build with Dapr we introduced the Distributed Application Runtime (Dapr), exploring its Building Block APIs and sidecar architecture for working with ACA.
    • In Secure ACA Access we learned how to secure ACA access to external services with - and without - Dapr, covering Secret Stores and Managed Identity.
    • Finally, Build ACA with Dapr tied it all together with a enterprise app scenario where an orders processor (ACA) uses Dapr APIs (PubSub, State Management) to receive and store order messages from Azure Service Bus.

    Build ACA with Dapr

    Check out this recent AskTheExpert Q&A session with the Azure Container Apps team for answers to popular community questions on core features and usage.

    Serverless Integrations

    In the first half of the month we looked at compute resources for building and deploying serverless applications. In the second half, we look at integration tools and resources that automate developer workflows to streamline the end-to-end developer experience.

    In Azure, this is enabled by services like Azure Logic Apps and Azure Event Grid. Azure Logic Apps provides a visual designer to create and automate workflows with little or no code involved. Azure Event Grid provides a highly-scable event broker with support for pub/sub communications to drive async event-driven architectures.

    • In Tracking Weather Data Changes With Logic Apps we look at how you can use Logic Apps to integrate the MSN weather service with Azure CosmosDB, allowing automated collection of weather data on changes.

    • In Teach the Cloud to Read & Categorize Mail we take it a step further, using Logic Apps to automate a workflow that includes a Computer Vision service to "read" images and store the results to CosmosDB.

    • In Integrate with Microsoft Graph we explore a multi-cloud scenario (Azure + M365) where change notifications from Microsoft Graph can be integrated using Logic Apps and Event Hubs to power an onboarding workflow.

    • In Cloud Events with Event Grid we learn about the CloudEvents specification (for consistently describing event data) - and learn how Event Grid brokers events in this format. Azure Logic Apps can be an Event handler (subscriber) that uses the event to trigger an automated workflow on receipt.

      Azure Event Grid And Logic Apps

    Want to explore other such integrations? Browse Azure Architectures and filter by selected Azure services for more real-world scenarios.


    End-to-End Solutions

    We've covered serverless compute solutions (for building your serverless applications) and serverless integration services to automate end-to-end workflows in synchronous or asynchronous event-driven architectures. In this final week, we want to leave you with a sense of end-to-end development tools and use cases that can be enabled by Serverless on Azure. Here are some key examples:

    ArticleDescription
    In this tutorial, you'll learn to deploy a containerized ASP.NET Core 6.0 application to Azure Container Apps - with a Blazor front-end and two Web API projects
    Deploy Java containers to cloudIn this tutorial you learn to build and deploy a Java application running on Spring Boot, by publishing it in a container to Azure Container Registry, then deploying to Azure Container Apps,, from ACR, via the Azure Portal.
    **Where am I? My GPS Location with Serverless Power Platform Custom Connector**In this step-by-step tutorial you learn to integrate a serverless application (built on Azure Functions and OpenAPI) with Power Platforms custom connectors via Azure API Management (API-M).This pattern can empower a new ecosystem of fusion apps for cases like inventory management.
    And in our Serverless Hacks initiative, we walked through an 8-step hack to build a serverless tollbooth. Check out this 12-part video walkthrough of a reference solution using .NET.

    Developer Tools

    But wait - there's more. Those are a sample of the end-to-end application scenarios that are built on serverless on Azure. But what about the developer experience? In this article, we say hello to the Azure Developer CLI - an open-source tool that streamlines your develop-deploy workflow, with simple commands that map to core stages of your development journey. Go from code to cloud with one CLI

    And watch this space for more such tutorials and content through October, including a special #Hacktoberfest focused initiative to encourage and support first-time contributors to open-source. Here's a sneak peek at the project we plan to share - the new awesome-azd templates gallery.


    Join us at Microsoft Ignite!

    Want to continue your learning journey, and learn about what's next for Serverless on Azure? Microsoft Ignite happens Oct 12-14 this year and has multiple sessions on relevant technologies and tools. Check out the Session Catalog and register here to attend online.

    - - + + \ No newline at end of file diff --git a/blog/tags/azure-container-apps/page/10/index.html b/blog/tags/azure-container-apps/page/10/index.html index 9872a640cb..d9372233f8 100644 --- a/blog/tags/azure-container-apps/page/10/index.html +++ b/blog/tags/azure-container-apps/page/10/index.html @@ -14,14 +14,14 @@ - - + +

    20 posts tagged with "azure-container-apps"

    View All Tags

    · 11 min read
    Kendall Roden

    Welcome to Day 13 of #30DaysOfServerless!

    In the previous post, we learned about all things Distributed Application Runtime (Dapr) and highlighted the capabilities you can unlock through managed Dapr in Azure Container Apps! Today, we'll dive into how we can make use of Container Apps secrets and managed identities to securely access cloud-hosted resources that your Container Apps depend on!

    Ready? Let's go.


    What We'll Cover

    • Secure access to external services overview
    • Using Container Apps Secrets
    • Using Managed Identity for connecting to Azure resources
    • Using Dapr secret store component references (Dapr-only)
    • Conclusion
    • Resources: For self-study!


    Securing access to external services

    In most, if not all, microservice-based applications, one or more services in the system will rely on other cloud-hosted resources; Think external services like databases, secret stores, message brokers, event sources, etc. To interact with these services, an application must have the ability to establish a secure connection. Traditionally, an application will authenticate to these backing resources using some type of connection string or password.

    I'm not sure if it was just me, but one of the first things I learned as a developer was to ensure credentials and other sensitive information were never checked into the codebase. The ability to inject these values at runtime is a non-negotiable.

    In Azure Container Apps, applications can securely leverage connection information via Container Apps Secrets. If the resource is Azure-based, a more ideal solution that removes the dependence on secrets altogether is using Managed Identity.

    Specifically for Dapr-enabled container apps, users can now tap into the power of the Dapr secrets API! With this new capability unlocked in Container Apps, users can call the Dapr secrets API from application code to securely access secrets from Key Vault or other backing secret stores. In addition, customers can also make use of a secret store component reference when wiring up Dapr state store components and more!

    ALSO, I'm excited to share that support for Dapr + Managed Identity is now available!!. What does this mean? It means that you can enable Managed Identity for your container app - and when establishing connections via Dapr, the Dapr sidecar can use this identity! This means simplified components without the need for secrets when connecting to Azure services!

    Let's dive a bit deeper into the following three topics:

    1. Using Container Apps secrets in your container apps
    2. Using Managed Identity to connect to Azure services
    3. Connecting to services securely for Dapr-enabled apps

    Secure access to external services without Dapr

    Leveraging Container Apps secrets at runtime

    Users can leverage this approach for any values which need to be securely stored, however, it is recommended to use Managed Identity where possible when connecting to Azure-specific resources.

    First, let's establish a few important points regarding secrets in container apps:

    • Secrets are scoped at the container app level, meaning secrets cannot be shared across container apps today
    • When running in multiple-revision mode,
      • changes to secrets do not generate a new revision
      • running revisions will not be automatically restarted to reflect changes. If you want to force-update existing container app revisions to reflect the changed secrets values, you will need to perform revision restarts.
    STEP 1

    Provide the secure value as a secret parameter when creating your container app using the syntax "SECRET_NAME=SECRET_VALUE"

    az containerapp create \
    --resource-group "my-resource-group" \
    --name queuereader \
    --environment "my-environment-name" \
    --image demos/queuereader:v1 \
    --secrets "queue-connection-string=$CONNECTION_STRING"
    STEP 2

    Create an environment variable which references the value of the secret created in step 1 using the syntax "ENV_VARIABLE_NAME=secretref:SECRET_NAME"

    az containerapp create \
    --resource-group "my-resource-group" \
    --name myQueueApp \
    --environment "my-environment-name" \
    --image demos/myQueueApp:v1 \
    --secrets "queue-connection-string=$CONNECTIONSTRING" \
    --env-vars "QueueName=myqueue" "ConnectionString=secretref:queue-connection-string"

    This ConnectionString environment variable can be used within your application code to securely access the connection string value at runtime.

    Using Managed Identity to connect to Azure services

    A managed identity from Azure Active Directory (Azure AD) allows your container app to access other Azure AD-protected resources. This approach is recommended where possible as it eliminates the need for managing secret credentials in your container apps and allows you to properly scope the permissions needed for a given container app using role-based access control. Both system-assigned and user-assigned identities are available in container apps. For more background on managed identities in Azure AD, see Managed identities for Azure resources.

    To configure your app with a system-assigned managed identity you will follow similar steps to the following:

    STEP 1

    Run the following command to create a system-assigned identity for your container app

    az containerapp identity assign \
    --name "myQueueApp" \
    --resource-group "my-resource-group" \
    --system-assigned
    STEP 2

    Retrieve the identity details for your container app and store the Principal ID for the identity in a variable "PRINCIPAL_ID"

    az containerapp identity show \
    --name "myQueueApp" \
    --resource-group "my-resource-group"
    STEP 3

    Assign the appropriate roles and permissions to your container app's managed identity using the Principal ID in step 2 based on the resources you need to access (example below)

    az role assignment create \
    --role "Storage Queue Data Contributor" \
    --assignee $PRINCIPAL_ID \
    --scope "/subscriptions/<subscription>/resourceGroups/<resource-group>/providers/Microsoft.Storage/storageAccounts/<storage-account>/queueServices/default/queues/<queue>"

    After running the above commands, your container app will be able to access your Azure Store Queue because it's managed identity has been assigned the "Store Queue Data Contributor" role. The role assignments you create will be contingent solely on the resources your container app needs to access. To instrument your code to use this managed identity, see more details here.

    In addition to using managed identity to access services from your container app, you can also use managed identity to pull your container images from Azure Container Registry.

    Secure access to external services with Dapr

    For Dapr-enabled apps, there are a few ways to connect to the resources your solutions depend on. In this section, we will discuss when to use each approach.

    1. Using Container Apps secrets in your Dapr components
    2. Using Managed Identity with Dapr Components
    3. Using Dapr Secret Stores for runtime secrets and component references

    Using Container Apps secrets in Dapr components

    Prior to providing support for the Dapr Secret's Management building block, this was the only approach available for securely storing sensitive values for use in Dapr components.

    In Dapr OSS, when no secret store reference is provided in a Dapr component file, the default secret store is set to "Kubernetes secrets". In Container Apps, we do not expose the ability to use this default store. Rather, Container Apps secrets can be used in it's place.

    With the introduction of the Secrets API and the ability to use Dapr + Managed Identity, this approach is useful for a limited number of scenarios:

    • Quick demos and dev/test scenarios using the Container Apps CLI
    • Securing values when a secret store is not configured or available for use
    • Using service principal credentials to configure an Azure Key Vault secret store component (Using Managed Identity is recommend)
    • Securing access credentials which may be required when creating a non-Azure secret store component
    STEP 1

    Create a Dapr component which can be used by one or more services in the container apps environment. In the below example, you will create a secret to store the storage account key and reference this secret from the appropriate Dapr metadata property.

       componentType: state.azure.blobstorage
    version: v1
    metadata:
    - name: accountName
    value: testStorage
    - name: accountKey
    secretRef: account-key
    - name: containerName
    value: myContainer
    secrets:
    - name: account-key
    value: "<STORAGE_ACCOUNT_KEY>"
    scopes:
    - myApp
    STEP 2

    Deploy the Dapr component using the below command with the appropriate arguments.

     az containerapp env dapr-component set \
    --name "my-environment" \
    --resource-group "my-resource-group" \
    --dapr-component-name statestore \
    --yaml "./statestore.yaml"

    Using Managed Identity with Dapr Components

    Dapr-enabled container apps can now make use of managed identities within Dapr components. This is the most ideal path for connecting to Azure services securely, and allows for the removal of sensitive values in the component itself.

    The Dapr sidecar makes use of the existing identities available within a given container app; Dapr itself does not have it's own identity. Therefore, the steps to enable Dapr + MI are similar to those in the section regarding managed identity for non-Dapr apps. See example steps below specifically for using a system-assigned identity:

    1. Create a system-assigned identity for your container app

    2. Retrieve the identity details for your container app and store the Principal ID for the identity in a variable "PRINCIPAL_ID"

    3. Assign the appropriate roles and permissions (for accessing resources backing your Dapr components) to your ACA's managed identity using the Principal ID

    4. Create a simplified Dapr component without any secrets required

          componentType: state.azure.blobstorage
      version: v1
      metadata:
      - name: accountName
      value: testStorage
      - name: containerName
      value: myContainer
      scopes:
      - myApp
    5. Deploy the component to test the connection from your container app via Dapr!

    Keep in mind, all Dapr components will be loaded by each Dapr-enabled container app in an environment by default. In order to avoid apps without the appropriate permissions from loading a component unsuccessfully, use scopes. This will ensure that only applications with the appropriate identities to access the backing resource load the component.

    Using Dapr Secret Stores for runtime secrets and component references

    Dapr integrates with secret stores to provide apps and other components with secure storage and access to secrets such as access keys and passwords. The Dapr Secrets API is now available for use in Container Apps.

    Using Dapr’s secret store building block typically involves the following:

    • Setting up a component for a specific secret store solution.
    • Retrieving secrets using the Dapr secrets API in the application code.
    • Optionally, referencing secrets in Dapr component files.

    Let's walk through a couple sample workflows involving the use of Dapr's Secrets Management capabilities!

    Setting up a component for a specific secret store solution

    1. Create an Azure Key Vault instance for hosting the secrets required by your application.

      az keyvault create --name "<your-unique-keyvault-name>" --resource-group "my-resource-group" --location "<your-location>"
    2. Create an Azure Key Vault component in your environment without the secrets values, as the connection will be established to Azure Key Vault via Managed Identity.

          componentType: secretstores.azure.keyvault
      version: v1
      metadata:
      - name: vaultName
      value: "[your_keyvault_name]"
      scopes:
      - myApp
      az containerapp env dapr-component set \
      --name "my-environment" \
      --resource-group "my-resource-group" \
      --dapr-component-name secretstore \
      --yaml "./secretstore.yaml"
    3. Run the following command to create a system-assigned identity for your container app

      az containerapp identity assign \
      --name "myApp" \
      --resource-group "my-resource-group" \
      --system-assigned
    4. Retrieve the identity details for your container app and store the Principal ID for the identity in a variable "PRINCIPAL_ID"

      az containerapp identity show \
      --name "myApp" \
      --resource-group "my-resource-group"
    5. Assign the appropriate roles and permissions to your container app's managed identity to access Azure Key Vault

      az role assignment create \
      --role "Key Vault Secrets Officer" \
      --assignee $PRINCIPAL_ID \
      --scope /subscriptions/{subscriptionid}/resourcegroups/{resource-group-name}/providers/Microsoft.KeyVault/vaults/{key-vault-name}
    6. Begin using the Dapr Secrets API in your application code to retrieve secrets! See additional details here.

    Referencing secrets in Dapr component files

    Once a Dapr secret store component is available in the environment, it can be used to retrieve secrets for use in other components. For example, when creating a state store component, you can add a reference to the Dapr secret store from which you would like to source connection information. You will no longer use secrets directly in the component spec, but rather will instruct the Dapr sidecar to retrieve the secrets from the specified store.

          componentType: state.azure.blobstorage
    version: v1
    metadata:
    - name: accountName
    value: testStorage
    - name: accountKey
    secretRef: account-key
    - name: containerName
    value: myContainer
    secretStoreComponent: "<SECRET_STORE_COMPONENT_NAME>"
    scopes:
    - myApp

    Summary

    In this post, we have covered the high-level details on how to work with secret values in Azure Container Apps for both Dapr and Non-Dapr apps. In the next article, we will walk through a complex Dapr example from end-to-end which makes use of the new support for Dapr + Managed Identity. Stayed tuned for additional documentation around Dapr secrets as it will be release in the next two weeks!

    Resources

    Here are the main resources to explore for self-study:

    - - + + \ No newline at end of file diff --git a/blog/tags/azure-container-apps/page/11/index.html b/blog/tags/azure-container-apps/page/11/index.html index 8e63e2d611..8d7d69d697 100644 --- a/blog/tags/azure-container-apps/page/11/index.html +++ b/blog/tags/azure-container-apps/page/11/index.html @@ -14,13 +14,13 @@ - - + +

    20 posts tagged with "azure-container-apps"

    View All Tags

    · 8 min read
    Nitya Narasimhan

    Welcome to Day 12 of #30DaysOfServerless!

    So far we've looked at Azure Container Apps - what it is, how it enables microservices communication, and how it enables auto-scaling with KEDA compliant scalers. Today we'll shift gears and talk about Dapr - the Distributed Application Runtime - and how it makes microservices development with ACA easier with core building blocks and a sidecar architecture!

    Ready? Let's go!


    What We'll Cover

    • What is Dapr and why use it?
    • Building Block APIs
    • Dapr Quickstart and Tutorials
    • Dapr-enabled ACA: A Sidecar Approach
    • Exercise: Build & Deploy a Dapr-enabled ACA.
    • Resources: For self-study!


    Hello, Dapr!

    Building distributed applications is hard. Building reliable and portable microservces means having middleware that deals with challenges like service discovery, sync and async communications, state management, secure information sharing and more. Integrating these support services into your application can be challenging from both development and maintenance perspectives, adding complexity that is independent of the core application logic you want to focus on.

    This is where Dapr (Distributed Application Runtime) shines - it's defined as::

    a portable, event-driven runtime that makes it easy for any developer to build resilient, stateless and stateful applications that run on the cloud and edge and embraces the diversity of languages and developer frameworks.

    But what does this actually mean to me as an app developer?


    Dapr + Apps: A Sidecar Approach

    The strength of Dapr lies in its ability to:

    • abstract complexities of distributed systems middleware - with Building Block APIs that implement components using best practices to tackle key challenges.
    • implement a Sidecar Pattern with interactions via APIs - allowing applications to keep their codebase clean and focus on app logic.
    • be Incrementally Adoptable - allowing developers to start by integrating one API, then evolving to use more as and when needed.
    • be Platform Agnostic - allowing applications to be developed in a preferred language or framework without impacting integration capabilities.

    The application-dapr sidecar interaction is illustrated below. The API abstraction allows applications to get the desired functionality without having to know how it was implemented, or without having to integrate Dapr-specific code into their codebase. Note how the sidecar process listens on port 3500 and the API provides clear routes for the specific building blocks supported by Dapr (e.g, /secrets, /state etc.)


    Dapr Building Blocks: API Interactions

    Dapr Building Blocks refers to HTTP and gRPC endpoints exposed by Dapr API endpoints exposed by the Dapr sidecar, providing key capabilities like state management, observability, service-to-service invocation, pub/sub messaging and more to the associated application.

    Building Blocks: Under the Hood
    The Dapr API is implemented by modular components that codify best practices for tackling the specific challenge that they represent. The API abstraction allows component implementations to evolve, or alternatives to be used , without requiring changes to the application codebase.

    The latest Dapr release has the building blocks shown in the above figure. Not all capabilities are available to Azure Container Apps by default - check the documentation for the latest updates on this. For now, Azure Container Apps + Dapr integration provides the following capabilities to the application:

    In the next section, we'll dive into Dapr-enabled Azure Container Apps. Before we do that, here are a couple of resources to help you explore the Dapr platform by itself, and get more hands-on experience with the concepts and capabilities:

    • Dapr Quickstarts - build your first Dapr app, then explore quickstarts for a core APIs including service-to-service invocation, pub/sub, state mangement, bindings and secrets management.
    • Dapr Tutorials - go beyond the basic quickstart and explore more realistic service integrations and usage scenarios. Try the distributed calculator example!

    Integrate Dapr & Azure Container Apps

    Dapr currently has a v1.9 (preview) version, but Azure Container Apps supports Dapr v1.8. In this section, we'll look at what it takes to enable, configure, and use, Dapr integration with Azure Container Apps. It involves 3 steps: enabling Dapr using settings, configuring Dapr components (API) for use, then invoking the APIs.

    Here's a simple a publisher-subscriber scenario from the documentation. We have two Container apps identified as publisher-app and subscriber-app deployed in a single environment. Each ACA has an activated daprd sidecar, allowing them to use the Pub/Sub API to communicate asynchronously with each other - without having to write the underlying pub/sub implementation themselves. Rather, we can see that the Dapr API uses a pubsub,azure.servicebus component to implement that capability.

    Pub/sub example

    Let's look at how this is setup.

    1. Enable Dapr in ACA: Settings

    We can enable Dapr integration in the Azure Container App during creation by specifying settings in one of two ways, based on your development preference:

    • Using Azure CLI: use custom commandline options for each setting
    • Using Infrastructure-as-Code (IaC): using properties for Bicep, ARM templates

    Once enabled, Dapr will run in the same environment as the Azure Container App, and listen on port 3500 for API requests. The Dapr sidecar can be shared my multiple Container Apps deployed in the same environment.

    There are four main settings we will focus on for this demo - the example below shows the ARM template properties, but you can find the equivalent CLI parameters here for comparison.

    • dapr.enabled - enable Dapr for Azure Container App
    • dapr.appPort - specify port on which app is listening
    • dapr.appProtocol - specify if using http (default) or gRPC for API
    • dapr.appId - specify unique application ID for service discovery, usage

    These are defined under the properties.configuration section for your resource. Changing Dapr settings does not update the revision but it will restart ACA revisions and replicas. Here is what the relevant section of the ARM template looks like for the publisher-app ACA in the scenario shown above.

    "dapr": {
    "enabled": true,
    "appId": "publisher-app",
    "appProcotol": "http",
    "appPort": 80
    }

    2. Configure Dapr in ACA: Components

    The next step after activating the Dapr sidecar, is to define the APIs that you want to use and potentially specify the Dapr components (specific implementations of that API) that you prefer. These components are created at environment-level and by default, Dapr-enabled containers apps in an environment will load the complete set of deployed components -- use the scopes property to ensure only components needed by a given app are loaded at runtime. Here's what the ARM template resources section looks like for the example above. This tells us that the environment has a dapr-pubsub component of type pubsub.azure.servicebus deployed - where that component is loaded by container apps with dapr ids (publisher-app, subscriber-app).

    USING MANAGED IDENTITY + DAPR

    The secrets approach used here is idea for demo purposes. However, we recommend using Managed Identity with Dapr in production. For more details on secrets, check out tomorrow's post on Secrets and Managed Identity in Azure Container Apps

    {
    "resources": [
    {
    "type": "daprComponents",
    "name": "dapr-pubsub",
    "properties": {
    "componentType": "pubsub.azure.servicebus",
    "version": "v1",
    "secrets": [
    {
    "name": "sb-root-connectionstring",
    "value": "value"
    }
    ],
    "metadata": [
    {
    "name": "connectionString",
    "secretRef": "sb-root-connectionstring"
    }
    ],
    // Application scopes
    "scopes": ["publisher-app", "subscriber-app"]

    }
    }
    ]
    }

    With this configuration, the ACA is now set to use pub/sub capabilities from the Dapr sidecar, using standard HTTP requests to the exposed API endpoint for this service.

    Exercise: Deploy Dapr-enabled ACA

    In the next couple posts in this series, we'll be discussing how you can use the Dapr secrets API and doing a walkthrough of a more complex example, to show how Dapr-enabled Azure Container Apps are created and deployed.

    However, you can get hands-on experience with these concepts by walking through one of these two tutorials, each providing an alternative approach to configure and setup the application describe in the scenario below:

    Resources

    Here are the main resources to explore for self-study:

    - - + + \ No newline at end of file diff --git a/blog/tags/azure-container-apps/page/12/index.html b/blog/tags/azure-container-apps/page/12/index.html index bf271ddaad..58b347d1cb 100644 --- a/blog/tags/azure-container-apps/page/12/index.html +++ b/blog/tags/azure-container-apps/page/12/index.html @@ -14,13 +14,13 @@ - - + +

    20 posts tagged with "azure-container-apps"

    View All Tags

    · 6 min read
    Melony Qin

    Welcome to Day 12 of #30DaysOfServerless!

    Today, we have a special set of posts from our Zero To Hero 🚀 initiative, featuring blog posts authored by our Product Engineering teams for #ServerlessSeptember. Posts were originally published on the Apps on Azure blog on Microsoft Tech Community.


    What We'll Cover

    • What are Custom Handlers, and why use them?
    • How Custom Handler Works
    • Message Processing With Azure Custom Handler
    • Azure Portal Monitoring


    If you have been working with Azure Functions for a while, you may know Azure Functions is a serverless FaaS (Function as a Service) offered provided by Microsoft Azure, which is built for your key scenarios, including building web APIs, processing file uploads, responding to database changes, processing IoT data streams, managing message queues, and more.

    Custom Handlers: What and Why

    Azure functions support multiple programming languages including C#, F#, Java, JavaScript, Python, typescript, and PowerShell. If you want to get extended language support with Azure functions for other languages such as Go, and Rust, that’s where custom handler comes in.

    An Azure function custom handler allows the use of any language that supports HTTP primitives and author Azure functions. With custom handlers, you can use triggers and input and output bindings via extension bundles, hence it supports all the triggers and bindings you're used to with Azure functions.

    How a Custom Handler Works

    Let’s take a look at custom handlers and how it works.

    • A request is sent to the function host when an event is triggered. It’s up to the function host to issue a request payload to the custom handler, which holds the trigger and inputs binding data as well as other metadata for the function.
    • The custom handler is a local HTTP web server. It executes the function code and returns a response payload to the Functions host.
    • The Functions host passes data from the response to the function's output bindings which will be passed to the downstream stream services for data processing.

    Check out this article to know more about Azure functions custom handlers.


    Message processing with Custom Handlers

    Message processing is one of the key scenarios that Azure functions are trying to address. In the message-processing scenario, events are often collected in queues. These events can trigger Azure functions to execute a piece of business logic.

    You can use the Service Bus trigger to respond to messages from an Azure Service Bus queue - it's then up to the Azure functions custom handlers to take further actions to process the messages. The process is described in the following diagram:

    Building Serverless Go Applications with Azure functions custom handlers

    In Azure function, the function.json defines the function's trigger, input and output bindings, and other configuration settings. Note that every function can have multiple bindings, but it can only have one trigger. The following is an example of setting up the Service Bus queue trigger in the function.json file :

    {
    "bindings": [
    {
    "name": "queueItem",
    "type": "serviceBusTrigger",
    "direction": "in",
    "queueName": "functionqueue",
    "connection": "ServiceBusConnection"
    }
    ]
    }

    You can add a binding definition in the function.json to write the output to a database or other locations of your desire. Supported bindings can be found here.

    As we’re programming in Go, so we need to set the value of defaultExecutablePath to handler in the customHandler.description section in the host.json file.

    Assume we’re programming in Windows OS, and we have named our go application as server.go, after we executed go build server.go command, it produces an executable called server.exe. So here we set server.exe in the host.json as the following example :

      "customHandler": {
    "description": {
    "defaultExecutablePath": "./server.exe",
    "workingDirectory": "",
    "arguments": []
    }
    }

    We’re showcasing a simple Go application here with Azure functions custom handlers where we print out the messages received from the function host. The following is the full code of server.go application :

    package main

    import (
    "encoding/json"
    "fmt"
    "log"
    "net/http"
    "os"
    )

    type InvokeRequest struct {
    Data map[string]json.RawMessage
    Metadata map[string]interface{}
    }

    func queueHandler(w http.ResponseWriter, r *http.Request) {
    var invokeRequest InvokeRequest

    d := json.NewDecoder(r.Body)
    d.Decode(&invokeRequest)

    var parsedMessage string
    json.Unmarshal(invokeRequest.Data["queueItem"], &parsedMessage)

    fmt.Println(parsedMessage)
    }

    func main() {
    customHandlerPort, exists := os.LookupEnv("FUNCTIONS_CUSTOMHANDLER_PORT")
    if !exists {
    customHandlerPort = "8080"
    }
    mux := http.NewServeMux()
    mux.HandleFunc("/MessageProcessorFunction", queueHandler)
    fmt.Println("Go server Listening on: ", customHandlerPort)
    log.Fatal(http.ListenAndServe(":"+customHandlerPort, mux))

    }

    Ensure you have Azure functions core tools installed, then we can use func start command to start our function. Then we’ll have have a C#-based Message Sender application on Github to send out 3000 messages to the Azure service bus queue. You’ll see Azure functions instantly start to process the messages and print out the message as the following:

    Monitoring Serverless Go Applications with Azure functions custom handlers


    Azure portal monitoring

    Let’s go back to Azure portal portal the events see how those messages in Azure Service Bus queue were being processed. There was 3000 messages were queued in the Service Bus queue ( the Blue line stands for incoming Messages ). The outgoing messages (the red line in smaller wave shape ) showing there are progressively being read by Azure functions as the following :

    Monitoring Serverless Go Applications with Azure functions custom handlers

    Check out this article about monitoring Azure Service bus for further information.

    Next steps

    Thanks for following along, we’re looking forward to hearing your feedback. Also, if you discover potential issues, please record them on Azure Functions host GitHub repository or tag us @AzureFunctions on Twitter.

    RESOURCES

    Start to build your serverless applications with custom handlers, check out the official documentation:

    Life is a journey of learning. Let’s stay tuned!

    - - + + \ No newline at end of file diff --git a/blog/tags/azure-container-apps/page/13/index.html b/blog/tags/azure-container-apps/page/13/index.html index a5c9751fc7..9539c19597 100644 --- a/blog/tags/azure-container-apps/page/13/index.html +++ b/blog/tags/azure-container-apps/page/13/index.html @@ -14,13 +14,13 @@ - - + +

    20 posts tagged with "azure-container-apps"

    View All Tags

    · 5 min read
    Anthony Chu

    Welcome to Day 12 of #30DaysOfServerless!

    Today, we have a special set of posts from our Zero To Hero 🚀 initiative, featuring blog posts authored by our Product Engineering teams for #ServerlessSeptember. Posts were originally published on the Apps on Azure blog on Microsoft Tech Community.


    What We'll Cover

    • Using Visual Studio
    • Using Visual Studio Code: Docker, ACA extensions
    • Using Azure CLI
    • Using CI/CD Pipelines


    Last week, @kendallroden wrote about what it means to be Cloud-Native and how Azure Container Apps provides a serverless containers platform for hosting all of your Cloud-Native applications. Today, we’ll walk through a few ways to get your apps up and running on Azure Container Apps.

    Depending on where you are in your Cloud-Native app development journey, you might choose to use different tools to deploy your apps.

    • “Right-click, publish” – Deploying an app directly from an IDE or code editor is often seen as a bad practice, but it’s one of the quickest ways to test out an app in a cloud environment.
    • Command line interface – CLIs are useful for deploying apps from a terminal. Commands can be run manually or in a script.
    • Continuous integration/deployment – To deploy production apps, the recommended approach is to automate the process in a robust CI/CD pipeline.

    Let's explore some of these options in more depth.

    Visual Studio

    Visual Studio 2022 has built-in support for deploying .NET applications to Azure Container Apps. You can use the familiar publish dialog to provision Container Apps resources and deploy to them directly. This helps you prototype an app and see it run in Azure Container Apps with the least amount of effort.

    Journey to the cloud with Azure Container Apps

    Once you’re happy with the app and it’s ready for production, Visual Studio allows you to push your code to GitHub and set up a GitHub Actions workflow to build and deploy your app every time you push changes. You can do this by checking a box.

    Journey to the cloud with Azure Container Apps

    Visual Studio Code

    There are a couple of valuable extensions that you’ll want to install if you’re working in VS Code.

    Docker extension

    The Docker extension provides commands for building a container image for your app and pushing it to a container registry. It can even do this without requiring Docker Desktop on your local machine --- the “Build image in Azure” command remotely builds and pushes a container image to Azure Container Registry.

    Journey to the cloud with Azure Container Apps

    And if your app doesn’t have a dockerfile, the extension will generate one for you.

    Journey to the cloud with Azure Container Apps

    Azure Container Apps extension

    Once you’ve built your container image and pushed it to a registry, the Azure Container Apps VS Code extension provides commands for creating a container app and deploying revisions using the image you’ve built.

    Journey to the cloud with Azure Container Apps

    Azure CLI

    The Azure CLI can be used to manage pretty much anything in Azure. For Azure Container Apps, you’ll find commands for creating, updating, and managing your Container Apps resources.

    Just like in VS Code, with a few commands in the Azure CLI, you can create your Azure resources, build and push your container image, and then deploy it to a container app.

    To make things as simple as possible, the Azure CLI also has an “az containerapp up” command. This single command takes care of everything that’s needed to turn your source code from your local machine to a cloud-hosted application in Azure Container Apps.

    az containerapp up --name myapp --source ./src

    We saw earlier that Visual Studio can generate a GitHub Actions workflow to automatically build and deploy your app on every commit. “az containerapp up” can do this too. The following adds a workflow to a repo.

    az containerapp up --name myapp --repo https://github.com/myorg/myproject

    CI/CD pipelines

    When it’s time to take your app to production, it’s strongly recommended to set up a CI/CD pipeline to automatically and repeatably build, test, and deploy it. We’ve already seen that tools such as Visual Studio and Azure CLI can automatically generate a workflow for GitHub Actions. You can set up a pipeline in Azure DevOps too. This is an example Azure DevOps pipeline.

    trigger:
    branches:
    include:
    - main

    pool:
    vmImage: ubuntu-latest

    stages:

    - stage: Build

    jobs:
    - job: build
    displayName: Build app

    steps:
    - task: Docker@2
    inputs:
    containerRegistry: 'myregistry'
    repository: 'hello-aca'
    command: 'buildAndPush'
    Dockerfile: 'hello-container-apps/Dockerfile'
    tags: '$(Build.BuildId)'

    - stage: Deploy

    jobs:
    - job: deploy
    displayName: Deploy app

    steps:
    - task: AzureCLI@2
    inputs:
    azureSubscription: 'my-subscription(5361b9d6-46ea-43c3-a898-15f14afb0db6)'
    scriptType: 'bash'
    scriptLocation: 'inlineScript'
    inlineScript: |
    # automatically install Container Apps CLI extension
    az config set extension.use_dynamic_install=yes_without_prompt

    # ensure registry is configured in container app
    az containerapp registry set \
    --name hello-aca \
    --resource-group mygroup \
    --server myregistry.azurecr.io \
    --identity system

    # update container app
    az containerapp update \
    --name hello-aca \
    --resource-group mygroup \
    --image myregistry.azurecr.io/hello-aca:$(Build.BuildId)

    Conclusion

    In this article, we looked at a few ways to deploy your Cloud-Native applications to Azure Container Apps and how to decide which one to use based on where you are in your app’s journey to the cloud.

    To learn more, visit Azure Container Apps | Microsoft Azure today!

    ASK THE EXPERT: LIVE Q&A

    The Azure Container Apps team will answer questions live on September 29.

    - - + + \ No newline at end of file diff --git a/blog/tags/azure-container-apps/page/14/index.html b/blog/tags/azure-container-apps/page/14/index.html index adbee122e8..632eb3c5de 100644 --- a/blog/tags/azure-container-apps/page/14/index.html +++ b/blog/tags/azure-container-apps/page/14/index.html @@ -14,13 +14,13 @@ - - + +

    20 posts tagged with "azure-container-apps"

    View All Tags

    · 7 min read
    Paul Yu

    Welcome to Day 11 of #30DaysOfServerless!

    Yesterday we explored Azure Container Concepts related to environments, networking and microservices communication - and illustrated these with a deployment example. Today, we turn our attention to scaling your container apps with demand.


    What We'll Cover

    • What makes ACA Serverless?
    • What is Keda?
    • Scaling Your ACA
    • ACA Scaling In Action
    • Exercise: Explore azure-opensource-labs examples
    • Resources: For self-study!


    So, what makes Azure Container Apps "serverless"?

    Today we are going to focus on what makes Azure Container Apps (ACA) a "serverless" offering. But what does the term "serverless" really mean? As much as we'd like to think there aren't any servers involved, that is certainly not the case. In general, "serverless" means that most (if not all) server maintenance has been abstracted away from you.

    With serverless, you don't spend any time managing and patching servers. This concern is offloaded to Azure and you simply focus on adding business value through application delivery. In addition to operational efficiency, cost efficiency can be achieved with serverless on-demand pricing models. Your workload horizontally scales out based on need and you only pay for what you use. To me, this is serverless, and my teammate @StevenMurawski said it best... "being able to scale to zero is what gives ACA it's serverless magic."

    Scaling your Container Apps

    If you don't know by now, ACA is built on a solid open-source foundation. Behind the scenes, it runs on a managed Kubernetes cluster and includes several open-source components out-of-the box including Dapr to help you build and run microservices, Envoy Proxy for ingress capabilities, and KEDA for event-driven autoscaling. Again, you do not need to install these components yourself. All you need to be concerned with is enabling and/or configuring your container app to leverage these components.

    Let's take a closer look at autoscaling in ACA to help you optimize your container app.

    What is KEDA?

    KEDA stands for Kubernetes Event-Driven Autoscaler. It is an open-source project initially started by Microsoft and Red Hat and has been donated to the Cloud-Native Computing Foundation (CNCF). It is being maintained by a community of 200+ contributors and adopted by many large organizations. In terms of its status as a CNCF project it is currently in the Incubating Stage which means the project has gone through significant due diligence and on its way towards the Graduation Stage.

    Prior to KEDA, horizontally scaling your Kubernetes deployment was achieved through the Horizontal Pod Autoscaler (HPA) which relies on resource metrics such as CPU and memory to determine when additional replicas should be deployed. Being limited to CPU and memory falls a bit short for certain workloads. This is especially true for apps that need to processes messages from a queue or HTTP-based apps that can handle a specific amount of incoming HTTP requests at a time. KEDA aims to fill that gap and provides a much more robust framework for scaling by working in conjunction with HPA. It offers many scalers for you to implement and even allows your deployments to scale to zero! 🥳

    KEDA architecture

    Configuring ACA scale rules

    As I mentioned above, ACA's autoscaling feature leverages KEDA and gives you the ability to configure the number of replicas to deploy based on rules (event triggers). The number of replicas can be configured as a static number or a range (minimum and maximum). So if you need your containers to run 24/7, set the min and max to be the same value. By default, when you deploy a container app, it is set to scale from 0 to 10 replicas. The default scaling rule uses HTTP scaling and defaults to a minimum of 10 concurrent requests per second. Once the threshold of 10 concurrent request per second is met, another replica will be deployed until it reaches the maximum number of replicas.

    At the time of this writing, a container app can have up to 30 replicas.

    Default autoscaler

    As a best practice, if you have a Min / max replicas range configured, you should configure a scaling rule even if it is just explicitly setting the default values.

    Adding HTTP scaling rule

    In addition to HTTP scaling, you can also configure an Azure queue rule, which allows you to use Azure Storage Queues as an event data source.

    Adding Azure Queue scaling rule

    The most flexibility comes with the Custom rule type. This opens up a LOT more options for scaling. All of KEDA's event-based scalers are supported with this option 🚀

    Adding Custom scaling rule

    Translating KEDA templates to Azure templates

    When you implement Custom rules, you need to become familiar with translating KEDA templates to Azure Resource Manager templates or ACA YAML manifests. The KEDA scaler documentation is great and it should be simple to translate KEDA template metadata to an ACA rule metadata.

    The images below shows how to translated a scaling rule which uses Azure Service Bus as an event data source. The custom rule type is set to azure-servicebus and details of the service bus is added to the Metadata section. One important thing to note here is that the connection string to the service bus was added as a secret on the container app and the trigger parameter must be set to connection.

    Azure Container App custom rule metadata

    Azure Container App custom rule metadata

    Additional examples of KEDA scaler conversion can be found in the resources section and example video below.

    See Container App scaling in action

    Now that we've built up some foundational knowledge on how ACA autoscaling is implemented and configured, let's look at a few examples.

    Autoscaling based on HTTP traffic load

    Autoscaling based on Azure Service Bus message queues

    Summary

    ACA brings you a true serverless experience and gives you the ability to configure autoscaling rules based on KEDA scaler templates. This gives you flexibility to scale based on a wide variety of data sources in an event-driven manner. With the amount built-in scalers currently available, there is probably a scaler out there for all your use cases. If not, I encourage you to get involved with the KEDA community and help make it better!

    Exercise

    By now, you've probably read and seen enough and now ready to give this autoscaling thing a try. The example I walked through in the videos above can be found at the azure-opensource-labs repo. I highly encourage you to head over to the containerapps-terraform folder and try the lab out. There you'll find instructions which will cover all the steps and tools you'll need implement autoscaling container apps within your own Azure subscription.

    If you have any questions or feedback, please let us know in the comments below or reach out on Twitter @pauldotyu

    Have fun scaling your containers!

    Resources

    - - + + \ No newline at end of file diff --git a/blog/tags/azure-container-apps/page/15/index.html b/blog/tags/azure-container-apps/page/15/index.html index 566992f0a2..8795034112 100644 --- a/blog/tags/azure-container-apps/page/15/index.html +++ b/blog/tags/azure-container-apps/page/15/index.html @@ -14,13 +14,13 @@ - - + +

    20 posts tagged with "azure-container-apps"

    View All Tags

    · 8 min read
    Paul Yu

    Welcome to Day 10 of #30DaysOfServerless!

    We continue our exploraton into Azure Container Apps, with today's focus being communication between microservices, and how to configure your Azure Container Apps environment in the context of a deployment example.


    What We'll Cover

    • ACA Environments & Virtual Networking
    • Basic Microservices Communications
    • Walkthrough: ACA Deployment Example
    • Summary and Next Steps
    • Exercise: Try this yourself!
    • Resources: For self-study!


    Introduction

    In yesterday's post, we learned what the Azure Container Apps (ACA) service is and the problems it aims to solve. It is considered to be a Container-as-a-Service platform since much of the complex implementation details of running a Kubernetes cluster is managed for you.

    Some of the use cases for ACA include event-driven processing jobs and background tasks, but this article will focus on hosting microservices, and how they can communicate with each other within the ACA service. At the end of this article, you will have a solid understanding of how networking and communication is handled and will leave you with a few tutorials to try.

    Environments and virtual networking in ACA

    Before we jump into microservices communication, we should review how networking works within ACA. With ACA being a managed service, Azure will take care of most of your underlying infrastructure concerns. As you provision an ACA resource, Azure provisions an Environment to deploy Container Apps into. This environment is your isolation boundary.

    Azure Container Apps Environment

    By default, Azure creates and manages a new Virtual Network (VNET) for you and the VNET is associated with the environment. As you deploy container apps, they are deployed into the same VNET and the environment is assigned a static public IP address which allows your apps to be accessible over the internet. This VNET is not visible or manageable.

    If you need control of the networking flows within the VNET, you can pre-provision one and tell Azure to deploy an environment within it. This "bring-your-own" VNET model allows you to deploy an environment in either External or Internal modes. Deploying an environment in External mode gives you the flexibility of managing your own VNET, while still allowing your containers to be accessible from outside the environment; a static public IP address is assigned to the environment. When deploying in Internal mode, your containers are accessible within the environment and/or VNET but not accessible from the internet.

    Bringing your own VNET will require some planning and you will need dedicate an empty subnet which will be used exclusively by the ACA environment. The size of your subnet will be dependant on how many containers you plan on deploying and your scaling requirements and one requirement to know is that the subnet address range must have have a /23 CIDR prefix at minimum. You will also need to think about your deployment strategy since ACA has the concept of Revisions which will also consume IPs from your subnet.

    Some additional restrictions to consider when planning your subnet address space is listed in the Resources section below and can be addressed in future posts, so be sure to follow us on dev.to and bookmark the ServerlessSeptember site.

    Basic microservices communication in ACA

    When it comes to communications between containers, ACA addresses this concern with its Ingress capabilities. With HTTP Ingress enabled on your container app, you can expose your app on a HTTPS endpoint.

    If your environment is deployed using default networking and your containers needs to be accessible from outside the environment, you will need to set the Ingress traffic option to Accepting traffic from anywhere. This will generate a Full-Qualified Domain Name (FQDN) which you can use to access your app right away. The ingress feature also generates and assigns a Secure Socket Layer (SSL) certificate for the FQDN.

    External ingress on Container App

    If your environment is deployed using default networking and your containers only need to communicate with other containers in the environment, you'll need to set the Ingress traffic option to Limited to Container Apps Environment. You get a FQDN here as well, but in the section below we'll see how that changes.

    Internal ingress on Container App

    As mentioned in the networking section above, if you deploy your ACA environment into a VNET in internal mode, your options will be Limited to Container Apps Environment or Limited to VNet.

    Ingress on internal virtual network

    Note how the Accepting traffic from anywhere option is greyed out. If your VNET is deployed in external mode, then the option will be available.

    Let's walk though an example ACA deployment

    The diagram below illustrates a simple microservices application that I deployed to ACA. The three container apps all have ingress enabled. The greeting-service app calls two backend services; a hello-service that returns the text Hello (in random casing) and a world-service that returns the text World (in a few random languages). The greeting-service concatenates the two strings together and returns Hello World to the browser. The greeting-service is the only service accessible via external ingress while two backend services are only accessible via internal ingress.

    Greeting Service overview

    With ingress enabled, let's take a quick look at the FQDN structures. Here is the FQDN of the external greeting-service.

    https://greeting-service.victoriouswave-3749d046.eastus.azurecontainerapps.io

    We can break it down into these components:

    https://[YOUR-CONTAINER-APP-NAME].[RANDOM-NAME]-[RANDOM-CHARACTERS].[AZURE-REGION].containerapps.io

    And here is the FQDN of the internal hello-service.

    https://hello-service.internal.victoriouswave-3749d046.eastus.azurecontainerapps.io

    Can you spot the difference between FQDNs?

    That was too easy 😉... the word internal is added as a subdomain in the FQDN between your container app name and the random name for all internal ingress endpoints.

    https://[YOUR-CONTAINER-APP-NAME].internal.[RANDOM-NAME]-[RANDOM-CHARACTERS].[AZURE-REGION].containerapps.io

    Now that we know the internal service FQDNs, we use them in the greeting-service app to achieve basic service-to-service communications.

    So we can inject FQDNs of downstream APIs to upstream apps using environment variables, but the downside to this approach is that need to deploy downstream containers ahead of time and this dependency will need to be planned for during your deployment process. There are ways around this and one option is to leverage the auto-injected environment variables within your app code.

    If I use the Console blade for the hello-service container app and run the env command, you will see environment variables named CONTAINER_APP_NAME and CONTAINER_APP_ENV_DNS_SUFFIX. You can use these values to determine FQDNs within your upstream app.

    hello-service environment variables

    Back in the greeting-service container I can invoke the hello-service container's sayhello method. I know the container app name is hello-service and this service is exposed over an internal ingress, therefore, if I add the internal subdomain to the CONTAINER_APP_ENV_DNS_SUFFIX I can invoke a HTTP request to the hello-service from my greeting-service container.

    Invoke the sayHello method from the greeting-service container

    As you can see, the ingress feature enables communications to other container apps over HTTP/S and ACA will inject environment variables into our container to help determine what the ingress FQDNs would be. All we need now is a little bit of code modification in the greeting-service app and build the FQDNs of our backend APIs by retrieving these environment variables.

    Greeting service code

    ... and now we have a working microservices app on ACA! 🎉

    Hello World

    Summary and next steps

    We've covered Container Apps networking and the basics of how containers communicate with one another. However, there is a better way to address service-to-service invocation using Dapr, which is an open-source framework for building microservices. It is natively integrated into the ACA service and in a future post, you'll learn how to enable it in your Container App to address microservices concerns and more. So stay tuned!

    Exercises

    As a takeaway for today's post, I encourage you to complete this tutorial and if you'd like to deploy the sample app that was presented in this article, my teammate @StevenMurawski is hosting a docker-compose-examples repo which includes samples for deploying to ACA using Docker Compose files. To learn more about the az containerapp compose command, a link to his blog articles are listed in the Resources section below.

    If you have any questions or feedback, please let us know in the comments below or reach out on Twitter @pauldotyu

    Have fun packing and shipping containers! See you in the next post!

    Resources

    The sample app presented here was inspired by services demonstrated in the book Introducing Distributed Application Runtime (Dapr): Simplifying Microservices Applications Development Through Proven and Reusable Patterns and Practices. Go check it out to leran more about Dapr!

    - - + + \ No newline at end of file diff --git a/blog/tags/azure-container-apps/page/16/index.html b/blog/tags/azure-container-apps/page/16/index.html index df0519916b..603bacd525 100644 --- a/blog/tags/azure-container-apps/page/16/index.html +++ b/blog/tags/azure-container-apps/page/16/index.html @@ -14,13 +14,13 @@ - - + +

    20 posts tagged with "azure-container-apps"

    View All Tags

    · 12 min read
    Nitya Narasimhan

    Welcome to Day 9 of #30DaysOfServerless!


    What We'll Cover

    • The Week Ahead
    • Hello, Container Apps!
    • Quickstart: Build Your First ACA!
    • Under The Hood: Core ACA Concepts
    • Exercise: Try this yourself!
    • Resources: For self-study!


    The Week Ahead

    Welcome to Week 2 of #ServerlessSeptember, where we put the focus on Microservices and building Cloud-Native applications that are optimized for serverless solutions on Azure. One week is not enough to do this complex topic justice so consider this a 7-part jumpstart to the longer journey.

    1. Hello, Container Apps (ACA) - Learn about Azure Container Apps, a key service that helps you run microservices and containerized apps on a serverless platform. Know the core concepts. (Tutorial 1: First ACA)
    2. Communication with Microservices - Dive deeper into two key concepts: environments and virtual networking. Learn how microservices communicate in ACA, and walkthrough an example. (Tutorial 2: ACA with 3 Microservices)
    3. Scaling Your Container Apps - Learn about KEDA. Understand how to configure your ACA for auto-scaling with KEDA-supported triggers. Put this into action by walking through a tutorial. (Tutorial 3: Configure Autoscaling)
    4. Hello, Distributed Application Runtime (Dapr) - Learn about Dapr and how its Building Block APIs simplify microservices development with ACA. Know how the sidecar pattern enables incremental adoption of Dapr APIs without requiring any Dapr code integration in app. (Tutorial 4: Setup & Explore Dapr)
    5. Building ACA with Dapr - See how Dapr works with ACA by building a Dapr-enabled Azure Container App. Walk through a .NET tutorial using Pub/Sub and State Management APIs in an enterprise scenario. (Tutorial 5: Build ACA with Dapr)
    6. Managing Secrets With Dapr - We'll look at the Secrets API (a key Building Block of Dapr) and learn how it simplifies management of sensitive information in ACA.
    7. Microservices + Serverless On Azure - We recap Week 2 (Microservices) and set the stage for Week 3 ( Integrations) of Serverless September. Plus, self-study resources including ACA development tutorials in different languages.

    Ready? Let's go!


    Azure Container Apps!

    When building your application, your first decision is about where you host your application. The Azure Architecture Center has a handy chart to help you decide between choices like Azure Functions, Azure App Service, Azure Container Instances, Azure Container Apps and more. But if you are new to this space, you'll need a good understanding of the terms and concepts behind the services Today, we'll focus on Azure Container Apps (ACA) - so let's start with the fundamentals.

    Containerized App Defined

    A containerized app is one where the application components, dependencies, and configuration, are packaged into a single file (container image), which can be instantiated in an isolated runtime environment (container) that is portable across hosts (OS). This makes containers lightweight and scalable - and ensures that applications behave consistently on different host platforms.

    Container images can be shared via container registries (public or private) helping developers discover and deploy related apps with less effort. Scaling a containerized app can be as simple as activating more instances of its container image. However, this requires container orchestrators to automate the management of container apps for efficiency. Orchestrators use technologies like Kubernetes to support capabilities like workload scheduling, self-healing and auto-scaling on demand.

    Cloud-Native & Microservices

    Containers are seen as one of the 5 pillars of Cloud-Native app development, an approach where applications are designed explicitly to take advantage of the unique benefits of modern dynamic environments (involving public, private and hybrid clouds). Containers are particularly suited to serverless solutions based on microservices.

    • With serverless - developers use managed services instead of managing their own infrastructure. Services are typically event-driven and can be configured for autoscaling with rules tied to event triggers. Serverless is cost-effective, with developers paying only for the compute cycles and resources they use.
    • With microservices - developers compose their applications from independent components. Each component can be deployed in its own container, and scaled at that granularity. This simplifies component reuse (across apps) and maintainability (over time) - with developers evolving functionality at microservice (vs. app) levels.

    Hello, Azure Container Apps!

    Azure Container Apps is the managed service that helps you run containerized apps and microservices as a serverless compute solution, on Azure. You can:

    • deploy serverless API endpoints - autoscaled by HTTP request traffic
    • host background processing apps - autoscaled by CPU or memory load
    • handle event-driven processing - autoscaled by #messages in queue
    • run microservices - autoscaled by any KEDA-supported scaler.

    Want a quick intro to the topic? Start by watching the short video below - then read these two posts from our ZeroToHero series:


    Deploy Your First ACA

    Dev Options

    We typically have three options for development:

    • Use the Azure Portal - provision and deploy from a browser.
    • Use Visual Studio Code (with relevant extensions) - if you prefer an IDE
    • Using Azure CLI - if you prefer to build and deploy from command line.

    The documentation site has quickstarts for three contexts:

    For this quickstart, we'll go with the first option (sample image) so we can move quickly to core concepts. We'll leave the others as an exercise for you to explore.

    1. Setup Resources

    PRE-REQUISITES

    You need:

    • An Azure account with an active subscription
    • An installed Azure CLI

    Start by logging into Azure from the CLI. The command should launch a browser to complete the auth flow (or give you an option to take an alternative path).

    $ az login

    Successful authentication will result in extensive command-line output detailing the status of your subscription.

    Next, install the Azure Container Apps extension for the CLI

    $ az extension add --name containerapp --upgrade
    ...
    The installed extension 'containerapp' is in preview.

    Once successfully installed, register the Microsoft.App namespace.

    $ az provider register --namespace Microsoft.App

    Then set local environment variables in that terminal - and verify they are set correctly:

    $ RESOURCE_GROUP="my-container-apps"
    $ LOCATION="canadacentral"
    $ CONTAINERAPPS_ENVIRONMENT="my-environment"

    $ echo $LOCATION $RESOURCE_GROUP $CONTAINERAPPS_ENVIRONMENT
    canadacentral my-container-apps my-environment

    Now you can use Azure CLI to provision a resource group for this tutorial. Creating a resource group also makes it easier for us to delete/reclaim all resources used at the end of this tutorial.

    az group create \
    --name $RESOURCE_GROUP \
    --location $LOCATION
    Congratulations

    You completed the Setup step!

    On completion, the console should print out the details of the newly created resource group. You should also be able to visit the Azure Portal and find the newly-active my-container-apps resource group under your active subscription.

    2. Create Environment

    An environment is like the picket fence around your property. It creates a secure boundary that contains a group of container apps - such that all apps deployed to it share the same virtual network and logging resources.

    $ az containerapp env create \
    --name $CONTAINERAPPS_ENVIRONMENT \
    --resource-group $RESOURCE_GROUP \
    --location $LOCATION

    No Log Analytics workspace provided.
    Generating a Log Analytics workspace with name ...

    This can take a few minutes. When done, you will see the terminal display more details. You can also check the resource group in the portal and see that a Container Apps Environment and a Log Analytics Workspace are created for you as part of this step.

    You've got the fence set up. Now it's time to build your home - er, container app!

    3. Create Container App

    Here's the command we'll use to create our first Azure Container App. Note that the --image argument provides the link to a pre-existing containerapps-helloworld image.

    az containerapp create \
    --name my-container-app \
    --resource-group $RESOURCE_GROUP \
    --environment $CONTAINERAPPS_ENVIRONMENT \
    --image mcr.microsoft.com/azuredocs/containerapps-helloworld:latest \
    --target-port 80 \
    --ingress 'external' \
    --query properties.configuration.ingress.fqdn
    ...
    ...

    Container app created. Access your app at <URL>

    The --ingress property shows that the app is open to external requests; in other words, it is publicly visible at the <URL> that is printed out on the terminal on successsful completion of this step.

    4. Verify Deployment

    Let's see if this works. You can verify that your container app by visitng the URL returned above in your browser. You should see something like this!

    Container App Hello World

    You can also visit the Azure Portal and look under the created Resource Group. You should see a new Container App type of resource was created after this step.

    Congratulations

    You just created and deployed your first "Hello World" Azure Container App! This validates your local development environment setup and existence of a valid Azure subscription.

    5. Clean Up Your Resources

    It's good practice to clean up resources once you are done with a tutorial.

    THIS ACTION IS IRREVERSIBLE

    This command deletes the resource group we created above - and all resources in it. So make sure you specified the right name, then confirm deletion.

    $ az group delete --name $RESOURCE_GROUP
    Are you sure you want to perform this operation? (y/n):

    Note that you can also delete the resource group from the Azure Portal interface if that feels more comfortable. For now, we'll just use the Portal to verify that deletion occurred. If you had previously opened the Resource Group page for the created resource, just refresh it. You should see something like this:

    Resource Not Found


    Core Concepts

    COMING SOON

    An illustrated guide summarizing these concepts in a single sketchnote.

    We covered a lot today - we'll stop with a quick overview of core concepts behind Azure Container Apps, each linked to documentation for self-study. We'll dive into more details on some of these concepts in upcoming articles:

    • Environments - are the secure boundary around a group of container apps that are deployed in the same virtual network. They write logs to a shared Log Analytics workspace and can communicate seamlessly using Dapr, if used.
    • Containers refer to the container image deployed in the Azure Container App. They can use any runtime, programming language, or development stack - and be discovered using any public or private container registry. A container app can support multiple containers.
    • Revisions are immutable snapshots of an Azure Container App. The first revision is created when the ACA is first deployed, with new revisions created when redeployment occurs with revision-scope changes. Multiple revisions can run concurrently in an environment.
    • Application Lifecycle Management revolves around these revisions, with a container app having three phases: deployment, update and deactivation.
    • Microservices are independent units of functionality in Cloud-Native architectures. A single container app typically represents a single microservice, and can be composed from one or more containers. Microservices can now be scaled and upgraded indepedently, giving your application more flexbility and control.
    • Networking architecture consist of a virtual network (VNET) associated with the environment. Unless you provide a custom VNET at environment creation time, a default VNET is automatically created. The VNET configuration determines access (ingress, internal vs. external) and can influence auto-scaling choices (e.g., use HTTP Edge Proxy and scale based on number of HTTP requests).
    • Observability is about monitoring the health of your application and diagnosing it to improve reliability or performance. Azure Container Apps has a number of features - from Log streaming and Container console to integration with Azure Monitor - to provide a holistic view of application status over time.
    • Easy Auth is possible with built-in support for authentication and authorization including support for popular identity providers like Facebook, Google, Twitter and GitHub - alongside the Microsoft Identity Platform.

    Keep these terms in mind as we walk through more tutorials this week, to see how they find application in real examples. Finally, a note on Dapr, the Distributed Application Runtime that abstracts away many of the challenges posed by distributed systems - and lets you focus on your application logic.

    DAPR INTEGRATION MADE EASY

    Dapr uses a sidecar architecture, allowing Azure Container Apps to communicate with Dapr Building Block APIs over either gRPC or HTTP. Your ACA can be built to run with or without Dapr - giving you the flexibility to incrementally adopt specific APIs and unlock related capabilities as the need arises.

    In later articles this week, we'll do a deeper dive into Dapr and build our first Dapr-enable Azure Container App to get a better understanding of this integration.

    Exercise

    Congratulations! You made it! By now you should have a good idea of what Cloud-Native development means, why Microservices and Containers are important to that vision - and how Azure Container Apps helps simplify the building and deployment of microservices based applications using serverless architectures on Azure.

    Now it's your turn to reinforce learning by doing.

    Resources

    Three key resources to bookmark and explore:

    - - + + \ No newline at end of file diff --git a/blog/tags/azure-container-apps/page/17/index.html b/blog/tags/azure-container-apps/page/17/index.html index 6fe8e68fdf..db451837ea 100644 --- a/blog/tags/azure-container-apps/page/17/index.html +++ b/blog/tags/azure-container-apps/page/17/index.html @@ -14,13 +14,13 @@ - - + +

    20 posts tagged with "azure-container-apps"

    View All Tags

    · 5 min read
    Nitya Narasimhan
    Devanshi Joshi

    SEP 08: CHANGE IN PUBLISHING SCHEDULE

    Starting from Week 2 (Sep 8), we'll be publishing blog posts in batches rather than on a daily basis, so you can read a series of related posts together. Don't want to miss updates? Just subscribe to the feed


    Welcome to Day 8 of #30DaysOfServerless!

    This marks the end of our Week 1 Roadmap focused on Azure Functions!! Today, we'll do a quick recap of all #ServerlessSeptember activities in Week 1, set the stage for Week 2 - and leave you with some excellent tutorials you should explore to build more advanced scenarios with Azure Functions.

    Ready? Let's go.


    What We'll Cover

    • Azure Functions: Week 1 Recap
    • Advanced Functions: Explore Samples
    • End-to-End: Serverless Hacks & Cloud Skills
    • What's Next: Hello, Containers & Microservices
    • Challenge: Complete the Learning Path


    Week 1 Recap: #30Days & Functions

    Congratulations!! We made it to the end of Week 1 of #ServerlessSeptember. Let's recap what we learned so far:

    • In Core Concepts we looked at where Azure Functions fits into the serverless options available on Azure. And we learned about key concepts like Triggers, Bindings, Custom Handlers and Durable Functions.
    • In Build Your First Function we looked at the tooling options for creating Functions apps, testing them locally, and deploying them to Azure - as we built and deployed our first Functions app.
    • In the next 4 posts, we explored new Triggers, Integrations, and Scenarios - as we looked at building Functions Apps in Java, JavaScript, .NET and Python.
    • And in the Zero-To-Hero series, we learned about Durable Entities - and how we can use them to create stateful serverless solutions using a Chirper Sample as an example scenario.

    The illustrated roadmap below summarizes what we covered each day this week, as we bring our Functions-as-a-Service exploration to a close.


    Advanced Functions: Code Samples

    So, now that we've got our first Functions app under our belt, and validated our local development setup for tooling, where can we go next? A good next step is to explore different triggers and bindings, that drive richer end-to-end scenarios. For example:

    • Integrate Functions with Azure Logic Apps - we'll discuss Azure Logic Apps in Week 3. For now, think of it as a workflow automation tool that lets you integrate seamlessly with other supported Azure services to drive an end-to-end scenario. In this tutorial, we set up a workflow connecting Twitter (get tweet) to Azure Cognitive Services (analyze sentiment) - and use that to trigger an Azure Functions app to send email about the result.
    • Integrate Functions with Event Grid - we'll discuss Azure Event Grid in Week 3. For now, think of it as an eventing service connecting event sources (publishers) to event handlers (subscribers) at cloud scale. In this tutorial, we handle a common use case - a workflow where loading an image to Blob Storage triggers an Azure Functions app that implements a resize function, helping automatically generate thumbnails for the uploaded image.
    • Integrate Functions with CosmosDB and SignalR to bring real-time push-based notifications to your web app. It achieves this by using a Functions app that is triggered by changes in a CosmosDB backend, causing it to broadcast that update (push notification to connected web clients over SignalR, in real time.

    Want more ideas? Check out the Azure Samples for Functions for implementations, and browse the Azure Architecture Center for reference architectures from real-world scenarios that involve Azure Functions usage.


    E2E Scenarios: Hacks & Cloud Skills

    Want to systematically work your way through a single End-to-End scenario involving Azure Functions alongside other serverless support technologies? Check out the Serverless Hacks activity happening during #ServerlessSeptember, and learn to build this "Serverless Tollbooth Application" in a series of 10 challenges. Check out the video series for a reference solution in .NET and sign up for weekly office hours to join peers and discuss your solutions or challenges.

    Or perhaps you prefer to learn core concepts with code in a structured learning path? We have that covered. Check out the 12-module "Create Serverless Applications" course from Microsoft Learn which walks your through concepts, one at a time, with code. Even better - sign up for the free Cloud Skills Challenge and complete the same path (in under 30 days) but this time, with the added fun of competing against your peers for a spot on a leaderboard, and swag.


    What's Next? Hello, Cloud-Native!

    So where to next? In Week 2 we turn our attention from Functions-as-a-Service to building more complex backends using Containers and Microservices. We'll focus on two core technologies - Azure Container Apps and Dapr (Distributed Application Runtime) - both key components of a broader vision around Building Cloud-Native Applications in Azure.

    What is Cloud-Native you ask?

    Fortunately for you, we have an excellent introduction in our Zero-to-Hero article on Go Cloud-Native with Azure Container Apps - that explains the 5 pillars of Cloud-Native and highlights the value of Azure Container Apps (scenarios) and Dapr (sidecar architecture) for simplified microservices-based solution with auto-scale capability. Prefer a visual summary? Here's an illustrate guide to that article for convenience.

    Go Cloud-Native Download a higher resolution version of the image


    Take The Challenge

    We typically end each post with an exercise or activity to reinforce what you learned. For Week 1, we encourage you to take the Cloud Skills Challenge and work your way through at least a subset of the modules, for hands-on experience with the different Azure Functions concepts, integrations, and usage.

    See you in Week 2!

    - - + + \ No newline at end of file diff --git a/blog/tags/azure-container-apps/page/18/index.html b/blog/tags/azure-container-apps/page/18/index.html index a6938fc563..7613b9897f 100644 --- a/blog/tags/azure-container-apps/page/18/index.html +++ b/blog/tags/azure-container-apps/page/18/index.html @@ -14,15 +14,15 @@ - - + +

    20 posts tagged with "azure-container-apps"

    View All Tags

    · 8 min read
    David Justo

    Welcome to Day 6 of #30DaysOfServerless!

    Today, we have a special set of posts from our Zero To Hero 🚀 initiative, featuring blog posts authored by our Product Engineering teams for #ServerlessSeptember. Posts were originally published on the Apps on Azure blog on Microsoft Tech Community.


    What We'll Cover

    • What are Durable Entities
    • Some Background
    • A Programming Model
    • Entities for a Micro-Blogging Platform


    Durable Entities are a special type of Azure Function that allow you to implement stateful objects in a serverless environment. They make it easy to introduce stateful components to your app without needing to manually persist data to external storage, so you can focus on your business logic. We’ll demonstrate their power with a real-life example in the last section.

    Entities 101: Some Background

    Programming Durable Entities feels a lot like object-oriented programming, except that these “objects” exist in a distributed system. Like objects, each Entity instance has a unique identifier, i.e. an entity ID that can be used to read and manipulate their internal state. Entities define a list of operations that constrain how their internal state is managed, like an object interface.

    Some experienced readers may realize that Entities sound a lot like an implementation of the Actor Pattern. For a discussion of the relationship between Entities and Actors, please refer to this documentation.

    Entities are a part of the Durable Functions Extension, an extension of Azure Functions that empowers programmers with stateful abstractions for serverless, such as Orchestrations (i.e. workflows).

    Durable Functions is available in most Azure Functions runtime environments: .NET, Node.js, Python, PowerShell, and Java (preview). For this article, we’ll focus on the C# experience, but note that Entities are also available in Node.js and Python; their availability in other languages is underway.

    Entities 102: The programming model

    Imagine you want to implement a simple Entity that just counts things. Its interface allows you to get the current count, add to the current count, and to reset the count to zero.

    If you implement this in an object-oriented way, you’d probably define a class (say “Counter”) with a method to get the current count (say “Counter.Get”), another to add to the count (say “Counter.Add”), and another to reset the count (say “Counter.Reset”). Well, the implementation of an Entity in C# is not that different from this sketch:

    [JsonObject(MemberSerialization.OptIn)] 
    public class Counter
    {
    [JsonProperty("value")]
    public int Value { get; set; }

    public void Add(int amount)
    {
    this.Value += amount;
    }

    public Task Reset()
    {
    this.Value = 0;
    return Task.CompletedTask;
    }

    public Task<int> Get()
    {
    return Task.FromResult(this.Value);
    }
    [FunctionName(nameof(Counter))]
    public static Task Run([EntityTrigger] IDurableEntityContext ctx)
    => ctx.DispatchAsync<Counter>();

    }

    We’ve defined a class named Counter, with an internal count stored in the variable “Value” which is manipulated through the “Add” and “Reset” methods, and which can be read via “Get”.

    The “Run” method is simply boilerplate required for the Azure Functions framework to interact with the object we’ve defined – it’s the method that the framework calls internally when it needs to load the Entity object. When DispatchAsync is called, the Entity and its corresponded state (the last count in “Value”) is loaded from storage. Again, this is mostly just boilerplate: your Entity’s business logic lies in the rest of the class.

    Finally, the Json annotation on top of the class and the Value field tells the Durable Functions framework that the “Value” field is to be durably persisted as part of the durable state on each Entity invocation. If you were to annotate other class variables with JsonProperty, they would also become part of the managed state.

    Entities for a micro-blogging platform

    We’ll try to implement a simple micro-blogging platform, a la Twitter. Let’s call it “Chirper”. In Chirper, users write chirps (i.e tweets), they can follow, and unfollow other users, and they can read the chirps of users they follow.

    Defining Entity

    Just like in OOP, it’s useful to begin by identifying what are the stateful agents of this scenario. In this case, users have state (who they follow and their chirps), and chirps have state in the form of their content. So, we could model these stateful agents as Entities!

    Below is a potential way to implement a User for Chirper as an Entity:

      [JsonObject(MemberSerialization = MemberSerialization.OptIn)] 
    public class User: IUser
    {
    [JsonProperty]
    public List<string> FollowedUsers { get; set; } = new List<string>();

    public void Add(string user)
    {
    FollowedUsers.Add(user);
    }

    public void Remove(string user)
    {
    FollowedUsers.Remove(user);
    }

    public Task<List<string>> Get()
    {
    return Task.FromResult(FollowedUsers);
    }
    // note: removed boilerplate “Run” method, for conciseness.
    }

    In this case, our Entity’s internal state is stored in “FollowedUsers” which is an array of accounts followed by this user. The operations exposed by this entity allow clients to read and modify this data: it can be read by “Get”, a new follower can be added via “Add”, and a user can be unfollowed via “Remove”.

    With that, we’ve modeled a Chirper’s user as an Entity! Recall that Entity instances each has a unique ID, so we can consider that unique ID to correspond to a specific user account.

    What about chirps? Should we represent them as Entities as well? That would certainly be valid. However, we would then need to create a mapping between an entity ID and every chirp entity ID that this user wrote.

    For demonstration purposes, a simpler approach would be to create an Entity that stores the list of all chirps authored by a given user; call it UserChirps. Then, we could fix each User Entity to share the same entity ID as its corresponding UserChirps Entity, making client operations easier.

    Below is a simple implementation of UserChirps:

      [JsonObject(MemberSerialization = MemberSerialization.OptIn)] 
    public class UserChirps : IUserChirps
    {
    [JsonProperty]
    public List<Chirp> Chirps { get; set; } = new List<Chirp>();

    public void Add(Chirp chirp)
    {
    Chirps.Add(chirp);
    }

    public void Remove(DateTime timestamp)
    {
    Chirps.RemoveAll(chirp => chirp.Timestamp == timestamp);
    }

    public Task<List<Chirp>> Get()
    {
    return Task.FromResult(Chirps);
    }

    // Omitted boilerplate “Run” function
    }

    Here, our state is stored in Chirps, a list of user posts. Our operations are the same as before: Get, Read, and Add. It’s the same pattern as before, but we’re representing different data.

    To put it all together, let’s set up Entity clients to generate and manipulate these Entities according to some REST API.

    Interacting with Entity

    Before going there, let’s talk briefly about how you can interact with an Entity. Entity interactions take one of two forms -- calls and signals:

    Calling an entity is a two-way communication. You send an operation message to the entity and then wait for the response message before you continue. The response can be a result value or an error. Signaling an entity is a one-way (fire-and-forget) communication. You send an operation message but don’t wait for a response. You have the reassurance that the message will be delivered eventually, but you don’t know when and don’t know what the response is. For example, when you read the state of an Entity, you are performing a “call” interaction. When you record that a user has followed another, you may choose to simply signal it.

    Now say user with a given userId (say “durableFan99” ) wants to post a chirp. For this, you can write an HTTP endpoint to signal the UserChips entity to record that chirp. We can leverage the HTTP Trigger functionality from Azure Functions and pair it with an entity client binding that signals the Add operation of our Chirp Entity:

    [FunctionName("UserChirpsPost")] 
    public static async Task<HttpResponseMessage> UserChirpsPost(
    [HttpTrigger(AuthorizationLevel.Function, "post", Route = "user/{userId}/chirps")]
    HttpRequestMessage req,
    DurableClient] IDurableClient client,
    ILogger log,
    string userId)
    {
    Authenticate(req, userId);
    var chirp = new Chirp()
    {
    UserId = userId,
    Timestamp = DateTime.UtcNow,
    Content = await req.Content.ReadAsStringAsync(),
    };
    await client.SignalEntityAsync<IUserChirps>(userId, x => x.Add(chirp));
    return req.CreateResponse(HttpStatusCode.Accepted, chirp);
    }

    Following the same pattern as above, to get all the chirps from a user, you could read the status of your Entity via ReadEntityStateAsync, which follows the call-interaction pattern as your client expects a response:

    [FunctionName("UserChirpsGet")] 
    public static async Task<HttpResponseMessage> UserChirpsGet(
    [HttpTrigger(AuthorizationLevel.Function, "get", Route = "user/{userId}/chirps")] HttpRequestMessage req,
    [DurableClient] IDurableClient client,
    ILogger log,
    string userId)
    {

    Authenticate(req, userId);
    var target = new EntityId(nameof(UserChirps), userId);
    var chirps = await client.ReadEntityStateAsync<UserChirps>(target);
    return chirps.EntityExists
    ? req.CreateResponse(HttpStatusCode.OK, chirps.EntityState.Chirps)
    : req.CreateResponse(HttpStatusCode.NotFound);
    }

    And there you have it! To play with a complete implementation of Chirper, you can try out our sample in the Durable Functions extension repo.

    Thank you!

    info

    Thanks for following along, and we hope you find Entities as useful as we do! If you have questions or feedback, please file issues in the repo above or tag us @AzureFunctions on Twitter

    - - + + \ No newline at end of file diff --git a/blog/tags/azure-container-apps/page/19/index.html b/blog/tags/azure-container-apps/page/19/index.html index 92f2f9feec..85f1f42b87 100644 --- a/blog/tags/azure-container-apps/page/19/index.html +++ b/blog/tags/azure-container-apps/page/19/index.html @@ -14,13 +14,13 @@ - - + +

    20 posts tagged with "azure-container-apps"

    View All Tags

    · 8 min read
    Kendall Roden

    Welcome to Day 6 of #30DaysOfServerless!

    Today, we have a special set of posts from our Zero To Hero 🚀 initiative, featuring blog posts authored by our Product Engineering teams for #ServerlessSeptember. Posts were originally published on the Apps on Azure blog on Microsoft Tech Community.


    What We'll Cover

    • Defining Cloud-Native
    • Introduction to Azure Container Apps
    • Dapr In Azure Container Apps
    • Conclusion


    Defining Cloud-Native

    While I’m positive I’m not the first person to ask this, I think it’s an appropriate way for us to kick off this article: “How many developers does it take to define Cloud-Native?” I hope you aren’t waiting for a punch line because I seriously want to know your thoughts (drop your perspectives in the comments..) but if you ask me, the limit does not exist!

    A quick online search of the topic returns a laundry list of articles, e-books, twitter threads, etc. all trying to nail down the one true definition. While diving into the rabbit hole of Cloud-Native, you will inevitably find yourself on the Cloud-Native Computing Foundation (CNCF) site. The CNCF is part of the Linux Foundation and aims to make "Cloud-Native computing ubiquitous" through deep open source project and community involvement. The CNCF has also published arguably the most popularized definition of Cloud-Native which begins with the following statement:

    “Cloud-Native technologies empower organizations to build and run scalable applications in modern, dynamic environments such as public, private, and hybrid clouds."

    Over the past four years, my day-to-day work has been driven primarily by the surging demand for application containerization and the drastic adoption of Kubernetes as the de-facto container orchestrator. Customers are eager to learn and leverage patterns, practices and technologies that enable building "loosely coupled systems that are resilient, manageable, and observable". Enterprise developers at these organizations are being tasked with rapidly deploying event-driven, horizontally-scalable, polyglot services via repeatable, code-to-cloud pipelines.

    While building Cloud-Native solutions can enable rapid innovation, the transition to adopting a Cloud-Native architectural approach comes with a steep learning curve and a new set of considerations. In a document published by Microsoft called What is Cloud-Native?, there are a few key areas highlighted to aid customers in the adoption of best practices for building modern, portable applications which I will summarize below:

    Cloud infrastructure

    • Cloud-Native applications leverage cloud infrastructure and make use of Platform-as-a-service offerings
    • Cloud-Native applications depend on highly-elastic infrastructure with automatic scaling, self-healing, and monitoring capabilities

    Modern application design

    • Cloud-Native applications should be constructed using principles outlined in the 12 factor methodology

    Microservices

    • Cloud-Native applications are typically composed of microservices where each core function, or service, is built and deployed independently

    Containers

    • Cloud-Native applications are typically deployed using containers as a packaging mechanism where an application's code and dependencies are bundled together for consistency of deployment
    • Cloud-Native applications leverage container orchestration technologies- primarily Kubernetes- for achieving capabilities such as workload scheduling, self-healing, auto-scale, etc.

    Backing services

    • Cloud-Native applications are ideally stateless workloads which retrieve and store data in data stores external to the application hosting infrastructure. Cloud providers like Azure provide an array of backing data services which can be securely accessed from application code and provide capabilities for ensuring application data is highly-available

    Automation

    • Cloud-Native solutions should use deployment automation for backing cloud infrastructure via versioned, parameterized Infrastructure as Code (IaC) templates which provide a consistent, repeatable process for provisioning cloud resources.
    • Cloud-Native solutions should make use of modern CI/CD practices and pipelines to ensure successful, reliable infrastructure and application deployment.

    Azure Container Apps

    In many of the conversations I've had with customers that involve talk of Kubernetes and containers, the topics of cost-optimization, security, networking, and reducing infrastructure and operations inevitably arise. I personally have yet to meet with any customers eager to have their developers get more involved with infrastructure concerns.

    One of my former colleagues, Jeff Hollan, made a statement while appearing on a 2019 episode of The Cloud-Native Show where he shared his perspective on Cloud-Native:

    "When I think about Cloud-Native... it's writing applications in a way where you are specifically thinking about the benefits the cloud can provide... to me, serverless is the perfect realization of that because the only reason you can write serverless applications is because the cloud exists."

    I must say that I agree with Jeff's perspective. In addition to optimizing development practices for the Cloud-Native world, reducing infrastructure exposure and operations is equally as important to many organizations and can be achieved as a result of cloud platform innovation.

    In May of 2022, Microsoft announced the general availability of Azure Container Apps. Azure Container Apps provides customers with the ability to run microservices and containerized applications on a serverless, consumption-based platform.

    For those interested in taking advantage of the open source ecosystem while reaping the benefits of a managed platform experience, Container Apps run on Kubernetes and provides a set of managed open source projects embedded directly into the platform including the Kubernetes Event Driven Autoscaler (KEDA), the Distributed Application Runtime (Dapr) and Envoy.

    Azure Kubernetes Service vs. Azure Container Apps

    Container apps provides other Cloud-Native features and capabilities in addition to those above including, but not limited to:

    The ability to dynamically scale and support growing numbers of users, events, and requests is one of the core requirements for most Cloud-Native, distributed applications. Azure Container Apps is purpose-built with this and other Cloud-Native tenants in mind.

    What can you build with Azure Container Apps?

    Dapr in Azure Container Apps

    As a quick personal note before we dive into this section I will say I am a bit bias about Dapr. When Dapr was first released, I had an opportunity to immediately get involved and became an early advocate for the project. It is created by developers for developers, and solves tangible problems customers architecting distributed systems face:

    HOW DO I
    • integrate with external systems that my app has to react and respond to?
    • create event driven apps which reliably send events from one service to another?
    • observe the calls and events between my services to diagnose issues in production?
    • access secrets securely from within my application?
    • discover other services and call methods on them?
    • prevent committing to a technology early and have the flexibility to swap out an alternative based on project or environment changes?

    While existing solutions were in the market which could be used to address some of the concerns above, there was not a lightweight, CNCF-backed project which could provide a unified approach to solve the more fundamental ask from customers: "How do I make it easy for developers to build microservices based on Cloud-Native best practices?"

    Enter Dapr!

    The Distributed Application Runtime (Dapr) provides APIs that simplify microservice connectivity. Whether your communication pattern is service to service invocation or pub/sub messaging, Dapr helps you write resilient and secured microservices. By letting Dapr’s sidecar take care of the complex challenges such as service discovery, message broker integration, encryption, observability, and secret management, you can focus on business logic and keep your code simple."

    The Container Apps platform provides a managed and supported Dapr integration which eliminates the need for deploying and managing the Dapr OSS project. In addition to providing managed upgrades, the platform also exposes a simplified Dapr interaction model to increase developer productivity and reduce the friction required to leverage Dapr capabilities. While the Dapr integration makes it easier for customers to adopt Cloud-Native best practices in container apps it is not required to make use of the container apps platform.

    Image on Dapr

    For additional insight into the dapr integration visit aka.ms/aca-dapr.

    Conclusion

    Backed by and integrated with powerful Cloud-Native technologies, Azure Container Apps strives to make developers productive, while reducing the operational overhead and learning curve that typically accompanies adopting a cloud-native strategy.

    If you are interested in building resilient, portable and highly-scalable apps visit Azure Container Apps | Microsoft Azure today!

    ASK THE EXPERT: LIVE Q&A

    The Azure Container Apps team will answer questions live on September 29.

    - - + + \ No newline at end of file diff --git a/blog/tags/azure-container-apps/page/2/index.html b/blog/tags/azure-container-apps/page/2/index.html index 571f763441..8adcb4fd6c 100644 --- a/blog/tags/azure-container-apps/page/2/index.html +++ b/blog/tags/azure-container-apps/page/2/index.html @@ -14,13 +14,13 @@ - - + +

    20 posts tagged with "azure-container-apps"

    View All Tags

    · 7 min read
    Brian Benz

    Welcome to Day 25 of #30DaysOfServerless!

    Azure Container Apps enable application code packaged in containers to run and scale without the overhead of managing cloud infrastructure and container orchestration. In this post I'll show you how to deploy a Java application running on Spring Boot in a container to Azure Container Registry and Azure Container Apps.


    What We'll Cover

    • Introduction to Deploying Java containers in the cloud
    • Step-by-step: Deploying to Azure Container Registry
    • Step-by-step: Deploying and running on Azure Container Apps
    • Resources: For self-study!


    Deploy Java containers to cloud

    We'll deploy a Java application running on Spring Boot in a container to Azure Container Registry and Azure Container Apps. Here are the main steps:

    • Create Azure Container Registry (ACR) on Azure portal
    • Create Azure Container App (ACA) on Azure portal.
    • Deploy code to Azure Container Registry from the Azure CLI.
    • Deploy container from ACR to ACA using the Azure portal.
    PRE-REQUISITES

    Sign in to Azure from the CLI using the az login command, and follow the prompts in your browser to complete the authentication process. Also, ensure you're running the latest version of the CLI by using the az upgrade command.

    1. Get Sample Code

    Fork and clone the sample GitHub repo to your local machine. Navigate to the and click Fork in the top-right corner of the page.

    The example code that we're using is a very basic containerized Spring Boot example. There are a lot more details to learn about Spring boot apps in docker, for a deep dive check out this Spring Boot Guide

    2. Run Sample Locally (Optional)

    If you have docker installed locally, you can optionally test the code on your local machine. Navigate to the root directory of the forked repository and run the following commands:

    docker build -t spring-boot-docker-aca .
    docker run -p 8080:8080 spring-boot-docker-aca

    Open a browser and go to https://localhost:8080. You should see this message:

    Hello Docker World

    That indicates the the Spring Boot app is successfully running locally in a docker container.

    Next, let's set up an Azure Container Registry an an Azure Container App and deploy this container to the cloud!


    3. Step-by-step: Deploy to ACR

    To create a container registry from the portal dashboard, Select Create a resource > Containers > Container Registry.

    Navigate to container registry in portal

    In the Basics tab, enter values for Resource group and Registry name. The registry name must be unique within Azure, and contain 5-50 alphanumeric characters. Create a new resource group in the West US location named spring-boot-docker-aca. Select the 'Basic' SKU.

    Keep the default values for the remaining settings. Then select Review + create, then Create. When the Deployment succeeded message appears, select the container registry in the portal.

    Note the registry server name ending with azurecr.io. You will use this in the following steps when you push and pull images with Docker.

    3.1 Log into registry using the Azure CLI

    Before pushing and pulling container images, you must log in to the registry instance. Sign into the Azure CLI on your local machine, then run the az acr login command. For this step, use the registry name, not the server name ending with azurecr.io.

    From the command line, type:

    az acr login --name myregistryname

    The command returns Login Succeeded once completed.

    3.2 Build & deploy with az acr build

    Next, we're going to deploy the docker container we created earlier using the AZ ACR Build command. AZ ACR Build creates a docker build from local code and pushes the container to Azure Container Registry if the build is successful.

    Go to your local clone of the spring-boot-docker-aca repo in the command line, type:

    az acr build --registry myregistryname --image spring-boot-docker-aca:v1 .

    3.3 List container images

    Once the AZ ACR Build command is complete, you should be able to view the container as a repository in the registry. In the portal, open your registry and select Repositories, then select the spring-boot-docker-aca repository you created with docker push. You should also see the v1 image under Tags.

    4. Deploy on ACA

    Now that we have an image in the Azure Container Registry, we can deploy it to Azure Container Apps. For the first deployment, we'll pull the container from our ACR as part of the ACA setup.

    4.1 Create a container app

    We'll create the container app at the same place that we created the container registry in the Azure portal. From the portal, select Create a resource > Containers > Container App. In the Basics tab, set these values:

    4.2 Enter project details

    SettingAction
    SubscriptionYour Azure subscription.
    Resource groupUse the spring-boot-docker-aca resource group
    Container app nameEnter spring-boot-docker-aca.

    4.3 Create an environment

    1. In the Create Container App environment field, select Create new.

    2. In the Create Container App Environment page on the Basics tab, enter the following values:

      SettingValue
      Environment nameEnter my-environment.
      RegionSelect westus3.
    3. Select OK.

    4. Select the Create button at the bottom of the Create Container App Environment page.

    5. Select the Next: App settings button at the bottom of the page.

    5. App settings tab

    The App settings tab is where you connect to the ACR and pull the repository image:

    SettingAction
    Use quickstart imageUncheck the checkbox.
    NameEnter spring-boot-docker-aca.
    Image sourceSelect Azure Container Registry
    RegistrySelect your ACR from the list.
    ImageSelect spring-boot-docker-aca from the list.
    Image TagSelect v1 from the list.

    5.1 Application ingress settings

    SettingAction
    IngressSelect Enabled.
    Ingress visibilitySelect External to publicly expose your container app.
    Target portEnter 8080.

    5.2 Deploy the container app

    1. Select the Review and create button at the bottom of the page.
    2. Select Create.

    Once the deployment is successfully completed, you'll see the message: Your deployment is complete.

    5.3 Verify deployment

    In the portal, go to the Overview of your spring-boot-docker-aca Azure Container App, and click on the Application Url. You should see this message in the browser:

    Hello Docker World

    That indicates the the Spring Boot app is running in a docker container in your spring-boot-docker-aca Azure Container App.

    Resources: For self-study!

    Once you have an understanding of the basics in ths post, there is so much more to learn!

    Thanks for stopping by!

    - - + + \ No newline at end of file diff --git a/blog/tags/azure-container-apps/page/20/index.html b/blog/tags/azure-container-apps/page/20/index.html index 0f2b35e6ad..bb0a1a9e56 100644 --- a/blog/tags/azure-container-apps/page/20/index.html +++ b/blog/tags/azure-container-apps/page/20/index.html @@ -14,13 +14,13 @@ - - + +

    20 posts tagged with "azure-container-apps"

    View All Tags

    · 7 min read
    Aaron Powell

    Welcome to Day 5 of #30DaysOfServerless!

    Yesterday we looked at Azure Functions from the perspective of a Java developer. Today, we'll do a similar walkthrough from the perspective of a JavaScript developer.

    And, we'll use this to explore another popular usage scenario for Azure Functions: building a serverless HTTP API using JavaScript.

    Ready? Let's go.


    What We'll Cover

    • Developer Guidance
    • Create Azure Function with CLI
    • Calling an external API
    • Azure Samples & Scenarios for JS
    • Exercise: Support searching
    • Resources: For self-study!


    Developer Guidance

    If you're a JavaScript developer new to serverless on Azure, start by exploring the Azure Functions JavaScript Developers Guide. It covers:

    • Quickstarts for Node.js - using Visual Code, CLI or Azure Portal
    • Guidance on hosting options and performance considerations
    • Azure Functions bindings and (code samples) for JavaScript
    • Scenario examples - integrations with other Azure Services

    Node.js 18 Support

    Node.js 18 Support (Public Preview)

    Azure Functions support for Node.js 18 entered Public Preview on Aug 31, 2022 and is supported by the Azure Functions v.4.x runtime!

    As we continue to explore how we can use Azure Functions, today we're going to look at using JavaScript to create one, and we're going to be using the newly released Node.js 18 support for Azure Functions to make the most out of the platform.

    Ensure you have Node.js 18 and Azure Functions v4.x versions installed, along with a text editor (I'll use VS Code in this post), and a terminal, then we're ready to go.

    Scenario: Calling The GitHub API

    The application we're going to be building today will use the GitHub API to return a random commit message, so that we don't need to come up with one ourselves! After all, naming things can be really hard! 🤣

    Creating the Azure Function

    To create our Azure Function, we're going to use the Azure Functions CLI, which we can install using npm:

    npm install --global azure-function-core-tools

    Once that's installed, we can use the new func command to initalise our project:

    func init --worker-runtime node --language javascript

    When running func init we can either provide the worker-runtime and language as arguments, or use the menu system that the tool will provide us. For brevity's stake, I've used the arguments here, specifying that we want node as the runtime and javascript as the language, but you could change that to typescript if you'd prefer to use TypeScript.

    Once the init command is completed, you should have a .vscode folder, and the files .gitignore, host.json, local.settings.json, and package.json.

    Files generated by func initFiles generated by func init

    Adding a HTTP Trigger

    We have an empty Functions app so far, what we need to do next is create a Function that it will run, and we're going to make a HTTP Trigger Function, which is a Function that responds to HTTP requests. We'll use the func new command to create that:

    func new --template "HTTP Trigger" --name "get-commit-message"

    When this completes, we'll have a folder for the Function, using the name we provided, that contains the filesfunction.json and index.js. Let's open the function.json to understand it a little bit:

    {
    "bindings": [
    {
    "authLevel": "function",
    "type": "httpTrigger",
    "direction": "in",
    "name": "req",
    "methods": [
    "get",
    "post"
    ]
    },
    {
    "type": "http",
    "direction": "out",
    "name": "res"
    }
    ]
    }

    This file is used to tell Functions about the Function that we've created and what it does, so it knows to handle the appropriate events. We have a bindings node which contains the event bindings for our Azure Function. The first binding is using the type httpTrigger, which indicates that it'll be executed, or triggered, by a HTTP event, and the methods indicates that it's listening to both GET and POST (you can change this for the right HTTP methods that you want to support). The HTTP request information will be bound to a property in the Functions context called req, so we can access query strings, the request body, etc.

    The other binding we have has the direction of out, meaning that it's something that the Function will return to the called, and since this is a HTTP API, the type is http, indicating that we'll return a HTTP response, and that response will be on a property called res that we add to the Functions context.

    Let's go ahead and start the Function and call it:

    func start

    Starting the FunctionStarting the Function

    With the Function started, access the endpoint http://localhost:7071/api/get-commit-message via a browser or using cURL:

    curl http://localhost:7071/api/get-commit-message\?name\=ServerlessSeptember

    Hello from Azure FunctionsHello from Azure Functions

    🎉 CONGRATULATIONS

    You created and ran a JavaScript function app locally!

    Calling an external API

    It's time to update the Function to do what we want to do - call the GitHub Search API and get some commit messages. The endpoint that we'll be calling is https://api.github.com/search/commits?q=language:javascript.

    Note: The GitHub API is rate limited and this sample will call it unauthenticated, so be aware of that in your own testing.

    To call this API, we'll leverage the newly released fetch support in Node 18 and async/await, to make for a very clean Function.

    Open up the index.js file, and delete the contents of the existing Function, so we have a empty one:

    module.exports = async function (context, req) {

    }

    The default template uses CommonJS, but you can use ES Modules with Azure Functions if you prefer.

    Now we'll use fetch to call the API, and unpack the JSON response:

    module.exports = async function (context, req) {
    const res = await fetch("https://api.github.com/search/commits?q=language:javascript");
    const json = await res.json();
    const messages = json.items.map(item => item.commit.message);
    context.res = {
    body: {
    messages
    }
    };
    }

    To send a response to the client, we're setting the context.res property, where res is the name of the output binding in our function.json, and giving it a body that contains the commit messages.

    Run func start again, and call the endpoint:

    curl http://localhost:7071/api/get-commit-message

    The you'll get some commit messages:

    A series of commit messages from the GitHub Search APIA series of commit messages from the GitHub Search API

    🎉 CONGRATULATIONS

    There we go, we've created an Azure Function which is used as a proxy to another API, that we call (using native fetch in Node.js 18) and from which we return a subset of the JSON payload.

    Next Steps

    Other Triggers, Bindings

    This article focused on using the HTTPTrigger and relevant bindings, to build a serverless API using Azure Functions. How can you explore other supported bindings, with code samples to illustrate usage?

    Scenarios with Integrations

    Once you've tried out the samples, try building an end-to-end scenario by using these triggers to integrate seamlessly with other services. Here are some suggestions:

    Exercise: Support searching

    The GitHub Search API allows you to provide search parameters via the q query string. In this sample, we hard-coded it to be language:javascript, but as a follow-on exercise, expand the Function to allow the caller to provide the search terms as a query string to the Azure Function, which is passed to the GitHub Search API. Hint - have a look at the req argument.

    Resources

    - - + + \ No newline at end of file diff --git a/blog/tags/azure-container-apps/page/3/index.html b/blog/tags/azure-container-apps/page/3/index.html index 8ebc51e7ce..ea96a45d55 100644 --- a/blog/tags/azure-container-apps/page/3/index.html +++ b/blog/tags/azure-container-apps/page/3/index.html @@ -14,13 +14,13 @@ - - + +

    20 posts tagged with "azure-container-apps"

    View All Tags

    · 19 min read
    Alex Wolf

    Welcome to Day 24 of #30DaysOfServerless!

    We continue exploring E2E scenarios with this tutorial where you'll deploy a containerized ASP.NET Core 6.0 application to Azure Container Apps.

    The application consists of a front-end web app built using Blazor Server, as well as two Web API projects to manage data. These projects will exist as three separate containers inside of a shared container apps environment.


    What We'll Cover

    • Deploy ASP.NET Core 6.0 app to Azure Container Apps
    • Automate deployment workflows using GitHub Actions
    • Provision and deploy resources using Azure Bicep
    • Exercise: Try this yourself!
    • Resources: For self-study!


    Introduction

    Azure Container Apps enables you to run microservices and containerized applications on a serverless platform. With Container Apps, you enjoy the benefits of running containers while leaving behind the concerns of manually configuring cloud infrastructure and complex container orchestrators.

    In this tutorial, you'll deploy a containerized ASP.NET Core 6.0 application to Azure Container Apps. The application consists of a front-end web app built using Blazor Server, as well as two Web API projects to manage data. These projects will exist as three separate containers inside of a shared container apps environment.

    You will use GitHub Actions in combination with Bicep to deploy the application. These tools provide an approachable and sustainable solution for building CI/CD pipelines and working with Container Apps.

    PRE-REQUISITES

    Architecture

    In this tutorial, we'll setup a container app environment with a separate container for each project in the sample store app. The major components of the sample project include:

    • A Blazor Server front-end web app to display product information
    • A products API to list available products
    • An inventory API to determine how many products are in stock
    • GitHub Actions and Bicep templates to provision Azure resources and then build and deploy the sample app.

    You will explore these templates later in the tutorial.

    Public internet traffic should be proxied to the Blazor app. The back-end APIs should only be reachable via requests from the Blazor app inside the container apps environment. This setup can be achieved using container apps environment ingress configurations during deployment.

    An architecture diagram of the shopping app


    Project Sources

    Want to follow along? Fork the sample below. The tutorial can be completed with or without Dapr integration. Pick the path you feel comfortable in. Dapr provides various benefits that make working with Microservices easier - you can learn more in the docs. For this tutorial you will need GitHub and Azure CLI.

    PICK YOUR PATH

    To follow along with this tutorial, fork the relevant sample project below.

    You can run the app locally from Visual Studio:

    • Right click on the Blazor Store project and select Set as Startup Project.
    • Press the start button at the top of Visual Studio to run the app.
    • (Once running) start each API in the background by
    • right-clicking on the project node
    • selecting Debug --> Start without debugging.

    Once the Blazor app is running, you should see something like this:

    An architecture diagram of the shopping app


    Configuring Azure credentials

    In order to deploy the application to Azure through GitHub Actions, you first need to create a service principal. The service principal will allow the GitHub Actions process to authenticate to your Azure subscription to create resources and deploy code. You can learn more about Service Principals in the Azure CLI documentation. For this step you'll need to be logged into the Azure CLI.

    1) If you have not done so already, make sure to fork the sample project to your own GitHub account or organization.

    1) Once you have completed this step, create a service principal using the Azure CLI command below:

    ```azurecli
    $subscriptionId=$(az account show --query id --output tsv)
    az ad sp create-for-rbac --sdk-auth --name WebAndApiSample --role Contributor --scopes /subscriptions/$subscriptionId
    ```

    1) Copy the JSON output of the CLI command to your clipboard

    1) Under the settings tab of your forked GitHub repo, create a new secret named AzureSPN. The name is important to match the Bicep templates included in the project, which we'll review later. Paste the copied service principal values on your clipboard into the secret and save your changes. This new secret will be used by the GitHub Actions workflow to authenticate to Azure.

    :::image type="content" source="./img/dotnet/github-secrets.png" alt-text="A screenshot of adding GitHub secrets.":::

    Deploy using Github Actions

    You are now ready to deploy the application to Azure Container Apps using GitHub Actions. The sample application includes a GitHub Actions template that is configured to build and deploy any changes to a branch named deploy. The deploy branch does not exist in your forked repository by default, but you can easily create it through the GitHub user interface.

    1) Switch to the Actions tab along the top navigation of your GitHub repository. If you have not done so already, ensure that workflows are enabled by clicking the button in the center of the page.

    A screenshot showing how to enable GitHub actions

    1) Navigate to the main Code tab of your repository and select the main dropdown. Enter deploy into the branch input box, and then select Create branch: deploy from 'main'.

    A screenshot showing how to create the deploy branch

    1) On the new deploy branch, navigate down into the .github/workflows folder. You should see a file called deploy.yml, which contains the main GitHub Actions workflow script. Click on the file to view its content. You'll learn more about this file later in the tutorial.

    1) Click the pencil icon in the upper right to edit the document.

    1) Change the RESOURCE_GROUP_NAME: value to msdocswebappapis or another valid resource group name of your choosing.

    1) In the upper right of the screen, select Start commit and then Commit changes to commit your edit. This will persist the change to the file and trigger the GitHub Actions workflow to build and deploy the app.

    A screenshot showing how to commit changes

    1) Switch to the Actions tab along the top navigation again. You should see the workflow running to create the necessary resources and deploy the app. The workflow may take several minutes to run. When it completes successfully, all of the jobs should have a green checkmark icon next to them.

    The completed GitHub workflow.

    Explore the Azure resources

    Once the GitHub Actions workflow has completed successfully you can browse the created resources in the Azure portal.

    1) On the left navigation, select Resource Groups. Next,choose the msdocswebappapis resource group that was created by the GitHub Actions workflow.

    2) You should see seven resources available that match the screenshot and table descriptions below.

    The resources created in Azure.

    Resource nameTypeDescription
    inventoryContainer appThe containerized inventory API.
    msdocswebappapisacrContainer registryA registry that stores the built Container images for your apps.
    msdocswebappapisaiApplication insightsApplication insights provides advanced monitoring, logging and metrics for your apps.
    msdocswebappapisenvContainer apps environmentA container environment that manages networking, security and resource concerns. All of your containers live in this environment.
    msdocswebappapislogsLog Analytics workspaceA workspace environment for managing logging and analytics for the container apps environment
    productsContainer appThe containerized products API.
    storeContainer appThe Blazor front-end web app.

    3) You can view your running app in the browser by clicking on the store container app. On the overview page, click the Application Url link on the upper right of the screen.

    :::image type="content" source="./img/dotnet/application-url.png" alt-text="The link to browse the app.":::

    Understanding the GitHub Actions workflow

    The GitHub Actions workflow created and deployed resources to Azure using the deploy.yml file in the .github folder at the root of the project. The primary purpose of this file is to respond to events - such as commits to a branch - and run jobs to accomplish tasks. The deploy.yml file in the sample project has three main jobs:

    • Provision: Create the necessary resources in Azure, such as the container apps environment. This step leverages Bicep templates to create the Azure resources, which you'll explore in a moment.
    • Build: Create the container images for the three apps in the project and store them in the container registry.
    • Deploy: Deploy the container images to the different container apps created during the provisioning job.

    The deploy.yml file also accepts parameters to make the workflow more dynamic, such as setting the resource group name or the Azure region resources will be provisioned to.

    Below is a commented version of the deploy.yml file that highlights the essential steps.

    name: Build and deploy .NET application to Container Apps

    # Trigger the workflow on pushes to the deploy branch
    on:
    push:
    branches:
    - deploy

    env:
    # Set workflow variables
    RESOURCE_GROUP_NAME: msdocswebappapis

    REGION: eastus

    STORE_DOCKER: Store/Dockerfile
    STORE_IMAGE: store

    INVENTORY_DOCKER: Store.InventoryApi/Dockerfile
    INVENTORY_IMAGE: inventory

    PRODUCTS_DOCKER: Store.ProductApi/Dockerfile
    PRODUCTS_IMAGE: products

    jobs:
    # Create the required Azure resources
    provision:
    runs-on: ubuntu-latest

    steps:

    - name: Checkout to the branch
    uses: actions/checkout@v2

    - name: Azure Login
    uses: azure/login@v1
    with:
    creds: ${{ secrets.AzureSPN }}

    - name: Create resource group
    uses: azure/CLI@v1
    with:
    inlineScript: >
    echo "Creating resource group in Azure"
    echo "Executing 'az group create -l ${{ env.REGION }} -n ${{ env.RESOURCE_GROUP_NAME }}'"
    az group create -l ${{ env.REGION }} -n ${{ env.RESOURCE_GROUP_NAME }}

    # Use Bicep templates to create the resources in Azure
    - name: Creating resources
    uses: azure/CLI@v1
    with:
    inlineScript: >
    echo "Creating resources"
    az deployment group create --resource-group ${{ env.RESOURCE_GROUP_NAME }} --template-file '/github/workspace/Azure/main.bicep' --debug

    # Build the three app container images
    build:
    runs-on: ubuntu-latest
    needs: provision

    steps:

    - name: Checkout to the branch
    uses: actions/checkout@v2

    - name: Azure Login
    uses: azure/login@v1
    with:
    creds: ${{ secrets.AzureSPN }}

    - name: Set up Docker Buildx
    uses: docker/setup-buildx-action@v1

    - name: Login to ACR
    run: |
    set -euo pipefail
    access_token=$(az account get-access-token --query accessToken -o tsv)
    refresh_token=$(curl https://${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io/oauth2/exchange -v -d "grant_type=access_token&service=${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io&access_token=$access_token" | jq -r .refresh_token)
    docker login -u 00000000-0000-0000-0000-000000000000 --password-stdin ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io <<< "$refresh_token"

    - name: Build the products api image and push it to ACR
    uses: docker/build-push-action@v2
    with:
    push: true
    tags: ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io/${{ env.PRODUCTS_IMAGE }}:${{ github.sha }}
    file: ${{ env.PRODUCTS_DOCKER }}

    - name: Build the inventory api image and push it to ACR
    uses: docker/build-push-action@v2
    with:
    push: true
    tags: ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io/${{ env.INVENTORY_IMAGE }}:${{ github.sha }}
    file: ${{ env.INVENTORY_DOCKER }}

    - name: Build the frontend image and push it to ACR
    uses: docker/build-push-action@v2
    with:
    push: true
    tags: ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io/${{ env.STORE_IMAGE }}:${{ github.sha }}
    file: ${{ env.STORE_DOCKER }}

    # Deploy the three container images
    deploy:
    runs-on: ubuntu-latest
    needs: build

    steps:

    - name: Checkout to the branch
    uses: actions/checkout@v2

    - name: Azure Login
    uses: azure/login@v1
    with:
    creds: ${{ secrets.AzureSPN }}

    - name: Installing Container Apps extension
    uses: azure/CLI@v1
    with:
    inlineScript: >
    az config set extension.use_dynamic_install=yes_without_prompt

    az extension add --name containerapp --yes

    - name: Login to ACR
    run: |
    set -euo pipefail
    access_token=$(az account get-access-token --query accessToken -o tsv)
    refresh_token=$(curl https://${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io/oauth2/exchange -v -d "grant_type=access_token&service=${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io&access_token=$access_token" | jq -r .refresh_token)
    docker login -u 00000000-0000-0000-0000-000000000000 --password-stdin ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io <<< "$refresh_token"

    - name: Deploy Container Apps
    uses: azure/CLI@v1
    with:
    inlineScript: >
    az containerapp registry set -n products -g ${{ env.RESOURCE_GROUP_NAME }} --server ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io

    az containerapp update -n products -g ${{ env.RESOURCE_GROUP_NAME }} -i ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io/${{ env.PRODUCTS_IMAGE }}:${{ github.sha }}

    az containerapp registry set -n inventory -g ${{ env.RESOURCE_GROUP_NAME }} --server ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io

    az containerapp update -n inventory -g ${{ env.RESOURCE_GROUP_NAME }} -i ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io/${{ env.INVENTORY_IMAGE }}:${{ github.sha }}

    az containerapp registry set -n store -g ${{ env.RESOURCE_GROUP_NAME }} --server ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io

    az containerapp update -n store -g ${{ env.RESOURCE_GROUP_NAME }} -i ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io/${{ env.STORE_IMAGE }}:${{ github.sha }}

    - name: logout
    run: >
    az logout

    Understanding the Bicep templates

    During the provisioning stage of the GitHub Actions workflow, the main.bicep file is processed. Bicep files provide a declarative way of generating resources in Azure and are ideal for managing infrastructure as code. You can learn more about Bicep in the related documentation. The main.bicep file in the sample project creates the following resources:

    • The container registry to store images of the containerized apps.
    • The container apps environment, which handles networking and resource management for the container apps.
    • Three container apps - one for the Blazor front-end and two for the back-end product and inventory APIs.
    • Configuration values to connect these services together

    main.bicep without Dapr

    param location string = resourceGroup().location

    # create the azure container registry
    resource acr 'Microsoft.ContainerRegistry/registries@2021-09-01' = {
    name: toLower('${resourceGroup().name}acr')
    location: location
    sku: {
    name: 'Basic'
    }
    properties: {
    adminUserEnabled: true
    }
    }

    # create the aca environment
    module env 'environment.bicep' = {
    name: 'containerAppEnvironment'
    params: {
    location: location
    }
    }

    # create the various configuration pairs
    var shared_config = [
    {
    name: 'ASPNETCORE_ENVIRONMENT'
    value: 'Development'
    }
    {
    name: 'APPINSIGHTS_INSTRUMENTATIONKEY'
    value: env.outputs.appInsightsInstrumentationKey
    }
    {
    name: 'APPLICATIONINSIGHTS_CONNECTION_STRING'
    value: env.outputs.appInsightsConnectionString
    }
    ]

    # create the products api container app
    module products 'container_app.bicep' = {
    name: 'products'
    params: {
    name: 'products'
    location: location
    registryPassword: acr.listCredentials().passwords[0].value
    registryUsername: acr.listCredentials().username
    containerAppEnvironmentId: env.outputs.id
    registry: acr.name
    envVars: shared_config
    externalIngress: false
    }
    }

    # create the inventory api container app
    module inventory 'container_app.bicep' = {
    name: 'inventory'
    params: {
    name: 'inventory'
    location: location
    registryPassword: acr.listCredentials().passwords[0].value
    registryUsername: acr.listCredentials().username
    containerAppEnvironmentId: env.outputs.id
    registry: acr.name
    envVars: shared_config
    externalIngress: false
    }
    }

    # create the store api container app
    var frontend_config = [
    {
    name: 'ProductsApi'
    value: 'http://${products.outputs.fqdn}'
    }
    {
    name: 'InventoryApi'
    value: 'http://${inventory.outputs.fqdn}'
    }
    ]

    module store 'container_app.bicep' = {
    name: 'store'
    params: {
    name: 'store'
    location: location
    registryPassword: acr.listCredentials().passwords[0].value
    registryUsername: acr.listCredentials().username
    containerAppEnvironmentId: env.outputs.id
    registry: acr.name
    envVars: union(shared_config, frontend_config)
    externalIngress: true
    }
    }

    main.bicep with Dapr


    param location string = resourceGroup().location

    # create the azure container registry
    resource acr 'Microsoft.ContainerRegistry/registries@2021-09-01' = {
    name: toLower('${resourceGroup().name}acr')
    location: location
    sku: {
    name: 'Basic'
    }
    properties: {
    adminUserEnabled: true
    }
    }

    # create the aca environment
    module env 'environment.bicep' = {
    name: 'containerAppEnvironment'
    params: {
    location: location
    }
    }

    # create the various config pairs
    var shared_config = [
    {
    name: 'ASPNETCORE_ENVIRONMENT'
    value: 'Development'
    }
    {
    name: 'APPINSIGHTS_INSTRUMENTATIONKEY'
    value: env.outputs.appInsightsInstrumentationKey
    }
    {
    name: 'APPLICATIONINSIGHTS_CONNECTION_STRING'
    value: env.outputs.appInsightsConnectionString
    }
    ]

    # create the products api container app
    module products 'container_app.bicep' = {
    name: 'products'
    params: {
    name: 'products'
    location: location
    registryPassword: acr.listCredentials().passwords[0].value
    registryUsername: acr.listCredentials().username
    containerAppEnvironmentId: env.outputs.id
    registry: acr.name
    envVars: shared_config
    externalIngress: false
    }
    }

    # create the inventory api container app
    module inventory 'container_app.bicep' = {
    name: 'inventory'
    params: {
    name: 'inventory'
    location: location
    registryPassword: acr.listCredentials().passwords[0].value
    registryUsername: acr.listCredentials().username
    containerAppEnvironmentId: env.outputs.id
    registry: acr.name
    envVars: shared_config
    externalIngress: false
    }
    }

    # create the store api container app
    module store 'container_app.bicep' = {
    name: 'store'
    params: {
    name: 'store'
    location: location
    registryPassword: acr.listCredentials().passwords[0].value
    registryUsername: acr.listCredentials().username
    containerAppEnvironmentId: env.outputs.id
    registry: acr.name
    envVars: shared_config
    externalIngress: true
    }
    }


    Bicep Modules

    The main.bicep file references modules to create resources, such as module products. Modules are a feature of Bicep templates that enable you to abstract resource declarations into their own files or sub-templates. As the main.bicep file is processed, the defined modules are also evaluated. Modules allow you to create resources in a more organized and reusable way. They can also define input and output parameters that are passed to and from the parent template, such as the name of a resource.

    For example, the environment.bicep module extracts the details of creating a container apps environment into a reusable template. The module defines necessary resource dependencies such as Log Analytics Workspaces and an Application Insights instance.

    environment.bicep without Dapr

    param baseName string = resourceGroup().name
    param location string = resourceGroup().location

    resource logs 'Microsoft.OperationalInsights/workspaces@2021-06-01' = {
    name: '${baseName}logs'
    location: location
    properties: any({
    retentionInDays: 30
    features: {
    searchVersion: 1
    }
    sku: {
    name: 'PerGB2018'
    }
    })
    }

    resource appInsights 'Microsoft.Insights/components@2020-02-02' = {
    name: '${baseName}ai'
    location: location
    kind: 'web'
    properties: {
    Application_Type: 'web'
    WorkspaceResourceId: logs.id
    }
    }

    resource env 'Microsoft.App/managedEnvironments@2022-01-01-preview' = {
    name: '${baseName}env'
    location: location
    properties: {
    appLogsConfiguration: {
    destination: 'log-analytics'
    logAnalyticsConfiguration: {
    customerId: logs.properties.customerId
    sharedKey: logs.listKeys().primarySharedKey
    }
    }
    }
    }

    output id string = env.id
    output appInsightsInstrumentationKey string = appInsights.properties.InstrumentationKey
    output appInsightsConnectionString string = appInsights.properties.ConnectionString

    environment.bicep with Dapr


    param baseName string = resourceGroup().name
    param location string = resourceGroup().location

    resource logs 'Microsoft.OperationalInsights/workspaces@2021-06-01' = {
    name: '${baseName}logs'
    location: location
    properties: any({
    retentionInDays: 30
    features: {
    searchVersion: 1
    }
    sku: {
    name: 'PerGB2018'
    }
    })
    }

    resource appInsights 'Microsoft.Insights/components@2020-02-02' = {
    name: '${baseName}ai'
    location: location
    kind: 'web'
    properties: {
    Application_Type: 'web'
    WorkspaceResourceId: logs.id
    }
    }

    resource env 'Microsoft.App/managedEnvironments@2022-01-01-preview' = {
    name: '${baseName}env'
    location: location
    properties: {
    appLogsConfiguration: {
    destination: 'log-analytics'
    logAnalyticsConfiguration: {
    customerId: logs.properties.customerId
    sharedKey: logs.listKeys().primarySharedKey
    }
    }
    }
    }

    output id string = env.id
    output appInsightsInstrumentationKey string = appInsights.properties.InstrumentationKey
    output appInsightsConnectionString string = appInsights.properties.ConnectionString


    The container_apps.bicep template defines numerous parameters to provide a reusable template for creating container apps. This allows the module to be used in other CI/CD pipelines as well.

    container_app.bicep without Dapr

    param name string
    param location string = resourceGroup().location
    param containerAppEnvironmentId string
    param repositoryImage string = 'mcr.microsoft.com/azuredocs/containerapps-helloworld:latest'
    param envVars array = []
    param registry string
    param minReplicas int = 1
    param maxReplicas int = 1
    param port int = 80
    param externalIngress bool = false
    param allowInsecure bool = true
    param transport string = 'http'
    param registryUsername string
    @secure()
    param registryPassword string

    resource containerApp 'Microsoft.App/containerApps@2022-01-01-preview' ={
    name: name
    location: location
    properties:{
    managedEnvironmentId: containerAppEnvironmentId
    configuration: {
    activeRevisionsMode: 'single'
    secrets: [
    {
    name: 'container-registry-password'
    value: registryPassword
    }
    ]
    registries: [
    {
    server: registry
    username: registryUsername
    passwordSecretRef: 'container-registry-password'
    }
    ]
    ingress: {
    external: externalIngress
    targetPort: port
    transport: transport
    allowInsecure: allowInsecure
    }
    }
    template: {
    containers: [
    {
    image: repositoryImage
    name: name
    env: envVars
    }
    ]
    scale: {
    minReplicas: minReplicas
    maxReplicas: maxReplicas
    }
    }
    }
    }

    output fqdn string = containerApp.properties.configuration.ingress.fqdn

    container_app.bicep with Dapr


    param name string
    param location string = resourceGroup().location
    param containerAppEnvironmentId string
    param repositoryImage string = 'mcr.microsoft.com/azuredocs/containerapps-helloworld:latest'
    param envVars array = []
    param registry string
    param minReplicas int = 1
    param maxReplicas int = 1
    param port int = 80
    param externalIngress bool = false
    param allowInsecure bool = true
    param transport string = 'http'
    param appProtocol string = 'http'
    param registryUsername string
    @secure()
    param registryPassword string

    resource containerApp 'Microsoft.App/containerApps@2022-01-01-preview' ={
    name: name
    location: location
    properties:{
    managedEnvironmentId: containerAppEnvironmentId
    configuration: {
    dapr: {
    enabled: true
    appId: name
    appPort: port
    appProtocol: appProtocol
    }
    activeRevisionsMode: 'single'
    secrets: [
    {
    name: 'container-registry-password'
    value: registryPassword
    }
    ]
    registries: [
    {
    server: registry
    username: registryUsername
    passwordSecretRef: 'container-registry-password'
    }
    ]
    ingress: {
    external: externalIngress
    targetPort: port
    transport: transport
    allowInsecure: allowInsecure
    }
    }
    template: {
    containers: [
    {
    image: repositoryImage
    name: name
    env: envVars
    }
    ]
    scale: {
    minReplicas: minReplicas
    maxReplicas: maxReplicas
    }
    }
    }
    }

    output fqdn string = containerApp.properties.configuration.ingress.fqdn


    Understanding configuration differences with Dapr

    The code for this specific sample application is largely the same whether or not Dapr is integrated. However, even with this simple app, there are a few benefits and configuration differences when using Dapr that are worth exploring.

    In this scenario most of the changes are related to communication between the container apps. However, you can explore the full range of Dapr benefits by reading the Dapr integration with Azure Container Apps article in the conceptual documentation.

    Without Dapr

    Without Dapr the main.bicep template handles wiring up the front-end store app to communicate with the back-end apis by manually managing environment variables. The bicep template retrieves the fully qualified domains (fqdn) of the API apps as output parameters when they are created. Those configurations are then set as environment variables on the store container app.


    # Retrieve environment variables from API container creation
    var frontend_config = [
    {
    name: 'ProductsApi'
    value: 'http://${products.outputs.fqdn}'
    }
    {
    name: 'InventoryApi'
    value: 'http://${inventory.outputs.fqdn}'
    }
    ]

    # create the store api container app, passing in config
    module store 'container_app.bicep' = {
    name: 'store'
    params: {
    name: 'store'
    location: location
    registryPassword: acr.listCredentials().passwords[0].value
    registryUsername: acr.listCredentials().username
    containerAppEnvironmentId: env.outputs.id
    registry: acr.name
    envVars: union(shared_config, frontend_config)
    externalIngress: true
    }
    }

    The environment variables are then retrieved inside of the program class and used to configure the base URLs of the corresponding HTTP clients.


    builder.Services.AddHttpClient("Products", (httpClient) => httpClient.BaseAddress = new Uri(builder.Configuration.GetValue<string>("ProductsApi")));
    builder.Services.AddHttpClient("Inventory", (httpClient) => httpClient.BaseAddress = new Uri(builder.Configuration.GetValue<string>("InventoryApi")));

    With Dapr

    Dapr can be enabled on a container app when it is created, as seen below. This configuration adds a Dapr sidecar to the app to streamline discovery and communication features between the different container apps in your environment.


    # Create the container app with Dapr enabled
    resource containerApp 'Microsoft.App/containerApps@2022-01-01-preview' ={
    name: name
    location: location
    properties:{
    managedEnvironmentId: containerAppEnvironmentId
    configuration: {
    dapr: {
    enabled: true
    appId: name
    appPort: port
    appProtocol: appProtocol
    }
    activeRevisionsMode: 'single'
    secrets: [
    {
    name: 'container-registry-password'
    value: registryPassword
    }
    ]

    # Rest of template omitted for brevity...
    }
    }

    Some of these Dapr features can be surfaced through the program file. You can configure your HttpClient to leverage Dapr configurations when communicating with other apps in your environment.


    // reconfigure code to make requests to Dapr sidecar
    var baseURL = (Environment.GetEnvironmentVariable("BASE_URL") ?? "http://localhost") + ":" + (Environment.GetEnvironmentVariable("DAPR_HTTP_PORT") ?? "3500");
    builder.Services.AddHttpClient("Products", (httpClient) =>
    {
    httpClient.BaseAddress = new Uri(baseURL);
    httpClient.DefaultRequestHeaders.Add("dapr-app-id", "Products");
    });

    builder.Services.AddHttpClient("Inventory", (httpClient) =>
    {
    httpClient.BaseAddress = new Uri(baseURL);
    httpClient.DefaultRequestHeaders.Add("dapr-app-id", "Inventory");
    });


    Clean up resources

    If you're not going to continue to use this application, you can delete the Azure Container Apps and all the associated services by removing the resource group.

    Follow these steps in the Azure portal to remove the resources you created:

    1. In the Azure portal, navigate to the msdocswebappsapi resource group using the left navigation or search bar.
    2. Select the Delete resource group button at the top of the resource group Overview.
    3. Enter the resource group name msdocswebappsapi in the Are you sure you want to delete "msdocswebappsapi" confirmation dialog.
    4. Select Delete.
      The process to delete the resource group may take a few minutes to complete.
    - - + + \ No newline at end of file diff --git a/blog/tags/azure-container-apps/page/4/index.html b/blog/tags/azure-container-apps/page/4/index.html index b9dea17081..15397adf2c 100644 --- a/blog/tags/azure-container-apps/page/4/index.html +++ b/blog/tags/azure-container-apps/page/4/index.html @@ -14,15 +14,15 @@ - - + +

    20 posts tagged with "azure-container-apps"

    View All Tags

    · 10 min read
    Ayca Bas

    Welcome to Day 20 of #30DaysOfServerless!

    Every day millions of people spend their precious time in productivity tools. What if you use data and intelligence behind the Microsoft applications (Microsoft Teams, Outlook, and many other Office apps) to build seamless automations and custom apps to boost productivity?

    In this post, we'll learn how to build a seamless onboarding experience for new employees joining a company with the power of Microsoft Graph, integrated with Event Hubs and Logic Apps!


    What We'll Cover

    • ✨ The power of Microsoft Graph
    • 🖇️ How do Microsoft Graph and Event Hubs work together?
    • 🛠 Let's Build an Onboarding Workflow!
      • 1️⃣ Setup Azure Event Hubs + Key Vault
      • 2️⃣ Subscribe to users, receive change notifications from Logic Apps
      • 3️⃣ Create Onboarding workflow in the Logic Apps
    • 🚀 Debug: Your onboarding experience
    • ✋ Exercise: Try this tutorial out yourself!
    • 📚 Resources: For Self-Study


    ✨ The Power of Microsoft Graph

    Microsoft Graph is the gateway to data and intelligence in Microsoft 365 platform. Microsoft Graph exploses Rest APIs and client libraries to access data across Microsoft 365 core services such as Calendar, Teams, To Do, Outlook, People, Planner, OneDrive, OneNote and more.

    Overview of Microsoft Graph

    You can build custom experiences by using Microsoft Graph such as automating the onboarding process for new employees. When new employees are created in the Azure Active Directory, they will be automatically added in the Onboarding team on Microsoft Teams.

    Solution architecture


    🖇️ Microsoft Graph with Event Hubs

    Microsoft Graph uses a webhook mechanism to track changes in resources and deliver change notifications to the clients. For example, with Microsoft Graph Change Notifications, you can receive change notifications when:

    • a new task is added in the to-do list
    • a user changes the presence status from busy to available
    • an event is deleted/cancelled from the calendar

    If you'd like to track a large set of resources at a high frequency, use Azure Events Hubs instead of traditional webhooks to receive change notifications. Azure Event Hubs is a popular real-time events ingestion and distribution service built for scale.

    EVENT GRID - PARTNER EVENTS

    Microsoft Graph Change Notifications can be also received by using Azure Event Grid -- currently available for Microsoft Partners! Read the Partner Events Overview documentation for details.

    Setup Azure Event Hubs + Key Vault.

    To get Microsoft Graph Change Notifications delivered to Azure Event Hubs, we'll have to setup Azure Event Hubs and Azure Key Vault. We'll use Azure Key Vault to access to Event Hubs connection string.

    1️⃣ Create Azure Event Hubs

    1. Go to Azure Portal and select Create a resource, type Event Hubs and select click Create.
    2. Fill in the Event Hubs namespace creation details, and then click Create.
    3. Go to the newly created Event Hubs namespace page, select Event Hubs tab from the left pane and + Event Hub:
      • Name your Event Hub as Event Hub
      • Click Create.
    4. Click the name of the Event Hub, and then select Shared access policies and + Add to add a new policy:
      • Give a name to the policy
      • Check Send and Listen
      • Click Create.
    5. After the policy has been created, click the name of the policy to open the details panel, and then copy the Connection string-primary key value. Write it down; you'll need it for the next step.
    6. Go to Consumer groups tab in the left pane and select + Consumer group, give a name for your consumer group as onboarding and select Create.

    2️⃣ Create Azure Key Vault

    1. Go to Azure Portal and select Create a resource, type Key Vault and select Create.
    2. Fill in the Key Vault creation details, and then click Review + Create.
    3. Go to newly created Key Vault and select Secrets tab from the left pane and click + Generate/Import:
      • Give a name to the secret
      • For the value, paste in the connection string you generated at the Event Hubs step
      • Click Create
      • Copy the name of the secret.
    4. Select Access Policies from the left pane and + Add Access Policy:
      • For Secret permissions, select Get
      • For Principal, select Microsoft Graph Change Tracking
      • Click Add.
    5. Select Overview tab from the left pane and copy the Vault URI.

    Subscribe for Logic Apps change notifications

    To start receiving Microsoft Graph Change Notifications, we'll need to create subscription to the resource that we'd like to track - here, 'users'. We'll use Azure Logic Apps to create subscription.

    To create subscription for Microsoft Graph Change Notifications, we'll need to make a http post request to https://graph.microsoft.com/v1.0/subscriptions. Microsoft Graph requires Azure Active Directory authentication make API calls. First, we'll need to register an app to Azure Active Directory, and then we will make the Microsoft Graph Subscription API call with Azure Logic Apps.

    1️⃣ Create an app in Azure Active Directory

    1. In the Azure Portal, go to Azure Active Directory and select App registrations from the left pane and select + New registration. Fill in the details for the new App registration form as below:
      • Name: Graph Subscription Flow Auth
      • Supported account types: Accounts in any organizational directory (Any Azure AD directory - Multitenant) and personal Microsoft accounts (e.g. Skype, Xbox)
      • Select Register.
    2. Go to newly registered app in Azure Active Directory, select API permissions:
      • Select + Add a permission and Microsoft Graph
      • Select Application permissions and add User.Read.All and Directory.Read.All.
      • Select Grant admin consent for the organization
    3. Select Certificates & secrets tab from the left pane, select + New client secret:
      • Choose desired expiry duration
      • Select Add
      • Copy the value of the secret.
    4. Go to Overview from the left pane, copy Application (client) ID and Directory (tenant) ID.

    2️⃣ Create subscription with Azure Logic Apps

    1. Go to Azure Portal and select Create a resource, type Logic apps and select click Create.

    2. Fill in the Logic Apps creation details, and then click Create.

    3. Go to the newly created Logic Apps page, select Workflows tab from the left pane and select + Add:

      • Give a name to the new workflow as graph-subscription-flow
      • Select Stateful as a state type
      • Click Create.
    4. Go to graph-subscription-flow, and then select Designer tab.

    5. In the Choose an operation section, search for Schedule and select Recurrence as a trigger. Fill in the parameters as below:

      • Interval: 61
      • Frequency: Minute
      • Time zone: Select your own time zone
      • Start time: Set a start time
    6. Select + button in the flow and select add an action. Search for HTTP and select HTTP as an action. Fill in the parameters as below:

      • Method: POST
      • URI: https://graph.microsoft.com/v1.0/subscriptions
      • Headers:
        • Key: Content-type
        • Value: application/json
      • Body:
      {
      "changeType": "created, updated",
      "clientState": "secretClientValue",
      "expirationDateTime": "@{addHours(utcNow(), 1)}",
      "notificationUrl": "EventHub:https://<YOUR-VAULT-URI>/secrets/<YOUR-KEY-VAULT-SECRET-NAME>?tenantId=72f988bf-86f1-41af-91ab-2d7cd011db47",
      "resource": "users"
      }

      In notificationUrl, make sure to replace <YOUR-VAULT-URI> with the vault uri and <YOUR-KEY-VAULT-SECRET-NAME> with the secret name that you copied from the Key Vault.

      In resource, define the resource type you'd like to track changes. For our example, we will track changes for users resource.

      • Authentication:
        • Authentication type: Active Directory OAuth
        • Authority: https://login.microsoft.com
        • Tenant: Directory (tenant) ID copied from AAD app
        • Audience: https://graph.microsoft.com
        • Client ID: Application (client) ID copied from AAD app
        • Credential Type: Secret
        • Secret: value of the secret copied from AAD app
    7. Select Save and run your workflow from the Overview tab.

      Check your subscription in Graph Explorer: If you'd like to make sure that your subscription is created successfully by Logic Apps, you can go to Graph Explorer, login with your Microsoft 365 account and make GET request to https://graph.microsoft.com/v1.0/subscriptions. Your subscription should appear in the response after it's created successfully.

    Subscription workflow success

    After subscription is created successfully by Logic Apps, Azure Event Hubs will receive notifications whenever there is a new user created in Azure Active Directory.


    Create Onboarding workflow in Logic Apps

    We'll create a second workflow in the Logic Apps to receive change notifications from Event Hubs when there is a new user created in the Azure Active Directory and add new user in Onboarding team on Microsoft Teams.

    1. Go to the Logic Apps you created in the previous steps, select Workflows tab and create a new workflow by selecting + Add:
      • Give a name to the new workflow as teams-onboarding-flow
      • Select Stateful as a state type
      • Click Create.
    2. Go to teams-onboarding-flow, and then select Designer tab.
    3. In the Choose an operation section, search for Event Hub, select When events are available in Event Hub as a trigger. Setup Event Hub connection as below:
      • Create Connection:
        • Connection name: Connection
        • Authentication Type: Connection String
        • Connection String: Go to Event Hubs > Shared Access Policies > RootManageSharedAccessKey and copy Connection string–primary key
        • Select Create.
      • Parameters:
        • Event Hub Name: Event Hub
        • Consumer Group Name: onboarding
    4. Select + in the flow and add an action, search for Control and add For each as an action. Fill in For each action as below:
      • Select output from previous steps: Events
    5. Inside For each, select + in the flow and add an action, search for Data operations and select Parse JSON. Fill in Parse JSON action as below:
      • Content: Events Content
      • Schema: Copy the json content from schema-parse.json and paste as a schema
    6. Select + in the flow and add an action, search for Control and add For each as an action. Fill in For each action as below:
      • Select output from previous steps: value
      1. Inside For each, select + in the flow and add an action, search for Microsoft Teams and select Add a member to a team. Login with your Microsoft 365 account to create a connection and fill in Add a member to a team action as below:
      • Team: Create an Onboarding team on Microsoft Teams and select
      • A user AAD ID for the user to add to a team: id
    7. Select Save.

    🚀 Debug your onboarding experience

    To debug our onboarding experience, we'll need to create a new user in Azure Active Directory and see if it's added in Microsoft Teams Onboarding team automatically.

    1. Go to Azure Portal and select Azure Active Directory from the left pane and go to Users. Select + New user and Create new user. Fill in the details as below:

      • User name: JaneDoe
      • Name: Jane Doe

      new user in Azure Active Directory

    2. When you added Jane Doe as a new user, it should trigger the teams-onboarding-flow to run. teams onboarding flow success

    3. Once the teams-onboarding-flow runs successfully, you should be able to see Jane Doe as a member of the Onboarding team on Microsoft Teams! 🥳 new member in Onboarding team on Microsoft Teams

    Congratulations! 🎉

    You just built an onboarding experience using Azure Logic Apps, Azure Event Hubs and Azure Key Vault.


    📚 Resources

    - - + + \ No newline at end of file diff --git a/blog/tags/azure-container-apps/page/5/index.html b/blog/tags/azure-container-apps/page/5/index.html index 423192d462..e64720865a 100644 --- a/blog/tags/azure-container-apps/page/5/index.html +++ b/blog/tags/azure-container-apps/page/5/index.html @@ -14,14 +14,14 @@ - - + +

    20 posts tagged with "azure-container-apps"

    View All Tags

    · 5 min read
    Mike Morton

    Welcome to Day 19 of #30DaysOfServerless!

    Today, we have a special set of posts from our Zero To Hero 🚀 initiative, featuring blog posts authored by our Product Engineering teams for #ServerlessSeptember. Posts were originally published on the Apps on Azure blog on Microsoft Tech Community.


    What We'll Cover

    • Log Streaming - in Azure Portal
    • Console Connect - in Azure Portal
    • Metrics - using Azure Monitor
    • Log Analytics - using Azure Monitor
    • Metric Alerts and Log Alerts - using Azure Monitor


    In past weeks, @kendallroden wrote about what it means to be Cloud-Native and @Anthony Chu the various ways to get your apps running on Azure Container Apps. Today, we will talk about the observability tools you can use to observe, debug, and diagnose your Azure Container Apps.

    Azure Container Apps provides several observability features to help you debug and diagnose your apps. There are both Azure portal and CLI options you can use to help understand the health of your apps and help identify when issues arise.

    While these features are helpful throughout your container app’s lifetime, there are two that are especially helpful. Log streaming and console connect can be a huge help in the initial stages when issues often rear their ugly head. Let's dig into both of these a little.

    Log Streaming

    Log streaming allows you to use the Azure portal to view the streaming logs from your app. You’ll see the logs written from the app to the container’s console (stderr and stdout). If your app is running multiple revisions, you can choose from which revision to view logs. You can also select a specific replica if your app is configured to scale. Lastly, you can choose from which container to view the log output. This is useful when you are running a custom or Dapr sidecar container. view streaming logs

    Here’s an example CLI command to view the logs of a container app.

    az containerapp logs show -n MyContainerapp -g MyResourceGroup

    You can find more information about the different options in our CLI docs.

    Console Connect

    In the Azure portal, you can connect to the console of a container in your app. Like log streaming, you can select the revision, replica, and container if applicable. After connecting to the console of the container, you can execute shell commands and utilities that you have installed in your container. You can view files and their contents, monitor processes, and perform other debugging tasks.

    This can be great for checking configuration files or even modifying a setting or library your container is using. Of course, updating a container in this fashion is not something you should do to a production app, but tweaking and re-testing an app in a non-production environment can speed up development.

    Here’s an example CLI command to connect to the console of a container app.

    az containerapp exec -n MyContainerapp -g MyResourceGroup

    You can find more information about the different options in our CLI docs.

    Metrics

    Azure Monitor collects metric data from your container app at regular intervals to help you gain insights into the performance and health of your container app. Container apps provide these metrics:

    • CPU usage
    • Memory working set bytes
    • Network in bytes
    • Network out bytes
    • Requests
    • Replica count
    • Replica restart count

    Here you can see the metrics explorer showing the replica count for an app as it scaled from one replica to fifteen, and then back down to one.

    You can also retrieve metric data through the Azure CLI.

    Log Analytics

    Azure Monitor Log Analytics is great for viewing your historical logs emitted from your container apps. There are two custom tables of interest, the ContainerAppConsoleLogs_CL which contains all the log messages written by your app (stdout and stderr), and the ContainerAppSystemLogs_CL which contain the system messages from the Azure Container Apps service.

    You can also query Log Analytics through the Azure CLI.

    Alerts

    Azure Monitor alerts notify you so that you can respond quickly to critical issues. There are two types of alerts that you can define:

    You can create alert rules from metric charts in the metric explorer and from queries in Log Analytics. You can also define and manage alerts from the Monitor|Alerts page.

    Here is what creating an alert looks like in the Azure portal. In this case we are setting an alert rule from the metric explorer to trigger an alert if the replica restart count for a specific container app is greater than two within the last fifteen minutes.

    To learn more about alerts, refer to Overview of alerts in Microsoft Azure.

    Conclusion

    In this article, we looked at the several ways to observe, debug, and diagnose your Azure Container Apps. As you can see there are rich portal tools and a complete set of CLI commands to use. All the tools are helpful throughout the lifecycle of your app, be sure to take advantage of them when having an issue and/or to prevent issues.

    To learn more, visit Azure Container Apps | Microsoft Azure today!

    ASK THE EXPERT: LIVE Q&A

    The Azure Container Apps team will answer questions live on September 29.

    - - + + \ No newline at end of file diff --git a/blog/tags/azure-container-apps/page/6/index.html b/blog/tags/azure-container-apps/page/6/index.html index 06ed27e1e1..d938f986ea 100644 --- a/blog/tags/azure-container-apps/page/6/index.html +++ b/blog/tags/azure-container-apps/page/6/index.html @@ -14,14 +14,14 @@ - - + +

    20 posts tagged with "azure-container-apps"

    View All Tags

    · 10 min read
    Brian Benz

    Welcome to Day 18 of #30DaysOfServerless!

    Yesterday my Serverless September post introduced you to making Azure Logic Apps and Azure Cosmos DB work together with a sample application that collects weather data. Today I'm sharing a more robust solution that actually reads my mail. Let's learn about Teaching the cloud to read your mail!

    Ready? Let's go!


    What We'll Cover

    • Introduction to the ReadMail solution
    • Setting up Azure storage, Cosmos DB and Computer Vision
    • Connecting it all together with a Logic App
    • Resources: For self-study!


    Introducing the ReadMail solution

    The US Postal system offers a subscription service that sends you images of mail it will be delivering to your home. I decided it would be cool to try getting Azure to collect data based on these images, so that I could categorize my mail and track the types of mail that I received.

    To do this, I used Azure storage, Cosmos DB, Logic Apps, and computer vision. When a new email comes in from the US Postal service (USPS), it triggers a logic app that:

    • Posts attachments to Azure storage
    • Triggers Azure Computer vision to perform an OCR function on attachments
    • Extracts any results into a JSON document
    • Writes the JSON document to Cosmos DB

    workflow for the readmail solution

    In this post I'll walk you through setting up the solution for yourself.

    Prerequisites

    Setup Azure Services

    First, we'll create all of the target environments we need to be used by our Logic App, then we;ll create the Logic App.

    1. Azure Storage

    We'll be using Azure storage to collect attached images from emails as they arrive. Adding images to Azure storage will also trigger a workflow that performs OCR on new attached images and stores the OCR data in Cosmos DB.

    To create a new Azure storage account from the portal dashboard, Select Create a resource > Storage account > Create.

    The Basics tab covers all of the features and information that we will need for this solution:

    SectionFieldRequired or optionalDescription
    Project detailsSubscriptionRequiredSelect the subscription for the new storage account.
    Project detailsResource groupRequiredCreate a new resource group that you will use for storage, Cosmos DB, Computer Vision and the Logic App.
    Instance detailsStorage account nameRequiredChoose a unique name for your storage account. Storage account names must be between 3 and 24 characters in length and may contain numbers and lowercase letters only.
    Instance detailsRegionRequiredSelect the appropriate region for your storage account.
    Instance detailsPerformanceRequiredSelect Standard performance for general-purpose v2 storage accounts (default).
    Instance detailsRedundancyRequiredSelect locally-redundant Storage (LRS) for this example.

    Select Review + create to accept the remaining default options, then validate and create the account.

    2. Azure CosmosDB

    CosmosDB will be used to store the JSON documents returned by the COmputer Vision OCR process.

    See more details and screen shots for setting up CosmosDB in yesterday's Serverless September post - Using Logic Apps with Cosmos DB

    To get started with Cosmos DB, you create an account, then a database, then a container to store JSON documents. To create a new Cosmos DB account from the portal dashboard, Select Create a resource > Azure Cosmos DB > Create. Choose core SQL for the API.

    Select your subscription, then for simplicity use the same resource group you created when you set up storage. Enter an account name and choose a location, select provisioned throughput capacity mode and apply the free tier discount. From here you can select Review and Create, then Create

    Next, create a new database and container. Go to the Data Explorer in your new Cosmos DB account, and choose New Container. Name the database, and keep all the other defaults except:

    SettingAction
    Container IDid
    Container partition/id

    Press OK to create a database and container

    3. Azure Computer Vision

    Azure Cognitive Services' Computer Vision will perform an OCR process on each image attachment that is stored in Azure storage.

    From the portal dashboard, Select Create a resource > AI + Machine Learning > Computer Vision > Create.

    The Basics and Identity tabs cover all of the features and information that we will need for this solution:

    Basics Tab

    SectionFieldRequired or optionalDescription
    Project detailsSubscriptionRequiredSelect the subscription for the new service.
    Project detailsResource groupRequiredUse the same resource group that you used for Azure storage and Cosmos DB.
    Instance detailsRegionRequiredSelect the appropriate region for your Computer Vision service.
    Instance detailsNameRequiredChoose a unique name for your Computer Vision service.
    Instance detailsPricingRequiredSelect the free tier for this example.

    Identity Tab

    SectionFieldRequired or optionalDescription
    System assigned managed identityStatusRequiredEnable system assigned identity to grant the resource access to other existing resources.

    Select Review + create to accept the remaining default options, then validate and create the account.


    Connect it all with a Logic App

    Now we're ready to put this all together in a Logic App workflow!

    1. Create Logic App

    From the portal dashboard, Select Create a resource > Integration > Logic App > Create. Name your Logic App and select a location, the rest of the settings can be left at their defaults.

    2. Create Workflow: Add Trigger

    Once the Logic App is created, select Create a workflow from designer.

    A workflow is a series of steps that defines a task or process. Each workflow starts with a single trigger, after which you must add one or more actions.

    When in designer, search for outlook.com on the right under Add a trigger. Choose outlook.com. Choose When a new email arrives as the trigger.

    A trigger is always the first step in any workflow and specifies the condition for running any further steps in that workflow.

    Set the following values:

    ParameterValue
    FolderInbox
    ImportanceAny
    Only With AttachmentsYes
    Include AttachmentsYes

    Then add a new parameter:

    ParameterValue
    FromAdd the email address that sends you the email with attachments
    3. Create Workflow: Add Action (for Trigger)

    Choose add an action and choose control > for-each.

    logic app for each

    Inside the for-each action, in Select an output from previous steps, choose attachments. Then, again inside the for-each action, add the create blob action:

    Set the following values:

    ParameterValue
    Folder Path/mailreaderinbox
    Blob NameAttachments Name
    Blob ContentAttachments Content

    This extracts attachments from the email and created a new blob for each attachment.

    Next, inside the same for-each action, add the get blob content action.

    Set the following values:

    ParameterValue
    Blobid
    Infer content typeYes

    We create and read from a blob for each attachment because Computer Vision needs a non-virtual source to read from when performing an OCR process. Because we enabled system assigned identity to grant Computer Vision to other existing resources, it can access the blob but not the outlook.com attachment. Also, we pass the ID of the blob to use as a unique ID when writing to Cosmos DB.

    create blob from attachments

    Next, inside the same for-each action, choose add an action and choose control > condition. Set the value to Media Type > is equal to > image/JPEG

    The USPS sends attachments of multiple types, but we only want to scan attachments that have images of our mail, which are always JPEG images. If the condition is true, we will process the image with Computer Vision OCR and write the results to a JSON document in CosmosDB.

    In the True section of the condition, add an action and choose Computer Vision API > Optical Character Recognition (OCR) to JSON.

    Set the following values:

    ParameterValue
    Image SourceImage Content
    Image contentFile Content

    In the same True section of the condition, choose add an action and choose Cosmos DB. Choose Create or Update Document from the actions. Select Access Key, and provide the primary read-write key (found under keys in Cosmos DB), and the Cosmos DB account ID (without 'documents.azure.com').

    Next, fill in your Cosmos DB Database ID and Collection ID. Create a JSON document by selecting dynamic content elements and wrapping JSON formatting around them.

    Be sure to use the ID passed from blob storage as your unique ID for CosmosDB. That way you can troubleshoot and JSON or OCR issues by tracing back the JSON document in Cosmos Db to the blob in Azure storage. Also, include the Computer Vision JSON response, as it contains the results of the Computer Vision OCR scan. all other elements are optional.

    4. TEST WORKFLOW

    When complete, you should have an action the Logic App designer that looks something like this:

    Logic App workflow create or update document in cosmosdb

    Save the workflow and test the connections by clicking Run Trigger > Run. If connections are working, you should see documents flowing into Cosmos DB each time that an email arrives with image attachments.

    Check the data in Cosmos Db by opening the Data explorer, then choosing the container you created and selecting items. You should see documents similar to this:

    Logic App workflow with trigger and action

    1. Congratulations

    You just built your personal ReadMail solution with Logic Apps! 🎉


    Resources: For self-study!

    Once you have an understanding of the basics in ths post, there is so much more to learn!

    Thanks for stopping by!

    - - + + \ No newline at end of file diff --git a/blog/tags/azure-container-apps/page/7/index.html b/blog/tags/azure-container-apps/page/7/index.html index d302510fdd..ba7efbd1ee 100644 --- a/blog/tags/azure-container-apps/page/7/index.html +++ b/blog/tags/azure-container-apps/page/7/index.html @@ -14,14 +14,14 @@ - - + +

    20 posts tagged with "azure-container-apps"

    View All Tags

    · 6 min read
    Brian Benz

    Welcome to Day 17 of #30DaysOfServerless!

    In past weeks, we've covered serverless technologies that provide core capabilities (functions, containers, microservices) for building serverless solutions. This week we're looking at technologies that make service integrations more seamless, starting with Logic Apps. Let's look at one usage example today!

    Ready? Let's Go!


    What We'll Cover

    • Introduction to Logic Apps
    • Settng up Cosmos DB for Logic Apps
    • Setting up a Logic App connection and event
    • Writing data to Cosmos DB from a Logic app
    • Resources: For self-study!


    Introduction to Logic Apps

    Previously in Serverless September, we've covered Azure Functions, where the event triggers code. In Logic Apps, the event triggers a workflow that you design. Logic Apps enable serverless applications to connect to external sources for data then automate business processes via workflows.

    In this post I'll walk you through setting up a Logic App that works with Cosmos DB. For this example, we'll connect to the MSN weather service, an design a logic app workflow that collects data when weather changes, and writes the data to Cosmos DB.

    PREREQUISITES

    Setup Cosmos DB for Logic Apps

    Cosmos DB has many APIs to choose from, but to use the default Logic App connection, we need to choose the a Cosmos DB SQL API. We'll set this up via the Azure Portal.

    To get started with Cosmos DB, you create an account, then a database, then a container to store JSON documents. To create a new Cosmos DB account from the portal dashboard, Select Create a resource > Azure Cosmos DB > Create. Choose core SQL for the API.

    Select your subscription, then create a new resource group called CosmosWeather. Enter an account name and choose a location, select provisioned throughput capacity mode and apply the free tier discount. From here you can select Review and Create, then Create

    Azure Cosmos DB is available in two different capacity modes: provisioned throughput and serverless. You can perform the same database operations in both modes, but the way you get billed for these operations is different. We wil be using provisioned throughput and the free tier for this example.

    Setup the CosmosDB account

    Next, create a new database and container. Go to the Data Explorer in your new Cosmos DB account, and choose New Container. Name the database, and keep all the orher defaults except:

    SettingAction
    Container IDid
    Container partition/id

    Press OK to create a database and container

    A database is analogous to a traditional DBMS namespace. It's used to organize one or more containers.

    Setup the CosmosDB Container

    Now we're ready to set up our logic app an write to Cosmos DB!

    Setup Logic App connection + event

    Once the Cosmos DB SQL API account is created, we can set up our Logic App. From the portal dashboard, Select Create a resource > Integration > Logic App > Create. Name your Logic App and select a location, the rest fo the settings can be left at their defaults. Once you new Logic App is created, select Create a workflow from designer to get started.

    A workflow is a series of steps that defines a task or process. Each workflow starts with a single trigger, after which you must add one or more actions.

    When in designer, search for weather on the right under Add a trigger. Choose MSN Weather. Choose When the current conditions change as the trigger.

    A trigger is always the first step in any workflow and specifies the condition for running any further steps in that workflow.

    Add a location. Valid locations are City, Region, State, Country, Landmark, Postal Code, latitude and longitude. This triggers a new workflow when the conditions change for a location.

    Write data from Logic App to Cosmos DB

    Now we are ready to set up the action to write data to Cosmos DB. Choose add an action and choose Cosmos DB.

    An action is each step in a workflow after the trigger. Every action runs some operation in a workflow.

    In this case, we will be writing a JSON document to the Cosmos DB container we created earlier. Choose Create or Update Document from the actions. At this point you should have a workflow in designer that looks something like this:

    Logic App workflow with trigger

    Start wth the connection for set up the Cosmos DB action. Select Access Key, and provide the primary read-write key (found under keys in Cosmos DB), and the Cosmos DB account ID (without 'documents.azure.com').

    Next, fill in your Cosmos DB Database ID and Collection ID. Create a JSON document bt selecting dynamic content elements and wrapping JSON formatting around them.

    You will need a unique ID for each document that you write to Cosmos DB, for that you can use an expression. Because we declared id to be our unique ID in Cosmos DB, we will use use that for the name. Under expressions, type guid() and press enter to add a unique ID to the JSON document. When complete, you should have a workflow in designer that looks something like this:

    Logic App workflow with trigger and action

    Save the workflow and test the connections by clicking Run Trigger > Run. If connections are working, you should see documents flowing into Cosmos DB over the next few minutes.

    Check the data in Cosmos Db by opening the Data explorer, then choosing the container you created and selecting items. You should see documents similar to this:

    Logic App workflow with trigger and action

    Resources: For self-study!

    Once you've grasped the basics in ths post, there is so much more to learn!

    Thanks for stopping by!

    - - + + \ No newline at end of file diff --git a/blog/tags/azure-container-apps/page/8/index.html b/blog/tags/azure-container-apps/page/8/index.html index 930d61655e..35bdcf06f0 100644 --- a/blog/tags/azure-container-apps/page/8/index.html +++ b/blog/tags/azure-container-apps/page/8/index.html @@ -14,13 +14,13 @@ - - + +

    20 posts tagged with "azure-container-apps"

    View All Tags

    · 4 min read
    Nitya Narasimhan
    Devanshi Joshi

    Welcome to Day 15 of #30DaysOfServerless!

    This post marks the midpoint of our Serverless on Azure journey! Our Week 2 Roadmap showcased two key technologies - Azure Container Apps (ACA) and Dapr - for building serverless microservices. We'll also look at what happened elsewhere in #ServerlessSeptember, then set the stage for our next week's focus: Serverless Integrations.

    Ready? Let's Go!


    What We'll Cover

    • ICYMI: This Week on #ServerlessSeptember
    • Recap: Microservices, Azure Container Apps & Dapr
    • Coming Next: Serverless Integrations
    • Exercise: Take the Cloud Skills Challenge
    • Resources: For self-study!

    This Week In Events

    We had a number of activities happen this week - here's a quick summary:

    This Week in #30Days

    In our #30Days series we focused on Azure Container Apps and Dapr.

    • In Hello Container Apps we learned how Azure Container Apps helps you run microservices and containerized apps on serverless platforms. And we build and deployed our first ACA.
    • In Microservices Communication we explored concepts like environments and virtual networking, with a hands-on example to show how two microservices communicate in a deployed ACA.
    • In Scaling Your Container Apps we learned about KEDA (Kubernetes Event-Driven Autoscaler) and how to configure autoscaling for your ACA based on KEDA-supported triggers.
    • In Build with Dapr we introduced the Distributed Application Runtime (Dapr) and learned how its Building Block APIs and sidecar architecture make it easier to develop microservices with ACA.
    • In Secure ACA Access we learned how to secure ACA access to external services with - and without - Dapr, covering Secret Stores and Managed Identity.
    • Finally, Build ACA with Dapr tied it all together with a enterprise app scenario where an orders processor (ACA) uses Dapr APIs (PubSub, State Management) to receive and store order messages from Azure Service Bus.

    Here's a visual recap:

    Self Study: Code Samples & Tutorials

    There's no better way to get familiar with the concepts, than to dive in and play with code samples and hands-on tutorials. Here are 4 resources to bookmark and try out:

    1. Dapr Quickstarts - these walk you through samples showcasing individual Building Block APIs - with multiple language options available.
    2. Dapr Tutorials provides more complex examples of microservices applications and tools usage, including a Distributed Calculator polyglot app.
    3. Next, try to Deploy a Dapr application to Azure Container Apps to get familiar with the process of setting up the environment, then deploying the app.
    4. Or, explore the many Azure Container Apps samples showcasing various features and more complex architectures tied to real world scenarios.

    What's Next: Serverless Integrations!

    So far we've talked about core technologies (Azure Functions, Azure Container Apps, Dapr) that provide foundational support for your serverless solution. Next, we'll look at Serverless Integrations - specifically at technologies like Azure Logic Apps and Azure Event Grid that automate workflows and create seamless end-to-end solutions that integrate other Azure services in serverless-friendly ways.

    Take the Challenge!

    The Cloud Skills Challenge is still going on, and we've already had hundreds of participants join and complete the learning modules to skill up on Serverless.

    There's still time to join and get yourself on the leaderboard. Get familiar with Azure Functions, SignalR, Logic Apps, Azure SQL and more - in serverless contexts!!


    - - + + \ No newline at end of file diff --git a/blog/tags/azure-container-apps/page/9/index.html b/blog/tags/azure-container-apps/page/9/index.html index b71709b665..c8b597261e 100644 --- a/blog/tags/azure-container-apps/page/9/index.html +++ b/blog/tags/azure-container-apps/page/9/index.html @@ -14,8 +14,8 @@ - - + +
    @@ -24,7 +24,7 @@ Image showing container apps role assignment

  • Lastly, we need to restart the container app revision, to do so run the command below:

     ##Get revision name and assign it to a variable
    $REVISION_NAME = (az containerapp revision list `
    --name $BACKEND_SVC_NAME `
    --resource-group $RESOURCE_GROUP `
    --query [0].name)

    ##Restart revision by name
    az containerapp revision restart `
    --resource-group $RESOURCE_GROUP `
    --name $BACKEND_SVC_NAME `
    --revision $REVISION_NAME
  • Run end-to-end Test on Azure

    From the Azure Portal, select the Azure Container App orders-processor and navigate to Log stream under Monitoring tab, leave the stream connected and opened. From the Azure Portal, select the Azure Service Bus Namespace ordersservices, select the topic orderreceivedtopic, select the subscription named orders-processor-subscription, then click on Service Bus Explorer (preview). From there we need to publish/send a message. Use the JSON payload below

    ```json
    {
    "data": {
    "reference": "Order 150",
    "quantity": 150,
    "createdOn": "2022-05-10T12:45:22.0983978Z"
    }
    }
    ```

    If all is configured correctly, you should start seeing the information logs in Container Apps Log stream, similar to the images below Image showing publishing messages from Azure Service

    Information logs on the Log stream of the deployed Azure Container App Image showing ACA Log Stream

    🎉 CONGRATULATIONS

    You have successfully deployed to the cloud an Azure Container App and configured Dapr Pub/Sub API with Azure Service Bus.

    9. Clean up

    If you are done with the tutorial, use the following command to delete the resource group and all its contained resources to avoid incurring further costs.

    az group delete --name $RESOURCE_GROUP

    Exercise

    I left for you the configuration of the Dapr State Store API with Azure Cosmos DB :)

    When you look at the action method OrderReceived in controller ExternalOrdersController, you will see that I left a line with ToDo: note, this line is responsible to save the received message (OrderModel) into Azure Cosmos DB.

    There is no need to change anything on the code base (other than removing this commented line), that's the beauty of Dapr Building Blocks and how easy it allows us to plug components to our microservice application without any plumping and brining external SDKs.

    For sure you need to work on the configuration part of Dapr State Store by creating a new component file like what we have done with the Pub/Sub API, things that you need to work on are:

    • Provision Azure Cosmos DB Account and obtain its masterKey.
    • Create a Dapr Component file adhering to Dapr Specs.
    • Create an Azure Container Apps component file adhering to ACA component specs.
    • Test locally on your dev machine using Dapr Component file.
    • Register the new Dapr State Store component with Azure Container Apps Environment and set the Cosmos Db masterKey from the Azure Portal. If you want to challenge yourself more, use the Managed Identity approach as done in this post! The right way to protect your keys and you will not worry about managing CosmosDb keys anymore!
    • Build a new image of the application and push it to Azure Container Registry.
    • Update Azure Container Apps and create a new revision which contains the updated code.
    • Verify the results by checking Azure Cosmos DB, you should see the Order Model stored in Cosmos DB.

    If you need help, you can always refer to my blog post Azure Container Apps State Store With Dapr State Management API which contains exactly what you need to implement here, so I'm very confident you will be able to complete this exercise with no issues, happy coding :)

    What's Next?

    If you enjoyed working with Dapr and Azure Container Apps, and you want to have a deep dive with more complex scenarios (Dapr bindings, service discovery, auto scaling with KEDA, sync services communication, distributed tracing, health probes, etc...) where multiple services deployed to a single Container App Environment; I have created a detailed tutorial which should walk you through step by step with through details to build the application.

    So far, the published posts below, and I'm publishing more posts on weekly basis, so stay tuned :)

    Resources

    - - + + \ No newline at end of file diff --git a/blog/tags/azure-developer-cli/index.html b/blog/tags/azure-developer-cli/index.html index 4e0f4dc138..54cfc84142 100644 --- a/blog/tags/azure-developer-cli/index.html +++ b/blog/tags/azure-developer-cli/index.html @@ -14,13 +14,13 @@ - - + +

    2 posts tagged with "azure-developer-cli"

    View All Tags

    · 5 min read
    Savannah Ostrowski

    Welcome to Beyond #30DaysOfServerless! in October!

    Yes, it's October!! And since we ended #ServerlessSeptember with a focus on End-to-End Development for Serverless on Azure, we thought it would be good to share updates in October that can help you skill up even further.

    Today, we're following up on the Code to Cloud with azd blog post (Day #29) where we introduced the Azure Developer CLI (azd), an open-source tool for streamlining your end-to-end developer experience going from local development environment to Azure cloud. In today's post, we celebrate the October 2022 release of the tool, with three cool new features.

    And if it's October, it must be #Hacktoberfest!! Read on to learn about how you can take advantage of one of the new features, to contribute to the azd open-source community and ecosystem!

    Ready? Let's go!


    What We'll Cover

    • Azure Friday: Introducing the Azure Developer CLI (Video)
    • October 2022 Release: What's New in the Azure Developer CLI?
      • Azure Pipelines for CI/CD: Learn more
      • Improved Infrastructure as Code structure via Bicep modules: Learn more
      • A new azd template gallery: The new azd-templates gallery for community use! Learn more
    • Awesome-Azd: The new azd-templates gallery for Community use
      • Features: discover, create, contribute, request - templates
      • Hacktoberfest: opportunities to contribute in October - and beyond!


    Azure Friday

    This post is a follow-up to our #ServerlessSeptember post on Code to Cloud with Azure Developer CLI where we introduced azd, a new open-source tool that makes it quick and simple for you to move your application from a local development environment to Azure, streamlining your end-to-end developer workflow in the process.

    Prefer to watch a video overview? I have you covered! Check out my recent conversation with Scott Hanselman on Azure Friday where we:

    • talked about the code-to-cloud developer journey
    • walkthrough the ins and outs of an azd template
    • explored Azure Developer CLI commands in the terminal and VS Code, and
    • (probably most importantly) got a web app up and running on Azure with a database, Key Vault and monitoring all in a couple of minutes

    October Release

    We're pleased to announce the October 2022 release of the Azure Developer CLI (currently 0.3.0-beta.2). Read the release announcement for more details. Here are the highlights:

    • Azure Pipelines for CI/CD: This addresses azure-dev#101, adding support for Azure Pipelines (alongside GitHub Actions) as a CI/CD provider. Learn more about usage and related documentation.
    • Improved Infrastructure as Code structure via Bicep modules: This addresses azure-dev#543, which recognized the complexity of using a single resources.bicep file for all resources. With this release, azd templates now come with Bicep modules organized by purpose making it easier to edit and understand. Learn more about this structure, and how to use it.
    • New Templates Gallery - awesome-azd: This addresses azure-dev#398, which aimed to make templates more discoverable and easier to contribute. Learn more about how the new gallery improves the template discovery experience.

    In the next section, we'll dive briefly into the last feature, introducing the new awesome-azd site and resource for templates discovery and contribution. And, since it's #Hacktoberfest season, we'll talk about the Contributor Guide and the many ways you can contribute to this project - with, or without, code.


    It's awesome-azd

    Welcome to awesome-azd a new template gallery hosted on GitHub Pages, and meant to be a destination site for discovering, requesting, and contributing azd-templates for community use!

    In addition, it's README reflects the awesome-list resource format, providing a location for the community to share "best of" resources for Azure Developer CLI - from blog posts and videos, to full-scale tutorials and templates.

    The Gallery is organized into three main areas:

    Take a minute to explore the Gallery and note the features:

    • Search for templates by name
    • Requested Templates - indicating asks from the community
    • Featured Templates - highlighting high-quality templates
    • Filters - to discover templates by and/or query combinations

    Check back often to see the latest contributed templates and requests!


    Hacktoberfest

    So, why is this a good time to talk about the Gallery? Because October means it's time for #Hacktoberfest - a month-long celebration of open-source projects and their maintainers, and an opportunity for first-time contributors to get support and guidance making their first pull-requests! Check out the #Hacktoberfest topic on GitHub for projects you can contribute to.

    And we hope you think of awesome-azd as another possible project to contribute to.

    Check out the FAQ section to learn how to create, discover, and contribute templates. Or take a couple of minutes to watch this video walkthrough from Jon Gallant:

    And don't hesitate to reach out to us - either via Issues on the repo, or in the Discussions section of this site, to give us feedback!

    Happy Hacking! 🎃


    - - + + \ No newline at end of file diff --git a/blog/tags/azure-developer-cli/page/2/index.html b/blog/tags/azure-developer-cli/page/2/index.html index 4301038b25..6b0c22a63d 100644 --- a/blog/tags/azure-developer-cli/page/2/index.html +++ b/blog/tags/azure-developer-cli/page/2/index.html @@ -14,8 +14,8 @@ - - + +
    @@ -26,7 +26,7 @@

    ...and that's it! We've successfully deployed our application on Azure!

    But there's more!

    Best practices: Monitoring and CI/CD!

    In my opinion, it's not enough to just set up the application on Azure! I want to know that my web app is performant and serving my users reliably! I also want to make sure that I'm not inadvertently breaking my application as I continue to make changes to it. Thankfully, the Azure Developer CLI also handles all of this via two additional commands - azd monitor and azd pipeline config.

    Application Monitoring

    When we provisioned all of our infrastructure, we also set up application monitoring via a Bicep file in our .infra/ directory that spec'd out an Application Insights dashboard. By running azd monitor we can see the dashboard with live metrics that was configured for the application.

    We can also navigate to the Application Dashboard by clicking on the resource group name, where you can set a specific refresh rate for the dashboard, and see usage, reliability, and performance metrics over time.

    I don't know about everyone else but I have spent a ton of time building out similar dashboards. It can be super time-consuming to write all the queries and create the visualizations so this feels like a real time saver.

    CI/CD

    Finally let's talk about setting up CI/CD! This might be my favorite azd feature. As I mentioned before, the Azure Developer CLI has a command, azd pipeline config, which uses the files in the .github/ directory to set up a GitHub Action. More than that, if there is no upstream repo, the Developer CLI will actually help you create one. But what does this mean exactly? Because our GitHub Action is using the same commands you'd run in the CLI under the hood, we're actually going to have CI/CD set up to run on every commit into the repo, against real Azure resources. What a sweet collaboration feature!

    That's it! We've gone end-to-end with the Azure Developer CLI - initialized a project, provisioned the resources on Azure, deployed our code on Azure, set up monitoring logs and dashboards, and set up a CI/CD pipeline with GitHub Actions to run on every commit into the repo (on real Azure resources!).

    Exercise: Try it yourself or create your own template!

    As an exercise, try out the workflow above with any template on GitHub!

    Or, try turning your own project into an Azure Developer CLI-enabled template by following this guidance. If you create your own template, don't forget to tag the repo with the azd-templates topic on GitHub to help others find it (unfamiliar with GitHub topics? Learn how to add topics to your repo)! We'd also love to chat with you about your experience creating an azd template - if you're open to providing feedback around this, please fill out this form!

    Resources

    - - + + \ No newline at end of file diff --git a/blog/tags/azure-event-grid/index.html b/blog/tags/azure-event-grid/index.html index f0ee41e863..84b11cb9cd 100644 --- a/blog/tags/azure-event-grid/index.html +++ b/blog/tags/azure-event-grid/index.html @@ -14,14 +14,14 @@ - - + +

    3 posts tagged with "azure-event-grid"

    View All Tags

    · 7 min read
    Devanshi Joshi

    It's Serverless September in a Nutshell! Join us as we unpack our month-long learning journey exploring the core technology pillars for Serverless architectures on Azure. Then end with a look at next steps to build your Cloud-native applications on Azure.


    What We'll Cover

    • Functions-as-a-Service (FaaS)
    • Microservices and Containers
    • Serverless Integrations
    • End-to-End Solutions
    • Developer Tools & #Hacktoberfest

    Banner for Serverless September


    Building Cloud-native Apps

    By definition, cloud-native technologies empower organizations to build and run scalable applications in modern, dynamic environments such as public, private, and hybrid clouds. You can learn more about cloud-native in Kendall Roden's #ServerlessSeptember post on Going Cloud-native with Azure Container Apps.

    Serveless technologies accelerate productivity and minimize costs for deploying applications at cloud scale. So, what can we build with serverless technologies in cloud-native on Azure? Anything that is event-driven - examples include:

    • Microservices - scaled by KEDA-compliant triggers
    • Public API Endpoints - scaled by #concurrent HTTP requests
    • Event-Driven Applications - scaled by length of message queue
    • Web Applications - scaled by #concurrent HTTP requests
    • Background Process - scaled by CPU and Memory usage

    Great - but as developers, we really want to know how we can get started building and deploying serverless solutions on Azure. That was the focus of our #ServerlessSeptember journey. Let's take a quick look at the four key themes.

    Functions-as-a-Service (FaaS)

    Functions-as-a-Service (FaaS) is the epitome of developer productivity for full-stack modern apps. As developers, you don't manage infrastructure and focus only on business logic and application code. And, with Serverless Compute you only pay for when your code runs - making this the simplest first step to begin migrating your application to cloud-native.

    In Azure, FaaS is provided by Azure Functions. Check out our Functions + Serverless on Azure to go from learning core concepts, to building your first Functions app in your programming language of choice. Azure functions support multiple programming languages including C#, F#, Java, JavaScript, Python, Typescript, and PowerShell.

    Want to get extended language support for languages like Go, and Rust? You can Use Custom Handlers to make this happen! But what if you want to have long-running functions, or create complex workflows involving more than one function? Read our post on Durable Entities to learn how you can orchestrate this with Azure Functions.

    Check out this recent AskTheExpert Q&A session with the Azure Functions team to get answers to popular community questions on Azure Functions features and usage.

    Microservices and Containers

    Functions-as-a-Service is an ideal first step towards serverless development. But Functions are just one of the 5 pillars of cloud-native. This week we'll look at two of the other pillars: microservices and containers - with specific focus on two core technologies: Azure Container Apps and Dapr (Distributed Application Runtime).

    In this 6-part series of posts, we walk through each technology independently, before looking at the value of building Azure Container Apps with Dapr.

    • In Hello Container Apps we learned core concepts & deployed our first ACA.
    • In Microservices Communication we learned about ACA environments and virtual networks, and how microservices communicate in ACA with a hands-on tutorial.
    • In Scaling Your Container Apps we learned about KEDA (Kubernetes Event-Driven Autoscaler) and configuring ACA for autoscaling with KEDA-compliant triggers.
    • In Build with Dapr we introduced the Distributed Application Runtime (Dapr), exploring its Building Block APIs and sidecar architecture for working with ACA.
    • In Secure ACA Access we learned how to secure ACA access to external services with - and without - Dapr, covering Secret Stores and Managed Identity.
    • Finally, Build ACA with Dapr tied it all together with a enterprise app scenario where an orders processor (ACA) uses Dapr APIs (PubSub, State Management) to receive and store order messages from Azure Service Bus.

    Build ACA with Dapr

    Check out this recent AskTheExpert Q&A session with the Azure Container Apps team for answers to popular community questions on core features and usage.

    Serverless Integrations

    In the first half of the month we looked at compute resources for building and deploying serverless applications. In the second half, we look at integration tools and resources that automate developer workflows to streamline the end-to-end developer experience.

    In Azure, this is enabled by services like Azure Logic Apps and Azure Event Grid. Azure Logic Apps provides a visual designer to create and automate workflows with little or no code involved. Azure Event Grid provides a highly-scable event broker with support for pub/sub communications to drive async event-driven architectures.

    • In Tracking Weather Data Changes With Logic Apps we look at how you can use Logic Apps to integrate the MSN weather service with Azure CosmosDB, allowing automated collection of weather data on changes.

    • In Teach the Cloud to Read & Categorize Mail we take it a step further, using Logic Apps to automate a workflow that includes a Computer Vision service to "read" images and store the results to CosmosDB.

    • In Integrate with Microsoft Graph we explore a multi-cloud scenario (Azure + M365) where change notifications from Microsoft Graph can be integrated using Logic Apps and Event Hubs to power an onboarding workflow.

    • In Cloud Events with Event Grid we learn about the CloudEvents specification (for consistently describing event data) - and learn how Event Grid brokers events in this format. Azure Logic Apps can be an Event handler (subscriber) that uses the event to trigger an automated workflow on receipt.

      Azure Event Grid And Logic Apps

    Want to explore other such integrations? Browse Azure Architectures and filter by selected Azure services for more real-world scenarios.


    End-to-End Solutions

    We've covered serverless compute solutions (for building your serverless applications) and serverless integration services to automate end-to-end workflows in synchronous or asynchronous event-driven architectures. In this final week, we want to leave you with a sense of end-to-end development tools and use cases that can be enabled by Serverless on Azure. Here are some key examples:

    ArticleDescription
    In this tutorial, you'll learn to deploy a containerized ASP.NET Core 6.0 application to Azure Container Apps - with a Blazor front-end and two Web API projects
    Deploy Java containers to cloudIn this tutorial you learn to build and deploy a Java application running on Spring Boot, by publishing it in a container to Azure Container Registry, then deploying to Azure Container Apps,, from ACR, via the Azure Portal.
    **Where am I? My GPS Location with Serverless Power Platform Custom Connector**In this step-by-step tutorial you learn to integrate a serverless application (built on Azure Functions and OpenAPI) with Power Platforms custom connectors via Azure API Management (API-M).This pattern can empower a new ecosystem of fusion apps for cases like inventory management.
    And in our Serverless Hacks initiative, we walked through an 8-step hack to build a serverless tollbooth. Check out this 12-part video walkthrough of a reference solution using .NET.

    Developer Tools

    But wait - there's more. Those are a sample of the end-to-end application scenarios that are built on serverless on Azure. But what about the developer experience? In this article, we say hello to the Azure Developer CLI - an open-source tool that streamlines your develop-deploy workflow, with simple commands that map to core stages of your development journey. Go from code to cloud with one CLI

    And watch this space for more such tutorials and content through October, including a special #Hacktoberfest focused initiative to encourage and support first-time contributors to open-source. Here's a sneak peek at the project we plan to share - the new awesome-azd templates gallery.


    Join us at Microsoft Ignite!

    Want to continue your learning journey, and learn about what's next for Serverless on Azure? Microsoft Ignite happens Oct 12-14 this year and has multiple sessions on relevant technologies and tools. Check out the Session Catalog and register here to attend online.

    - - + + \ No newline at end of file diff --git a/blog/tags/azure-event-grid/page/2/index.html b/blog/tags/azure-event-grid/page/2/index.html index c4c774aebd..9e57443b80 100644 --- a/blog/tags/azure-event-grid/page/2/index.html +++ b/blog/tags/azure-event-grid/page/2/index.html @@ -14,13 +14,13 @@ - - + +

    3 posts tagged with "azure-event-grid"

    View All Tags

    · 9 min read
    Justin Yoo

    Welcome to Day 21 of #30DaysOfServerless!

    We've so far walked through what Azure Event Grid is and how it generally works. Today, let's discuss how Azure Event Grid deals with CloudEvents.


    What We'll Cover


    OK. Let's get started!

    What is CloudEvents?

    Needless to say, events are everywhere. Events come not only from event-driven systems but also from many different systems and devices, including IoT ones like Raspberry PI.

    But the problem is that every event publisher (system/device that creates events) describes their events differently, meaning there is no standard way of describing events. It has caused many issues between systems, mainly from the interoperability perspective.

    1. Consistency: No standard way of describing events resulted in developers having to write their own event handling logic for each event source.
    2. Accessibility: There were no common libraries, tooling and infrastructure to deliver events across systems.
    3. Productivity: The overall productivity decreases because of the lack of the standard format of events.

    Cloud Events Logo

    Therefore, CNCF (Cloud-Native Computing Foundation) has brought up the concept, called CloudEvents. CloudEvents is a specification that commonly describes event data. Conforming any event data to this spec will simplify the event declaration and delivery across systems and platforms and more, resulting in a huge productivity increase.

    How Azure Event Grid brokers CloudEvents

    Before CloudEvents, Azure Event Grid described events in their own way. Therefore, if you want to use Azure Event Grid, you should follow the event format/schema that Azure Event Grid declares. However, not every system/service/application follows the Azure Event Grid schema. Therefore, Azure Event Grid now supports CloudEvents spec as input and output formats.

    Azure Event Grid for Azure

    Take a look at the simple diagram below, which describes how Azure Event Grid captures events raised from various Azure services. In this diagram, Azure Key Vault takes the role of the event source or event publisher, and Azure Logic Apps takes the role of the event handler (I'll discuss Azure Logic Apps as the event handler later in this post). We use Azure Event Grid System Topic for Azure.

    Azure Event Grid for Azure

    Therefore, let's create an Azure Event Grid System Topic that captures events raised from Azure Key Vault when a new version of a secret is added.

    Azure Event Grid System Topic for Key Vault

    As Azure Event Grid makes use of the pub/sub pattern, you need to create the Azure Event Grid Subscription to consume the events. Here's the subscription that uses the Event Grid data format:

    ![Azure Event Grid System Subscription for Key Vault in Event Grid Format][./img/21-cloudevents-via-event-grid-03.png]

    Once you create the subscription, create a new version of the secret on Azure Key Vault. Then, Azure Key Vault raises an event, which is captured in the Event Grid format:

    [
    {
    "id": "6f44b9c0-d37e-40e7-89be-f70a6da291cc",
    "topic": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/rg-aegce-krc/providers/Microsoft.KeyVault/vaults/kv-xxxxxxxx",
    "subject": "hello",
    "eventType": "Microsoft.KeyVault.SecretNewVersionCreated",
    "data": {
    "Id": "https://kv-xxxxxxxx.vault.azure.net/secrets/hello/064dfc082fec463f8d4610ed6118811d",
    "VaultName": "kv-xxxxxxxx",
    "ObjectType": "Secret",
    "ObjectName": "hello",
    "Version": "064dfc082fec463f8d4610ed6118811d",
    "NBF": null,
    "EXP": null
    },
    "dataVersion": "1",
    "metadataVersion": "1",
    "eventTime": "2022-09-21T07:08:09.1234567Z"
    }
    ]

    So, how is it different from the CloudEvents format? Let's take a look. According to the spec, the JSON data in CloudEvents might look like this:

    {
    "id" : "C234-1234-1234",
    "source" : "/mycontext",
    "specversion" : "1.0",
    "type" : "com.example.someevent",
    "comexampleextension1" : "value",
    "time" : "2018-04-05T17:31:00Z",
    "datacontenttype" : "application/cloudevents+json",
    "data" : {
    "appinfoA" : "abc",
    "appinfoB" : 123,
    "appinfoC" : true
    }
    }

    This time, let's create another subscription using the CloudEvents schema. Here's how to create the subscription against the system topic:

    Azure Event Grid System Subscription for Key Vault in CloudEvents Format

    Therefore, Azure Key Vault emits the event data in the CloudEvents format:

    {
    "id": "6f44b9c0-d37e-40e7-89be-f70a6da291cc",
    "source": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/rg-aegce-krc/providers/Microsoft.KeyVault/vaults/kv-xxxxxxxx",
    "specversion": "1.0",
    "type": "Microsoft.KeyVault.SecretNewVersionCreated",
    "subject": "hello",
    "time": "2022-09-21T07:08:09.1234567Z",
    "data": {
    "Id": "https://kv-xxxxxxxx.vault.azure.net/secrets/hello/064dfc082fec463f8d4610ed6118811d",
    "VaultName": "kv-xxxxxxxx",
    "ObjectType": "Secret",
    "ObjectName": "hello",
    "Version": "064dfc082fec463f8d4610ed6118811d",
    "NBF": null,
    "EXP": null
    }
    }

    Can you identify some differences between the Event Grid format and the CloudEvents format? Fortunately, both Event Grid schema and CloudEvents schema look similar to each other. But they might be significantly different if you use a different event source outside Azure.

    Azure Event Grid for Systems outside Azure

    As mentioned above, the event data described outside Azure or your own applications within Azure might not be understandable by Azure Event Grid. In this case, we need to use Azure Event Grid Custom Topic. Here's the diagram for it:

    Azure Event Grid for Applications outside Azure

    Let's create the Azure Event Grid Custom Topic. When you create the topic, make sure that you use the CloudEvent schema during the provisioning process:

    Azure Event Grid Custom Topic

    If your application needs to publish events to Azure Event Grid Custom Topic, your application should build the event data in the CloudEvents format. If you use a .NET application, add the NuGet package first.

    dotnet add package Azure.Messaging.EventGrid

    Then, create the publisher instance. You've already got the topic endpoint URL and the access key.

    var topicEndpoint = new Uri("<Azure Event Grid Custom Topic Endpoint URL>");
    var credential = new AzureKeyCredential("<Azure Event Grid Custom Topic Access Key>");
    var publisher = new EventGridPublisherClient(topicEndpoint, credential);

    Now, build the event data like below. Make sure that you follow the CloudEvents schema that requires additional metadata like event source, event type and content type.

    var source = "/your/event/source";
    var type = "com.source.event.your/OnEventOccurs";

    var data = new MyEventData() { Hello = "World" };

    var @event = new CloudEvent(source, type, data);

    And finally, send the event to Azure Event Grid Custom Topic.

    await publisher.SendEventAsync(@event);

    The captured event data looks like the following:

    {
    "id": "cc2b2775-52b8-43b8-a7cc-c1c33c2b2e59",
    "source": "/your/event/source",
    "type": "com.source.event.my/OnEventOccurs",
    "data": {
    "Hello": "World"
    },
    "time": "2022-09-21T07:08:09.1234567+00:00",
    "specversion": "1.0"
    }

    However, due to limitations, someone might insist that their existing application doesn't or can't emit the event data in the CloudEvents format. In this case, what should we do? There's no standard way of sending the event data in the CloudEvents format to Azure Event Grid Custom Topic. One of the approaches we may be able to apply is to put a converter between the existing application and Azure Event Grid Custom Topic like below:

    Azure Event Grid for Applications outside Azure with Converter

    Once the Function app (or any converter app) receives legacy event data, it internally converts the CloudEvents format and publishes it to Azure Event Grid.

    var data = default(MyRequestData);
    using (var reader = new StreamReader(req.Body))
    {
    var serialised = await reader.ReadToEndAsync();
    data = JsonConvert.DeserializeObject<MyRequestData>(serialised);
    }

    var converted = new MyEventData() { Hello = data.Lorem };
    var @event = new CloudEvent(source, type, converted);

    The converted event data is captured like this:

    {
    "id": "df296da3-77cd-4da2-8122-91f631941610",
    "source": "/your/event/source",
    "type": "com.source.event.my/OnEventOccurs",
    "data": {
    "Hello": "ipsum"
    },
    "time": "2022-09-21T07:08:09.1234567+00:00",
    "specversion": "1.0"
    }

    This approach is beneficial in many integration scenarios to make all the event data canonicalised.

    How Azure Logic Apps consumes CloudEvents

    I put Azure Logic Apps as the event handler in the previous diagrams. According to the CloudEvents spec, each event handler must implement request validation to avoid abuse. One good thing about using Azure Logic Apps is that it has already implemented this request validation feature. It implies that we just subscribe to the topic and consume the event data.

    Create a new Logic Apps instance and add the HTTP Request trigger. Once it saves, you will get the endpoint URL.

    Azure Logic Apps with HTTP Request Trigger

    Then, create the Azure Event Grid Subscription with:

    • Endpoint type: Webhook
    • Endpoint URL: The Logic Apps URL from above.

    Azure Logic Apps with HTTP Request Trigger

    Once the subscription is ready, this Logic Apps works well as the event handler. Here's how it receives the CloudEvents data from the subscription.

    Azure Logic Apps that Received CloudEvents data

    Now you've got the CloudEvents data. It's entirely up to you to handle that event data however you want!

    Exercise: Try this yourself!

    You can fork this GitHub repository to your account and play around with it to see how Azure Event Grid with CloudEvents works. Alternatively, the "Deploy to Azure" button below will provision all necessary Azure resources and deploy an Azure Functions app to mimic the event publisher.

    Deploy To Azure

    Resources: For self-study!

    Want to know more about CloudEvents in real-life examples? Here are several resources you can take a look at:

    - - + + \ No newline at end of file diff --git a/blog/tags/azure-event-grid/page/3/index.html b/blog/tags/azure-event-grid/page/3/index.html index 3b12b3c44a..d937344bc9 100644 --- a/blog/tags/azure-event-grid/page/3/index.html +++ b/blog/tags/azure-event-grid/page/3/index.html @@ -14,13 +14,13 @@ - - + +

    3 posts tagged with "azure-event-grid"

    View All Tags

    · 5 min read
    Nitya Narasimhan
    Devanshi Joshi

    SEP 08: CHANGE IN PUBLISHING SCHEDULE

    Starting from Week 2 (Sep 8), we'll be publishing blog posts in batches rather than on a daily basis, so you can read a series of related posts together. Don't want to miss updates? Just subscribe to the feed


    Welcome to Day 8 of #30DaysOfServerless!

    This marks the end of our Week 1 Roadmap focused on Azure Functions!! Today, we'll do a quick recap of all #ServerlessSeptember activities in Week 1, set the stage for Week 2 - and leave you with some excellent tutorials you should explore to build more advanced scenarios with Azure Functions.

    Ready? Let's go.


    What We'll Cover

    • Azure Functions: Week 1 Recap
    • Advanced Functions: Explore Samples
    • End-to-End: Serverless Hacks & Cloud Skills
    • What's Next: Hello, Containers & Microservices
    • Challenge: Complete the Learning Path


    Week 1 Recap: #30Days & Functions

    Congratulations!! We made it to the end of Week 1 of #ServerlessSeptember. Let's recap what we learned so far:

    • In Core Concepts we looked at where Azure Functions fits into the serverless options available on Azure. And we learned about key concepts like Triggers, Bindings, Custom Handlers and Durable Functions.
    • In Build Your First Function we looked at the tooling options for creating Functions apps, testing them locally, and deploying them to Azure - as we built and deployed our first Functions app.
    • In the next 4 posts, we explored new Triggers, Integrations, and Scenarios - as we looked at building Functions Apps in Java, JavaScript, .NET and Python.
    • And in the Zero-To-Hero series, we learned about Durable Entities - and how we can use them to create stateful serverless solutions using a Chirper Sample as an example scenario.

    The illustrated roadmap below summarizes what we covered each day this week, as we bring our Functions-as-a-Service exploration to a close.


    Advanced Functions: Code Samples

    So, now that we've got our first Functions app under our belt, and validated our local development setup for tooling, where can we go next? A good next step is to explore different triggers and bindings, that drive richer end-to-end scenarios. For example:

    • Integrate Functions with Azure Logic Apps - we'll discuss Azure Logic Apps in Week 3. For now, think of it as a workflow automation tool that lets you integrate seamlessly with other supported Azure services to drive an end-to-end scenario. In this tutorial, we set up a workflow connecting Twitter (get tweet) to Azure Cognitive Services (analyze sentiment) - and use that to trigger an Azure Functions app to send email about the result.
    • Integrate Functions with Event Grid - we'll discuss Azure Event Grid in Week 3. For now, think of it as an eventing service connecting event sources (publishers) to event handlers (subscribers) at cloud scale. In this tutorial, we handle a common use case - a workflow where loading an image to Blob Storage triggers an Azure Functions app that implements a resize function, helping automatically generate thumbnails for the uploaded image.
    • Integrate Functions with CosmosDB and SignalR to bring real-time push-based notifications to your web app. It achieves this by using a Functions app that is triggered by changes in a CosmosDB backend, causing it to broadcast that update (push notification to connected web clients over SignalR, in real time.

    Want more ideas? Check out the Azure Samples for Functions for implementations, and browse the Azure Architecture Center for reference architectures from real-world scenarios that involve Azure Functions usage.


    E2E Scenarios: Hacks & Cloud Skills

    Want to systematically work your way through a single End-to-End scenario involving Azure Functions alongside other serverless support technologies? Check out the Serverless Hacks activity happening during #ServerlessSeptember, and learn to build this "Serverless Tollbooth Application" in a series of 10 challenges. Check out the video series for a reference solution in .NET and sign up for weekly office hours to join peers and discuss your solutions or challenges.

    Or perhaps you prefer to learn core concepts with code in a structured learning path? We have that covered. Check out the 12-module "Create Serverless Applications" course from Microsoft Learn which walks your through concepts, one at a time, with code. Even better - sign up for the free Cloud Skills Challenge and complete the same path (in under 30 days) but this time, with the added fun of competing against your peers for a spot on a leaderboard, and swag.


    What's Next? Hello, Cloud-Native!

    So where to next? In Week 2 we turn our attention from Functions-as-a-Service to building more complex backends using Containers and Microservices. We'll focus on two core technologies - Azure Container Apps and Dapr (Distributed Application Runtime) - both key components of a broader vision around Building Cloud-Native Applications in Azure.

    What is Cloud-Native you ask?

    Fortunately for you, we have an excellent introduction in our Zero-to-Hero article on Go Cloud-Native with Azure Container Apps - that explains the 5 pillars of Cloud-Native and highlights the value of Azure Container Apps (scenarios) and Dapr (sidecar architecture) for simplified microservices-based solution with auto-scale capability. Prefer a visual summary? Here's an illustrate guide to that article for convenience.

    Go Cloud-Native Download a higher resolution version of the image


    Take The Challenge

    We typically end each post with an exercise or activity to reinforce what you learned. For Week 1, we encourage you to take the Cloud Skills Challenge and work your way through at least a subset of the modules, for hands-on experience with the different Azure Functions concepts, integrations, and usage.

    See you in Week 2!

    - - + + \ No newline at end of file diff --git a/blog/tags/azure-functions/index.html b/blog/tags/azure-functions/index.html index de27eb4eda..de4c7310a4 100644 --- a/blog/tags/azure-functions/index.html +++ b/blog/tags/azure-functions/index.html @@ -14,14 +14,14 @@ - - + +

    16 posts tagged with "azure-functions"

    View All Tags

    · 7 min read
    Devanshi Joshi

    It's Serverless September in a Nutshell! Join us as we unpack our month-long learning journey exploring the core technology pillars for Serverless architectures on Azure. Then end with a look at next steps to build your Cloud-native applications on Azure.


    What We'll Cover

    • Functions-as-a-Service (FaaS)
    • Microservices and Containers
    • Serverless Integrations
    • End-to-End Solutions
    • Developer Tools & #Hacktoberfest

    Banner for Serverless September


    Building Cloud-native Apps

    By definition, cloud-native technologies empower organizations to build and run scalable applications in modern, dynamic environments such as public, private, and hybrid clouds. You can learn more about cloud-native in Kendall Roden's #ServerlessSeptember post on Going Cloud-native with Azure Container Apps.

    Serveless technologies accelerate productivity and minimize costs for deploying applications at cloud scale. So, what can we build with serverless technologies in cloud-native on Azure? Anything that is event-driven - examples include:

    • Microservices - scaled by KEDA-compliant triggers
    • Public API Endpoints - scaled by #concurrent HTTP requests
    • Event-Driven Applications - scaled by length of message queue
    • Web Applications - scaled by #concurrent HTTP requests
    • Background Process - scaled by CPU and Memory usage

    Great - but as developers, we really want to know how we can get started building and deploying serverless solutions on Azure. That was the focus of our #ServerlessSeptember journey. Let's take a quick look at the four key themes.

    Functions-as-a-Service (FaaS)

    Functions-as-a-Service (FaaS) is the epitome of developer productivity for full-stack modern apps. As developers, you don't manage infrastructure and focus only on business logic and application code. And, with Serverless Compute you only pay for when your code runs - making this the simplest first step to begin migrating your application to cloud-native.

    In Azure, FaaS is provided by Azure Functions. Check out our Functions + Serverless on Azure to go from learning core concepts, to building your first Functions app in your programming language of choice. Azure functions support multiple programming languages including C#, F#, Java, JavaScript, Python, Typescript, and PowerShell.

    Want to get extended language support for languages like Go, and Rust? You can Use Custom Handlers to make this happen! But what if you want to have long-running functions, or create complex workflows involving more than one function? Read our post on Durable Entities to learn how you can orchestrate this with Azure Functions.

    Check out this recent AskTheExpert Q&A session with the Azure Functions team to get answers to popular community questions on Azure Functions features and usage.

    Microservices and Containers

    Functions-as-a-Service is an ideal first step towards serverless development. But Functions are just one of the 5 pillars of cloud-native. This week we'll look at two of the other pillars: microservices and containers - with specific focus on two core technologies: Azure Container Apps and Dapr (Distributed Application Runtime).

    In this 6-part series of posts, we walk through each technology independently, before looking at the value of building Azure Container Apps with Dapr.

    • In Hello Container Apps we learned core concepts & deployed our first ACA.
    • In Microservices Communication we learned about ACA environments and virtual networks, and how microservices communicate in ACA with a hands-on tutorial.
    • In Scaling Your Container Apps we learned about KEDA (Kubernetes Event-Driven Autoscaler) and configuring ACA for autoscaling with KEDA-compliant triggers.
    • In Build with Dapr we introduced the Distributed Application Runtime (Dapr), exploring its Building Block APIs and sidecar architecture for working with ACA.
    • In Secure ACA Access we learned how to secure ACA access to external services with - and without - Dapr, covering Secret Stores and Managed Identity.
    • Finally, Build ACA with Dapr tied it all together with a enterprise app scenario where an orders processor (ACA) uses Dapr APIs (PubSub, State Management) to receive and store order messages from Azure Service Bus.

    Build ACA with Dapr

    Check out this recent AskTheExpert Q&A session with the Azure Container Apps team for answers to popular community questions on core features and usage.

    Serverless Integrations

    In the first half of the month we looked at compute resources for building and deploying serverless applications. In the second half, we look at integration tools and resources that automate developer workflows to streamline the end-to-end developer experience.

    In Azure, this is enabled by services like Azure Logic Apps and Azure Event Grid. Azure Logic Apps provides a visual designer to create and automate workflows with little or no code involved. Azure Event Grid provides a highly-scable event broker with support for pub/sub communications to drive async event-driven architectures.

    • In Tracking Weather Data Changes With Logic Apps we look at how you can use Logic Apps to integrate the MSN weather service with Azure CosmosDB, allowing automated collection of weather data on changes.

    • In Teach the Cloud to Read & Categorize Mail we take it a step further, using Logic Apps to automate a workflow that includes a Computer Vision service to "read" images and store the results to CosmosDB.

    • In Integrate with Microsoft Graph we explore a multi-cloud scenario (Azure + M365) where change notifications from Microsoft Graph can be integrated using Logic Apps and Event Hubs to power an onboarding workflow.

    • In Cloud Events with Event Grid we learn about the CloudEvents specification (for consistently describing event data) - and learn how Event Grid brokers events in this format. Azure Logic Apps can be an Event handler (subscriber) that uses the event to trigger an automated workflow on receipt.

      Azure Event Grid And Logic Apps

    Want to explore other such integrations? Browse Azure Architectures and filter by selected Azure services for more real-world scenarios.


    End-to-End Solutions

    We've covered serverless compute solutions (for building your serverless applications) and serverless integration services to automate end-to-end workflows in synchronous or asynchronous event-driven architectures. In this final week, we want to leave you with a sense of end-to-end development tools and use cases that can be enabled by Serverless on Azure. Here are some key examples:

    ArticleDescription
    In this tutorial, you'll learn to deploy a containerized ASP.NET Core 6.0 application to Azure Container Apps - with a Blazor front-end and two Web API projects
    Deploy Java containers to cloudIn this tutorial you learn to build and deploy a Java application running on Spring Boot, by publishing it in a container to Azure Container Registry, then deploying to Azure Container Apps,, from ACR, via the Azure Portal.
    **Where am I? My GPS Location with Serverless Power Platform Custom Connector**In this step-by-step tutorial you learn to integrate a serverless application (built on Azure Functions and OpenAPI) with Power Platforms custom connectors via Azure API Management (API-M).This pattern can empower a new ecosystem of fusion apps for cases like inventory management.
    And in our Serverless Hacks initiative, we walked through an 8-step hack to build a serverless tollbooth. Check out this 12-part video walkthrough of a reference solution using .NET.

    Developer Tools

    But wait - there's more. Those are a sample of the end-to-end application scenarios that are built on serverless on Azure. But what about the developer experience? In this article, we say hello to the Azure Developer CLI - an open-source tool that streamlines your develop-deploy workflow, with simple commands that map to core stages of your development journey. Go from code to cloud with one CLI

    And watch this space for more such tutorials and content through October, including a special #Hacktoberfest focused initiative to encourage and support first-time contributors to open-source. Here's a sneak peek at the project we plan to share - the new awesome-azd templates gallery.


    Join us at Microsoft Ignite!

    Want to continue your learning journey, and learn about what's next for Serverless on Azure? Microsoft Ignite happens Oct 12-14 this year and has multiple sessions on relevant technologies and tools. Check out the Session Catalog and register here to attend online.

    - - + + \ No newline at end of file diff --git a/blog/tags/azure-functions/page/10/index.html b/blog/tags/azure-functions/page/10/index.html index 2cf5c10e73..b4cc6b1265 100644 --- a/blog/tags/azure-functions/page/10/index.html +++ b/blog/tags/azure-functions/page/10/index.html @@ -14,13 +14,13 @@ - - + +

    16 posts tagged with "azure-functions"

    View All Tags

    · 10 min read
    Mike James
    Matt Soucoup

    Welcome to Day 6 of #30DaysOfServerless!

    The theme for this week is Azure Functions. Today we're going to talk about why Azure Functions are a great fit for .NET developers.


    What We'll Cover

    • What is serverless computing?
    • How does Azure Functions fit in?
    • Let's build a simple Azure Function in .NET
    • Developer Guide, Samples & Scenarios
    • Exercise: Explore the Create Serverless Applications path.
    • Resources: For self-study!

    A banner image that has the title of this article with the author&#39;s photo and a drawing that summarizes the demo application.


    The leaves are changing colors and there's a chill in the air, or for those lucky folks in the Southern Hemisphere, the leaves are budding and a warmth is in the air. Either way, that can only mean one thing - it's Serverless September!🍂 So today, we're going to take a look at Azure Functions - what they are, and why they're a great fit for .NET developers.

    What is serverless computing?

    For developers, serverless computing means you write highly compact individual functions that do one thing - and run in the cloud. These functions are triggered by some external event. That event could be a record being inserted into a database, a file uploaded into BLOB storage, a timer interval elapsed, or even a simple HTTP request.

    But... servers are still definitely involved! What has changed from other types of cloud computing is that the idea and ownership of the server has been abstracted away.

    A lot of the time you'll hear folks refer to this as Functions as a Service or FaaS. The defining characteristic is all you need to do is put together your application logic. Your code is going to be invoked in response to events - and the cloud provider takes care of everything else. You literally get to focus on only the business logic you need to run in response to something of interest - no worries about hosting.

    You do not need to worry about wiring up the plumbing between the service that originates the event and the serverless runtime environment. The cloud provider will handle the mechanism to call your function in response to whatever event you chose to have the function react to. And it passes along any data that is relevant to the event to your code.

    And here's a really neat thing. You only pay for the time the serverless function is running. So, if you have a function that is triggered by an HTTP request, and you rarely get requests to your function, you would rarely pay.

    How does Azure Functions fit in?

    Microsoft's Azure Functions is a modern serverless architecture, offering event-driven cloud computing that is easy for developers to use. It provides a way to run small pieces of code or Functions in the cloud without developers having to worry themselves about the infrastructure or platform the Function is running on.

    That means we're only concerned about writing the logic of the Function. And we can write that logic in our choice of languages... like C#. We are also able to add packages from NuGet to Azure Functions—this way, we don't have to reinvent the wheel and can use well-tested libraries.

    And the Azure Functions runtime takes care of a ton of neat stuff for us, like passing in information about the event that caused it to kick off - in a strongly typed variable. It also "binds" to other services, like Azure Storage, we can easily access those services from our code without having to worry about new'ing them up.

    Let's build an Azure Function!

    Scaffold the Function

    Don't worry about having an Azure subscription or even being connected to the internet—we can develop and debug Azure Functions locally using either Visual Studio or Visual Studio Code!

    For this example, I'm going to use Visual Studio Code to build up a Function that responds to an HTTP trigger and then writes a message to an Azure Storage Queue.

    Diagram of the how the Azure Function will use the HTTP trigger and the Azure Storage Queue Binding

    The incoming HTTP call is the trigger and the message queue the Function writes to is an output binding. Let's have at it!

    info

    You do need to have some tools downloaded and installed to get started. First and foremost, you'll need Visual Studio Code. Then you'll need the Azure Functions extension for VS Code to do the development with. Finally, you'll need the Azurite Emulator installed as well—this will allow us to write to a message queue locally.

    Oh! And of course, .NET 6!

    Now with all of the tooling out of the way, let's write a Function!

    1. Fire up Visual Studio Code. Then, from the command palette, type: Azure Functions: Create New Project

      Screenshot of create a new function dialog in VS Code

    2. Follow the steps as to which directory you want to create the project in and which .NET runtime and language you want to use.

      Screenshot of VS Code prompting which directory and language to use

    3. Pick .NET 6 and C#.

      It will then prompt you to pick the folder in which your Function app resides and then select a template.

      Screenshot of VS Code prompting you to pick the Function trigger template

      Pick the HTTP trigger template. When prompted for a name, call it: PostToAQueue.

    Execute the Function Locally

    1. After giving it a namespace, it prompts for an authorization level—pick Anonymous. Now we have a Function! Let's go ahead and hit F5 and see it run!
    info

    After the templates have finished installing, you may get a prompt to download additional components—these are NuGet packages. Go ahead and do that.

    When it runs, you'll see the Azure Functions logo appear in the Terminal window with the URL the Function is located at. Copy that link.

    Screenshot of the Azure Functions local runtime starting up

    1. Type the link into a browser, adding a name parameter as shown in this example: http://localhost:7071/api/PostToAQueue?name=Matt. The Function will respond with a message. You can even set breakpoints in Visual Studio Code and step through the code!

    Write To Azure Storage Queue

    Next, we'll get this HTTP trigger Function to write to a local Azure Storage Queue. First we need to add the Storage NuGet package to our project. In the terminal, type:

    dotnet add package Microsoft.Azure.WebJobs.Extensions.Storage

    Then set a configuration setting to tell the Function runtime where to find the Storage. Open up local.settings.json and set "AzureWebJobsStorage" to "UseDevelopmentStorage=true". The full file will look like:

    {
    "IsEncrypted": false,
    "Values": {
    "AzureWebJobsStorage": "UseDevelopmentStorage=true",
    "AzureWebJobsDashboard": ""
    }
    }

    Then create a new class within your project. This class will hold nothing but properties. Call it whatever you want and add whatever properties you want to it. I called mine TheMessage and added an Id and Name properties to it.

    public class TheMessage
    {
    public string Id { get; set; }
    public string Name { get; set; }
    }

    Finally, change your PostToAQueue Function, so it looks like the following:


    public static class PostToAQueue
    {
    [FunctionName("PostToAQueue")]
    public static async Task<IActionResult> Run(
    [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req,
    [Queue("demoqueue", Connection = "AzureWebJobsStorage")] IAsyncCollector<TheMessage> messages,
    ILogger log)
    {
    string name = req.Query["name"];

    await messages.AddAsync(new TheMessage { Id = System.Guid.NewGuid().ToString(), Name = name });

    return new OkResult();
    }
    }

    Note the addition of the messages variable. This is telling the Function to use the storage connection we specified before via the Connection property. And it is also specifying which queue to use in that storage account, in this case demoqueue.

    All the code is doing is pulling out the name from the query string, new'ing up a new TheMessage class and adding that to the IAsyncCollector variable.

    That will add the new message to the queue!

    Make sure Azurite is started within VS Code (both the queue and blob emulators). Run the app and send the same GET request as before: http://localhost:7071/api/PostToAQueue?name=Matt.

    If you have the Azure Storage Explorer installed, you can browse your local Queue and see the new message in there!

    Screenshot of Azure Storage Explorer with the new message in the queue

    Summing Up

    We had a quick look at what Microsoft's serverless offering, Azure Functions, is comprised of. It's a full-featured FaaS offering that enables you to write functions in your language of choice, including reusing packages such as those from NuGet.

    A highlight of Azure Functions is the way they are triggered and bound. The triggers define how a Function starts, and bindings are akin to input and output parameters on it that correspond to external services. The best part is that the Azure Function runtime takes care of maintaining the connection to the external services so you don't have to worry about new'ing up or disposing of the connections yourself.

    We then wrote a quick Function that gets triggered off an HTTP request and then writes a query string parameters from that request into a local Azure Storage Queue.

    What's Next

    So, where can you go from here?

    Think about how you can build real-world scenarios by integrating other Azure services. For example, you could use serverless integrations to build a workflow where the input payload received using an HTTP Trigger, is now stored in Blob Storage (output binding), which in turn triggers another service (e.g., Cognitive Services) that processes the blob and returns an enhanced result.

    Keep an eye out for an update to this post where we walk through a scenario like this with code. Check out the resources below to help you get started on your own.

    Exercise

    This brings us close to the end of Week 1 with Azure Functions. We've learned core concepts, built and deployed our first Functions app, and explored quickstarts and scenarios for different programming languages. So, what can you do to explore this topic on your own?

    • Explore the Create Serverless Applications learning path which has several modules that explore Azure Functions integrations with various services.
    • Take up the Cloud Skills Challenge and complete those modules in a fun setting where you compete with peers for a spot on the leaderboard!

    Then come back tomorrow as we wrap up the week with a discussion on end-to-end scenarios, a recap of what we covered this week, and a look at what's ahead next week.

    Resources

    Start here for developer guidance in getting started with Azure Functions as a .NET/C# developer:

    Then learn about supported Triggers and Bindings for C#, with code snippets to show how they are used.

    Finally, explore Azure Functions samples for C# and learn to implement serverless solutions. Examples include:

    - - + + \ No newline at end of file diff --git a/blog/tags/azure-functions/page/11/index.html b/blog/tags/azure-functions/page/11/index.html index 612a433648..3b2ac49ade 100644 --- a/blog/tags/azure-functions/page/11/index.html +++ b/blog/tags/azure-functions/page/11/index.html @@ -14,15 +14,15 @@ - - + +

    16 posts tagged with "azure-functions"

    View All Tags

    · 8 min read
    David Justo

    Welcome to Day 6 of #30DaysOfServerless!

    Today, we have a special set of posts from our Zero To Hero 🚀 initiative, featuring blog posts authored by our Product Engineering teams for #ServerlessSeptember. Posts were originally published on the Apps on Azure blog on Microsoft Tech Community.


    What We'll Cover

    • What are Durable Entities
    • Some Background
    • A Programming Model
    • Entities for a Micro-Blogging Platform


    Durable Entities are a special type of Azure Function that allow you to implement stateful objects in a serverless environment. They make it easy to introduce stateful components to your app without needing to manually persist data to external storage, so you can focus on your business logic. We’ll demonstrate their power with a real-life example in the last section.

    Entities 101: Some Background

    Programming Durable Entities feels a lot like object-oriented programming, except that these “objects” exist in a distributed system. Like objects, each Entity instance has a unique identifier, i.e. an entity ID that can be used to read and manipulate their internal state. Entities define a list of operations that constrain how their internal state is managed, like an object interface.

    Some experienced readers may realize that Entities sound a lot like an implementation of the Actor Pattern. For a discussion of the relationship between Entities and Actors, please refer to this documentation.

    Entities are a part of the Durable Functions Extension, an extension of Azure Functions that empowers programmers with stateful abstractions for serverless, such as Orchestrations (i.e. workflows).

    Durable Functions is available in most Azure Functions runtime environments: .NET, Node.js, Python, PowerShell, and Java (preview). For this article, we’ll focus on the C# experience, but note that Entities are also available in Node.js and Python; their availability in other languages is underway.

    Entities 102: The programming model

    Imagine you want to implement a simple Entity that just counts things. Its interface allows you to get the current count, add to the current count, and to reset the count to zero.

    If you implement this in an object-oriented way, you’d probably define a class (say “Counter”) with a method to get the current count (say “Counter.Get”), another to add to the count (say “Counter.Add”), and another to reset the count (say “Counter.Reset”). Well, the implementation of an Entity in C# is not that different from this sketch:

    [JsonObject(MemberSerialization.OptIn)] 
    public class Counter
    {
    [JsonProperty("value")]
    public int Value { get; set; }

    public void Add(int amount)
    {
    this.Value += amount;
    }

    public Task Reset()
    {
    this.Value = 0;
    return Task.CompletedTask;
    }

    public Task<int> Get()
    {
    return Task.FromResult(this.Value);
    }
    [FunctionName(nameof(Counter))]
    public static Task Run([EntityTrigger] IDurableEntityContext ctx)
    => ctx.DispatchAsync<Counter>();

    }

    We’ve defined a class named Counter, with an internal count stored in the variable “Value” which is manipulated through the “Add” and “Reset” methods, and which can be read via “Get”.

    The “Run” method is simply boilerplate required for the Azure Functions framework to interact with the object we’ve defined – it’s the method that the framework calls internally when it needs to load the Entity object. When DispatchAsync is called, the Entity and its corresponded state (the last count in “Value”) is loaded from storage. Again, this is mostly just boilerplate: your Entity’s business logic lies in the rest of the class.

    Finally, the Json annotation on top of the class and the Value field tells the Durable Functions framework that the “Value” field is to be durably persisted as part of the durable state on each Entity invocation. If you were to annotate other class variables with JsonProperty, they would also become part of the managed state.

    Entities for a micro-blogging platform

    We’ll try to implement a simple micro-blogging platform, a la Twitter. Let’s call it “Chirper”. In Chirper, users write chirps (i.e tweets), they can follow, and unfollow other users, and they can read the chirps of users they follow.

    Defining Entity

    Just like in OOP, it’s useful to begin by identifying what are the stateful agents of this scenario. In this case, users have state (who they follow and their chirps), and chirps have state in the form of their content. So, we could model these stateful agents as Entities!

    Below is a potential way to implement a User for Chirper as an Entity:

      [JsonObject(MemberSerialization = MemberSerialization.OptIn)] 
    public class User: IUser
    {
    [JsonProperty]
    public List<string> FollowedUsers { get; set; } = new List<string>();

    public void Add(string user)
    {
    FollowedUsers.Add(user);
    }

    public void Remove(string user)
    {
    FollowedUsers.Remove(user);
    }

    public Task<List<string>> Get()
    {
    return Task.FromResult(FollowedUsers);
    }
    // note: removed boilerplate “Run” method, for conciseness.
    }

    In this case, our Entity’s internal state is stored in “FollowedUsers” which is an array of accounts followed by this user. The operations exposed by this entity allow clients to read and modify this data: it can be read by “Get”, a new follower can be added via “Add”, and a user can be unfollowed via “Remove”.

    With that, we’ve modeled a Chirper’s user as an Entity! Recall that Entity instances each has a unique ID, so we can consider that unique ID to correspond to a specific user account.

    What about chirps? Should we represent them as Entities as well? That would certainly be valid. However, we would then need to create a mapping between an entity ID and every chirp entity ID that this user wrote.

    For demonstration purposes, a simpler approach would be to create an Entity that stores the list of all chirps authored by a given user; call it UserChirps. Then, we could fix each User Entity to share the same entity ID as its corresponding UserChirps Entity, making client operations easier.

    Below is a simple implementation of UserChirps:

      [JsonObject(MemberSerialization = MemberSerialization.OptIn)] 
    public class UserChirps : IUserChirps
    {
    [JsonProperty]
    public List<Chirp> Chirps { get; set; } = new List<Chirp>();

    public void Add(Chirp chirp)
    {
    Chirps.Add(chirp);
    }

    public void Remove(DateTime timestamp)
    {
    Chirps.RemoveAll(chirp => chirp.Timestamp == timestamp);
    }

    public Task<List<Chirp>> Get()
    {
    return Task.FromResult(Chirps);
    }

    // Omitted boilerplate “Run” function
    }

    Here, our state is stored in Chirps, a list of user posts. Our operations are the same as before: Get, Read, and Add. It’s the same pattern as before, but we’re representing different data.

    To put it all together, let’s set up Entity clients to generate and manipulate these Entities according to some REST API.

    Interacting with Entity

    Before going there, let’s talk briefly about how you can interact with an Entity. Entity interactions take one of two forms -- calls and signals:

    Calling an entity is a two-way communication. You send an operation message to the entity and then wait for the response message before you continue. The response can be a result value or an error. Signaling an entity is a one-way (fire-and-forget) communication. You send an operation message but don’t wait for a response. You have the reassurance that the message will be delivered eventually, but you don’t know when and don’t know what the response is. For example, when you read the state of an Entity, you are performing a “call” interaction. When you record that a user has followed another, you may choose to simply signal it.

    Now say user with a given userId (say “durableFan99” ) wants to post a chirp. For this, you can write an HTTP endpoint to signal the UserChips entity to record that chirp. We can leverage the HTTP Trigger functionality from Azure Functions and pair it with an entity client binding that signals the Add operation of our Chirp Entity:

    [FunctionName("UserChirpsPost")] 
    public static async Task<HttpResponseMessage> UserChirpsPost(
    [HttpTrigger(AuthorizationLevel.Function, "post", Route = "user/{userId}/chirps")]
    HttpRequestMessage req,
    DurableClient] IDurableClient client,
    ILogger log,
    string userId)
    {
    Authenticate(req, userId);
    var chirp = new Chirp()
    {
    UserId = userId,
    Timestamp = DateTime.UtcNow,
    Content = await req.Content.ReadAsStringAsync(),
    };
    await client.SignalEntityAsync<IUserChirps>(userId, x => x.Add(chirp));
    return req.CreateResponse(HttpStatusCode.Accepted, chirp);
    }

    Following the same pattern as above, to get all the chirps from a user, you could read the status of your Entity via ReadEntityStateAsync, which follows the call-interaction pattern as your client expects a response:

    [FunctionName("UserChirpsGet")] 
    public static async Task<HttpResponseMessage> UserChirpsGet(
    [HttpTrigger(AuthorizationLevel.Function, "get", Route = "user/{userId}/chirps")] HttpRequestMessage req,
    [DurableClient] IDurableClient client,
    ILogger log,
    string userId)
    {

    Authenticate(req, userId);
    var target = new EntityId(nameof(UserChirps), userId);
    var chirps = await client.ReadEntityStateAsync<UserChirps>(target);
    return chirps.EntityExists
    ? req.CreateResponse(HttpStatusCode.OK, chirps.EntityState.Chirps)
    : req.CreateResponse(HttpStatusCode.NotFound);
    }

    And there you have it! To play with a complete implementation of Chirper, you can try out our sample in the Durable Functions extension repo.

    Thank you!

    info

    Thanks for following along, and we hope you find Entities as useful as we do! If you have questions or feedback, please file issues in the repo above or tag us @AzureFunctions on Twitter

    - - + + \ No newline at end of file diff --git a/blog/tags/azure-functions/page/12/index.html b/blog/tags/azure-functions/page/12/index.html index d366f16e2d..badbf5eaf3 100644 --- a/blog/tags/azure-functions/page/12/index.html +++ b/blog/tags/azure-functions/page/12/index.html @@ -14,13 +14,13 @@ - - + +

    16 posts tagged with "azure-functions"

    View All Tags

    · 8 min read
    Kendall Roden

    Welcome to Day 6 of #30DaysOfServerless!

    Today, we have a special set of posts from our Zero To Hero 🚀 initiative, featuring blog posts authored by our Product Engineering teams for #ServerlessSeptember. Posts were originally published on the Apps on Azure blog on Microsoft Tech Community.


    What We'll Cover

    • Defining Cloud-Native
    • Introduction to Azure Container Apps
    • Dapr In Azure Container Apps
    • Conclusion


    Defining Cloud-Native

    While I’m positive I’m not the first person to ask this, I think it’s an appropriate way for us to kick off this article: “How many developers does it take to define Cloud-Native?” I hope you aren’t waiting for a punch line because I seriously want to know your thoughts (drop your perspectives in the comments..) but if you ask me, the limit does not exist!

    A quick online search of the topic returns a laundry list of articles, e-books, twitter threads, etc. all trying to nail down the one true definition. While diving into the rabbit hole of Cloud-Native, you will inevitably find yourself on the Cloud-Native Computing Foundation (CNCF) site. The CNCF is part of the Linux Foundation and aims to make "Cloud-Native computing ubiquitous" through deep open source project and community involvement. The CNCF has also published arguably the most popularized definition of Cloud-Native which begins with the following statement:

    “Cloud-Native technologies empower organizations to build and run scalable applications in modern, dynamic environments such as public, private, and hybrid clouds."

    Over the past four years, my day-to-day work has been driven primarily by the surging demand for application containerization and the drastic adoption of Kubernetes as the de-facto container orchestrator. Customers are eager to learn and leverage patterns, practices and technologies that enable building "loosely coupled systems that are resilient, manageable, and observable". Enterprise developers at these organizations are being tasked with rapidly deploying event-driven, horizontally-scalable, polyglot services via repeatable, code-to-cloud pipelines.

    While building Cloud-Native solutions can enable rapid innovation, the transition to adopting a Cloud-Native architectural approach comes with a steep learning curve and a new set of considerations. In a document published by Microsoft called What is Cloud-Native?, there are a few key areas highlighted to aid customers in the adoption of best practices for building modern, portable applications which I will summarize below:

    Cloud infrastructure

    • Cloud-Native applications leverage cloud infrastructure and make use of Platform-as-a-service offerings
    • Cloud-Native applications depend on highly-elastic infrastructure with automatic scaling, self-healing, and monitoring capabilities

    Modern application design

    • Cloud-Native applications should be constructed using principles outlined in the 12 factor methodology

    Microservices

    • Cloud-Native applications are typically composed of microservices where each core function, or service, is built and deployed independently

    Containers

    • Cloud-Native applications are typically deployed using containers as a packaging mechanism where an application's code and dependencies are bundled together for consistency of deployment
    • Cloud-Native applications leverage container orchestration technologies- primarily Kubernetes- for achieving capabilities such as workload scheduling, self-healing, auto-scale, etc.

    Backing services

    • Cloud-Native applications are ideally stateless workloads which retrieve and store data in data stores external to the application hosting infrastructure. Cloud providers like Azure provide an array of backing data services which can be securely accessed from application code and provide capabilities for ensuring application data is highly-available

    Automation

    • Cloud-Native solutions should use deployment automation for backing cloud infrastructure via versioned, parameterized Infrastructure as Code (IaC) templates which provide a consistent, repeatable process for provisioning cloud resources.
    • Cloud-Native solutions should make use of modern CI/CD practices and pipelines to ensure successful, reliable infrastructure and application deployment.

    Azure Container Apps

    In many of the conversations I've had with customers that involve talk of Kubernetes and containers, the topics of cost-optimization, security, networking, and reducing infrastructure and operations inevitably arise. I personally have yet to meet with any customers eager to have their developers get more involved with infrastructure concerns.

    One of my former colleagues, Jeff Hollan, made a statement while appearing on a 2019 episode of The Cloud-Native Show where he shared his perspective on Cloud-Native:

    "When I think about Cloud-Native... it's writing applications in a way where you are specifically thinking about the benefits the cloud can provide... to me, serverless is the perfect realization of that because the only reason you can write serverless applications is because the cloud exists."

    I must say that I agree with Jeff's perspective. In addition to optimizing development practices for the Cloud-Native world, reducing infrastructure exposure and operations is equally as important to many organizations and can be achieved as a result of cloud platform innovation.

    In May of 2022, Microsoft announced the general availability of Azure Container Apps. Azure Container Apps provides customers with the ability to run microservices and containerized applications on a serverless, consumption-based platform.

    For those interested in taking advantage of the open source ecosystem while reaping the benefits of a managed platform experience, Container Apps run on Kubernetes and provides a set of managed open source projects embedded directly into the platform including the Kubernetes Event Driven Autoscaler (KEDA), the Distributed Application Runtime (Dapr) and Envoy.

    Azure Kubernetes Service vs. Azure Container Apps

    Container apps provides other Cloud-Native features and capabilities in addition to those above including, but not limited to:

    The ability to dynamically scale and support growing numbers of users, events, and requests is one of the core requirements for most Cloud-Native, distributed applications. Azure Container Apps is purpose-built with this and other Cloud-Native tenants in mind.

    What can you build with Azure Container Apps?

    Dapr in Azure Container Apps

    As a quick personal note before we dive into this section I will say I am a bit bias about Dapr. When Dapr was first released, I had an opportunity to immediately get involved and became an early advocate for the project. It is created by developers for developers, and solves tangible problems customers architecting distributed systems face:

    HOW DO I
    • integrate with external systems that my app has to react and respond to?
    • create event driven apps which reliably send events from one service to another?
    • observe the calls and events between my services to diagnose issues in production?
    • access secrets securely from within my application?
    • discover other services and call methods on them?
    • prevent committing to a technology early and have the flexibility to swap out an alternative based on project or environment changes?

    While existing solutions were in the market which could be used to address some of the concerns above, there was not a lightweight, CNCF-backed project which could provide a unified approach to solve the more fundamental ask from customers: "How do I make it easy for developers to build microservices based on Cloud-Native best practices?"

    Enter Dapr!

    The Distributed Application Runtime (Dapr) provides APIs that simplify microservice connectivity. Whether your communication pattern is service to service invocation or pub/sub messaging, Dapr helps you write resilient and secured microservices. By letting Dapr’s sidecar take care of the complex challenges such as service discovery, message broker integration, encryption, observability, and secret management, you can focus on business logic and keep your code simple."

    The Container Apps platform provides a managed and supported Dapr integration which eliminates the need for deploying and managing the Dapr OSS project. In addition to providing managed upgrades, the platform also exposes a simplified Dapr interaction model to increase developer productivity and reduce the friction required to leverage Dapr capabilities. While the Dapr integration makes it easier for customers to adopt Cloud-Native best practices in container apps it is not required to make use of the container apps platform.

    Image on Dapr

    For additional insight into the dapr integration visit aka.ms/aca-dapr.

    Conclusion

    Backed by and integrated with powerful Cloud-Native technologies, Azure Container Apps strives to make developers productive, while reducing the operational overhead and learning curve that typically accompanies adopting a cloud-native strategy.

    If you are interested in building resilient, portable and highly-scalable apps visit Azure Container Apps | Microsoft Azure today!

    ASK THE EXPERT: LIVE Q&A

    The Azure Container Apps team will answer questions live on September 29.

    - - + + \ No newline at end of file diff --git a/blog/tags/azure-functions/page/13/index.html b/blog/tags/azure-functions/page/13/index.html index 3fe7e73a41..9304b345dc 100644 --- a/blog/tags/azure-functions/page/13/index.html +++ b/blog/tags/azure-functions/page/13/index.html @@ -14,13 +14,13 @@ - - + +

    16 posts tagged with "azure-functions"

    View All Tags

    · 7 min read
    Aaron Powell

    Welcome to Day 5 of #30DaysOfServerless!

    Yesterday we looked at Azure Functions from the perspective of a Java developer. Today, we'll do a similar walkthrough from the perspective of a JavaScript developer.

    And, we'll use this to explore another popular usage scenario for Azure Functions: building a serverless HTTP API using JavaScript.

    Ready? Let's go.


    What We'll Cover

    • Developer Guidance
    • Create Azure Function with CLI
    • Calling an external API
    • Azure Samples & Scenarios for JS
    • Exercise: Support searching
    • Resources: For self-study!


    Developer Guidance

    If you're a JavaScript developer new to serverless on Azure, start by exploring the Azure Functions JavaScript Developers Guide. It covers:

    • Quickstarts for Node.js - using Visual Code, CLI or Azure Portal
    • Guidance on hosting options and performance considerations
    • Azure Functions bindings and (code samples) for JavaScript
    • Scenario examples - integrations with other Azure Services

    Node.js 18 Support

    Node.js 18 Support (Public Preview)

    Azure Functions support for Node.js 18 entered Public Preview on Aug 31, 2022 and is supported by the Azure Functions v.4.x runtime!

    As we continue to explore how we can use Azure Functions, today we're going to look at using JavaScript to create one, and we're going to be using the newly released Node.js 18 support for Azure Functions to make the most out of the platform.

    Ensure you have Node.js 18 and Azure Functions v4.x versions installed, along with a text editor (I'll use VS Code in this post), and a terminal, then we're ready to go.

    Scenario: Calling The GitHub API

    The application we're going to be building today will use the GitHub API to return a random commit message, so that we don't need to come up with one ourselves! After all, naming things can be really hard! 🤣

    Creating the Azure Function

    To create our Azure Function, we're going to use the Azure Functions CLI, which we can install using npm:

    npm install --global azure-function-core-tools

    Once that's installed, we can use the new func command to initalise our project:

    func init --worker-runtime node --language javascript

    When running func init we can either provide the worker-runtime and language as arguments, or use the menu system that the tool will provide us. For brevity's stake, I've used the arguments here, specifying that we want node as the runtime and javascript as the language, but you could change that to typescript if you'd prefer to use TypeScript.

    Once the init command is completed, you should have a .vscode folder, and the files .gitignore, host.json, local.settings.json, and package.json.

    Files generated by func initFiles generated by func init

    Adding a HTTP Trigger

    We have an empty Functions app so far, what we need to do next is create a Function that it will run, and we're going to make a HTTP Trigger Function, which is a Function that responds to HTTP requests. We'll use the func new command to create that:

    func new --template "HTTP Trigger" --name "get-commit-message"

    When this completes, we'll have a folder for the Function, using the name we provided, that contains the filesfunction.json and index.js. Let's open the function.json to understand it a little bit:

    {
    "bindings": [
    {
    "authLevel": "function",
    "type": "httpTrigger",
    "direction": "in",
    "name": "req",
    "methods": [
    "get",
    "post"
    ]
    },
    {
    "type": "http",
    "direction": "out",
    "name": "res"
    }
    ]
    }

    This file is used to tell Functions about the Function that we've created and what it does, so it knows to handle the appropriate events. We have a bindings node which contains the event bindings for our Azure Function. The first binding is using the type httpTrigger, which indicates that it'll be executed, or triggered, by a HTTP event, and the methods indicates that it's listening to both GET and POST (you can change this for the right HTTP methods that you want to support). The HTTP request information will be bound to a property in the Functions context called req, so we can access query strings, the request body, etc.

    The other binding we have has the direction of out, meaning that it's something that the Function will return to the called, and since this is a HTTP API, the type is http, indicating that we'll return a HTTP response, and that response will be on a property called res that we add to the Functions context.

    Let's go ahead and start the Function and call it:

    func start

    Starting the FunctionStarting the Function

    With the Function started, access the endpoint http://localhost:7071/api/get-commit-message via a browser or using cURL:

    curl http://localhost:7071/api/get-commit-message\?name\=ServerlessSeptember

    Hello from Azure FunctionsHello from Azure Functions

    🎉 CONGRATULATIONS

    You created and ran a JavaScript function app locally!

    Calling an external API

    It's time to update the Function to do what we want to do - call the GitHub Search API and get some commit messages. The endpoint that we'll be calling is https://api.github.com/search/commits?q=language:javascript.

    Note: The GitHub API is rate limited and this sample will call it unauthenticated, so be aware of that in your own testing.

    To call this API, we'll leverage the newly released fetch support in Node 18 and async/await, to make for a very clean Function.

    Open up the index.js file, and delete the contents of the existing Function, so we have a empty one:

    module.exports = async function (context, req) {

    }

    The default template uses CommonJS, but you can use ES Modules with Azure Functions if you prefer.

    Now we'll use fetch to call the API, and unpack the JSON response:

    module.exports = async function (context, req) {
    const res = await fetch("https://api.github.com/search/commits?q=language:javascript");
    const json = await res.json();
    const messages = json.items.map(item => item.commit.message);
    context.res = {
    body: {
    messages
    }
    };
    }

    To send a response to the client, we're setting the context.res property, where res is the name of the output binding in our function.json, and giving it a body that contains the commit messages.

    Run func start again, and call the endpoint:

    curl http://localhost:7071/api/get-commit-message

    The you'll get some commit messages:

    A series of commit messages from the GitHub Search APIA series of commit messages from the GitHub Search API

    🎉 CONGRATULATIONS

    There we go, we've created an Azure Function which is used as a proxy to another API, that we call (using native fetch in Node.js 18) and from which we return a subset of the JSON payload.

    Next Steps

    Other Triggers, Bindings

    This article focused on using the HTTPTrigger and relevant bindings, to build a serverless API using Azure Functions. How can you explore other supported bindings, with code samples to illustrate usage?

    Scenarios with Integrations

    Once you've tried out the samples, try building an end-to-end scenario by using these triggers to integrate seamlessly with other services. Here are some suggestions:

    Exercise: Support searching

    The GitHub Search API allows you to provide search parameters via the q query string. In this sample, we hard-coded it to be language:javascript, but as a follow-on exercise, expand the Function to allow the caller to provide the search terms as a query string to the Azure Function, which is passed to the GitHub Search API. Hint - have a look at the req argument.

    Resources

    - - + + \ No newline at end of file diff --git a/blog/tags/azure-functions/page/14/index.html b/blog/tags/azure-functions/page/14/index.html index 0f4c582fbb..f2d2c03889 100644 --- a/blog/tags/azure-functions/page/14/index.html +++ b/blog/tags/azure-functions/page/14/index.html @@ -14,13 +14,13 @@ - - + +

    16 posts tagged with "azure-functions"

    View All Tags

    · 8 min read
    Rory Preddy

    Welcome to Day 4 of #30DaysOfServerless!

    Yesterday we walked through an Azure Functions Quickstart with JavaScript, and used it to understand the general Functions App structure, tooling and developer experience.

    Today we'll look at developing Functions app with a different programming language - namely, Java - and explore developer guidance, tools and resources to build serverless Java solutions on Azure.


    What We'll Cover


    Developer Guidance

    If you're a Java developer new to serverless on Azure, start by exploring the Azure Functions Java Developer Guide. It covers:

    In this blog post, we'll dive into one quickstart, and discuss other resources briefly, for awareness! Do check out the recommended exercises and resources for self-study!


    My First Java Functions App

    In today's post, we'll walk through the Quickstart: Azure Functions tutorial using Visual Studio Code. In the process, we'll setup our development environment with the relevant command-line tools and VS Code extensions to make building Functions app simpler.

    Note: Completing this exercise may incur a a cost of a few USD cents based on your Azure subscription. Explore pricing details to learn more.

    First, make sure you have your development environment setup and configured.

    PRE-REQUISITES
    1. An Azure account with an active subscription - Create an account for free
    2. The Java Development Kit, version 11 or 8. - Install
    3. Apache Maven, version 3.0 or above. - Install
    4. Visual Studio Code. - Install
    5. The Java extension pack - Install
    6. The Azure Functions extension for Visual Studio Code - Install

    VS Code Setup

    NEW TO VISUAL STUDIO CODE?

    Start with the Java in Visual Studio Code tutorial to jumpstart your learning!

    Install the Extension Pack for Java (shown below) to install 6 popular extensions to help development workflow from creation to testing, debugging, and deployment.

    Extension Pack for Java

    Now, it's time to get started on our first Java-based Functions app.

    1. Create App

    1. Open a command-line terminal and create a folder for your project. Use the code command to launch Visual Studio Code from that directory as shown:

      $ mkdir java-function-resource-group-api
      $ cd java-function-resource-group-api
      $ code .
    2. Open the Visual Studio Command Palette (Ctrl + Shift + p) and select Azure Functions: create new project to kickstart the create workflow. Alternatively, you can click the Azure icon (on activity sidebar), to get the Workspace window, click "+" and pick the "Create Function" option as shown below.

      Screenshot of creating function in Azure from Visual Studio Code.

    3. This triggers a multi-step workflow. Fill in the information for each step as shown in the following prompts. Important: Start this process from an empty folder - the workflow will populate it with the scaffold for your Java-based Functions app.

      PromptValue
      Choose the directory location.You should either create a new folder or choose an empty folder for the project workspace. Don't choose a project folder that is already part of a workspace.
      Select a languageChoose Java.
      Select a version of JavaChoose Java 11 or Java 8, the Java version on which your functions run in Azure. Choose a Java version that you've verified locally.
      Provide a group IDChoose com.function.
      Provide an artifact IDEnter myFunction.
      Provide a versionChoose 1.0-SNAPSHOT.
      Provide a package nameChoose com.function.
      Provide an app nameEnter HttpExample.
      Select the build tool for Java projectChoose Maven.

    Visual Studio Code uses the provided information and generates an Azure Functions project. You can view the local project files in the Explorer - it should look like this:

    Azure Functions Scaffold For Java

    2. Preview App

    Visual Studio Code integrates with the Azure Functions Core tools to let you run this project on your local development computer before you publish to Azure.

    1. To build and run the application, use the following Maven command. You should see output similar to that shown below.

      $ mvn clean package azure-functions:run
      ..
      ..
      Now listening on: http://0.0.0.0:7071
      Application started. Press Ctrl+C to shut down.

      Http Functions:

      HttpExample: [GET,POST] http://localhost:7071/api/HttpExample
      ...
    2. Copy the URL of your HttpExample function from this output to a browser and append the query string ?name=<YOUR_NAME>, making the full URL something like http://localhost:7071/api/HttpExample?name=Functions. The browser should display a message that echoes back your query string value. The terminal in which you started your project also shows log output as you make requests.

    🎉 CONGRATULATIONS

    You created and ran a function app locally!

    With the Terminal panel focused, press Ctrl + C to stop Core Tools and disconnect the debugger. After you've verified that the function runs correctly on your local computer, it's time to use Visual Studio Code and Maven to publish and test the project on Azure.

    3. Sign into Azure

    Before you can deploy, sign in to your Azure subscription.

    az login

    The az login command signs you into your Azure account.

    Use the following command to deploy your project to a new function app.

    mvn clean package azure-functions:deploy

    When the creation is complete, the following Azure resources are created in your subscription:

    • Resource group. Named as java-functions-group.
    • Storage account. Required by Functions. The name is generated randomly based on Storage account name requirements.
    • Hosting plan. Serverless hosting for your function app.The name is java-functions-app-service-plan.
    • Function app. A function app is the deployment and execution unit for your functions. The name is randomly generated based on your artifactId, appended with a randomly generated number.

    4. Deploy App

    1. Back in the Resources area in the side bar, expand your subscription, your new function app, and Functions. Right-click (Windows) or Ctrl - click (macOS) the HttpExample function and choose Execute Function Now....

      Screenshot of executing function in Azure from Visual Studio Code.

    2. In Enter request body you see the request message body value of { "name": "Azure" }. Press Enter to send this request message to your function.

    3. When the function executes in Azure and returns a response, a notification is raised in Visual Studio Code.

    You can also copy the complete Invoke URL shown in the output of the publish command into a browser address bar, appending the query parameter ?name=Functions. The browser should display similar output as when you ran the function locally.

    🎉 CONGRATULATIONS

    You deployed your function app to Azure, and invoked it!

    5. Clean up

    Use the following command to delete the resource group and all its contained resources to avoid incurring further costs.

    az group delete --name java-functions-group

    Next Steps

    So, where can you go from here? The example above used a familiar HTTP Trigger scenario with a single Azure service (Azure Functions). Now, think about how you can build richer workflows by using other triggers and integrating with other Azure or third-party services.

    Other Triggers, Bindings

    Check out Azure Functions Samples In Java for samples (and short use cases) that highlight other triggers - with code! This includes triggers to integrate with CosmosDB, Blob Storage, Event Grid, Event Hub, Kafka and more.

    Scenario with Integrations

    Once you've tried out the samples, try building an end-to-end scenario by using these triggers to integrate seamlessly with other Services. Here are a couple of useful tutorials:

    Exercise

    Time to put this into action and validate your development workflow:

    Resources

    - - + + \ No newline at end of file diff --git a/blog/tags/azure-functions/page/15/index.html b/blog/tags/azure-functions/page/15/index.html index 8df73171d7..e56c735cf0 100644 --- a/blog/tags/azure-functions/page/15/index.html +++ b/blog/tags/azure-functions/page/15/index.html @@ -14,13 +14,13 @@ - - + +

    16 posts tagged with "azure-functions"

    View All Tags

    · 9 min read
    Nitya Narasimhan

    Welcome to Day 3 of #30DaysOfServerless!

    Yesterday we learned core concepts and terminology for Azure Functions, the signature Functions-as-a-Service option on Azure. Today we take our first steps into building and deploying an Azure Functions app, and validate local development setup.

    Ready? Let's go.


    What We'll Cover


    Developer Guidance

    Before we jump into development, let's familiarize ourselves with language-specific guidance from the Azure Functions Developer Guide. We'll review the JavaScript version but guides for F#, Java, Python, C# and PowerShell are also available.

    1. A function is defined by two things: code (written in a supported programming language) and configuration (specified in a functions.json file, declaring the triggers, bindings and other context for execution).

    2. A function app is the unit of deployment for your functions, and is associated with a single execution context or runtime. It can contain multiple functions, but they must be in the same language.

    3. A host configuration is runtime-specific configuration that affects all functions running in a given function app instance. It is defined in a host.json file.

    4. A recommended folder structure is defined for the function app, but may vary based on the programming language used. Check the documentation on folder structures to learn the default for your preferred language.

    Here's an example of the JavaScript folder structure for a function app containing two functions with some shared dependencies. Note that host.json (runtime configuration) is defined once, in the root directory. And function.json is defined separately for each function.

    FunctionsProject
    | - MyFirstFunction
    | | - index.js
    | | - function.json
    | - MySecondFunction
    | | - index.js
    | | - function.json
    | - SharedCode
    | | - myFirstHelperFunction.js
    | | - mySecondHelperFunction.js
    | - node_modules
    | - host.json
    | - package.json
    | - local.settings.json

    We'll dive into what the contents of these files look like, when we build and deploy the first function. We'll cover local.settings.json in the About Local Testing section at the end.


    My First Function App

    The documentation provides quickstart options for all supported languages. We'll walk through the JavaScript versions in this article. You have two options for development:

    I'm a huge fan of VS Code - so I'll be working through that tutorial today.

    PRE-REQUISITES

    Don't forget to validate your setup by checking the versions of installed software.

    Install VSCode Extension

    Installing the Visual Studio Code extension should automatically open this page in your IDE with similar quickstart instructions, but potentially more recent screenshots.

    Visual Studio Code Extension for VS Code

    Note that it may make sense to install the Azure tools for Visual Studio Code extensions pack if you plan on working through the many projects in Serverless September. This includes the Azure Functions extension by default.

    Create First Function App

    Walk through the Create local [project] steps of the quickstart. The process is quick and painless and scaffolds out this folder structure and files. Note the existence (and locations) of functions.json and host.json files.

    Final screenshot for VS Code workflow

    Explore the Code

    Check out the functions.json configuration file. It shows that the function is activated by an httpTrigger with an input binding (tied to req payload) and an output binding (tied to res payload). And it supports both GET and POST requests on the exposed URL.

    {
    "bindings": [
    {
    "authLevel": "anonymous",
    "type": "httpTrigger",
    "direction": "in",
    "name": "req",
    "methods": [
    "get",
    "post"
    ]
    },
    {
    "type": "http",
    "direction": "out",
    "name": "res"
    }
    ]
    }

    Check out index.js - the function implementation. We see it logs a message to the console when invoked. It then extracts a name value from the input payload (req) and crafts a different responseMessage based on the presence/absence of a valid name. It returns this response in the output payload (res).

    module.exports = async function (context, req) {
    context.log('JavaScript HTTP trigger function processed a request.');

    const name = (req.query.name || (req.body && req.body.name));
    const responseMessage = name
    ? "Hello, " + name + ". This HTTP triggered function executed successfully."
    : "This HTTP triggered function executed successfully. Pass a name in the query string or in the request body for a personalized response.";

    context.res = {
    // status: 200, /* Defaults to 200 */
    body: responseMessage
    };
    }

    Preview Function App Locally

    You can now run this function app locally using Azure Functions Core Tools. VS Code integrates seamlessly with this CLI-based tool, making it possible for you to exploit all its capabilities without leaving the IDE. In fact, the workflow will even prompt you to install those tools if they didn't already exist in your local dev environment.

    Now run the function app locally by clicking on the "Run and Debug" icon in the activity bar (highlighted, left) and pressing the "▶️" (Attach to Node Functions) to start execution. On success, your console output should show something like this.

    Final screenshot for VS Code workflow

    You can test the function locally by visiting the Function Url shown (http://localhost:7071/api/HttpTrigger1) or by opening the Workspace region of the Azure extension, and selecting the Execute Function now menu item as shown.

    Final screenshot for VS Code workflow

    In the latter case, the Enter request body popup will show a pre-populated request of {"name":"Azure"} that you can submit.

    Final screenshot for VS Code workflow

    On successful execution, your VS Code window will show a notification as follows. Take note of the console output - it shows the message encoded in index.js.

    Final screenshot for VS Code workflow

    You can also visit the deployed function URL directly in a local browser - testing the case for a request made with no name payload attached. Note how the response in the browser now shows the non-personalized version of the message!

    Final screenshot for VS Code workflow

    🎉 Congratulations

    You created and ran a function app locally!

    (Re)Deploy to Azure

    Now, just follow the creating a function app in Azure steps to deploy it to Azure, using an active subscription! The deployed app resource should now show up under the Function App Resources where you can click Execute Function Now to test the Azure-deployed version instead. You can also look up the function URL in the portal and visit that link in your local browser to trigger the function without the name context.

    🎉 Congratulations

    You have an Azure-hosted serverless function app!

    Challenge yourself and try to change the code and redeploy to Azure to return something different. You have effectively created a serverless API endpoint!


    About Core Tools

    That was a lot to cover! In the next few days we'll have more examples for Azure Functions app development - focused on different programming languages. So let's wrap today's post by reviewing two helpful resources.

    First, let's talk about Azure Functions Core Tools - the command-line tool that lets you develop, manage, and deploy, Azure Functions projects from your local development environment. It is used transparently by the VS Code extension - but you can use it directly from a terminal for a powerful command-line end-to-end developer experience! The Core Tools commands are organized into the following contexts:

    Learn how to work with Azure Functions Core Tools. Not only can it help with quick command execution, it can also be invaluable for debugging issues that may not always be visible or understandable in an IDE.

    About Local Testing

    You might have noticed that the scaffold also produced a local.settings.json file. What is that and why is it useful? By definition, the local.settings.json file "stores app settings and settings used by local development tools. Settings in the local.settings.json file are used only when you're running your project locally."

    Read the guidance on Code and test Azure Functions Locally to learn more about how to configure development environments locally, for your preferred programming language, to support testing and debugging on the local Functions runtime.

    Exercise

    We made it! Now it's your turn!! Here are a few things you can try to apply what you learned and reinforce your understanding:

    Resources

    Bookmark and visit the #30DaysOfServerless Collection. It's the one-stop collection of resources we will keep updated with links to relevant documentation and learning resources.

    - - + + \ No newline at end of file diff --git a/blog/tags/azure-functions/page/16/index.html b/blog/tags/azure-functions/page/16/index.html index e683a1b11e..7623f153f9 100644 --- a/blog/tags/azure-functions/page/16/index.html +++ b/blog/tags/azure-functions/page/16/index.html @@ -14,15 +14,15 @@ - - + +

    16 posts tagged with "azure-functions"

    View All Tags

    · 9 min read
    Nitya Narasimhan

    Welcome to Day 2️⃣ of #30DaysOfServerless!

    Today, we kickstart our journey into serveless on Azure with a look at Functions As a Service. We'll explore Azure Functions - from core concepts to usage patterns.

    Ready? Let's Go!


    What We'll Cover

    • What is Functions-as-a-Service? (FaaS)
    • What is Azure Functions?
    • Triggers, Bindings and Custom Handlers
    • What is Durable Functions?
    • Orchestrators, Entity Functions and Application Patterns
    • Exercise: Take the Cloud Skills Challenge!
    • Resources: #30DaysOfServerless Collection.


    1. What is FaaS?

    Faas stands for Functions As a Service (FaaS). But what does that mean for us as application developers? We know that building and deploying modern applications at scale can get complicated and it starts with us needing to take decisions on Compute. In other words, we need to answer this question: "where should I host my application given my resource dependencies and scaling requirements?"

    this useful flowchart

    Azure has this useful flowchart (shown below) to guide your decision-making. You'll see that hosting options generally fall into three categories:

    • Infrastructure as a Service (IaaS) - where you provision and manage Virtual Machines yourself (cloud provider manages infra).
    • Platform as a Service (PaaS) - where you use a provider-managed hosting environment like Azure Container Apps.
    • Functions as a Service (FaaS) - where you forget about hosting environments and simply deploy your code for the provider to run.

    Here, "serverless" compute refers to hosting options where we (as developers) can focus on building apps without having to manage the infrastructure. See serverless compute options on Azure for more information.


    2. Azure Functions

    Azure Functions is the Functions-as-a-Service (FaaS) option on Azure. It is the ideal serverless solution if your application is event-driven with short-lived workloads. With Azure Functions, we develop applications as modular blocks of code (functions) that are executed on demand, in response to configured events (triggers). This approach brings us two advantages:

    • It saves us money. We only pay for the time the function runs.
    • It scales with demand. We have 3 hosting plans for flexible scaling behaviors.

    Azure Functions can be programmed in many popular languages (C#, F#, Java, JavaScript, TypeScript, PowerShell or Python), with Azure providing language-specific handlers and default runtimes to execute them.

    Concept: Custom Handlers
    • What if we wanted to program in a non-supported language?
    • Or we wanted to use a different runtime for a supported language?

    Custom Handlers have you covered! These are lightweight webservers that can receive and process input events from the Functions host - and return responses that can be delivered to any output targets. By this definition, custom handlers can be implemented by any language that supports receiving HTTP events. Check out the quickstart for writing a custom handler in Rust or Go.

    Custom Handlers

    Concept: Trigger and Bindings

    We talked about what functions are (code blocks). But when are they invoked or executed? And how do we provide inputs (arguments) and retrieve outputs (results) from this execution?

    This is where triggers and bindings come in.

    • Triggers define how a function is invoked and what associated data it will provide. A function must have exactly one trigger.
    • Bindings declaratively define how a resource is connected to the function. The resource or binding can be of type input, output, or both. Bindings are optional. A Function can have multiple input, output bindings.

    Azure Functions comes with a number of supported bindings that can be used to integrate relevant services to power a specific scenario. For instance:

    • HTTP Triggers - invokes the function in response to an HTTP request. Use this to implement serverless APIs for your application.
    • Event Grid Triggers invokes the function on receiving events from an Event Grid. Use this to process events reactively, and potentially publish responses back to custom Event Grid topics.
    • SignalR Service Trigger invokes the function in response to messages from Azure SignalR, allowing your application to take actions with real-time contexts.

    Triggers and bindings help you abstract your function's interfaces to other components it interacts with, eliminating hardcoded integrations. They are configured differently based on the programming language you use. For example - JavaScript functions are configured in the functions.json file. Here's an example of what that looks like.

    {
    "disabled":false,
    "bindings":[
    // ... bindings here
    {
    "type": "bindingType",
    "direction": "in",
    "name": "myParamName",
    // ... more depending on binding
    }
    ]
    }

    The key thing to remember is that triggers and bindings have a direction property - triggers are always in, input bindings are in and output bindings are out. Some bindings can support a special inout direction.

    The documentation has code examples for bindings to popular Azure services. Here's an example of the bindings and trigger configuration for a BlobStorage use case.

    // function.json configuration

    {
    "bindings": [
    {
    "queueName": "myqueue-items",
    "connection": "MyStorageConnectionAppSetting",
    "name": "myQueueItem",
    "type": "queueTrigger",
    "direction": "in"
    },
    {
    "name": "myInputBlob",
    "type": "blob",
    "path": "samples-workitems/{queueTrigger}",
    "connection": "MyStorageConnectionAppSetting",
    "direction": "in"
    },
    {
    "name": "myOutputBlob",
    "type": "blob",
    "path": "samples-workitems/{queueTrigger}-Copy",
    "connection": "MyStorageConnectionAppSetting",
    "direction": "out"
    }
    ],
    "disabled": false
    }

    The code below shows the function implementation. In this scenario, the function is triggered by a queue message carrying an input payload with a blob name. In response, it copies that data to the resource associated with the output binding.

    // function implementation

    module.exports = async function(context) {
    context.log('Node.js Queue trigger function processed', context.bindings.myQueueItem);
    context.bindings.myOutputBlob = context.bindings.myInputBlob;
    };
    Concept: Custom Bindings

    What if we have a more complex scenario that requires bindings for non-supported resources?

    There is an option create custom bindings if necessary. We don't have time to dive into details here but definitely check out the documentation


    3. Durable Functions

    This sounds great, right?. But now, let's talk about one challenge for Azure Functions. In the use cases so far, the functions are stateless - they take inputs at runtime if necessary, and return output results if required. But they are otherwise self-contained, which is great for scalability!

    But what if I needed to build more complex workflows that need to store and transfer state, and complete operations in a reliable manner? Durable Functions are an extension of Azure Functions that makes stateful workflows possible.

    Concept: Orchestrator Functions

    How can I create workflows that coordinate functions?

    Durable Functions use orchestrator functions to coordinate execution of other Durable functions within a given Functions app. These functions are durable and reliable. Later in this post, we'll talk briefly about some application patterns that showcase popular orchestration scenarios.

    Concept: Entity Functions

    How do I persist and manage state across workflows?

    Entity Functions provide explicit state mangement for Durable Functions, defining operations to read and write state to durable entities. They are associated with a special entity trigger for invocation. These are currently available only for a subset of programming languages so check to see if they are supported for your programming language of choice.

    USAGE: Application Patterns

    Durable Functions are a fascinating topic that would require a separate, longer post, to do justice. For now, let's look at some application patterns that showcase the value of these starting with the simplest one - Function Chaining as shown below:

    Function Chaining

    Here, we want to execute a sequence of named functions in a specific order. As shown in the snippet below, the orchestrator function coordinates invocations on the given functions in the desired sequence - "chaining" inputs and outputs to establish the workflow. Take note of the yield keyword. This triggers a checkpoint, preserving the current state of the function for reliable operation.

    const df = require("durable-functions");

    module.exports = df.orchestrator(function*(context) {
    try {
    const x = yield context.df.callActivity("F1");
    const y = yield context.df.callActivity("F2", x);
    const z = yield context.df.callActivity("F3", y);
    return yield context.df.callActivity("F4", z);
    } catch (error) {
    // Error handling or compensation goes here.
    }
    });

    Other application patterns for durable functions include:

    There's a lot more to explore but we won't have time to do that today. Definitely check the documentation and take a minute to read the comparison with Azure Logic Apps to understand what each technology provides for serverless workflow automation.


    4. Exercise

    That was a lot of information to absorb! Thankfully, there are a lot of examples in the documentation that can help put these in context. Here are a couple of exercises you can do, to reinforce your understanding of these concepts.


    5. What's Next?

    The goal for today was to give you a quick tour of key terminology and concepts related to Azure Functions. Tomorrow, we dive into the developer experience, starting with core tools for local development and ending by deploying our first Functions app.

    Want to do some prep work? Here are a few useful links:


    6. Resources


    - - + + \ No newline at end of file diff --git a/blog/tags/azure-functions/page/2/index.html b/blog/tags/azure-functions/page/2/index.html index 19dcea8eb6..7fb7f7317e 100644 --- a/blog/tags/azure-functions/page/2/index.html +++ b/blog/tags/azure-functions/page/2/index.html @@ -14,13 +14,13 @@ - - + +

    16 posts tagged with "azure-functions"

    View All Tags

    · 14 min read
    Justin Yoo

    Welcome to Day 28 of #30DaysOfServerless!

    Since it's the serverless end-to-end week, I'm going to discuss how to use a serverless application Azure Functions with OpenAPI extension to be seamlessly integrated with Power Platform custom connector through Azure API Management - in a post I call "Where am I? My GPS Location with Serverless Power Platform Custom Connector"

    OK. Are you ready? Let's get started!


    What We'll Cover

    • What is Power Platform custom connector?
    • Proxy app to Google Maps and Naver Map API
    • API Management integration
    • Two ways of building custom connector
    • Where am I? Power Apps app
    • Exercise: Try this yourself!
    • Resources: For self-study!


    SAMPLE REPO

    Want to follow along? Check out the sample app on GitHub repository used in this post.

    What is Power Platform custom connector?

    Power Platform is a low-code/no-code application development tool for fusion teams that consist of a group of people. Those people come from various disciplines, including field experts (domain experts), IT professionals and professional developers, to draw business values successfully. Within the fusion team, the domain experts become citizen developers or low-code developers by Power Platform. In addition, Making Power Platform more powerful is that it offers hundreds of connectors to other Microsoft 365 and third-party services like SAP, ServiceNow, Salesforce, Google, etc.

    However, what if you want to use your internal APIs or APIs not yet offering their official connectors? Here's an example. If your company has an inventory management system, and you want to use it within your Power Apps or Power Automate. That point is exactly where Power Platform custom connectors is necessary.

    Inventory Management System for Power Apps

    Therefore, Power Platform custom connectors enrich those citizen developers' capabilities because those connectors can connect any API applications for the citizen developers to use.

    In this post, let's build a custom connector that provides a static map image generated by Google Maps API and Naver Map API using your GPS location.

    Proxy app to Google Maps and Naver Map API

    First, let's build an Azure Functions app that connects to Google Maps and Naver Map. Suppose that you've already got the API keys for both services. If you haven't yet, get the keys first by visiting here for Google and here for Naver. Then, store them to local.settings.json within your Azure Functions app.

    {
    "Values": {
    ...
    "Maps__Google__ApiKey": "<GOOGLE_MAPS_API_KEY>",
    "Maps__Naver__ClientId": "<NAVER_MAP_API_CLIENT_ID>",
    "Maps__Naver__ClientSecret": "<NAVER_MAP_API_CLIENT_SECRET>"
    }
    }

    Here's the sample logic to get the static image from Google Maps API. It takes the latitude and longitude of your current location and image zoom level, then returns the static map image. There are a few hard-coded assumptions, though:

    • The image size should be 400x400.
    • The image should be in .png format.
    • The marker should show be red and show my location.
    public class GoogleMapService : IMapService
    {
    public async Task<byte[]> GetMapAsync(HttpRequest req)
    {
    var latitude = req.Query["lat"];
    var longitude = req.Query["long"];
    var zoom = (string)req.Query["zoom"] ?? "14";

    var sb = new StringBuilder();
    sb.Append("https://maps.googleapis.com/maps/api/staticmap")
    .Append($"?center={latitude},{longitude}")
    .Append("&size=400x400")
    .Append($"&zoom={zoom}")
    .Append($"&markers=color:red|{latitude},{longitude}")
    .Append("&format=png32")
    .Append($"&key={this._settings.Google.ApiKey}");
    var requestUri = new Uri(sb.ToString());

    var bytes = await this._http.GetByteArrayAsync(requestUri).ConfigureAwait(false);

    return bytes;
    }
    }

    The NaverMapService class has a similar logic with the same input and assumptions. Here's the code:

    public class NaverMapService : IMapService
    {
    public async Task<byte[]> GetMapAsync(HttpRequest req)
    {
    var latitude = req.Query["lat"];
    var longitude = req.Query["long"];
    var zoom = (string)req.Query["zoom"] ?? "13";

    var sb = new StringBuilder();
    sb.Append("https://naveropenapi.apigw.ntruss.com/map-static/v2/raster")
    .Append($"?center={longitude},{latitude}")
    .Append("&w=400")
    .Append("&h=400")
    .Append($"&level={zoom}")
    .Append($"&markers=color:blue|pos:{longitude}%20{latitude}")
    .Append("&format=png")
    .Append("&lang=en");
    var requestUri = new Uri(sb.ToString());

    this._http.DefaultRequestHeaders.Clear();
    this._http.DefaultRequestHeaders.Add("X-NCP-APIGW-API-KEY-ID", this._settings.Naver.ClientId);
    this._http.DefaultRequestHeaders.Add("X-NCP-APIGW-API-KEY", this._settings.Naver.ClientSecret);

    var bytes = await this._http.GetByteArrayAsync(requestUri).ConfigureAwait(false);

    return bytes;
    }
    }

    Let's take a look at the function endpoints. Here's for the Google Maps and Naver Map. As the GetMapAsync(req) method returns a byte array value, you need to transform it as FileContentResult, with the content type of image/png.

    // Google Maps
    public class GoogleMapsTrigger
    {
    [FunctionName(nameof(GoogleMapsTrigger.GetGoogleMapImage))]
    public async Task<IActionResult> GetGoogleMapImage(
    [HttpTrigger(AuthorizationLevel.Anonymous, "GET", Route = "google/image")] HttpRequest req)
    {
    this._logger.LogInformation("C# HTTP trigger function processed a request.");

    var bytes = await this._service.GetMapAsync(req).ConfigureAwait(false);

    return new FileContentResult(bytes, "image/png");
    }
    }

    // Naver Map
    public class NaverMapsTrigger
    {
    [FunctionName(nameof(NaverMapsTrigger.GetNaverMapImage))]
    public async Task<IActionResult> GetNaverMapImage(
    [HttpTrigger(AuthorizationLevel.Anonymous, "GET", Route = "naver/image")] HttpRequest req)
    {
    this._logger.LogInformation("C# HTTP trigger function processed a request.");

    var bytes = await this._service.GetMapAsync(req).ConfigureAwait(false);

    return new FileContentResult(bytes, "image/png");
    }
    }

    Then, add the OpenAPI capability to each function endpoint. Here's the example:

    // Google Maps
    public class GoogleMapsTrigger
    {
    [FunctionName(nameof(GoogleMapsTrigger.GetGoogleMapImage))]
    // ⬇️⬇️⬇️ Add decorators provided by the OpenAPI extension ⬇️⬇️⬇️
    [OpenApiOperation(operationId: nameof(GoogleMapsTrigger.GetGoogleMapImage), tags: new[] { "google" })]
    [OpenApiParameter(name: "lat", In = ParameterLocation.Query, Required = true, Type = typeof(string), Description = "The **latitude** parameter")]
    [OpenApiParameter(name: "long", In = ParameterLocation.Query, Required = true, Type = typeof(string), Description = "The **longitude** parameter")]
    [OpenApiParameter(name: "zoom", In = ParameterLocation.Query, Required = false, Type = typeof(string), Description = "The **zoom level** parameter &ndash; Default value is `14`")]
    [OpenApiResponseWithBody(statusCode: HttpStatusCode.OK, contentType: "image/png", bodyType: typeof(byte[]), Description = "The map image as an OK response")]
    // ⬆️⬆️⬆️ Add decorators provided by the OpenAPI extension ⬆️⬆️⬆️
    public async Task<IActionResult> GetGoogleMapImage(
    [HttpTrigger(AuthorizationLevel.Anonymous, "GET", Route = "google/image")] HttpRequest req)
    {
    ...
    }
    }

    // Naver Map
    public class NaverMapsTrigger
    {
    [FunctionName(nameof(NaverMapsTrigger.GetNaverMapImage))]
    // ⬇️⬇️⬇️ Add decorators provided by the OpenAPI extension ⬇️⬇️⬇️
    [OpenApiOperation(operationId: nameof(NaverMapsTrigger.GetNaverMapImage), tags: new[] { "naver" })]
    [OpenApiParameter(name: "lat", In = ParameterLocation.Query, Required = true, Type = typeof(string), Description = "The **latitude** parameter")]
    [OpenApiParameter(name: "long", In = ParameterLocation.Query, Required = true, Type = typeof(string), Description = "The **longitude** parameter")]
    [OpenApiParameter(name: "zoom", In = ParameterLocation.Query, Required = false, Type = typeof(string), Description = "The **zoom level** parameter &ndash; Default value is `13`")]
    [OpenApiResponseWithBody(statusCode: HttpStatusCode.OK, contentType: "image/png", bodyType: typeof(byte[]), Description = "The map image as an OK response")]
    // ⬆️⬆️⬆️ Add decorators provided by the OpenAPI extension ⬆️⬆️⬆️
    public async Task<IActionResult> GetNaverMapImage(
    [HttpTrigger(AuthorizationLevel.Anonymous, "GET", Route = "naver/image")] HttpRequest req)
    {
    ...
    }
    }

    Run the function app in the local. Here are the latitude and longitude values for Seoul, Korea.

    • latitude: 37.574703
    • longitude: 126.978519

    Google Map for Seoul

    It seems to be working! Let's deploy it to Azure.

    API Management integration

    Visual Studio 2022 provides a built-in deployment tool for Azure Functions app onto Azure. In addition, the deployment tool supports seamless integration with Azure API Management as long as your Azure Functions app enables the OpenAPI capability. In this post, I'm going to use this feature. Right-mouse click on the Azure Functions project and select the "Publish" menu.

    Visual Studio context menu for publish

    Then, you will see the publish screen. Click the "➕ New" button to create a new publish profile.

    Create a new publish profile

    Choose "Azure" and click the "Next" button.

    Choose the target platform for publish

    Select the app instance. This time simply pick up the "Azure Function App (Windows)" option, then click "Next".

    Choose the target OS for publish

    If you already provision an Azure Function app instance, you will see it on the screen. Otherwise, create a new one. Then, click "Next".

    Choose the target instance for publish

    In the next step, you are asked to choose the Azure API Management instance for integration. Choose one, or create a new one. Then, click "Next".

    Choose the APIM instance for integration

    Finally, select the publish method either local publish or GitHub Actions workflow. Let's pick up the local publish method for now. Then, click "Finish".

    Choose the deployment type

    The publish profile has been created. Click "Close" to move on.

    Publish profile created

    Now the function app is ready for deployment. Click the "Publish" button and see how it goes.

    Publish function app

    The Azure function app has been deployed and integrated with the Azure API Management instance.

    Function app published

    Go to the published function app site, and everything looks OK.

    Function app on Azure

    And API Management shows the function app integrated perfectly.

    Function app integrated with APIM

    Now, you are ready to create a custom connector. Let's move on.

    Two ways of building custom connector

    There are two ways to create a custom connector.

    Export custom connector from API Management

    First, you can directly use the built-in API Management feature. Then, click the ellipsis icon and select the "Create Power Connector" menu.

    Create Power Connector menu

    Then, you are redirected to this screen. While the "API" and "API display name" fields are pre-populated, you need to choose the Power Platform environment tied to your tenant. Choose an environment, click "Authenticate", and click "Create".

    Create custom connector screen

    Check your custom connector on Power Apps or Power Automate side.

    Custom connector created on Power Apps

    However, there's a caveat to this approach. Because it's tied to your tenant, you should use the second approach if you want to use this custom connector on the other tenant.

    Import custom connector from OpenAPI document or URL

    Click the ellipsis icon again and select the "Export" menu.

    Export menu

    On the Export API screen, choose the "OpenAPI v2 (JSON)" panel because Power Platform custom connector currently accepts version 2 of the OpenAPI document.

    Select OpenAPI v2

    Download the OpenAPI document to your local computer and move to your Power Apps or Power Automate page under your desired environment. I'm going to use the Power Automate page. First, go to the "Data" ➡️ "Custom connectors" page. Then, click the "➕ New custom connector" ➡️ "Import an OpenAPI file" at the top right corner.

    New custom connector

    When a modal pops up, give the custom connector name and import the OpenAPI document exported above. Then, click "Continue".

    Import custom connector

    Actually, that's it! Next, click the "✔️ Create connector" button to create the connector.

    Create custom connector

    Go back to the custom connector page, and you will see the "Maps API" custom connector you just created.

    Custom connector imported

    So, you are ready to create a Power Apps app to display your location on Google Maps or Naver Map! Let's move on.

    Where am I? Power Apps app

    Open the Power Apps Studio, and create an empty canvas app, named Who am I with a phone layout.

    Custom connector integration

    To use the custom connector created above, you need to add it to the Power App. Click the cylinder icon on the left and click the "Add data" button.

    Add custom connector to data pane

    Search the custom connector name, "Maps API", and click the custom connector to add.

    Search custom connector

    To use the custom connector, you also need to create a connection to it. Click the "Connect" button and move on.

    Create connection to custom connector

    Now, you've got the connection to the custom connector.

    Connection to custom connector ready

    Controls

    Let's build the Power Apps app. First of all, put three controls Image, Slider and Button onto the canvas.

    Power Apps control added

    Click the "Screen1" control and change the value on the property "OnVisible" to the formula below. The formula stores the current slider value in the zoomlevel collection.

    ClearCollect(
    zoomlevel,
    Slider1.Value
    )

    Click the "Botton1" control and change the value on the property "OnSelected" to the formula below. It passes the current latitude, longitude and zoom level to the custom connector and receives the image data. The received image data is stored in the result collection.

    ClearCollect(
    result,
    MAPS.GetGoogleMapImage(
    Location.Latitude,
    Location.Longitude,
    { zoom: First(zoomlevel).Value }
    )
    )

    Click the "Image1" control and change the value on the property "Image" to the formula below. It gets the image data from the result collection.

    First(result).Url

    Click the "Slider1" control and change the value on the property "OnChange" to the formula below. It stores the current slider value to the zoomlevel collection, followed by calling the custom connector to get the image data against the current location.

    ClearCollect(
    zoomlevel,
    Slider1.Value
    );
    ClearCollect(
    result,
    MAPS.GetGoogleMapImage(
    Location.Latitude,
    Location.Longitude,
    { zoom: First(zoomlevel).Value }
    )
    )

    That seems to be OK. Let's click the "Where am I?" button. But it doesn't show the image. The First(result).Url value is actually similar to this:

    appres://blobmanager/1090a86393a843adbfcf428f0b90e91b/1

    It's the image reference value somewhere you can't get there.

    Workaround Power Automate workflow

    Therefore, you need a workaround using a Power Automate workflow to sort out this issue. Open the Power Automate Studio, create an instant cloud flow with the Power App trigger, and give it the "Where am I" name. Then add input parameters of lat, long and zoom.

    Power Apps trigger on Power Automate workflow

    Add custom connector action to get the map image.

    Select action to get the Google Maps image

    In the action, pass the appropriate parameters to the action.

    Pass parameters to the custom connector action

    Add a "Response" action and put the following values into each field.

    • "Body" field:

      {
      "base64Image": <power_automate_expression>
      }

      The <power_automate_expression> should be concat('data:', body('GetGoogleMapImage')?['$content-type'], ';base64,', body('GetGoogleMapImage')?['$content']).

    • "Response Body JSON Schema" field:

      {
      "type": "object",
      "properties": {
      "base64Image": {
      "type": "string"
      }
      }
      }

    Format the Response action

    Let's return to the Power Apps Studio and add the Power Automate workflow you created.

    Add Power Automate workflow

    Select "Button1" and change the value on the property "OnSelect" below. It replaces the direct call to the custom connector with the Power Automate workflow.

    ClearCollect(
    result,
    WhereamI.Run(
    Location.Latitude,
    Location.Longitude,
    First(zoomlevel).Value
    )
    )

    Also, change the value on the property "OnChange" of the "Slider1" control below, replacing the custom connector call with the Power Automate workflow call.

    ClearCollect(
    zoomlevel,
    Slider1.Value
    );
    ClearCollect(
    result,
    WhereamI.Run(
    Location.Latitude,
    Location.Longitude,
    First(zoomlevel).Value
    )
    )

    And finally, change the "Image1" control's "Image" property value below.

    First(result).base64Image

    The workaround has been applied. Click the "Where am I?" button to see your current location from Google Maps.

    Run Power Apps app #1

    If you change the slider left or right, you will see either the zoomed-in image or the zoomed-out image.

    Run Power Apps app #2

    Now, you've created a Power Apps app to show your current location using:

    • Google Maps API through the custom connector, and
    • Custom connector written in Azure Functions with OpenAPI extension!

    Exercise: Try this yourself!

    You can fork this GitHub repository to your account and play around with it to see how the custom connector works. After forking the repository, make sure that you create all the necessary secrets to your repository documented in the README file.

    Then, click the "Deploy to Azure" button, and it will provision all necessary Azure resources and deploy an Azure Functions app for a custom connector.

    Deploy To Azure

    Once everything is deployed successfully, try to create a Power Apps app and Power Automate workflow to see your current location in real-time!

    Resources: For self-study!

    Want to know more about Power Platform custom connector and Azure Functions OpenAPI extension? Here are several resources you can take a look at:

    - - + + \ No newline at end of file diff --git a/blog/tags/azure-functions/page/3/index.html b/blog/tags/azure-functions/page/3/index.html index 6613f66722..a2ad5ee662 100644 --- a/blog/tags/azure-functions/page/3/index.html +++ b/blog/tags/azure-functions/page/3/index.html @@ -14,14 +14,14 @@ - - + +

    16 posts tagged with "azure-functions"

    View All Tags

    · 5 min read
    Madhura Bharadwaj

    Welcome to Day 26 of #30DaysOfServerless!

    Today, we have a special set of posts from our Zero To Hero 🚀 initiative, featuring blog posts authored by our Product Engineering teams for #ServerlessSeptember. Posts were originally published on the Apps on Azure blog on Microsoft Tech Community.


    What We'll Cover

    • Monitoring your Azure Functions
    • Built-in log streaming
    • Live Metrics stream
    • Troubleshooting Azure Functions


    Monitoring your Azure Functions:

    Azure Functions uses Application Insights to collect and analyze log data from individual function executions in your function app.

    Using Application Insights

    Application Insights collects log, performance, and error data. By automatically detecting performance anomalies and featuring powerful analytics tools, you can more easily diagnose issues and better understand how your functions are used. These tools are designed to help you continuously improve performance and usability of your functions. You can even use Application Insights during local function app project development.

    Typically, you create an Application Insights instance when you create your function app. In this case, the instrumentation key required for the integration is already set as an application setting named APPINSIGHTS_INSTRUMENTATIONKEY. With Application Insights integration enabled, telemetry data is sent to your connected Application Insights instance. This data includes logs generated by the Functions host, traces written from your functions code, and performance data. In addition to data from your functions and the Functions host, you can also collect data from the Functions scale controller.

    By default, the data collected from your function app is stored in Application Insights. In the Azure portal, Application Insights provides an extensive set of visualizations of your telemetry data. You can drill into error logs and query events and metrics. To learn more, including basic examples of how to view and query your collected data, see Analyze Azure Functions telemetry in Application Insights.

    Using Log Streaming

    In addition to this, you can have a smoother debugging experience through log streaming. There are two ways to view a stream of log files being generated by your function executions.

    • Built-in log streaming: the App Service platform lets you view a stream of your application log files. This is equivalent to the output seen when you debug your functions during local development and when you use the Test tab in the portal. All log-based information is displayed. For more information, see Stream logs. This streaming method supports only a single instance and can't be used with an app running on Linux in a Consumption plan.
    • Live Metrics Stream: when your function app is connected to Application Insights, you can view log data and other metrics in near real-time in the Azure portal using Live Metrics Stream. Use this method when monitoring functions running on multiple-instances or on Linux in a Consumption plan. This method uses sampled data. Log streams can be viewed both in the portal and in most local development environments.
    Monitoring Azure Functions

    Learn how to configure monitoring for your Azure Functions. See Monitoring Azure Functions data reference for detailed information on the metrics and logs metrics created by Azure Functions.

    In addition to this, Azure Functions uses Azure Monitor to monitor the health of your function apps. Azure Functions collects the same kinds of monitoring data as other Azure resources that are described in Azure Monitor data collection. See Monitoring Azure Functions data reference for detailed information on the metrics and logs metrics created by Azure Functions.

    Troubleshooting your Azure Functions:

    When you do run into issues with your function app, Azure Functions diagnostics points out what’s wrong. It guides you to the right information to troubleshoot and resolve the issue more easily and quickly.

    Let’s explore how to use Azure Functions diagnostics to diagnose and solve common function app issues.

    1. Navigate to your function app in the Azure portal.
    2. Select Diagnose and solve problems to open Azure Functions diagnostics.
    3. Once you’re here, there are multiple ways to retrieve the information you’re looking for. Choose a category that best describes the issue of your function app by using the keywords in the homepage tile. You can also type a keyword that best describes your issue in the search bar. There’s also a section at the bottom of the page that will directly take you to some of the more popular troubleshooting tools. For example, you could type execution to see a list of diagnostic reports related to your function app execution and open them directly from the homepage.

    Monitoring and troubleshooting apps in Azure Functions

    1. For example, click on the Function App Down or Reporting Errors link under Popular troubleshooting tools section. You will find detailed analysis, insights and next steps for the issues that were detected. On the left you’ll see a list of detectors. Click on them to explore more, or if there’s a particular keyword you want to look for, type it Into the search bar on the top.

    Monitoring and troubleshooting apps in Azure Functions

    TROUBLESHOOTING TIP

    Here are some general troubleshooting tips that you can follow if you find your Function App throwing Azure Functions Runtime unreachable error.

    Also be sure to check out the recommended best practices to ensure your Azure Functions are highly reliable. This article details some best practices for designing and deploying efficient function apps that remain healthy and perform well in a cloud-based environment.

    Bonus tip:

    - - + + \ No newline at end of file diff --git a/blog/tags/azure-functions/page/4/index.html b/blog/tags/azure-functions/page/4/index.html index 9c0baedac7..f822cc0240 100644 --- a/blog/tags/azure-functions/page/4/index.html +++ b/blog/tags/azure-functions/page/4/index.html @@ -14,13 +14,13 @@ - - + +

    16 posts tagged with "azure-functions"

    View All Tags

    · 9 min read
    Justin Yoo

    Welcome to Day 21 of #30DaysOfServerless!

    We've so far walked through what Azure Event Grid is and how it generally works. Today, let's discuss how Azure Event Grid deals with CloudEvents.


    What We'll Cover


    OK. Let's get started!

    What is CloudEvents?

    Needless to say, events are everywhere. Events come not only from event-driven systems but also from many different systems and devices, including IoT ones like Raspberry PI.

    But the problem is that every event publisher (system/device that creates events) describes their events differently, meaning there is no standard way of describing events. It has caused many issues between systems, mainly from the interoperability perspective.

    1. Consistency: No standard way of describing events resulted in developers having to write their own event handling logic for each event source.
    2. Accessibility: There were no common libraries, tooling and infrastructure to deliver events across systems.
    3. Productivity: The overall productivity decreases because of the lack of the standard format of events.

    Cloud Events Logo

    Therefore, CNCF (Cloud-Native Computing Foundation) has brought up the concept, called CloudEvents. CloudEvents is a specification that commonly describes event data. Conforming any event data to this spec will simplify the event declaration and delivery across systems and platforms and more, resulting in a huge productivity increase.

    How Azure Event Grid brokers CloudEvents

    Before CloudEvents, Azure Event Grid described events in their own way. Therefore, if you want to use Azure Event Grid, you should follow the event format/schema that Azure Event Grid declares. However, not every system/service/application follows the Azure Event Grid schema. Therefore, Azure Event Grid now supports CloudEvents spec as input and output formats.

    Azure Event Grid for Azure

    Take a look at the simple diagram below, which describes how Azure Event Grid captures events raised from various Azure services. In this diagram, Azure Key Vault takes the role of the event source or event publisher, and Azure Logic Apps takes the role of the event handler (I'll discuss Azure Logic Apps as the event handler later in this post). We use Azure Event Grid System Topic for Azure.

    Azure Event Grid for Azure

    Therefore, let's create an Azure Event Grid System Topic that captures events raised from Azure Key Vault when a new version of a secret is added.

    Azure Event Grid System Topic for Key Vault

    As Azure Event Grid makes use of the pub/sub pattern, you need to create the Azure Event Grid Subscription to consume the events. Here's the subscription that uses the Event Grid data format:

    ![Azure Event Grid System Subscription for Key Vault in Event Grid Format][./img/21-cloudevents-via-event-grid-03.png]

    Once you create the subscription, create a new version of the secret on Azure Key Vault. Then, Azure Key Vault raises an event, which is captured in the Event Grid format:

    [
    {
    "id": "6f44b9c0-d37e-40e7-89be-f70a6da291cc",
    "topic": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/rg-aegce-krc/providers/Microsoft.KeyVault/vaults/kv-xxxxxxxx",
    "subject": "hello",
    "eventType": "Microsoft.KeyVault.SecretNewVersionCreated",
    "data": {
    "Id": "https://kv-xxxxxxxx.vault.azure.net/secrets/hello/064dfc082fec463f8d4610ed6118811d",
    "VaultName": "kv-xxxxxxxx",
    "ObjectType": "Secret",
    "ObjectName": "hello",
    "Version": "064dfc082fec463f8d4610ed6118811d",
    "NBF": null,
    "EXP": null
    },
    "dataVersion": "1",
    "metadataVersion": "1",
    "eventTime": "2022-09-21T07:08:09.1234567Z"
    }
    ]

    So, how is it different from the CloudEvents format? Let's take a look. According to the spec, the JSON data in CloudEvents might look like this:

    {
    "id" : "C234-1234-1234",
    "source" : "/mycontext",
    "specversion" : "1.0",
    "type" : "com.example.someevent",
    "comexampleextension1" : "value",
    "time" : "2018-04-05T17:31:00Z",
    "datacontenttype" : "application/cloudevents+json",
    "data" : {
    "appinfoA" : "abc",
    "appinfoB" : 123,
    "appinfoC" : true
    }
    }

    This time, let's create another subscription using the CloudEvents schema. Here's how to create the subscription against the system topic:

    Azure Event Grid System Subscription for Key Vault in CloudEvents Format

    Therefore, Azure Key Vault emits the event data in the CloudEvents format:

    {
    "id": "6f44b9c0-d37e-40e7-89be-f70a6da291cc",
    "source": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/rg-aegce-krc/providers/Microsoft.KeyVault/vaults/kv-xxxxxxxx",
    "specversion": "1.0",
    "type": "Microsoft.KeyVault.SecretNewVersionCreated",
    "subject": "hello",
    "time": "2022-09-21T07:08:09.1234567Z",
    "data": {
    "Id": "https://kv-xxxxxxxx.vault.azure.net/secrets/hello/064dfc082fec463f8d4610ed6118811d",
    "VaultName": "kv-xxxxxxxx",
    "ObjectType": "Secret",
    "ObjectName": "hello",
    "Version": "064dfc082fec463f8d4610ed6118811d",
    "NBF": null,
    "EXP": null
    }
    }

    Can you identify some differences between the Event Grid format and the CloudEvents format? Fortunately, both Event Grid schema and CloudEvents schema look similar to each other. But they might be significantly different if you use a different event source outside Azure.

    Azure Event Grid for Systems outside Azure

    As mentioned above, the event data described outside Azure or your own applications within Azure might not be understandable by Azure Event Grid. In this case, we need to use Azure Event Grid Custom Topic. Here's the diagram for it:

    Azure Event Grid for Applications outside Azure

    Let's create the Azure Event Grid Custom Topic. When you create the topic, make sure that you use the CloudEvent schema during the provisioning process:

    Azure Event Grid Custom Topic

    If your application needs to publish events to Azure Event Grid Custom Topic, your application should build the event data in the CloudEvents format. If you use a .NET application, add the NuGet package first.

    dotnet add package Azure.Messaging.EventGrid

    Then, create the publisher instance. You've already got the topic endpoint URL and the access key.

    var topicEndpoint = new Uri("<Azure Event Grid Custom Topic Endpoint URL>");
    var credential = new AzureKeyCredential("<Azure Event Grid Custom Topic Access Key>");
    var publisher = new EventGridPublisherClient(topicEndpoint, credential);

    Now, build the event data like below. Make sure that you follow the CloudEvents schema that requires additional metadata like event source, event type and content type.

    var source = "/your/event/source";
    var type = "com.source.event.your/OnEventOccurs";

    var data = new MyEventData() { Hello = "World" };

    var @event = new CloudEvent(source, type, data);

    And finally, send the event to Azure Event Grid Custom Topic.

    await publisher.SendEventAsync(@event);

    The captured event data looks like the following:

    {
    "id": "cc2b2775-52b8-43b8-a7cc-c1c33c2b2e59",
    "source": "/your/event/source",
    "type": "com.source.event.my/OnEventOccurs",
    "data": {
    "Hello": "World"
    },
    "time": "2022-09-21T07:08:09.1234567+00:00",
    "specversion": "1.0"
    }

    However, due to limitations, someone might insist that their existing application doesn't or can't emit the event data in the CloudEvents format. In this case, what should we do? There's no standard way of sending the event data in the CloudEvents format to Azure Event Grid Custom Topic. One of the approaches we may be able to apply is to put a converter between the existing application and Azure Event Grid Custom Topic like below:

    Azure Event Grid for Applications outside Azure with Converter

    Once the Function app (or any converter app) receives legacy event data, it internally converts the CloudEvents format and publishes it to Azure Event Grid.

    var data = default(MyRequestData);
    using (var reader = new StreamReader(req.Body))
    {
    var serialised = await reader.ReadToEndAsync();
    data = JsonConvert.DeserializeObject<MyRequestData>(serialised);
    }

    var converted = new MyEventData() { Hello = data.Lorem };
    var @event = new CloudEvent(source, type, converted);

    The converted event data is captured like this:

    {
    "id": "df296da3-77cd-4da2-8122-91f631941610",
    "source": "/your/event/source",
    "type": "com.source.event.my/OnEventOccurs",
    "data": {
    "Hello": "ipsum"
    },
    "time": "2022-09-21T07:08:09.1234567+00:00",
    "specversion": "1.0"
    }

    This approach is beneficial in many integration scenarios to make all the event data canonicalised.

    How Azure Logic Apps consumes CloudEvents

    I put Azure Logic Apps as the event handler in the previous diagrams. According to the CloudEvents spec, each event handler must implement request validation to avoid abuse. One good thing about using Azure Logic Apps is that it has already implemented this request validation feature. It implies that we just subscribe to the topic and consume the event data.

    Create a new Logic Apps instance and add the HTTP Request trigger. Once it saves, you will get the endpoint URL.

    Azure Logic Apps with HTTP Request Trigger

    Then, create the Azure Event Grid Subscription with:

    • Endpoint type: Webhook
    • Endpoint URL: The Logic Apps URL from above.

    Azure Logic Apps with HTTP Request Trigger

    Once the subscription is ready, this Logic Apps works well as the event handler. Here's how it receives the CloudEvents data from the subscription.

    Azure Logic Apps that Received CloudEvents data

    Now you've got the CloudEvents data. It's entirely up to you to handle that event data however you want!

    Exercise: Try this yourself!

    You can fork this GitHub repository to your account and play around with it to see how Azure Event Grid with CloudEvents works. Alternatively, the "Deploy to Azure" button below will provision all necessary Azure resources and deploy an Azure Functions app to mimic the event publisher.

    Deploy To Azure

    Resources: For self-study!

    Want to know more about CloudEvents in real-life examples? Here are several resources you can take a look at:

    - - + + \ No newline at end of file diff --git a/blog/tags/azure-functions/page/5/index.html b/blog/tags/azure-functions/page/5/index.html index 082f09066b..246485c0b5 100644 --- a/blog/tags/azure-functions/page/5/index.html +++ b/blog/tags/azure-functions/page/5/index.html @@ -14,13 +14,13 @@ - - + +

    16 posts tagged with "azure-functions"

    View All Tags

    · 6 min read
    Ramya Oruganti

    Welcome to Day 19 of #30DaysOfServerless!

    Today, we have a special set of posts from our Zero To Hero 🚀 initiative, featuring blog posts authored by our Product Engineering teams for #ServerlessSeptember. Posts were originally published on the Apps on Azure blog on Microsoft Tech Community.


    What We'll Cover

    • Retry Policy Support - in Apache Kafka Extension
    • AutoOffsetReset property - in Apache Kafka Extension
    • Key support for Kafka messages - in Apache Kafka Extension
    • References: Apache Kafka Extension for Azure Functions


    Recently we launched the Apache Kafka extension for Azure functions in GA with some cool new features like deserialization of Avro Generic records and Kafka headers support. We received great responses - so we're back with more updates!

    Retry Policy support

    Handling errors in Azure Functions is important to avoid data loss or miss events or monitor the health of an application. Apache Kafka Extension for Azure Functions supports retry policy which tells the runtime to rerun a failed execution until either successful completion occurs or the maximum number of retries is reached.

    A retry policy is evaluated when a trigger function raises an uncaught exception. As a best practice, you should catch all exceptions in your code and rethrow any errors that you want to result in a retry.

    There are two retry strategies supported by policy that you can configure :- fixed delay and exponential backoff

    1. Fixed Delay - A specified amount of time is allowed to elapse between each retry.
    2. Exponential Backoff - The first retry waits for the minimum delay. On subsequent retries, time is added exponentially to the initial duration for each retry, until the maximum delay is reached. Exponential back-off adds some small randomization to delays to stagger retries in high-throughput scenarios.
    Please Note

    Retry Policy for Kafka extension is NOT supported for C# (in proc and out proc) trigger and output binding. This is supported for languages like Java, Node (JS , TypeScript), PowerShell and Python trigger and output bindings.

    Here is the sample code view of exponential backoff retry strategy

    Error Handling with Apache Kafka extension for Azure Functions

    AutoOffsetReset property

    AutoOffsetReset property enables customers to configure the behaviour in the absence of an initial offset. Imagine a scenario when there is a need to change consumer group name. The consumer connected using a new consumer group had to reprocess all events starting from the oldest (earliest) one, as this was the default one and this setting wasn’t exposed as configurable option in the Apache Kafka extension for Azure Functions(previously). With the help of this kafka setting you can configure on how to start processing events for newly created consumer groups.

    Due to lack of the ability to configure this setting, offset commit errors were causing topics to restart from earliest offset· Users were looking to be able to set offset setting to either latest or earliest based on their requirements.

    We are happy to share that we have enabled the AutoOffsetReset setting as a configurable one to either - Earliest(Default) and Latest. Setting the value to Earliest configures the consumption of the messages from the the earliest/smallest offset or beginning of the topic partition. Setting the property to Latest configures the consumption of the messages from the latest/largest offset or from the end of the topic partition. This is supported for all the Azure Functions supported languages (C# (in & out), Java, Node (JS and TypeScript), PowerShell and python) and can be used for both triggers and output binding

    Error Handling with Apache Kafka extension for Azure Functions

    Key support for Kafka messages

    With keys the producer/output binding can be mapped to broker and partition to write based on the message. So alongside the message value, we can choose to send a message key and that key can be whatever you want it could be a string, it could be a number . In case you don’t send the key, the key is set to null then the data will be sent in a Round Robin fashion to make it very simple. But in case you send a key with your message, all the messages that share the same key will always go to the same partition and thus you can enable grouping of similar messages into partitions

    Previously while consuming a Kafka event message using the Azure Function kafka extension, the event key was always none although the key was present in the event message.

    Key support was implemented in the extension which enables customers to set/view key in the Kafka event messages coming in to the kafka trigger and set keys to the messages going in to kafka topics (with keys set) through output binding. Therefore key support was enabled in the extension to support both trigger and output binding for all Azure Functions supported languages ( (C# (in & out), Java, Node (JS and TypeScript), PowerShell and python)

    Here is the view of an output binding producer code where Kafka messages are being set with key

    Error Handling with Apache Kafka extension for Azure Functions

    Conclusion:

    In this article you have learnt about the latest additions to the Apache Kafka extension for Azure Functions. Incase you have been waiting for these features to get released or need them you are all set and please go head and try them out!! They are available in the latest extension bundles

    Want to learn more?

    Please refer to Apache Kafka bindings for Azure Functions | Microsoft Docs for detail documentation, samples on the Azure function supported languages and more!

    References

    FEEDBACK WELCOME

    Keep in touch with us on Twitter via @AzureFunctions.

    - - + + \ No newline at end of file diff --git a/blog/tags/azure-functions/page/6/index.html b/blog/tags/azure-functions/page/6/index.html index fce63e5dbc..bbf12b335f 100644 --- a/blog/tags/azure-functions/page/6/index.html +++ b/blog/tags/azure-functions/page/6/index.html @@ -14,13 +14,13 @@ - - + +

    16 posts tagged with "azure-functions"

    View All Tags

    · 6 min read
    Melony Qin

    Welcome to Day 12 of #30DaysOfServerless!

    Today, we have a special set of posts from our Zero To Hero 🚀 initiative, featuring blog posts authored by our Product Engineering teams for #ServerlessSeptember. Posts were originally published on the Apps on Azure blog on Microsoft Tech Community.


    What We'll Cover

    • What are Custom Handlers, and why use them?
    • How Custom Handler Works
    • Message Processing With Azure Custom Handler
    • Azure Portal Monitoring


    If you have been working with Azure Functions for a while, you may know Azure Functions is a serverless FaaS (Function as a Service) offered provided by Microsoft Azure, which is built for your key scenarios, including building web APIs, processing file uploads, responding to database changes, processing IoT data streams, managing message queues, and more.

    Custom Handlers: What and Why

    Azure functions support multiple programming languages including C#, F#, Java, JavaScript, Python, typescript, and PowerShell. If you want to get extended language support with Azure functions for other languages such as Go, and Rust, that’s where custom handler comes in.

    An Azure function custom handler allows the use of any language that supports HTTP primitives and author Azure functions. With custom handlers, you can use triggers and input and output bindings via extension bundles, hence it supports all the triggers and bindings you're used to with Azure functions.

    How a Custom Handler Works

    Let’s take a look at custom handlers and how it works.

    • A request is sent to the function host when an event is triggered. It’s up to the function host to issue a request payload to the custom handler, which holds the trigger and inputs binding data as well as other metadata for the function.
    • The custom handler is a local HTTP web server. It executes the function code and returns a response payload to the Functions host.
    • The Functions host passes data from the response to the function's output bindings which will be passed to the downstream stream services for data processing.

    Check out this article to know more about Azure functions custom handlers.


    Message processing with Custom Handlers

    Message processing is one of the key scenarios that Azure functions are trying to address. In the message-processing scenario, events are often collected in queues. These events can trigger Azure functions to execute a piece of business logic.

    You can use the Service Bus trigger to respond to messages from an Azure Service Bus queue - it's then up to the Azure functions custom handlers to take further actions to process the messages. The process is described in the following diagram:

    Building Serverless Go Applications with Azure functions custom handlers

    In Azure function, the function.json defines the function's trigger, input and output bindings, and other configuration settings. Note that every function can have multiple bindings, but it can only have one trigger. The following is an example of setting up the Service Bus queue trigger in the function.json file :

    {
    "bindings": [
    {
    "name": "queueItem",
    "type": "serviceBusTrigger",
    "direction": "in",
    "queueName": "functionqueue",
    "connection": "ServiceBusConnection"
    }
    ]
    }

    You can add a binding definition in the function.json to write the output to a database or other locations of your desire. Supported bindings can be found here.

    As we’re programming in Go, so we need to set the value of defaultExecutablePath to handler in the customHandler.description section in the host.json file.

    Assume we’re programming in Windows OS, and we have named our go application as server.go, after we executed go build server.go command, it produces an executable called server.exe. So here we set server.exe in the host.json as the following example :

      "customHandler": {
    "description": {
    "defaultExecutablePath": "./server.exe",
    "workingDirectory": "",
    "arguments": []
    }
    }

    We’re showcasing a simple Go application here with Azure functions custom handlers where we print out the messages received from the function host. The following is the full code of server.go application :

    package main

    import (
    "encoding/json"
    "fmt"
    "log"
    "net/http"
    "os"
    )

    type InvokeRequest struct {
    Data map[string]json.RawMessage
    Metadata map[string]interface{}
    }

    func queueHandler(w http.ResponseWriter, r *http.Request) {
    var invokeRequest InvokeRequest

    d := json.NewDecoder(r.Body)
    d.Decode(&invokeRequest)

    var parsedMessage string
    json.Unmarshal(invokeRequest.Data["queueItem"], &parsedMessage)

    fmt.Println(parsedMessage)
    }

    func main() {
    customHandlerPort, exists := os.LookupEnv("FUNCTIONS_CUSTOMHANDLER_PORT")
    if !exists {
    customHandlerPort = "8080"
    }
    mux := http.NewServeMux()
    mux.HandleFunc("/MessageProcessorFunction", queueHandler)
    fmt.Println("Go server Listening on: ", customHandlerPort)
    log.Fatal(http.ListenAndServe(":"+customHandlerPort, mux))

    }

    Ensure you have Azure functions core tools installed, then we can use func start command to start our function. Then we’ll have have a C#-based Message Sender application on Github to send out 3000 messages to the Azure service bus queue. You’ll see Azure functions instantly start to process the messages and print out the message as the following:

    Monitoring Serverless Go Applications with Azure functions custom handlers


    Azure portal monitoring

    Let’s go back to Azure portal portal the events see how those messages in Azure Service Bus queue were being processed. There was 3000 messages were queued in the Service Bus queue ( the Blue line stands for incoming Messages ). The outgoing messages (the red line in smaller wave shape ) showing there are progressively being read by Azure functions as the following :

    Monitoring Serverless Go Applications with Azure functions custom handlers

    Check out this article about monitoring Azure Service bus for further information.

    Next steps

    Thanks for following along, we’re looking forward to hearing your feedback. Also, if you discover potential issues, please record them on Azure Functions host GitHub repository or tag us @AzureFunctions on Twitter.

    RESOURCES

    Start to build your serverless applications with custom handlers, check out the official documentation:

    Life is a journey of learning. Let’s stay tuned!

    - - + + \ No newline at end of file diff --git a/blog/tags/azure-functions/page/7/index.html b/blog/tags/azure-functions/page/7/index.html index 07ecebabb1..53e08bc138 100644 --- a/blog/tags/azure-functions/page/7/index.html +++ b/blog/tags/azure-functions/page/7/index.html @@ -14,13 +14,13 @@ - - + +

    16 posts tagged with "azure-functions"

    View All Tags

    · 5 min read
    Anthony Chu

    Welcome to Day 12 of #30DaysOfServerless!

    Today, we have a special set of posts from our Zero To Hero 🚀 initiative, featuring blog posts authored by our Product Engineering teams for #ServerlessSeptember. Posts were originally published on the Apps on Azure blog on Microsoft Tech Community.


    What We'll Cover

    • Using Visual Studio
    • Using Visual Studio Code: Docker, ACA extensions
    • Using Azure CLI
    • Using CI/CD Pipelines


    Last week, @kendallroden wrote about what it means to be Cloud-Native and how Azure Container Apps provides a serverless containers platform for hosting all of your Cloud-Native applications. Today, we’ll walk through a few ways to get your apps up and running on Azure Container Apps.

    Depending on where you are in your Cloud-Native app development journey, you might choose to use different tools to deploy your apps.

    • “Right-click, publish” – Deploying an app directly from an IDE or code editor is often seen as a bad practice, but it’s one of the quickest ways to test out an app in a cloud environment.
    • Command line interface – CLIs are useful for deploying apps from a terminal. Commands can be run manually or in a script.
    • Continuous integration/deployment – To deploy production apps, the recommended approach is to automate the process in a robust CI/CD pipeline.

    Let's explore some of these options in more depth.

    Visual Studio

    Visual Studio 2022 has built-in support for deploying .NET applications to Azure Container Apps. You can use the familiar publish dialog to provision Container Apps resources and deploy to them directly. This helps you prototype an app and see it run in Azure Container Apps with the least amount of effort.

    Journey to the cloud with Azure Container Apps

    Once you’re happy with the app and it’s ready for production, Visual Studio allows you to push your code to GitHub and set up a GitHub Actions workflow to build and deploy your app every time you push changes. You can do this by checking a box.

    Journey to the cloud with Azure Container Apps

    Visual Studio Code

    There are a couple of valuable extensions that you’ll want to install if you’re working in VS Code.

    Docker extension

    The Docker extension provides commands for building a container image for your app and pushing it to a container registry. It can even do this without requiring Docker Desktop on your local machine --- the “Build image in Azure” command remotely builds and pushes a container image to Azure Container Registry.

    Journey to the cloud with Azure Container Apps

    And if your app doesn’t have a dockerfile, the extension will generate one for you.

    Journey to the cloud with Azure Container Apps

    Azure Container Apps extension

    Once you’ve built your container image and pushed it to a registry, the Azure Container Apps VS Code extension provides commands for creating a container app and deploying revisions using the image you’ve built.

    Journey to the cloud with Azure Container Apps

    Azure CLI

    The Azure CLI can be used to manage pretty much anything in Azure. For Azure Container Apps, you’ll find commands for creating, updating, and managing your Container Apps resources.

    Just like in VS Code, with a few commands in the Azure CLI, you can create your Azure resources, build and push your container image, and then deploy it to a container app.

    To make things as simple as possible, the Azure CLI also has an “az containerapp up” command. This single command takes care of everything that’s needed to turn your source code from your local machine to a cloud-hosted application in Azure Container Apps.

    az containerapp up --name myapp --source ./src

    We saw earlier that Visual Studio can generate a GitHub Actions workflow to automatically build and deploy your app on every commit. “az containerapp up” can do this too. The following adds a workflow to a repo.

    az containerapp up --name myapp --repo https://github.com/myorg/myproject

    CI/CD pipelines

    When it’s time to take your app to production, it’s strongly recommended to set up a CI/CD pipeline to automatically and repeatably build, test, and deploy it. We’ve already seen that tools such as Visual Studio and Azure CLI can automatically generate a workflow for GitHub Actions. You can set up a pipeline in Azure DevOps too. This is an example Azure DevOps pipeline.

    trigger:
    branches:
    include:
    - main

    pool:
    vmImage: ubuntu-latest

    stages:

    - stage: Build

    jobs:
    - job: build
    displayName: Build app

    steps:
    - task: Docker@2
    inputs:
    containerRegistry: 'myregistry'
    repository: 'hello-aca'
    command: 'buildAndPush'
    Dockerfile: 'hello-container-apps/Dockerfile'
    tags: '$(Build.BuildId)'

    - stage: Deploy

    jobs:
    - job: deploy
    displayName: Deploy app

    steps:
    - task: AzureCLI@2
    inputs:
    azureSubscription: 'my-subscription(5361b9d6-46ea-43c3-a898-15f14afb0db6)'
    scriptType: 'bash'
    scriptLocation: 'inlineScript'
    inlineScript: |
    # automatically install Container Apps CLI extension
    az config set extension.use_dynamic_install=yes_without_prompt

    # ensure registry is configured in container app
    az containerapp registry set \
    --name hello-aca \
    --resource-group mygroup \
    --server myregistry.azurecr.io \
    --identity system

    # update container app
    az containerapp update \
    --name hello-aca \
    --resource-group mygroup \
    --image myregistry.azurecr.io/hello-aca:$(Build.BuildId)

    Conclusion

    In this article, we looked at a few ways to deploy your Cloud-Native applications to Azure Container Apps and how to decide which one to use based on where you are in your app’s journey to the cloud.

    To learn more, visit Azure Container Apps | Microsoft Azure today!

    ASK THE EXPERT: LIVE Q&A

    The Azure Container Apps team will answer questions live on September 29.

    - - + + \ No newline at end of file diff --git a/blog/tags/azure-functions/page/8/index.html b/blog/tags/azure-functions/page/8/index.html index eef7b3b6fe..26b1930e57 100644 --- a/blog/tags/azure-functions/page/8/index.html +++ b/blog/tags/azure-functions/page/8/index.html @@ -14,13 +14,13 @@ - - + +

    16 posts tagged with "azure-functions"

    View All Tags

    · 5 min read
    Nitya Narasimhan
    Devanshi Joshi

    SEP 08: CHANGE IN PUBLISHING SCHEDULE

    Starting from Week 2 (Sep 8), we'll be publishing blog posts in batches rather than on a daily basis, so you can read a series of related posts together. Don't want to miss updates? Just subscribe to the feed


    Welcome to Day 8 of #30DaysOfServerless!

    This marks the end of our Week 1 Roadmap focused on Azure Functions!! Today, we'll do a quick recap of all #ServerlessSeptember activities in Week 1, set the stage for Week 2 - and leave you with some excellent tutorials you should explore to build more advanced scenarios with Azure Functions.

    Ready? Let's go.


    What We'll Cover

    • Azure Functions: Week 1 Recap
    • Advanced Functions: Explore Samples
    • End-to-End: Serverless Hacks & Cloud Skills
    • What's Next: Hello, Containers & Microservices
    • Challenge: Complete the Learning Path


    Week 1 Recap: #30Days & Functions

    Congratulations!! We made it to the end of Week 1 of #ServerlessSeptember. Let's recap what we learned so far:

    • In Core Concepts we looked at where Azure Functions fits into the serverless options available on Azure. And we learned about key concepts like Triggers, Bindings, Custom Handlers and Durable Functions.
    • In Build Your First Function we looked at the tooling options for creating Functions apps, testing them locally, and deploying them to Azure - as we built and deployed our first Functions app.
    • In the next 4 posts, we explored new Triggers, Integrations, and Scenarios - as we looked at building Functions Apps in Java, JavaScript, .NET and Python.
    • And in the Zero-To-Hero series, we learned about Durable Entities - and how we can use them to create stateful serverless solutions using a Chirper Sample as an example scenario.

    The illustrated roadmap below summarizes what we covered each day this week, as we bring our Functions-as-a-Service exploration to a close.


    Advanced Functions: Code Samples

    So, now that we've got our first Functions app under our belt, and validated our local development setup for tooling, where can we go next? A good next step is to explore different triggers and bindings, that drive richer end-to-end scenarios. For example:

    • Integrate Functions with Azure Logic Apps - we'll discuss Azure Logic Apps in Week 3. For now, think of it as a workflow automation tool that lets you integrate seamlessly with other supported Azure services to drive an end-to-end scenario. In this tutorial, we set up a workflow connecting Twitter (get tweet) to Azure Cognitive Services (analyze sentiment) - and use that to trigger an Azure Functions app to send email about the result.
    • Integrate Functions with Event Grid - we'll discuss Azure Event Grid in Week 3. For now, think of it as an eventing service connecting event sources (publishers) to event handlers (subscribers) at cloud scale. In this tutorial, we handle a common use case - a workflow where loading an image to Blob Storage triggers an Azure Functions app that implements a resize function, helping automatically generate thumbnails for the uploaded image.
    • Integrate Functions with CosmosDB and SignalR to bring real-time push-based notifications to your web app. It achieves this by using a Functions app that is triggered by changes in a CosmosDB backend, causing it to broadcast that update (push notification to connected web clients over SignalR, in real time.

    Want more ideas? Check out the Azure Samples for Functions for implementations, and browse the Azure Architecture Center for reference architectures from real-world scenarios that involve Azure Functions usage.


    E2E Scenarios: Hacks & Cloud Skills

    Want to systematically work your way through a single End-to-End scenario involving Azure Functions alongside other serverless support technologies? Check out the Serverless Hacks activity happening during #ServerlessSeptember, and learn to build this "Serverless Tollbooth Application" in a series of 10 challenges. Check out the video series for a reference solution in .NET and sign up for weekly office hours to join peers and discuss your solutions or challenges.

    Or perhaps you prefer to learn core concepts with code in a structured learning path? We have that covered. Check out the 12-module "Create Serverless Applications" course from Microsoft Learn which walks your through concepts, one at a time, with code. Even better - sign up for the free Cloud Skills Challenge and complete the same path (in under 30 days) but this time, with the added fun of competing against your peers for a spot on a leaderboard, and swag.


    What's Next? Hello, Cloud-Native!

    So where to next? In Week 2 we turn our attention from Functions-as-a-Service to building more complex backends using Containers and Microservices. We'll focus on two core technologies - Azure Container Apps and Dapr (Distributed Application Runtime) - both key components of a broader vision around Building Cloud-Native Applications in Azure.

    What is Cloud-Native you ask?

    Fortunately for you, we have an excellent introduction in our Zero-to-Hero article on Go Cloud-Native with Azure Container Apps - that explains the 5 pillars of Cloud-Native and highlights the value of Azure Container Apps (scenarios) and Dapr (sidecar architecture) for simplified microservices-based solution with auto-scale capability. Prefer a visual summary? Here's an illustrate guide to that article for convenience.

    Go Cloud-Native Download a higher resolution version of the image


    Take The Challenge

    We typically end each post with an exercise or activity to reinforce what you learned. For Week 1, we encourage you to take the Cloud Skills Challenge and work your way through at least a subset of the modules, for hands-on experience with the different Azure Functions concepts, integrations, and usage.

    See you in Week 2!

    - - + + \ No newline at end of file diff --git a/blog/tags/azure-functions/page/9/index.html b/blog/tags/azure-functions/page/9/index.html index 7a7a71810e..edb8dee526 100644 --- a/blog/tags/azure-functions/page/9/index.html +++ b/blog/tags/azure-functions/page/9/index.html @@ -14,15 +14,15 @@ - - + +

    16 posts tagged with "azure-functions"

    View All Tags

    · 7 min read
    Jay Miller

    Welcome to Day 7 of #30DaysOfServerless!

    Over the past couple of days, we've explored Azure Functions from the perspective of specific programming languages. Today we'll continue that trend by looking at Python - exploring the Timer Trigger and CosmosDB binding, and showcasing integration with a FastAPI-implemented web app.

    Ready? Let's go.


    What We'll Cover

    • Developer Guidance: Azure Functions On Python
    • Build & Deploy: Wildfire Detection Apps with Timer Trigger + CosmosDB
    • Demo: My Fire Map App: Using FastAPI and Azure Maps to visualize data
    • Next Steps: Explore Azure Samples
    • Exercise: Try this yourself!
    • Resources: For self-study!


    Developer Guidance

    If you're a Python developer new to serverless on Azure, start with the Azure Functions Python Developer Guide. It covers:

    • Quickstarts with Visual Studio Code and Azure CLI
    • Adopting best practices for hosting, reliability and efficiency.
    • Tutorials showcasing Azure automation, image classification and more
    • Samples showcasing Azure Functions features for Python developers

    Now let's dive in and build our first Python-based Azure Functions app.


    Detecting Wildfires Around the World?

    I live in California which is known for lots of wildfires. I wanted to create a proof of concept for developing an application that could let me know if there was a wildfire detected near my home.

    NASA has a few satelites orbiting the Earth that can detect wildfires. These satelites take scans of the radiative heat in and use that to determine the likelihood of a wildfire. NASA updates their information about every 30 minutes and it can take about four hours for to scan and process information.

    Fire Point Near Austin, TX

    I want to get the information but I don't want to ping NASA or another service every time I check.

    What if I occaisionally download all the data I need? Then I can ping that as much as I like.

    I can create a script that does just that. Any time I say I can create a script that is a verbal queue for me to consider using an Azure function. With the function being ran in the cloud, I can ensure the script runs even when I'm not at my computer.

    How the Timer Trigger Works

    This function will utilize the Timer Trigger. This means Azure will call this function to run at a scheduled interval. This isn't the only way to keep the data in sync, but we know that arcgis, the service that we're using says that data is only updated every 30 minutes or so.

    To learn more about the TimerTrigger as a concept, check out the Azure Functions documentation around Timers.

    When we create the function we tell it a few things like where the script will live (in our case in __init__.py) the type and direction and notably often it should run. We specify the timer using schedule": <The CRON INTERVAL>. For us we're using 0 0,30 * * * which means every 30 minutes at the hour and half-hour.

    {
    "scriptFile": "__init__.py",
    "bindings": [
    {
    "name": "reqTimer",
    "type": "timerTrigger",
    "direction": "in",
    "schedule": "0 0,30 * * * *"
    }
    ]
    }

    Next, we create the code that runs when the function is called.

    Connecting to the Database and our Source

    Disclaimer: The data that we're pulling is for educational purposes only. This is not meant to be a production level application. You're welcome play with this project but ensure that you're using the data in compliance with Esri.

    Our function does two important things.

    1. It pulls data from ArcGIS that meets the parameters
    2. It stores that pulled data into our database

    If you want to check out the code in its entirety, check out the GitHub repository.

    Pulling the data from ArcGIS is easy. We can use the ArcGIS Python API. Then, we need to load the service layer. Finally we query that layer for the specific data.

    def write_new_file_data(gis_id:str, layer:int=0) -> FeatureSet:
    """Returns a JSON String of the Dataframe"""
    fire_data = g.content.get(gis_id)
    feature = fire_data.layers[layer] # Loading Featured Layer from ArcGIS
    q = feature.query(
    where="confidence >= 65 AND hours_old <= 4", #The filter for the query
    return_distince_values=True,
    out_fields="confidence, hours_old", # The data we want to store with our points
    out_sr=4326, # The spatial reference of the data
    )
    return q

    Then we need to store the data in our database.

    We're using Cosmos DB for this. COSMOSDB is a NoSQL database, which means that the data looks a lot like a python dictionary as it's JSON. This means that we don't need to worry about converting the data into a format that can be stored in a relational database.

    The second reason is that Cosmos DB is tied into the Azure ecosystem so that if we want to create functions Azure events around it, we can.

    Our script grabs the information that we pulled from ArcGIS and stores it in our database.

    async with CosmosClient.from_connection_string(COSMOS_CONNECTION_STRING) as client:
    container = database.get_container_client(container=CONTAINER)
    for record in data:
    await container.create_item(
    record,
    enable_automatic_id_generation=True,
    )

    In our code each of these functions live in their own space. So in the main function we focus solely on what azure functions will be doing. The script that gets called is __init__.py. There we'll have the function call the other functions running.

    We created another function called load_and_write that does all the work outlined above. __init__.py will call that.

    async def main(reqTimer: func.TimerRequest) -> None:
    database=database
    container=container
    await update_db.load_and_write(gis_id=GIS_LAYER_ID, database=database, container=container)

    Then we deploy the function to Azure. I like to use VS Code's Azure Extension but you can also deploy it a few other ways.

    Deploying the function via VS Code

    Once the function is deployed we can load the Azure portal and see a ping whenever the function is called. The pings correspond to the Function being ran

    We can also see the data now living in the datastore. Document in Cosmos DB

    It's in the Database, Now What?

    Now the real fun begins. We just loaded the last bit of fire data into a database. We can now query that data and serve it to others.

    As I mentioned before, our Cosmos DB data is also stored in Azure, which means that we can deploy Azure Functions to trigger when new data is added. Perhaps you can use this to check for fires near you and use a Logic App to send an alert to your phone or email.

    Another option is to create a web application that talks to the database and displays the data. I've created an example of this using FastAPI – https://jm-func-us-fire-notify.azurewebsites.net.

    Website that Checks for Fires


    Next Steps

    This article showcased the Timer Trigger and the HTTP Trigger for Azure Functions in Python. Now try exploring other triggers and bindings by browsing Bindings code samples for Python and Azure Functions samples for Python

    Once you've tried out the samples, you may want to explore more advanced integrations or extensions for serverless Python scenarios. Here are some suggestions:

    And check out the resources for more tutorials to build up your Azure Functions skills.

    Exercise

    I encourage you to fork the repository and try building and deploying it yourself! You can see the TimerTrigger and a HTTPTrigger building the website.

    Then try extending it. Perhaps if wildfires are a big thing in your area, you can use some of the data available in Planetary Computer to check out some other datasets.

    Resources

    - - + + \ No newline at end of file diff --git a/blog/tags/azure-logic-apps/index.html b/blog/tags/azure-logic-apps/index.html index 7cdbdb011b..71ded4da34 100644 --- a/blog/tags/azure-logic-apps/index.html +++ b/blog/tags/azure-logic-apps/index.html @@ -14,14 +14,14 @@ - - + +

    3 posts tagged with "azure-logic-apps"

    View All Tags

    · 7 min read
    Devanshi Joshi

    It's Serverless September in a Nutshell! Join us as we unpack our month-long learning journey exploring the core technology pillars for Serverless architectures on Azure. Then end with a look at next steps to build your Cloud-native applications on Azure.


    What We'll Cover

    • Functions-as-a-Service (FaaS)
    • Microservices and Containers
    • Serverless Integrations
    • End-to-End Solutions
    • Developer Tools & #Hacktoberfest

    Banner for Serverless September


    Building Cloud-native Apps

    By definition, cloud-native technologies empower organizations to build and run scalable applications in modern, dynamic environments such as public, private, and hybrid clouds. You can learn more about cloud-native in Kendall Roden's #ServerlessSeptember post on Going Cloud-native with Azure Container Apps.

    Serveless technologies accelerate productivity and minimize costs for deploying applications at cloud scale. So, what can we build with serverless technologies in cloud-native on Azure? Anything that is event-driven - examples include:

    • Microservices - scaled by KEDA-compliant triggers
    • Public API Endpoints - scaled by #concurrent HTTP requests
    • Event-Driven Applications - scaled by length of message queue
    • Web Applications - scaled by #concurrent HTTP requests
    • Background Process - scaled by CPU and Memory usage

    Great - but as developers, we really want to know how we can get started building and deploying serverless solutions on Azure. That was the focus of our #ServerlessSeptember journey. Let's take a quick look at the four key themes.

    Functions-as-a-Service (FaaS)

    Functions-as-a-Service (FaaS) is the epitome of developer productivity for full-stack modern apps. As developers, you don't manage infrastructure and focus only on business logic and application code. And, with Serverless Compute you only pay for when your code runs - making this the simplest first step to begin migrating your application to cloud-native.

    In Azure, FaaS is provided by Azure Functions. Check out our Functions + Serverless on Azure to go from learning core concepts, to building your first Functions app in your programming language of choice. Azure functions support multiple programming languages including C#, F#, Java, JavaScript, Python, Typescript, and PowerShell.

    Want to get extended language support for languages like Go, and Rust? You can Use Custom Handlers to make this happen! But what if you want to have long-running functions, or create complex workflows involving more than one function? Read our post on Durable Entities to learn how you can orchestrate this with Azure Functions.

    Check out this recent AskTheExpert Q&A session with the Azure Functions team to get answers to popular community questions on Azure Functions features and usage.

    Microservices and Containers

    Functions-as-a-Service is an ideal first step towards serverless development. But Functions are just one of the 5 pillars of cloud-native. This week we'll look at two of the other pillars: microservices and containers - with specific focus on two core technologies: Azure Container Apps and Dapr (Distributed Application Runtime).

    In this 6-part series of posts, we walk through each technology independently, before looking at the value of building Azure Container Apps with Dapr.

    • In Hello Container Apps we learned core concepts & deployed our first ACA.
    • In Microservices Communication we learned about ACA environments and virtual networks, and how microservices communicate in ACA with a hands-on tutorial.
    • In Scaling Your Container Apps we learned about KEDA (Kubernetes Event-Driven Autoscaler) and configuring ACA for autoscaling with KEDA-compliant triggers.
    • In Build with Dapr we introduced the Distributed Application Runtime (Dapr), exploring its Building Block APIs and sidecar architecture for working with ACA.
    • In Secure ACA Access we learned how to secure ACA access to external services with - and without - Dapr, covering Secret Stores and Managed Identity.
    • Finally, Build ACA with Dapr tied it all together with a enterprise app scenario where an orders processor (ACA) uses Dapr APIs (PubSub, State Management) to receive and store order messages from Azure Service Bus.

    Build ACA with Dapr

    Check out this recent AskTheExpert Q&A session with the Azure Container Apps team for answers to popular community questions on core features and usage.

    Serverless Integrations

    In the first half of the month we looked at compute resources for building and deploying serverless applications. In the second half, we look at integration tools and resources that automate developer workflows to streamline the end-to-end developer experience.

    In Azure, this is enabled by services like Azure Logic Apps and Azure Event Grid. Azure Logic Apps provides a visual designer to create and automate workflows with little or no code involved. Azure Event Grid provides a highly-scable event broker with support for pub/sub communications to drive async event-driven architectures.

    • In Tracking Weather Data Changes With Logic Apps we look at how you can use Logic Apps to integrate the MSN weather service with Azure CosmosDB, allowing automated collection of weather data on changes.

    • In Teach the Cloud to Read & Categorize Mail we take it a step further, using Logic Apps to automate a workflow that includes a Computer Vision service to "read" images and store the results to CosmosDB.

    • In Integrate with Microsoft Graph we explore a multi-cloud scenario (Azure + M365) where change notifications from Microsoft Graph can be integrated using Logic Apps and Event Hubs to power an onboarding workflow.

    • In Cloud Events with Event Grid we learn about the CloudEvents specification (for consistently describing event data) - and learn how Event Grid brokers events in this format. Azure Logic Apps can be an Event handler (subscriber) that uses the event to trigger an automated workflow on receipt.

      Azure Event Grid And Logic Apps

    Want to explore other such integrations? Browse Azure Architectures and filter by selected Azure services for more real-world scenarios.


    End-to-End Solutions

    We've covered serverless compute solutions (for building your serverless applications) and serverless integration services to automate end-to-end workflows in synchronous or asynchronous event-driven architectures. In this final week, we want to leave you with a sense of end-to-end development tools and use cases that can be enabled by Serverless on Azure. Here are some key examples:

    ArticleDescription
    In this tutorial, you'll learn to deploy a containerized ASP.NET Core 6.0 application to Azure Container Apps - with a Blazor front-end and two Web API projects
    Deploy Java containers to cloudIn this tutorial you learn to build and deploy a Java application running on Spring Boot, by publishing it in a container to Azure Container Registry, then deploying to Azure Container Apps,, from ACR, via the Azure Portal.
    **Where am I? My GPS Location with Serverless Power Platform Custom Connector**In this step-by-step tutorial you learn to integrate a serverless application (built on Azure Functions and OpenAPI) with Power Platforms custom connectors via Azure API Management (API-M).This pattern can empower a new ecosystem of fusion apps for cases like inventory management.
    And in our Serverless Hacks initiative, we walked through an 8-step hack to build a serverless tollbooth. Check out this 12-part video walkthrough of a reference solution using .NET.

    Developer Tools

    But wait - there's more. Those are a sample of the end-to-end application scenarios that are built on serverless on Azure. But what about the developer experience? In this article, we say hello to the Azure Developer CLI - an open-source tool that streamlines your develop-deploy workflow, with simple commands that map to core stages of your development journey. Go from code to cloud with one CLI

    And watch this space for more such tutorials and content through October, including a special #Hacktoberfest focused initiative to encourage and support first-time contributors to open-source. Here's a sneak peek at the project we plan to share - the new awesome-azd templates gallery.


    Join us at Microsoft Ignite!

    Want to continue your learning journey, and learn about what's next for Serverless on Azure? Microsoft Ignite happens Oct 12-14 this year and has multiple sessions on relevant technologies and tools. Check out the Session Catalog and register here to attend online.

    - - + + \ No newline at end of file diff --git a/blog/tags/azure-logic-apps/page/2/index.html b/blog/tags/azure-logic-apps/page/2/index.html index 822dcb30af..f36aebaf6f 100644 --- a/blog/tags/azure-logic-apps/page/2/index.html +++ b/blog/tags/azure-logic-apps/page/2/index.html @@ -14,13 +14,13 @@ - - + +

    3 posts tagged with "azure-logic-apps"

    View All Tags

    · 9 min read
    Justin Yoo

    Welcome to Day 21 of #30DaysOfServerless!

    We've so far walked through what Azure Event Grid is and how it generally works. Today, let's discuss how Azure Event Grid deals with CloudEvents.


    What We'll Cover


    OK. Let's get started!

    What is CloudEvents?

    Needless to say, events are everywhere. Events come not only from event-driven systems but also from many different systems and devices, including IoT ones like Raspberry PI.

    But the problem is that every event publisher (system/device that creates events) describes their events differently, meaning there is no standard way of describing events. It has caused many issues between systems, mainly from the interoperability perspective.

    1. Consistency: No standard way of describing events resulted in developers having to write their own event handling logic for each event source.
    2. Accessibility: There were no common libraries, tooling and infrastructure to deliver events across systems.
    3. Productivity: The overall productivity decreases because of the lack of the standard format of events.

    Cloud Events Logo

    Therefore, CNCF (Cloud-Native Computing Foundation) has brought up the concept, called CloudEvents. CloudEvents is a specification that commonly describes event data. Conforming any event data to this spec will simplify the event declaration and delivery across systems and platforms and more, resulting in a huge productivity increase.

    How Azure Event Grid brokers CloudEvents

    Before CloudEvents, Azure Event Grid described events in their own way. Therefore, if you want to use Azure Event Grid, you should follow the event format/schema that Azure Event Grid declares. However, not every system/service/application follows the Azure Event Grid schema. Therefore, Azure Event Grid now supports CloudEvents spec as input and output formats.

    Azure Event Grid for Azure

    Take a look at the simple diagram below, which describes how Azure Event Grid captures events raised from various Azure services. In this diagram, Azure Key Vault takes the role of the event source or event publisher, and Azure Logic Apps takes the role of the event handler (I'll discuss Azure Logic Apps as the event handler later in this post). We use Azure Event Grid System Topic for Azure.

    Azure Event Grid for Azure

    Therefore, let's create an Azure Event Grid System Topic that captures events raised from Azure Key Vault when a new version of a secret is added.

    Azure Event Grid System Topic for Key Vault

    As Azure Event Grid makes use of the pub/sub pattern, you need to create the Azure Event Grid Subscription to consume the events. Here's the subscription that uses the Event Grid data format:

    ![Azure Event Grid System Subscription for Key Vault in Event Grid Format][./img/21-cloudevents-via-event-grid-03.png]

    Once you create the subscription, create a new version of the secret on Azure Key Vault. Then, Azure Key Vault raises an event, which is captured in the Event Grid format:

    [
    {
    "id": "6f44b9c0-d37e-40e7-89be-f70a6da291cc",
    "topic": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/rg-aegce-krc/providers/Microsoft.KeyVault/vaults/kv-xxxxxxxx",
    "subject": "hello",
    "eventType": "Microsoft.KeyVault.SecretNewVersionCreated",
    "data": {
    "Id": "https://kv-xxxxxxxx.vault.azure.net/secrets/hello/064dfc082fec463f8d4610ed6118811d",
    "VaultName": "kv-xxxxxxxx",
    "ObjectType": "Secret",
    "ObjectName": "hello",
    "Version": "064dfc082fec463f8d4610ed6118811d",
    "NBF": null,
    "EXP": null
    },
    "dataVersion": "1",
    "metadataVersion": "1",
    "eventTime": "2022-09-21T07:08:09.1234567Z"
    }
    ]

    So, how is it different from the CloudEvents format? Let's take a look. According to the spec, the JSON data in CloudEvents might look like this:

    {
    "id" : "C234-1234-1234",
    "source" : "/mycontext",
    "specversion" : "1.0",
    "type" : "com.example.someevent",
    "comexampleextension1" : "value",
    "time" : "2018-04-05T17:31:00Z",
    "datacontenttype" : "application/cloudevents+json",
    "data" : {
    "appinfoA" : "abc",
    "appinfoB" : 123,
    "appinfoC" : true
    }
    }

    This time, let's create another subscription using the CloudEvents schema. Here's how to create the subscription against the system topic:

    Azure Event Grid System Subscription for Key Vault in CloudEvents Format

    Therefore, Azure Key Vault emits the event data in the CloudEvents format:

    {
    "id": "6f44b9c0-d37e-40e7-89be-f70a6da291cc",
    "source": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/rg-aegce-krc/providers/Microsoft.KeyVault/vaults/kv-xxxxxxxx",
    "specversion": "1.0",
    "type": "Microsoft.KeyVault.SecretNewVersionCreated",
    "subject": "hello",
    "time": "2022-09-21T07:08:09.1234567Z",
    "data": {
    "Id": "https://kv-xxxxxxxx.vault.azure.net/secrets/hello/064dfc082fec463f8d4610ed6118811d",
    "VaultName": "kv-xxxxxxxx",
    "ObjectType": "Secret",
    "ObjectName": "hello",
    "Version": "064dfc082fec463f8d4610ed6118811d",
    "NBF": null,
    "EXP": null
    }
    }

    Can you identify some differences between the Event Grid format and the CloudEvents format? Fortunately, both Event Grid schema and CloudEvents schema look similar to each other. But they might be significantly different if you use a different event source outside Azure.

    Azure Event Grid for Systems outside Azure

    As mentioned above, the event data described outside Azure or your own applications within Azure might not be understandable by Azure Event Grid. In this case, we need to use Azure Event Grid Custom Topic. Here's the diagram for it:

    Azure Event Grid for Applications outside Azure

    Let's create the Azure Event Grid Custom Topic. When you create the topic, make sure that you use the CloudEvent schema during the provisioning process:

    Azure Event Grid Custom Topic

    If your application needs to publish events to Azure Event Grid Custom Topic, your application should build the event data in the CloudEvents format. If you use a .NET application, add the NuGet package first.

    dotnet add package Azure.Messaging.EventGrid

    Then, create the publisher instance. You've already got the topic endpoint URL and the access key.

    var topicEndpoint = new Uri("<Azure Event Grid Custom Topic Endpoint URL>");
    var credential = new AzureKeyCredential("<Azure Event Grid Custom Topic Access Key>");
    var publisher = new EventGridPublisherClient(topicEndpoint, credential);

    Now, build the event data like below. Make sure that you follow the CloudEvents schema that requires additional metadata like event source, event type and content type.

    var source = "/your/event/source";
    var type = "com.source.event.your/OnEventOccurs";

    var data = new MyEventData() { Hello = "World" };

    var @event = new CloudEvent(source, type, data);

    And finally, send the event to Azure Event Grid Custom Topic.

    await publisher.SendEventAsync(@event);

    The captured event data looks like the following:

    {
    "id": "cc2b2775-52b8-43b8-a7cc-c1c33c2b2e59",
    "source": "/your/event/source",
    "type": "com.source.event.my/OnEventOccurs",
    "data": {
    "Hello": "World"
    },
    "time": "2022-09-21T07:08:09.1234567+00:00",
    "specversion": "1.0"
    }

    However, due to limitations, someone might insist that their existing application doesn't or can't emit the event data in the CloudEvents format. In this case, what should we do? There's no standard way of sending the event data in the CloudEvents format to Azure Event Grid Custom Topic. One of the approaches we may be able to apply is to put a converter between the existing application and Azure Event Grid Custom Topic like below:

    Azure Event Grid for Applications outside Azure with Converter

    Once the Function app (or any converter app) receives legacy event data, it internally converts the CloudEvents format and publishes it to Azure Event Grid.

    var data = default(MyRequestData);
    using (var reader = new StreamReader(req.Body))
    {
    var serialised = await reader.ReadToEndAsync();
    data = JsonConvert.DeserializeObject<MyRequestData>(serialised);
    }

    var converted = new MyEventData() { Hello = data.Lorem };
    var @event = new CloudEvent(source, type, converted);

    The converted event data is captured like this:

    {
    "id": "df296da3-77cd-4da2-8122-91f631941610",
    "source": "/your/event/source",
    "type": "com.source.event.my/OnEventOccurs",
    "data": {
    "Hello": "ipsum"
    },
    "time": "2022-09-21T07:08:09.1234567+00:00",
    "specversion": "1.0"
    }

    This approach is beneficial in many integration scenarios to make all the event data canonicalised.

    How Azure Logic Apps consumes CloudEvents

    I put Azure Logic Apps as the event handler in the previous diagrams. According to the CloudEvents spec, each event handler must implement request validation to avoid abuse. One good thing about using Azure Logic Apps is that it has already implemented this request validation feature. It implies that we just subscribe to the topic and consume the event data.

    Create a new Logic Apps instance and add the HTTP Request trigger. Once it saves, you will get the endpoint URL.

    Azure Logic Apps with HTTP Request Trigger

    Then, create the Azure Event Grid Subscription with:

    • Endpoint type: Webhook
    • Endpoint URL: The Logic Apps URL from above.

    Azure Logic Apps with HTTP Request Trigger

    Once the subscription is ready, this Logic Apps works well as the event handler. Here's how it receives the CloudEvents data from the subscription.

    Azure Logic Apps that Received CloudEvents data

    Now you've got the CloudEvents data. It's entirely up to you to handle that event data however you want!

    Exercise: Try this yourself!

    You can fork this GitHub repository to your account and play around with it to see how Azure Event Grid with CloudEvents works. Alternatively, the "Deploy to Azure" button below will provision all necessary Azure resources and deploy an Azure Functions app to mimic the event publisher.

    Deploy To Azure

    Resources: For self-study!

    Want to know more about CloudEvents in real-life examples? Here are several resources you can take a look at:

    - - + + \ No newline at end of file diff --git a/blog/tags/azure-logic-apps/page/3/index.html b/blog/tags/azure-logic-apps/page/3/index.html index 08c8799fac..9b3f8a0ad8 100644 --- a/blog/tags/azure-logic-apps/page/3/index.html +++ b/blog/tags/azure-logic-apps/page/3/index.html @@ -14,13 +14,13 @@ - - + +

    3 posts tagged with "azure-logic-apps"

    View All Tags

    · 5 min read
    Nitya Narasimhan
    Devanshi Joshi

    SEP 08: CHANGE IN PUBLISHING SCHEDULE

    Starting from Week 2 (Sep 8), we'll be publishing blog posts in batches rather than on a daily basis, so you can read a series of related posts together. Don't want to miss updates? Just subscribe to the feed


    Welcome to Day 8 of #30DaysOfServerless!

    This marks the end of our Week 1 Roadmap focused on Azure Functions!! Today, we'll do a quick recap of all #ServerlessSeptember activities in Week 1, set the stage for Week 2 - and leave you with some excellent tutorials you should explore to build more advanced scenarios with Azure Functions.

    Ready? Let's go.


    What We'll Cover

    • Azure Functions: Week 1 Recap
    • Advanced Functions: Explore Samples
    • End-to-End: Serverless Hacks & Cloud Skills
    • What's Next: Hello, Containers & Microservices
    • Challenge: Complete the Learning Path


    Week 1 Recap: #30Days & Functions

    Congratulations!! We made it to the end of Week 1 of #ServerlessSeptember. Let's recap what we learned so far:

    • In Core Concepts we looked at where Azure Functions fits into the serverless options available on Azure. And we learned about key concepts like Triggers, Bindings, Custom Handlers and Durable Functions.
    • In Build Your First Function we looked at the tooling options for creating Functions apps, testing them locally, and deploying them to Azure - as we built and deployed our first Functions app.
    • In the next 4 posts, we explored new Triggers, Integrations, and Scenarios - as we looked at building Functions Apps in Java, JavaScript, .NET and Python.
    • And in the Zero-To-Hero series, we learned about Durable Entities - and how we can use them to create stateful serverless solutions using a Chirper Sample as an example scenario.

    The illustrated roadmap below summarizes what we covered each day this week, as we bring our Functions-as-a-Service exploration to a close.


    Advanced Functions: Code Samples

    So, now that we've got our first Functions app under our belt, and validated our local development setup for tooling, where can we go next? A good next step is to explore different triggers and bindings, that drive richer end-to-end scenarios. For example:

    • Integrate Functions with Azure Logic Apps - we'll discuss Azure Logic Apps in Week 3. For now, think of it as a workflow automation tool that lets you integrate seamlessly with other supported Azure services to drive an end-to-end scenario. In this tutorial, we set up a workflow connecting Twitter (get tweet) to Azure Cognitive Services (analyze sentiment) - and use that to trigger an Azure Functions app to send email about the result.
    • Integrate Functions with Event Grid - we'll discuss Azure Event Grid in Week 3. For now, think of it as an eventing service connecting event sources (publishers) to event handlers (subscribers) at cloud scale. In this tutorial, we handle a common use case - a workflow where loading an image to Blob Storage triggers an Azure Functions app that implements a resize function, helping automatically generate thumbnails for the uploaded image.
    • Integrate Functions with CosmosDB and SignalR to bring real-time push-based notifications to your web app. It achieves this by using a Functions app that is triggered by changes in a CosmosDB backend, causing it to broadcast that update (push notification to connected web clients over SignalR, in real time.

    Want more ideas? Check out the Azure Samples for Functions for implementations, and browse the Azure Architecture Center for reference architectures from real-world scenarios that involve Azure Functions usage.


    E2E Scenarios: Hacks & Cloud Skills

    Want to systematically work your way through a single End-to-End scenario involving Azure Functions alongside other serverless support technologies? Check out the Serverless Hacks activity happening during #ServerlessSeptember, and learn to build this "Serverless Tollbooth Application" in a series of 10 challenges. Check out the video series for a reference solution in .NET and sign up for weekly office hours to join peers and discuss your solutions or challenges.

    Or perhaps you prefer to learn core concepts with code in a structured learning path? We have that covered. Check out the 12-module "Create Serverless Applications" course from Microsoft Learn which walks your through concepts, one at a time, with code. Even better - sign up for the free Cloud Skills Challenge and complete the same path (in under 30 days) but this time, with the added fun of competing against your peers for a spot on a leaderboard, and swag.


    What's Next? Hello, Cloud-Native!

    So where to next? In Week 2 we turn our attention from Functions-as-a-Service to building more complex backends using Containers and Microservices. We'll focus on two core technologies - Azure Container Apps and Dapr (Distributed Application Runtime) - both key components of a broader vision around Building Cloud-Native Applications in Azure.

    What is Cloud-Native you ask?

    Fortunately for you, we have an excellent introduction in our Zero-to-Hero article on Go Cloud-Native with Azure Container Apps - that explains the 5 pillars of Cloud-Native and highlights the value of Azure Container Apps (scenarios) and Dapr (sidecar architecture) for simplified microservices-based solution with auto-scale capability. Prefer a visual summary? Here's an illustrate guide to that article for convenience.

    Go Cloud-Native Download a higher resolution version of the image


    Take The Challenge

    We typically end each post with an exercise or activity to reinforce what you learned. For Week 1, we encourage you to take the Cloud Skills Challenge and work your way through at least a subset of the modules, for hands-on experience with the different Azure Functions concepts, integrations, and usage.

    See you in Week 2!

    - - + + \ No newline at end of file diff --git a/blog/tags/cloud-native/index.html b/blog/tags/cloud-native/index.html index 84e3729d13..0726b5d172 100644 --- a/blog/tags/cloud-native/index.html +++ b/blog/tags/cloud-native/index.html @@ -14,13 +14,13 @@ - - + +

    One post tagged with "cloud-native"

    View All Tags

    · 5 min read
    Savannah Ostrowski

    Welcome to Beyond #30DaysOfServerless! in October!

    Yes, it's October!! And since we ended #ServerlessSeptember with a focus on End-to-End Development for Serverless on Azure, we thought it would be good to share updates in October that can help you skill up even further.

    Today, we're following up on the Code to Cloud with azd blog post (Day #29) where we introduced the Azure Developer CLI (azd), an open-source tool for streamlining your end-to-end developer experience going from local development environment to Azure cloud. In today's post, we celebrate the October 2022 release of the tool, with three cool new features.

    And if it's October, it must be #Hacktoberfest!! Read on to learn about how you can take advantage of one of the new features, to contribute to the azd open-source community and ecosystem!

    Ready? Let's go!


    What We'll Cover

    • Azure Friday: Introducing the Azure Developer CLI (Video)
    • October 2022 Release: What's New in the Azure Developer CLI?
      • Azure Pipelines for CI/CD: Learn more
      • Improved Infrastructure as Code structure via Bicep modules: Learn more
      • A new azd template gallery: The new azd-templates gallery for community use! Learn more
    • Awesome-Azd: The new azd-templates gallery for Community use
      • Features: discover, create, contribute, request - templates
      • Hacktoberfest: opportunities to contribute in October - and beyond!


    Azure Friday

    This post is a follow-up to our #ServerlessSeptember post on Code to Cloud with Azure Developer CLI where we introduced azd, a new open-source tool that makes it quick and simple for you to move your application from a local development environment to Azure, streamlining your end-to-end developer workflow in the process.

    Prefer to watch a video overview? I have you covered! Check out my recent conversation with Scott Hanselman on Azure Friday where we:

    • talked about the code-to-cloud developer journey
    • walkthrough the ins and outs of an azd template
    • explored Azure Developer CLI commands in the terminal and VS Code, and
    • (probably most importantly) got a web app up and running on Azure with a database, Key Vault and monitoring all in a couple of minutes

    October Release

    We're pleased to announce the October 2022 release of the Azure Developer CLI (currently 0.3.0-beta.2). Read the release announcement for more details. Here are the highlights:

    • Azure Pipelines for CI/CD: This addresses azure-dev#101, adding support for Azure Pipelines (alongside GitHub Actions) as a CI/CD provider. Learn more about usage and related documentation.
    • Improved Infrastructure as Code structure via Bicep modules: This addresses azure-dev#543, which recognized the complexity of using a single resources.bicep file for all resources. With this release, azd templates now come with Bicep modules organized by purpose making it easier to edit and understand. Learn more about this structure, and how to use it.
    • New Templates Gallery - awesome-azd: This addresses azure-dev#398, which aimed to make templates more discoverable and easier to contribute. Learn more about how the new gallery improves the template discovery experience.

    In the next section, we'll dive briefly into the last feature, introducing the new awesome-azd site and resource for templates discovery and contribution. And, since it's #Hacktoberfest season, we'll talk about the Contributor Guide and the many ways you can contribute to this project - with, or without, code.


    It's awesome-azd

    Welcome to awesome-azd a new template gallery hosted on GitHub Pages, and meant to be a destination site for discovering, requesting, and contributing azd-templates for community use!

    In addition, it's README reflects the awesome-list resource format, providing a location for the community to share "best of" resources for Azure Developer CLI - from blog posts and videos, to full-scale tutorials and templates.

    The Gallery is organized into three main areas:

    Take a minute to explore the Gallery and note the features:

    • Search for templates by name
    • Requested Templates - indicating asks from the community
    • Featured Templates - highlighting high-quality templates
    • Filters - to discover templates by and/or query combinations

    Check back often to see the latest contributed templates and requests!


    Hacktoberfest

    So, why is this a good time to talk about the Gallery? Because October means it's time for #Hacktoberfest - a month-long celebration of open-source projects and their maintainers, and an opportunity for first-time contributors to get support and guidance making their first pull-requests! Check out the #Hacktoberfest topic on GitHub for projects you can contribute to.

    And we hope you think of awesome-azd as another possible project to contribute to.

    Check out the FAQ section to learn how to create, discover, and contribute templates. Or take a couple of minutes to watch this video walkthrough from Jon Gallant:

    And don't hesitate to reach out to us - either via Issues on the repo, or in the Discussions section of this site, to give us feedback!

    Happy Hacking! 🎃


    - - + + \ No newline at end of file diff --git a/blog/tags/cloudevents/index.html b/blog/tags/cloudevents/index.html index 90c02811fb..feb1ea8237 100644 --- a/blog/tags/cloudevents/index.html +++ b/blog/tags/cloudevents/index.html @@ -14,13 +14,13 @@ - - + +

    One post tagged with "cloudevents"

    View All Tags

    · 9 min read
    Justin Yoo

    Welcome to Day 21 of #30DaysOfServerless!

    We've so far walked through what Azure Event Grid is and how it generally works. Today, let's discuss how Azure Event Grid deals with CloudEvents.


    What We'll Cover


    OK. Let's get started!

    What is CloudEvents?

    Needless to say, events are everywhere. Events come not only from event-driven systems but also from many different systems and devices, including IoT ones like Raspberry PI.

    But the problem is that every event publisher (system/device that creates events) describes their events differently, meaning there is no standard way of describing events. It has caused many issues between systems, mainly from the interoperability perspective.

    1. Consistency: No standard way of describing events resulted in developers having to write their own event handling logic for each event source.
    2. Accessibility: There were no common libraries, tooling and infrastructure to deliver events across systems.
    3. Productivity: The overall productivity decreases because of the lack of the standard format of events.

    Cloud Events Logo

    Therefore, CNCF (Cloud-Native Computing Foundation) has brought up the concept, called CloudEvents. CloudEvents is a specification that commonly describes event data. Conforming any event data to this spec will simplify the event declaration and delivery across systems and platforms and more, resulting in a huge productivity increase.

    How Azure Event Grid brokers CloudEvents

    Before CloudEvents, Azure Event Grid described events in their own way. Therefore, if you want to use Azure Event Grid, you should follow the event format/schema that Azure Event Grid declares. However, not every system/service/application follows the Azure Event Grid schema. Therefore, Azure Event Grid now supports CloudEvents spec as input and output formats.

    Azure Event Grid for Azure

    Take a look at the simple diagram below, which describes how Azure Event Grid captures events raised from various Azure services. In this diagram, Azure Key Vault takes the role of the event source or event publisher, and Azure Logic Apps takes the role of the event handler (I'll discuss Azure Logic Apps as the event handler later in this post). We use Azure Event Grid System Topic for Azure.

    Azure Event Grid for Azure

    Therefore, let's create an Azure Event Grid System Topic that captures events raised from Azure Key Vault when a new version of a secret is added.

    Azure Event Grid System Topic for Key Vault

    As Azure Event Grid makes use of the pub/sub pattern, you need to create the Azure Event Grid Subscription to consume the events. Here's the subscription that uses the Event Grid data format:

    ![Azure Event Grid System Subscription for Key Vault in Event Grid Format][./img/21-cloudevents-via-event-grid-03.png]

    Once you create the subscription, create a new version of the secret on Azure Key Vault. Then, Azure Key Vault raises an event, which is captured in the Event Grid format:

    [
    {
    "id": "6f44b9c0-d37e-40e7-89be-f70a6da291cc",
    "topic": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/rg-aegce-krc/providers/Microsoft.KeyVault/vaults/kv-xxxxxxxx",
    "subject": "hello",
    "eventType": "Microsoft.KeyVault.SecretNewVersionCreated",
    "data": {
    "Id": "https://kv-xxxxxxxx.vault.azure.net/secrets/hello/064dfc082fec463f8d4610ed6118811d",
    "VaultName": "kv-xxxxxxxx",
    "ObjectType": "Secret",
    "ObjectName": "hello",
    "Version": "064dfc082fec463f8d4610ed6118811d",
    "NBF": null,
    "EXP": null
    },
    "dataVersion": "1",
    "metadataVersion": "1",
    "eventTime": "2022-09-21T07:08:09.1234567Z"
    }
    ]

    So, how is it different from the CloudEvents format? Let's take a look. According to the spec, the JSON data in CloudEvents might look like this:

    {
    "id" : "C234-1234-1234",
    "source" : "/mycontext",
    "specversion" : "1.0",
    "type" : "com.example.someevent",
    "comexampleextension1" : "value",
    "time" : "2018-04-05T17:31:00Z",
    "datacontenttype" : "application/cloudevents+json",
    "data" : {
    "appinfoA" : "abc",
    "appinfoB" : 123,
    "appinfoC" : true
    }
    }

    This time, let's create another subscription using the CloudEvents schema. Here's how to create the subscription against the system topic:

    Azure Event Grid System Subscription for Key Vault in CloudEvents Format

    Therefore, Azure Key Vault emits the event data in the CloudEvents format:

    {
    "id": "6f44b9c0-d37e-40e7-89be-f70a6da291cc",
    "source": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/rg-aegce-krc/providers/Microsoft.KeyVault/vaults/kv-xxxxxxxx",
    "specversion": "1.0",
    "type": "Microsoft.KeyVault.SecretNewVersionCreated",
    "subject": "hello",
    "time": "2022-09-21T07:08:09.1234567Z",
    "data": {
    "Id": "https://kv-xxxxxxxx.vault.azure.net/secrets/hello/064dfc082fec463f8d4610ed6118811d",
    "VaultName": "kv-xxxxxxxx",
    "ObjectType": "Secret",
    "ObjectName": "hello",
    "Version": "064dfc082fec463f8d4610ed6118811d",
    "NBF": null,
    "EXP": null
    }
    }

    Can you identify some differences between the Event Grid format and the CloudEvents format? Fortunately, both Event Grid schema and CloudEvents schema look similar to each other. But they might be significantly different if you use a different event source outside Azure.

    Azure Event Grid for Systems outside Azure

    As mentioned above, the event data described outside Azure or your own applications within Azure might not be understandable by Azure Event Grid. In this case, we need to use Azure Event Grid Custom Topic. Here's the diagram for it:

    Azure Event Grid for Applications outside Azure

    Let's create the Azure Event Grid Custom Topic. When you create the topic, make sure that you use the CloudEvent schema during the provisioning process:

    Azure Event Grid Custom Topic

    If your application needs to publish events to Azure Event Grid Custom Topic, your application should build the event data in the CloudEvents format. If you use a .NET application, add the NuGet package first.

    dotnet add package Azure.Messaging.EventGrid

    Then, create the publisher instance. You've already got the topic endpoint URL and the access key.

    var topicEndpoint = new Uri("<Azure Event Grid Custom Topic Endpoint URL>");
    var credential = new AzureKeyCredential("<Azure Event Grid Custom Topic Access Key>");
    var publisher = new EventGridPublisherClient(topicEndpoint, credential);

    Now, build the event data like below. Make sure that you follow the CloudEvents schema that requires additional metadata like event source, event type and content type.

    var source = "/your/event/source";
    var type = "com.source.event.your/OnEventOccurs";

    var data = new MyEventData() { Hello = "World" };

    var @event = new CloudEvent(source, type, data);

    And finally, send the event to Azure Event Grid Custom Topic.

    await publisher.SendEventAsync(@event);

    The captured event data looks like the following:

    {
    "id": "cc2b2775-52b8-43b8-a7cc-c1c33c2b2e59",
    "source": "/your/event/source",
    "type": "com.source.event.my/OnEventOccurs",
    "data": {
    "Hello": "World"
    },
    "time": "2022-09-21T07:08:09.1234567+00:00",
    "specversion": "1.0"
    }

    However, due to limitations, someone might insist that their existing application doesn't or can't emit the event data in the CloudEvents format. In this case, what should we do? There's no standard way of sending the event data in the CloudEvents format to Azure Event Grid Custom Topic. One of the approaches we may be able to apply is to put a converter between the existing application and Azure Event Grid Custom Topic like below:

    Azure Event Grid for Applications outside Azure with Converter

    Once the Function app (or any converter app) receives legacy event data, it internally converts the CloudEvents format and publishes it to Azure Event Grid.

    var data = default(MyRequestData);
    using (var reader = new StreamReader(req.Body))
    {
    var serialised = await reader.ReadToEndAsync();
    data = JsonConvert.DeserializeObject<MyRequestData>(serialised);
    }

    var converted = new MyEventData() { Hello = data.Lorem };
    var @event = new CloudEvent(source, type, converted);

    The converted event data is captured like this:

    {
    "id": "df296da3-77cd-4da2-8122-91f631941610",
    "source": "/your/event/source",
    "type": "com.source.event.my/OnEventOccurs",
    "data": {
    "Hello": "ipsum"
    },
    "time": "2022-09-21T07:08:09.1234567+00:00",
    "specversion": "1.0"
    }

    This approach is beneficial in many integration scenarios to make all the event data canonicalised.

    How Azure Logic Apps consumes CloudEvents

    I put Azure Logic Apps as the event handler in the previous diagrams. According to the CloudEvents spec, each event handler must implement request validation to avoid abuse. One good thing about using Azure Logic Apps is that it has already implemented this request validation feature. It implies that we just subscribe to the topic and consume the event data.

    Create a new Logic Apps instance and add the HTTP Request trigger. Once it saves, you will get the endpoint URL.

    Azure Logic Apps with HTTP Request Trigger

    Then, create the Azure Event Grid Subscription with:

    • Endpoint type: Webhook
    • Endpoint URL: The Logic Apps URL from above.

    Azure Logic Apps with HTTP Request Trigger

    Once the subscription is ready, this Logic Apps works well as the event handler. Here's how it receives the CloudEvents data from the subscription.

    Azure Logic Apps that Received CloudEvents data

    Now you've got the CloudEvents data. It's entirely up to you to handle that event data however you want!

    Exercise: Try this yourself!

    You can fork this GitHub repository to your account and play around with it to see how Azure Event Grid with CloudEvents works. Alternatively, the "Deploy to Azure" button below will provision all necessary Azure resources and deploy an Azure Functions app to mimic the event publisher.

    Deploy To Azure

    Resources: For self-study!

    Want to know more about CloudEvents in real-life examples? Here are several resources you can take a look at:

    - - + + \ No newline at end of file diff --git a/blog/tags/custom-connector/index.html b/blog/tags/custom-connector/index.html index 17c514b681..124c2eb9ad 100644 --- a/blog/tags/custom-connector/index.html +++ b/blog/tags/custom-connector/index.html @@ -14,13 +14,13 @@ - - + +

    One post tagged with "custom-connector"

    View All Tags

    · 14 min read
    Justin Yoo

    Welcome to Day 28 of #30DaysOfServerless!

    Since it's the serverless end-to-end week, I'm going to discuss how to use a serverless application Azure Functions with OpenAPI extension to be seamlessly integrated with Power Platform custom connector through Azure API Management - in a post I call "Where am I? My GPS Location with Serverless Power Platform Custom Connector"

    OK. Are you ready? Let's get started!


    What We'll Cover

    • What is Power Platform custom connector?
    • Proxy app to Google Maps and Naver Map API
    • API Management integration
    • Two ways of building custom connector
    • Where am I? Power Apps app
    • Exercise: Try this yourself!
    • Resources: For self-study!


    SAMPLE REPO

    Want to follow along? Check out the sample app on GitHub repository used in this post.

    What is Power Platform custom connector?

    Power Platform is a low-code/no-code application development tool for fusion teams that consist of a group of people. Those people come from various disciplines, including field experts (domain experts), IT professionals and professional developers, to draw business values successfully. Within the fusion team, the domain experts become citizen developers or low-code developers by Power Platform. In addition, Making Power Platform more powerful is that it offers hundreds of connectors to other Microsoft 365 and third-party services like SAP, ServiceNow, Salesforce, Google, etc.

    However, what if you want to use your internal APIs or APIs not yet offering their official connectors? Here's an example. If your company has an inventory management system, and you want to use it within your Power Apps or Power Automate. That point is exactly where Power Platform custom connectors is necessary.

    Inventory Management System for Power Apps

    Therefore, Power Platform custom connectors enrich those citizen developers' capabilities because those connectors can connect any API applications for the citizen developers to use.

    In this post, let's build a custom connector that provides a static map image generated by Google Maps API and Naver Map API using your GPS location.

    Proxy app to Google Maps and Naver Map API

    First, let's build an Azure Functions app that connects to Google Maps and Naver Map. Suppose that you've already got the API keys for both services. If you haven't yet, get the keys first by visiting here for Google and here for Naver. Then, store them to local.settings.json within your Azure Functions app.

    {
    "Values": {
    ...
    "Maps__Google__ApiKey": "<GOOGLE_MAPS_API_KEY>",
    "Maps__Naver__ClientId": "<NAVER_MAP_API_CLIENT_ID>",
    "Maps__Naver__ClientSecret": "<NAVER_MAP_API_CLIENT_SECRET>"
    }
    }

    Here's the sample logic to get the static image from Google Maps API. It takes the latitude and longitude of your current location and image zoom level, then returns the static map image. There are a few hard-coded assumptions, though:

    • The image size should be 400x400.
    • The image should be in .png format.
    • The marker should show be red and show my location.
    public class GoogleMapService : IMapService
    {
    public async Task<byte[]> GetMapAsync(HttpRequest req)
    {
    var latitude = req.Query["lat"];
    var longitude = req.Query["long"];
    var zoom = (string)req.Query["zoom"] ?? "14";

    var sb = new StringBuilder();
    sb.Append("https://maps.googleapis.com/maps/api/staticmap")
    .Append($"?center={latitude},{longitude}")
    .Append("&size=400x400")
    .Append($"&zoom={zoom}")
    .Append($"&markers=color:red|{latitude},{longitude}")
    .Append("&format=png32")
    .Append($"&key={this._settings.Google.ApiKey}");
    var requestUri = new Uri(sb.ToString());

    var bytes = await this._http.GetByteArrayAsync(requestUri).ConfigureAwait(false);

    return bytes;
    }
    }

    The NaverMapService class has a similar logic with the same input and assumptions. Here's the code:

    public class NaverMapService : IMapService
    {
    public async Task<byte[]> GetMapAsync(HttpRequest req)
    {
    var latitude = req.Query["lat"];
    var longitude = req.Query["long"];
    var zoom = (string)req.Query["zoom"] ?? "13";

    var sb = new StringBuilder();
    sb.Append("https://naveropenapi.apigw.ntruss.com/map-static/v2/raster")
    .Append($"?center={longitude},{latitude}")
    .Append("&w=400")
    .Append("&h=400")
    .Append($"&level={zoom}")
    .Append($"&markers=color:blue|pos:{longitude}%20{latitude}")
    .Append("&format=png")
    .Append("&lang=en");
    var requestUri = new Uri(sb.ToString());

    this._http.DefaultRequestHeaders.Clear();
    this._http.DefaultRequestHeaders.Add("X-NCP-APIGW-API-KEY-ID", this._settings.Naver.ClientId);
    this._http.DefaultRequestHeaders.Add("X-NCP-APIGW-API-KEY", this._settings.Naver.ClientSecret);

    var bytes = await this._http.GetByteArrayAsync(requestUri).ConfigureAwait(false);

    return bytes;
    }
    }

    Let's take a look at the function endpoints. Here's for the Google Maps and Naver Map. As the GetMapAsync(req) method returns a byte array value, you need to transform it as FileContentResult, with the content type of image/png.

    // Google Maps
    public class GoogleMapsTrigger
    {
    [FunctionName(nameof(GoogleMapsTrigger.GetGoogleMapImage))]
    public async Task<IActionResult> GetGoogleMapImage(
    [HttpTrigger(AuthorizationLevel.Anonymous, "GET", Route = "google/image")] HttpRequest req)
    {
    this._logger.LogInformation("C# HTTP trigger function processed a request.");

    var bytes = await this._service.GetMapAsync(req).ConfigureAwait(false);

    return new FileContentResult(bytes, "image/png");
    }
    }

    // Naver Map
    public class NaverMapsTrigger
    {
    [FunctionName(nameof(NaverMapsTrigger.GetNaverMapImage))]
    public async Task<IActionResult> GetNaverMapImage(
    [HttpTrigger(AuthorizationLevel.Anonymous, "GET", Route = "naver/image")] HttpRequest req)
    {
    this._logger.LogInformation("C# HTTP trigger function processed a request.");

    var bytes = await this._service.GetMapAsync(req).ConfigureAwait(false);

    return new FileContentResult(bytes, "image/png");
    }
    }

    Then, add the OpenAPI capability to each function endpoint. Here's the example:

    // Google Maps
    public class GoogleMapsTrigger
    {
    [FunctionName(nameof(GoogleMapsTrigger.GetGoogleMapImage))]
    // ⬇️⬇️⬇️ Add decorators provided by the OpenAPI extension ⬇️⬇️⬇️
    [OpenApiOperation(operationId: nameof(GoogleMapsTrigger.GetGoogleMapImage), tags: new[] { "google" })]
    [OpenApiParameter(name: "lat", In = ParameterLocation.Query, Required = true, Type = typeof(string), Description = "The **latitude** parameter")]
    [OpenApiParameter(name: "long", In = ParameterLocation.Query, Required = true, Type = typeof(string), Description = "The **longitude** parameter")]
    [OpenApiParameter(name: "zoom", In = ParameterLocation.Query, Required = false, Type = typeof(string), Description = "The **zoom level** parameter &ndash; Default value is `14`")]
    [OpenApiResponseWithBody(statusCode: HttpStatusCode.OK, contentType: "image/png", bodyType: typeof(byte[]), Description = "The map image as an OK response")]
    // ⬆️⬆️⬆️ Add decorators provided by the OpenAPI extension ⬆️⬆️⬆️
    public async Task<IActionResult> GetGoogleMapImage(
    [HttpTrigger(AuthorizationLevel.Anonymous, "GET", Route = "google/image")] HttpRequest req)
    {
    ...
    }
    }

    // Naver Map
    public class NaverMapsTrigger
    {
    [FunctionName(nameof(NaverMapsTrigger.GetNaverMapImage))]
    // ⬇️⬇️⬇️ Add decorators provided by the OpenAPI extension ⬇️⬇️⬇️
    [OpenApiOperation(operationId: nameof(NaverMapsTrigger.GetNaverMapImage), tags: new[] { "naver" })]
    [OpenApiParameter(name: "lat", In = ParameterLocation.Query, Required = true, Type = typeof(string), Description = "The **latitude** parameter")]
    [OpenApiParameter(name: "long", In = ParameterLocation.Query, Required = true, Type = typeof(string), Description = "The **longitude** parameter")]
    [OpenApiParameter(name: "zoom", In = ParameterLocation.Query, Required = false, Type = typeof(string), Description = "The **zoom level** parameter &ndash; Default value is `13`")]
    [OpenApiResponseWithBody(statusCode: HttpStatusCode.OK, contentType: "image/png", bodyType: typeof(byte[]), Description = "The map image as an OK response")]
    // ⬆️⬆️⬆️ Add decorators provided by the OpenAPI extension ⬆️⬆️⬆️
    public async Task<IActionResult> GetNaverMapImage(
    [HttpTrigger(AuthorizationLevel.Anonymous, "GET", Route = "naver/image")] HttpRequest req)
    {
    ...
    }
    }

    Run the function app in the local. Here are the latitude and longitude values for Seoul, Korea.

    • latitude: 37.574703
    • longitude: 126.978519

    Google Map for Seoul

    It seems to be working! Let's deploy it to Azure.

    API Management integration

    Visual Studio 2022 provides a built-in deployment tool for Azure Functions app onto Azure. In addition, the deployment tool supports seamless integration with Azure API Management as long as your Azure Functions app enables the OpenAPI capability. In this post, I'm going to use this feature. Right-mouse click on the Azure Functions project and select the "Publish" menu.

    Visual Studio context menu for publish

    Then, you will see the publish screen. Click the "➕ New" button to create a new publish profile.

    Create a new publish profile

    Choose "Azure" and click the "Next" button.

    Choose the target platform for publish

    Select the app instance. This time simply pick up the "Azure Function App (Windows)" option, then click "Next".

    Choose the target OS for publish

    If you already provision an Azure Function app instance, you will see it on the screen. Otherwise, create a new one. Then, click "Next".

    Choose the target instance for publish

    In the next step, you are asked to choose the Azure API Management instance for integration. Choose one, or create a new one. Then, click "Next".

    Choose the APIM instance for integration

    Finally, select the publish method either local publish or GitHub Actions workflow. Let's pick up the local publish method for now. Then, click "Finish".

    Choose the deployment type

    The publish profile has been created. Click "Close" to move on.

    Publish profile created

    Now the function app is ready for deployment. Click the "Publish" button and see how it goes.

    Publish function app

    The Azure function app has been deployed and integrated with the Azure API Management instance.

    Function app published

    Go to the published function app site, and everything looks OK.

    Function app on Azure

    And API Management shows the function app integrated perfectly.

    Function app integrated with APIM

    Now, you are ready to create a custom connector. Let's move on.

    Two ways of building custom connector

    There are two ways to create a custom connector.

    Export custom connector from API Management

    First, you can directly use the built-in API Management feature. Then, click the ellipsis icon and select the "Create Power Connector" menu.

    Create Power Connector menu

    Then, you are redirected to this screen. While the "API" and "API display name" fields are pre-populated, you need to choose the Power Platform environment tied to your tenant. Choose an environment, click "Authenticate", and click "Create".

    Create custom connector screen

    Check your custom connector on Power Apps or Power Automate side.

    Custom connector created on Power Apps

    However, there's a caveat to this approach. Because it's tied to your tenant, you should use the second approach if you want to use this custom connector on the other tenant.

    Import custom connector from OpenAPI document or URL

    Click the ellipsis icon again and select the "Export" menu.

    Export menu

    On the Export API screen, choose the "OpenAPI v2 (JSON)" panel because Power Platform custom connector currently accepts version 2 of the OpenAPI document.

    Select OpenAPI v2

    Download the OpenAPI document to your local computer and move to your Power Apps or Power Automate page under your desired environment. I'm going to use the Power Automate page. First, go to the "Data" ➡️ "Custom connectors" page. Then, click the "➕ New custom connector" ➡️ "Import an OpenAPI file" at the top right corner.

    New custom connector

    When a modal pops up, give the custom connector name and import the OpenAPI document exported above. Then, click "Continue".

    Import custom connector

    Actually, that's it! Next, click the "✔️ Create connector" button to create the connector.

    Create custom connector

    Go back to the custom connector page, and you will see the "Maps API" custom connector you just created.

    Custom connector imported

    So, you are ready to create a Power Apps app to display your location on Google Maps or Naver Map! Let's move on.

    Where am I? Power Apps app

    Open the Power Apps Studio, and create an empty canvas app, named Who am I with a phone layout.

    Custom connector integration

    To use the custom connector created above, you need to add it to the Power App. Click the cylinder icon on the left and click the "Add data" button.

    Add custom connector to data pane

    Search the custom connector name, "Maps API", and click the custom connector to add.

    Search custom connector

    To use the custom connector, you also need to create a connection to it. Click the "Connect" button and move on.

    Create connection to custom connector

    Now, you've got the connection to the custom connector.

    Connection to custom connector ready

    Controls

    Let's build the Power Apps app. First of all, put three controls Image, Slider and Button onto the canvas.

    Power Apps control added

    Click the "Screen1" control and change the value on the property "OnVisible" to the formula below. The formula stores the current slider value in the zoomlevel collection.

    ClearCollect(
    zoomlevel,
    Slider1.Value
    )

    Click the "Botton1" control and change the value on the property "OnSelected" to the formula below. It passes the current latitude, longitude and zoom level to the custom connector and receives the image data. The received image data is stored in the result collection.

    ClearCollect(
    result,
    MAPS.GetGoogleMapImage(
    Location.Latitude,
    Location.Longitude,
    { zoom: First(zoomlevel).Value }
    )
    )

    Click the "Image1" control and change the value on the property "Image" to the formula below. It gets the image data from the result collection.

    First(result).Url

    Click the "Slider1" control and change the value on the property "OnChange" to the formula below. It stores the current slider value to the zoomlevel collection, followed by calling the custom connector to get the image data against the current location.

    ClearCollect(
    zoomlevel,
    Slider1.Value
    );
    ClearCollect(
    result,
    MAPS.GetGoogleMapImage(
    Location.Latitude,
    Location.Longitude,
    { zoom: First(zoomlevel).Value }
    )
    )

    That seems to be OK. Let's click the "Where am I?" button. But it doesn't show the image. The First(result).Url value is actually similar to this:

    appres://blobmanager/1090a86393a843adbfcf428f0b90e91b/1

    It's the image reference value somewhere you can't get there.

    Workaround Power Automate workflow

    Therefore, you need a workaround using a Power Automate workflow to sort out this issue. Open the Power Automate Studio, create an instant cloud flow with the Power App trigger, and give it the "Where am I" name. Then add input parameters of lat, long and zoom.

    Power Apps trigger on Power Automate workflow

    Add custom connector action to get the map image.

    Select action to get the Google Maps image

    In the action, pass the appropriate parameters to the action.

    Pass parameters to the custom connector action

    Add a "Response" action and put the following values into each field.

    • "Body" field:

      {
      "base64Image": <power_automate_expression>
      }

      The <power_automate_expression> should be concat('data:', body('GetGoogleMapImage')?['$content-type'], ';base64,', body('GetGoogleMapImage')?['$content']).

    • "Response Body JSON Schema" field:

      {
      "type": "object",
      "properties": {
      "base64Image": {
      "type": "string"
      }
      }
      }

    Format the Response action

    Let's return to the Power Apps Studio and add the Power Automate workflow you created.

    Add Power Automate workflow

    Select "Button1" and change the value on the property "OnSelect" below. It replaces the direct call to the custom connector with the Power Automate workflow.

    ClearCollect(
    result,
    WhereamI.Run(
    Location.Latitude,
    Location.Longitude,
    First(zoomlevel).Value
    )
    )

    Also, change the value on the property "OnChange" of the "Slider1" control below, replacing the custom connector call with the Power Automate workflow call.

    ClearCollect(
    zoomlevel,
    Slider1.Value
    );
    ClearCollect(
    result,
    WhereamI.Run(
    Location.Latitude,
    Location.Longitude,
    First(zoomlevel).Value
    )
    )

    And finally, change the "Image1" control's "Image" property value below.

    First(result).base64Image

    The workaround has been applied. Click the "Where am I?" button to see your current location from Google Maps.

    Run Power Apps app #1

    If you change the slider left or right, you will see either the zoomed-in image or the zoomed-out image.

    Run Power Apps app #2

    Now, you've created a Power Apps app to show your current location using:

    • Google Maps API through the custom connector, and
    • Custom connector written in Azure Functions with OpenAPI extension!

    Exercise: Try this yourself!

    You can fork this GitHub repository to your account and play around with it to see how the custom connector works. After forking the repository, make sure that you create all the necessary secrets to your repository documented in the README file.

    Then, click the "Deploy to Azure" button, and it will provision all necessary Azure resources and deploy an Azure Functions app for a custom connector.

    Deploy To Azure

    Once everything is deployed successfully, try to create a Power Apps app and Power Automate workflow to see your current location in real-time!

    Resources: For self-study!

    Want to know more about Power Platform custom connector and Azure Functions OpenAPI extension? Here are several resources you can take a look at:

    - - + + \ No newline at end of file diff --git a/blog/tags/dapr/index.html b/blog/tags/dapr/index.html index 457ee4a4b4..27182da52d 100644 --- a/blog/tags/dapr/index.html +++ b/blog/tags/dapr/index.html @@ -14,14 +14,14 @@ - - + +

    16 posts tagged with "dapr"

    View All Tags

    · 7 min read
    Devanshi Joshi

    It's Serverless September in a Nutshell! Join us as we unpack our month-long learning journey exploring the core technology pillars for Serverless architectures on Azure. Then end with a look at next steps to build your Cloud-native applications on Azure.


    What We'll Cover

    • Functions-as-a-Service (FaaS)
    • Microservices and Containers
    • Serverless Integrations
    • End-to-End Solutions
    • Developer Tools & #Hacktoberfest

    Banner for Serverless September


    Building Cloud-native Apps

    By definition, cloud-native technologies empower organizations to build and run scalable applications in modern, dynamic environments such as public, private, and hybrid clouds. You can learn more about cloud-native in Kendall Roden's #ServerlessSeptember post on Going Cloud-native with Azure Container Apps.

    Serveless technologies accelerate productivity and minimize costs for deploying applications at cloud scale. So, what can we build with serverless technologies in cloud-native on Azure? Anything that is event-driven - examples include:

    • Microservices - scaled by KEDA-compliant triggers
    • Public API Endpoints - scaled by #concurrent HTTP requests
    • Event-Driven Applications - scaled by length of message queue
    • Web Applications - scaled by #concurrent HTTP requests
    • Background Process - scaled by CPU and Memory usage

    Great - but as developers, we really want to know how we can get started building and deploying serverless solutions on Azure. That was the focus of our #ServerlessSeptember journey. Let's take a quick look at the four key themes.

    Functions-as-a-Service (FaaS)

    Functions-as-a-Service (FaaS) is the epitome of developer productivity for full-stack modern apps. As developers, you don't manage infrastructure and focus only on business logic and application code. And, with Serverless Compute you only pay for when your code runs - making this the simplest first step to begin migrating your application to cloud-native.

    In Azure, FaaS is provided by Azure Functions. Check out our Functions + Serverless on Azure to go from learning core concepts, to building your first Functions app in your programming language of choice. Azure functions support multiple programming languages including C#, F#, Java, JavaScript, Python, Typescript, and PowerShell.

    Want to get extended language support for languages like Go, and Rust? You can Use Custom Handlers to make this happen! But what if you want to have long-running functions, or create complex workflows involving more than one function? Read our post on Durable Entities to learn how you can orchestrate this with Azure Functions.

    Check out this recent AskTheExpert Q&A session with the Azure Functions team to get answers to popular community questions on Azure Functions features and usage.

    Microservices and Containers

    Functions-as-a-Service is an ideal first step towards serverless development. But Functions are just one of the 5 pillars of cloud-native. This week we'll look at two of the other pillars: microservices and containers - with specific focus on two core technologies: Azure Container Apps and Dapr (Distributed Application Runtime).

    In this 6-part series of posts, we walk through each technology independently, before looking at the value of building Azure Container Apps with Dapr.

    • In Hello Container Apps we learned core concepts & deployed our first ACA.
    • In Microservices Communication we learned about ACA environments and virtual networks, and how microservices communicate in ACA with a hands-on tutorial.
    • In Scaling Your Container Apps we learned about KEDA (Kubernetes Event-Driven Autoscaler) and configuring ACA for autoscaling with KEDA-compliant triggers.
    • In Build with Dapr we introduced the Distributed Application Runtime (Dapr), exploring its Building Block APIs and sidecar architecture for working with ACA.
    • In Secure ACA Access we learned how to secure ACA access to external services with - and without - Dapr, covering Secret Stores and Managed Identity.
    • Finally, Build ACA with Dapr tied it all together with a enterprise app scenario where an orders processor (ACA) uses Dapr APIs (PubSub, State Management) to receive and store order messages from Azure Service Bus.

    Build ACA with Dapr

    Check out this recent AskTheExpert Q&A session with the Azure Container Apps team for answers to popular community questions on core features and usage.

    Serverless Integrations

    In the first half of the month we looked at compute resources for building and deploying serverless applications. In the second half, we look at integration tools and resources that automate developer workflows to streamline the end-to-end developer experience.

    In Azure, this is enabled by services like Azure Logic Apps and Azure Event Grid. Azure Logic Apps provides a visual designer to create and automate workflows with little or no code involved. Azure Event Grid provides a highly-scable event broker with support for pub/sub communications to drive async event-driven architectures.

    • In Tracking Weather Data Changes With Logic Apps we look at how you can use Logic Apps to integrate the MSN weather service with Azure CosmosDB, allowing automated collection of weather data on changes.

    • In Teach the Cloud to Read & Categorize Mail we take it a step further, using Logic Apps to automate a workflow that includes a Computer Vision service to "read" images and store the results to CosmosDB.

    • In Integrate with Microsoft Graph we explore a multi-cloud scenario (Azure + M365) where change notifications from Microsoft Graph can be integrated using Logic Apps and Event Hubs to power an onboarding workflow.

    • In Cloud Events with Event Grid we learn about the CloudEvents specification (for consistently describing event data) - and learn how Event Grid brokers events in this format. Azure Logic Apps can be an Event handler (subscriber) that uses the event to trigger an automated workflow on receipt.

      Azure Event Grid And Logic Apps

    Want to explore other such integrations? Browse Azure Architectures and filter by selected Azure services for more real-world scenarios.


    End-to-End Solutions

    We've covered serverless compute solutions (for building your serverless applications) and serverless integration services to automate end-to-end workflows in synchronous or asynchronous event-driven architectures. In this final week, we want to leave you with a sense of end-to-end development tools and use cases that can be enabled by Serverless on Azure. Here are some key examples:

    ArticleDescription
    In this tutorial, you'll learn to deploy a containerized ASP.NET Core 6.0 application to Azure Container Apps - with a Blazor front-end and two Web API projects
    Deploy Java containers to cloudIn this tutorial you learn to build and deploy a Java application running on Spring Boot, by publishing it in a container to Azure Container Registry, then deploying to Azure Container Apps,, from ACR, via the Azure Portal.
    **Where am I? My GPS Location with Serverless Power Platform Custom Connector**In this step-by-step tutorial you learn to integrate a serverless application (built on Azure Functions and OpenAPI) with Power Platforms custom connectors via Azure API Management (API-M).This pattern can empower a new ecosystem of fusion apps for cases like inventory management.
    And in our Serverless Hacks initiative, we walked through an 8-step hack to build a serverless tollbooth. Check out this 12-part video walkthrough of a reference solution using .NET.

    Developer Tools

    But wait - there's more. Those are a sample of the end-to-end application scenarios that are built on serverless on Azure. But what about the developer experience? In this article, we say hello to the Azure Developer CLI - an open-source tool that streamlines your develop-deploy workflow, with simple commands that map to core stages of your development journey. Go from code to cloud with one CLI

    And watch this space for more such tutorials and content through October, including a special #Hacktoberfest focused initiative to encourage and support first-time contributors to open-source. Here's a sneak peek at the project we plan to share - the new awesome-azd templates gallery.


    Join us at Microsoft Ignite!

    Want to continue your learning journey, and learn about what's next for Serverless on Azure? Microsoft Ignite happens Oct 12-14 this year and has multiple sessions on relevant technologies and tools. Check out the Session Catalog and register here to attend online.

    - - + + \ No newline at end of file diff --git a/blog/tags/dapr/page/10/index.html b/blog/tags/dapr/page/10/index.html index 0cdbfef60a..97cea9eae9 100644 --- a/blog/tags/dapr/page/10/index.html +++ b/blog/tags/dapr/page/10/index.html @@ -14,14 +14,14 @@ - - + +

    16 posts tagged with "dapr"

    View All Tags

    · 11 min read
    Kendall Roden

    Welcome to Day 13 of #30DaysOfServerless!

    In the previous post, we learned about all things Distributed Application Runtime (Dapr) and highlighted the capabilities you can unlock through managed Dapr in Azure Container Apps! Today, we'll dive into how we can make use of Container Apps secrets and managed identities to securely access cloud-hosted resources that your Container Apps depend on!

    Ready? Let's go.


    What We'll Cover

    • Secure access to external services overview
    • Using Container Apps Secrets
    • Using Managed Identity for connecting to Azure resources
    • Using Dapr secret store component references (Dapr-only)
    • Conclusion
    • Resources: For self-study!


    Securing access to external services

    In most, if not all, microservice-based applications, one or more services in the system will rely on other cloud-hosted resources; Think external services like databases, secret stores, message brokers, event sources, etc. To interact with these services, an application must have the ability to establish a secure connection. Traditionally, an application will authenticate to these backing resources using some type of connection string or password.

    I'm not sure if it was just me, but one of the first things I learned as a developer was to ensure credentials and other sensitive information were never checked into the codebase. The ability to inject these values at runtime is a non-negotiable.

    In Azure Container Apps, applications can securely leverage connection information via Container Apps Secrets. If the resource is Azure-based, a more ideal solution that removes the dependence on secrets altogether is using Managed Identity.

    Specifically for Dapr-enabled container apps, users can now tap into the power of the Dapr secrets API! With this new capability unlocked in Container Apps, users can call the Dapr secrets API from application code to securely access secrets from Key Vault or other backing secret stores. In addition, customers can also make use of a secret store component reference when wiring up Dapr state store components and more!

    ALSO, I'm excited to share that support for Dapr + Managed Identity is now available!!. What does this mean? It means that you can enable Managed Identity for your container app - and when establishing connections via Dapr, the Dapr sidecar can use this identity! This means simplified components without the need for secrets when connecting to Azure services!

    Let's dive a bit deeper into the following three topics:

    1. Using Container Apps secrets in your container apps
    2. Using Managed Identity to connect to Azure services
    3. Connecting to services securely for Dapr-enabled apps

    Secure access to external services without Dapr

    Leveraging Container Apps secrets at runtime

    Users can leverage this approach for any values which need to be securely stored, however, it is recommended to use Managed Identity where possible when connecting to Azure-specific resources.

    First, let's establish a few important points regarding secrets in container apps:

    • Secrets are scoped at the container app level, meaning secrets cannot be shared across container apps today
    • When running in multiple-revision mode,
      • changes to secrets do not generate a new revision
      • running revisions will not be automatically restarted to reflect changes. If you want to force-update existing container app revisions to reflect the changed secrets values, you will need to perform revision restarts.
    STEP 1

    Provide the secure value as a secret parameter when creating your container app using the syntax "SECRET_NAME=SECRET_VALUE"

    az containerapp create \
    --resource-group "my-resource-group" \
    --name queuereader \
    --environment "my-environment-name" \
    --image demos/queuereader:v1 \
    --secrets "queue-connection-string=$CONNECTION_STRING"
    STEP 2

    Create an environment variable which references the value of the secret created in step 1 using the syntax "ENV_VARIABLE_NAME=secretref:SECRET_NAME"

    az containerapp create \
    --resource-group "my-resource-group" \
    --name myQueueApp \
    --environment "my-environment-name" \
    --image demos/myQueueApp:v1 \
    --secrets "queue-connection-string=$CONNECTIONSTRING" \
    --env-vars "QueueName=myqueue" "ConnectionString=secretref:queue-connection-string"

    This ConnectionString environment variable can be used within your application code to securely access the connection string value at runtime.

    Using Managed Identity to connect to Azure services

    A managed identity from Azure Active Directory (Azure AD) allows your container app to access other Azure AD-protected resources. This approach is recommended where possible as it eliminates the need for managing secret credentials in your container apps and allows you to properly scope the permissions needed for a given container app using role-based access control. Both system-assigned and user-assigned identities are available in container apps. For more background on managed identities in Azure AD, see Managed identities for Azure resources.

    To configure your app with a system-assigned managed identity you will follow similar steps to the following:

    STEP 1

    Run the following command to create a system-assigned identity for your container app

    az containerapp identity assign \
    --name "myQueueApp" \
    --resource-group "my-resource-group" \
    --system-assigned
    STEP 2

    Retrieve the identity details for your container app and store the Principal ID for the identity in a variable "PRINCIPAL_ID"

    az containerapp identity show \
    --name "myQueueApp" \
    --resource-group "my-resource-group"
    STEP 3

    Assign the appropriate roles and permissions to your container app's managed identity using the Principal ID in step 2 based on the resources you need to access (example below)

    az role assignment create \
    --role "Storage Queue Data Contributor" \
    --assignee $PRINCIPAL_ID \
    --scope "/subscriptions/<subscription>/resourceGroups/<resource-group>/providers/Microsoft.Storage/storageAccounts/<storage-account>/queueServices/default/queues/<queue>"

    After running the above commands, your container app will be able to access your Azure Store Queue because it's managed identity has been assigned the "Store Queue Data Contributor" role. The role assignments you create will be contingent solely on the resources your container app needs to access. To instrument your code to use this managed identity, see more details here.

    In addition to using managed identity to access services from your container app, you can also use managed identity to pull your container images from Azure Container Registry.

    Secure access to external services with Dapr

    For Dapr-enabled apps, there are a few ways to connect to the resources your solutions depend on. In this section, we will discuss when to use each approach.

    1. Using Container Apps secrets in your Dapr components
    2. Using Managed Identity with Dapr Components
    3. Using Dapr Secret Stores for runtime secrets and component references

    Using Container Apps secrets in Dapr components

    Prior to providing support for the Dapr Secret's Management building block, this was the only approach available for securely storing sensitive values for use in Dapr components.

    In Dapr OSS, when no secret store reference is provided in a Dapr component file, the default secret store is set to "Kubernetes secrets". In Container Apps, we do not expose the ability to use this default store. Rather, Container Apps secrets can be used in it's place.

    With the introduction of the Secrets API and the ability to use Dapr + Managed Identity, this approach is useful for a limited number of scenarios:

    • Quick demos and dev/test scenarios using the Container Apps CLI
    • Securing values when a secret store is not configured or available for use
    • Using service principal credentials to configure an Azure Key Vault secret store component (Using Managed Identity is recommend)
    • Securing access credentials which may be required when creating a non-Azure secret store component
    STEP 1

    Create a Dapr component which can be used by one or more services in the container apps environment. In the below example, you will create a secret to store the storage account key and reference this secret from the appropriate Dapr metadata property.

       componentType: state.azure.blobstorage
    version: v1
    metadata:
    - name: accountName
    value: testStorage
    - name: accountKey
    secretRef: account-key
    - name: containerName
    value: myContainer
    secrets:
    - name: account-key
    value: "<STORAGE_ACCOUNT_KEY>"
    scopes:
    - myApp
    STEP 2

    Deploy the Dapr component using the below command with the appropriate arguments.

     az containerapp env dapr-component set \
    --name "my-environment" \
    --resource-group "my-resource-group" \
    --dapr-component-name statestore \
    --yaml "./statestore.yaml"

    Using Managed Identity with Dapr Components

    Dapr-enabled container apps can now make use of managed identities within Dapr components. This is the most ideal path for connecting to Azure services securely, and allows for the removal of sensitive values in the component itself.

    The Dapr sidecar makes use of the existing identities available within a given container app; Dapr itself does not have it's own identity. Therefore, the steps to enable Dapr + MI are similar to those in the section regarding managed identity for non-Dapr apps. See example steps below specifically for using a system-assigned identity:

    1. Create a system-assigned identity for your container app

    2. Retrieve the identity details for your container app and store the Principal ID for the identity in a variable "PRINCIPAL_ID"

    3. Assign the appropriate roles and permissions (for accessing resources backing your Dapr components) to your ACA's managed identity using the Principal ID

    4. Create a simplified Dapr component without any secrets required

          componentType: state.azure.blobstorage
      version: v1
      metadata:
      - name: accountName
      value: testStorage
      - name: containerName
      value: myContainer
      scopes:
      - myApp
    5. Deploy the component to test the connection from your container app via Dapr!

    Keep in mind, all Dapr components will be loaded by each Dapr-enabled container app in an environment by default. In order to avoid apps without the appropriate permissions from loading a component unsuccessfully, use scopes. This will ensure that only applications with the appropriate identities to access the backing resource load the component.

    Using Dapr Secret Stores for runtime secrets and component references

    Dapr integrates with secret stores to provide apps and other components with secure storage and access to secrets such as access keys and passwords. The Dapr Secrets API is now available for use in Container Apps.

    Using Dapr’s secret store building block typically involves the following:

    • Setting up a component for a specific secret store solution.
    • Retrieving secrets using the Dapr secrets API in the application code.
    • Optionally, referencing secrets in Dapr component files.

    Let's walk through a couple sample workflows involving the use of Dapr's Secrets Management capabilities!

    Setting up a component for a specific secret store solution

    1. Create an Azure Key Vault instance for hosting the secrets required by your application.

      az keyvault create --name "<your-unique-keyvault-name>" --resource-group "my-resource-group" --location "<your-location>"
    2. Create an Azure Key Vault component in your environment without the secrets values, as the connection will be established to Azure Key Vault via Managed Identity.

          componentType: secretstores.azure.keyvault
      version: v1
      metadata:
      - name: vaultName
      value: "[your_keyvault_name]"
      scopes:
      - myApp
      az containerapp env dapr-component set \
      --name "my-environment" \
      --resource-group "my-resource-group" \
      --dapr-component-name secretstore \
      --yaml "./secretstore.yaml"
    3. Run the following command to create a system-assigned identity for your container app

      az containerapp identity assign \
      --name "myApp" \
      --resource-group "my-resource-group" \
      --system-assigned
    4. Retrieve the identity details for your container app and store the Principal ID for the identity in a variable "PRINCIPAL_ID"

      az containerapp identity show \
      --name "myApp" \
      --resource-group "my-resource-group"
    5. Assign the appropriate roles and permissions to your container app's managed identity to access Azure Key Vault

      az role assignment create \
      --role "Key Vault Secrets Officer" \
      --assignee $PRINCIPAL_ID \
      --scope /subscriptions/{subscriptionid}/resourcegroups/{resource-group-name}/providers/Microsoft.KeyVault/vaults/{key-vault-name}
    6. Begin using the Dapr Secrets API in your application code to retrieve secrets! See additional details here.

    Referencing secrets in Dapr component files

    Once a Dapr secret store component is available in the environment, it can be used to retrieve secrets for use in other components. For example, when creating a state store component, you can add a reference to the Dapr secret store from which you would like to source connection information. You will no longer use secrets directly in the component spec, but rather will instruct the Dapr sidecar to retrieve the secrets from the specified store.

          componentType: state.azure.blobstorage
    version: v1
    metadata:
    - name: accountName
    value: testStorage
    - name: accountKey
    secretRef: account-key
    - name: containerName
    value: myContainer
    secretStoreComponent: "<SECRET_STORE_COMPONENT_NAME>"
    scopes:
    - myApp

    Summary

    In this post, we have covered the high-level details on how to work with secret values in Azure Container Apps for both Dapr and Non-Dapr apps. In the next article, we will walk through a complex Dapr example from end-to-end which makes use of the new support for Dapr + Managed Identity. Stayed tuned for additional documentation around Dapr secrets as it will be release in the next two weeks!

    Resources

    Here are the main resources to explore for self-study:

    - - + + \ No newline at end of file diff --git a/blog/tags/dapr/page/11/index.html b/blog/tags/dapr/page/11/index.html index 618cf87192..8ca9aeb025 100644 --- a/blog/tags/dapr/page/11/index.html +++ b/blog/tags/dapr/page/11/index.html @@ -14,13 +14,13 @@ - - + +

    16 posts tagged with "dapr"

    View All Tags

    · 8 min read
    Nitya Narasimhan

    Welcome to Day 12 of #30DaysOfServerless!

    So far we've looked at Azure Container Apps - what it is, how it enables microservices communication, and how it enables auto-scaling with KEDA compliant scalers. Today we'll shift gears and talk about Dapr - the Distributed Application Runtime - and how it makes microservices development with ACA easier with core building blocks and a sidecar architecture!

    Ready? Let's go!


    What We'll Cover

    • What is Dapr and why use it?
    • Building Block APIs
    • Dapr Quickstart and Tutorials
    • Dapr-enabled ACA: A Sidecar Approach
    • Exercise: Build & Deploy a Dapr-enabled ACA.
    • Resources: For self-study!


    Hello, Dapr!

    Building distributed applications is hard. Building reliable and portable microservces means having middleware that deals with challenges like service discovery, sync and async communications, state management, secure information sharing and more. Integrating these support services into your application can be challenging from both development and maintenance perspectives, adding complexity that is independent of the core application logic you want to focus on.

    This is where Dapr (Distributed Application Runtime) shines - it's defined as::

    a portable, event-driven runtime that makes it easy for any developer to build resilient, stateless and stateful applications that run on the cloud and edge and embraces the diversity of languages and developer frameworks.

    But what does this actually mean to me as an app developer?


    Dapr + Apps: A Sidecar Approach

    The strength of Dapr lies in its ability to:

    • abstract complexities of distributed systems middleware - with Building Block APIs that implement components using best practices to tackle key challenges.
    • implement a Sidecar Pattern with interactions via APIs - allowing applications to keep their codebase clean and focus on app logic.
    • be Incrementally Adoptable - allowing developers to start by integrating one API, then evolving to use more as and when needed.
    • be Platform Agnostic - allowing applications to be developed in a preferred language or framework without impacting integration capabilities.

    The application-dapr sidecar interaction is illustrated below. The API abstraction allows applications to get the desired functionality without having to know how it was implemented, or without having to integrate Dapr-specific code into their codebase. Note how the sidecar process listens on port 3500 and the API provides clear routes for the specific building blocks supported by Dapr (e.g, /secrets, /state etc.)


    Dapr Building Blocks: API Interactions

    Dapr Building Blocks refers to HTTP and gRPC endpoints exposed by Dapr API endpoints exposed by the Dapr sidecar, providing key capabilities like state management, observability, service-to-service invocation, pub/sub messaging and more to the associated application.

    Building Blocks: Under the Hood
    The Dapr API is implemented by modular components that codify best practices for tackling the specific challenge that they represent. The API abstraction allows component implementations to evolve, or alternatives to be used , without requiring changes to the application codebase.

    The latest Dapr release has the building blocks shown in the above figure. Not all capabilities are available to Azure Container Apps by default - check the documentation for the latest updates on this. For now, Azure Container Apps + Dapr integration provides the following capabilities to the application:

    In the next section, we'll dive into Dapr-enabled Azure Container Apps. Before we do that, here are a couple of resources to help you explore the Dapr platform by itself, and get more hands-on experience with the concepts and capabilities:

    • Dapr Quickstarts - build your first Dapr app, then explore quickstarts for a core APIs including service-to-service invocation, pub/sub, state mangement, bindings and secrets management.
    • Dapr Tutorials - go beyond the basic quickstart and explore more realistic service integrations and usage scenarios. Try the distributed calculator example!

    Integrate Dapr & Azure Container Apps

    Dapr currently has a v1.9 (preview) version, but Azure Container Apps supports Dapr v1.8. In this section, we'll look at what it takes to enable, configure, and use, Dapr integration with Azure Container Apps. It involves 3 steps: enabling Dapr using settings, configuring Dapr components (API) for use, then invoking the APIs.

    Here's a simple a publisher-subscriber scenario from the documentation. We have two Container apps identified as publisher-app and subscriber-app deployed in a single environment. Each ACA has an activated daprd sidecar, allowing them to use the Pub/Sub API to communicate asynchronously with each other - without having to write the underlying pub/sub implementation themselves. Rather, we can see that the Dapr API uses a pubsub,azure.servicebus component to implement that capability.

    Pub/sub example

    Let's look at how this is setup.

    1. Enable Dapr in ACA: Settings

    We can enable Dapr integration in the Azure Container App during creation by specifying settings in one of two ways, based on your development preference:

    • Using Azure CLI: use custom commandline options for each setting
    • Using Infrastructure-as-Code (IaC): using properties for Bicep, ARM templates

    Once enabled, Dapr will run in the same environment as the Azure Container App, and listen on port 3500 for API requests. The Dapr sidecar can be shared my multiple Container Apps deployed in the same environment.

    There are four main settings we will focus on for this demo - the example below shows the ARM template properties, but you can find the equivalent CLI parameters here for comparison.

    • dapr.enabled - enable Dapr for Azure Container App
    • dapr.appPort - specify port on which app is listening
    • dapr.appProtocol - specify if using http (default) or gRPC for API
    • dapr.appId - specify unique application ID for service discovery, usage

    These are defined under the properties.configuration section for your resource. Changing Dapr settings does not update the revision but it will restart ACA revisions and replicas. Here is what the relevant section of the ARM template looks like for the publisher-app ACA in the scenario shown above.

    "dapr": {
    "enabled": true,
    "appId": "publisher-app",
    "appProcotol": "http",
    "appPort": 80
    }

    2. Configure Dapr in ACA: Components

    The next step after activating the Dapr sidecar, is to define the APIs that you want to use and potentially specify the Dapr components (specific implementations of that API) that you prefer. These components are created at environment-level and by default, Dapr-enabled containers apps in an environment will load the complete set of deployed components -- use the scopes property to ensure only components needed by a given app are loaded at runtime. Here's what the ARM template resources section looks like for the example above. This tells us that the environment has a dapr-pubsub component of type pubsub.azure.servicebus deployed - where that component is loaded by container apps with dapr ids (publisher-app, subscriber-app).

    USING MANAGED IDENTITY + DAPR

    The secrets approach used here is idea for demo purposes. However, we recommend using Managed Identity with Dapr in production. For more details on secrets, check out tomorrow's post on Secrets and Managed Identity in Azure Container Apps

    {
    "resources": [
    {
    "type": "daprComponents",
    "name": "dapr-pubsub",
    "properties": {
    "componentType": "pubsub.azure.servicebus",
    "version": "v1",
    "secrets": [
    {
    "name": "sb-root-connectionstring",
    "value": "value"
    }
    ],
    "metadata": [
    {
    "name": "connectionString",
    "secretRef": "sb-root-connectionstring"
    }
    ],
    // Application scopes
    "scopes": ["publisher-app", "subscriber-app"]

    }
    }
    ]
    }

    With this configuration, the ACA is now set to use pub/sub capabilities from the Dapr sidecar, using standard HTTP requests to the exposed API endpoint for this service.

    Exercise: Deploy Dapr-enabled ACA

    In the next couple posts in this series, we'll be discussing how you can use the Dapr secrets API and doing a walkthrough of a more complex example, to show how Dapr-enabled Azure Container Apps are created and deployed.

    However, you can get hands-on experience with these concepts by walking through one of these two tutorials, each providing an alternative approach to configure and setup the application describe in the scenario below:

    Resources

    Here are the main resources to explore for self-study:

    - - + + \ No newline at end of file diff --git a/blog/tags/dapr/page/12/index.html b/blog/tags/dapr/page/12/index.html index 0c379406ff..aac73a7483 100644 --- a/blog/tags/dapr/page/12/index.html +++ b/blog/tags/dapr/page/12/index.html @@ -14,13 +14,13 @@ - - + +

    16 posts tagged with "dapr"

    View All Tags

    · 6 min read
    Melony Qin

    Welcome to Day 12 of #30DaysOfServerless!

    Today, we have a special set of posts from our Zero To Hero 🚀 initiative, featuring blog posts authored by our Product Engineering teams for #ServerlessSeptember. Posts were originally published on the Apps on Azure blog on Microsoft Tech Community.


    What We'll Cover

    • What are Custom Handlers, and why use them?
    • How Custom Handler Works
    • Message Processing With Azure Custom Handler
    • Azure Portal Monitoring


    If you have been working with Azure Functions for a while, you may know Azure Functions is a serverless FaaS (Function as a Service) offered provided by Microsoft Azure, which is built for your key scenarios, including building web APIs, processing file uploads, responding to database changes, processing IoT data streams, managing message queues, and more.

    Custom Handlers: What and Why

    Azure functions support multiple programming languages including C#, F#, Java, JavaScript, Python, typescript, and PowerShell. If you want to get extended language support with Azure functions for other languages such as Go, and Rust, that’s where custom handler comes in.

    An Azure function custom handler allows the use of any language that supports HTTP primitives and author Azure functions. With custom handlers, you can use triggers and input and output bindings via extension bundles, hence it supports all the triggers and bindings you're used to with Azure functions.

    How a Custom Handler Works

    Let’s take a look at custom handlers and how it works.

    • A request is sent to the function host when an event is triggered. It’s up to the function host to issue a request payload to the custom handler, which holds the trigger and inputs binding data as well as other metadata for the function.
    • The custom handler is a local HTTP web server. It executes the function code and returns a response payload to the Functions host.
    • The Functions host passes data from the response to the function's output bindings which will be passed to the downstream stream services for data processing.

    Check out this article to know more about Azure functions custom handlers.


    Message processing with Custom Handlers

    Message processing is one of the key scenarios that Azure functions are trying to address. In the message-processing scenario, events are often collected in queues. These events can trigger Azure functions to execute a piece of business logic.

    You can use the Service Bus trigger to respond to messages from an Azure Service Bus queue - it's then up to the Azure functions custom handlers to take further actions to process the messages. The process is described in the following diagram:

    Building Serverless Go Applications with Azure functions custom handlers

    In Azure function, the function.json defines the function's trigger, input and output bindings, and other configuration settings. Note that every function can have multiple bindings, but it can only have one trigger. The following is an example of setting up the Service Bus queue trigger in the function.json file :

    {
    "bindings": [
    {
    "name": "queueItem",
    "type": "serviceBusTrigger",
    "direction": "in",
    "queueName": "functionqueue",
    "connection": "ServiceBusConnection"
    }
    ]
    }

    You can add a binding definition in the function.json to write the output to a database or other locations of your desire. Supported bindings can be found here.

    As we’re programming in Go, so we need to set the value of defaultExecutablePath to handler in the customHandler.description section in the host.json file.

    Assume we’re programming in Windows OS, and we have named our go application as server.go, after we executed go build server.go command, it produces an executable called server.exe. So here we set server.exe in the host.json as the following example :

      "customHandler": {
    "description": {
    "defaultExecutablePath": "./server.exe",
    "workingDirectory": "",
    "arguments": []
    }
    }

    We’re showcasing a simple Go application here with Azure functions custom handlers where we print out the messages received from the function host. The following is the full code of server.go application :

    package main

    import (
    "encoding/json"
    "fmt"
    "log"
    "net/http"
    "os"
    )

    type InvokeRequest struct {
    Data map[string]json.RawMessage
    Metadata map[string]interface{}
    }

    func queueHandler(w http.ResponseWriter, r *http.Request) {
    var invokeRequest InvokeRequest

    d := json.NewDecoder(r.Body)
    d.Decode(&invokeRequest)

    var parsedMessage string
    json.Unmarshal(invokeRequest.Data["queueItem"], &parsedMessage)

    fmt.Println(parsedMessage)
    }

    func main() {
    customHandlerPort, exists := os.LookupEnv("FUNCTIONS_CUSTOMHANDLER_PORT")
    if !exists {
    customHandlerPort = "8080"
    }
    mux := http.NewServeMux()
    mux.HandleFunc("/MessageProcessorFunction", queueHandler)
    fmt.Println("Go server Listening on: ", customHandlerPort)
    log.Fatal(http.ListenAndServe(":"+customHandlerPort, mux))

    }

    Ensure you have Azure functions core tools installed, then we can use func start command to start our function. Then we’ll have have a C#-based Message Sender application on Github to send out 3000 messages to the Azure service bus queue. You’ll see Azure functions instantly start to process the messages and print out the message as the following:

    Monitoring Serverless Go Applications with Azure functions custom handlers


    Azure portal monitoring

    Let’s go back to Azure portal portal the events see how those messages in Azure Service Bus queue were being processed. There was 3000 messages were queued in the Service Bus queue ( the Blue line stands for incoming Messages ). The outgoing messages (the red line in smaller wave shape ) showing there are progressively being read by Azure functions as the following :

    Monitoring Serverless Go Applications with Azure functions custom handlers

    Check out this article about monitoring Azure Service bus for further information.

    Next steps

    Thanks for following along, we’re looking forward to hearing your feedback. Also, if you discover potential issues, please record them on Azure Functions host GitHub repository or tag us @AzureFunctions on Twitter.

    RESOURCES

    Start to build your serverless applications with custom handlers, check out the official documentation:

    Life is a journey of learning. Let’s stay tuned!

    - - + + \ No newline at end of file diff --git a/blog/tags/dapr/page/13/index.html b/blog/tags/dapr/page/13/index.html index 7e21e2a723..e36eca040c 100644 --- a/blog/tags/dapr/page/13/index.html +++ b/blog/tags/dapr/page/13/index.html @@ -14,13 +14,13 @@ - - + +

    16 posts tagged with "dapr"

    View All Tags

    · 5 min read
    Anthony Chu

    Welcome to Day 12 of #30DaysOfServerless!

    Today, we have a special set of posts from our Zero To Hero 🚀 initiative, featuring blog posts authored by our Product Engineering teams for #ServerlessSeptember. Posts were originally published on the Apps on Azure blog on Microsoft Tech Community.


    What We'll Cover

    • Using Visual Studio
    • Using Visual Studio Code: Docker, ACA extensions
    • Using Azure CLI
    • Using CI/CD Pipelines


    Last week, @kendallroden wrote about what it means to be Cloud-Native and how Azure Container Apps provides a serverless containers platform for hosting all of your Cloud-Native applications. Today, we’ll walk through a few ways to get your apps up and running on Azure Container Apps.

    Depending on where you are in your Cloud-Native app development journey, you might choose to use different tools to deploy your apps.

    • “Right-click, publish” – Deploying an app directly from an IDE or code editor is often seen as a bad practice, but it’s one of the quickest ways to test out an app in a cloud environment.
    • Command line interface – CLIs are useful for deploying apps from a terminal. Commands can be run manually or in a script.
    • Continuous integration/deployment – To deploy production apps, the recommended approach is to automate the process in a robust CI/CD pipeline.

    Let's explore some of these options in more depth.

    Visual Studio

    Visual Studio 2022 has built-in support for deploying .NET applications to Azure Container Apps. You can use the familiar publish dialog to provision Container Apps resources and deploy to them directly. This helps you prototype an app and see it run in Azure Container Apps with the least amount of effort.

    Journey to the cloud with Azure Container Apps

    Once you’re happy with the app and it’s ready for production, Visual Studio allows you to push your code to GitHub and set up a GitHub Actions workflow to build and deploy your app every time you push changes. You can do this by checking a box.

    Journey to the cloud with Azure Container Apps

    Visual Studio Code

    There are a couple of valuable extensions that you’ll want to install if you’re working in VS Code.

    Docker extension

    The Docker extension provides commands for building a container image for your app and pushing it to a container registry. It can even do this without requiring Docker Desktop on your local machine --- the “Build image in Azure” command remotely builds and pushes a container image to Azure Container Registry.

    Journey to the cloud with Azure Container Apps

    And if your app doesn’t have a dockerfile, the extension will generate one for you.

    Journey to the cloud with Azure Container Apps

    Azure Container Apps extension

    Once you’ve built your container image and pushed it to a registry, the Azure Container Apps VS Code extension provides commands for creating a container app and deploying revisions using the image you’ve built.

    Journey to the cloud with Azure Container Apps

    Azure CLI

    The Azure CLI can be used to manage pretty much anything in Azure. For Azure Container Apps, you’ll find commands for creating, updating, and managing your Container Apps resources.

    Just like in VS Code, with a few commands in the Azure CLI, you can create your Azure resources, build and push your container image, and then deploy it to a container app.

    To make things as simple as possible, the Azure CLI also has an “az containerapp up” command. This single command takes care of everything that’s needed to turn your source code from your local machine to a cloud-hosted application in Azure Container Apps.

    az containerapp up --name myapp --source ./src

    We saw earlier that Visual Studio can generate a GitHub Actions workflow to automatically build and deploy your app on every commit. “az containerapp up” can do this too. The following adds a workflow to a repo.

    az containerapp up --name myapp --repo https://github.com/myorg/myproject

    CI/CD pipelines

    When it’s time to take your app to production, it’s strongly recommended to set up a CI/CD pipeline to automatically and repeatably build, test, and deploy it. We’ve already seen that tools such as Visual Studio and Azure CLI can automatically generate a workflow for GitHub Actions. You can set up a pipeline in Azure DevOps too. This is an example Azure DevOps pipeline.

    trigger:
    branches:
    include:
    - main

    pool:
    vmImage: ubuntu-latest

    stages:

    - stage: Build

    jobs:
    - job: build
    displayName: Build app

    steps:
    - task: Docker@2
    inputs:
    containerRegistry: 'myregistry'
    repository: 'hello-aca'
    command: 'buildAndPush'
    Dockerfile: 'hello-container-apps/Dockerfile'
    tags: '$(Build.BuildId)'

    - stage: Deploy

    jobs:
    - job: deploy
    displayName: Deploy app

    steps:
    - task: AzureCLI@2
    inputs:
    azureSubscription: 'my-subscription(5361b9d6-46ea-43c3-a898-15f14afb0db6)'
    scriptType: 'bash'
    scriptLocation: 'inlineScript'
    inlineScript: |
    # automatically install Container Apps CLI extension
    az config set extension.use_dynamic_install=yes_without_prompt

    # ensure registry is configured in container app
    az containerapp registry set \
    --name hello-aca \
    --resource-group mygroup \
    --server myregistry.azurecr.io \
    --identity system

    # update container app
    az containerapp update \
    --name hello-aca \
    --resource-group mygroup \
    --image myregistry.azurecr.io/hello-aca:$(Build.BuildId)

    Conclusion

    In this article, we looked at a few ways to deploy your Cloud-Native applications to Azure Container Apps and how to decide which one to use based on where you are in your app’s journey to the cloud.

    To learn more, visit Azure Container Apps | Microsoft Azure today!

    ASK THE EXPERT: LIVE Q&A

    The Azure Container Apps team will answer questions live on September 29.

    - - + + \ No newline at end of file diff --git a/blog/tags/dapr/page/14/index.html b/blog/tags/dapr/page/14/index.html index cd78c40c52..ecdf828d45 100644 --- a/blog/tags/dapr/page/14/index.html +++ b/blog/tags/dapr/page/14/index.html @@ -14,13 +14,13 @@ - - + +

    16 posts tagged with "dapr"

    View All Tags

    · 12 min read
    Nitya Narasimhan

    Welcome to Day 9 of #30DaysOfServerless!


    What We'll Cover

    • The Week Ahead
    • Hello, Container Apps!
    • Quickstart: Build Your First ACA!
    • Under The Hood: Core ACA Concepts
    • Exercise: Try this yourself!
    • Resources: For self-study!


    The Week Ahead

    Welcome to Week 2 of #ServerlessSeptember, where we put the focus on Microservices and building Cloud-Native applications that are optimized for serverless solutions on Azure. One week is not enough to do this complex topic justice so consider this a 7-part jumpstart to the longer journey.

    1. Hello, Container Apps (ACA) - Learn about Azure Container Apps, a key service that helps you run microservices and containerized apps on a serverless platform. Know the core concepts. (Tutorial 1: First ACA)
    2. Communication with Microservices - Dive deeper into two key concepts: environments and virtual networking. Learn how microservices communicate in ACA, and walkthrough an example. (Tutorial 2: ACA with 3 Microservices)
    3. Scaling Your Container Apps - Learn about KEDA. Understand how to configure your ACA for auto-scaling with KEDA-supported triggers. Put this into action by walking through a tutorial. (Tutorial 3: Configure Autoscaling)
    4. Hello, Distributed Application Runtime (Dapr) - Learn about Dapr and how its Building Block APIs simplify microservices development with ACA. Know how the sidecar pattern enables incremental adoption of Dapr APIs without requiring any Dapr code integration in app. (Tutorial 4: Setup & Explore Dapr)
    5. Building ACA with Dapr - See how Dapr works with ACA by building a Dapr-enabled Azure Container App. Walk through a .NET tutorial using Pub/Sub and State Management APIs in an enterprise scenario. (Tutorial 5: Build ACA with Dapr)
    6. Managing Secrets With Dapr - We'll look at the Secrets API (a key Building Block of Dapr) and learn how it simplifies management of sensitive information in ACA.
    7. Microservices + Serverless On Azure - We recap Week 2 (Microservices) and set the stage for Week 3 ( Integrations) of Serverless September. Plus, self-study resources including ACA development tutorials in different languages.

    Ready? Let's go!


    Azure Container Apps!

    When building your application, your first decision is about where you host your application. The Azure Architecture Center has a handy chart to help you decide between choices like Azure Functions, Azure App Service, Azure Container Instances, Azure Container Apps and more. But if you are new to this space, you'll need a good understanding of the terms and concepts behind the services Today, we'll focus on Azure Container Apps (ACA) - so let's start with the fundamentals.

    Containerized App Defined

    A containerized app is one where the application components, dependencies, and configuration, are packaged into a single file (container image), which can be instantiated in an isolated runtime environment (container) that is portable across hosts (OS). This makes containers lightweight and scalable - and ensures that applications behave consistently on different host platforms.

    Container images can be shared via container registries (public or private) helping developers discover and deploy related apps with less effort. Scaling a containerized app can be as simple as activating more instances of its container image. However, this requires container orchestrators to automate the management of container apps for efficiency. Orchestrators use technologies like Kubernetes to support capabilities like workload scheduling, self-healing and auto-scaling on demand.

    Cloud-Native & Microservices

    Containers are seen as one of the 5 pillars of Cloud-Native app development, an approach where applications are designed explicitly to take advantage of the unique benefits of modern dynamic environments (involving public, private and hybrid clouds). Containers are particularly suited to serverless solutions based on microservices.

    • With serverless - developers use managed services instead of managing their own infrastructure. Services are typically event-driven and can be configured for autoscaling with rules tied to event triggers. Serverless is cost-effective, with developers paying only for the compute cycles and resources they use.
    • With microservices - developers compose their applications from independent components. Each component can be deployed in its own container, and scaled at that granularity. This simplifies component reuse (across apps) and maintainability (over time) - with developers evolving functionality at microservice (vs. app) levels.

    Hello, Azure Container Apps!

    Azure Container Apps is the managed service that helps you run containerized apps and microservices as a serverless compute solution, on Azure. You can:

    • deploy serverless API endpoints - autoscaled by HTTP request traffic
    • host background processing apps - autoscaled by CPU or memory load
    • handle event-driven processing - autoscaled by #messages in queue
    • run microservices - autoscaled by any KEDA-supported scaler.

    Want a quick intro to the topic? Start by watching the short video below - then read these two posts from our ZeroToHero series:


    Deploy Your First ACA

    Dev Options

    We typically have three options for development:

    • Use the Azure Portal - provision and deploy from a browser.
    • Use Visual Studio Code (with relevant extensions) - if you prefer an IDE
    • Using Azure CLI - if you prefer to build and deploy from command line.

    The documentation site has quickstarts for three contexts:

    For this quickstart, we'll go with the first option (sample image) so we can move quickly to core concepts. We'll leave the others as an exercise for you to explore.

    1. Setup Resources

    PRE-REQUISITES

    You need:

    • An Azure account with an active subscription
    • An installed Azure CLI

    Start by logging into Azure from the CLI. The command should launch a browser to complete the auth flow (or give you an option to take an alternative path).

    $ az login

    Successful authentication will result in extensive command-line output detailing the status of your subscription.

    Next, install the Azure Container Apps extension for the CLI

    $ az extension add --name containerapp --upgrade
    ...
    The installed extension 'containerapp' is in preview.

    Once successfully installed, register the Microsoft.App namespace.

    $ az provider register --namespace Microsoft.App

    Then set local environment variables in that terminal - and verify they are set correctly:

    $ RESOURCE_GROUP="my-container-apps"
    $ LOCATION="canadacentral"
    $ CONTAINERAPPS_ENVIRONMENT="my-environment"

    $ echo $LOCATION $RESOURCE_GROUP $CONTAINERAPPS_ENVIRONMENT
    canadacentral my-container-apps my-environment

    Now you can use Azure CLI to provision a resource group for this tutorial. Creating a resource group also makes it easier for us to delete/reclaim all resources used at the end of this tutorial.

    az group create \
    --name $RESOURCE_GROUP \
    --location $LOCATION
    Congratulations

    You completed the Setup step!

    On completion, the console should print out the details of the newly created resource group. You should also be able to visit the Azure Portal and find the newly-active my-container-apps resource group under your active subscription.

    2. Create Environment

    An environment is like the picket fence around your property. It creates a secure boundary that contains a group of container apps - such that all apps deployed to it share the same virtual network and logging resources.

    $ az containerapp env create \
    --name $CONTAINERAPPS_ENVIRONMENT \
    --resource-group $RESOURCE_GROUP \
    --location $LOCATION

    No Log Analytics workspace provided.
    Generating a Log Analytics workspace with name ...

    This can take a few minutes. When done, you will see the terminal display more details. You can also check the resource group in the portal and see that a Container Apps Environment and a Log Analytics Workspace are created for you as part of this step.

    You've got the fence set up. Now it's time to build your home - er, container app!

    3. Create Container App

    Here's the command we'll use to create our first Azure Container App. Note that the --image argument provides the link to a pre-existing containerapps-helloworld image.

    az containerapp create \
    --name my-container-app \
    --resource-group $RESOURCE_GROUP \
    --environment $CONTAINERAPPS_ENVIRONMENT \
    --image mcr.microsoft.com/azuredocs/containerapps-helloworld:latest \
    --target-port 80 \
    --ingress 'external' \
    --query properties.configuration.ingress.fqdn
    ...
    ...

    Container app created. Access your app at <URL>

    The --ingress property shows that the app is open to external requests; in other words, it is publicly visible at the <URL> that is printed out on the terminal on successsful completion of this step.

    4. Verify Deployment

    Let's see if this works. You can verify that your container app by visitng the URL returned above in your browser. You should see something like this!

    Container App Hello World

    You can also visit the Azure Portal and look under the created Resource Group. You should see a new Container App type of resource was created after this step.

    Congratulations

    You just created and deployed your first "Hello World" Azure Container App! This validates your local development environment setup and existence of a valid Azure subscription.

    5. Clean Up Your Resources

    It's good practice to clean up resources once you are done with a tutorial.

    THIS ACTION IS IRREVERSIBLE

    This command deletes the resource group we created above - and all resources in it. So make sure you specified the right name, then confirm deletion.

    $ az group delete --name $RESOURCE_GROUP
    Are you sure you want to perform this operation? (y/n):

    Note that you can also delete the resource group from the Azure Portal interface if that feels more comfortable. For now, we'll just use the Portal to verify that deletion occurred. If you had previously opened the Resource Group page for the created resource, just refresh it. You should see something like this:

    Resource Not Found


    Core Concepts

    COMING SOON

    An illustrated guide summarizing these concepts in a single sketchnote.

    We covered a lot today - we'll stop with a quick overview of core concepts behind Azure Container Apps, each linked to documentation for self-study. We'll dive into more details on some of these concepts in upcoming articles:

    • Environments - are the secure boundary around a group of container apps that are deployed in the same virtual network. They write logs to a shared Log Analytics workspace and can communicate seamlessly using Dapr, if used.
    • Containers refer to the container image deployed in the Azure Container App. They can use any runtime, programming language, or development stack - and be discovered using any public or private container registry. A container app can support multiple containers.
    • Revisions are immutable snapshots of an Azure Container App. The first revision is created when the ACA is first deployed, with new revisions created when redeployment occurs with revision-scope changes. Multiple revisions can run concurrently in an environment.
    • Application Lifecycle Management revolves around these revisions, with a container app having three phases: deployment, update and deactivation.
    • Microservices are independent units of functionality in Cloud-Native architectures. A single container app typically represents a single microservice, and can be composed from one or more containers. Microservices can now be scaled and upgraded indepedently, giving your application more flexbility and control.
    • Networking architecture consist of a virtual network (VNET) associated with the environment. Unless you provide a custom VNET at environment creation time, a default VNET is automatically created. The VNET configuration determines access (ingress, internal vs. external) and can influence auto-scaling choices (e.g., use HTTP Edge Proxy and scale based on number of HTTP requests).
    • Observability is about monitoring the health of your application and diagnosing it to improve reliability or performance. Azure Container Apps has a number of features - from Log streaming and Container console to integration with Azure Monitor - to provide a holistic view of application status over time.
    • Easy Auth is possible with built-in support for authentication and authorization including support for popular identity providers like Facebook, Google, Twitter and GitHub - alongside the Microsoft Identity Platform.

    Keep these terms in mind as we walk through more tutorials this week, to see how they find application in real examples. Finally, a note on Dapr, the Distributed Application Runtime that abstracts away many of the challenges posed by distributed systems - and lets you focus on your application logic.

    DAPR INTEGRATION MADE EASY

    Dapr uses a sidecar architecture, allowing Azure Container Apps to communicate with Dapr Building Block APIs over either gRPC or HTTP. Your ACA can be built to run with or without Dapr - giving you the flexibility to incrementally adopt specific APIs and unlock related capabilities as the need arises.

    In later articles this week, we'll do a deeper dive into Dapr and build our first Dapr-enable Azure Container App to get a better understanding of this integration.

    Exercise

    Congratulations! You made it! By now you should have a good idea of what Cloud-Native development means, why Microservices and Containers are important to that vision - and how Azure Container Apps helps simplify the building and deployment of microservices based applications using serverless architectures on Azure.

    Now it's your turn to reinforce learning by doing.

    Resources

    Three key resources to bookmark and explore:

    - - + + \ No newline at end of file diff --git a/blog/tags/dapr/page/15/index.html b/blog/tags/dapr/page/15/index.html index 613293796b..8acc7c291b 100644 --- a/blog/tags/dapr/page/15/index.html +++ b/blog/tags/dapr/page/15/index.html @@ -14,15 +14,15 @@ - - + +

    16 posts tagged with "dapr"

    View All Tags

    · 8 min read
    David Justo

    Welcome to Day 6 of #30DaysOfServerless!

    Today, we have a special set of posts from our Zero To Hero 🚀 initiative, featuring blog posts authored by our Product Engineering teams for #ServerlessSeptember. Posts were originally published on the Apps on Azure blog on Microsoft Tech Community.


    What We'll Cover

    • What are Durable Entities
    • Some Background
    • A Programming Model
    • Entities for a Micro-Blogging Platform


    Durable Entities are a special type of Azure Function that allow you to implement stateful objects in a serverless environment. They make it easy to introduce stateful components to your app without needing to manually persist data to external storage, so you can focus on your business logic. We’ll demonstrate their power with a real-life example in the last section.

    Entities 101: Some Background

    Programming Durable Entities feels a lot like object-oriented programming, except that these “objects” exist in a distributed system. Like objects, each Entity instance has a unique identifier, i.e. an entity ID that can be used to read and manipulate their internal state. Entities define a list of operations that constrain how their internal state is managed, like an object interface.

    Some experienced readers may realize that Entities sound a lot like an implementation of the Actor Pattern. For a discussion of the relationship between Entities and Actors, please refer to this documentation.

    Entities are a part of the Durable Functions Extension, an extension of Azure Functions that empowers programmers with stateful abstractions for serverless, such as Orchestrations (i.e. workflows).

    Durable Functions is available in most Azure Functions runtime environments: .NET, Node.js, Python, PowerShell, and Java (preview). For this article, we’ll focus on the C# experience, but note that Entities are also available in Node.js and Python; their availability in other languages is underway.

    Entities 102: The programming model

    Imagine you want to implement a simple Entity that just counts things. Its interface allows you to get the current count, add to the current count, and to reset the count to zero.

    If you implement this in an object-oriented way, you’d probably define a class (say “Counter”) with a method to get the current count (say “Counter.Get”), another to add to the count (say “Counter.Add”), and another to reset the count (say “Counter.Reset”). Well, the implementation of an Entity in C# is not that different from this sketch:

    [JsonObject(MemberSerialization.OptIn)] 
    public class Counter
    {
    [JsonProperty("value")]
    public int Value { get; set; }

    public void Add(int amount)
    {
    this.Value += amount;
    }

    public Task Reset()
    {
    this.Value = 0;
    return Task.CompletedTask;
    }

    public Task<int> Get()
    {
    return Task.FromResult(this.Value);
    }
    [FunctionName(nameof(Counter))]
    public static Task Run([EntityTrigger] IDurableEntityContext ctx)
    => ctx.DispatchAsync<Counter>();

    }

    We’ve defined a class named Counter, with an internal count stored in the variable “Value” which is manipulated through the “Add” and “Reset” methods, and which can be read via “Get”.

    The “Run” method is simply boilerplate required for the Azure Functions framework to interact with the object we’ve defined – it’s the method that the framework calls internally when it needs to load the Entity object. When DispatchAsync is called, the Entity and its corresponded state (the last count in “Value”) is loaded from storage. Again, this is mostly just boilerplate: your Entity’s business logic lies in the rest of the class.

    Finally, the Json annotation on top of the class and the Value field tells the Durable Functions framework that the “Value” field is to be durably persisted as part of the durable state on each Entity invocation. If you were to annotate other class variables with JsonProperty, they would also become part of the managed state.

    Entities for a micro-blogging platform

    We’ll try to implement a simple micro-blogging platform, a la Twitter. Let’s call it “Chirper”. In Chirper, users write chirps (i.e tweets), they can follow, and unfollow other users, and they can read the chirps of users they follow.

    Defining Entity

    Just like in OOP, it’s useful to begin by identifying what are the stateful agents of this scenario. In this case, users have state (who they follow and their chirps), and chirps have state in the form of their content. So, we could model these stateful agents as Entities!

    Below is a potential way to implement a User for Chirper as an Entity:

      [JsonObject(MemberSerialization = MemberSerialization.OptIn)] 
    public class User: IUser
    {
    [JsonProperty]
    public List<string> FollowedUsers { get; set; } = new List<string>();

    public void Add(string user)
    {
    FollowedUsers.Add(user);
    }

    public void Remove(string user)
    {
    FollowedUsers.Remove(user);
    }

    public Task<List<string>> Get()
    {
    return Task.FromResult(FollowedUsers);
    }
    // note: removed boilerplate “Run” method, for conciseness.
    }

    In this case, our Entity’s internal state is stored in “FollowedUsers” which is an array of accounts followed by this user. The operations exposed by this entity allow clients to read and modify this data: it can be read by “Get”, a new follower can be added via “Add”, and a user can be unfollowed via “Remove”.

    With that, we’ve modeled a Chirper’s user as an Entity! Recall that Entity instances each has a unique ID, so we can consider that unique ID to correspond to a specific user account.

    What about chirps? Should we represent them as Entities as well? That would certainly be valid. However, we would then need to create a mapping between an entity ID and every chirp entity ID that this user wrote.

    For demonstration purposes, a simpler approach would be to create an Entity that stores the list of all chirps authored by a given user; call it UserChirps. Then, we could fix each User Entity to share the same entity ID as its corresponding UserChirps Entity, making client operations easier.

    Below is a simple implementation of UserChirps:

      [JsonObject(MemberSerialization = MemberSerialization.OptIn)] 
    public class UserChirps : IUserChirps
    {
    [JsonProperty]
    public List<Chirp> Chirps { get; set; } = new List<Chirp>();

    public void Add(Chirp chirp)
    {
    Chirps.Add(chirp);
    }

    public void Remove(DateTime timestamp)
    {
    Chirps.RemoveAll(chirp => chirp.Timestamp == timestamp);
    }

    public Task<List<Chirp>> Get()
    {
    return Task.FromResult(Chirps);
    }

    // Omitted boilerplate “Run” function
    }

    Here, our state is stored in Chirps, a list of user posts. Our operations are the same as before: Get, Read, and Add. It’s the same pattern as before, but we’re representing different data.

    To put it all together, let’s set up Entity clients to generate and manipulate these Entities according to some REST API.

    Interacting with Entity

    Before going there, let’s talk briefly about how you can interact with an Entity. Entity interactions take one of two forms -- calls and signals:

    Calling an entity is a two-way communication. You send an operation message to the entity and then wait for the response message before you continue. The response can be a result value or an error. Signaling an entity is a one-way (fire-and-forget) communication. You send an operation message but don’t wait for a response. You have the reassurance that the message will be delivered eventually, but you don’t know when and don’t know what the response is. For example, when you read the state of an Entity, you are performing a “call” interaction. When you record that a user has followed another, you may choose to simply signal it.

    Now say user with a given userId (say “durableFan99” ) wants to post a chirp. For this, you can write an HTTP endpoint to signal the UserChips entity to record that chirp. We can leverage the HTTP Trigger functionality from Azure Functions and pair it with an entity client binding that signals the Add operation of our Chirp Entity:

    [FunctionName("UserChirpsPost")] 
    public static async Task<HttpResponseMessage> UserChirpsPost(
    [HttpTrigger(AuthorizationLevel.Function, "post", Route = "user/{userId}/chirps")]
    HttpRequestMessage req,
    DurableClient] IDurableClient client,
    ILogger log,
    string userId)
    {
    Authenticate(req, userId);
    var chirp = new Chirp()
    {
    UserId = userId,
    Timestamp = DateTime.UtcNow,
    Content = await req.Content.ReadAsStringAsync(),
    };
    await client.SignalEntityAsync<IUserChirps>(userId, x => x.Add(chirp));
    return req.CreateResponse(HttpStatusCode.Accepted, chirp);
    }

    Following the same pattern as above, to get all the chirps from a user, you could read the status of your Entity via ReadEntityStateAsync, which follows the call-interaction pattern as your client expects a response:

    [FunctionName("UserChirpsGet")] 
    public static async Task<HttpResponseMessage> UserChirpsGet(
    [HttpTrigger(AuthorizationLevel.Function, "get", Route = "user/{userId}/chirps")] HttpRequestMessage req,
    [DurableClient] IDurableClient client,
    ILogger log,
    string userId)
    {

    Authenticate(req, userId);
    var target = new EntityId(nameof(UserChirps), userId);
    var chirps = await client.ReadEntityStateAsync<UserChirps>(target);
    return chirps.EntityExists
    ? req.CreateResponse(HttpStatusCode.OK, chirps.EntityState.Chirps)
    : req.CreateResponse(HttpStatusCode.NotFound);
    }

    And there you have it! To play with a complete implementation of Chirper, you can try out our sample in the Durable Functions extension repo.

    Thank you!

    info

    Thanks for following along, and we hope you find Entities as useful as we do! If you have questions or feedback, please file issues in the repo above or tag us @AzureFunctions on Twitter

    - - + + \ No newline at end of file diff --git a/blog/tags/dapr/page/16/index.html b/blog/tags/dapr/page/16/index.html index 6f6798c775..a6cb3cff5b 100644 --- a/blog/tags/dapr/page/16/index.html +++ b/blog/tags/dapr/page/16/index.html @@ -14,13 +14,13 @@ - - + +

    16 posts tagged with "dapr"

    View All Tags

    · 8 min read
    Kendall Roden

    Welcome to Day 6 of #30DaysOfServerless!

    Today, we have a special set of posts from our Zero To Hero 🚀 initiative, featuring blog posts authored by our Product Engineering teams for #ServerlessSeptember. Posts were originally published on the Apps on Azure blog on Microsoft Tech Community.


    What We'll Cover

    • Defining Cloud-Native
    • Introduction to Azure Container Apps
    • Dapr In Azure Container Apps
    • Conclusion


    Defining Cloud-Native

    While I’m positive I’m not the first person to ask this, I think it’s an appropriate way for us to kick off this article: “How many developers does it take to define Cloud-Native?” I hope you aren’t waiting for a punch line because I seriously want to know your thoughts (drop your perspectives in the comments..) but if you ask me, the limit does not exist!

    A quick online search of the topic returns a laundry list of articles, e-books, twitter threads, etc. all trying to nail down the one true definition. While diving into the rabbit hole of Cloud-Native, you will inevitably find yourself on the Cloud-Native Computing Foundation (CNCF) site. The CNCF is part of the Linux Foundation and aims to make "Cloud-Native computing ubiquitous" through deep open source project and community involvement. The CNCF has also published arguably the most popularized definition of Cloud-Native which begins with the following statement:

    “Cloud-Native technologies empower organizations to build and run scalable applications in modern, dynamic environments such as public, private, and hybrid clouds."

    Over the past four years, my day-to-day work has been driven primarily by the surging demand for application containerization and the drastic adoption of Kubernetes as the de-facto container orchestrator. Customers are eager to learn and leverage patterns, practices and technologies that enable building "loosely coupled systems that are resilient, manageable, and observable". Enterprise developers at these organizations are being tasked with rapidly deploying event-driven, horizontally-scalable, polyglot services via repeatable, code-to-cloud pipelines.

    While building Cloud-Native solutions can enable rapid innovation, the transition to adopting a Cloud-Native architectural approach comes with a steep learning curve and a new set of considerations. In a document published by Microsoft called What is Cloud-Native?, there are a few key areas highlighted to aid customers in the adoption of best practices for building modern, portable applications which I will summarize below:

    Cloud infrastructure

    • Cloud-Native applications leverage cloud infrastructure and make use of Platform-as-a-service offerings
    • Cloud-Native applications depend on highly-elastic infrastructure with automatic scaling, self-healing, and monitoring capabilities

    Modern application design

    • Cloud-Native applications should be constructed using principles outlined in the 12 factor methodology

    Microservices

    • Cloud-Native applications are typically composed of microservices where each core function, or service, is built and deployed independently

    Containers

    • Cloud-Native applications are typically deployed using containers as a packaging mechanism where an application's code and dependencies are bundled together for consistency of deployment
    • Cloud-Native applications leverage container orchestration technologies- primarily Kubernetes- for achieving capabilities such as workload scheduling, self-healing, auto-scale, etc.

    Backing services

    • Cloud-Native applications are ideally stateless workloads which retrieve and store data in data stores external to the application hosting infrastructure. Cloud providers like Azure provide an array of backing data services which can be securely accessed from application code and provide capabilities for ensuring application data is highly-available

    Automation

    • Cloud-Native solutions should use deployment automation for backing cloud infrastructure via versioned, parameterized Infrastructure as Code (IaC) templates which provide a consistent, repeatable process for provisioning cloud resources.
    • Cloud-Native solutions should make use of modern CI/CD practices and pipelines to ensure successful, reliable infrastructure and application deployment.

    Azure Container Apps

    In many of the conversations I've had with customers that involve talk of Kubernetes and containers, the topics of cost-optimization, security, networking, and reducing infrastructure and operations inevitably arise. I personally have yet to meet with any customers eager to have their developers get more involved with infrastructure concerns.

    One of my former colleagues, Jeff Hollan, made a statement while appearing on a 2019 episode of The Cloud-Native Show where he shared his perspective on Cloud-Native:

    "When I think about Cloud-Native... it's writing applications in a way where you are specifically thinking about the benefits the cloud can provide... to me, serverless is the perfect realization of that because the only reason you can write serverless applications is because the cloud exists."

    I must say that I agree with Jeff's perspective. In addition to optimizing development practices for the Cloud-Native world, reducing infrastructure exposure and operations is equally as important to many organizations and can be achieved as a result of cloud platform innovation.

    In May of 2022, Microsoft announced the general availability of Azure Container Apps. Azure Container Apps provides customers with the ability to run microservices and containerized applications on a serverless, consumption-based platform.

    For those interested in taking advantage of the open source ecosystem while reaping the benefits of a managed platform experience, Container Apps run on Kubernetes and provides a set of managed open source projects embedded directly into the platform including the Kubernetes Event Driven Autoscaler (KEDA), the Distributed Application Runtime (Dapr) and Envoy.

    Azure Kubernetes Service vs. Azure Container Apps

    Container apps provides other Cloud-Native features and capabilities in addition to those above including, but not limited to:

    The ability to dynamically scale and support growing numbers of users, events, and requests is one of the core requirements for most Cloud-Native, distributed applications. Azure Container Apps is purpose-built with this and other Cloud-Native tenants in mind.

    What can you build with Azure Container Apps?

    Dapr in Azure Container Apps

    As a quick personal note before we dive into this section I will say I am a bit bias about Dapr. When Dapr was first released, I had an opportunity to immediately get involved and became an early advocate for the project. It is created by developers for developers, and solves tangible problems customers architecting distributed systems face:

    HOW DO I
    • integrate with external systems that my app has to react and respond to?
    • create event driven apps which reliably send events from one service to another?
    • observe the calls and events between my services to diagnose issues in production?
    • access secrets securely from within my application?
    • discover other services and call methods on them?
    • prevent committing to a technology early and have the flexibility to swap out an alternative based on project or environment changes?

    While existing solutions were in the market which could be used to address some of the concerns above, there was not a lightweight, CNCF-backed project which could provide a unified approach to solve the more fundamental ask from customers: "How do I make it easy for developers to build microservices based on Cloud-Native best practices?"

    Enter Dapr!

    The Distributed Application Runtime (Dapr) provides APIs that simplify microservice connectivity. Whether your communication pattern is service to service invocation or pub/sub messaging, Dapr helps you write resilient and secured microservices. By letting Dapr’s sidecar take care of the complex challenges such as service discovery, message broker integration, encryption, observability, and secret management, you can focus on business logic and keep your code simple."

    The Container Apps platform provides a managed and supported Dapr integration which eliminates the need for deploying and managing the Dapr OSS project. In addition to providing managed upgrades, the platform also exposes a simplified Dapr interaction model to increase developer productivity and reduce the friction required to leverage Dapr capabilities. While the Dapr integration makes it easier for customers to adopt Cloud-Native best practices in container apps it is not required to make use of the container apps platform.

    Image on Dapr

    For additional insight into the dapr integration visit aka.ms/aca-dapr.

    Conclusion

    Backed by and integrated with powerful Cloud-Native technologies, Azure Container Apps strives to make developers productive, while reducing the operational overhead and learning curve that typically accompanies adopting a cloud-native strategy.

    If you are interested in building resilient, portable and highly-scalable apps visit Azure Container Apps | Microsoft Azure today!

    ASK THE EXPERT: LIVE Q&A

    The Azure Container Apps team will answer questions live on September 29.

    - - + + \ No newline at end of file diff --git a/blog/tags/dapr/page/2/index.html b/blog/tags/dapr/page/2/index.html index 1513177a38..62897d6bca 100644 --- a/blog/tags/dapr/page/2/index.html +++ b/blog/tags/dapr/page/2/index.html @@ -14,13 +14,13 @@ - - + +

    16 posts tagged with "dapr"

    View All Tags

    · 7 min read
    Brian Benz

    Welcome to Day 25 of #30DaysOfServerless!

    Azure Container Apps enable application code packaged in containers to run and scale without the overhead of managing cloud infrastructure and container orchestration. In this post I'll show you how to deploy a Java application running on Spring Boot in a container to Azure Container Registry and Azure Container Apps.


    What We'll Cover

    • Introduction to Deploying Java containers in the cloud
    • Step-by-step: Deploying to Azure Container Registry
    • Step-by-step: Deploying and running on Azure Container Apps
    • Resources: For self-study!


    Deploy Java containers to cloud

    We'll deploy a Java application running on Spring Boot in a container to Azure Container Registry and Azure Container Apps. Here are the main steps:

    • Create Azure Container Registry (ACR) on Azure portal
    • Create Azure Container App (ACA) on Azure portal.
    • Deploy code to Azure Container Registry from the Azure CLI.
    • Deploy container from ACR to ACA using the Azure portal.
    PRE-REQUISITES

    Sign in to Azure from the CLI using the az login command, and follow the prompts in your browser to complete the authentication process. Also, ensure you're running the latest version of the CLI by using the az upgrade command.

    1. Get Sample Code

    Fork and clone the sample GitHub repo to your local machine. Navigate to the and click Fork in the top-right corner of the page.

    The example code that we're using is a very basic containerized Spring Boot example. There are a lot more details to learn about Spring boot apps in docker, for a deep dive check out this Spring Boot Guide

    2. Run Sample Locally (Optional)

    If you have docker installed locally, you can optionally test the code on your local machine. Navigate to the root directory of the forked repository and run the following commands:

    docker build -t spring-boot-docker-aca .
    docker run -p 8080:8080 spring-boot-docker-aca

    Open a browser and go to https://localhost:8080. You should see this message:

    Hello Docker World

    That indicates the the Spring Boot app is successfully running locally in a docker container.

    Next, let's set up an Azure Container Registry an an Azure Container App and deploy this container to the cloud!


    3. Step-by-step: Deploy to ACR

    To create a container registry from the portal dashboard, Select Create a resource > Containers > Container Registry.

    Navigate to container registry in portal

    In the Basics tab, enter values for Resource group and Registry name. The registry name must be unique within Azure, and contain 5-50 alphanumeric characters. Create a new resource group in the West US location named spring-boot-docker-aca. Select the 'Basic' SKU.

    Keep the default values for the remaining settings. Then select Review + create, then Create. When the Deployment succeeded message appears, select the container registry in the portal.

    Note the registry server name ending with azurecr.io. You will use this in the following steps when you push and pull images with Docker.

    3.1 Log into registry using the Azure CLI

    Before pushing and pulling container images, you must log in to the registry instance. Sign into the Azure CLI on your local machine, then run the az acr login command. For this step, use the registry name, not the server name ending with azurecr.io.

    From the command line, type:

    az acr login --name myregistryname

    The command returns Login Succeeded once completed.

    3.2 Build & deploy with az acr build

    Next, we're going to deploy the docker container we created earlier using the AZ ACR Build command. AZ ACR Build creates a docker build from local code and pushes the container to Azure Container Registry if the build is successful.

    Go to your local clone of the spring-boot-docker-aca repo in the command line, type:

    az acr build --registry myregistryname --image spring-boot-docker-aca:v1 .

    3.3 List container images

    Once the AZ ACR Build command is complete, you should be able to view the container as a repository in the registry. In the portal, open your registry and select Repositories, then select the spring-boot-docker-aca repository you created with docker push. You should also see the v1 image under Tags.

    4. Deploy on ACA

    Now that we have an image in the Azure Container Registry, we can deploy it to Azure Container Apps. For the first deployment, we'll pull the container from our ACR as part of the ACA setup.

    4.1 Create a container app

    We'll create the container app at the same place that we created the container registry in the Azure portal. From the portal, select Create a resource > Containers > Container App. In the Basics tab, set these values:

    4.2 Enter project details

    SettingAction
    SubscriptionYour Azure subscription.
    Resource groupUse the spring-boot-docker-aca resource group
    Container app nameEnter spring-boot-docker-aca.

    4.3 Create an environment

    1. In the Create Container App environment field, select Create new.

    2. In the Create Container App Environment page on the Basics tab, enter the following values:

      SettingValue
      Environment nameEnter my-environment.
      RegionSelect westus3.
    3. Select OK.

    4. Select the Create button at the bottom of the Create Container App Environment page.

    5. Select the Next: App settings button at the bottom of the page.

    5. App settings tab

    The App settings tab is where you connect to the ACR and pull the repository image:

    SettingAction
    Use quickstart imageUncheck the checkbox.
    NameEnter spring-boot-docker-aca.
    Image sourceSelect Azure Container Registry
    RegistrySelect your ACR from the list.
    ImageSelect spring-boot-docker-aca from the list.
    Image TagSelect v1 from the list.

    5.1 Application ingress settings

    SettingAction
    IngressSelect Enabled.
    Ingress visibilitySelect External to publicly expose your container app.
    Target portEnter 8080.

    5.2 Deploy the container app

    1. Select the Review and create button at the bottom of the page.
    2. Select Create.

    Once the deployment is successfully completed, you'll see the message: Your deployment is complete.

    5.3 Verify deployment

    In the portal, go to the Overview of your spring-boot-docker-aca Azure Container App, and click on the Application Url. You should see this message in the browser:

    Hello Docker World

    That indicates the the Spring Boot app is running in a docker container in your spring-boot-docker-aca Azure Container App.

    Resources: For self-study!

    Once you have an understanding of the basics in ths post, there is so much more to learn!

    Thanks for stopping by!

    - - + + \ No newline at end of file diff --git a/blog/tags/dapr/page/3/index.html b/blog/tags/dapr/page/3/index.html index f9fb248d1c..c2a704208d 100644 --- a/blog/tags/dapr/page/3/index.html +++ b/blog/tags/dapr/page/3/index.html @@ -14,13 +14,13 @@ - - + +

    16 posts tagged with "dapr"

    View All Tags

    · 19 min read
    Alex Wolf

    Welcome to Day 24 of #30DaysOfServerless!

    We continue exploring E2E scenarios with this tutorial where you'll deploy a containerized ASP.NET Core 6.0 application to Azure Container Apps.

    The application consists of a front-end web app built using Blazor Server, as well as two Web API projects to manage data. These projects will exist as three separate containers inside of a shared container apps environment.


    What We'll Cover

    • Deploy ASP.NET Core 6.0 app to Azure Container Apps
    • Automate deployment workflows using GitHub Actions
    • Provision and deploy resources using Azure Bicep
    • Exercise: Try this yourself!
    • Resources: For self-study!


    Introduction

    Azure Container Apps enables you to run microservices and containerized applications on a serverless platform. With Container Apps, you enjoy the benefits of running containers while leaving behind the concerns of manually configuring cloud infrastructure and complex container orchestrators.

    In this tutorial, you'll deploy a containerized ASP.NET Core 6.0 application to Azure Container Apps. The application consists of a front-end web app built using Blazor Server, as well as two Web API projects to manage data. These projects will exist as three separate containers inside of a shared container apps environment.

    You will use GitHub Actions in combination with Bicep to deploy the application. These tools provide an approachable and sustainable solution for building CI/CD pipelines and working with Container Apps.

    PRE-REQUISITES

    Architecture

    In this tutorial, we'll setup a container app environment with a separate container for each project in the sample store app. The major components of the sample project include:

    • A Blazor Server front-end web app to display product information
    • A products API to list available products
    • An inventory API to determine how many products are in stock
    • GitHub Actions and Bicep templates to provision Azure resources and then build and deploy the sample app.

    You will explore these templates later in the tutorial.

    Public internet traffic should be proxied to the Blazor app. The back-end APIs should only be reachable via requests from the Blazor app inside the container apps environment. This setup can be achieved using container apps environment ingress configurations during deployment.

    An architecture diagram of the shopping app


    Project Sources

    Want to follow along? Fork the sample below. The tutorial can be completed with or without Dapr integration. Pick the path you feel comfortable in. Dapr provides various benefits that make working with Microservices easier - you can learn more in the docs. For this tutorial you will need GitHub and Azure CLI.

    PICK YOUR PATH

    To follow along with this tutorial, fork the relevant sample project below.

    You can run the app locally from Visual Studio:

    • Right click on the Blazor Store project and select Set as Startup Project.
    • Press the start button at the top of Visual Studio to run the app.
    • (Once running) start each API in the background by
    • right-clicking on the project node
    • selecting Debug --> Start without debugging.

    Once the Blazor app is running, you should see something like this:

    An architecture diagram of the shopping app


    Configuring Azure credentials

    In order to deploy the application to Azure through GitHub Actions, you first need to create a service principal. The service principal will allow the GitHub Actions process to authenticate to your Azure subscription to create resources and deploy code. You can learn more about Service Principals in the Azure CLI documentation. For this step you'll need to be logged into the Azure CLI.

    1) If you have not done so already, make sure to fork the sample project to your own GitHub account or organization.

    1) Once you have completed this step, create a service principal using the Azure CLI command below:

    ```azurecli
    $subscriptionId=$(az account show --query id --output tsv)
    az ad sp create-for-rbac --sdk-auth --name WebAndApiSample --role Contributor --scopes /subscriptions/$subscriptionId
    ```

    1) Copy the JSON output of the CLI command to your clipboard

    1) Under the settings tab of your forked GitHub repo, create a new secret named AzureSPN. The name is important to match the Bicep templates included in the project, which we'll review later. Paste the copied service principal values on your clipboard into the secret and save your changes. This new secret will be used by the GitHub Actions workflow to authenticate to Azure.

    :::image type="content" source="./img/dotnet/github-secrets.png" alt-text="A screenshot of adding GitHub secrets.":::

    Deploy using Github Actions

    You are now ready to deploy the application to Azure Container Apps using GitHub Actions. The sample application includes a GitHub Actions template that is configured to build and deploy any changes to a branch named deploy. The deploy branch does not exist in your forked repository by default, but you can easily create it through the GitHub user interface.

    1) Switch to the Actions tab along the top navigation of your GitHub repository. If you have not done so already, ensure that workflows are enabled by clicking the button in the center of the page.

    A screenshot showing how to enable GitHub actions

    1) Navigate to the main Code tab of your repository and select the main dropdown. Enter deploy into the branch input box, and then select Create branch: deploy from 'main'.

    A screenshot showing how to create the deploy branch

    1) On the new deploy branch, navigate down into the .github/workflows folder. You should see a file called deploy.yml, which contains the main GitHub Actions workflow script. Click on the file to view its content. You'll learn more about this file later in the tutorial.

    1) Click the pencil icon in the upper right to edit the document.

    1) Change the RESOURCE_GROUP_NAME: value to msdocswebappapis or another valid resource group name of your choosing.

    1) In the upper right of the screen, select Start commit and then Commit changes to commit your edit. This will persist the change to the file and trigger the GitHub Actions workflow to build and deploy the app.

    A screenshot showing how to commit changes

    1) Switch to the Actions tab along the top navigation again. You should see the workflow running to create the necessary resources and deploy the app. The workflow may take several minutes to run. When it completes successfully, all of the jobs should have a green checkmark icon next to them.

    The completed GitHub workflow.

    Explore the Azure resources

    Once the GitHub Actions workflow has completed successfully you can browse the created resources in the Azure portal.

    1) On the left navigation, select Resource Groups. Next,choose the msdocswebappapis resource group that was created by the GitHub Actions workflow.

    2) You should see seven resources available that match the screenshot and table descriptions below.

    The resources created in Azure.

    Resource nameTypeDescription
    inventoryContainer appThe containerized inventory API.
    msdocswebappapisacrContainer registryA registry that stores the built Container images for your apps.
    msdocswebappapisaiApplication insightsApplication insights provides advanced monitoring, logging and metrics for your apps.
    msdocswebappapisenvContainer apps environmentA container environment that manages networking, security and resource concerns. All of your containers live in this environment.
    msdocswebappapislogsLog Analytics workspaceA workspace environment for managing logging and analytics for the container apps environment
    productsContainer appThe containerized products API.
    storeContainer appThe Blazor front-end web app.

    3) You can view your running app in the browser by clicking on the store container app. On the overview page, click the Application Url link on the upper right of the screen.

    :::image type="content" source="./img/dotnet/application-url.png" alt-text="The link to browse the app.":::

    Understanding the GitHub Actions workflow

    The GitHub Actions workflow created and deployed resources to Azure using the deploy.yml file in the .github folder at the root of the project. The primary purpose of this file is to respond to events - such as commits to a branch - and run jobs to accomplish tasks. The deploy.yml file in the sample project has three main jobs:

    • Provision: Create the necessary resources in Azure, such as the container apps environment. This step leverages Bicep templates to create the Azure resources, which you'll explore in a moment.
    • Build: Create the container images for the three apps in the project and store them in the container registry.
    • Deploy: Deploy the container images to the different container apps created during the provisioning job.

    The deploy.yml file also accepts parameters to make the workflow more dynamic, such as setting the resource group name or the Azure region resources will be provisioned to.

    Below is a commented version of the deploy.yml file that highlights the essential steps.

    name: Build and deploy .NET application to Container Apps

    # Trigger the workflow on pushes to the deploy branch
    on:
    push:
    branches:
    - deploy

    env:
    # Set workflow variables
    RESOURCE_GROUP_NAME: msdocswebappapis

    REGION: eastus

    STORE_DOCKER: Store/Dockerfile
    STORE_IMAGE: store

    INVENTORY_DOCKER: Store.InventoryApi/Dockerfile
    INVENTORY_IMAGE: inventory

    PRODUCTS_DOCKER: Store.ProductApi/Dockerfile
    PRODUCTS_IMAGE: products

    jobs:
    # Create the required Azure resources
    provision:
    runs-on: ubuntu-latest

    steps:

    - name: Checkout to the branch
    uses: actions/checkout@v2

    - name: Azure Login
    uses: azure/login@v1
    with:
    creds: ${{ secrets.AzureSPN }}

    - name: Create resource group
    uses: azure/CLI@v1
    with:
    inlineScript: >
    echo "Creating resource group in Azure"
    echo "Executing 'az group create -l ${{ env.REGION }} -n ${{ env.RESOURCE_GROUP_NAME }}'"
    az group create -l ${{ env.REGION }} -n ${{ env.RESOURCE_GROUP_NAME }}

    # Use Bicep templates to create the resources in Azure
    - name: Creating resources
    uses: azure/CLI@v1
    with:
    inlineScript: >
    echo "Creating resources"
    az deployment group create --resource-group ${{ env.RESOURCE_GROUP_NAME }} --template-file '/github/workspace/Azure/main.bicep' --debug

    # Build the three app container images
    build:
    runs-on: ubuntu-latest
    needs: provision

    steps:

    - name: Checkout to the branch
    uses: actions/checkout@v2

    - name: Azure Login
    uses: azure/login@v1
    with:
    creds: ${{ secrets.AzureSPN }}

    - name: Set up Docker Buildx
    uses: docker/setup-buildx-action@v1

    - name: Login to ACR
    run: |
    set -euo pipefail
    access_token=$(az account get-access-token --query accessToken -o tsv)
    refresh_token=$(curl https://${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io/oauth2/exchange -v -d "grant_type=access_token&service=${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io&access_token=$access_token" | jq -r .refresh_token)
    docker login -u 00000000-0000-0000-0000-000000000000 --password-stdin ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io <<< "$refresh_token"

    - name: Build the products api image and push it to ACR
    uses: docker/build-push-action@v2
    with:
    push: true
    tags: ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io/${{ env.PRODUCTS_IMAGE }}:${{ github.sha }}
    file: ${{ env.PRODUCTS_DOCKER }}

    - name: Build the inventory api image and push it to ACR
    uses: docker/build-push-action@v2
    with:
    push: true
    tags: ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io/${{ env.INVENTORY_IMAGE }}:${{ github.sha }}
    file: ${{ env.INVENTORY_DOCKER }}

    - name: Build the frontend image and push it to ACR
    uses: docker/build-push-action@v2
    with:
    push: true
    tags: ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io/${{ env.STORE_IMAGE }}:${{ github.sha }}
    file: ${{ env.STORE_DOCKER }}

    # Deploy the three container images
    deploy:
    runs-on: ubuntu-latest
    needs: build

    steps:

    - name: Checkout to the branch
    uses: actions/checkout@v2

    - name: Azure Login
    uses: azure/login@v1
    with:
    creds: ${{ secrets.AzureSPN }}

    - name: Installing Container Apps extension
    uses: azure/CLI@v1
    with:
    inlineScript: >
    az config set extension.use_dynamic_install=yes_without_prompt

    az extension add --name containerapp --yes

    - name: Login to ACR
    run: |
    set -euo pipefail
    access_token=$(az account get-access-token --query accessToken -o tsv)
    refresh_token=$(curl https://${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io/oauth2/exchange -v -d "grant_type=access_token&service=${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io&access_token=$access_token" | jq -r .refresh_token)
    docker login -u 00000000-0000-0000-0000-000000000000 --password-stdin ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io <<< "$refresh_token"

    - name: Deploy Container Apps
    uses: azure/CLI@v1
    with:
    inlineScript: >
    az containerapp registry set -n products -g ${{ env.RESOURCE_GROUP_NAME }} --server ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io

    az containerapp update -n products -g ${{ env.RESOURCE_GROUP_NAME }} -i ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io/${{ env.PRODUCTS_IMAGE }}:${{ github.sha }}

    az containerapp registry set -n inventory -g ${{ env.RESOURCE_GROUP_NAME }} --server ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io

    az containerapp update -n inventory -g ${{ env.RESOURCE_GROUP_NAME }} -i ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io/${{ env.INVENTORY_IMAGE }}:${{ github.sha }}

    az containerapp registry set -n store -g ${{ env.RESOURCE_GROUP_NAME }} --server ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io

    az containerapp update -n store -g ${{ env.RESOURCE_GROUP_NAME }} -i ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io/${{ env.STORE_IMAGE }}:${{ github.sha }}

    - name: logout
    run: >
    az logout

    Understanding the Bicep templates

    During the provisioning stage of the GitHub Actions workflow, the main.bicep file is processed. Bicep files provide a declarative way of generating resources in Azure and are ideal for managing infrastructure as code. You can learn more about Bicep in the related documentation. The main.bicep file in the sample project creates the following resources:

    • The container registry to store images of the containerized apps.
    • The container apps environment, which handles networking and resource management for the container apps.
    • Three container apps - one for the Blazor front-end and two for the back-end product and inventory APIs.
    • Configuration values to connect these services together

    main.bicep without Dapr

    param location string = resourceGroup().location

    # create the azure container registry
    resource acr 'Microsoft.ContainerRegistry/registries@2021-09-01' = {
    name: toLower('${resourceGroup().name}acr')
    location: location
    sku: {
    name: 'Basic'
    }
    properties: {
    adminUserEnabled: true
    }
    }

    # create the aca environment
    module env 'environment.bicep' = {
    name: 'containerAppEnvironment'
    params: {
    location: location
    }
    }

    # create the various configuration pairs
    var shared_config = [
    {
    name: 'ASPNETCORE_ENVIRONMENT'
    value: 'Development'
    }
    {
    name: 'APPINSIGHTS_INSTRUMENTATIONKEY'
    value: env.outputs.appInsightsInstrumentationKey
    }
    {
    name: 'APPLICATIONINSIGHTS_CONNECTION_STRING'
    value: env.outputs.appInsightsConnectionString
    }
    ]

    # create the products api container app
    module products 'container_app.bicep' = {
    name: 'products'
    params: {
    name: 'products'
    location: location
    registryPassword: acr.listCredentials().passwords[0].value
    registryUsername: acr.listCredentials().username
    containerAppEnvironmentId: env.outputs.id
    registry: acr.name
    envVars: shared_config
    externalIngress: false
    }
    }

    # create the inventory api container app
    module inventory 'container_app.bicep' = {
    name: 'inventory'
    params: {
    name: 'inventory'
    location: location
    registryPassword: acr.listCredentials().passwords[0].value
    registryUsername: acr.listCredentials().username
    containerAppEnvironmentId: env.outputs.id
    registry: acr.name
    envVars: shared_config
    externalIngress: false
    }
    }

    # create the store api container app
    var frontend_config = [
    {
    name: 'ProductsApi'
    value: 'http://${products.outputs.fqdn}'
    }
    {
    name: 'InventoryApi'
    value: 'http://${inventory.outputs.fqdn}'
    }
    ]

    module store 'container_app.bicep' = {
    name: 'store'
    params: {
    name: 'store'
    location: location
    registryPassword: acr.listCredentials().passwords[0].value
    registryUsername: acr.listCredentials().username
    containerAppEnvironmentId: env.outputs.id
    registry: acr.name
    envVars: union(shared_config, frontend_config)
    externalIngress: true
    }
    }

    main.bicep with Dapr


    param location string = resourceGroup().location

    # create the azure container registry
    resource acr 'Microsoft.ContainerRegistry/registries@2021-09-01' = {
    name: toLower('${resourceGroup().name}acr')
    location: location
    sku: {
    name: 'Basic'
    }
    properties: {
    adminUserEnabled: true
    }
    }

    # create the aca environment
    module env 'environment.bicep' = {
    name: 'containerAppEnvironment'
    params: {
    location: location
    }
    }

    # create the various config pairs
    var shared_config = [
    {
    name: 'ASPNETCORE_ENVIRONMENT'
    value: 'Development'
    }
    {
    name: 'APPINSIGHTS_INSTRUMENTATIONKEY'
    value: env.outputs.appInsightsInstrumentationKey
    }
    {
    name: 'APPLICATIONINSIGHTS_CONNECTION_STRING'
    value: env.outputs.appInsightsConnectionString
    }
    ]

    # create the products api container app
    module products 'container_app.bicep' = {
    name: 'products'
    params: {
    name: 'products'
    location: location
    registryPassword: acr.listCredentials().passwords[0].value
    registryUsername: acr.listCredentials().username
    containerAppEnvironmentId: env.outputs.id
    registry: acr.name
    envVars: shared_config
    externalIngress: false
    }
    }

    # create the inventory api container app
    module inventory 'container_app.bicep' = {
    name: 'inventory'
    params: {
    name: 'inventory'
    location: location
    registryPassword: acr.listCredentials().passwords[0].value
    registryUsername: acr.listCredentials().username
    containerAppEnvironmentId: env.outputs.id
    registry: acr.name
    envVars: shared_config
    externalIngress: false
    }
    }

    # create the store api container app
    module store 'container_app.bicep' = {
    name: 'store'
    params: {
    name: 'store'
    location: location
    registryPassword: acr.listCredentials().passwords[0].value
    registryUsername: acr.listCredentials().username
    containerAppEnvironmentId: env.outputs.id
    registry: acr.name
    envVars: shared_config
    externalIngress: true
    }
    }


    Bicep Modules

    The main.bicep file references modules to create resources, such as module products. Modules are a feature of Bicep templates that enable you to abstract resource declarations into their own files or sub-templates. As the main.bicep file is processed, the defined modules are also evaluated. Modules allow you to create resources in a more organized and reusable way. They can also define input and output parameters that are passed to and from the parent template, such as the name of a resource.

    For example, the environment.bicep module extracts the details of creating a container apps environment into a reusable template. The module defines necessary resource dependencies such as Log Analytics Workspaces and an Application Insights instance.

    environment.bicep without Dapr

    param baseName string = resourceGroup().name
    param location string = resourceGroup().location

    resource logs 'Microsoft.OperationalInsights/workspaces@2021-06-01' = {
    name: '${baseName}logs'
    location: location
    properties: any({
    retentionInDays: 30
    features: {
    searchVersion: 1
    }
    sku: {
    name: 'PerGB2018'
    }
    })
    }

    resource appInsights 'Microsoft.Insights/components@2020-02-02' = {
    name: '${baseName}ai'
    location: location
    kind: 'web'
    properties: {
    Application_Type: 'web'
    WorkspaceResourceId: logs.id
    }
    }

    resource env 'Microsoft.App/managedEnvironments@2022-01-01-preview' = {
    name: '${baseName}env'
    location: location
    properties: {
    appLogsConfiguration: {
    destination: 'log-analytics'
    logAnalyticsConfiguration: {
    customerId: logs.properties.customerId
    sharedKey: logs.listKeys().primarySharedKey
    }
    }
    }
    }

    output id string = env.id
    output appInsightsInstrumentationKey string = appInsights.properties.InstrumentationKey
    output appInsightsConnectionString string = appInsights.properties.ConnectionString

    environment.bicep with Dapr


    param baseName string = resourceGroup().name
    param location string = resourceGroup().location

    resource logs 'Microsoft.OperationalInsights/workspaces@2021-06-01' = {
    name: '${baseName}logs'
    location: location
    properties: any({
    retentionInDays: 30
    features: {
    searchVersion: 1
    }
    sku: {
    name: 'PerGB2018'
    }
    })
    }

    resource appInsights 'Microsoft.Insights/components@2020-02-02' = {
    name: '${baseName}ai'
    location: location
    kind: 'web'
    properties: {
    Application_Type: 'web'
    WorkspaceResourceId: logs.id
    }
    }

    resource env 'Microsoft.App/managedEnvironments@2022-01-01-preview' = {
    name: '${baseName}env'
    location: location
    properties: {
    appLogsConfiguration: {
    destination: 'log-analytics'
    logAnalyticsConfiguration: {
    customerId: logs.properties.customerId
    sharedKey: logs.listKeys().primarySharedKey
    }
    }
    }
    }

    output id string = env.id
    output appInsightsInstrumentationKey string = appInsights.properties.InstrumentationKey
    output appInsightsConnectionString string = appInsights.properties.ConnectionString


    The container_apps.bicep template defines numerous parameters to provide a reusable template for creating container apps. This allows the module to be used in other CI/CD pipelines as well.

    container_app.bicep without Dapr

    param name string
    param location string = resourceGroup().location
    param containerAppEnvironmentId string
    param repositoryImage string = 'mcr.microsoft.com/azuredocs/containerapps-helloworld:latest'
    param envVars array = []
    param registry string
    param minReplicas int = 1
    param maxReplicas int = 1
    param port int = 80
    param externalIngress bool = false
    param allowInsecure bool = true
    param transport string = 'http'
    param registryUsername string
    @secure()
    param registryPassword string

    resource containerApp 'Microsoft.App/containerApps@2022-01-01-preview' ={
    name: name
    location: location
    properties:{
    managedEnvironmentId: containerAppEnvironmentId
    configuration: {
    activeRevisionsMode: 'single'
    secrets: [
    {
    name: 'container-registry-password'
    value: registryPassword
    }
    ]
    registries: [
    {
    server: registry
    username: registryUsername
    passwordSecretRef: 'container-registry-password'
    }
    ]
    ingress: {
    external: externalIngress
    targetPort: port
    transport: transport
    allowInsecure: allowInsecure
    }
    }
    template: {
    containers: [
    {
    image: repositoryImage
    name: name
    env: envVars
    }
    ]
    scale: {
    minReplicas: minReplicas
    maxReplicas: maxReplicas
    }
    }
    }
    }

    output fqdn string = containerApp.properties.configuration.ingress.fqdn

    container_app.bicep with Dapr


    param name string
    param location string = resourceGroup().location
    param containerAppEnvironmentId string
    param repositoryImage string = 'mcr.microsoft.com/azuredocs/containerapps-helloworld:latest'
    param envVars array = []
    param registry string
    param minReplicas int = 1
    param maxReplicas int = 1
    param port int = 80
    param externalIngress bool = false
    param allowInsecure bool = true
    param transport string = 'http'
    param appProtocol string = 'http'
    param registryUsername string
    @secure()
    param registryPassword string

    resource containerApp 'Microsoft.App/containerApps@2022-01-01-preview' ={
    name: name
    location: location
    properties:{
    managedEnvironmentId: containerAppEnvironmentId
    configuration: {
    dapr: {
    enabled: true
    appId: name
    appPort: port
    appProtocol: appProtocol
    }
    activeRevisionsMode: 'single'
    secrets: [
    {
    name: 'container-registry-password'
    value: registryPassword
    }
    ]
    registries: [
    {
    server: registry
    username: registryUsername
    passwordSecretRef: 'container-registry-password'
    }
    ]
    ingress: {
    external: externalIngress
    targetPort: port
    transport: transport
    allowInsecure: allowInsecure
    }
    }
    template: {
    containers: [
    {
    image: repositoryImage
    name: name
    env: envVars
    }
    ]
    scale: {
    minReplicas: minReplicas
    maxReplicas: maxReplicas
    }
    }
    }
    }

    output fqdn string = containerApp.properties.configuration.ingress.fqdn


    Understanding configuration differences with Dapr

    The code for this specific sample application is largely the same whether or not Dapr is integrated. However, even with this simple app, there are a few benefits and configuration differences when using Dapr that are worth exploring.

    In this scenario most of the changes are related to communication between the container apps. However, you can explore the full range of Dapr benefits by reading the Dapr integration with Azure Container Apps article in the conceptual documentation.

    Without Dapr

    Without Dapr the main.bicep template handles wiring up the front-end store app to communicate with the back-end apis by manually managing environment variables. The bicep template retrieves the fully qualified domains (fqdn) of the API apps as output parameters when they are created. Those configurations are then set as environment variables on the store container app.


    # Retrieve environment variables from API container creation
    var frontend_config = [
    {
    name: 'ProductsApi'
    value: 'http://${products.outputs.fqdn}'
    }
    {
    name: 'InventoryApi'
    value: 'http://${inventory.outputs.fqdn}'
    }
    ]

    # create the store api container app, passing in config
    module store 'container_app.bicep' = {
    name: 'store'
    params: {
    name: 'store'
    location: location
    registryPassword: acr.listCredentials().passwords[0].value
    registryUsername: acr.listCredentials().username
    containerAppEnvironmentId: env.outputs.id
    registry: acr.name
    envVars: union(shared_config, frontend_config)
    externalIngress: true
    }
    }

    The environment variables are then retrieved inside of the program class and used to configure the base URLs of the corresponding HTTP clients.


    builder.Services.AddHttpClient("Products", (httpClient) => httpClient.BaseAddress = new Uri(builder.Configuration.GetValue<string>("ProductsApi")));
    builder.Services.AddHttpClient("Inventory", (httpClient) => httpClient.BaseAddress = new Uri(builder.Configuration.GetValue<string>("InventoryApi")));

    With Dapr

    Dapr can be enabled on a container app when it is created, as seen below. This configuration adds a Dapr sidecar to the app to streamline discovery and communication features between the different container apps in your environment.


    # Create the container app with Dapr enabled
    resource containerApp 'Microsoft.App/containerApps@2022-01-01-preview' ={
    name: name
    location: location
    properties:{
    managedEnvironmentId: containerAppEnvironmentId
    configuration: {
    dapr: {
    enabled: true
    appId: name
    appPort: port
    appProtocol: appProtocol
    }
    activeRevisionsMode: 'single'
    secrets: [
    {
    name: 'container-registry-password'
    value: registryPassword
    }
    ]

    # Rest of template omitted for brevity...
    }
    }

    Some of these Dapr features can be surfaced through the program file. You can configure your HttpClient to leverage Dapr configurations when communicating with other apps in your environment.


    // reconfigure code to make requests to Dapr sidecar
    var baseURL = (Environment.GetEnvironmentVariable("BASE_URL") ?? "http://localhost") + ":" + (Environment.GetEnvironmentVariable("DAPR_HTTP_PORT") ?? "3500");
    builder.Services.AddHttpClient("Products", (httpClient) =>
    {
    httpClient.BaseAddress = new Uri(baseURL);
    httpClient.DefaultRequestHeaders.Add("dapr-app-id", "Products");
    });

    builder.Services.AddHttpClient("Inventory", (httpClient) =>
    {
    httpClient.BaseAddress = new Uri(baseURL);
    httpClient.DefaultRequestHeaders.Add("dapr-app-id", "Inventory");
    });


    Clean up resources

    If you're not going to continue to use this application, you can delete the Azure Container Apps and all the associated services by removing the resource group.

    Follow these steps in the Azure portal to remove the resources you created:

    1. In the Azure portal, navigate to the msdocswebappsapi resource group using the left navigation or search bar.
    2. Select the Delete resource group button at the top of the resource group Overview.
    3. Enter the resource group name msdocswebappsapi in the Are you sure you want to delete "msdocswebappsapi" confirmation dialog.
    4. Select Delete.
      The process to delete the resource group may take a few minutes to complete.
    - - + + \ No newline at end of file diff --git a/blog/tags/dapr/page/4/index.html b/blog/tags/dapr/page/4/index.html index 2e9b140a97..e3a5d006a6 100644 --- a/blog/tags/dapr/page/4/index.html +++ b/blog/tags/dapr/page/4/index.html @@ -14,15 +14,15 @@ - - + +

    16 posts tagged with "dapr"

    View All Tags

    · 10 min read
    Ayca Bas

    Welcome to Day 20 of #30DaysOfServerless!

    Every day millions of people spend their precious time in productivity tools. What if you use data and intelligence behind the Microsoft applications (Microsoft Teams, Outlook, and many other Office apps) to build seamless automations and custom apps to boost productivity?

    In this post, we'll learn how to build a seamless onboarding experience for new employees joining a company with the power of Microsoft Graph, integrated with Event Hubs and Logic Apps!


    What We'll Cover

    • ✨ The power of Microsoft Graph
    • 🖇️ How do Microsoft Graph and Event Hubs work together?
    • 🛠 Let's Build an Onboarding Workflow!
      • 1️⃣ Setup Azure Event Hubs + Key Vault
      • 2️⃣ Subscribe to users, receive change notifications from Logic Apps
      • 3️⃣ Create Onboarding workflow in the Logic Apps
    • 🚀 Debug: Your onboarding experience
    • ✋ Exercise: Try this tutorial out yourself!
    • 📚 Resources: For Self-Study


    ✨ The Power of Microsoft Graph

    Microsoft Graph is the gateway to data and intelligence in Microsoft 365 platform. Microsoft Graph exploses Rest APIs and client libraries to access data across Microsoft 365 core services such as Calendar, Teams, To Do, Outlook, People, Planner, OneDrive, OneNote and more.

    Overview of Microsoft Graph

    You can build custom experiences by using Microsoft Graph such as automating the onboarding process for new employees. When new employees are created in the Azure Active Directory, they will be automatically added in the Onboarding team on Microsoft Teams.

    Solution architecture


    🖇️ Microsoft Graph with Event Hubs

    Microsoft Graph uses a webhook mechanism to track changes in resources and deliver change notifications to the clients. For example, with Microsoft Graph Change Notifications, you can receive change notifications when:

    • a new task is added in the to-do list
    • a user changes the presence status from busy to available
    • an event is deleted/cancelled from the calendar

    If you'd like to track a large set of resources at a high frequency, use Azure Events Hubs instead of traditional webhooks to receive change notifications. Azure Event Hubs is a popular real-time events ingestion and distribution service built for scale.

    EVENT GRID - PARTNER EVENTS

    Microsoft Graph Change Notifications can be also received by using Azure Event Grid -- currently available for Microsoft Partners! Read the Partner Events Overview documentation for details.

    Setup Azure Event Hubs + Key Vault.

    To get Microsoft Graph Change Notifications delivered to Azure Event Hubs, we'll have to setup Azure Event Hubs and Azure Key Vault. We'll use Azure Key Vault to access to Event Hubs connection string.

    1️⃣ Create Azure Event Hubs

    1. Go to Azure Portal and select Create a resource, type Event Hubs and select click Create.
    2. Fill in the Event Hubs namespace creation details, and then click Create.
    3. Go to the newly created Event Hubs namespace page, select Event Hubs tab from the left pane and + Event Hub:
      • Name your Event Hub as Event Hub
      • Click Create.
    4. Click the name of the Event Hub, and then select Shared access policies and + Add to add a new policy:
      • Give a name to the policy
      • Check Send and Listen
      • Click Create.
    5. After the policy has been created, click the name of the policy to open the details panel, and then copy the Connection string-primary key value. Write it down; you'll need it for the next step.
    6. Go to Consumer groups tab in the left pane and select + Consumer group, give a name for your consumer group as onboarding and select Create.

    2️⃣ Create Azure Key Vault

    1. Go to Azure Portal and select Create a resource, type Key Vault and select Create.
    2. Fill in the Key Vault creation details, and then click Review + Create.
    3. Go to newly created Key Vault and select Secrets tab from the left pane and click + Generate/Import:
      • Give a name to the secret
      • For the value, paste in the connection string you generated at the Event Hubs step
      • Click Create
      • Copy the name of the secret.
    4. Select Access Policies from the left pane and + Add Access Policy:
      • For Secret permissions, select Get
      • For Principal, select Microsoft Graph Change Tracking
      • Click Add.
    5. Select Overview tab from the left pane and copy the Vault URI.

    Subscribe for Logic Apps change notifications

    To start receiving Microsoft Graph Change Notifications, we'll need to create subscription to the resource that we'd like to track - here, 'users'. We'll use Azure Logic Apps to create subscription.

    To create subscription for Microsoft Graph Change Notifications, we'll need to make a http post request to https://graph.microsoft.com/v1.0/subscriptions. Microsoft Graph requires Azure Active Directory authentication make API calls. First, we'll need to register an app to Azure Active Directory, and then we will make the Microsoft Graph Subscription API call with Azure Logic Apps.

    1️⃣ Create an app in Azure Active Directory

    1. In the Azure Portal, go to Azure Active Directory and select App registrations from the left pane and select + New registration. Fill in the details for the new App registration form as below:
      • Name: Graph Subscription Flow Auth
      • Supported account types: Accounts in any organizational directory (Any Azure AD directory - Multitenant) and personal Microsoft accounts (e.g. Skype, Xbox)
      • Select Register.
    2. Go to newly registered app in Azure Active Directory, select API permissions:
      • Select + Add a permission and Microsoft Graph
      • Select Application permissions and add User.Read.All and Directory.Read.All.
      • Select Grant admin consent for the organization
    3. Select Certificates & secrets tab from the left pane, select + New client secret:
      • Choose desired expiry duration
      • Select Add
      • Copy the value of the secret.
    4. Go to Overview from the left pane, copy Application (client) ID and Directory (tenant) ID.

    2️⃣ Create subscription with Azure Logic Apps

    1. Go to Azure Portal and select Create a resource, type Logic apps and select click Create.

    2. Fill in the Logic Apps creation details, and then click Create.

    3. Go to the newly created Logic Apps page, select Workflows tab from the left pane and select + Add:

      • Give a name to the new workflow as graph-subscription-flow
      • Select Stateful as a state type
      • Click Create.
    4. Go to graph-subscription-flow, and then select Designer tab.

    5. In the Choose an operation section, search for Schedule and select Recurrence as a trigger. Fill in the parameters as below:

      • Interval: 61
      • Frequency: Minute
      • Time zone: Select your own time zone
      • Start time: Set a start time
    6. Select + button in the flow and select add an action. Search for HTTP and select HTTP as an action. Fill in the parameters as below:

      • Method: POST
      • URI: https://graph.microsoft.com/v1.0/subscriptions
      • Headers:
        • Key: Content-type
        • Value: application/json
      • Body:
      {
      "changeType": "created, updated",
      "clientState": "secretClientValue",
      "expirationDateTime": "@{addHours(utcNow(), 1)}",
      "notificationUrl": "EventHub:https://<YOUR-VAULT-URI>/secrets/<YOUR-KEY-VAULT-SECRET-NAME>?tenantId=72f988bf-86f1-41af-91ab-2d7cd011db47",
      "resource": "users"
      }

      In notificationUrl, make sure to replace <YOUR-VAULT-URI> with the vault uri and <YOUR-KEY-VAULT-SECRET-NAME> with the secret name that you copied from the Key Vault.

      In resource, define the resource type you'd like to track changes. For our example, we will track changes for users resource.

      • Authentication:
        • Authentication type: Active Directory OAuth
        • Authority: https://login.microsoft.com
        • Tenant: Directory (tenant) ID copied from AAD app
        • Audience: https://graph.microsoft.com
        • Client ID: Application (client) ID copied from AAD app
        • Credential Type: Secret
        • Secret: value of the secret copied from AAD app
    7. Select Save and run your workflow from the Overview tab.

      Check your subscription in Graph Explorer: If you'd like to make sure that your subscription is created successfully by Logic Apps, you can go to Graph Explorer, login with your Microsoft 365 account and make GET request to https://graph.microsoft.com/v1.0/subscriptions. Your subscription should appear in the response after it's created successfully.

    Subscription workflow success

    After subscription is created successfully by Logic Apps, Azure Event Hubs will receive notifications whenever there is a new user created in Azure Active Directory.


    Create Onboarding workflow in Logic Apps

    We'll create a second workflow in the Logic Apps to receive change notifications from Event Hubs when there is a new user created in the Azure Active Directory and add new user in Onboarding team on Microsoft Teams.

    1. Go to the Logic Apps you created in the previous steps, select Workflows tab and create a new workflow by selecting + Add:
      • Give a name to the new workflow as teams-onboarding-flow
      • Select Stateful as a state type
      • Click Create.
    2. Go to teams-onboarding-flow, and then select Designer tab.
    3. In the Choose an operation section, search for Event Hub, select When events are available in Event Hub as a trigger. Setup Event Hub connection as below:
      • Create Connection:
        • Connection name: Connection
        • Authentication Type: Connection String
        • Connection String: Go to Event Hubs > Shared Access Policies > RootManageSharedAccessKey and copy Connection string–primary key
        • Select Create.
      • Parameters:
        • Event Hub Name: Event Hub
        • Consumer Group Name: onboarding
    4. Select + in the flow and add an action, search for Control and add For each as an action. Fill in For each action as below:
      • Select output from previous steps: Events
    5. Inside For each, select + in the flow and add an action, search for Data operations and select Parse JSON. Fill in Parse JSON action as below:
      • Content: Events Content
      • Schema: Copy the json content from schema-parse.json and paste as a schema
    6. Select + in the flow and add an action, search for Control and add For each as an action. Fill in For each action as below:
      • Select output from previous steps: value
      1. Inside For each, select + in the flow and add an action, search for Microsoft Teams and select Add a member to a team. Login with your Microsoft 365 account to create a connection and fill in Add a member to a team action as below:
      • Team: Create an Onboarding team on Microsoft Teams and select
      • A user AAD ID for the user to add to a team: id
    7. Select Save.

    🚀 Debug your onboarding experience

    To debug our onboarding experience, we'll need to create a new user in Azure Active Directory and see if it's added in Microsoft Teams Onboarding team automatically.

    1. Go to Azure Portal and select Azure Active Directory from the left pane and go to Users. Select + New user and Create new user. Fill in the details as below:

      • User name: JaneDoe
      • Name: Jane Doe

      new user in Azure Active Directory

    2. When you added Jane Doe as a new user, it should trigger the teams-onboarding-flow to run. teams onboarding flow success

    3. Once the teams-onboarding-flow runs successfully, you should be able to see Jane Doe as a member of the Onboarding team on Microsoft Teams! 🥳 new member in Onboarding team on Microsoft Teams

    Congratulations! 🎉

    You just built an onboarding experience using Azure Logic Apps, Azure Event Hubs and Azure Key Vault.


    📚 Resources

    - - + + \ No newline at end of file diff --git a/blog/tags/dapr/page/5/index.html b/blog/tags/dapr/page/5/index.html index 6f04473967..1f2f85d57c 100644 --- a/blog/tags/dapr/page/5/index.html +++ b/blog/tags/dapr/page/5/index.html @@ -14,14 +14,14 @@ - - + +

    16 posts tagged with "dapr"

    View All Tags

    · 5 min read
    Mike Morton

    Welcome to Day 19 of #30DaysOfServerless!

    Today, we have a special set of posts from our Zero To Hero 🚀 initiative, featuring blog posts authored by our Product Engineering teams for #ServerlessSeptember. Posts were originally published on the Apps on Azure blog on Microsoft Tech Community.


    What We'll Cover

    • Log Streaming - in Azure Portal
    • Console Connect - in Azure Portal
    • Metrics - using Azure Monitor
    • Log Analytics - using Azure Monitor
    • Metric Alerts and Log Alerts - using Azure Monitor


    In past weeks, @kendallroden wrote about what it means to be Cloud-Native and @Anthony Chu the various ways to get your apps running on Azure Container Apps. Today, we will talk about the observability tools you can use to observe, debug, and diagnose your Azure Container Apps.

    Azure Container Apps provides several observability features to help you debug and diagnose your apps. There are both Azure portal and CLI options you can use to help understand the health of your apps and help identify when issues arise.

    While these features are helpful throughout your container app’s lifetime, there are two that are especially helpful. Log streaming and console connect can be a huge help in the initial stages when issues often rear their ugly head. Let's dig into both of these a little.

    Log Streaming

    Log streaming allows you to use the Azure portal to view the streaming logs from your app. You’ll see the logs written from the app to the container’s console (stderr and stdout). If your app is running multiple revisions, you can choose from which revision to view logs. You can also select a specific replica if your app is configured to scale. Lastly, you can choose from which container to view the log output. This is useful when you are running a custom or Dapr sidecar container. view streaming logs

    Here’s an example CLI command to view the logs of a container app.

    az containerapp logs show -n MyContainerapp -g MyResourceGroup

    You can find more information about the different options in our CLI docs.

    Console Connect

    In the Azure portal, you can connect to the console of a container in your app. Like log streaming, you can select the revision, replica, and container if applicable. After connecting to the console of the container, you can execute shell commands and utilities that you have installed in your container. You can view files and their contents, monitor processes, and perform other debugging tasks.

    This can be great for checking configuration files or even modifying a setting or library your container is using. Of course, updating a container in this fashion is not something you should do to a production app, but tweaking and re-testing an app in a non-production environment can speed up development.

    Here’s an example CLI command to connect to the console of a container app.

    az containerapp exec -n MyContainerapp -g MyResourceGroup

    You can find more information about the different options in our CLI docs.

    Metrics

    Azure Monitor collects metric data from your container app at regular intervals to help you gain insights into the performance and health of your container app. Container apps provide these metrics:

    • CPU usage
    • Memory working set bytes
    • Network in bytes
    • Network out bytes
    • Requests
    • Replica count
    • Replica restart count

    Here you can see the metrics explorer showing the replica count for an app as it scaled from one replica to fifteen, and then back down to one.

    You can also retrieve metric data through the Azure CLI.

    Log Analytics

    Azure Monitor Log Analytics is great for viewing your historical logs emitted from your container apps. There are two custom tables of interest, the ContainerAppConsoleLogs_CL which contains all the log messages written by your app (stdout and stderr), and the ContainerAppSystemLogs_CL which contain the system messages from the Azure Container Apps service.

    You can also query Log Analytics through the Azure CLI.

    Alerts

    Azure Monitor alerts notify you so that you can respond quickly to critical issues. There are two types of alerts that you can define:

    You can create alert rules from metric charts in the metric explorer and from queries in Log Analytics. You can also define and manage alerts from the Monitor|Alerts page.

    Here is what creating an alert looks like in the Azure portal. In this case we are setting an alert rule from the metric explorer to trigger an alert if the replica restart count for a specific container app is greater than two within the last fifteen minutes.

    To learn more about alerts, refer to Overview of alerts in Microsoft Azure.

    Conclusion

    In this article, we looked at the several ways to observe, debug, and diagnose your Azure Container Apps. As you can see there are rich portal tools and a complete set of CLI commands to use. All the tools are helpful throughout the lifecycle of your app, be sure to take advantage of them when having an issue and/or to prevent issues.

    To learn more, visit Azure Container Apps | Microsoft Azure today!

    ASK THE EXPERT: LIVE Q&A

    The Azure Container Apps team will answer questions live on September 29.

    - - + + \ No newline at end of file diff --git a/blog/tags/dapr/page/6/index.html b/blog/tags/dapr/page/6/index.html index b75c4c299d..395fcb1860 100644 --- a/blog/tags/dapr/page/6/index.html +++ b/blog/tags/dapr/page/6/index.html @@ -14,14 +14,14 @@ - - + +

    16 posts tagged with "dapr"

    View All Tags

    · 10 min read
    Brian Benz

    Welcome to Day 18 of #30DaysOfServerless!

    Yesterday my Serverless September post introduced you to making Azure Logic Apps and Azure Cosmos DB work together with a sample application that collects weather data. Today I'm sharing a more robust solution that actually reads my mail. Let's learn about Teaching the cloud to read your mail!

    Ready? Let's go!


    What We'll Cover

    • Introduction to the ReadMail solution
    • Setting up Azure storage, Cosmos DB and Computer Vision
    • Connecting it all together with a Logic App
    • Resources: For self-study!


    Introducing the ReadMail solution

    The US Postal system offers a subscription service that sends you images of mail it will be delivering to your home. I decided it would be cool to try getting Azure to collect data based on these images, so that I could categorize my mail and track the types of mail that I received.

    To do this, I used Azure storage, Cosmos DB, Logic Apps, and computer vision. When a new email comes in from the US Postal service (USPS), it triggers a logic app that:

    • Posts attachments to Azure storage
    • Triggers Azure Computer vision to perform an OCR function on attachments
    • Extracts any results into a JSON document
    • Writes the JSON document to Cosmos DB

    workflow for the readmail solution

    In this post I'll walk you through setting up the solution for yourself.

    Prerequisites

    Setup Azure Services

    First, we'll create all of the target environments we need to be used by our Logic App, then we;ll create the Logic App.

    1. Azure Storage

    We'll be using Azure storage to collect attached images from emails as they arrive. Adding images to Azure storage will also trigger a workflow that performs OCR on new attached images and stores the OCR data in Cosmos DB.

    To create a new Azure storage account from the portal dashboard, Select Create a resource > Storage account > Create.

    The Basics tab covers all of the features and information that we will need for this solution:

    SectionFieldRequired or optionalDescription
    Project detailsSubscriptionRequiredSelect the subscription for the new storage account.
    Project detailsResource groupRequiredCreate a new resource group that you will use for storage, Cosmos DB, Computer Vision and the Logic App.
    Instance detailsStorage account nameRequiredChoose a unique name for your storage account. Storage account names must be between 3 and 24 characters in length and may contain numbers and lowercase letters only.
    Instance detailsRegionRequiredSelect the appropriate region for your storage account.
    Instance detailsPerformanceRequiredSelect Standard performance for general-purpose v2 storage accounts (default).
    Instance detailsRedundancyRequiredSelect locally-redundant Storage (LRS) for this example.

    Select Review + create to accept the remaining default options, then validate and create the account.

    2. Azure CosmosDB

    CosmosDB will be used to store the JSON documents returned by the COmputer Vision OCR process.

    See more details and screen shots for setting up CosmosDB in yesterday's Serverless September post - Using Logic Apps with Cosmos DB

    To get started with Cosmos DB, you create an account, then a database, then a container to store JSON documents. To create a new Cosmos DB account from the portal dashboard, Select Create a resource > Azure Cosmos DB > Create. Choose core SQL for the API.

    Select your subscription, then for simplicity use the same resource group you created when you set up storage. Enter an account name and choose a location, select provisioned throughput capacity mode and apply the free tier discount. From here you can select Review and Create, then Create

    Next, create a new database and container. Go to the Data Explorer in your new Cosmos DB account, and choose New Container. Name the database, and keep all the other defaults except:

    SettingAction
    Container IDid
    Container partition/id

    Press OK to create a database and container

    3. Azure Computer Vision

    Azure Cognitive Services' Computer Vision will perform an OCR process on each image attachment that is stored in Azure storage.

    From the portal dashboard, Select Create a resource > AI + Machine Learning > Computer Vision > Create.

    The Basics and Identity tabs cover all of the features and information that we will need for this solution:

    Basics Tab

    SectionFieldRequired or optionalDescription
    Project detailsSubscriptionRequiredSelect the subscription for the new service.
    Project detailsResource groupRequiredUse the same resource group that you used for Azure storage and Cosmos DB.
    Instance detailsRegionRequiredSelect the appropriate region for your Computer Vision service.
    Instance detailsNameRequiredChoose a unique name for your Computer Vision service.
    Instance detailsPricingRequiredSelect the free tier for this example.

    Identity Tab

    SectionFieldRequired or optionalDescription
    System assigned managed identityStatusRequiredEnable system assigned identity to grant the resource access to other existing resources.

    Select Review + create to accept the remaining default options, then validate and create the account.


    Connect it all with a Logic App

    Now we're ready to put this all together in a Logic App workflow!

    1. Create Logic App

    From the portal dashboard, Select Create a resource > Integration > Logic App > Create. Name your Logic App and select a location, the rest of the settings can be left at their defaults.

    2. Create Workflow: Add Trigger

    Once the Logic App is created, select Create a workflow from designer.

    A workflow is a series of steps that defines a task or process. Each workflow starts with a single trigger, after which you must add one or more actions.

    When in designer, search for outlook.com on the right under Add a trigger. Choose outlook.com. Choose When a new email arrives as the trigger.

    A trigger is always the first step in any workflow and specifies the condition for running any further steps in that workflow.

    Set the following values:

    ParameterValue
    FolderInbox
    ImportanceAny
    Only With AttachmentsYes
    Include AttachmentsYes

    Then add a new parameter:

    ParameterValue
    FromAdd the email address that sends you the email with attachments
    3. Create Workflow: Add Action (for Trigger)

    Choose add an action and choose control > for-each.

    logic app for each

    Inside the for-each action, in Select an output from previous steps, choose attachments. Then, again inside the for-each action, add the create blob action:

    Set the following values:

    ParameterValue
    Folder Path/mailreaderinbox
    Blob NameAttachments Name
    Blob ContentAttachments Content

    This extracts attachments from the email and created a new blob for each attachment.

    Next, inside the same for-each action, add the get blob content action.

    Set the following values:

    ParameterValue
    Blobid
    Infer content typeYes

    We create and read from a blob for each attachment because Computer Vision needs a non-virtual source to read from when performing an OCR process. Because we enabled system assigned identity to grant Computer Vision to other existing resources, it can access the blob but not the outlook.com attachment. Also, we pass the ID of the blob to use as a unique ID when writing to Cosmos DB.

    create blob from attachments

    Next, inside the same for-each action, choose add an action and choose control > condition. Set the value to Media Type > is equal to > image/JPEG

    The USPS sends attachments of multiple types, but we only want to scan attachments that have images of our mail, which are always JPEG images. If the condition is true, we will process the image with Computer Vision OCR and write the results to a JSON document in CosmosDB.

    In the True section of the condition, add an action and choose Computer Vision API > Optical Character Recognition (OCR) to JSON.

    Set the following values:

    ParameterValue
    Image SourceImage Content
    Image contentFile Content

    In the same True section of the condition, choose add an action and choose Cosmos DB. Choose Create or Update Document from the actions. Select Access Key, and provide the primary read-write key (found under keys in Cosmos DB), and the Cosmos DB account ID (without 'documents.azure.com').

    Next, fill in your Cosmos DB Database ID and Collection ID. Create a JSON document by selecting dynamic content elements and wrapping JSON formatting around them.

    Be sure to use the ID passed from blob storage as your unique ID for CosmosDB. That way you can troubleshoot and JSON or OCR issues by tracing back the JSON document in Cosmos Db to the blob in Azure storage. Also, include the Computer Vision JSON response, as it contains the results of the Computer Vision OCR scan. all other elements are optional.

    4. TEST WORKFLOW

    When complete, you should have an action the Logic App designer that looks something like this:

    Logic App workflow create or update document in cosmosdb

    Save the workflow and test the connections by clicking Run Trigger > Run. If connections are working, you should see documents flowing into Cosmos DB each time that an email arrives with image attachments.

    Check the data in Cosmos Db by opening the Data explorer, then choosing the container you created and selecting items. You should see documents similar to this:

    Logic App workflow with trigger and action

    1. Congratulations

    You just built your personal ReadMail solution with Logic Apps! 🎉


    Resources: For self-study!

    Once you have an understanding of the basics in ths post, there is so much more to learn!

    Thanks for stopping by!

    - - + + \ No newline at end of file diff --git a/blog/tags/dapr/page/7/index.html b/blog/tags/dapr/page/7/index.html index 99c4d0ecf5..92dc3c2e1f 100644 --- a/blog/tags/dapr/page/7/index.html +++ b/blog/tags/dapr/page/7/index.html @@ -14,14 +14,14 @@ - - + +

    16 posts tagged with "dapr"

    View All Tags

    · 6 min read
    Brian Benz

    Welcome to Day 17 of #30DaysOfServerless!

    In past weeks, we've covered serverless technologies that provide core capabilities (functions, containers, microservices) for building serverless solutions. This week we're looking at technologies that make service integrations more seamless, starting with Logic Apps. Let's look at one usage example today!

    Ready? Let's Go!


    What We'll Cover

    • Introduction to Logic Apps
    • Settng up Cosmos DB for Logic Apps
    • Setting up a Logic App connection and event
    • Writing data to Cosmos DB from a Logic app
    • Resources: For self-study!


    Introduction to Logic Apps

    Previously in Serverless September, we've covered Azure Functions, where the event triggers code. In Logic Apps, the event triggers a workflow that you design. Logic Apps enable serverless applications to connect to external sources for data then automate business processes via workflows.

    In this post I'll walk you through setting up a Logic App that works with Cosmos DB. For this example, we'll connect to the MSN weather service, an design a logic app workflow that collects data when weather changes, and writes the data to Cosmos DB.

    PREREQUISITES

    Setup Cosmos DB for Logic Apps

    Cosmos DB has many APIs to choose from, but to use the default Logic App connection, we need to choose the a Cosmos DB SQL API. We'll set this up via the Azure Portal.

    To get started with Cosmos DB, you create an account, then a database, then a container to store JSON documents. To create a new Cosmos DB account from the portal dashboard, Select Create a resource > Azure Cosmos DB > Create. Choose core SQL for the API.

    Select your subscription, then create a new resource group called CosmosWeather. Enter an account name and choose a location, select provisioned throughput capacity mode and apply the free tier discount. From here you can select Review and Create, then Create

    Azure Cosmos DB is available in two different capacity modes: provisioned throughput and serverless. You can perform the same database operations in both modes, but the way you get billed for these operations is different. We wil be using provisioned throughput and the free tier for this example.

    Setup the CosmosDB account

    Next, create a new database and container. Go to the Data Explorer in your new Cosmos DB account, and choose New Container. Name the database, and keep all the orher defaults except:

    SettingAction
    Container IDid
    Container partition/id

    Press OK to create a database and container

    A database is analogous to a traditional DBMS namespace. It's used to organize one or more containers.

    Setup the CosmosDB Container

    Now we're ready to set up our logic app an write to Cosmos DB!

    Setup Logic App connection + event

    Once the Cosmos DB SQL API account is created, we can set up our Logic App. From the portal dashboard, Select Create a resource > Integration > Logic App > Create. Name your Logic App and select a location, the rest fo the settings can be left at their defaults. Once you new Logic App is created, select Create a workflow from designer to get started.

    A workflow is a series of steps that defines a task or process. Each workflow starts with a single trigger, after which you must add one or more actions.

    When in designer, search for weather on the right under Add a trigger. Choose MSN Weather. Choose When the current conditions change as the trigger.

    A trigger is always the first step in any workflow and specifies the condition for running any further steps in that workflow.

    Add a location. Valid locations are City, Region, State, Country, Landmark, Postal Code, latitude and longitude. This triggers a new workflow when the conditions change for a location.

    Write data from Logic App to Cosmos DB

    Now we are ready to set up the action to write data to Cosmos DB. Choose add an action and choose Cosmos DB.

    An action is each step in a workflow after the trigger. Every action runs some operation in a workflow.

    In this case, we will be writing a JSON document to the Cosmos DB container we created earlier. Choose Create or Update Document from the actions. At this point you should have a workflow in designer that looks something like this:

    Logic App workflow with trigger

    Start wth the connection for set up the Cosmos DB action. Select Access Key, and provide the primary read-write key (found under keys in Cosmos DB), and the Cosmos DB account ID (without 'documents.azure.com').

    Next, fill in your Cosmos DB Database ID and Collection ID. Create a JSON document bt selecting dynamic content elements and wrapping JSON formatting around them.

    You will need a unique ID for each document that you write to Cosmos DB, for that you can use an expression. Because we declared id to be our unique ID in Cosmos DB, we will use use that for the name. Under expressions, type guid() and press enter to add a unique ID to the JSON document. When complete, you should have a workflow in designer that looks something like this:

    Logic App workflow with trigger and action

    Save the workflow and test the connections by clicking Run Trigger > Run. If connections are working, you should see documents flowing into Cosmos DB over the next few minutes.

    Check the data in Cosmos Db by opening the Data explorer, then choosing the container you created and selecting items. You should see documents similar to this:

    Logic App workflow with trigger and action

    Resources: For self-study!

    Once you've grasped the basics in ths post, there is so much more to learn!

    Thanks for stopping by!

    - - + + \ No newline at end of file diff --git a/blog/tags/dapr/page/8/index.html b/blog/tags/dapr/page/8/index.html index 72064f2631..9816826cb6 100644 --- a/blog/tags/dapr/page/8/index.html +++ b/blog/tags/dapr/page/8/index.html @@ -14,13 +14,13 @@ - - + +

    16 posts tagged with "dapr"

    View All Tags

    · 4 min read
    Nitya Narasimhan
    Devanshi Joshi

    Welcome to Day 15 of #30DaysOfServerless!

    This post marks the midpoint of our Serverless on Azure journey! Our Week 2 Roadmap showcased two key technologies - Azure Container Apps (ACA) and Dapr - for building serverless microservices. We'll also look at what happened elsewhere in #ServerlessSeptember, then set the stage for our next week's focus: Serverless Integrations.

    Ready? Let's Go!


    What We'll Cover

    • ICYMI: This Week on #ServerlessSeptember
    • Recap: Microservices, Azure Container Apps & Dapr
    • Coming Next: Serverless Integrations
    • Exercise: Take the Cloud Skills Challenge
    • Resources: For self-study!

    This Week In Events

    We had a number of activities happen this week - here's a quick summary:

    This Week in #30Days

    In our #30Days series we focused on Azure Container Apps and Dapr.

    • In Hello Container Apps we learned how Azure Container Apps helps you run microservices and containerized apps on serverless platforms. And we build and deployed our first ACA.
    • In Microservices Communication we explored concepts like environments and virtual networking, with a hands-on example to show how two microservices communicate in a deployed ACA.
    • In Scaling Your Container Apps we learned about KEDA (Kubernetes Event-Driven Autoscaler) and how to configure autoscaling for your ACA based on KEDA-supported triggers.
    • In Build with Dapr we introduced the Distributed Application Runtime (Dapr) and learned how its Building Block APIs and sidecar architecture make it easier to develop microservices with ACA.
    • In Secure ACA Access we learned how to secure ACA access to external services with - and without - Dapr, covering Secret Stores and Managed Identity.
    • Finally, Build ACA with Dapr tied it all together with a enterprise app scenario where an orders processor (ACA) uses Dapr APIs (PubSub, State Management) to receive and store order messages from Azure Service Bus.

    Here's a visual recap:

    Self Study: Code Samples & Tutorials

    There's no better way to get familiar with the concepts, than to dive in and play with code samples and hands-on tutorials. Here are 4 resources to bookmark and try out:

    1. Dapr Quickstarts - these walk you through samples showcasing individual Building Block APIs - with multiple language options available.
    2. Dapr Tutorials provides more complex examples of microservices applications and tools usage, including a Distributed Calculator polyglot app.
    3. Next, try to Deploy a Dapr application to Azure Container Apps to get familiar with the process of setting up the environment, then deploying the app.
    4. Or, explore the many Azure Container Apps samples showcasing various features and more complex architectures tied to real world scenarios.

    What's Next: Serverless Integrations!

    So far we've talked about core technologies (Azure Functions, Azure Container Apps, Dapr) that provide foundational support for your serverless solution. Next, we'll look at Serverless Integrations - specifically at technologies like Azure Logic Apps and Azure Event Grid that automate workflows and create seamless end-to-end solutions that integrate other Azure services in serverless-friendly ways.

    Take the Challenge!

    The Cloud Skills Challenge is still going on, and we've already had hundreds of participants join and complete the learning modules to skill up on Serverless.

    There's still time to join and get yourself on the leaderboard. Get familiar with Azure Functions, SignalR, Logic Apps, Azure SQL and more - in serverless contexts!!


    - - + + \ No newline at end of file diff --git a/blog/tags/dapr/page/9/index.html b/blog/tags/dapr/page/9/index.html index 3c4fcc602b..31e80ec0ad 100644 --- a/blog/tags/dapr/page/9/index.html +++ b/blog/tags/dapr/page/9/index.html @@ -14,8 +14,8 @@ - - + +
    @@ -24,7 +24,7 @@ Image showing container apps role assignment

  • Lastly, we need to restart the container app revision, to do so run the command below:

     ##Get revision name and assign it to a variable
    $REVISION_NAME = (az containerapp revision list `
    --name $BACKEND_SVC_NAME `
    --resource-group $RESOURCE_GROUP `
    --query [0].name)

    ##Restart revision by name
    az containerapp revision restart `
    --resource-group $RESOURCE_GROUP `
    --name $BACKEND_SVC_NAME `
    --revision $REVISION_NAME
  • Run end-to-end Test on Azure

    From the Azure Portal, select the Azure Container App orders-processor and navigate to Log stream under Monitoring tab, leave the stream connected and opened. From the Azure Portal, select the Azure Service Bus Namespace ordersservices, select the topic orderreceivedtopic, select the subscription named orders-processor-subscription, then click on Service Bus Explorer (preview). From there we need to publish/send a message. Use the JSON payload below

    ```json
    {
    "data": {
    "reference": "Order 150",
    "quantity": 150,
    "createdOn": "2022-05-10T12:45:22.0983978Z"
    }
    }
    ```

    If all is configured correctly, you should start seeing the information logs in Container Apps Log stream, similar to the images below Image showing publishing messages from Azure Service

    Information logs on the Log stream of the deployed Azure Container App Image showing ACA Log Stream

    🎉 CONGRATULATIONS

    You have successfully deployed to the cloud an Azure Container App and configured Dapr Pub/Sub API with Azure Service Bus.

    9. Clean up

    If you are done with the tutorial, use the following command to delete the resource group and all its contained resources to avoid incurring further costs.

    az group delete --name $RESOURCE_GROUP

    Exercise

    I left for you the configuration of the Dapr State Store API with Azure Cosmos DB :)

    When you look at the action method OrderReceived in controller ExternalOrdersController, you will see that I left a line with ToDo: note, this line is responsible to save the received message (OrderModel) into Azure Cosmos DB.

    There is no need to change anything on the code base (other than removing this commented line), that's the beauty of Dapr Building Blocks and how easy it allows us to plug components to our microservice application without any plumping and brining external SDKs.

    For sure you need to work on the configuration part of Dapr State Store by creating a new component file like what we have done with the Pub/Sub API, things that you need to work on are:

    • Provision Azure Cosmos DB Account and obtain its masterKey.
    • Create a Dapr Component file adhering to Dapr Specs.
    • Create an Azure Container Apps component file adhering to ACA component specs.
    • Test locally on your dev machine using Dapr Component file.
    • Register the new Dapr State Store component with Azure Container Apps Environment and set the Cosmos Db masterKey from the Azure Portal. If you want to challenge yourself more, use the Managed Identity approach as done in this post! The right way to protect your keys and you will not worry about managing CosmosDb keys anymore!
    • Build a new image of the application and push it to Azure Container Registry.
    • Update Azure Container Apps and create a new revision which contains the updated code.
    • Verify the results by checking Azure Cosmos DB, you should see the Order Model stored in Cosmos DB.

    If you need help, you can always refer to my blog post Azure Container Apps State Store With Dapr State Management API which contains exactly what you need to implement here, so I'm very confident you will be able to complete this exercise with no issues, happy coding :)

    What's Next?

    If you enjoyed working with Dapr and Azure Container Apps, and you want to have a deep dive with more complex scenarios (Dapr bindings, service discovery, auto scaling with KEDA, sync services communication, distributed tracing, health probes, etc...) where multiple services deployed to a single Container App Environment; I have created a detailed tutorial which should walk you through step by step with through details to build the application.

    So far, the published posts below, and I'm publishing more posts on weekly basis, so stay tuned :)

    Resources

    - - + + \ No newline at end of file diff --git a/blog/tags/devtools/index.html b/blog/tags/devtools/index.html index 62aeb75576..0f4a98a79e 100644 --- a/blog/tags/devtools/index.html +++ b/blog/tags/devtools/index.html @@ -14,13 +14,13 @@ - - + +

    2 posts tagged with "devtools"

    View All Tags

    · 5 min read
    Savannah Ostrowski

    Welcome to Beyond #30DaysOfServerless! in October!

    Yes, it's October!! And since we ended #ServerlessSeptember with a focus on End-to-End Development for Serverless on Azure, we thought it would be good to share updates in October that can help you skill up even further.

    Today, we're following up on the Code to Cloud with azd blog post (Day #29) where we introduced the Azure Developer CLI (azd), an open-source tool for streamlining your end-to-end developer experience going from local development environment to Azure cloud. In today's post, we celebrate the October 2022 release of the tool, with three cool new features.

    And if it's October, it must be #Hacktoberfest!! Read on to learn about how you can take advantage of one of the new features, to contribute to the azd open-source community and ecosystem!

    Ready? Let's go!


    What We'll Cover

    • Azure Friday: Introducing the Azure Developer CLI (Video)
    • October 2022 Release: What's New in the Azure Developer CLI?
      • Azure Pipelines for CI/CD: Learn more
      • Improved Infrastructure as Code structure via Bicep modules: Learn more
      • A new azd template gallery: The new azd-templates gallery for community use! Learn more
    • Awesome-Azd: The new azd-templates gallery for Community use
      • Features: discover, create, contribute, request - templates
      • Hacktoberfest: opportunities to contribute in October - and beyond!


    Azure Friday

    This post is a follow-up to our #ServerlessSeptember post on Code to Cloud with Azure Developer CLI where we introduced azd, a new open-source tool that makes it quick and simple for you to move your application from a local development environment to Azure, streamlining your end-to-end developer workflow in the process.

    Prefer to watch a video overview? I have you covered! Check out my recent conversation with Scott Hanselman on Azure Friday where we:

    • talked about the code-to-cloud developer journey
    • walkthrough the ins and outs of an azd template
    • explored Azure Developer CLI commands in the terminal and VS Code, and
    • (probably most importantly) got a web app up and running on Azure with a database, Key Vault and monitoring all in a couple of minutes

    October Release

    We're pleased to announce the October 2022 release of the Azure Developer CLI (currently 0.3.0-beta.2). Read the release announcement for more details. Here are the highlights:

    • Azure Pipelines for CI/CD: This addresses azure-dev#101, adding support for Azure Pipelines (alongside GitHub Actions) as a CI/CD provider. Learn more about usage and related documentation.
    • Improved Infrastructure as Code structure via Bicep modules: This addresses azure-dev#543, which recognized the complexity of using a single resources.bicep file for all resources. With this release, azd templates now come with Bicep modules organized by purpose making it easier to edit and understand. Learn more about this structure, and how to use it.
    • New Templates Gallery - awesome-azd: This addresses azure-dev#398, which aimed to make templates more discoverable and easier to contribute. Learn more about how the new gallery improves the template discovery experience.

    In the next section, we'll dive briefly into the last feature, introducing the new awesome-azd site and resource for templates discovery and contribution. And, since it's #Hacktoberfest season, we'll talk about the Contributor Guide and the many ways you can contribute to this project - with, or without, code.


    It's awesome-azd

    Welcome to awesome-azd a new template gallery hosted on GitHub Pages, and meant to be a destination site for discovering, requesting, and contributing azd-templates for community use!

    In addition, it's README reflects the awesome-list resource format, providing a location for the community to share "best of" resources for Azure Developer CLI - from blog posts and videos, to full-scale tutorials and templates.

    The Gallery is organized into three main areas:

    Take a minute to explore the Gallery and note the features:

    • Search for templates by name
    • Requested Templates - indicating asks from the community
    • Featured Templates - highlighting high-quality templates
    • Filters - to discover templates by and/or query combinations

    Check back often to see the latest contributed templates and requests!


    Hacktoberfest

    So, why is this a good time to talk about the Gallery? Because October means it's time for #Hacktoberfest - a month-long celebration of open-source projects and their maintainers, and an opportunity for first-time contributors to get support and guidance making their first pull-requests! Check out the #Hacktoberfest topic on GitHub for projects you can contribute to.

    And we hope you think of awesome-azd as another possible project to contribute to.

    Check out the FAQ section to learn how to create, discover, and contribute templates. Or take a couple of minutes to watch this video walkthrough from Jon Gallant:

    And don't hesitate to reach out to us - either via Issues on the repo, or in the Discussions section of this site, to give us feedback!

    Happy Hacking! 🎃


    - - + + \ No newline at end of file diff --git a/blog/tags/devtools/page/2/index.html b/blog/tags/devtools/page/2/index.html index cc48d1471a..193803ea53 100644 --- a/blog/tags/devtools/page/2/index.html +++ b/blog/tags/devtools/page/2/index.html @@ -14,13 +14,13 @@ - - + +

    2 posts tagged with "devtools"

    View All Tags

    · 9 min read
    Nitya Narasimhan

    Welcome to Day 3 of #30DaysOfServerless!

    Yesterday we learned core concepts and terminology for Azure Functions, the signature Functions-as-a-Service option on Azure. Today we take our first steps into building and deploying an Azure Functions app, and validate local development setup.

    Ready? Let's go.


    What We'll Cover


    Developer Guidance

    Before we jump into development, let's familiarize ourselves with language-specific guidance from the Azure Functions Developer Guide. We'll review the JavaScript version but guides for F#, Java, Python, C# and PowerShell are also available.

    1. A function is defined by two things: code (written in a supported programming language) and configuration (specified in a functions.json file, declaring the triggers, bindings and other context for execution).

    2. A function app is the unit of deployment for your functions, and is associated with a single execution context or runtime. It can contain multiple functions, but they must be in the same language.

    3. A host configuration is runtime-specific configuration that affects all functions running in a given function app instance. It is defined in a host.json file.

    4. A recommended folder structure is defined for the function app, but may vary based on the programming language used. Check the documentation on folder structures to learn the default for your preferred language.

    Here's an example of the JavaScript folder structure for a function app containing two functions with some shared dependencies. Note that host.json (runtime configuration) is defined once, in the root directory. And function.json is defined separately for each function.

    FunctionsProject
    | - MyFirstFunction
    | | - index.js
    | | - function.json
    | - MySecondFunction
    | | - index.js
    | | - function.json
    | - SharedCode
    | | - myFirstHelperFunction.js
    | | - mySecondHelperFunction.js
    | - node_modules
    | - host.json
    | - package.json
    | - local.settings.json

    We'll dive into what the contents of these files look like, when we build and deploy the first function. We'll cover local.settings.json in the About Local Testing section at the end.


    My First Function App

    The documentation provides quickstart options for all supported languages. We'll walk through the JavaScript versions in this article. You have two options for development:

    I'm a huge fan of VS Code - so I'll be working through that tutorial today.

    PRE-REQUISITES

    Don't forget to validate your setup by checking the versions of installed software.

    Install VSCode Extension

    Installing the Visual Studio Code extension should automatically open this page in your IDE with similar quickstart instructions, but potentially more recent screenshots.

    Visual Studio Code Extension for VS Code

    Note that it may make sense to install the Azure tools for Visual Studio Code extensions pack if you plan on working through the many projects in Serverless September. This includes the Azure Functions extension by default.

    Create First Function App

    Walk through the Create local [project] steps of the quickstart. The process is quick and painless and scaffolds out this folder structure and files. Note the existence (and locations) of functions.json and host.json files.

    Final screenshot for VS Code workflow

    Explore the Code

    Check out the functions.json configuration file. It shows that the function is activated by an httpTrigger with an input binding (tied to req payload) and an output binding (tied to res payload). And it supports both GET and POST requests on the exposed URL.

    {
    "bindings": [
    {
    "authLevel": "anonymous",
    "type": "httpTrigger",
    "direction": "in",
    "name": "req",
    "methods": [
    "get",
    "post"
    ]
    },
    {
    "type": "http",
    "direction": "out",
    "name": "res"
    }
    ]
    }

    Check out index.js - the function implementation. We see it logs a message to the console when invoked. It then extracts a name value from the input payload (req) and crafts a different responseMessage based on the presence/absence of a valid name. It returns this response in the output payload (res).

    module.exports = async function (context, req) {
    context.log('JavaScript HTTP trigger function processed a request.');

    const name = (req.query.name || (req.body && req.body.name));
    const responseMessage = name
    ? "Hello, " + name + ". This HTTP triggered function executed successfully."
    : "This HTTP triggered function executed successfully. Pass a name in the query string or in the request body for a personalized response.";

    context.res = {
    // status: 200, /* Defaults to 200 */
    body: responseMessage
    };
    }

    Preview Function App Locally

    You can now run this function app locally using Azure Functions Core Tools. VS Code integrates seamlessly with this CLI-based tool, making it possible for you to exploit all its capabilities without leaving the IDE. In fact, the workflow will even prompt you to install those tools if they didn't already exist in your local dev environment.

    Now run the function app locally by clicking on the "Run and Debug" icon in the activity bar (highlighted, left) and pressing the "▶️" (Attach to Node Functions) to start execution. On success, your console output should show something like this.

    Final screenshot for VS Code workflow

    You can test the function locally by visiting the Function Url shown (http://localhost:7071/api/HttpTrigger1) or by opening the Workspace region of the Azure extension, and selecting the Execute Function now menu item as shown.

    Final screenshot for VS Code workflow

    In the latter case, the Enter request body popup will show a pre-populated request of {"name":"Azure"} that you can submit.

    Final screenshot for VS Code workflow

    On successful execution, your VS Code window will show a notification as follows. Take note of the console output - it shows the message encoded in index.js.

    Final screenshot for VS Code workflow

    You can also visit the deployed function URL directly in a local browser - testing the case for a request made with no name payload attached. Note how the response in the browser now shows the non-personalized version of the message!

    Final screenshot for VS Code workflow

    🎉 Congratulations

    You created and ran a function app locally!

    (Re)Deploy to Azure

    Now, just follow the creating a function app in Azure steps to deploy it to Azure, using an active subscription! The deployed app resource should now show up under the Function App Resources where you can click Execute Function Now to test the Azure-deployed version instead. You can also look up the function URL in the portal and visit that link in your local browser to trigger the function without the name context.

    🎉 Congratulations

    You have an Azure-hosted serverless function app!

    Challenge yourself and try to change the code and redeploy to Azure to return something different. You have effectively created a serverless API endpoint!


    About Core Tools

    That was a lot to cover! In the next few days we'll have more examples for Azure Functions app development - focused on different programming languages. So let's wrap today's post by reviewing two helpful resources.

    First, let's talk about Azure Functions Core Tools - the command-line tool that lets you develop, manage, and deploy, Azure Functions projects from your local development environment. It is used transparently by the VS Code extension - but you can use it directly from a terminal for a powerful command-line end-to-end developer experience! The Core Tools commands are organized into the following contexts:

    Learn how to work with Azure Functions Core Tools. Not only can it help with quick command execution, it can also be invaluable for debugging issues that may not always be visible or understandable in an IDE.

    About Local Testing

    You might have noticed that the scaffold also produced a local.settings.json file. What is that and why is it useful? By definition, the local.settings.json file "stores app settings and settings used by local development tools. Settings in the local.settings.json file are used only when you're running your project locally."

    Read the guidance on Code and test Azure Functions Locally to learn more about how to configure development environments locally, for your preferred programming language, to support testing and debugging on the local Functions runtime.

    Exercise

    We made it! Now it's your turn!! Here are a few things you can try to apply what you learned and reinforce your understanding:

    Resources

    Bookmark and visit the #30DaysOfServerless Collection. It's the one-stop collection of resources we will keep updated with links to relevant documentation and learning resources.

    - - + + \ No newline at end of file diff --git a/blog/tags/docker-compose/index.html b/blog/tags/docker-compose/index.html index a3e33fb251..0d8aea7025 100644 --- a/blog/tags/docker-compose/index.html +++ b/blog/tags/docker-compose/index.html @@ -14,13 +14,13 @@ - - + +

    One post tagged with "docker-compose"

    View All Tags

    · 8 min read
    Paul Yu

    Welcome to Day 10 of #30DaysOfServerless!

    We continue our exploraton into Azure Container Apps, with today's focus being communication between microservices, and how to configure your Azure Container Apps environment in the context of a deployment example.


    What We'll Cover

    • ACA Environments & Virtual Networking
    • Basic Microservices Communications
    • Walkthrough: ACA Deployment Example
    • Summary and Next Steps
    • Exercise: Try this yourself!
    • Resources: For self-study!


    Introduction

    In yesterday's post, we learned what the Azure Container Apps (ACA) service is and the problems it aims to solve. It is considered to be a Container-as-a-Service platform since much of the complex implementation details of running a Kubernetes cluster is managed for you.

    Some of the use cases for ACA include event-driven processing jobs and background tasks, but this article will focus on hosting microservices, and how they can communicate with each other within the ACA service. At the end of this article, you will have a solid understanding of how networking and communication is handled and will leave you with a few tutorials to try.

    Environments and virtual networking in ACA

    Before we jump into microservices communication, we should review how networking works within ACA. With ACA being a managed service, Azure will take care of most of your underlying infrastructure concerns. As you provision an ACA resource, Azure provisions an Environment to deploy Container Apps into. This environment is your isolation boundary.

    Azure Container Apps Environment

    By default, Azure creates and manages a new Virtual Network (VNET) for you and the VNET is associated with the environment. As you deploy container apps, they are deployed into the same VNET and the environment is assigned a static public IP address which allows your apps to be accessible over the internet. This VNET is not visible or manageable.

    If you need control of the networking flows within the VNET, you can pre-provision one and tell Azure to deploy an environment within it. This "bring-your-own" VNET model allows you to deploy an environment in either External or Internal modes. Deploying an environment in External mode gives you the flexibility of managing your own VNET, while still allowing your containers to be accessible from outside the environment; a static public IP address is assigned to the environment. When deploying in Internal mode, your containers are accessible within the environment and/or VNET but not accessible from the internet.

    Bringing your own VNET will require some planning and you will need dedicate an empty subnet which will be used exclusively by the ACA environment. The size of your subnet will be dependant on how many containers you plan on deploying and your scaling requirements and one requirement to know is that the subnet address range must have have a /23 CIDR prefix at minimum. You will also need to think about your deployment strategy since ACA has the concept of Revisions which will also consume IPs from your subnet.

    Some additional restrictions to consider when planning your subnet address space is listed in the Resources section below and can be addressed in future posts, so be sure to follow us on dev.to and bookmark the ServerlessSeptember site.

    Basic microservices communication in ACA

    When it comes to communications between containers, ACA addresses this concern with its Ingress capabilities. With HTTP Ingress enabled on your container app, you can expose your app on a HTTPS endpoint.

    If your environment is deployed using default networking and your containers needs to be accessible from outside the environment, you will need to set the Ingress traffic option to Accepting traffic from anywhere. This will generate a Full-Qualified Domain Name (FQDN) which you can use to access your app right away. The ingress feature also generates and assigns a Secure Socket Layer (SSL) certificate for the FQDN.

    External ingress on Container App

    If your environment is deployed using default networking and your containers only need to communicate with other containers in the environment, you'll need to set the Ingress traffic option to Limited to Container Apps Environment. You get a FQDN here as well, but in the section below we'll see how that changes.

    Internal ingress on Container App

    As mentioned in the networking section above, if you deploy your ACA environment into a VNET in internal mode, your options will be Limited to Container Apps Environment or Limited to VNet.

    Ingress on internal virtual network

    Note how the Accepting traffic from anywhere option is greyed out. If your VNET is deployed in external mode, then the option will be available.

    Let's walk though an example ACA deployment

    The diagram below illustrates a simple microservices application that I deployed to ACA. The three container apps all have ingress enabled. The greeting-service app calls two backend services; a hello-service that returns the text Hello (in random casing) and a world-service that returns the text World (in a few random languages). The greeting-service concatenates the two strings together and returns Hello World to the browser. The greeting-service is the only service accessible via external ingress while two backend services are only accessible via internal ingress.

    Greeting Service overview

    With ingress enabled, let's take a quick look at the FQDN structures. Here is the FQDN of the external greeting-service.

    https://greeting-service.victoriouswave-3749d046.eastus.azurecontainerapps.io

    We can break it down into these components:

    https://[YOUR-CONTAINER-APP-NAME].[RANDOM-NAME]-[RANDOM-CHARACTERS].[AZURE-REGION].containerapps.io

    And here is the FQDN of the internal hello-service.

    https://hello-service.internal.victoriouswave-3749d046.eastus.azurecontainerapps.io

    Can you spot the difference between FQDNs?

    That was too easy 😉... the word internal is added as a subdomain in the FQDN between your container app name and the random name for all internal ingress endpoints.

    https://[YOUR-CONTAINER-APP-NAME].internal.[RANDOM-NAME]-[RANDOM-CHARACTERS].[AZURE-REGION].containerapps.io

    Now that we know the internal service FQDNs, we use them in the greeting-service app to achieve basic service-to-service communications.

    So we can inject FQDNs of downstream APIs to upstream apps using environment variables, but the downside to this approach is that need to deploy downstream containers ahead of time and this dependency will need to be planned for during your deployment process. There are ways around this and one option is to leverage the auto-injected environment variables within your app code.

    If I use the Console blade for the hello-service container app and run the env command, you will see environment variables named CONTAINER_APP_NAME and CONTAINER_APP_ENV_DNS_SUFFIX. You can use these values to determine FQDNs within your upstream app.

    hello-service environment variables

    Back in the greeting-service container I can invoke the hello-service container's sayhello method. I know the container app name is hello-service and this service is exposed over an internal ingress, therefore, if I add the internal subdomain to the CONTAINER_APP_ENV_DNS_SUFFIX I can invoke a HTTP request to the hello-service from my greeting-service container.

    Invoke the sayHello method from the greeting-service container

    As you can see, the ingress feature enables communications to other container apps over HTTP/S and ACA will inject environment variables into our container to help determine what the ingress FQDNs would be. All we need now is a little bit of code modification in the greeting-service app and build the FQDNs of our backend APIs by retrieving these environment variables.

    Greeting service code

    ... and now we have a working microservices app on ACA! 🎉

    Hello World

    Summary and next steps

    We've covered Container Apps networking and the basics of how containers communicate with one another. However, there is a better way to address service-to-service invocation using Dapr, which is an open-source framework for building microservices. It is natively integrated into the ACA service and in a future post, you'll learn how to enable it in your Container App to address microservices concerns and more. So stay tuned!

    Exercises

    As a takeaway for today's post, I encourage you to complete this tutorial and if you'd like to deploy the sample app that was presented in this article, my teammate @StevenMurawski is hosting a docker-compose-examples repo which includes samples for deploying to ACA using Docker Compose files. To learn more about the az containerapp compose command, a link to his blog articles are listed in the Resources section below.

    If you have any questions or feedback, please let us know in the comments below or reach out on Twitter @pauldotyu

    Have fun packing and shipping containers! See you in the next post!

    Resources

    The sample app presented here was inspired by services demonstrated in the book Introducing Distributed Application Runtime (Dapr): Simplifying Microservices Applications Development Through Proven and Reusable Patterns and Practices. Go check it out to leran more about Dapr!

    - - + + \ No newline at end of file diff --git a/blog/tags/dotnet/index.html b/blog/tags/dotnet/index.html index b1dd0f8c9b..98cbb27a3f 100644 --- a/blog/tags/dotnet/index.html +++ b/blog/tags/dotnet/index.html @@ -14,13 +14,13 @@ - - + +

    2 posts tagged with "dotnet"

    View All Tags

    · 19 min read
    Alex Wolf

    Welcome to Day 24 of #30DaysOfServerless!

    We continue exploring E2E scenarios with this tutorial where you'll deploy a containerized ASP.NET Core 6.0 application to Azure Container Apps.

    The application consists of a front-end web app built using Blazor Server, as well as two Web API projects to manage data. These projects will exist as three separate containers inside of a shared container apps environment.


    What We'll Cover

    • Deploy ASP.NET Core 6.0 app to Azure Container Apps
    • Automate deployment workflows using GitHub Actions
    • Provision and deploy resources using Azure Bicep
    • Exercise: Try this yourself!
    • Resources: For self-study!


    Introduction

    Azure Container Apps enables you to run microservices and containerized applications on a serverless platform. With Container Apps, you enjoy the benefits of running containers while leaving behind the concerns of manually configuring cloud infrastructure and complex container orchestrators.

    In this tutorial, you'll deploy a containerized ASP.NET Core 6.0 application to Azure Container Apps. The application consists of a front-end web app built using Blazor Server, as well as two Web API projects to manage data. These projects will exist as three separate containers inside of a shared container apps environment.

    You will use GitHub Actions in combination with Bicep to deploy the application. These tools provide an approachable and sustainable solution for building CI/CD pipelines and working with Container Apps.

    PRE-REQUISITES

    Architecture

    In this tutorial, we'll setup a container app environment with a separate container for each project in the sample store app. The major components of the sample project include:

    • A Blazor Server front-end web app to display product information
    • A products API to list available products
    • An inventory API to determine how many products are in stock
    • GitHub Actions and Bicep templates to provision Azure resources and then build and deploy the sample app.

    You will explore these templates later in the tutorial.

    Public internet traffic should be proxied to the Blazor app. The back-end APIs should only be reachable via requests from the Blazor app inside the container apps environment. This setup can be achieved using container apps environment ingress configurations during deployment.

    An architecture diagram of the shopping app


    Project Sources

    Want to follow along? Fork the sample below. The tutorial can be completed with or without Dapr integration. Pick the path you feel comfortable in. Dapr provides various benefits that make working with Microservices easier - you can learn more in the docs. For this tutorial you will need GitHub and Azure CLI.

    PICK YOUR PATH

    To follow along with this tutorial, fork the relevant sample project below.

    You can run the app locally from Visual Studio:

    • Right click on the Blazor Store project and select Set as Startup Project.
    • Press the start button at the top of Visual Studio to run the app.
    • (Once running) start each API in the background by
    • right-clicking on the project node
    • selecting Debug --> Start without debugging.

    Once the Blazor app is running, you should see something like this:

    An architecture diagram of the shopping app


    Configuring Azure credentials

    In order to deploy the application to Azure through GitHub Actions, you first need to create a service principal. The service principal will allow the GitHub Actions process to authenticate to your Azure subscription to create resources and deploy code. You can learn more about Service Principals in the Azure CLI documentation. For this step you'll need to be logged into the Azure CLI.

    1) If you have not done so already, make sure to fork the sample project to your own GitHub account or organization.

    1) Once you have completed this step, create a service principal using the Azure CLI command below:

    ```azurecli
    $subscriptionId=$(az account show --query id --output tsv)
    az ad sp create-for-rbac --sdk-auth --name WebAndApiSample --role Contributor --scopes /subscriptions/$subscriptionId
    ```

    1) Copy the JSON output of the CLI command to your clipboard

    1) Under the settings tab of your forked GitHub repo, create a new secret named AzureSPN. The name is important to match the Bicep templates included in the project, which we'll review later. Paste the copied service principal values on your clipboard into the secret and save your changes. This new secret will be used by the GitHub Actions workflow to authenticate to Azure.

    :::image type="content" source="./img/dotnet/github-secrets.png" alt-text="A screenshot of adding GitHub secrets.":::

    Deploy using Github Actions

    You are now ready to deploy the application to Azure Container Apps using GitHub Actions. The sample application includes a GitHub Actions template that is configured to build and deploy any changes to a branch named deploy. The deploy branch does not exist in your forked repository by default, but you can easily create it through the GitHub user interface.

    1) Switch to the Actions tab along the top navigation of your GitHub repository. If you have not done so already, ensure that workflows are enabled by clicking the button in the center of the page.

    A screenshot showing how to enable GitHub actions

    1) Navigate to the main Code tab of your repository and select the main dropdown. Enter deploy into the branch input box, and then select Create branch: deploy from 'main'.

    A screenshot showing how to create the deploy branch

    1) On the new deploy branch, navigate down into the .github/workflows folder. You should see a file called deploy.yml, which contains the main GitHub Actions workflow script. Click on the file to view its content. You'll learn more about this file later in the tutorial.

    1) Click the pencil icon in the upper right to edit the document.

    1) Change the RESOURCE_GROUP_NAME: value to msdocswebappapis or another valid resource group name of your choosing.

    1) In the upper right of the screen, select Start commit and then Commit changes to commit your edit. This will persist the change to the file and trigger the GitHub Actions workflow to build and deploy the app.

    A screenshot showing how to commit changes

    1) Switch to the Actions tab along the top navigation again. You should see the workflow running to create the necessary resources and deploy the app. The workflow may take several minutes to run. When it completes successfully, all of the jobs should have a green checkmark icon next to them.

    The completed GitHub workflow.

    Explore the Azure resources

    Once the GitHub Actions workflow has completed successfully you can browse the created resources in the Azure portal.

    1) On the left navigation, select Resource Groups. Next,choose the msdocswebappapis resource group that was created by the GitHub Actions workflow.

    2) You should see seven resources available that match the screenshot and table descriptions below.

    The resources created in Azure.

    Resource nameTypeDescription
    inventoryContainer appThe containerized inventory API.
    msdocswebappapisacrContainer registryA registry that stores the built Container images for your apps.
    msdocswebappapisaiApplication insightsApplication insights provides advanced monitoring, logging and metrics for your apps.
    msdocswebappapisenvContainer apps environmentA container environment that manages networking, security and resource concerns. All of your containers live in this environment.
    msdocswebappapislogsLog Analytics workspaceA workspace environment for managing logging and analytics for the container apps environment
    productsContainer appThe containerized products API.
    storeContainer appThe Blazor front-end web app.

    3) You can view your running app in the browser by clicking on the store container app. On the overview page, click the Application Url link on the upper right of the screen.

    :::image type="content" source="./img/dotnet/application-url.png" alt-text="The link to browse the app.":::

    Understanding the GitHub Actions workflow

    The GitHub Actions workflow created and deployed resources to Azure using the deploy.yml file in the .github folder at the root of the project. The primary purpose of this file is to respond to events - such as commits to a branch - and run jobs to accomplish tasks. The deploy.yml file in the sample project has three main jobs:

    • Provision: Create the necessary resources in Azure, such as the container apps environment. This step leverages Bicep templates to create the Azure resources, which you'll explore in a moment.
    • Build: Create the container images for the three apps in the project and store them in the container registry.
    • Deploy: Deploy the container images to the different container apps created during the provisioning job.

    The deploy.yml file also accepts parameters to make the workflow more dynamic, such as setting the resource group name or the Azure region resources will be provisioned to.

    Below is a commented version of the deploy.yml file that highlights the essential steps.

    name: Build and deploy .NET application to Container Apps

    # Trigger the workflow on pushes to the deploy branch
    on:
    push:
    branches:
    - deploy

    env:
    # Set workflow variables
    RESOURCE_GROUP_NAME: msdocswebappapis

    REGION: eastus

    STORE_DOCKER: Store/Dockerfile
    STORE_IMAGE: store

    INVENTORY_DOCKER: Store.InventoryApi/Dockerfile
    INVENTORY_IMAGE: inventory

    PRODUCTS_DOCKER: Store.ProductApi/Dockerfile
    PRODUCTS_IMAGE: products

    jobs:
    # Create the required Azure resources
    provision:
    runs-on: ubuntu-latest

    steps:

    - name: Checkout to the branch
    uses: actions/checkout@v2

    - name: Azure Login
    uses: azure/login@v1
    with:
    creds: ${{ secrets.AzureSPN }}

    - name: Create resource group
    uses: azure/CLI@v1
    with:
    inlineScript: >
    echo "Creating resource group in Azure"
    echo "Executing 'az group create -l ${{ env.REGION }} -n ${{ env.RESOURCE_GROUP_NAME }}'"
    az group create -l ${{ env.REGION }} -n ${{ env.RESOURCE_GROUP_NAME }}

    # Use Bicep templates to create the resources in Azure
    - name: Creating resources
    uses: azure/CLI@v1
    with:
    inlineScript: >
    echo "Creating resources"
    az deployment group create --resource-group ${{ env.RESOURCE_GROUP_NAME }} --template-file '/github/workspace/Azure/main.bicep' --debug

    # Build the three app container images
    build:
    runs-on: ubuntu-latest
    needs: provision

    steps:

    - name: Checkout to the branch
    uses: actions/checkout@v2

    - name: Azure Login
    uses: azure/login@v1
    with:
    creds: ${{ secrets.AzureSPN }}

    - name: Set up Docker Buildx
    uses: docker/setup-buildx-action@v1

    - name: Login to ACR
    run: |
    set -euo pipefail
    access_token=$(az account get-access-token --query accessToken -o tsv)
    refresh_token=$(curl https://${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io/oauth2/exchange -v -d "grant_type=access_token&service=${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io&access_token=$access_token" | jq -r .refresh_token)
    docker login -u 00000000-0000-0000-0000-000000000000 --password-stdin ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io <<< "$refresh_token"

    - name: Build the products api image and push it to ACR
    uses: docker/build-push-action@v2
    with:
    push: true
    tags: ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io/${{ env.PRODUCTS_IMAGE }}:${{ github.sha }}
    file: ${{ env.PRODUCTS_DOCKER }}

    - name: Build the inventory api image and push it to ACR
    uses: docker/build-push-action@v2
    with:
    push: true
    tags: ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io/${{ env.INVENTORY_IMAGE }}:${{ github.sha }}
    file: ${{ env.INVENTORY_DOCKER }}

    - name: Build the frontend image and push it to ACR
    uses: docker/build-push-action@v2
    with:
    push: true
    tags: ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io/${{ env.STORE_IMAGE }}:${{ github.sha }}
    file: ${{ env.STORE_DOCKER }}

    # Deploy the three container images
    deploy:
    runs-on: ubuntu-latest
    needs: build

    steps:

    - name: Checkout to the branch
    uses: actions/checkout@v2

    - name: Azure Login
    uses: azure/login@v1
    with:
    creds: ${{ secrets.AzureSPN }}

    - name: Installing Container Apps extension
    uses: azure/CLI@v1
    with:
    inlineScript: >
    az config set extension.use_dynamic_install=yes_without_prompt

    az extension add --name containerapp --yes

    - name: Login to ACR
    run: |
    set -euo pipefail
    access_token=$(az account get-access-token --query accessToken -o tsv)
    refresh_token=$(curl https://${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io/oauth2/exchange -v -d "grant_type=access_token&service=${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io&access_token=$access_token" | jq -r .refresh_token)
    docker login -u 00000000-0000-0000-0000-000000000000 --password-stdin ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io <<< "$refresh_token"

    - name: Deploy Container Apps
    uses: azure/CLI@v1
    with:
    inlineScript: >
    az containerapp registry set -n products -g ${{ env.RESOURCE_GROUP_NAME }} --server ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io

    az containerapp update -n products -g ${{ env.RESOURCE_GROUP_NAME }} -i ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io/${{ env.PRODUCTS_IMAGE }}:${{ github.sha }}

    az containerapp registry set -n inventory -g ${{ env.RESOURCE_GROUP_NAME }} --server ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io

    az containerapp update -n inventory -g ${{ env.RESOURCE_GROUP_NAME }} -i ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io/${{ env.INVENTORY_IMAGE }}:${{ github.sha }}

    az containerapp registry set -n store -g ${{ env.RESOURCE_GROUP_NAME }} --server ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io

    az containerapp update -n store -g ${{ env.RESOURCE_GROUP_NAME }} -i ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io/${{ env.STORE_IMAGE }}:${{ github.sha }}

    - name: logout
    run: >
    az logout

    Understanding the Bicep templates

    During the provisioning stage of the GitHub Actions workflow, the main.bicep file is processed. Bicep files provide a declarative way of generating resources in Azure and are ideal for managing infrastructure as code. You can learn more about Bicep in the related documentation. The main.bicep file in the sample project creates the following resources:

    • The container registry to store images of the containerized apps.
    • The container apps environment, which handles networking and resource management for the container apps.
    • Three container apps - one for the Blazor front-end and two for the back-end product and inventory APIs.
    • Configuration values to connect these services together

    main.bicep without Dapr

    param location string = resourceGroup().location

    # create the azure container registry
    resource acr 'Microsoft.ContainerRegistry/registries@2021-09-01' = {
    name: toLower('${resourceGroup().name}acr')
    location: location
    sku: {
    name: 'Basic'
    }
    properties: {
    adminUserEnabled: true
    }
    }

    # create the aca environment
    module env 'environment.bicep' = {
    name: 'containerAppEnvironment'
    params: {
    location: location
    }
    }

    # create the various configuration pairs
    var shared_config = [
    {
    name: 'ASPNETCORE_ENVIRONMENT'
    value: 'Development'
    }
    {
    name: 'APPINSIGHTS_INSTRUMENTATIONKEY'
    value: env.outputs.appInsightsInstrumentationKey
    }
    {
    name: 'APPLICATIONINSIGHTS_CONNECTION_STRING'
    value: env.outputs.appInsightsConnectionString
    }
    ]

    # create the products api container app
    module products 'container_app.bicep' = {
    name: 'products'
    params: {
    name: 'products'
    location: location
    registryPassword: acr.listCredentials().passwords[0].value
    registryUsername: acr.listCredentials().username
    containerAppEnvironmentId: env.outputs.id
    registry: acr.name
    envVars: shared_config
    externalIngress: false
    }
    }

    # create the inventory api container app
    module inventory 'container_app.bicep' = {
    name: 'inventory'
    params: {
    name: 'inventory'
    location: location
    registryPassword: acr.listCredentials().passwords[0].value
    registryUsername: acr.listCredentials().username
    containerAppEnvironmentId: env.outputs.id
    registry: acr.name
    envVars: shared_config
    externalIngress: false
    }
    }

    # create the store api container app
    var frontend_config = [
    {
    name: 'ProductsApi'
    value: 'http://${products.outputs.fqdn}'
    }
    {
    name: 'InventoryApi'
    value: 'http://${inventory.outputs.fqdn}'
    }
    ]

    module store 'container_app.bicep' = {
    name: 'store'
    params: {
    name: 'store'
    location: location
    registryPassword: acr.listCredentials().passwords[0].value
    registryUsername: acr.listCredentials().username
    containerAppEnvironmentId: env.outputs.id
    registry: acr.name
    envVars: union(shared_config, frontend_config)
    externalIngress: true
    }
    }

    main.bicep with Dapr


    param location string = resourceGroup().location

    # create the azure container registry
    resource acr 'Microsoft.ContainerRegistry/registries@2021-09-01' = {
    name: toLower('${resourceGroup().name}acr')
    location: location
    sku: {
    name: 'Basic'
    }
    properties: {
    adminUserEnabled: true
    }
    }

    # create the aca environment
    module env 'environment.bicep' = {
    name: 'containerAppEnvironment'
    params: {
    location: location
    }
    }

    # create the various config pairs
    var shared_config = [
    {
    name: 'ASPNETCORE_ENVIRONMENT'
    value: 'Development'
    }
    {
    name: 'APPINSIGHTS_INSTRUMENTATIONKEY'
    value: env.outputs.appInsightsInstrumentationKey
    }
    {
    name: 'APPLICATIONINSIGHTS_CONNECTION_STRING'
    value: env.outputs.appInsightsConnectionString
    }
    ]

    # create the products api container app
    module products 'container_app.bicep' = {
    name: 'products'
    params: {
    name: 'products'
    location: location
    registryPassword: acr.listCredentials().passwords[0].value
    registryUsername: acr.listCredentials().username
    containerAppEnvironmentId: env.outputs.id
    registry: acr.name
    envVars: shared_config
    externalIngress: false
    }
    }

    # create the inventory api container app
    module inventory 'container_app.bicep' = {
    name: 'inventory'
    params: {
    name: 'inventory'
    location: location
    registryPassword: acr.listCredentials().passwords[0].value
    registryUsername: acr.listCredentials().username
    containerAppEnvironmentId: env.outputs.id
    registry: acr.name
    envVars: shared_config
    externalIngress: false
    }
    }

    # create the store api container app
    module store 'container_app.bicep' = {
    name: 'store'
    params: {
    name: 'store'
    location: location
    registryPassword: acr.listCredentials().passwords[0].value
    registryUsername: acr.listCredentials().username
    containerAppEnvironmentId: env.outputs.id
    registry: acr.name
    envVars: shared_config
    externalIngress: true
    }
    }


    Bicep Modules

    The main.bicep file references modules to create resources, such as module products. Modules are a feature of Bicep templates that enable you to abstract resource declarations into their own files or sub-templates. As the main.bicep file is processed, the defined modules are also evaluated. Modules allow you to create resources in a more organized and reusable way. They can also define input and output parameters that are passed to and from the parent template, such as the name of a resource.

    For example, the environment.bicep module extracts the details of creating a container apps environment into a reusable template. The module defines necessary resource dependencies such as Log Analytics Workspaces and an Application Insights instance.

    environment.bicep without Dapr

    param baseName string = resourceGroup().name
    param location string = resourceGroup().location

    resource logs 'Microsoft.OperationalInsights/workspaces@2021-06-01' = {
    name: '${baseName}logs'
    location: location
    properties: any({
    retentionInDays: 30
    features: {
    searchVersion: 1
    }
    sku: {
    name: 'PerGB2018'
    }
    })
    }

    resource appInsights 'Microsoft.Insights/components@2020-02-02' = {
    name: '${baseName}ai'
    location: location
    kind: 'web'
    properties: {
    Application_Type: 'web'
    WorkspaceResourceId: logs.id
    }
    }

    resource env 'Microsoft.App/managedEnvironments@2022-01-01-preview' = {
    name: '${baseName}env'
    location: location
    properties: {
    appLogsConfiguration: {
    destination: 'log-analytics'
    logAnalyticsConfiguration: {
    customerId: logs.properties.customerId
    sharedKey: logs.listKeys().primarySharedKey
    }
    }
    }
    }

    output id string = env.id
    output appInsightsInstrumentationKey string = appInsights.properties.InstrumentationKey
    output appInsightsConnectionString string = appInsights.properties.ConnectionString

    environment.bicep with Dapr


    param baseName string = resourceGroup().name
    param location string = resourceGroup().location

    resource logs 'Microsoft.OperationalInsights/workspaces@2021-06-01' = {
    name: '${baseName}logs'
    location: location
    properties: any({
    retentionInDays: 30
    features: {
    searchVersion: 1
    }
    sku: {
    name: 'PerGB2018'
    }
    })
    }

    resource appInsights 'Microsoft.Insights/components@2020-02-02' = {
    name: '${baseName}ai'
    location: location
    kind: 'web'
    properties: {
    Application_Type: 'web'
    WorkspaceResourceId: logs.id
    }
    }

    resource env 'Microsoft.App/managedEnvironments@2022-01-01-preview' = {
    name: '${baseName}env'
    location: location
    properties: {
    appLogsConfiguration: {
    destination: 'log-analytics'
    logAnalyticsConfiguration: {
    customerId: logs.properties.customerId
    sharedKey: logs.listKeys().primarySharedKey
    }
    }
    }
    }

    output id string = env.id
    output appInsightsInstrumentationKey string = appInsights.properties.InstrumentationKey
    output appInsightsConnectionString string = appInsights.properties.ConnectionString


    The container_apps.bicep template defines numerous parameters to provide a reusable template for creating container apps. This allows the module to be used in other CI/CD pipelines as well.

    container_app.bicep without Dapr

    param name string
    param location string = resourceGroup().location
    param containerAppEnvironmentId string
    param repositoryImage string = 'mcr.microsoft.com/azuredocs/containerapps-helloworld:latest'
    param envVars array = []
    param registry string
    param minReplicas int = 1
    param maxReplicas int = 1
    param port int = 80
    param externalIngress bool = false
    param allowInsecure bool = true
    param transport string = 'http'
    param registryUsername string
    @secure()
    param registryPassword string

    resource containerApp 'Microsoft.App/containerApps@2022-01-01-preview' ={
    name: name
    location: location
    properties:{
    managedEnvironmentId: containerAppEnvironmentId
    configuration: {
    activeRevisionsMode: 'single'
    secrets: [
    {
    name: 'container-registry-password'
    value: registryPassword
    }
    ]
    registries: [
    {
    server: registry
    username: registryUsername
    passwordSecretRef: 'container-registry-password'
    }
    ]
    ingress: {
    external: externalIngress
    targetPort: port
    transport: transport
    allowInsecure: allowInsecure
    }
    }
    template: {
    containers: [
    {
    image: repositoryImage
    name: name
    env: envVars
    }
    ]
    scale: {
    minReplicas: minReplicas
    maxReplicas: maxReplicas
    }
    }
    }
    }

    output fqdn string = containerApp.properties.configuration.ingress.fqdn

    container_app.bicep with Dapr


    param name string
    param location string = resourceGroup().location
    param containerAppEnvironmentId string
    param repositoryImage string = 'mcr.microsoft.com/azuredocs/containerapps-helloworld:latest'
    param envVars array = []
    param registry string
    param minReplicas int = 1
    param maxReplicas int = 1
    param port int = 80
    param externalIngress bool = false
    param allowInsecure bool = true
    param transport string = 'http'
    param appProtocol string = 'http'
    param registryUsername string
    @secure()
    param registryPassword string

    resource containerApp 'Microsoft.App/containerApps@2022-01-01-preview' ={
    name: name
    location: location
    properties:{
    managedEnvironmentId: containerAppEnvironmentId
    configuration: {
    dapr: {
    enabled: true
    appId: name
    appPort: port
    appProtocol: appProtocol
    }
    activeRevisionsMode: 'single'
    secrets: [
    {
    name: 'container-registry-password'
    value: registryPassword
    }
    ]
    registries: [
    {
    server: registry
    username: registryUsername
    passwordSecretRef: 'container-registry-password'
    }
    ]
    ingress: {
    external: externalIngress
    targetPort: port
    transport: transport
    allowInsecure: allowInsecure
    }
    }
    template: {
    containers: [
    {
    image: repositoryImage
    name: name
    env: envVars
    }
    ]
    scale: {
    minReplicas: minReplicas
    maxReplicas: maxReplicas
    }
    }
    }
    }

    output fqdn string = containerApp.properties.configuration.ingress.fqdn


    Understanding configuration differences with Dapr

    The code for this specific sample application is largely the same whether or not Dapr is integrated. However, even with this simple app, there are a few benefits and configuration differences when using Dapr that are worth exploring.

    In this scenario most of the changes are related to communication between the container apps. However, you can explore the full range of Dapr benefits by reading the Dapr integration with Azure Container Apps article in the conceptual documentation.

    Without Dapr

    Without Dapr the main.bicep template handles wiring up the front-end store app to communicate with the back-end apis by manually managing environment variables. The bicep template retrieves the fully qualified domains (fqdn) of the API apps as output parameters when they are created. Those configurations are then set as environment variables on the store container app.


    # Retrieve environment variables from API container creation
    var frontend_config = [
    {
    name: 'ProductsApi'
    value: 'http://${products.outputs.fqdn}'
    }
    {
    name: 'InventoryApi'
    value: 'http://${inventory.outputs.fqdn}'
    }
    ]

    # create the store api container app, passing in config
    module store 'container_app.bicep' = {
    name: 'store'
    params: {
    name: 'store'
    location: location
    registryPassword: acr.listCredentials().passwords[0].value
    registryUsername: acr.listCredentials().username
    containerAppEnvironmentId: env.outputs.id
    registry: acr.name
    envVars: union(shared_config, frontend_config)
    externalIngress: true
    }
    }

    The environment variables are then retrieved inside of the program class and used to configure the base URLs of the corresponding HTTP clients.


    builder.Services.AddHttpClient("Products", (httpClient) => httpClient.BaseAddress = new Uri(builder.Configuration.GetValue<string>("ProductsApi")));
    builder.Services.AddHttpClient("Inventory", (httpClient) => httpClient.BaseAddress = new Uri(builder.Configuration.GetValue<string>("InventoryApi")));

    With Dapr

    Dapr can be enabled on a container app when it is created, as seen below. This configuration adds a Dapr sidecar to the app to streamline discovery and communication features between the different container apps in your environment.


    # Create the container app with Dapr enabled
    resource containerApp 'Microsoft.App/containerApps@2022-01-01-preview' ={
    name: name
    location: location
    properties:{
    managedEnvironmentId: containerAppEnvironmentId
    configuration: {
    dapr: {
    enabled: true
    appId: name
    appPort: port
    appProtocol: appProtocol
    }
    activeRevisionsMode: 'single'
    secrets: [
    {
    name: 'container-registry-password'
    value: registryPassword
    }
    ]

    # Rest of template omitted for brevity...
    }
    }

    Some of these Dapr features can be surfaced through the program file. You can configure your HttpClient to leverage Dapr configurations when communicating with other apps in your environment.


    // reconfigure code to make requests to Dapr sidecar
    var baseURL = (Environment.GetEnvironmentVariable("BASE_URL") ?? "http://localhost") + ":" + (Environment.GetEnvironmentVariable("DAPR_HTTP_PORT") ?? "3500");
    builder.Services.AddHttpClient("Products", (httpClient) =>
    {
    httpClient.BaseAddress = new Uri(baseURL);
    httpClient.DefaultRequestHeaders.Add("dapr-app-id", "Products");
    });

    builder.Services.AddHttpClient("Inventory", (httpClient) =>
    {
    httpClient.BaseAddress = new Uri(baseURL);
    httpClient.DefaultRequestHeaders.Add("dapr-app-id", "Inventory");
    });


    Clean up resources

    If you're not going to continue to use this application, you can delete the Azure Container Apps and all the associated services by removing the resource group.

    Follow these steps in the Azure portal to remove the resources you created:

    1. In the Azure portal, navigate to the msdocswebappsapi resource group using the left navigation or search bar.
    2. Select the Delete resource group button at the top of the resource group Overview.
    3. Enter the resource group name msdocswebappsapi in the Are you sure you want to delete "msdocswebappsapi" confirmation dialog.
    4. Select Delete.
      The process to delete the resource group may take a few minutes to complete.
    - - + + \ No newline at end of file diff --git a/blog/tags/dotnet/page/2/index.html b/blog/tags/dotnet/page/2/index.html index 5b96ba8e2f..33dcb00f72 100644 --- a/blog/tags/dotnet/page/2/index.html +++ b/blog/tags/dotnet/page/2/index.html @@ -14,13 +14,13 @@ - - + +

    2 posts tagged with "dotnet"

    View All Tags

    · 10 min read
    Mike James
    Matt Soucoup

    Welcome to Day 6 of #30DaysOfServerless!

    The theme for this week is Azure Functions. Today we're going to talk about why Azure Functions are a great fit for .NET developers.


    What We'll Cover

    • What is serverless computing?
    • How does Azure Functions fit in?
    • Let's build a simple Azure Function in .NET
    • Developer Guide, Samples & Scenarios
    • Exercise: Explore the Create Serverless Applications path.
    • Resources: For self-study!

    A banner image that has the title of this article with the author&#39;s photo and a drawing that summarizes the demo application.


    The leaves are changing colors and there's a chill in the air, or for those lucky folks in the Southern Hemisphere, the leaves are budding and a warmth is in the air. Either way, that can only mean one thing - it's Serverless September!🍂 So today, we're going to take a look at Azure Functions - what they are, and why they're a great fit for .NET developers.

    What is serverless computing?

    For developers, serverless computing means you write highly compact individual functions that do one thing - and run in the cloud. These functions are triggered by some external event. That event could be a record being inserted into a database, a file uploaded into BLOB storage, a timer interval elapsed, or even a simple HTTP request.

    But... servers are still definitely involved! What has changed from other types of cloud computing is that the idea and ownership of the server has been abstracted away.

    A lot of the time you'll hear folks refer to this as Functions as a Service or FaaS. The defining characteristic is all you need to do is put together your application logic. Your code is going to be invoked in response to events - and the cloud provider takes care of everything else. You literally get to focus on only the business logic you need to run in response to something of interest - no worries about hosting.

    You do not need to worry about wiring up the plumbing between the service that originates the event and the serverless runtime environment. The cloud provider will handle the mechanism to call your function in response to whatever event you chose to have the function react to. And it passes along any data that is relevant to the event to your code.

    And here's a really neat thing. You only pay for the time the serverless function is running. So, if you have a function that is triggered by an HTTP request, and you rarely get requests to your function, you would rarely pay.

    How does Azure Functions fit in?

    Microsoft's Azure Functions is a modern serverless architecture, offering event-driven cloud computing that is easy for developers to use. It provides a way to run small pieces of code or Functions in the cloud without developers having to worry themselves about the infrastructure or platform the Function is running on.

    That means we're only concerned about writing the logic of the Function. And we can write that logic in our choice of languages... like C#. We are also able to add packages from NuGet to Azure Functions—this way, we don't have to reinvent the wheel and can use well-tested libraries.

    And the Azure Functions runtime takes care of a ton of neat stuff for us, like passing in information about the event that caused it to kick off - in a strongly typed variable. It also "binds" to other services, like Azure Storage, we can easily access those services from our code without having to worry about new'ing them up.

    Let's build an Azure Function!

    Scaffold the Function

    Don't worry about having an Azure subscription or even being connected to the internet—we can develop and debug Azure Functions locally using either Visual Studio or Visual Studio Code!

    For this example, I'm going to use Visual Studio Code to build up a Function that responds to an HTTP trigger and then writes a message to an Azure Storage Queue.

    Diagram of the how the Azure Function will use the HTTP trigger and the Azure Storage Queue Binding

    The incoming HTTP call is the trigger and the message queue the Function writes to is an output binding. Let's have at it!

    info

    You do need to have some tools downloaded and installed to get started. First and foremost, you'll need Visual Studio Code. Then you'll need the Azure Functions extension for VS Code to do the development with. Finally, you'll need the Azurite Emulator installed as well—this will allow us to write to a message queue locally.

    Oh! And of course, .NET 6!

    Now with all of the tooling out of the way, let's write a Function!

    1. Fire up Visual Studio Code. Then, from the command palette, type: Azure Functions: Create New Project

      Screenshot of create a new function dialog in VS Code

    2. Follow the steps as to which directory you want to create the project in and which .NET runtime and language you want to use.

      Screenshot of VS Code prompting which directory and language to use

    3. Pick .NET 6 and C#.

      It will then prompt you to pick the folder in which your Function app resides and then select a template.

      Screenshot of VS Code prompting you to pick the Function trigger template

      Pick the HTTP trigger template. When prompted for a name, call it: PostToAQueue.

    Execute the Function Locally

    1. After giving it a namespace, it prompts for an authorization level—pick Anonymous. Now we have a Function! Let's go ahead and hit F5 and see it run!
    info

    After the templates have finished installing, you may get a prompt to download additional components—these are NuGet packages. Go ahead and do that.

    When it runs, you'll see the Azure Functions logo appear in the Terminal window with the URL the Function is located at. Copy that link.

    Screenshot of the Azure Functions local runtime starting up

    1. Type the link into a browser, adding a name parameter as shown in this example: http://localhost:7071/api/PostToAQueue?name=Matt. The Function will respond with a message. You can even set breakpoints in Visual Studio Code and step through the code!

    Write To Azure Storage Queue

    Next, we'll get this HTTP trigger Function to write to a local Azure Storage Queue. First we need to add the Storage NuGet package to our project. In the terminal, type:

    dotnet add package Microsoft.Azure.WebJobs.Extensions.Storage

    Then set a configuration setting to tell the Function runtime where to find the Storage. Open up local.settings.json and set "AzureWebJobsStorage" to "UseDevelopmentStorage=true". The full file will look like:

    {
    "IsEncrypted": false,
    "Values": {
    "AzureWebJobsStorage": "UseDevelopmentStorage=true",
    "AzureWebJobsDashboard": ""
    }
    }

    Then create a new class within your project. This class will hold nothing but properties. Call it whatever you want and add whatever properties you want to it. I called mine TheMessage and added an Id and Name properties to it.

    public class TheMessage
    {
    public string Id { get; set; }
    public string Name { get; set; }
    }

    Finally, change your PostToAQueue Function, so it looks like the following:


    public static class PostToAQueue
    {
    [FunctionName("PostToAQueue")]
    public static async Task<IActionResult> Run(
    [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req,
    [Queue("demoqueue", Connection = "AzureWebJobsStorage")] IAsyncCollector<TheMessage> messages,
    ILogger log)
    {
    string name = req.Query["name"];

    await messages.AddAsync(new TheMessage { Id = System.Guid.NewGuid().ToString(), Name = name });

    return new OkResult();
    }
    }

    Note the addition of the messages variable. This is telling the Function to use the storage connection we specified before via the Connection property. And it is also specifying which queue to use in that storage account, in this case demoqueue.

    All the code is doing is pulling out the name from the query string, new'ing up a new TheMessage class and adding that to the IAsyncCollector variable.

    That will add the new message to the queue!

    Make sure Azurite is started within VS Code (both the queue and blob emulators). Run the app and send the same GET request as before: http://localhost:7071/api/PostToAQueue?name=Matt.

    If you have the Azure Storage Explorer installed, you can browse your local Queue and see the new message in there!

    Screenshot of Azure Storage Explorer with the new message in the queue

    Summing Up

    We had a quick look at what Microsoft's serverless offering, Azure Functions, is comprised of. It's a full-featured FaaS offering that enables you to write functions in your language of choice, including reusing packages such as those from NuGet.

    A highlight of Azure Functions is the way they are triggered and bound. The triggers define how a Function starts, and bindings are akin to input and output parameters on it that correspond to external services. The best part is that the Azure Function runtime takes care of maintaining the connection to the external services so you don't have to worry about new'ing up or disposing of the connections yourself.

    We then wrote a quick Function that gets triggered off an HTTP request and then writes a query string parameters from that request into a local Azure Storage Queue.

    What's Next

    So, where can you go from here?

    Think about how you can build real-world scenarios by integrating other Azure services. For example, you could use serverless integrations to build a workflow where the input payload received using an HTTP Trigger, is now stored in Blob Storage (output binding), which in turn triggers another service (e.g., Cognitive Services) that processes the blob and returns an enhanced result.

    Keep an eye out for an update to this post where we walk through a scenario like this with code. Check out the resources below to help you get started on your own.

    Exercise

    This brings us close to the end of Week 1 with Azure Functions. We've learned core concepts, built and deployed our first Functions app, and explored quickstarts and scenarios for different programming languages. So, what can you do to explore this topic on your own?

    • Explore the Create Serverless Applications learning path which has several modules that explore Azure Functions integrations with various services.
    • Take up the Cloud Skills Challenge and complete those modules in a fun setting where you compete with peers for a spot on the leaderboard!

    Then come back tomorrow as we wrap up the week with a discussion on end-to-end scenarios, a recap of what we covered this week, and a look at what's ahead next week.

    Resources

    Start here for developer guidance in getting started with Azure Functions as a .NET/C# developer:

    Then learn about supported Triggers and Bindings for C#, with code snippets to show how they are used.

    Finally, explore Azure Functions samples for C# and learn to implement serverless solutions. Examples include:

    - - + + \ No newline at end of file diff --git a/blog/tags/event-hubs/index.html b/blog/tags/event-hubs/index.html index f01325bb2a..2e1e38fbaf 100644 --- a/blog/tags/event-hubs/index.html +++ b/blog/tags/event-hubs/index.html @@ -14,15 +14,15 @@ - - + +

    One post tagged with "event-hubs"

    View All Tags

    · 10 min read
    Ayca Bas

    Welcome to Day 20 of #30DaysOfServerless!

    Every day millions of people spend their precious time in productivity tools. What if you use data and intelligence behind the Microsoft applications (Microsoft Teams, Outlook, and many other Office apps) to build seamless automations and custom apps to boost productivity?

    In this post, we'll learn how to build a seamless onboarding experience for new employees joining a company with the power of Microsoft Graph, integrated with Event Hubs and Logic Apps!


    What We'll Cover

    • ✨ The power of Microsoft Graph
    • 🖇️ How do Microsoft Graph and Event Hubs work together?
    • 🛠 Let's Build an Onboarding Workflow!
      • 1️⃣ Setup Azure Event Hubs + Key Vault
      • 2️⃣ Subscribe to users, receive change notifications from Logic Apps
      • 3️⃣ Create Onboarding workflow in the Logic Apps
    • 🚀 Debug: Your onboarding experience
    • ✋ Exercise: Try this tutorial out yourself!
    • 📚 Resources: For Self-Study


    ✨ The Power of Microsoft Graph

    Microsoft Graph is the gateway to data and intelligence in Microsoft 365 platform. Microsoft Graph exploses Rest APIs and client libraries to access data across Microsoft 365 core services such as Calendar, Teams, To Do, Outlook, People, Planner, OneDrive, OneNote and more.

    Overview of Microsoft Graph

    You can build custom experiences by using Microsoft Graph such as automating the onboarding process for new employees. When new employees are created in the Azure Active Directory, they will be automatically added in the Onboarding team on Microsoft Teams.

    Solution architecture


    🖇️ Microsoft Graph with Event Hubs

    Microsoft Graph uses a webhook mechanism to track changes in resources and deliver change notifications to the clients. For example, with Microsoft Graph Change Notifications, you can receive change notifications when:

    • a new task is added in the to-do list
    • a user changes the presence status from busy to available
    • an event is deleted/cancelled from the calendar

    If you'd like to track a large set of resources at a high frequency, use Azure Events Hubs instead of traditional webhooks to receive change notifications. Azure Event Hubs is a popular real-time events ingestion and distribution service built for scale.

    EVENT GRID - PARTNER EVENTS

    Microsoft Graph Change Notifications can be also received by using Azure Event Grid -- currently available for Microsoft Partners! Read the Partner Events Overview documentation for details.

    Setup Azure Event Hubs + Key Vault.

    To get Microsoft Graph Change Notifications delivered to Azure Event Hubs, we'll have to setup Azure Event Hubs and Azure Key Vault. We'll use Azure Key Vault to access to Event Hubs connection string.

    1️⃣ Create Azure Event Hubs

    1. Go to Azure Portal and select Create a resource, type Event Hubs and select click Create.
    2. Fill in the Event Hubs namespace creation details, and then click Create.
    3. Go to the newly created Event Hubs namespace page, select Event Hubs tab from the left pane and + Event Hub:
      • Name your Event Hub as Event Hub
      • Click Create.
    4. Click the name of the Event Hub, and then select Shared access policies and + Add to add a new policy:
      • Give a name to the policy
      • Check Send and Listen
      • Click Create.
    5. After the policy has been created, click the name of the policy to open the details panel, and then copy the Connection string-primary key value. Write it down; you'll need it for the next step.
    6. Go to Consumer groups tab in the left pane and select + Consumer group, give a name for your consumer group as onboarding and select Create.

    2️⃣ Create Azure Key Vault

    1. Go to Azure Portal and select Create a resource, type Key Vault and select Create.
    2. Fill in the Key Vault creation details, and then click Review + Create.
    3. Go to newly created Key Vault and select Secrets tab from the left pane and click + Generate/Import:
      • Give a name to the secret
      • For the value, paste in the connection string you generated at the Event Hubs step
      • Click Create
      • Copy the name of the secret.
    4. Select Access Policies from the left pane and + Add Access Policy:
      • For Secret permissions, select Get
      • For Principal, select Microsoft Graph Change Tracking
      • Click Add.
    5. Select Overview tab from the left pane and copy the Vault URI.

    Subscribe for Logic Apps change notifications

    To start receiving Microsoft Graph Change Notifications, we'll need to create subscription to the resource that we'd like to track - here, 'users'. We'll use Azure Logic Apps to create subscription.

    To create subscription for Microsoft Graph Change Notifications, we'll need to make a http post request to https://graph.microsoft.com/v1.0/subscriptions. Microsoft Graph requires Azure Active Directory authentication make API calls. First, we'll need to register an app to Azure Active Directory, and then we will make the Microsoft Graph Subscription API call with Azure Logic Apps.

    1️⃣ Create an app in Azure Active Directory

    1. In the Azure Portal, go to Azure Active Directory and select App registrations from the left pane and select + New registration. Fill in the details for the new App registration form as below:
      • Name: Graph Subscription Flow Auth
      • Supported account types: Accounts in any organizational directory (Any Azure AD directory - Multitenant) and personal Microsoft accounts (e.g. Skype, Xbox)
      • Select Register.
    2. Go to newly registered app in Azure Active Directory, select API permissions:
      • Select + Add a permission and Microsoft Graph
      • Select Application permissions and add User.Read.All and Directory.Read.All.
      • Select Grant admin consent for the organization
    3. Select Certificates & secrets tab from the left pane, select + New client secret:
      • Choose desired expiry duration
      • Select Add
      • Copy the value of the secret.
    4. Go to Overview from the left pane, copy Application (client) ID and Directory (tenant) ID.

    2️⃣ Create subscription with Azure Logic Apps

    1. Go to Azure Portal and select Create a resource, type Logic apps and select click Create.

    2. Fill in the Logic Apps creation details, and then click Create.

    3. Go to the newly created Logic Apps page, select Workflows tab from the left pane and select + Add:

      • Give a name to the new workflow as graph-subscription-flow
      • Select Stateful as a state type
      • Click Create.
    4. Go to graph-subscription-flow, and then select Designer tab.

    5. In the Choose an operation section, search for Schedule and select Recurrence as a trigger. Fill in the parameters as below:

      • Interval: 61
      • Frequency: Minute
      • Time zone: Select your own time zone
      • Start time: Set a start time
    6. Select + button in the flow and select add an action. Search for HTTP and select HTTP as an action. Fill in the parameters as below:

      • Method: POST
      • URI: https://graph.microsoft.com/v1.0/subscriptions
      • Headers:
        • Key: Content-type
        • Value: application/json
      • Body:
      {
      "changeType": "created, updated",
      "clientState": "secretClientValue",
      "expirationDateTime": "@{addHours(utcNow(), 1)}",
      "notificationUrl": "EventHub:https://<YOUR-VAULT-URI>/secrets/<YOUR-KEY-VAULT-SECRET-NAME>?tenantId=72f988bf-86f1-41af-91ab-2d7cd011db47",
      "resource": "users"
      }

      In notificationUrl, make sure to replace <YOUR-VAULT-URI> with the vault uri and <YOUR-KEY-VAULT-SECRET-NAME> with the secret name that you copied from the Key Vault.

      In resource, define the resource type you'd like to track changes. For our example, we will track changes for users resource.

      • Authentication:
        • Authentication type: Active Directory OAuth
        • Authority: https://login.microsoft.com
        • Tenant: Directory (tenant) ID copied from AAD app
        • Audience: https://graph.microsoft.com
        • Client ID: Application (client) ID copied from AAD app
        • Credential Type: Secret
        • Secret: value of the secret copied from AAD app
    7. Select Save and run your workflow from the Overview tab.

      Check your subscription in Graph Explorer: If you'd like to make sure that your subscription is created successfully by Logic Apps, you can go to Graph Explorer, login with your Microsoft 365 account and make GET request to https://graph.microsoft.com/v1.0/subscriptions. Your subscription should appear in the response after it's created successfully.

    Subscription workflow success

    After subscription is created successfully by Logic Apps, Azure Event Hubs will receive notifications whenever there is a new user created in Azure Active Directory.


    Create Onboarding workflow in Logic Apps

    We'll create a second workflow in the Logic Apps to receive change notifications from Event Hubs when there is a new user created in the Azure Active Directory and add new user in Onboarding team on Microsoft Teams.

    1. Go to the Logic Apps you created in the previous steps, select Workflows tab and create a new workflow by selecting + Add:
      • Give a name to the new workflow as teams-onboarding-flow
      • Select Stateful as a state type
      • Click Create.
    2. Go to teams-onboarding-flow, and then select Designer tab.
    3. In the Choose an operation section, search for Event Hub, select When events are available in Event Hub as a trigger. Setup Event Hub connection as below:
      • Create Connection:
        • Connection name: Connection
        • Authentication Type: Connection String
        • Connection String: Go to Event Hubs > Shared Access Policies > RootManageSharedAccessKey and copy Connection string–primary key
        • Select Create.
      • Parameters:
        • Event Hub Name: Event Hub
        • Consumer Group Name: onboarding
    4. Select + in the flow and add an action, search for Control and add For each as an action. Fill in For each action as below:
      • Select output from previous steps: Events
    5. Inside For each, select + in the flow and add an action, search for Data operations and select Parse JSON. Fill in Parse JSON action as below:
      • Content: Events Content
      • Schema: Copy the json content from schema-parse.json and paste as a schema
    6. Select + in the flow and add an action, search for Control and add For each as an action. Fill in For each action as below:
      • Select output from previous steps: value
      1. Inside For each, select + in the flow and add an action, search for Microsoft Teams and select Add a member to a team. Login with your Microsoft 365 account to create a connection and fill in Add a member to a team action as below:
      • Team: Create an Onboarding team on Microsoft Teams and select
      • A user AAD ID for the user to add to a team: id
    7. Select Save.

    🚀 Debug your onboarding experience

    To debug our onboarding experience, we'll need to create a new user in Azure Active Directory and see if it's added in Microsoft Teams Onboarding team automatically.

    1. Go to Azure Portal and select Azure Active Directory from the left pane and go to Users. Select + New user and Create new user. Fill in the details as below:

      • User name: JaneDoe
      • Name: Jane Doe

      new user in Azure Active Directory

    2. When you added Jane Doe as a new user, it should trigger the teams-onboarding-flow to run. teams onboarding flow success

    3. Once the teams-onboarding-flow runs successfully, you should be able to see Jane Doe as a member of the Onboarding team on Microsoft Teams! 🥳 new member in Onboarding team on Microsoft Teams

    Congratulations! 🎉

    You just built an onboarding experience using Azure Logic Apps, Azure Event Hubs and Azure Key Vault.


    📚 Resources

    - - + + \ No newline at end of file diff --git a/blog/tags/hacktoberfest/index.html b/blog/tags/hacktoberfest/index.html index 539dcbdba9..6fe99609e0 100644 --- a/blog/tags/hacktoberfest/index.html +++ b/blog/tags/hacktoberfest/index.html @@ -14,13 +14,13 @@ - - + +

    One post tagged with "hacktoberfest"

    View All Tags

    · 5 min read
    Savannah Ostrowski

    Welcome to Beyond #30DaysOfServerless! in October!

    Yes, it's October!! And since we ended #ServerlessSeptember with a focus on End-to-End Development for Serverless on Azure, we thought it would be good to share updates in October that can help you skill up even further.

    Today, we're following up on the Code to Cloud with azd blog post (Day #29) where we introduced the Azure Developer CLI (azd), an open-source tool for streamlining your end-to-end developer experience going from local development environment to Azure cloud. In today's post, we celebrate the October 2022 release of the tool, with three cool new features.

    And if it's October, it must be #Hacktoberfest!! Read on to learn about how you can take advantage of one of the new features, to contribute to the azd open-source community and ecosystem!

    Ready? Let's go!


    What We'll Cover

    • Azure Friday: Introducing the Azure Developer CLI (Video)
    • October 2022 Release: What's New in the Azure Developer CLI?
      • Azure Pipelines for CI/CD: Learn more
      • Improved Infrastructure as Code structure via Bicep modules: Learn more
      • A new azd template gallery: The new azd-templates gallery for community use! Learn more
    • Awesome-Azd: The new azd-templates gallery for Community use
      • Features: discover, create, contribute, request - templates
      • Hacktoberfest: opportunities to contribute in October - and beyond!


    Azure Friday

    This post is a follow-up to our #ServerlessSeptember post on Code to Cloud with Azure Developer CLI where we introduced azd, a new open-source tool that makes it quick and simple for you to move your application from a local development environment to Azure, streamlining your end-to-end developer workflow in the process.

    Prefer to watch a video overview? I have you covered! Check out my recent conversation with Scott Hanselman on Azure Friday where we:

    • talked about the code-to-cloud developer journey
    • walkthrough the ins and outs of an azd template
    • explored Azure Developer CLI commands in the terminal and VS Code, and
    • (probably most importantly) got a web app up and running on Azure with a database, Key Vault and monitoring all in a couple of minutes

    October Release

    We're pleased to announce the October 2022 release of the Azure Developer CLI (currently 0.3.0-beta.2). Read the release announcement for more details. Here are the highlights:

    • Azure Pipelines for CI/CD: This addresses azure-dev#101, adding support for Azure Pipelines (alongside GitHub Actions) as a CI/CD provider. Learn more about usage and related documentation.
    • Improved Infrastructure as Code structure via Bicep modules: This addresses azure-dev#543, which recognized the complexity of using a single resources.bicep file for all resources. With this release, azd templates now come with Bicep modules organized by purpose making it easier to edit and understand. Learn more about this structure, and how to use it.
    • New Templates Gallery - awesome-azd: This addresses azure-dev#398, which aimed to make templates more discoverable and easier to contribute. Learn more about how the new gallery improves the template discovery experience.

    In the next section, we'll dive briefly into the last feature, introducing the new awesome-azd site and resource for templates discovery and contribution. And, since it's #Hacktoberfest season, we'll talk about the Contributor Guide and the many ways you can contribute to this project - with, or without, code.


    It's awesome-azd

    Welcome to awesome-azd a new template gallery hosted on GitHub Pages, and meant to be a destination site for discovering, requesting, and contributing azd-templates for community use!

    In addition, it's README reflects the awesome-list resource format, providing a location for the community to share "best of" resources for Azure Developer CLI - from blog posts and videos, to full-scale tutorials and templates.

    The Gallery is organized into three main areas:

    Take a minute to explore the Gallery and note the features:

    • Search for templates by name
    • Requested Templates - indicating asks from the community
    • Featured Templates - highlighting high-quality templates
    • Filters - to discover templates by and/or query combinations

    Check back often to see the latest contributed templates and requests!


    Hacktoberfest

    So, why is this a good time to talk about the Gallery? Because October means it's time for #Hacktoberfest - a month-long celebration of open-source projects and their maintainers, and an opportunity for first-time contributors to get support and guidance making their first pull-requests! Check out the #Hacktoberfest topic on GitHub for projects you can contribute to.

    And we hope you think of awesome-azd as another possible project to contribute to.

    Check out the FAQ section to learn how to create, discover, and contribute templates. Or take a couple of minutes to watch this video walkthrough from Jon Gallant:

    And don't hesitate to reach out to us - either via Issues on the repo, or in the Discussions section of this site, to give us feedback!

    Happy Hacking! 🎃


    - - + + \ No newline at end of file diff --git a/blog/tags/hello/index.html b/blog/tags/hello/index.html index 2fed4762c6..6f0d8463bb 100644 --- a/blog/tags/hello/index.html +++ b/blog/tags/hello/index.html @@ -14,13 +14,13 @@ - - + +

    2 posts tagged with "hello"

    View All Tags

    · 5 min read
    Nitya Narasimhan
    Devanshi Joshi

    What We'll Cover

    • What is Serverless September? (6 initiatives)
    • How can I participate? (3 actions)
    • How can I skill up (30 days)
    • Who is behind this? (Team Contributors)
    • How can you contribute? (Custom Issues)
    • Exercise: Take the Cloud Skills Challenge!
    • Resources: #30DaysOfServerless Collection.

    Serverless September

    Welcome to Day 01 of 🍂 #ServerlessSeptember! Today, we kick off a full month of content and activities to skill you up on all things Serverless on Azure with content, events, and community interactions! Read on to learn about what we have planned!


    Explore our initiatives

    We have a number of initiatives planned for the month to help you learn and skill up on relevant technologies. Click on the links to visit the relevant pages for each.

    We'll go into more details about #30DaysOfServerless in this post - don't forget to subscribe to the blog to get daily posts delivered directly to your preferred feed reader!


    Register for events!

    What are 3 things you can do today, to jumpstart your learning journey?

    Serverless Hacks


    #30DaysOfServerless

    #30DaysOfServerless is a month-long series of daily blog posts grouped into 4 themed weeks - taking you from core concepts to end-to-end solution examples in 30 days. Each article will be short (5-8 mins reading time) and provide exercises and resources to help you reinforce learnings and take next steps.

    This series focuses on the Serverless On Azure learning journey in four stages, each building on the previous week to help you skill up in a beginner-friendly way:

    We have a tentative roadmap for the topics we hope to cover and will keep this updated as we go with links to actual articles as they get published.

    Week 1: FOCUS ON FUNCTIONS ⚡️

    Here's a sneak peek at what we have planned for week 1. We'll start with a broad look at fundamentals, walkthrough examples for each targeted programming language, then wrap with a post that showcases the role of Azure Functions in powering different serverless scenarios.

    • Sep 02: Learn Core Concepts for Azure Functions
    • Sep 03: Build and deploy your first Function
    • Sep 04: Azure Functions - for Java Developers!
    • Sep 05: Azure Functions - for JavaScript Developers!
    • Sep 06: Azure Functions - for .NET Developers!
    • Sep 07: Azure Functions - for Python Developers!
    • Sep 08: Wrap: Azure Functions + Serverless on Azure

    Ways to Participate..

    We hope you are as excited as we are, to jumpstart this journey. We want to make this a useful, beginner-friendly journey and we need your help!

    Here are the many ways you can participate:

    • Follow Azure on dev.to - we'll republish posts under this series page and welcome comments and feedback there!
    • Discussions on GitHub - Use this if you have feedback for us (on how we can improve these resources), or want to chat with your peers about serverless topics.
    • Custom Issues - just pick a template, create a new issue by filling in the requested details, and submit. You can use these to:
      • submit questions for AskTheExpert (live Q&A) ahead of time
      • submit your own articles or projects for community to learn from
      • share your ServerlessHack and get listed in our Hall Of Fame!
      • report bugs or share ideas for improvements

    Here's the list of custom issues currently defined.

    Community Buzz

    Let's Get Started!

    Now you know everything! We hope you are as excited as we are to dive into a full month of active learning and doing! Don't forget to subscribe for updates in your favorite feed reader! And look out for our first Azure Functions post tomorrow!


    - - + + \ No newline at end of file diff --git a/blog/tags/hello/page/2/index.html b/blog/tags/hello/page/2/index.html index bfe1cd6cf0..886c56f046 100644 --- a/blog/tags/hello/page/2/index.html +++ b/blog/tags/hello/page/2/index.html @@ -14,13 +14,13 @@ - - + +

    2 posts tagged with "hello"

    View All Tags

    · 3 min read
    Nitya Narasimhan
    Devanshi Joshi

    🍂 It's September?

    Well, almost! September 1 is a few days away and I'm excited! Why? Because it's the perfect time to revisit #Serverless September, a month of

    ".. content-driven learning where experts and practitioners share their insights and tutorials on how to use serverless technologies effectively in today's ecosystems"

    If the words look familiar, it's because I actually wrote them 2 years ago when we launched the 2020 edition of this series. You might even recall this whimsical image I drew to capture the concept of September (fall) and Serverless (event-driven on-demand compute). Since then, a lot has happened in the serverless ecosystem!

    You can still browse the 2020 Content Collection to find great talks, articles and code samples to get started using Serverless on Azure. But read on to learn what's new!

    🧐 What's New?

    Well - quite a few things actually. This year, Devanshi Joshi and I expanded the original concept in a number of ways. Here's just a few of them that come to mind.

    New Website

    This year, we created this website (shortcut: https://aka.ms/serverless-september) to serve as a permanent home for content in 2022 and beyond - making it a canonical source for the #serverless posts we publish to tech communities like dev.to, Azure Developer Community and Apps On Azure. We hope this also makes it easier for you to search for, or discover, current and past articles that support your learning journey!

    Start by bookmarking these two sites:

    More Options

    Previous years focused on curating and sharing content authored by Microsoft and community contributors, showcasing serverless examples and best practices. This was perfect for those who already had experience with the core devtools and concepts.

    This year, we wanted to combine beginner-friendly options (for those just starting their serverless journey) with more advanced insights (for those looking to skill up further). Here's a sneak peek at some of the initiatives we've got planned!

    We'll also explore the full spectrum of serverless - from Functions-as-a-Service (for granularity) to Containerization (for deployment) and Microservices (for scalability). Here are a few services and technologies you'll get to learn more about:

    ⚡️ Join us!

    This has been a labor of love from multiple teams at Microsoft! We can't wait to share all the resources that we hope will help you skill up on all things Serverless this September! Here are a couple of ways to participate:

    - - + + \ No newline at end of file diff --git a/blog/tags/index.html b/blog/tags/index.html index 8ebf127803..429850fce4 100644 --- a/blog/tags/index.html +++ b/blog/tags/index.html @@ -14,13 +14,13 @@ - - + +

    Tags

    - - + + \ No newline at end of file diff --git a/blog/tags/java/index.html b/blog/tags/java/index.html index 65e67cb832..b49982eef8 100644 --- a/blog/tags/java/index.html +++ b/blog/tags/java/index.html @@ -14,13 +14,13 @@ - - + +

    One post tagged with "java"

    View All Tags

    · 8 min read
    Rory Preddy

    Welcome to Day 4 of #30DaysOfServerless!

    Yesterday we walked through an Azure Functions Quickstart with JavaScript, and used it to understand the general Functions App structure, tooling and developer experience.

    Today we'll look at developing Functions app with a different programming language - namely, Java - and explore developer guidance, tools and resources to build serverless Java solutions on Azure.


    What We'll Cover


    Developer Guidance

    If you're a Java developer new to serverless on Azure, start by exploring the Azure Functions Java Developer Guide. It covers:

    In this blog post, we'll dive into one quickstart, and discuss other resources briefly, for awareness! Do check out the recommended exercises and resources for self-study!


    My First Java Functions App

    In today's post, we'll walk through the Quickstart: Azure Functions tutorial using Visual Studio Code. In the process, we'll setup our development environment with the relevant command-line tools and VS Code extensions to make building Functions app simpler.

    Note: Completing this exercise may incur a a cost of a few USD cents based on your Azure subscription. Explore pricing details to learn more.

    First, make sure you have your development environment setup and configured.

    PRE-REQUISITES
    1. An Azure account with an active subscription - Create an account for free
    2. The Java Development Kit, version 11 or 8. - Install
    3. Apache Maven, version 3.0 or above. - Install
    4. Visual Studio Code. - Install
    5. The Java extension pack - Install
    6. The Azure Functions extension for Visual Studio Code - Install

    VS Code Setup

    NEW TO VISUAL STUDIO CODE?

    Start with the Java in Visual Studio Code tutorial to jumpstart your learning!

    Install the Extension Pack for Java (shown below) to install 6 popular extensions to help development workflow from creation to testing, debugging, and deployment.

    Extension Pack for Java

    Now, it's time to get started on our first Java-based Functions app.

    1. Create App

    1. Open a command-line terminal and create a folder for your project. Use the code command to launch Visual Studio Code from that directory as shown:

      $ mkdir java-function-resource-group-api
      $ cd java-function-resource-group-api
      $ code .
    2. Open the Visual Studio Command Palette (Ctrl + Shift + p) and select Azure Functions: create new project to kickstart the create workflow. Alternatively, you can click the Azure icon (on activity sidebar), to get the Workspace window, click "+" and pick the "Create Function" option as shown below.

      Screenshot of creating function in Azure from Visual Studio Code.

    3. This triggers a multi-step workflow. Fill in the information for each step as shown in the following prompts. Important: Start this process from an empty folder - the workflow will populate it with the scaffold for your Java-based Functions app.

      PromptValue
      Choose the directory location.You should either create a new folder or choose an empty folder for the project workspace. Don't choose a project folder that is already part of a workspace.
      Select a languageChoose Java.
      Select a version of JavaChoose Java 11 or Java 8, the Java version on which your functions run in Azure. Choose a Java version that you've verified locally.
      Provide a group IDChoose com.function.
      Provide an artifact IDEnter myFunction.
      Provide a versionChoose 1.0-SNAPSHOT.
      Provide a package nameChoose com.function.
      Provide an app nameEnter HttpExample.
      Select the build tool for Java projectChoose Maven.

    Visual Studio Code uses the provided information and generates an Azure Functions project. You can view the local project files in the Explorer - it should look like this:

    Azure Functions Scaffold For Java

    2. Preview App

    Visual Studio Code integrates with the Azure Functions Core tools to let you run this project on your local development computer before you publish to Azure.

    1. To build and run the application, use the following Maven command. You should see output similar to that shown below.

      $ mvn clean package azure-functions:run
      ..
      ..
      Now listening on: http://0.0.0.0:7071
      Application started. Press Ctrl+C to shut down.

      Http Functions:

      HttpExample: [GET,POST] http://localhost:7071/api/HttpExample
      ...
    2. Copy the URL of your HttpExample function from this output to a browser and append the query string ?name=<YOUR_NAME>, making the full URL something like http://localhost:7071/api/HttpExample?name=Functions. The browser should display a message that echoes back your query string value. The terminal in which you started your project also shows log output as you make requests.

    🎉 CONGRATULATIONS

    You created and ran a function app locally!

    With the Terminal panel focused, press Ctrl + C to stop Core Tools and disconnect the debugger. After you've verified that the function runs correctly on your local computer, it's time to use Visual Studio Code and Maven to publish and test the project on Azure.

    3. Sign into Azure

    Before you can deploy, sign in to your Azure subscription.

    az login

    The az login command signs you into your Azure account.

    Use the following command to deploy your project to a new function app.

    mvn clean package azure-functions:deploy

    When the creation is complete, the following Azure resources are created in your subscription:

    • Resource group. Named as java-functions-group.
    • Storage account. Required by Functions. The name is generated randomly based on Storage account name requirements.
    • Hosting plan. Serverless hosting for your function app.The name is java-functions-app-service-plan.
    • Function app. A function app is the deployment and execution unit for your functions. The name is randomly generated based on your artifactId, appended with a randomly generated number.

    4. Deploy App

    1. Back in the Resources area in the side bar, expand your subscription, your new function app, and Functions. Right-click (Windows) or Ctrl - click (macOS) the HttpExample function and choose Execute Function Now....

      Screenshot of executing function in Azure from Visual Studio Code.

    2. In Enter request body you see the request message body value of { "name": "Azure" }. Press Enter to send this request message to your function.

    3. When the function executes in Azure and returns a response, a notification is raised in Visual Studio Code.

    You can also copy the complete Invoke URL shown in the output of the publish command into a browser address bar, appending the query parameter ?name=Functions. The browser should display similar output as when you ran the function locally.

    🎉 CONGRATULATIONS

    You deployed your function app to Azure, and invoked it!

    5. Clean up

    Use the following command to delete the resource group and all its contained resources to avoid incurring further costs.

    az group delete --name java-functions-group

    Next Steps

    So, where can you go from here? The example above used a familiar HTTP Trigger scenario with a single Azure service (Azure Functions). Now, think about how you can build richer workflows by using other triggers and integrating with other Azure or third-party services.

    Other Triggers, Bindings

    Check out Azure Functions Samples In Java for samples (and short use cases) that highlight other triggers - with code! This includes triggers to integrate with CosmosDB, Blob Storage, Event Grid, Event Hub, Kafka and more.

    Scenario with Integrations

    Once you've tried out the samples, try building an end-to-end scenario by using these triggers to integrate seamlessly with other Services. Here are a couple of useful tutorials:

    Exercise

    Time to put this into action and validate your development workflow:

    Resources

    - - + + \ No newline at end of file diff --git a/blog/tags/javascript/index.html b/blog/tags/javascript/index.html index 8a4017bebd..370a5a3af6 100644 --- a/blog/tags/javascript/index.html +++ b/blog/tags/javascript/index.html @@ -14,13 +14,13 @@ - - + +

    One post tagged with "javascript"

    View All Tags

    · 7 min read
    Aaron Powell

    Welcome to Day 5 of #30DaysOfServerless!

    Yesterday we looked at Azure Functions from the perspective of a Java developer. Today, we'll do a similar walkthrough from the perspective of a JavaScript developer.

    And, we'll use this to explore another popular usage scenario for Azure Functions: building a serverless HTTP API using JavaScript.

    Ready? Let's go.


    What We'll Cover

    • Developer Guidance
    • Create Azure Function with CLI
    • Calling an external API
    • Azure Samples & Scenarios for JS
    • Exercise: Support searching
    • Resources: For self-study!


    Developer Guidance

    If you're a JavaScript developer new to serverless on Azure, start by exploring the Azure Functions JavaScript Developers Guide. It covers:

    • Quickstarts for Node.js - using Visual Code, CLI or Azure Portal
    • Guidance on hosting options and performance considerations
    • Azure Functions bindings and (code samples) for JavaScript
    • Scenario examples - integrations with other Azure Services

    Node.js 18 Support

    Node.js 18 Support (Public Preview)

    Azure Functions support for Node.js 18 entered Public Preview on Aug 31, 2022 and is supported by the Azure Functions v.4.x runtime!

    As we continue to explore how we can use Azure Functions, today we're going to look at using JavaScript to create one, and we're going to be using the newly released Node.js 18 support for Azure Functions to make the most out of the platform.

    Ensure you have Node.js 18 and Azure Functions v4.x versions installed, along with a text editor (I'll use VS Code in this post), and a terminal, then we're ready to go.

    Scenario: Calling The GitHub API

    The application we're going to be building today will use the GitHub API to return a random commit message, so that we don't need to come up with one ourselves! After all, naming things can be really hard! 🤣

    Creating the Azure Function

    To create our Azure Function, we're going to use the Azure Functions CLI, which we can install using npm:

    npm install --global azure-function-core-tools

    Once that's installed, we can use the new func command to initalise our project:

    func init --worker-runtime node --language javascript

    When running func init we can either provide the worker-runtime and language as arguments, or use the menu system that the tool will provide us. For brevity's stake, I've used the arguments here, specifying that we want node as the runtime and javascript as the language, but you could change that to typescript if you'd prefer to use TypeScript.

    Once the init command is completed, you should have a .vscode folder, and the files .gitignore, host.json, local.settings.json, and package.json.

    Files generated by func initFiles generated by func init

    Adding a HTTP Trigger

    We have an empty Functions app so far, what we need to do next is create a Function that it will run, and we're going to make a HTTP Trigger Function, which is a Function that responds to HTTP requests. We'll use the func new command to create that:

    func new --template "HTTP Trigger" --name "get-commit-message"

    When this completes, we'll have a folder for the Function, using the name we provided, that contains the filesfunction.json and index.js. Let's open the function.json to understand it a little bit:

    {
    "bindings": [
    {
    "authLevel": "function",
    "type": "httpTrigger",
    "direction": "in",
    "name": "req",
    "methods": [
    "get",
    "post"
    ]
    },
    {
    "type": "http",
    "direction": "out",
    "name": "res"
    }
    ]
    }

    This file is used to tell Functions about the Function that we've created and what it does, so it knows to handle the appropriate events. We have a bindings node which contains the event bindings for our Azure Function. The first binding is using the type httpTrigger, which indicates that it'll be executed, or triggered, by a HTTP event, and the methods indicates that it's listening to both GET and POST (you can change this for the right HTTP methods that you want to support). The HTTP request information will be bound to a property in the Functions context called req, so we can access query strings, the request body, etc.

    The other binding we have has the direction of out, meaning that it's something that the Function will return to the called, and since this is a HTTP API, the type is http, indicating that we'll return a HTTP response, and that response will be on a property called res that we add to the Functions context.

    Let's go ahead and start the Function and call it:

    func start

    Starting the FunctionStarting the Function

    With the Function started, access the endpoint http://localhost:7071/api/get-commit-message via a browser or using cURL:

    curl http://localhost:7071/api/get-commit-message\?name\=ServerlessSeptember

    Hello from Azure FunctionsHello from Azure Functions

    🎉 CONGRATULATIONS

    You created and ran a JavaScript function app locally!

    Calling an external API

    It's time to update the Function to do what we want to do - call the GitHub Search API and get some commit messages. The endpoint that we'll be calling is https://api.github.com/search/commits?q=language:javascript.

    Note: The GitHub API is rate limited and this sample will call it unauthenticated, so be aware of that in your own testing.

    To call this API, we'll leverage the newly released fetch support in Node 18 and async/await, to make for a very clean Function.

    Open up the index.js file, and delete the contents of the existing Function, so we have a empty one:

    module.exports = async function (context, req) {

    }

    The default template uses CommonJS, but you can use ES Modules with Azure Functions if you prefer.

    Now we'll use fetch to call the API, and unpack the JSON response:

    module.exports = async function (context, req) {
    const res = await fetch("https://api.github.com/search/commits?q=language:javascript");
    const json = await res.json();
    const messages = json.items.map(item => item.commit.message);
    context.res = {
    body: {
    messages
    }
    };
    }

    To send a response to the client, we're setting the context.res property, where res is the name of the output binding in our function.json, and giving it a body that contains the commit messages.

    Run func start again, and call the endpoint:

    curl http://localhost:7071/api/get-commit-message

    The you'll get some commit messages:

    A series of commit messages from the GitHub Search APIA series of commit messages from the GitHub Search API

    🎉 CONGRATULATIONS

    There we go, we've created an Azure Function which is used as a proxy to another API, that we call (using native fetch in Node.js 18) and from which we return a subset of the JSON payload.

    Next Steps

    Other Triggers, Bindings

    This article focused on using the HTTPTrigger and relevant bindings, to build a serverless API using Azure Functions. How can you explore other supported bindings, with code samples to illustrate usage?

    Scenarios with Integrations

    Once you've tried out the samples, try building an end-to-end scenario by using these triggers to integrate seamlessly with other services. Here are some suggestions:

    Exercise: Support searching

    The GitHub Search API allows you to provide search parameters via the q query string. In this sample, we hard-coded it to be language:javascript, but as a follow-on exercise, expand the Function to allow the caller to provide the search terms as a query string to the Azure Function, which is passed to the GitHub Search API. Hint - have a look at the req argument.

    Resources

    - - + + \ No newline at end of file diff --git a/blog/tags/keda/index.html b/blog/tags/keda/index.html index cb2899b903..b4cef2627c 100644 --- a/blog/tags/keda/index.html +++ b/blog/tags/keda/index.html @@ -14,13 +14,13 @@ - - + +

    One post tagged with "keda"

    View All Tags

    · 7 min read
    Paul Yu

    Welcome to Day 11 of #30DaysOfServerless!

    Yesterday we explored Azure Container Concepts related to environments, networking and microservices communication - and illustrated these with a deployment example. Today, we turn our attention to scaling your container apps with demand.


    What We'll Cover

    • What makes ACA Serverless?
    • What is Keda?
    • Scaling Your ACA
    • ACA Scaling In Action
    • Exercise: Explore azure-opensource-labs examples
    • Resources: For self-study!


    So, what makes Azure Container Apps "serverless"?

    Today we are going to focus on what makes Azure Container Apps (ACA) a "serverless" offering. But what does the term "serverless" really mean? As much as we'd like to think there aren't any servers involved, that is certainly not the case. In general, "serverless" means that most (if not all) server maintenance has been abstracted away from you.

    With serverless, you don't spend any time managing and patching servers. This concern is offloaded to Azure and you simply focus on adding business value through application delivery. In addition to operational efficiency, cost efficiency can be achieved with serverless on-demand pricing models. Your workload horizontally scales out based on need and you only pay for what you use. To me, this is serverless, and my teammate @StevenMurawski said it best... "being able to scale to zero is what gives ACA it's serverless magic."

    Scaling your Container Apps

    If you don't know by now, ACA is built on a solid open-source foundation. Behind the scenes, it runs on a managed Kubernetes cluster and includes several open-source components out-of-the box including Dapr to help you build and run microservices, Envoy Proxy for ingress capabilities, and KEDA for event-driven autoscaling. Again, you do not need to install these components yourself. All you need to be concerned with is enabling and/or configuring your container app to leverage these components.

    Let's take a closer look at autoscaling in ACA to help you optimize your container app.

    What is KEDA?

    KEDA stands for Kubernetes Event-Driven Autoscaler. It is an open-source project initially started by Microsoft and Red Hat and has been donated to the Cloud-Native Computing Foundation (CNCF). It is being maintained by a community of 200+ contributors and adopted by many large organizations. In terms of its status as a CNCF project it is currently in the Incubating Stage which means the project has gone through significant due diligence and on its way towards the Graduation Stage.

    Prior to KEDA, horizontally scaling your Kubernetes deployment was achieved through the Horizontal Pod Autoscaler (HPA) which relies on resource metrics such as CPU and memory to determine when additional replicas should be deployed. Being limited to CPU and memory falls a bit short for certain workloads. This is especially true for apps that need to processes messages from a queue or HTTP-based apps that can handle a specific amount of incoming HTTP requests at a time. KEDA aims to fill that gap and provides a much more robust framework for scaling by working in conjunction with HPA. It offers many scalers for you to implement and even allows your deployments to scale to zero! 🥳

    KEDA architecture

    Configuring ACA scale rules

    As I mentioned above, ACA's autoscaling feature leverages KEDA and gives you the ability to configure the number of replicas to deploy based on rules (event triggers). The number of replicas can be configured as a static number or a range (minimum and maximum). So if you need your containers to run 24/7, set the min and max to be the same value. By default, when you deploy a container app, it is set to scale from 0 to 10 replicas. The default scaling rule uses HTTP scaling and defaults to a minimum of 10 concurrent requests per second. Once the threshold of 10 concurrent request per second is met, another replica will be deployed until it reaches the maximum number of replicas.

    At the time of this writing, a container app can have up to 30 replicas.

    Default autoscaler

    As a best practice, if you have a Min / max replicas range configured, you should configure a scaling rule even if it is just explicitly setting the default values.

    Adding HTTP scaling rule

    In addition to HTTP scaling, you can also configure an Azure queue rule, which allows you to use Azure Storage Queues as an event data source.

    Adding Azure Queue scaling rule

    The most flexibility comes with the Custom rule type. This opens up a LOT more options for scaling. All of KEDA's event-based scalers are supported with this option 🚀

    Adding Custom scaling rule

    Translating KEDA templates to Azure templates

    When you implement Custom rules, you need to become familiar with translating KEDA templates to Azure Resource Manager templates or ACA YAML manifests. The KEDA scaler documentation is great and it should be simple to translate KEDA template metadata to an ACA rule metadata.

    The images below shows how to translated a scaling rule which uses Azure Service Bus as an event data source. The custom rule type is set to azure-servicebus and details of the service bus is added to the Metadata section. One important thing to note here is that the connection string to the service bus was added as a secret on the container app and the trigger parameter must be set to connection.

    Azure Container App custom rule metadata

    Azure Container App custom rule metadata

    Additional examples of KEDA scaler conversion can be found in the resources section and example video below.

    See Container App scaling in action

    Now that we've built up some foundational knowledge on how ACA autoscaling is implemented and configured, let's look at a few examples.

    Autoscaling based on HTTP traffic load

    Autoscaling based on Azure Service Bus message queues

    Summary

    ACA brings you a true serverless experience and gives you the ability to configure autoscaling rules based on KEDA scaler templates. This gives you flexibility to scale based on a wide variety of data sources in an event-driven manner. With the amount built-in scalers currently available, there is probably a scaler out there for all your use cases. If not, I encourage you to get involved with the KEDA community and help make it better!

    Exercise

    By now, you've probably read and seen enough and now ready to give this autoscaling thing a try. The example I walked through in the videos above can be found at the azure-opensource-labs repo. I highly encourage you to head over to the containerapps-terraform folder and try the lab out. There you'll find instructions which will cover all the steps and tools you'll need implement autoscaling container apps within your own Azure subscription.

    If you have any questions or feedback, please let us know in the comments below or reach out on Twitter @pauldotyu

    Have fun scaling your containers!

    Resources

    - - + + \ No newline at end of file diff --git a/blog/tags/logic-apps/index.html b/blog/tags/logic-apps/index.html index 3b1156c29a..bf081dab3a 100644 --- a/blog/tags/logic-apps/index.html +++ b/blog/tags/logic-apps/index.html @@ -14,15 +14,15 @@ - - + +

    One post tagged with "logic-apps"

    View All Tags

    · 10 min read
    Ayca Bas

    Welcome to Day 20 of #30DaysOfServerless!

    Every day millions of people spend their precious time in productivity tools. What if you use data and intelligence behind the Microsoft applications (Microsoft Teams, Outlook, and many other Office apps) to build seamless automations and custom apps to boost productivity?

    In this post, we'll learn how to build a seamless onboarding experience for new employees joining a company with the power of Microsoft Graph, integrated with Event Hubs and Logic Apps!


    What We'll Cover

    • ✨ The power of Microsoft Graph
    • 🖇️ How do Microsoft Graph and Event Hubs work together?
    • 🛠 Let's Build an Onboarding Workflow!
      • 1️⃣ Setup Azure Event Hubs + Key Vault
      • 2️⃣ Subscribe to users, receive change notifications from Logic Apps
      • 3️⃣ Create Onboarding workflow in the Logic Apps
    • 🚀 Debug: Your onboarding experience
    • ✋ Exercise: Try this tutorial out yourself!
    • 📚 Resources: For Self-Study


    ✨ The Power of Microsoft Graph

    Microsoft Graph is the gateway to data and intelligence in Microsoft 365 platform. Microsoft Graph exploses Rest APIs and client libraries to access data across Microsoft 365 core services such as Calendar, Teams, To Do, Outlook, People, Planner, OneDrive, OneNote and more.

    Overview of Microsoft Graph

    You can build custom experiences by using Microsoft Graph such as automating the onboarding process for new employees. When new employees are created in the Azure Active Directory, they will be automatically added in the Onboarding team on Microsoft Teams.

    Solution architecture


    🖇️ Microsoft Graph with Event Hubs

    Microsoft Graph uses a webhook mechanism to track changes in resources and deliver change notifications to the clients. For example, with Microsoft Graph Change Notifications, you can receive change notifications when:

    • a new task is added in the to-do list
    • a user changes the presence status from busy to available
    • an event is deleted/cancelled from the calendar

    If you'd like to track a large set of resources at a high frequency, use Azure Events Hubs instead of traditional webhooks to receive change notifications. Azure Event Hubs is a popular real-time events ingestion and distribution service built for scale.

    EVENT GRID - PARTNER EVENTS

    Microsoft Graph Change Notifications can be also received by using Azure Event Grid -- currently available for Microsoft Partners! Read the Partner Events Overview documentation for details.

    Setup Azure Event Hubs + Key Vault.

    To get Microsoft Graph Change Notifications delivered to Azure Event Hubs, we'll have to setup Azure Event Hubs and Azure Key Vault. We'll use Azure Key Vault to access to Event Hubs connection string.

    1️⃣ Create Azure Event Hubs

    1. Go to Azure Portal and select Create a resource, type Event Hubs and select click Create.
    2. Fill in the Event Hubs namespace creation details, and then click Create.
    3. Go to the newly created Event Hubs namespace page, select Event Hubs tab from the left pane and + Event Hub:
      • Name your Event Hub as Event Hub
      • Click Create.
    4. Click the name of the Event Hub, and then select Shared access policies and + Add to add a new policy:
      • Give a name to the policy
      • Check Send and Listen
      • Click Create.
    5. After the policy has been created, click the name of the policy to open the details panel, and then copy the Connection string-primary key value. Write it down; you'll need it for the next step.
    6. Go to Consumer groups tab in the left pane and select + Consumer group, give a name for your consumer group as onboarding and select Create.

    2️⃣ Create Azure Key Vault

    1. Go to Azure Portal and select Create a resource, type Key Vault and select Create.
    2. Fill in the Key Vault creation details, and then click Review + Create.
    3. Go to newly created Key Vault and select Secrets tab from the left pane and click + Generate/Import:
      • Give a name to the secret
      • For the value, paste in the connection string you generated at the Event Hubs step
      • Click Create
      • Copy the name of the secret.
    4. Select Access Policies from the left pane and + Add Access Policy:
      • For Secret permissions, select Get
      • For Principal, select Microsoft Graph Change Tracking
      • Click Add.
    5. Select Overview tab from the left pane and copy the Vault URI.

    Subscribe for Logic Apps change notifications

    To start receiving Microsoft Graph Change Notifications, we'll need to create subscription to the resource that we'd like to track - here, 'users'. We'll use Azure Logic Apps to create subscription.

    To create subscription for Microsoft Graph Change Notifications, we'll need to make a http post request to https://graph.microsoft.com/v1.0/subscriptions. Microsoft Graph requires Azure Active Directory authentication make API calls. First, we'll need to register an app to Azure Active Directory, and then we will make the Microsoft Graph Subscription API call with Azure Logic Apps.

    1️⃣ Create an app in Azure Active Directory

    1. In the Azure Portal, go to Azure Active Directory and select App registrations from the left pane and select + New registration. Fill in the details for the new App registration form as below:
      • Name: Graph Subscription Flow Auth
      • Supported account types: Accounts in any organizational directory (Any Azure AD directory - Multitenant) and personal Microsoft accounts (e.g. Skype, Xbox)
      • Select Register.
    2. Go to newly registered app in Azure Active Directory, select API permissions:
      • Select + Add a permission and Microsoft Graph
      • Select Application permissions and add User.Read.All and Directory.Read.All.
      • Select Grant admin consent for the organization
    3. Select Certificates & secrets tab from the left pane, select + New client secret:
      • Choose desired expiry duration
      • Select Add
      • Copy the value of the secret.
    4. Go to Overview from the left pane, copy Application (client) ID and Directory (tenant) ID.

    2️⃣ Create subscription with Azure Logic Apps

    1. Go to Azure Portal and select Create a resource, type Logic apps and select click Create.

    2. Fill in the Logic Apps creation details, and then click Create.

    3. Go to the newly created Logic Apps page, select Workflows tab from the left pane and select + Add:

      • Give a name to the new workflow as graph-subscription-flow
      • Select Stateful as a state type
      • Click Create.
    4. Go to graph-subscription-flow, and then select Designer tab.

    5. In the Choose an operation section, search for Schedule and select Recurrence as a trigger. Fill in the parameters as below:

      • Interval: 61
      • Frequency: Minute
      • Time zone: Select your own time zone
      • Start time: Set a start time
    6. Select + button in the flow and select add an action. Search for HTTP and select HTTP as an action. Fill in the parameters as below:

      • Method: POST
      • URI: https://graph.microsoft.com/v1.0/subscriptions
      • Headers:
        • Key: Content-type
        • Value: application/json
      • Body:
      {
      "changeType": "created, updated",
      "clientState": "secretClientValue",
      "expirationDateTime": "@{addHours(utcNow(), 1)}",
      "notificationUrl": "EventHub:https://<YOUR-VAULT-URI>/secrets/<YOUR-KEY-VAULT-SECRET-NAME>?tenantId=72f988bf-86f1-41af-91ab-2d7cd011db47",
      "resource": "users"
      }

      In notificationUrl, make sure to replace <YOUR-VAULT-URI> with the vault uri and <YOUR-KEY-VAULT-SECRET-NAME> with the secret name that you copied from the Key Vault.

      In resource, define the resource type you'd like to track changes. For our example, we will track changes for users resource.

      • Authentication:
        • Authentication type: Active Directory OAuth
        • Authority: https://login.microsoft.com
        • Tenant: Directory (tenant) ID copied from AAD app
        • Audience: https://graph.microsoft.com
        • Client ID: Application (client) ID copied from AAD app
        • Credential Type: Secret
        • Secret: value of the secret copied from AAD app
    7. Select Save and run your workflow from the Overview tab.

      Check your subscription in Graph Explorer: If you'd like to make sure that your subscription is created successfully by Logic Apps, you can go to Graph Explorer, login with your Microsoft 365 account and make GET request to https://graph.microsoft.com/v1.0/subscriptions. Your subscription should appear in the response after it's created successfully.

    Subscription workflow success

    After subscription is created successfully by Logic Apps, Azure Event Hubs will receive notifications whenever there is a new user created in Azure Active Directory.


    Create Onboarding workflow in Logic Apps

    We'll create a second workflow in the Logic Apps to receive change notifications from Event Hubs when there is a new user created in the Azure Active Directory and add new user in Onboarding team on Microsoft Teams.

    1. Go to the Logic Apps you created in the previous steps, select Workflows tab and create a new workflow by selecting + Add:
      • Give a name to the new workflow as teams-onboarding-flow
      • Select Stateful as a state type
      • Click Create.
    2. Go to teams-onboarding-flow, and then select Designer tab.
    3. In the Choose an operation section, search for Event Hub, select When events are available in Event Hub as a trigger. Setup Event Hub connection as below:
      • Create Connection:
        • Connection name: Connection
        • Authentication Type: Connection String
        • Connection String: Go to Event Hubs > Shared Access Policies > RootManageSharedAccessKey and copy Connection string–primary key
        • Select Create.
      • Parameters:
        • Event Hub Name: Event Hub
        • Consumer Group Name: onboarding
    4. Select + in the flow and add an action, search for Control and add For each as an action. Fill in For each action as below:
      • Select output from previous steps: Events
    5. Inside For each, select + in the flow and add an action, search for Data operations and select Parse JSON. Fill in Parse JSON action as below:
      • Content: Events Content
      • Schema: Copy the json content from schema-parse.json and paste as a schema
    6. Select + in the flow and add an action, search for Control and add For each as an action. Fill in For each action as below:
      • Select output from previous steps: value
      1. Inside For each, select + in the flow and add an action, search for Microsoft Teams and select Add a member to a team. Login with your Microsoft 365 account to create a connection and fill in Add a member to a team action as below:
      • Team: Create an Onboarding team on Microsoft Teams and select
      • A user AAD ID for the user to add to a team: id
    7. Select Save.

    🚀 Debug your onboarding experience

    To debug our onboarding experience, we'll need to create a new user in Azure Active Directory and see if it's added in Microsoft Teams Onboarding team automatically.

    1. Go to Azure Portal and select Azure Active Directory from the left pane and go to Users. Select + New user and Create new user. Fill in the details as below:

      • User name: JaneDoe
      • Name: Jane Doe

      new user in Azure Active Directory

    2. When you added Jane Doe as a new user, it should trigger the teams-onboarding-flow to run. teams onboarding flow success

    3. Once the teams-onboarding-flow runs successfully, you should be able to see Jane Doe as a member of the Onboarding team on Microsoft Teams! 🥳 new member in Onboarding team on Microsoft Teams

    Congratulations! 🎉

    You just built an onboarding experience using Azure Logic Apps, Azure Event Hubs and Azure Key Vault.


    📚 Resources

    - - + + \ No newline at end of file diff --git a/blog/tags/microservices/index.html b/blog/tags/microservices/index.html index 4b032eab58..1a24851f2b 100644 --- a/blog/tags/microservices/index.html +++ b/blog/tags/microservices/index.html @@ -14,8 +14,8 @@ - - + +
    @@ -26,7 +26,7 @@

    ...and that's it! We've successfully deployed our application on Azure!

    But there's more!

    Best practices: Monitoring and CI/CD!

    In my opinion, it's not enough to just set up the application on Azure! I want to know that my web app is performant and serving my users reliably! I also want to make sure that I'm not inadvertently breaking my application as I continue to make changes to it. Thankfully, the Azure Developer CLI also handles all of this via two additional commands - azd monitor and azd pipeline config.

    Application Monitoring

    When we provisioned all of our infrastructure, we also set up application monitoring via a Bicep file in our .infra/ directory that spec'd out an Application Insights dashboard. By running azd monitor we can see the dashboard with live metrics that was configured for the application.

    We can also navigate to the Application Dashboard by clicking on the resource group name, where you can set a specific refresh rate for the dashboard, and see usage, reliability, and performance metrics over time.

    I don't know about everyone else but I have spent a ton of time building out similar dashboards. It can be super time-consuming to write all the queries and create the visualizations so this feels like a real time saver.

    CI/CD

    Finally let's talk about setting up CI/CD! This might be my favorite azd feature. As I mentioned before, the Azure Developer CLI has a command, azd pipeline config, which uses the files in the .github/ directory to set up a GitHub Action. More than that, if there is no upstream repo, the Developer CLI will actually help you create one. But what does this mean exactly? Because our GitHub Action is using the same commands you'd run in the CLI under the hood, we're actually going to have CI/CD set up to run on every commit into the repo, against real Azure resources. What a sweet collaboration feature!

    That's it! We've gone end-to-end with the Azure Developer CLI - initialized a project, provisioned the resources on Azure, deployed our code on Azure, set up monitoring logs and dashboards, and set up a CI/CD pipeline with GitHub Actions to run on every commit into the repo (on real Azure resources!).

    Exercise: Try it yourself or create your own template!

    As an exercise, try out the workflow above with any template on GitHub!

    Or, try turning your own project into an Azure Developer CLI-enabled template by following this guidance. If you create your own template, don't forget to tag the repo with the azd-templates topic on GitHub to help others find it (unfamiliar with GitHub topics? Learn how to add topics to your repo)! We'd also love to chat with you about your experience creating an azd template - if you're open to providing feedback around this, please fill out this form!

    Resources

    - - + + \ No newline at end of file diff --git a/blog/tags/microservices/page/10/index.html b/blog/tags/microservices/page/10/index.html index 2258216723..eb21a62e7c 100644 --- a/blog/tags/microservices/page/10/index.html +++ b/blog/tags/microservices/page/10/index.html @@ -14,13 +14,13 @@ - - + +

    11 posts tagged with "microservices"

    View All Tags

    · 8 min read
    Paul Yu

    Welcome to Day 10 of #30DaysOfServerless!

    We continue our exploraton into Azure Container Apps, with today's focus being communication between microservices, and how to configure your Azure Container Apps environment in the context of a deployment example.


    What We'll Cover

    • ACA Environments & Virtual Networking
    • Basic Microservices Communications
    • Walkthrough: ACA Deployment Example
    • Summary and Next Steps
    • Exercise: Try this yourself!
    • Resources: For self-study!


    Introduction

    In yesterday's post, we learned what the Azure Container Apps (ACA) service is and the problems it aims to solve. It is considered to be a Container-as-a-Service platform since much of the complex implementation details of running a Kubernetes cluster is managed for you.

    Some of the use cases for ACA include event-driven processing jobs and background tasks, but this article will focus on hosting microservices, and how they can communicate with each other within the ACA service. At the end of this article, you will have a solid understanding of how networking and communication is handled and will leave you with a few tutorials to try.

    Environments and virtual networking in ACA

    Before we jump into microservices communication, we should review how networking works within ACA. With ACA being a managed service, Azure will take care of most of your underlying infrastructure concerns. As you provision an ACA resource, Azure provisions an Environment to deploy Container Apps into. This environment is your isolation boundary.

    Azure Container Apps Environment

    By default, Azure creates and manages a new Virtual Network (VNET) for you and the VNET is associated with the environment. As you deploy container apps, they are deployed into the same VNET and the environment is assigned a static public IP address which allows your apps to be accessible over the internet. This VNET is not visible or manageable.

    If you need control of the networking flows within the VNET, you can pre-provision one and tell Azure to deploy an environment within it. This "bring-your-own" VNET model allows you to deploy an environment in either External or Internal modes. Deploying an environment in External mode gives you the flexibility of managing your own VNET, while still allowing your containers to be accessible from outside the environment; a static public IP address is assigned to the environment. When deploying in Internal mode, your containers are accessible within the environment and/or VNET but not accessible from the internet.

    Bringing your own VNET will require some planning and you will need dedicate an empty subnet which will be used exclusively by the ACA environment. The size of your subnet will be dependant on how many containers you plan on deploying and your scaling requirements and one requirement to know is that the subnet address range must have have a /23 CIDR prefix at minimum. You will also need to think about your deployment strategy since ACA has the concept of Revisions which will also consume IPs from your subnet.

    Some additional restrictions to consider when planning your subnet address space is listed in the Resources section below and can be addressed in future posts, so be sure to follow us on dev.to and bookmark the ServerlessSeptember site.

    Basic microservices communication in ACA

    When it comes to communications between containers, ACA addresses this concern with its Ingress capabilities. With HTTP Ingress enabled on your container app, you can expose your app on a HTTPS endpoint.

    If your environment is deployed using default networking and your containers needs to be accessible from outside the environment, you will need to set the Ingress traffic option to Accepting traffic from anywhere. This will generate a Full-Qualified Domain Name (FQDN) which you can use to access your app right away. The ingress feature also generates and assigns a Secure Socket Layer (SSL) certificate for the FQDN.

    External ingress on Container App

    If your environment is deployed using default networking and your containers only need to communicate with other containers in the environment, you'll need to set the Ingress traffic option to Limited to Container Apps Environment. You get a FQDN here as well, but in the section below we'll see how that changes.

    Internal ingress on Container App

    As mentioned in the networking section above, if you deploy your ACA environment into a VNET in internal mode, your options will be Limited to Container Apps Environment or Limited to VNet.

    Ingress on internal virtual network

    Note how the Accepting traffic from anywhere option is greyed out. If your VNET is deployed in external mode, then the option will be available.

    Let's walk though an example ACA deployment

    The diagram below illustrates a simple microservices application that I deployed to ACA. The three container apps all have ingress enabled. The greeting-service app calls two backend services; a hello-service that returns the text Hello (in random casing) and a world-service that returns the text World (in a few random languages). The greeting-service concatenates the two strings together and returns Hello World to the browser. The greeting-service is the only service accessible via external ingress while two backend services are only accessible via internal ingress.

    Greeting Service overview

    With ingress enabled, let's take a quick look at the FQDN structures. Here is the FQDN of the external greeting-service.

    https://greeting-service.victoriouswave-3749d046.eastus.azurecontainerapps.io

    We can break it down into these components:

    https://[YOUR-CONTAINER-APP-NAME].[RANDOM-NAME]-[RANDOM-CHARACTERS].[AZURE-REGION].containerapps.io

    And here is the FQDN of the internal hello-service.

    https://hello-service.internal.victoriouswave-3749d046.eastus.azurecontainerapps.io

    Can you spot the difference between FQDNs?

    That was too easy 😉... the word internal is added as a subdomain in the FQDN between your container app name and the random name for all internal ingress endpoints.

    https://[YOUR-CONTAINER-APP-NAME].internal.[RANDOM-NAME]-[RANDOM-CHARACTERS].[AZURE-REGION].containerapps.io

    Now that we know the internal service FQDNs, we use them in the greeting-service app to achieve basic service-to-service communications.

    So we can inject FQDNs of downstream APIs to upstream apps using environment variables, but the downside to this approach is that need to deploy downstream containers ahead of time and this dependency will need to be planned for during your deployment process. There are ways around this and one option is to leverage the auto-injected environment variables within your app code.

    If I use the Console blade for the hello-service container app and run the env command, you will see environment variables named CONTAINER_APP_NAME and CONTAINER_APP_ENV_DNS_SUFFIX. You can use these values to determine FQDNs within your upstream app.

    hello-service environment variables

    Back in the greeting-service container I can invoke the hello-service container's sayhello method. I know the container app name is hello-service and this service is exposed over an internal ingress, therefore, if I add the internal subdomain to the CONTAINER_APP_ENV_DNS_SUFFIX I can invoke a HTTP request to the hello-service from my greeting-service container.

    Invoke the sayHello method from the greeting-service container

    As you can see, the ingress feature enables communications to other container apps over HTTP/S and ACA will inject environment variables into our container to help determine what the ingress FQDNs would be. All we need now is a little bit of code modification in the greeting-service app and build the FQDNs of our backend APIs by retrieving these environment variables.

    Greeting service code

    ... and now we have a working microservices app on ACA! 🎉

    Hello World

    Summary and next steps

    We've covered Container Apps networking and the basics of how containers communicate with one another. However, there is a better way to address service-to-service invocation using Dapr, which is an open-source framework for building microservices. It is natively integrated into the ACA service and in a future post, you'll learn how to enable it in your Container App to address microservices concerns and more. So stay tuned!

    Exercises

    As a takeaway for today's post, I encourage you to complete this tutorial and if you'd like to deploy the sample app that was presented in this article, my teammate @StevenMurawski is hosting a docker-compose-examples repo which includes samples for deploying to ACA using Docker Compose files. To learn more about the az containerapp compose command, a link to his blog articles are listed in the Resources section below.

    If you have any questions or feedback, please let us know in the comments below or reach out on Twitter @pauldotyu

    Have fun packing and shipping containers! See you in the next post!

    Resources

    The sample app presented here was inspired by services demonstrated in the book Introducing Distributed Application Runtime (Dapr): Simplifying Microservices Applications Development Through Proven and Reusable Patterns and Practices. Go check it out to leran more about Dapr!

    - - + + \ No newline at end of file diff --git a/blog/tags/microservices/page/11/index.html b/blog/tags/microservices/page/11/index.html index e0a6ba72fe..fac14d4c1e 100644 --- a/blog/tags/microservices/page/11/index.html +++ b/blog/tags/microservices/page/11/index.html @@ -14,13 +14,13 @@ - - + +

    11 posts tagged with "microservices"

    View All Tags

    · 12 min read
    Nitya Narasimhan

    Welcome to Day 9 of #30DaysOfServerless!


    What We'll Cover

    • The Week Ahead
    • Hello, Container Apps!
    • Quickstart: Build Your First ACA!
    • Under The Hood: Core ACA Concepts
    • Exercise: Try this yourself!
    • Resources: For self-study!


    The Week Ahead

    Welcome to Week 2 of #ServerlessSeptember, where we put the focus on Microservices and building Cloud-Native applications that are optimized for serverless solutions on Azure. One week is not enough to do this complex topic justice so consider this a 7-part jumpstart to the longer journey.

    1. Hello, Container Apps (ACA) - Learn about Azure Container Apps, a key service that helps you run microservices and containerized apps on a serverless platform. Know the core concepts. (Tutorial 1: First ACA)
    2. Communication with Microservices - Dive deeper into two key concepts: environments and virtual networking. Learn how microservices communicate in ACA, and walkthrough an example. (Tutorial 2: ACA with 3 Microservices)
    3. Scaling Your Container Apps - Learn about KEDA. Understand how to configure your ACA for auto-scaling with KEDA-supported triggers. Put this into action by walking through a tutorial. (Tutorial 3: Configure Autoscaling)
    4. Hello, Distributed Application Runtime (Dapr) - Learn about Dapr and how its Building Block APIs simplify microservices development with ACA. Know how the sidecar pattern enables incremental adoption of Dapr APIs without requiring any Dapr code integration in app. (Tutorial 4: Setup & Explore Dapr)
    5. Building ACA with Dapr - See how Dapr works with ACA by building a Dapr-enabled Azure Container App. Walk through a .NET tutorial using Pub/Sub and State Management APIs in an enterprise scenario. (Tutorial 5: Build ACA with Dapr)
    6. Managing Secrets With Dapr - We'll look at the Secrets API (a key Building Block of Dapr) and learn how it simplifies management of sensitive information in ACA.
    7. Microservices + Serverless On Azure - We recap Week 2 (Microservices) and set the stage for Week 3 ( Integrations) of Serverless September. Plus, self-study resources including ACA development tutorials in different languages.

    Ready? Let's go!


    Azure Container Apps!

    When building your application, your first decision is about where you host your application. The Azure Architecture Center has a handy chart to help you decide between choices like Azure Functions, Azure App Service, Azure Container Instances, Azure Container Apps and more. But if you are new to this space, you'll need a good understanding of the terms and concepts behind the services Today, we'll focus on Azure Container Apps (ACA) - so let's start with the fundamentals.

    Containerized App Defined

    A containerized app is one where the application components, dependencies, and configuration, are packaged into a single file (container image), which can be instantiated in an isolated runtime environment (container) that is portable across hosts (OS). This makes containers lightweight and scalable - and ensures that applications behave consistently on different host platforms.

    Container images can be shared via container registries (public or private) helping developers discover and deploy related apps with less effort. Scaling a containerized app can be as simple as activating more instances of its container image. However, this requires container orchestrators to automate the management of container apps for efficiency. Orchestrators use technologies like Kubernetes to support capabilities like workload scheduling, self-healing and auto-scaling on demand.

    Cloud-Native & Microservices

    Containers are seen as one of the 5 pillars of Cloud-Native app development, an approach where applications are designed explicitly to take advantage of the unique benefits of modern dynamic environments (involving public, private and hybrid clouds). Containers are particularly suited to serverless solutions based on microservices.

    • With serverless - developers use managed services instead of managing their own infrastructure. Services are typically event-driven and can be configured for autoscaling with rules tied to event triggers. Serverless is cost-effective, with developers paying only for the compute cycles and resources they use.
    • With microservices - developers compose their applications from independent components. Each component can be deployed in its own container, and scaled at that granularity. This simplifies component reuse (across apps) and maintainability (over time) - with developers evolving functionality at microservice (vs. app) levels.

    Hello, Azure Container Apps!

    Azure Container Apps is the managed service that helps you run containerized apps and microservices as a serverless compute solution, on Azure. You can:

    • deploy serverless API endpoints - autoscaled by HTTP request traffic
    • host background processing apps - autoscaled by CPU or memory load
    • handle event-driven processing - autoscaled by #messages in queue
    • run microservices - autoscaled by any KEDA-supported scaler.

    Want a quick intro to the topic? Start by watching the short video below - then read these two posts from our ZeroToHero series:


    Deploy Your First ACA

    Dev Options

    We typically have three options for development:

    • Use the Azure Portal - provision and deploy from a browser.
    • Use Visual Studio Code (with relevant extensions) - if you prefer an IDE
    • Using Azure CLI - if you prefer to build and deploy from command line.

    The documentation site has quickstarts for three contexts:

    For this quickstart, we'll go with the first option (sample image) so we can move quickly to core concepts. We'll leave the others as an exercise for you to explore.

    1. Setup Resources

    PRE-REQUISITES

    You need:

    • An Azure account with an active subscription
    • An installed Azure CLI

    Start by logging into Azure from the CLI. The command should launch a browser to complete the auth flow (or give you an option to take an alternative path).

    $ az login

    Successful authentication will result in extensive command-line output detailing the status of your subscription.

    Next, install the Azure Container Apps extension for the CLI

    $ az extension add --name containerapp --upgrade
    ...
    The installed extension 'containerapp' is in preview.

    Once successfully installed, register the Microsoft.App namespace.

    $ az provider register --namespace Microsoft.App

    Then set local environment variables in that terminal - and verify they are set correctly:

    $ RESOURCE_GROUP="my-container-apps"
    $ LOCATION="canadacentral"
    $ CONTAINERAPPS_ENVIRONMENT="my-environment"

    $ echo $LOCATION $RESOURCE_GROUP $CONTAINERAPPS_ENVIRONMENT
    canadacentral my-container-apps my-environment

    Now you can use Azure CLI to provision a resource group for this tutorial. Creating a resource group also makes it easier for us to delete/reclaim all resources used at the end of this tutorial.

    az group create \
    --name $RESOURCE_GROUP \
    --location $LOCATION
    Congratulations

    You completed the Setup step!

    On completion, the console should print out the details of the newly created resource group. You should also be able to visit the Azure Portal and find the newly-active my-container-apps resource group under your active subscription.

    2. Create Environment

    An environment is like the picket fence around your property. It creates a secure boundary that contains a group of container apps - such that all apps deployed to it share the same virtual network and logging resources.

    $ az containerapp env create \
    --name $CONTAINERAPPS_ENVIRONMENT \
    --resource-group $RESOURCE_GROUP \
    --location $LOCATION

    No Log Analytics workspace provided.
    Generating a Log Analytics workspace with name ...

    This can take a few minutes. When done, you will see the terminal display more details. You can also check the resource group in the portal and see that a Container Apps Environment and a Log Analytics Workspace are created for you as part of this step.

    You've got the fence set up. Now it's time to build your home - er, container app!

    3. Create Container App

    Here's the command we'll use to create our first Azure Container App. Note that the --image argument provides the link to a pre-existing containerapps-helloworld image.

    az containerapp create \
    --name my-container-app \
    --resource-group $RESOURCE_GROUP \
    --environment $CONTAINERAPPS_ENVIRONMENT \
    --image mcr.microsoft.com/azuredocs/containerapps-helloworld:latest \
    --target-port 80 \
    --ingress 'external' \
    --query properties.configuration.ingress.fqdn
    ...
    ...

    Container app created. Access your app at <URL>

    The --ingress property shows that the app is open to external requests; in other words, it is publicly visible at the <URL> that is printed out on the terminal on successsful completion of this step.

    4. Verify Deployment

    Let's see if this works. You can verify that your container app by visitng the URL returned above in your browser. You should see something like this!

    Container App Hello World

    You can also visit the Azure Portal and look under the created Resource Group. You should see a new Container App type of resource was created after this step.

    Congratulations

    You just created and deployed your first "Hello World" Azure Container App! This validates your local development environment setup and existence of a valid Azure subscription.

    5. Clean Up Your Resources

    It's good practice to clean up resources once you are done with a tutorial.

    THIS ACTION IS IRREVERSIBLE

    This command deletes the resource group we created above - and all resources in it. So make sure you specified the right name, then confirm deletion.

    $ az group delete --name $RESOURCE_GROUP
    Are you sure you want to perform this operation? (y/n):

    Note that you can also delete the resource group from the Azure Portal interface if that feels more comfortable. For now, we'll just use the Portal to verify that deletion occurred. If you had previously opened the Resource Group page for the created resource, just refresh it. You should see something like this:

    Resource Not Found


    Core Concepts

    COMING SOON

    An illustrated guide summarizing these concepts in a single sketchnote.

    We covered a lot today - we'll stop with a quick overview of core concepts behind Azure Container Apps, each linked to documentation for self-study. We'll dive into more details on some of these concepts in upcoming articles:

    • Environments - are the secure boundary around a group of container apps that are deployed in the same virtual network. They write logs to a shared Log Analytics workspace and can communicate seamlessly using Dapr, if used.
    • Containers refer to the container image deployed in the Azure Container App. They can use any runtime, programming language, or development stack - and be discovered using any public or private container registry. A container app can support multiple containers.
    • Revisions are immutable snapshots of an Azure Container App. The first revision is created when the ACA is first deployed, with new revisions created when redeployment occurs with revision-scope changes. Multiple revisions can run concurrently in an environment.
    • Application Lifecycle Management revolves around these revisions, with a container app having three phases: deployment, update and deactivation.
    • Microservices are independent units of functionality in Cloud-Native architectures. A single container app typically represents a single microservice, and can be composed from one or more containers. Microservices can now be scaled and upgraded indepedently, giving your application more flexbility and control.
    • Networking architecture consist of a virtual network (VNET) associated with the environment. Unless you provide a custom VNET at environment creation time, a default VNET is automatically created. The VNET configuration determines access (ingress, internal vs. external) and can influence auto-scaling choices (e.g., use HTTP Edge Proxy and scale based on number of HTTP requests).
    • Observability is about monitoring the health of your application and diagnosing it to improve reliability or performance. Azure Container Apps has a number of features - from Log streaming and Container console to integration with Azure Monitor - to provide a holistic view of application status over time.
    • Easy Auth is possible with built-in support for authentication and authorization including support for popular identity providers like Facebook, Google, Twitter and GitHub - alongside the Microsoft Identity Platform.

    Keep these terms in mind as we walk through more tutorials this week, to see how they find application in real examples. Finally, a note on Dapr, the Distributed Application Runtime that abstracts away many of the challenges posed by distributed systems - and lets you focus on your application logic.

    DAPR INTEGRATION MADE EASY

    Dapr uses a sidecar architecture, allowing Azure Container Apps to communicate with Dapr Building Block APIs over either gRPC or HTTP. Your ACA can be built to run with or without Dapr - giving you the flexibility to incrementally adopt specific APIs and unlock related capabilities as the need arises.

    In later articles this week, we'll do a deeper dive into Dapr and build our first Dapr-enable Azure Container App to get a better understanding of this integration.

    Exercise

    Congratulations! You made it! By now you should have a good idea of what Cloud-Native development means, why Microservices and Containers are important to that vision - and how Azure Container Apps helps simplify the building and deployment of microservices based applications using serverless architectures on Azure.

    Now it's your turn to reinforce learning by doing.

    Resources

    Three key resources to bookmark and explore:

    - - + + \ No newline at end of file diff --git a/blog/tags/microservices/page/2/index.html b/blog/tags/microservices/page/2/index.html index 6b3ce7a23f..891062870c 100644 --- a/blog/tags/microservices/page/2/index.html +++ b/blog/tags/microservices/page/2/index.html @@ -14,13 +14,13 @@ - - + +

    11 posts tagged with "microservices"

    View All Tags

    · 7 min read
    Brian Benz

    Welcome to Day 25 of #30DaysOfServerless!

    Azure Container Apps enable application code packaged in containers to run and scale without the overhead of managing cloud infrastructure and container orchestration. In this post I'll show you how to deploy a Java application running on Spring Boot in a container to Azure Container Registry and Azure Container Apps.


    What We'll Cover

    • Introduction to Deploying Java containers in the cloud
    • Step-by-step: Deploying to Azure Container Registry
    • Step-by-step: Deploying and running on Azure Container Apps
    • Resources: For self-study!


    Deploy Java containers to cloud

    We'll deploy a Java application running on Spring Boot in a container to Azure Container Registry and Azure Container Apps. Here are the main steps:

    • Create Azure Container Registry (ACR) on Azure portal
    • Create Azure Container App (ACA) on Azure portal.
    • Deploy code to Azure Container Registry from the Azure CLI.
    • Deploy container from ACR to ACA using the Azure portal.
    PRE-REQUISITES

    Sign in to Azure from the CLI using the az login command, and follow the prompts in your browser to complete the authentication process. Also, ensure you're running the latest version of the CLI by using the az upgrade command.

    1. Get Sample Code

    Fork and clone the sample GitHub repo to your local machine. Navigate to the and click Fork in the top-right corner of the page.

    The example code that we're using is a very basic containerized Spring Boot example. There are a lot more details to learn about Spring boot apps in docker, for a deep dive check out this Spring Boot Guide

    2. Run Sample Locally (Optional)

    If you have docker installed locally, you can optionally test the code on your local machine. Navigate to the root directory of the forked repository and run the following commands:

    docker build -t spring-boot-docker-aca .
    docker run -p 8080:8080 spring-boot-docker-aca

    Open a browser and go to https://localhost:8080. You should see this message:

    Hello Docker World

    That indicates the the Spring Boot app is successfully running locally in a docker container.

    Next, let's set up an Azure Container Registry an an Azure Container App and deploy this container to the cloud!


    3. Step-by-step: Deploy to ACR

    To create a container registry from the portal dashboard, Select Create a resource > Containers > Container Registry.

    Navigate to container registry in portal

    In the Basics tab, enter values for Resource group and Registry name. The registry name must be unique within Azure, and contain 5-50 alphanumeric characters. Create a new resource group in the West US location named spring-boot-docker-aca. Select the 'Basic' SKU.

    Keep the default values for the remaining settings. Then select Review + create, then Create. When the Deployment succeeded message appears, select the container registry in the portal.

    Note the registry server name ending with azurecr.io. You will use this in the following steps when you push and pull images with Docker.

    3.1 Log into registry using the Azure CLI

    Before pushing and pulling container images, you must log in to the registry instance. Sign into the Azure CLI on your local machine, then run the az acr login command. For this step, use the registry name, not the server name ending with azurecr.io.

    From the command line, type:

    az acr login --name myregistryname

    The command returns Login Succeeded once completed.

    3.2 Build & deploy with az acr build

    Next, we're going to deploy the docker container we created earlier using the AZ ACR Build command. AZ ACR Build creates a docker build from local code and pushes the container to Azure Container Registry if the build is successful.

    Go to your local clone of the spring-boot-docker-aca repo in the command line, type:

    az acr build --registry myregistryname --image spring-boot-docker-aca:v1 .

    3.3 List container images

    Once the AZ ACR Build command is complete, you should be able to view the container as a repository in the registry. In the portal, open your registry and select Repositories, then select the spring-boot-docker-aca repository you created with docker push. You should also see the v1 image under Tags.

    4. Deploy on ACA

    Now that we have an image in the Azure Container Registry, we can deploy it to Azure Container Apps. For the first deployment, we'll pull the container from our ACR as part of the ACA setup.

    4.1 Create a container app

    We'll create the container app at the same place that we created the container registry in the Azure portal. From the portal, select Create a resource > Containers > Container App. In the Basics tab, set these values:

    4.2 Enter project details

    SettingAction
    SubscriptionYour Azure subscription.
    Resource groupUse the spring-boot-docker-aca resource group
    Container app nameEnter spring-boot-docker-aca.

    4.3 Create an environment

    1. In the Create Container App environment field, select Create new.

    2. In the Create Container App Environment page on the Basics tab, enter the following values:

      SettingValue
      Environment nameEnter my-environment.
      RegionSelect westus3.
    3. Select OK.

    4. Select the Create button at the bottom of the Create Container App Environment page.

    5. Select the Next: App settings button at the bottom of the page.

    5. App settings tab

    The App settings tab is where you connect to the ACR and pull the repository image:

    SettingAction
    Use quickstart imageUncheck the checkbox.
    NameEnter spring-boot-docker-aca.
    Image sourceSelect Azure Container Registry
    RegistrySelect your ACR from the list.
    ImageSelect spring-boot-docker-aca from the list.
    Image TagSelect v1 from the list.

    5.1 Application ingress settings

    SettingAction
    IngressSelect Enabled.
    Ingress visibilitySelect External to publicly expose your container app.
    Target portEnter 8080.

    5.2 Deploy the container app

    1. Select the Review and create button at the bottom of the page.
    2. Select Create.

    Once the deployment is successfully completed, you'll see the message: Your deployment is complete.

    5.3 Verify deployment

    In the portal, go to the Overview of your spring-boot-docker-aca Azure Container App, and click on the Application Url. You should see this message in the browser:

    Hello Docker World

    That indicates the the Spring Boot app is running in a docker container in your spring-boot-docker-aca Azure Container App.

    Resources: For self-study!

    Once you have an understanding of the basics in ths post, there is so much more to learn!

    Thanks for stopping by!

    - - + + \ No newline at end of file diff --git a/blog/tags/microservices/page/3/index.html b/blog/tags/microservices/page/3/index.html index 2d051f0cad..e95a83711e 100644 --- a/blog/tags/microservices/page/3/index.html +++ b/blog/tags/microservices/page/3/index.html @@ -14,13 +14,13 @@ - - + +

    11 posts tagged with "microservices"

    View All Tags

    · 19 min read
    Alex Wolf

    Welcome to Day 24 of #30DaysOfServerless!

    We continue exploring E2E scenarios with this tutorial where you'll deploy a containerized ASP.NET Core 6.0 application to Azure Container Apps.

    The application consists of a front-end web app built using Blazor Server, as well as two Web API projects to manage data. These projects will exist as three separate containers inside of a shared container apps environment.


    What We'll Cover

    • Deploy ASP.NET Core 6.0 app to Azure Container Apps
    • Automate deployment workflows using GitHub Actions
    • Provision and deploy resources using Azure Bicep
    • Exercise: Try this yourself!
    • Resources: For self-study!


    Introduction

    Azure Container Apps enables you to run microservices and containerized applications on a serverless platform. With Container Apps, you enjoy the benefits of running containers while leaving behind the concerns of manually configuring cloud infrastructure and complex container orchestrators.

    In this tutorial, you'll deploy a containerized ASP.NET Core 6.0 application to Azure Container Apps. The application consists of a front-end web app built using Blazor Server, as well as two Web API projects to manage data. These projects will exist as three separate containers inside of a shared container apps environment.

    You will use GitHub Actions in combination with Bicep to deploy the application. These tools provide an approachable and sustainable solution for building CI/CD pipelines and working with Container Apps.

    PRE-REQUISITES

    Architecture

    In this tutorial, we'll setup a container app environment with a separate container for each project in the sample store app. The major components of the sample project include:

    • A Blazor Server front-end web app to display product information
    • A products API to list available products
    • An inventory API to determine how many products are in stock
    • GitHub Actions and Bicep templates to provision Azure resources and then build and deploy the sample app.

    You will explore these templates later in the tutorial.

    Public internet traffic should be proxied to the Blazor app. The back-end APIs should only be reachable via requests from the Blazor app inside the container apps environment. This setup can be achieved using container apps environment ingress configurations during deployment.

    An architecture diagram of the shopping app


    Project Sources

    Want to follow along? Fork the sample below. The tutorial can be completed with or without Dapr integration. Pick the path you feel comfortable in. Dapr provides various benefits that make working with Microservices easier - you can learn more in the docs. For this tutorial you will need GitHub and Azure CLI.

    PICK YOUR PATH

    To follow along with this tutorial, fork the relevant sample project below.

    You can run the app locally from Visual Studio:

    • Right click on the Blazor Store project and select Set as Startup Project.
    • Press the start button at the top of Visual Studio to run the app.
    • (Once running) start each API in the background by
    • right-clicking on the project node
    • selecting Debug --> Start without debugging.

    Once the Blazor app is running, you should see something like this:

    An architecture diagram of the shopping app


    Configuring Azure credentials

    In order to deploy the application to Azure through GitHub Actions, you first need to create a service principal. The service principal will allow the GitHub Actions process to authenticate to your Azure subscription to create resources and deploy code. You can learn more about Service Principals in the Azure CLI documentation. For this step you'll need to be logged into the Azure CLI.

    1) If you have not done so already, make sure to fork the sample project to your own GitHub account or organization.

    1) Once you have completed this step, create a service principal using the Azure CLI command below:

    ```azurecli
    $subscriptionId=$(az account show --query id --output tsv)
    az ad sp create-for-rbac --sdk-auth --name WebAndApiSample --role Contributor --scopes /subscriptions/$subscriptionId
    ```

    1) Copy the JSON output of the CLI command to your clipboard

    1) Under the settings tab of your forked GitHub repo, create a new secret named AzureSPN. The name is important to match the Bicep templates included in the project, which we'll review later. Paste the copied service principal values on your clipboard into the secret and save your changes. This new secret will be used by the GitHub Actions workflow to authenticate to Azure.

    :::image type="content" source="./img/dotnet/github-secrets.png" alt-text="A screenshot of adding GitHub secrets.":::

    Deploy using Github Actions

    You are now ready to deploy the application to Azure Container Apps using GitHub Actions. The sample application includes a GitHub Actions template that is configured to build and deploy any changes to a branch named deploy. The deploy branch does not exist in your forked repository by default, but you can easily create it through the GitHub user interface.

    1) Switch to the Actions tab along the top navigation of your GitHub repository. If you have not done so already, ensure that workflows are enabled by clicking the button in the center of the page.

    A screenshot showing how to enable GitHub actions

    1) Navigate to the main Code tab of your repository and select the main dropdown. Enter deploy into the branch input box, and then select Create branch: deploy from 'main'.

    A screenshot showing how to create the deploy branch

    1) On the new deploy branch, navigate down into the .github/workflows folder. You should see a file called deploy.yml, which contains the main GitHub Actions workflow script. Click on the file to view its content. You'll learn more about this file later in the tutorial.

    1) Click the pencil icon in the upper right to edit the document.

    1) Change the RESOURCE_GROUP_NAME: value to msdocswebappapis or another valid resource group name of your choosing.

    1) In the upper right of the screen, select Start commit and then Commit changes to commit your edit. This will persist the change to the file and trigger the GitHub Actions workflow to build and deploy the app.

    A screenshot showing how to commit changes

    1) Switch to the Actions tab along the top navigation again. You should see the workflow running to create the necessary resources and deploy the app. The workflow may take several minutes to run. When it completes successfully, all of the jobs should have a green checkmark icon next to them.

    The completed GitHub workflow.

    Explore the Azure resources

    Once the GitHub Actions workflow has completed successfully you can browse the created resources in the Azure portal.

    1) On the left navigation, select Resource Groups. Next,choose the msdocswebappapis resource group that was created by the GitHub Actions workflow.

    2) You should see seven resources available that match the screenshot and table descriptions below.

    The resources created in Azure.

    Resource nameTypeDescription
    inventoryContainer appThe containerized inventory API.
    msdocswebappapisacrContainer registryA registry that stores the built Container images for your apps.
    msdocswebappapisaiApplication insightsApplication insights provides advanced monitoring, logging and metrics for your apps.
    msdocswebappapisenvContainer apps environmentA container environment that manages networking, security and resource concerns. All of your containers live in this environment.
    msdocswebappapislogsLog Analytics workspaceA workspace environment for managing logging and analytics for the container apps environment
    productsContainer appThe containerized products API.
    storeContainer appThe Blazor front-end web app.

    3) You can view your running app in the browser by clicking on the store container app. On the overview page, click the Application Url link on the upper right of the screen.

    :::image type="content" source="./img/dotnet/application-url.png" alt-text="The link to browse the app.":::

    Understanding the GitHub Actions workflow

    The GitHub Actions workflow created and deployed resources to Azure using the deploy.yml file in the .github folder at the root of the project. The primary purpose of this file is to respond to events - such as commits to a branch - and run jobs to accomplish tasks. The deploy.yml file in the sample project has three main jobs:

    • Provision: Create the necessary resources in Azure, such as the container apps environment. This step leverages Bicep templates to create the Azure resources, which you'll explore in a moment.
    • Build: Create the container images for the three apps in the project and store them in the container registry.
    • Deploy: Deploy the container images to the different container apps created during the provisioning job.

    The deploy.yml file also accepts parameters to make the workflow more dynamic, such as setting the resource group name or the Azure region resources will be provisioned to.

    Below is a commented version of the deploy.yml file that highlights the essential steps.

    name: Build and deploy .NET application to Container Apps

    # Trigger the workflow on pushes to the deploy branch
    on:
    push:
    branches:
    - deploy

    env:
    # Set workflow variables
    RESOURCE_GROUP_NAME: msdocswebappapis

    REGION: eastus

    STORE_DOCKER: Store/Dockerfile
    STORE_IMAGE: store

    INVENTORY_DOCKER: Store.InventoryApi/Dockerfile
    INVENTORY_IMAGE: inventory

    PRODUCTS_DOCKER: Store.ProductApi/Dockerfile
    PRODUCTS_IMAGE: products

    jobs:
    # Create the required Azure resources
    provision:
    runs-on: ubuntu-latest

    steps:

    - name: Checkout to the branch
    uses: actions/checkout@v2

    - name: Azure Login
    uses: azure/login@v1
    with:
    creds: ${{ secrets.AzureSPN }}

    - name: Create resource group
    uses: azure/CLI@v1
    with:
    inlineScript: >
    echo "Creating resource group in Azure"
    echo "Executing 'az group create -l ${{ env.REGION }} -n ${{ env.RESOURCE_GROUP_NAME }}'"
    az group create -l ${{ env.REGION }} -n ${{ env.RESOURCE_GROUP_NAME }}

    # Use Bicep templates to create the resources in Azure
    - name: Creating resources
    uses: azure/CLI@v1
    with:
    inlineScript: >
    echo "Creating resources"
    az deployment group create --resource-group ${{ env.RESOURCE_GROUP_NAME }} --template-file '/github/workspace/Azure/main.bicep' --debug

    # Build the three app container images
    build:
    runs-on: ubuntu-latest
    needs: provision

    steps:

    - name: Checkout to the branch
    uses: actions/checkout@v2

    - name: Azure Login
    uses: azure/login@v1
    with:
    creds: ${{ secrets.AzureSPN }}

    - name: Set up Docker Buildx
    uses: docker/setup-buildx-action@v1

    - name: Login to ACR
    run: |
    set -euo pipefail
    access_token=$(az account get-access-token --query accessToken -o tsv)
    refresh_token=$(curl https://${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io/oauth2/exchange -v -d "grant_type=access_token&service=${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io&access_token=$access_token" | jq -r .refresh_token)
    docker login -u 00000000-0000-0000-0000-000000000000 --password-stdin ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io <<< "$refresh_token"

    - name: Build the products api image and push it to ACR
    uses: docker/build-push-action@v2
    with:
    push: true
    tags: ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io/${{ env.PRODUCTS_IMAGE }}:${{ github.sha }}
    file: ${{ env.PRODUCTS_DOCKER }}

    - name: Build the inventory api image and push it to ACR
    uses: docker/build-push-action@v2
    with:
    push: true
    tags: ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io/${{ env.INVENTORY_IMAGE }}:${{ github.sha }}
    file: ${{ env.INVENTORY_DOCKER }}

    - name: Build the frontend image and push it to ACR
    uses: docker/build-push-action@v2
    with:
    push: true
    tags: ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io/${{ env.STORE_IMAGE }}:${{ github.sha }}
    file: ${{ env.STORE_DOCKER }}

    # Deploy the three container images
    deploy:
    runs-on: ubuntu-latest
    needs: build

    steps:

    - name: Checkout to the branch
    uses: actions/checkout@v2

    - name: Azure Login
    uses: azure/login@v1
    with:
    creds: ${{ secrets.AzureSPN }}

    - name: Installing Container Apps extension
    uses: azure/CLI@v1
    with:
    inlineScript: >
    az config set extension.use_dynamic_install=yes_without_prompt

    az extension add --name containerapp --yes

    - name: Login to ACR
    run: |
    set -euo pipefail
    access_token=$(az account get-access-token --query accessToken -o tsv)
    refresh_token=$(curl https://${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io/oauth2/exchange -v -d "grant_type=access_token&service=${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io&access_token=$access_token" | jq -r .refresh_token)
    docker login -u 00000000-0000-0000-0000-000000000000 --password-stdin ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io <<< "$refresh_token"

    - name: Deploy Container Apps
    uses: azure/CLI@v1
    with:
    inlineScript: >
    az containerapp registry set -n products -g ${{ env.RESOURCE_GROUP_NAME }} --server ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io

    az containerapp update -n products -g ${{ env.RESOURCE_GROUP_NAME }} -i ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io/${{ env.PRODUCTS_IMAGE }}:${{ github.sha }}

    az containerapp registry set -n inventory -g ${{ env.RESOURCE_GROUP_NAME }} --server ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io

    az containerapp update -n inventory -g ${{ env.RESOURCE_GROUP_NAME }} -i ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io/${{ env.INVENTORY_IMAGE }}:${{ github.sha }}

    az containerapp registry set -n store -g ${{ env.RESOURCE_GROUP_NAME }} --server ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io

    az containerapp update -n store -g ${{ env.RESOURCE_GROUP_NAME }} -i ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io/${{ env.STORE_IMAGE }}:${{ github.sha }}

    - name: logout
    run: >
    az logout

    Understanding the Bicep templates

    During the provisioning stage of the GitHub Actions workflow, the main.bicep file is processed. Bicep files provide a declarative way of generating resources in Azure and are ideal for managing infrastructure as code. You can learn more about Bicep in the related documentation. The main.bicep file in the sample project creates the following resources:

    • The container registry to store images of the containerized apps.
    • The container apps environment, which handles networking and resource management for the container apps.
    • Three container apps - one for the Blazor front-end and two for the back-end product and inventory APIs.
    • Configuration values to connect these services together

    main.bicep without Dapr

    param location string = resourceGroup().location

    # create the azure container registry
    resource acr 'Microsoft.ContainerRegistry/registries@2021-09-01' = {
    name: toLower('${resourceGroup().name}acr')
    location: location
    sku: {
    name: 'Basic'
    }
    properties: {
    adminUserEnabled: true
    }
    }

    # create the aca environment
    module env 'environment.bicep' = {
    name: 'containerAppEnvironment'
    params: {
    location: location
    }
    }

    # create the various configuration pairs
    var shared_config = [
    {
    name: 'ASPNETCORE_ENVIRONMENT'
    value: 'Development'
    }
    {
    name: 'APPINSIGHTS_INSTRUMENTATIONKEY'
    value: env.outputs.appInsightsInstrumentationKey
    }
    {
    name: 'APPLICATIONINSIGHTS_CONNECTION_STRING'
    value: env.outputs.appInsightsConnectionString
    }
    ]

    # create the products api container app
    module products 'container_app.bicep' = {
    name: 'products'
    params: {
    name: 'products'
    location: location
    registryPassword: acr.listCredentials().passwords[0].value
    registryUsername: acr.listCredentials().username
    containerAppEnvironmentId: env.outputs.id
    registry: acr.name
    envVars: shared_config
    externalIngress: false
    }
    }

    # create the inventory api container app
    module inventory 'container_app.bicep' = {
    name: 'inventory'
    params: {
    name: 'inventory'
    location: location
    registryPassword: acr.listCredentials().passwords[0].value
    registryUsername: acr.listCredentials().username
    containerAppEnvironmentId: env.outputs.id
    registry: acr.name
    envVars: shared_config
    externalIngress: false
    }
    }

    # create the store api container app
    var frontend_config = [
    {
    name: 'ProductsApi'
    value: 'http://${products.outputs.fqdn}'
    }
    {
    name: 'InventoryApi'
    value: 'http://${inventory.outputs.fqdn}'
    }
    ]

    module store 'container_app.bicep' = {
    name: 'store'
    params: {
    name: 'store'
    location: location
    registryPassword: acr.listCredentials().passwords[0].value
    registryUsername: acr.listCredentials().username
    containerAppEnvironmentId: env.outputs.id
    registry: acr.name
    envVars: union(shared_config, frontend_config)
    externalIngress: true
    }
    }

    main.bicep with Dapr


    param location string = resourceGroup().location

    # create the azure container registry
    resource acr 'Microsoft.ContainerRegistry/registries@2021-09-01' = {
    name: toLower('${resourceGroup().name}acr')
    location: location
    sku: {
    name: 'Basic'
    }
    properties: {
    adminUserEnabled: true
    }
    }

    # create the aca environment
    module env 'environment.bicep' = {
    name: 'containerAppEnvironment'
    params: {
    location: location
    }
    }

    # create the various config pairs
    var shared_config = [
    {
    name: 'ASPNETCORE_ENVIRONMENT'
    value: 'Development'
    }
    {
    name: 'APPINSIGHTS_INSTRUMENTATIONKEY'
    value: env.outputs.appInsightsInstrumentationKey
    }
    {
    name: 'APPLICATIONINSIGHTS_CONNECTION_STRING'
    value: env.outputs.appInsightsConnectionString
    }
    ]

    # create the products api container app
    module products 'container_app.bicep' = {
    name: 'products'
    params: {
    name: 'products'
    location: location
    registryPassword: acr.listCredentials().passwords[0].value
    registryUsername: acr.listCredentials().username
    containerAppEnvironmentId: env.outputs.id
    registry: acr.name
    envVars: shared_config
    externalIngress: false
    }
    }

    # create the inventory api container app
    module inventory 'container_app.bicep' = {
    name: 'inventory'
    params: {
    name: 'inventory'
    location: location
    registryPassword: acr.listCredentials().passwords[0].value
    registryUsername: acr.listCredentials().username
    containerAppEnvironmentId: env.outputs.id
    registry: acr.name
    envVars: shared_config
    externalIngress: false
    }
    }

    # create the store api container app
    module store 'container_app.bicep' = {
    name: 'store'
    params: {
    name: 'store'
    location: location
    registryPassword: acr.listCredentials().passwords[0].value
    registryUsername: acr.listCredentials().username
    containerAppEnvironmentId: env.outputs.id
    registry: acr.name
    envVars: shared_config
    externalIngress: true
    }
    }


    Bicep Modules

    The main.bicep file references modules to create resources, such as module products. Modules are a feature of Bicep templates that enable you to abstract resource declarations into their own files or sub-templates. As the main.bicep file is processed, the defined modules are also evaluated. Modules allow you to create resources in a more organized and reusable way. They can also define input and output parameters that are passed to and from the parent template, such as the name of a resource.

    For example, the environment.bicep module extracts the details of creating a container apps environment into a reusable template. The module defines necessary resource dependencies such as Log Analytics Workspaces and an Application Insights instance.

    environment.bicep without Dapr

    param baseName string = resourceGroup().name
    param location string = resourceGroup().location

    resource logs 'Microsoft.OperationalInsights/workspaces@2021-06-01' = {
    name: '${baseName}logs'
    location: location
    properties: any({
    retentionInDays: 30
    features: {
    searchVersion: 1
    }
    sku: {
    name: 'PerGB2018'
    }
    })
    }

    resource appInsights 'Microsoft.Insights/components@2020-02-02' = {
    name: '${baseName}ai'
    location: location
    kind: 'web'
    properties: {
    Application_Type: 'web'
    WorkspaceResourceId: logs.id
    }
    }

    resource env 'Microsoft.App/managedEnvironments@2022-01-01-preview' = {
    name: '${baseName}env'
    location: location
    properties: {
    appLogsConfiguration: {
    destination: 'log-analytics'
    logAnalyticsConfiguration: {
    customerId: logs.properties.customerId
    sharedKey: logs.listKeys().primarySharedKey
    }
    }
    }
    }

    output id string = env.id
    output appInsightsInstrumentationKey string = appInsights.properties.InstrumentationKey
    output appInsightsConnectionString string = appInsights.properties.ConnectionString

    environment.bicep with Dapr


    param baseName string = resourceGroup().name
    param location string = resourceGroup().location

    resource logs 'Microsoft.OperationalInsights/workspaces@2021-06-01' = {
    name: '${baseName}logs'
    location: location
    properties: any({
    retentionInDays: 30
    features: {
    searchVersion: 1
    }
    sku: {
    name: 'PerGB2018'
    }
    })
    }

    resource appInsights 'Microsoft.Insights/components@2020-02-02' = {
    name: '${baseName}ai'
    location: location
    kind: 'web'
    properties: {
    Application_Type: 'web'
    WorkspaceResourceId: logs.id
    }
    }

    resource env 'Microsoft.App/managedEnvironments@2022-01-01-preview' = {
    name: '${baseName}env'
    location: location
    properties: {
    appLogsConfiguration: {
    destination: 'log-analytics'
    logAnalyticsConfiguration: {
    customerId: logs.properties.customerId
    sharedKey: logs.listKeys().primarySharedKey
    }
    }
    }
    }

    output id string = env.id
    output appInsightsInstrumentationKey string = appInsights.properties.InstrumentationKey
    output appInsightsConnectionString string = appInsights.properties.ConnectionString


    The container_apps.bicep template defines numerous parameters to provide a reusable template for creating container apps. This allows the module to be used in other CI/CD pipelines as well.

    container_app.bicep without Dapr

    param name string
    param location string = resourceGroup().location
    param containerAppEnvironmentId string
    param repositoryImage string = 'mcr.microsoft.com/azuredocs/containerapps-helloworld:latest'
    param envVars array = []
    param registry string
    param minReplicas int = 1
    param maxReplicas int = 1
    param port int = 80
    param externalIngress bool = false
    param allowInsecure bool = true
    param transport string = 'http'
    param registryUsername string
    @secure()
    param registryPassword string

    resource containerApp 'Microsoft.App/containerApps@2022-01-01-preview' ={
    name: name
    location: location
    properties:{
    managedEnvironmentId: containerAppEnvironmentId
    configuration: {
    activeRevisionsMode: 'single'
    secrets: [
    {
    name: 'container-registry-password'
    value: registryPassword
    }
    ]
    registries: [
    {
    server: registry
    username: registryUsername
    passwordSecretRef: 'container-registry-password'
    }
    ]
    ingress: {
    external: externalIngress
    targetPort: port
    transport: transport
    allowInsecure: allowInsecure
    }
    }
    template: {
    containers: [
    {
    image: repositoryImage
    name: name
    env: envVars
    }
    ]
    scale: {
    minReplicas: minReplicas
    maxReplicas: maxReplicas
    }
    }
    }
    }

    output fqdn string = containerApp.properties.configuration.ingress.fqdn

    container_app.bicep with Dapr


    param name string
    param location string = resourceGroup().location
    param containerAppEnvironmentId string
    param repositoryImage string = 'mcr.microsoft.com/azuredocs/containerapps-helloworld:latest'
    param envVars array = []
    param registry string
    param minReplicas int = 1
    param maxReplicas int = 1
    param port int = 80
    param externalIngress bool = false
    param allowInsecure bool = true
    param transport string = 'http'
    param appProtocol string = 'http'
    param registryUsername string
    @secure()
    param registryPassword string

    resource containerApp 'Microsoft.App/containerApps@2022-01-01-preview' ={
    name: name
    location: location
    properties:{
    managedEnvironmentId: containerAppEnvironmentId
    configuration: {
    dapr: {
    enabled: true
    appId: name
    appPort: port
    appProtocol: appProtocol
    }
    activeRevisionsMode: 'single'
    secrets: [
    {
    name: 'container-registry-password'
    value: registryPassword
    }
    ]
    registries: [
    {
    server: registry
    username: registryUsername
    passwordSecretRef: 'container-registry-password'
    }
    ]
    ingress: {
    external: externalIngress
    targetPort: port
    transport: transport
    allowInsecure: allowInsecure
    }
    }
    template: {
    containers: [
    {
    image: repositoryImage
    name: name
    env: envVars
    }
    ]
    scale: {
    minReplicas: minReplicas
    maxReplicas: maxReplicas
    }
    }
    }
    }

    output fqdn string = containerApp.properties.configuration.ingress.fqdn


    Understanding configuration differences with Dapr

    The code for this specific sample application is largely the same whether or not Dapr is integrated. However, even with this simple app, there are a few benefits and configuration differences when using Dapr that are worth exploring.

    In this scenario most of the changes are related to communication between the container apps. However, you can explore the full range of Dapr benefits by reading the Dapr integration with Azure Container Apps article in the conceptual documentation.

    Without Dapr

    Without Dapr the main.bicep template handles wiring up the front-end store app to communicate with the back-end apis by manually managing environment variables. The bicep template retrieves the fully qualified domains (fqdn) of the API apps as output parameters when they are created. Those configurations are then set as environment variables on the store container app.


    # Retrieve environment variables from API container creation
    var frontend_config = [
    {
    name: 'ProductsApi'
    value: 'http://${products.outputs.fqdn}'
    }
    {
    name: 'InventoryApi'
    value: 'http://${inventory.outputs.fqdn}'
    }
    ]

    # create the store api container app, passing in config
    module store 'container_app.bicep' = {
    name: 'store'
    params: {
    name: 'store'
    location: location
    registryPassword: acr.listCredentials().passwords[0].value
    registryUsername: acr.listCredentials().username
    containerAppEnvironmentId: env.outputs.id
    registry: acr.name
    envVars: union(shared_config, frontend_config)
    externalIngress: true
    }
    }

    The environment variables are then retrieved inside of the program class and used to configure the base URLs of the corresponding HTTP clients.


    builder.Services.AddHttpClient("Products", (httpClient) => httpClient.BaseAddress = new Uri(builder.Configuration.GetValue<string>("ProductsApi")));
    builder.Services.AddHttpClient("Inventory", (httpClient) => httpClient.BaseAddress = new Uri(builder.Configuration.GetValue<string>("InventoryApi")));

    With Dapr

    Dapr can be enabled on a container app when it is created, as seen below. This configuration adds a Dapr sidecar to the app to streamline discovery and communication features between the different container apps in your environment.


    # Create the container app with Dapr enabled
    resource containerApp 'Microsoft.App/containerApps@2022-01-01-preview' ={
    name: name
    location: location
    properties:{
    managedEnvironmentId: containerAppEnvironmentId
    configuration: {
    dapr: {
    enabled: true
    appId: name
    appPort: port
    appProtocol: appProtocol
    }
    activeRevisionsMode: 'single'
    secrets: [
    {
    name: 'container-registry-password'
    value: registryPassword
    }
    ]

    # Rest of template omitted for brevity...
    }
    }

    Some of these Dapr features can be surfaced through the program file. You can configure your HttpClient to leverage Dapr configurations when communicating with other apps in your environment.


    // reconfigure code to make requests to Dapr sidecar
    var baseURL = (Environment.GetEnvironmentVariable("BASE_URL") ?? "http://localhost") + ":" + (Environment.GetEnvironmentVariable("DAPR_HTTP_PORT") ?? "3500");
    builder.Services.AddHttpClient("Products", (httpClient) =>
    {
    httpClient.BaseAddress = new Uri(baseURL);
    httpClient.DefaultRequestHeaders.Add("dapr-app-id", "Products");
    });

    builder.Services.AddHttpClient("Inventory", (httpClient) =>
    {
    httpClient.BaseAddress = new Uri(baseURL);
    httpClient.DefaultRequestHeaders.Add("dapr-app-id", "Inventory");
    });


    Clean up resources

    If you're not going to continue to use this application, you can delete the Azure Container Apps and all the associated services by removing the resource group.

    Follow these steps in the Azure portal to remove the resources you created:

    1. In the Azure portal, navigate to the msdocswebappsapi resource group using the left navigation or search bar.
    2. Select the Delete resource group button at the top of the resource group Overview.
    3. Enter the resource group name msdocswebappsapi in the Are you sure you want to delete "msdocswebappsapi" confirmation dialog.
    4. Select Delete.
      The process to delete the resource group may take a few minutes to complete.
    - - + + \ No newline at end of file diff --git a/blog/tags/microservices/page/4/index.html b/blog/tags/microservices/page/4/index.html index a44300cf3b..3ea73ae90f 100644 --- a/blog/tags/microservices/page/4/index.html +++ b/blog/tags/microservices/page/4/index.html @@ -14,15 +14,15 @@ - - + +

    11 posts tagged with "microservices"

    View All Tags

    · 10 min read
    Ayca Bas

    Welcome to Day 20 of #30DaysOfServerless!

    Every day millions of people spend their precious time in productivity tools. What if you use data and intelligence behind the Microsoft applications (Microsoft Teams, Outlook, and many other Office apps) to build seamless automations and custom apps to boost productivity?

    In this post, we'll learn how to build a seamless onboarding experience for new employees joining a company with the power of Microsoft Graph, integrated with Event Hubs and Logic Apps!


    What We'll Cover

    • ✨ The power of Microsoft Graph
    • 🖇️ How do Microsoft Graph and Event Hubs work together?
    • 🛠 Let's Build an Onboarding Workflow!
      • 1️⃣ Setup Azure Event Hubs + Key Vault
      • 2️⃣ Subscribe to users, receive change notifications from Logic Apps
      • 3️⃣ Create Onboarding workflow in the Logic Apps
    • 🚀 Debug: Your onboarding experience
    • ✋ Exercise: Try this tutorial out yourself!
    • 📚 Resources: For Self-Study


    ✨ The Power of Microsoft Graph

    Microsoft Graph is the gateway to data and intelligence in Microsoft 365 platform. Microsoft Graph exploses Rest APIs and client libraries to access data across Microsoft 365 core services such as Calendar, Teams, To Do, Outlook, People, Planner, OneDrive, OneNote and more.

    Overview of Microsoft Graph

    You can build custom experiences by using Microsoft Graph such as automating the onboarding process for new employees. When new employees are created in the Azure Active Directory, they will be automatically added in the Onboarding team on Microsoft Teams.

    Solution architecture


    🖇️ Microsoft Graph with Event Hubs

    Microsoft Graph uses a webhook mechanism to track changes in resources and deliver change notifications to the clients. For example, with Microsoft Graph Change Notifications, you can receive change notifications when:

    • a new task is added in the to-do list
    • a user changes the presence status from busy to available
    • an event is deleted/cancelled from the calendar

    If you'd like to track a large set of resources at a high frequency, use Azure Events Hubs instead of traditional webhooks to receive change notifications. Azure Event Hubs is a popular real-time events ingestion and distribution service built for scale.

    EVENT GRID - PARTNER EVENTS

    Microsoft Graph Change Notifications can be also received by using Azure Event Grid -- currently available for Microsoft Partners! Read the Partner Events Overview documentation for details.

    Setup Azure Event Hubs + Key Vault.

    To get Microsoft Graph Change Notifications delivered to Azure Event Hubs, we'll have to setup Azure Event Hubs and Azure Key Vault. We'll use Azure Key Vault to access to Event Hubs connection string.

    1️⃣ Create Azure Event Hubs

    1. Go to Azure Portal and select Create a resource, type Event Hubs and select click Create.
    2. Fill in the Event Hubs namespace creation details, and then click Create.
    3. Go to the newly created Event Hubs namespace page, select Event Hubs tab from the left pane and + Event Hub:
      • Name your Event Hub as Event Hub
      • Click Create.
    4. Click the name of the Event Hub, and then select Shared access policies and + Add to add a new policy:
      • Give a name to the policy
      • Check Send and Listen
      • Click Create.
    5. After the policy has been created, click the name of the policy to open the details panel, and then copy the Connection string-primary key value. Write it down; you'll need it for the next step.
    6. Go to Consumer groups tab in the left pane and select + Consumer group, give a name for your consumer group as onboarding and select Create.

    2️⃣ Create Azure Key Vault

    1. Go to Azure Portal and select Create a resource, type Key Vault and select Create.
    2. Fill in the Key Vault creation details, and then click Review + Create.
    3. Go to newly created Key Vault and select Secrets tab from the left pane and click + Generate/Import:
      • Give a name to the secret
      • For the value, paste in the connection string you generated at the Event Hubs step
      • Click Create
      • Copy the name of the secret.
    4. Select Access Policies from the left pane and + Add Access Policy:
      • For Secret permissions, select Get
      • For Principal, select Microsoft Graph Change Tracking
      • Click Add.
    5. Select Overview tab from the left pane and copy the Vault URI.

    Subscribe for Logic Apps change notifications

    To start receiving Microsoft Graph Change Notifications, we'll need to create subscription to the resource that we'd like to track - here, 'users'. We'll use Azure Logic Apps to create subscription.

    To create subscription for Microsoft Graph Change Notifications, we'll need to make a http post request to https://graph.microsoft.com/v1.0/subscriptions. Microsoft Graph requires Azure Active Directory authentication make API calls. First, we'll need to register an app to Azure Active Directory, and then we will make the Microsoft Graph Subscription API call with Azure Logic Apps.

    1️⃣ Create an app in Azure Active Directory

    1. In the Azure Portal, go to Azure Active Directory and select App registrations from the left pane and select + New registration. Fill in the details for the new App registration form as below:
      • Name: Graph Subscription Flow Auth
      • Supported account types: Accounts in any organizational directory (Any Azure AD directory - Multitenant) and personal Microsoft accounts (e.g. Skype, Xbox)
      • Select Register.
    2. Go to newly registered app in Azure Active Directory, select API permissions:
      • Select + Add a permission and Microsoft Graph
      • Select Application permissions and add User.Read.All and Directory.Read.All.
      • Select Grant admin consent for the organization
    3. Select Certificates & secrets tab from the left pane, select + New client secret:
      • Choose desired expiry duration
      • Select Add
      • Copy the value of the secret.
    4. Go to Overview from the left pane, copy Application (client) ID and Directory (tenant) ID.

    2️⃣ Create subscription with Azure Logic Apps

    1. Go to Azure Portal and select Create a resource, type Logic apps and select click Create.

    2. Fill in the Logic Apps creation details, and then click Create.

    3. Go to the newly created Logic Apps page, select Workflows tab from the left pane and select + Add:

      • Give a name to the new workflow as graph-subscription-flow
      • Select Stateful as a state type
      • Click Create.
    4. Go to graph-subscription-flow, and then select Designer tab.

    5. In the Choose an operation section, search for Schedule and select Recurrence as a trigger. Fill in the parameters as below:

      • Interval: 61
      • Frequency: Minute
      • Time zone: Select your own time zone
      • Start time: Set a start time
    6. Select + button in the flow and select add an action. Search for HTTP and select HTTP as an action. Fill in the parameters as below:

      • Method: POST
      • URI: https://graph.microsoft.com/v1.0/subscriptions
      • Headers:
        • Key: Content-type
        • Value: application/json
      • Body:
      {
      "changeType": "created, updated",
      "clientState": "secretClientValue",
      "expirationDateTime": "@{addHours(utcNow(), 1)}",
      "notificationUrl": "EventHub:https://<YOUR-VAULT-URI>/secrets/<YOUR-KEY-VAULT-SECRET-NAME>?tenantId=72f988bf-86f1-41af-91ab-2d7cd011db47",
      "resource": "users"
      }

      In notificationUrl, make sure to replace <YOUR-VAULT-URI> with the vault uri and <YOUR-KEY-VAULT-SECRET-NAME> with the secret name that you copied from the Key Vault.

      In resource, define the resource type you'd like to track changes. For our example, we will track changes for users resource.

      • Authentication:
        • Authentication type: Active Directory OAuth
        • Authority: https://login.microsoft.com
        • Tenant: Directory (tenant) ID copied from AAD app
        • Audience: https://graph.microsoft.com
        • Client ID: Application (client) ID copied from AAD app
        • Credential Type: Secret
        • Secret: value of the secret copied from AAD app
    7. Select Save and run your workflow from the Overview tab.

      Check your subscription in Graph Explorer: If you'd like to make sure that your subscription is created successfully by Logic Apps, you can go to Graph Explorer, login with your Microsoft 365 account and make GET request to https://graph.microsoft.com/v1.0/subscriptions. Your subscription should appear in the response after it's created successfully.

    Subscription workflow success

    After subscription is created successfully by Logic Apps, Azure Event Hubs will receive notifications whenever there is a new user created in Azure Active Directory.


    Create Onboarding workflow in Logic Apps

    We'll create a second workflow in the Logic Apps to receive change notifications from Event Hubs when there is a new user created in the Azure Active Directory and add new user in Onboarding team on Microsoft Teams.

    1. Go to the Logic Apps you created in the previous steps, select Workflows tab and create a new workflow by selecting + Add:
      • Give a name to the new workflow as teams-onboarding-flow
      • Select Stateful as a state type
      • Click Create.
    2. Go to teams-onboarding-flow, and then select Designer tab.
    3. In the Choose an operation section, search for Event Hub, select When events are available in Event Hub as a trigger. Setup Event Hub connection as below:
      • Create Connection:
        • Connection name: Connection
        • Authentication Type: Connection String
        • Connection String: Go to Event Hubs > Shared Access Policies > RootManageSharedAccessKey and copy Connection string–primary key
        • Select Create.
      • Parameters:
        • Event Hub Name: Event Hub
        • Consumer Group Name: onboarding
    4. Select + in the flow and add an action, search for Control and add For each as an action. Fill in For each action as below:
      • Select output from previous steps: Events
    5. Inside For each, select + in the flow and add an action, search for Data operations and select Parse JSON. Fill in Parse JSON action as below:
      • Content: Events Content
      • Schema: Copy the json content from schema-parse.json and paste as a schema
    6. Select + in the flow and add an action, search for Control and add For each as an action. Fill in For each action as below:
      • Select output from previous steps: value
      1. Inside For each, select + in the flow and add an action, search for Microsoft Teams and select Add a member to a team. Login with your Microsoft 365 account to create a connection and fill in Add a member to a team action as below:
      • Team: Create an Onboarding team on Microsoft Teams and select
      • A user AAD ID for the user to add to a team: id
    7. Select Save.

    🚀 Debug your onboarding experience

    To debug our onboarding experience, we'll need to create a new user in Azure Active Directory and see if it's added in Microsoft Teams Onboarding team automatically.

    1. Go to Azure Portal and select Azure Active Directory from the left pane and go to Users. Select + New user and Create new user. Fill in the details as below:

      • User name: JaneDoe
      • Name: Jane Doe

      new user in Azure Active Directory

    2. When you added Jane Doe as a new user, it should trigger the teams-onboarding-flow to run. teams onboarding flow success

    3. Once the teams-onboarding-flow runs successfully, you should be able to see Jane Doe as a member of the Onboarding team on Microsoft Teams! 🥳 new member in Onboarding team on Microsoft Teams

    Congratulations! 🎉

    You just built an onboarding experience using Azure Logic Apps, Azure Event Hubs and Azure Key Vault.


    📚 Resources

    - - + + \ No newline at end of file diff --git a/blog/tags/microservices/page/5/index.html b/blog/tags/microservices/page/5/index.html index b6028d465d..f308a78378 100644 --- a/blog/tags/microservices/page/5/index.html +++ b/blog/tags/microservices/page/5/index.html @@ -14,14 +14,14 @@ - - + +

    11 posts tagged with "microservices"

    View All Tags

    · 10 min read
    Brian Benz

    Welcome to Day 18 of #30DaysOfServerless!

    Yesterday my Serverless September post introduced you to making Azure Logic Apps and Azure Cosmos DB work together with a sample application that collects weather data. Today I'm sharing a more robust solution that actually reads my mail. Let's learn about Teaching the cloud to read your mail!

    Ready? Let's go!


    What We'll Cover

    • Introduction to the ReadMail solution
    • Setting up Azure storage, Cosmos DB and Computer Vision
    • Connecting it all together with a Logic App
    • Resources: For self-study!


    Introducing the ReadMail solution

    The US Postal system offers a subscription service that sends you images of mail it will be delivering to your home. I decided it would be cool to try getting Azure to collect data based on these images, so that I could categorize my mail and track the types of mail that I received.

    To do this, I used Azure storage, Cosmos DB, Logic Apps, and computer vision. When a new email comes in from the US Postal service (USPS), it triggers a logic app that:

    • Posts attachments to Azure storage
    • Triggers Azure Computer vision to perform an OCR function on attachments
    • Extracts any results into a JSON document
    • Writes the JSON document to Cosmos DB

    workflow for the readmail solution

    In this post I'll walk you through setting up the solution for yourself.

    Prerequisites

    Setup Azure Services

    First, we'll create all of the target environments we need to be used by our Logic App, then we;ll create the Logic App.

    1. Azure Storage

    We'll be using Azure storage to collect attached images from emails as they arrive. Adding images to Azure storage will also trigger a workflow that performs OCR on new attached images and stores the OCR data in Cosmos DB.

    To create a new Azure storage account from the portal dashboard, Select Create a resource > Storage account > Create.

    The Basics tab covers all of the features and information that we will need for this solution:

    SectionFieldRequired or optionalDescription
    Project detailsSubscriptionRequiredSelect the subscription for the new storage account.
    Project detailsResource groupRequiredCreate a new resource group that you will use for storage, Cosmos DB, Computer Vision and the Logic App.
    Instance detailsStorage account nameRequiredChoose a unique name for your storage account. Storage account names must be between 3 and 24 characters in length and may contain numbers and lowercase letters only.
    Instance detailsRegionRequiredSelect the appropriate region for your storage account.
    Instance detailsPerformanceRequiredSelect Standard performance for general-purpose v2 storage accounts (default).
    Instance detailsRedundancyRequiredSelect locally-redundant Storage (LRS) for this example.

    Select Review + create to accept the remaining default options, then validate and create the account.

    2. Azure CosmosDB

    CosmosDB will be used to store the JSON documents returned by the COmputer Vision OCR process.

    See more details and screen shots for setting up CosmosDB in yesterday's Serverless September post - Using Logic Apps with Cosmos DB

    To get started with Cosmos DB, you create an account, then a database, then a container to store JSON documents. To create a new Cosmos DB account from the portal dashboard, Select Create a resource > Azure Cosmos DB > Create. Choose core SQL for the API.

    Select your subscription, then for simplicity use the same resource group you created when you set up storage. Enter an account name and choose a location, select provisioned throughput capacity mode and apply the free tier discount. From here you can select Review and Create, then Create

    Next, create a new database and container. Go to the Data Explorer in your new Cosmos DB account, and choose New Container. Name the database, and keep all the other defaults except:

    SettingAction
    Container IDid
    Container partition/id

    Press OK to create a database and container

    3. Azure Computer Vision

    Azure Cognitive Services' Computer Vision will perform an OCR process on each image attachment that is stored in Azure storage.

    From the portal dashboard, Select Create a resource > AI + Machine Learning > Computer Vision > Create.

    The Basics and Identity tabs cover all of the features and information that we will need for this solution:

    Basics Tab

    SectionFieldRequired or optionalDescription
    Project detailsSubscriptionRequiredSelect the subscription for the new service.
    Project detailsResource groupRequiredUse the same resource group that you used for Azure storage and Cosmos DB.
    Instance detailsRegionRequiredSelect the appropriate region for your Computer Vision service.
    Instance detailsNameRequiredChoose a unique name for your Computer Vision service.
    Instance detailsPricingRequiredSelect the free tier for this example.

    Identity Tab

    SectionFieldRequired or optionalDescription
    System assigned managed identityStatusRequiredEnable system assigned identity to grant the resource access to other existing resources.

    Select Review + create to accept the remaining default options, then validate and create the account.


    Connect it all with a Logic App

    Now we're ready to put this all together in a Logic App workflow!

    1. Create Logic App

    From the portal dashboard, Select Create a resource > Integration > Logic App > Create. Name your Logic App and select a location, the rest of the settings can be left at their defaults.

    2. Create Workflow: Add Trigger

    Once the Logic App is created, select Create a workflow from designer.

    A workflow is a series of steps that defines a task or process. Each workflow starts with a single trigger, after which you must add one or more actions.

    When in designer, search for outlook.com on the right under Add a trigger. Choose outlook.com. Choose When a new email arrives as the trigger.

    A trigger is always the first step in any workflow and specifies the condition for running any further steps in that workflow.

    Set the following values:

    ParameterValue
    FolderInbox
    ImportanceAny
    Only With AttachmentsYes
    Include AttachmentsYes

    Then add a new parameter:

    ParameterValue
    FromAdd the email address that sends you the email with attachments
    3. Create Workflow: Add Action (for Trigger)

    Choose add an action and choose control > for-each.

    logic app for each

    Inside the for-each action, in Select an output from previous steps, choose attachments. Then, again inside the for-each action, add the create blob action:

    Set the following values:

    ParameterValue
    Folder Path/mailreaderinbox
    Blob NameAttachments Name
    Blob ContentAttachments Content

    This extracts attachments from the email and created a new blob for each attachment.

    Next, inside the same for-each action, add the get blob content action.

    Set the following values:

    ParameterValue
    Blobid
    Infer content typeYes

    We create and read from a blob for each attachment because Computer Vision needs a non-virtual source to read from when performing an OCR process. Because we enabled system assigned identity to grant Computer Vision to other existing resources, it can access the blob but not the outlook.com attachment. Also, we pass the ID of the blob to use as a unique ID when writing to Cosmos DB.

    create blob from attachments

    Next, inside the same for-each action, choose add an action and choose control > condition. Set the value to Media Type > is equal to > image/JPEG

    The USPS sends attachments of multiple types, but we only want to scan attachments that have images of our mail, which are always JPEG images. If the condition is true, we will process the image with Computer Vision OCR and write the results to a JSON document in CosmosDB.

    In the True section of the condition, add an action and choose Computer Vision API > Optical Character Recognition (OCR) to JSON.

    Set the following values:

    ParameterValue
    Image SourceImage Content
    Image contentFile Content

    In the same True section of the condition, choose add an action and choose Cosmos DB. Choose Create or Update Document from the actions. Select Access Key, and provide the primary read-write key (found under keys in Cosmos DB), and the Cosmos DB account ID (without 'documents.azure.com').

    Next, fill in your Cosmos DB Database ID and Collection ID. Create a JSON document by selecting dynamic content elements and wrapping JSON formatting around them.

    Be sure to use the ID passed from blob storage as your unique ID for CosmosDB. That way you can troubleshoot and JSON or OCR issues by tracing back the JSON document in Cosmos Db to the blob in Azure storage. Also, include the Computer Vision JSON response, as it contains the results of the Computer Vision OCR scan. all other elements are optional.

    4. TEST WORKFLOW

    When complete, you should have an action the Logic App designer that looks something like this:

    Logic App workflow create or update document in cosmosdb

    Save the workflow and test the connections by clicking Run Trigger > Run. If connections are working, you should see documents flowing into Cosmos DB each time that an email arrives with image attachments.

    Check the data in Cosmos Db by opening the Data explorer, then choosing the container you created and selecting items. You should see documents similar to this:

    Logic App workflow with trigger and action

    1. Congratulations

    You just built your personal ReadMail solution with Logic Apps! 🎉


    Resources: For self-study!

    Once you have an understanding of the basics in ths post, there is so much more to learn!

    Thanks for stopping by!

    - - + + \ No newline at end of file diff --git a/blog/tags/microservices/page/6/index.html b/blog/tags/microservices/page/6/index.html index e1fdea22b0..9ce261c0b0 100644 --- a/blog/tags/microservices/page/6/index.html +++ b/blog/tags/microservices/page/6/index.html @@ -14,14 +14,14 @@ - - + +

    11 posts tagged with "microservices"

    View All Tags

    · 6 min read
    Brian Benz

    Welcome to Day 17 of #30DaysOfServerless!

    In past weeks, we've covered serverless technologies that provide core capabilities (functions, containers, microservices) for building serverless solutions. This week we're looking at technologies that make service integrations more seamless, starting with Logic Apps. Let's look at one usage example today!

    Ready? Let's Go!


    What We'll Cover

    • Introduction to Logic Apps
    • Settng up Cosmos DB for Logic Apps
    • Setting up a Logic App connection and event
    • Writing data to Cosmos DB from a Logic app
    • Resources: For self-study!


    Introduction to Logic Apps

    Previously in Serverless September, we've covered Azure Functions, where the event triggers code. In Logic Apps, the event triggers a workflow that you design. Logic Apps enable serverless applications to connect to external sources for data then automate business processes via workflows.

    In this post I'll walk you through setting up a Logic App that works with Cosmos DB. For this example, we'll connect to the MSN weather service, an design a logic app workflow that collects data when weather changes, and writes the data to Cosmos DB.

    PREREQUISITES

    Setup Cosmos DB for Logic Apps

    Cosmos DB has many APIs to choose from, but to use the default Logic App connection, we need to choose the a Cosmos DB SQL API. We'll set this up via the Azure Portal.

    To get started with Cosmos DB, you create an account, then a database, then a container to store JSON documents. To create a new Cosmos DB account from the portal dashboard, Select Create a resource > Azure Cosmos DB > Create. Choose core SQL for the API.

    Select your subscription, then create a new resource group called CosmosWeather. Enter an account name and choose a location, select provisioned throughput capacity mode and apply the free tier discount. From here you can select Review and Create, then Create

    Azure Cosmos DB is available in two different capacity modes: provisioned throughput and serverless. You can perform the same database operations in both modes, but the way you get billed for these operations is different. We wil be using provisioned throughput and the free tier for this example.

    Setup the CosmosDB account

    Next, create a new database and container. Go to the Data Explorer in your new Cosmos DB account, and choose New Container. Name the database, and keep all the orher defaults except:

    SettingAction
    Container IDid
    Container partition/id

    Press OK to create a database and container

    A database is analogous to a traditional DBMS namespace. It's used to organize one or more containers.

    Setup the CosmosDB Container

    Now we're ready to set up our logic app an write to Cosmos DB!

    Setup Logic App connection + event

    Once the Cosmos DB SQL API account is created, we can set up our Logic App. From the portal dashboard, Select Create a resource > Integration > Logic App > Create. Name your Logic App and select a location, the rest fo the settings can be left at their defaults. Once you new Logic App is created, select Create a workflow from designer to get started.

    A workflow is a series of steps that defines a task or process. Each workflow starts with a single trigger, after which you must add one or more actions.

    When in designer, search for weather on the right under Add a trigger. Choose MSN Weather. Choose When the current conditions change as the trigger.

    A trigger is always the first step in any workflow and specifies the condition for running any further steps in that workflow.

    Add a location. Valid locations are City, Region, State, Country, Landmark, Postal Code, latitude and longitude. This triggers a new workflow when the conditions change for a location.

    Write data from Logic App to Cosmos DB

    Now we are ready to set up the action to write data to Cosmos DB. Choose add an action and choose Cosmos DB.

    An action is each step in a workflow after the trigger. Every action runs some operation in a workflow.

    In this case, we will be writing a JSON document to the Cosmos DB container we created earlier. Choose Create or Update Document from the actions. At this point you should have a workflow in designer that looks something like this:

    Logic App workflow with trigger

    Start wth the connection for set up the Cosmos DB action. Select Access Key, and provide the primary read-write key (found under keys in Cosmos DB), and the Cosmos DB account ID (without 'documents.azure.com').

    Next, fill in your Cosmos DB Database ID and Collection ID. Create a JSON document bt selecting dynamic content elements and wrapping JSON formatting around them.

    You will need a unique ID for each document that you write to Cosmos DB, for that you can use an expression. Because we declared id to be our unique ID in Cosmos DB, we will use use that for the name. Under expressions, type guid() and press enter to add a unique ID to the JSON document. When complete, you should have a workflow in designer that looks something like this:

    Logic App workflow with trigger and action

    Save the workflow and test the connections by clicking Run Trigger > Run. If connections are working, you should see documents flowing into Cosmos DB over the next few minutes.

    Check the data in Cosmos Db by opening the Data explorer, then choosing the container you created and selecting items. You should see documents similar to this:

    Logic App workflow with trigger and action

    Resources: For self-study!

    Once you've grasped the basics in ths post, there is so much more to learn!

    Thanks for stopping by!

    - - + + \ No newline at end of file diff --git a/blog/tags/microservices/page/7/index.html b/blog/tags/microservices/page/7/index.html index 79e4e87ee9..923923a050 100644 --- a/blog/tags/microservices/page/7/index.html +++ b/blog/tags/microservices/page/7/index.html @@ -14,8 +14,8 @@ - - + +
    @@ -24,7 +24,7 @@ Image showing container apps role assignment

  • Lastly, we need to restart the container app revision, to do so run the command below:

     ##Get revision name and assign it to a variable
    $REVISION_NAME = (az containerapp revision list `
    --name $BACKEND_SVC_NAME `
    --resource-group $RESOURCE_GROUP `
    --query [0].name)

    ##Restart revision by name
    az containerapp revision restart `
    --resource-group $RESOURCE_GROUP `
    --name $BACKEND_SVC_NAME `
    --revision $REVISION_NAME
  • Run end-to-end Test on Azure

    From the Azure Portal, select the Azure Container App orders-processor and navigate to Log stream under Monitoring tab, leave the stream connected and opened. From the Azure Portal, select the Azure Service Bus Namespace ordersservices, select the topic orderreceivedtopic, select the subscription named orders-processor-subscription, then click on Service Bus Explorer (preview). From there we need to publish/send a message. Use the JSON payload below

    ```json
    {
    "data": {
    "reference": "Order 150",
    "quantity": 150,
    "createdOn": "2022-05-10T12:45:22.0983978Z"
    }
    }
    ```

    If all is configured correctly, you should start seeing the information logs in Container Apps Log stream, similar to the images below Image showing publishing messages from Azure Service

    Information logs on the Log stream of the deployed Azure Container App Image showing ACA Log Stream

    🎉 CONGRATULATIONS

    You have successfully deployed to the cloud an Azure Container App and configured Dapr Pub/Sub API with Azure Service Bus.

    9. Clean up

    If you are done with the tutorial, use the following command to delete the resource group and all its contained resources to avoid incurring further costs.

    az group delete --name $RESOURCE_GROUP

    Exercise

    I left for you the configuration of the Dapr State Store API with Azure Cosmos DB :)

    When you look at the action method OrderReceived in controller ExternalOrdersController, you will see that I left a line with ToDo: note, this line is responsible to save the received message (OrderModel) into Azure Cosmos DB.

    There is no need to change anything on the code base (other than removing this commented line), that's the beauty of Dapr Building Blocks and how easy it allows us to plug components to our microservice application without any plumping and brining external SDKs.

    For sure you need to work on the configuration part of Dapr State Store by creating a new component file like what we have done with the Pub/Sub API, things that you need to work on are:

    • Provision Azure Cosmos DB Account and obtain its masterKey.
    • Create a Dapr Component file adhering to Dapr Specs.
    • Create an Azure Container Apps component file adhering to ACA component specs.
    • Test locally on your dev machine using Dapr Component file.
    • Register the new Dapr State Store component with Azure Container Apps Environment and set the Cosmos Db masterKey from the Azure Portal. If you want to challenge yourself more, use the Managed Identity approach as done in this post! The right way to protect your keys and you will not worry about managing CosmosDb keys anymore!
    • Build a new image of the application and push it to Azure Container Registry.
    • Update Azure Container Apps and create a new revision which contains the updated code.
    • Verify the results by checking Azure Cosmos DB, you should see the Order Model stored in Cosmos DB.

    If you need help, you can always refer to my blog post Azure Container Apps State Store With Dapr State Management API which contains exactly what you need to implement here, so I'm very confident you will be able to complete this exercise with no issues, happy coding :)

    What's Next?

    If you enjoyed working with Dapr and Azure Container Apps, and you want to have a deep dive with more complex scenarios (Dapr bindings, service discovery, auto scaling with KEDA, sync services communication, distributed tracing, health probes, etc...) where multiple services deployed to a single Container App Environment; I have created a detailed tutorial which should walk you through step by step with through details to build the application.

    So far, the published posts below, and I'm publishing more posts on weekly basis, so stay tuned :)

    Resources

    - - + + \ No newline at end of file diff --git a/blog/tags/microservices/page/8/index.html b/blog/tags/microservices/page/8/index.html index 08924cd37c..6e35397bd3 100644 --- a/blog/tags/microservices/page/8/index.html +++ b/blog/tags/microservices/page/8/index.html @@ -14,14 +14,14 @@ - - + +

    11 posts tagged with "microservices"

    View All Tags

    · 11 min read
    Kendall Roden

    Welcome to Day 13 of #30DaysOfServerless!

    In the previous post, we learned about all things Distributed Application Runtime (Dapr) and highlighted the capabilities you can unlock through managed Dapr in Azure Container Apps! Today, we'll dive into how we can make use of Container Apps secrets and managed identities to securely access cloud-hosted resources that your Container Apps depend on!

    Ready? Let's go.


    What We'll Cover

    • Secure access to external services overview
    • Using Container Apps Secrets
    • Using Managed Identity for connecting to Azure resources
    • Using Dapr secret store component references (Dapr-only)
    • Conclusion
    • Resources: For self-study!


    Securing access to external services

    In most, if not all, microservice-based applications, one or more services in the system will rely on other cloud-hosted resources; Think external services like databases, secret stores, message brokers, event sources, etc. To interact with these services, an application must have the ability to establish a secure connection. Traditionally, an application will authenticate to these backing resources using some type of connection string or password.

    I'm not sure if it was just me, but one of the first things I learned as a developer was to ensure credentials and other sensitive information were never checked into the codebase. The ability to inject these values at runtime is a non-negotiable.

    In Azure Container Apps, applications can securely leverage connection information via Container Apps Secrets. If the resource is Azure-based, a more ideal solution that removes the dependence on secrets altogether is using Managed Identity.

    Specifically for Dapr-enabled container apps, users can now tap into the power of the Dapr secrets API! With this new capability unlocked in Container Apps, users can call the Dapr secrets API from application code to securely access secrets from Key Vault or other backing secret stores. In addition, customers can also make use of a secret store component reference when wiring up Dapr state store components and more!

    ALSO, I'm excited to share that support for Dapr + Managed Identity is now available!!. What does this mean? It means that you can enable Managed Identity for your container app - and when establishing connections via Dapr, the Dapr sidecar can use this identity! This means simplified components without the need for secrets when connecting to Azure services!

    Let's dive a bit deeper into the following three topics:

    1. Using Container Apps secrets in your container apps
    2. Using Managed Identity to connect to Azure services
    3. Connecting to services securely for Dapr-enabled apps

    Secure access to external services without Dapr

    Leveraging Container Apps secrets at runtime

    Users can leverage this approach for any values which need to be securely stored, however, it is recommended to use Managed Identity where possible when connecting to Azure-specific resources.

    First, let's establish a few important points regarding secrets in container apps:

    • Secrets are scoped at the container app level, meaning secrets cannot be shared across container apps today
    • When running in multiple-revision mode,
      • changes to secrets do not generate a new revision
      • running revisions will not be automatically restarted to reflect changes. If you want to force-update existing container app revisions to reflect the changed secrets values, you will need to perform revision restarts.
    STEP 1

    Provide the secure value as a secret parameter when creating your container app using the syntax "SECRET_NAME=SECRET_VALUE"

    az containerapp create \
    --resource-group "my-resource-group" \
    --name queuereader \
    --environment "my-environment-name" \
    --image demos/queuereader:v1 \
    --secrets "queue-connection-string=$CONNECTION_STRING"
    STEP 2

    Create an environment variable which references the value of the secret created in step 1 using the syntax "ENV_VARIABLE_NAME=secretref:SECRET_NAME"

    az containerapp create \
    --resource-group "my-resource-group" \
    --name myQueueApp \
    --environment "my-environment-name" \
    --image demos/myQueueApp:v1 \
    --secrets "queue-connection-string=$CONNECTIONSTRING" \
    --env-vars "QueueName=myqueue" "ConnectionString=secretref:queue-connection-string"

    This ConnectionString environment variable can be used within your application code to securely access the connection string value at runtime.

    Using Managed Identity to connect to Azure services

    A managed identity from Azure Active Directory (Azure AD) allows your container app to access other Azure AD-protected resources. This approach is recommended where possible as it eliminates the need for managing secret credentials in your container apps and allows you to properly scope the permissions needed for a given container app using role-based access control. Both system-assigned and user-assigned identities are available in container apps. For more background on managed identities in Azure AD, see Managed identities for Azure resources.

    To configure your app with a system-assigned managed identity you will follow similar steps to the following:

    STEP 1

    Run the following command to create a system-assigned identity for your container app

    az containerapp identity assign \
    --name "myQueueApp" \
    --resource-group "my-resource-group" \
    --system-assigned
    STEP 2

    Retrieve the identity details for your container app and store the Principal ID for the identity in a variable "PRINCIPAL_ID"

    az containerapp identity show \
    --name "myQueueApp" \
    --resource-group "my-resource-group"
    STEP 3

    Assign the appropriate roles and permissions to your container app's managed identity using the Principal ID in step 2 based on the resources you need to access (example below)

    az role assignment create \
    --role "Storage Queue Data Contributor" \
    --assignee $PRINCIPAL_ID \
    --scope "/subscriptions/<subscription>/resourceGroups/<resource-group>/providers/Microsoft.Storage/storageAccounts/<storage-account>/queueServices/default/queues/<queue>"

    After running the above commands, your container app will be able to access your Azure Store Queue because it's managed identity has been assigned the "Store Queue Data Contributor" role. The role assignments you create will be contingent solely on the resources your container app needs to access. To instrument your code to use this managed identity, see more details here.

    In addition to using managed identity to access services from your container app, you can also use managed identity to pull your container images from Azure Container Registry.

    Secure access to external services with Dapr

    For Dapr-enabled apps, there are a few ways to connect to the resources your solutions depend on. In this section, we will discuss when to use each approach.

    1. Using Container Apps secrets in your Dapr components
    2. Using Managed Identity with Dapr Components
    3. Using Dapr Secret Stores for runtime secrets and component references

    Using Container Apps secrets in Dapr components

    Prior to providing support for the Dapr Secret's Management building block, this was the only approach available for securely storing sensitive values for use in Dapr components.

    In Dapr OSS, when no secret store reference is provided in a Dapr component file, the default secret store is set to "Kubernetes secrets". In Container Apps, we do not expose the ability to use this default store. Rather, Container Apps secrets can be used in it's place.

    With the introduction of the Secrets API and the ability to use Dapr + Managed Identity, this approach is useful for a limited number of scenarios:

    • Quick demos and dev/test scenarios using the Container Apps CLI
    • Securing values when a secret store is not configured or available for use
    • Using service principal credentials to configure an Azure Key Vault secret store component (Using Managed Identity is recommend)
    • Securing access credentials which may be required when creating a non-Azure secret store component
    STEP 1

    Create a Dapr component which can be used by one or more services in the container apps environment. In the below example, you will create a secret to store the storage account key and reference this secret from the appropriate Dapr metadata property.

       componentType: state.azure.blobstorage
    version: v1
    metadata:
    - name: accountName
    value: testStorage
    - name: accountKey
    secretRef: account-key
    - name: containerName
    value: myContainer
    secrets:
    - name: account-key
    value: "<STORAGE_ACCOUNT_KEY>"
    scopes:
    - myApp
    STEP 2

    Deploy the Dapr component using the below command with the appropriate arguments.

     az containerapp env dapr-component set \
    --name "my-environment" \
    --resource-group "my-resource-group" \
    --dapr-component-name statestore \
    --yaml "./statestore.yaml"

    Using Managed Identity with Dapr Components

    Dapr-enabled container apps can now make use of managed identities within Dapr components. This is the most ideal path for connecting to Azure services securely, and allows for the removal of sensitive values in the component itself.

    The Dapr sidecar makes use of the existing identities available within a given container app; Dapr itself does not have it's own identity. Therefore, the steps to enable Dapr + MI are similar to those in the section regarding managed identity for non-Dapr apps. See example steps below specifically for using a system-assigned identity:

    1. Create a system-assigned identity for your container app

    2. Retrieve the identity details for your container app and store the Principal ID for the identity in a variable "PRINCIPAL_ID"

    3. Assign the appropriate roles and permissions (for accessing resources backing your Dapr components) to your ACA's managed identity using the Principal ID

    4. Create a simplified Dapr component without any secrets required

          componentType: state.azure.blobstorage
      version: v1
      metadata:
      - name: accountName
      value: testStorage
      - name: containerName
      value: myContainer
      scopes:
      - myApp
    5. Deploy the component to test the connection from your container app via Dapr!

    Keep in mind, all Dapr components will be loaded by each Dapr-enabled container app in an environment by default. In order to avoid apps without the appropriate permissions from loading a component unsuccessfully, use scopes. This will ensure that only applications with the appropriate identities to access the backing resource load the component.

    Using Dapr Secret Stores for runtime secrets and component references

    Dapr integrates with secret stores to provide apps and other components with secure storage and access to secrets such as access keys and passwords. The Dapr Secrets API is now available for use in Container Apps.

    Using Dapr’s secret store building block typically involves the following:

    • Setting up a component for a specific secret store solution.
    • Retrieving secrets using the Dapr secrets API in the application code.
    • Optionally, referencing secrets in Dapr component files.

    Let's walk through a couple sample workflows involving the use of Dapr's Secrets Management capabilities!

    Setting up a component for a specific secret store solution

    1. Create an Azure Key Vault instance for hosting the secrets required by your application.

      az keyvault create --name "<your-unique-keyvault-name>" --resource-group "my-resource-group" --location "<your-location>"
    2. Create an Azure Key Vault component in your environment without the secrets values, as the connection will be established to Azure Key Vault via Managed Identity.

          componentType: secretstores.azure.keyvault
      version: v1
      metadata:
      - name: vaultName
      value: "[your_keyvault_name]"
      scopes:
      - myApp
      az containerapp env dapr-component set \
      --name "my-environment" \
      --resource-group "my-resource-group" \
      --dapr-component-name secretstore \
      --yaml "./secretstore.yaml"
    3. Run the following command to create a system-assigned identity for your container app

      az containerapp identity assign \
      --name "myApp" \
      --resource-group "my-resource-group" \
      --system-assigned
    4. Retrieve the identity details for your container app and store the Principal ID for the identity in a variable "PRINCIPAL_ID"

      az containerapp identity show \
      --name "myApp" \
      --resource-group "my-resource-group"
    5. Assign the appropriate roles and permissions to your container app's managed identity to access Azure Key Vault

      az role assignment create \
      --role "Key Vault Secrets Officer" \
      --assignee $PRINCIPAL_ID \
      --scope /subscriptions/{subscriptionid}/resourcegroups/{resource-group-name}/providers/Microsoft.KeyVault/vaults/{key-vault-name}
    6. Begin using the Dapr Secrets API in your application code to retrieve secrets! See additional details here.

    Referencing secrets in Dapr component files

    Once a Dapr secret store component is available in the environment, it can be used to retrieve secrets for use in other components. For example, when creating a state store component, you can add a reference to the Dapr secret store from which you would like to source connection information. You will no longer use secrets directly in the component spec, but rather will instruct the Dapr sidecar to retrieve the secrets from the specified store.

          componentType: state.azure.blobstorage
    version: v1
    metadata:
    - name: accountName
    value: testStorage
    - name: accountKey
    secretRef: account-key
    - name: containerName
    value: myContainer
    secretStoreComponent: "<SECRET_STORE_COMPONENT_NAME>"
    scopes:
    - myApp

    Summary

    In this post, we have covered the high-level details on how to work with secret values in Azure Container Apps for both Dapr and Non-Dapr apps. In the next article, we will walk through a complex Dapr example from end-to-end which makes use of the new support for Dapr + Managed Identity. Stayed tuned for additional documentation around Dapr secrets as it will be release in the next two weeks!

    Resources

    Here are the main resources to explore for self-study:

    - - + + \ No newline at end of file diff --git a/blog/tags/microservices/page/9/index.html b/blog/tags/microservices/page/9/index.html index db70f39485..96557d3a71 100644 --- a/blog/tags/microservices/page/9/index.html +++ b/blog/tags/microservices/page/9/index.html @@ -14,13 +14,13 @@ - - + +

    11 posts tagged with "microservices"

    View All Tags

    · 8 min read
    Nitya Narasimhan

    Welcome to Day 12 of #30DaysOfServerless!

    So far we've looked at Azure Container Apps - what it is, how it enables microservices communication, and how it enables auto-scaling with KEDA compliant scalers. Today we'll shift gears and talk about Dapr - the Distributed Application Runtime - and how it makes microservices development with ACA easier with core building blocks and a sidecar architecture!

    Ready? Let's go!


    What We'll Cover

    • What is Dapr and why use it?
    • Building Block APIs
    • Dapr Quickstart and Tutorials
    • Dapr-enabled ACA: A Sidecar Approach
    • Exercise: Build & Deploy a Dapr-enabled ACA.
    • Resources: For self-study!


    Hello, Dapr!

    Building distributed applications is hard. Building reliable and portable microservces means having middleware that deals with challenges like service discovery, sync and async communications, state management, secure information sharing and more. Integrating these support services into your application can be challenging from both development and maintenance perspectives, adding complexity that is independent of the core application logic you want to focus on.

    This is where Dapr (Distributed Application Runtime) shines - it's defined as::

    a portable, event-driven runtime that makes it easy for any developer to build resilient, stateless and stateful applications that run on the cloud and edge and embraces the diversity of languages and developer frameworks.

    But what does this actually mean to me as an app developer?


    Dapr + Apps: A Sidecar Approach

    The strength of Dapr lies in its ability to:

    • abstract complexities of distributed systems middleware - with Building Block APIs that implement components using best practices to tackle key challenges.
    • implement a Sidecar Pattern with interactions via APIs - allowing applications to keep their codebase clean and focus on app logic.
    • be Incrementally Adoptable - allowing developers to start by integrating one API, then evolving to use more as and when needed.
    • be Platform Agnostic - allowing applications to be developed in a preferred language or framework without impacting integration capabilities.

    The application-dapr sidecar interaction is illustrated below. The API abstraction allows applications to get the desired functionality without having to know how it was implemented, or without having to integrate Dapr-specific code into their codebase. Note how the sidecar process listens on port 3500 and the API provides clear routes for the specific building blocks supported by Dapr (e.g, /secrets, /state etc.)


    Dapr Building Blocks: API Interactions

    Dapr Building Blocks refers to HTTP and gRPC endpoints exposed by Dapr API endpoints exposed by the Dapr sidecar, providing key capabilities like state management, observability, service-to-service invocation, pub/sub messaging and more to the associated application.

    Building Blocks: Under the Hood
    The Dapr API is implemented by modular components that codify best practices for tackling the specific challenge that they represent. The API abstraction allows component implementations to evolve, or alternatives to be used , without requiring changes to the application codebase.

    The latest Dapr release has the building blocks shown in the above figure. Not all capabilities are available to Azure Container Apps by default - check the documentation for the latest updates on this. For now, Azure Container Apps + Dapr integration provides the following capabilities to the application:

    In the next section, we'll dive into Dapr-enabled Azure Container Apps. Before we do that, here are a couple of resources to help you explore the Dapr platform by itself, and get more hands-on experience with the concepts and capabilities:

    • Dapr Quickstarts - build your first Dapr app, then explore quickstarts for a core APIs including service-to-service invocation, pub/sub, state mangement, bindings and secrets management.
    • Dapr Tutorials - go beyond the basic quickstart and explore more realistic service integrations and usage scenarios. Try the distributed calculator example!

    Integrate Dapr & Azure Container Apps

    Dapr currently has a v1.9 (preview) version, but Azure Container Apps supports Dapr v1.8. In this section, we'll look at what it takes to enable, configure, and use, Dapr integration with Azure Container Apps. It involves 3 steps: enabling Dapr using settings, configuring Dapr components (API) for use, then invoking the APIs.

    Here's a simple a publisher-subscriber scenario from the documentation. We have two Container apps identified as publisher-app and subscriber-app deployed in a single environment. Each ACA has an activated daprd sidecar, allowing them to use the Pub/Sub API to communicate asynchronously with each other - without having to write the underlying pub/sub implementation themselves. Rather, we can see that the Dapr API uses a pubsub,azure.servicebus component to implement that capability.

    Pub/sub example

    Let's look at how this is setup.

    1. Enable Dapr in ACA: Settings

    We can enable Dapr integration in the Azure Container App during creation by specifying settings in one of two ways, based on your development preference:

    • Using Azure CLI: use custom commandline options for each setting
    • Using Infrastructure-as-Code (IaC): using properties for Bicep, ARM templates

    Once enabled, Dapr will run in the same environment as the Azure Container App, and listen on port 3500 for API requests. The Dapr sidecar can be shared my multiple Container Apps deployed in the same environment.

    There are four main settings we will focus on for this demo - the example below shows the ARM template properties, but you can find the equivalent CLI parameters here for comparison.

    • dapr.enabled - enable Dapr for Azure Container App
    • dapr.appPort - specify port on which app is listening
    • dapr.appProtocol - specify if using http (default) or gRPC for API
    • dapr.appId - specify unique application ID for service discovery, usage

    These are defined under the properties.configuration section for your resource. Changing Dapr settings does not update the revision but it will restart ACA revisions and replicas. Here is what the relevant section of the ARM template looks like for the publisher-app ACA in the scenario shown above.

    "dapr": {
    "enabled": true,
    "appId": "publisher-app",
    "appProcotol": "http",
    "appPort": 80
    }

    2. Configure Dapr in ACA: Components

    The next step after activating the Dapr sidecar, is to define the APIs that you want to use and potentially specify the Dapr components (specific implementations of that API) that you prefer. These components are created at environment-level and by default, Dapr-enabled containers apps in an environment will load the complete set of deployed components -- use the scopes property to ensure only components needed by a given app are loaded at runtime. Here's what the ARM template resources section looks like for the example above. This tells us that the environment has a dapr-pubsub component of type pubsub.azure.servicebus deployed - where that component is loaded by container apps with dapr ids (publisher-app, subscriber-app).

    USING MANAGED IDENTITY + DAPR

    The secrets approach used here is idea for demo purposes. However, we recommend using Managed Identity with Dapr in production. For more details on secrets, check out tomorrow's post on Secrets and Managed Identity in Azure Container Apps

    {
    "resources": [
    {
    "type": "daprComponents",
    "name": "dapr-pubsub",
    "properties": {
    "componentType": "pubsub.azure.servicebus",
    "version": "v1",
    "secrets": [
    {
    "name": "sb-root-connectionstring",
    "value": "value"
    }
    ],
    "metadata": [
    {
    "name": "connectionString",
    "secretRef": "sb-root-connectionstring"
    }
    ],
    // Application scopes
    "scopes": ["publisher-app", "subscriber-app"]

    }
    }
    ]
    }

    With this configuration, the ACA is now set to use pub/sub capabilities from the Dapr sidecar, using standard HTTP requests to the exposed API endpoint for this service.

    Exercise: Deploy Dapr-enabled ACA

    In the next couple posts in this series, we'll be discussing how you can use the Dapr secrets API and doing a walkthrough of a more complex example, to show how Dapr-enabled Azure Container Apps are created and deployed.

    However, you can get hands-on experience with these concepts by walking through one of these two tutorials, each providing an alternative approach to configure and setup the application describe in the scenario below:

    Resources

    Here are the main resources to explore for self-study:

    - - + + \ No newline at end of file diff --git a/blog/tags/microsoft-365/index.html b/blog/tags/microsoft-365/index.html index 9b714e447a..4f62d5db90 100644 --- a/blog/tags/microsoft-365/index.html +++ b/blog/tags/microsoft-365/index.html @@ -14,15 +14,15 @@ - - + +

    One post tagged with "microsoft-365"

    View All Tags

    · 10 min read
    Ayca Bas

    Welcome to Day 20 of #30DaysOfServerless!

    Every day millions of people spend their precious time in productivity tools. What if you use data and intelligence behind the Microsoft applications (Microsoft Teams, Outlook, and many other Office apps) to build seamless automations and custom apps to boost productivity?

    In this post, we'll learn how to build a seamless onboarding experience for new employees joining a company with the power of Microsoft Graph, integrated with Event Hubs and Logic Apps!


    What We'll Cover

    • ✨ The power of Microsoft Graph
    • 🖇️ How do Microsoft Graph and Event Hubs work together?
    • 🛠 Let's Build an Onboarding Workflow!
      • 1️⃣ Setup Azure Event Hubs + Key Vault
      • 2️⃣ Subscribe to users, receive change notifications from Logic Apps
      • 3️⃣ Create Onboarding workflow in the Logic Apps
    • 🚀 Debug: Your onboarding experience
    • ✋ Exercise: Try this tutorial out yourself!
    • 📚 Resources: For Self-Study


    ✨ The Power of Microsoft Graph

    Microsoft Graph is the gateway to data and intelligence in Microsoft 365 platform. Microsoft Graph exploses Rest APIs and client libraries to access data across Microsoft 365 core services such as Calendar, Teams, To Do, Outlook, People, Planner, OneDrive, OneNote and more.

    Overview of Microsoft Graph

    You can build custom experiences by using Microsoft Graph such as automating the onboarding process for new employees. When new employees are created in the Azure Active Directory, they will be automatically added in the Onboarding team on Microsoft Teams.

    Solution architecture


    🖇️ Microsoft Graph with Event Hubs

    Microsoft Graph uses a webhook mechanism to track changes in resources and deliver change notifications to the clients. For example, with Microsoft Graph Change Notifications, you can receive change notifications when:

    • a new task is added in the to-do list
    • a user changes the presence status from busy to available
    • an event is deleted/cancelled from the calendar

    If you'd like to track a large set of resources at a high frequency, use Azure Events Hubs instead of traditional webhooks to receive change notifications. Azure Event Hubs is a popular real-time events ingestion and distribution service built for scale.

    EVENT GRID - PARTNER EVENTS

    Microsoft Graph Change Notifications can be also received by using Azure Event Grid -- currently available for Microsoft Partners! Read the Partner Events Overview documentation for details.

    Setup Azure Event Hubs + Key Vault.

    To get Microsoft Graph Change Notifications delivered to Azure Event Hubs, we'll have to setup Azure Event Hubs and Azure Key Vault. We'll use Azure Key Vault to access to Event Hubs connection string.

    1️⃣ Create Azure Event Hubs

    1. Go to Azure Portal and select Create a resource, type Event Hubs and select click Create.
    2. Fill in the Event Hubs namespace creation details, and then click Create.
    3. Go to the newly created Event Hubs namespace page, select Event Hubs tab from the left pane and + Event Hub:
      • Name your Event Hub as Event Hub
      • Click Create.
    4. Click the name of the Event Hub, and then select Shared access policies and + Add to add a new policy:
      • Give a name to the policy
      • Check Send and Listen
      • Click Create.
    5. After the policy has been created, click the name of the policy to open the details panel, and then copy the Connection string-primary key value. Write it down; you'll need it for the next step.
    6. Go to Consumer groups tab in the left pane and select + Consumer group, give a name for your consumer group as onboarding and select Create.

    2️⃣ Create Azure Key Vault

    1. Go to Azure Portal and select Create a resource, type Key Vault and select Create.
    2. Fill in the Key Vault creation details, and then click Review + Create.
    3. Go to newly created Key Vault and select Secrets tab from the left pane and click + Generate/Import:
      • Give a name to the secret
      • For the value, paste in the connection string you generated at the Event Hubs step
      • Click Create
      • Copy the name of the secret.
    4. Select Access Policies from the left pane and + Add Access Policy:
      • For Secret permissions, select Get
      • For Principal, select Microsoft Graph Change Tracking
      • Click Add.
    5. Select Overview tab from the left pane and copy the Vault URI.

    Subscribe for Logic Apps change notifications

    To start receiving Microsoft Graph Change Notifications, we'll need to create subscription to the resource that we'd like to track - here, 'users'. We'll use Azure Logic Apps to create subscription.

    To create subscription for Microsoft Graph Change Notifications, we'll need to make a http post request to https://graph.microsoft.com/v1.0/subscriptions. Microsoft Graph requires Azure Active Directory authentication make API calls. First, we'll need to register an app to Azure Active Directory, and then we will make the Microsoft Graph Subscription API call with Azure Logic Apps.

    1️⃣ Create an app in Azure Active Directory

    1. In the Azure Portal, go to Azure Active Directory and select App registrations from the left pane and select + New registration. Fill in the details for the new App registration form as below:
      • Name: Graph Subscription Flow Auth
      • Supported account types: Accounts in any organizational directory (Any Azure AD directory - Multitenant) and personal Microsoft accounts (e.g. Skype, Xbox)
      • Select Register.
    2. Go to newly registered app in Azure Active Directory, select API permissions:
      • Select + Add a permission and Microsoft Graph
      • Select Application permissions and add User.Read.All and Directory.Read.All.
      • Select Grant admin consent for the organization
    3. Select Certificates & secrets tab from the left pane, select + New client secret:
      • Choose desired expiry duration
      • Select Add
      • Copy the value of the secret.
    4. Go to Overview from the left pane, copy Application (client) ID and Directory (tenant) ID.

    2️⃣ Create subscription with Azure Logic Apps

    1. Go to Azure Portal and select Create a resource, type Logic apps and select click Create.

    2. Fill in the Logic Apps creation details, and then click Create.

    3. Go to the newly created Logic Apps page, select Workflows tab from the left pane and select + Add:

      • Give a name to the new workflow as graph-subscription-flow
      • Select Stateful as a state type
      • Click Create.
    4. Go to graph-subscription-flow, and then select Designer tab.

    5. In the Choose an operation section, search for Schedule and select Recurrence as a trigger. Fill in the parameters as below:

      • Interval: 61
      • Frequency: Minute
      • Time zone: Select your own time zone
      • Start time: Set a start time
    6. Select + button in the flow and select add an action. Search for HTTP and select HTTP as an action. Fill in the parameters as below:

      • Method: POST
      • URI: https://graph.microsoft.com/v1.0/subscriptions
      • Headers:
        • Key: Content-type
        • Value: application/json
      • Body:
      {
      "changeType": "created, updated",
      "clientState": "secretClientValue",
      "expirationDateTime": "@{addHours(utcNow(), 1)}",
      "notificationUrl": "EventHub:https://<YOUR-VAULT-URI>/secrets/<YOUR-KEY-VAULT-SECRET-NAME>?tenantId=72f988bf-86f1-41af-91ab-2d7cd011db47",
      "resource": "users"
      }

      In notificationUrl, make sure to replace <YOUR-VAULT-URI> with the vault uri and <YOUR-KEY-VAULT-SECRET-NAME> with the secret name that you copied from the Key Vault.

      In resource, define the resource type you'd like to track changes. For our example, we will track changes for users resource.

      • Authentication:
        • Authentication type: Active Directory OAuth
        • Authority: https://login.microsoft.com
        • Tenant: Directory (tenant) ID copied from AAD app
        • Audience: https://graph.microsoft.com
        • Client ID: Application (client) ID copied from AAD app
        • Credential Type: Secret
        • Secret: value of the secret copied from AAD app
    7. Select Save and run your workflow from the Overview tab.

      Check your subscription in Graph Explorer: If you'd like to make sure that your subscription is created successfully by Logic Apps, you can go to Graph Explorer, login with your Microsoft 365 account and make GET request to https://graph.microsoft.com/v1.0/subscriptions. Your subscription should appear in the response after it's created successfully.

    Subscription workflow success

    After subscription is created successfully by Logic Apps, Azure Event Hubs will receive notifications whenever there is a new user created in Azure Active Directory.


    Create Onboarding workflow in Logic Apps

    We'll create a second workflow in the Logic Apps to receive change notifications from Event Hubs when there is a new user created in the Azure Active Directory and add new user in Onboarding team on Microsoft Teams.

    1. Go to the Logic Apps you created in the previous steps, select Workflows tab and create a new workflow by selecting + Add:
      • Give a name to the new workflow as teams-onboarding-flow
      • Select Stateful as a state type
      • Click Create.
    2. Go to teams-onboarding-flow, and then select Designer tab.
    3. In the Choose an operation section, search for Event Hub, select When events are available in Event Hub as a trigger. Setup Event Hub connection as below:
      • Create Connection:
        • Connection name: Connection
        • Authentication Type: Connection String
        • Connection String: Go to Event Hubs > Shared Access Policies > RootManageSharedAccessKey and copy Connection string–primary key
        • Select Create.
      • Parameters:
        • Event Hub Name: Event Hub
        • Consumer Group Name: onboarding
    4. Select + in the flow and add an action, search for Control and add For each as an action. Fill in For each action as below:
      • Select output from previous steps: Events
    5. Inside For each, select + in the flow and add an action, search for Data operations and select Parse JSON. Fill in Parse JSON action as below:
      • Content: Events Content
      • Schema: Copy the json content from schema-parse.json and paste as a schema
    6. Select + in the flow and add an action, search for Control and add For each as an action. Fill in For each action as below:
      • Select output from previous steps: value
      1. Inside For each, select + in the flow and add an action, search for Microsoft Teams and select Add a member to a team. Login with your Microsoft 365 account to create a connection and fill in Add a member to a team action as below:
      • Team: Create an Onboarding team on Microsoft Teams and select
      • A user AAD ID for the user to add to a team: id
    7. Select Save.

    🚀 Debug your onboarding experience

    To debug our onboarding experience, we'll need to create a new user in Azure Active Directory and see if it's added in Microsoft Teams Onboarding team automatically.

    1. Go to Azure Portal and select Azure Active Directory from the left pane and go to Users. Select + New user and Create new user. Fill in the details as below:

      • User name: JaneDoe
      • Name: Jane Doe

      new user in Azure Active Directory

    2. When you added Jane Doe as a new user, it should trigger the teams-onboarding-flow to run. teams onboarding flow success

    3. Once the teams-onboarding-flow runs successfully, you should be able to see Jane Doe as a member of the Onboarding team on Microsoft Teams! 🥳 new member in Onboarding team on Microsoft Teams

    Congratulations! 🎉

    You just built an onboarding experience using Azure Logic Apps, Azure Event Hubs and Azure Key Vault.


    📚 Resources

    - - + + \ No newline at end of file diff --git a/blog/tags/microsoft-graph/index.html b/blog/tags/microsoft-graph/index.html index f64df83000..e64bd20cd7 100644 --- a/blog/tags/microsoft-graph/index.html +++ b/blog/tags/microsoft-graph/index.html @@ -14,15 +14,15 @@ - - + +

    One post tagged with "microsoft-graph"

    View All Tags

    · 10 min read
    Ayca Bas

    Welcome to Day 20 of #30DaysOfServerless!

    Every day millions of people spend their precious time in productivity tools. What if you use data and intelligence behind the Microsoft applications (Microsoft Teams, Outlook, and many other Office apps) to build seamless automations and custom apps to boost productivity?

    In this post, we'll learn how to build a seamless onboarding experience for new employees joining a company with the power of Microsoft Graph, integrated with Event Hubs and Logic Apps!


    What We'll Cover

    • ✨ The power of Microsoft Graph
    • 🖇️ How do Microsoft Graph and Event Hubs work together?
    • 🛠 Let's Build an Onboarding Workflow!
      • 1️⃣ Setup Azure Event Hubs + Key Vault
      • 2️⃣ Subscribe to users, receive change notifications from Logic Apps
      • 3️⃣ Create Onboarding workflow in the Logic Apps
    • 🚀 Debug: Your onboarding experience
    • ✋ Exercise: Try this tutorial out yourself!
    • 📚 Resources: For Self-Study


    ✨ The Power of Microsoft Graph

    Microsoft Graph is the gateway to data and intelligence in Microsoft 365 platform. Microsoft Graph exploses Rest APIs and client libraries to access data across Microsoft 365 core services such as Calendar, Teams, To Do, Outlook, People, Planner, OneDrive, OneNote and more.

    Overview of Microsoft Graph

    You can build custom experiences by using Microsoft Graph such as automating the onboarding process for new employees. When new employees are created in the Azure Active Directory, they will be automatically added in the Onboarding team on Microsoft Teams.

    Solution architecture


    🖇️ Microsoft Graph with Event Hubs

    Microsoft Graph uses a webhook mechanism to track changes in resources and deliver change notifications to the clients. For example, with Microsoft Graph Change Notifications, you can receive change notifications when:

    • a new task is added in the to-do list
    • a user changes the presence status from busy to available
    • an event is deleted/cancelled from the calendar

    If you'd like to track a large set of resources at a high frequency, use Azure Events Hubs instead of traditional webhooks to receive change notifications. Azure Event Hubs is a popular real-time events ingestion and distribution service built for scale.

    EVENT GRID - PARTNER EVENTS

    Microsoft Graph Change Notifications can be also received by using Azure Event Grid -- currently available for Microsoft Partners! Read the Partner Events Overview documentation for details.

    Setup Azure Event Hubs + Key Vault.

    To get Microsoft Graph Change Notifications delivered to Azure Event Hubs, we'll have to setup Azure Event Hubs and Azure Key Vault. We'll use Azure Key Vault to access to Event Hubs connection string.

    1️⃣ Create Azure Event Hubs

    1. Go to Azure Portal and select Create a resource, type Event Hubs and select click Create.
    2. Fill in the Event Hubs namespace creation details, and then click Create.
    3. Go to the newly created Event Hubs namespace page, select Event Hubs tab from the left pane and + Event Hub:
      • Name your Event Hub as Event Hub
      • Click Create.
    4. Click the name of the Event Hub, and then select Shared access policies and + Add to add a new policy:
      • Give a name to the policy
      • Check Send and Listen
      • Click Create.
    5. After the policy has been created, click the name of the policy to open the details panel, and then copy the Connection string-primary key value. Write it down; you'll need it for the next step.
    6. Go to Consumer groups tab in the left pane and select + Consumer group, give a name for your consumer group as onboarding and select Create.

    2️⃣ Create Azure Key Vault

    1. Go to Azure Portal and select Create a resource, type Key Vault and select Create.
    2. Fill in the Key Vault creation details, and then click Review + Create.
    3. Go to newly created Key Vault and select Secrets tab from the left pane and click + Generate/Import:
      • Give a name to the secret
      • For the value, paste in the connection string you generated at the Event Hubs step
      • Click Create
      • Copy the name of the secret.
    4. Select Access Policies from the left pane and + Add Access Policy:
      • For Secret permissions, select Get
      • For Principal, select Microsoft Graph Change Tracking
      • Click Add.
    5. Select Overview tab from the left pane and copy the Vault URI.

    Subscribe for Logic Apps change notifications

    To start receiving Microsoft Graph Change Notifications, we'll need to create subscription to the resource that we'd like to track - here, 'users'. We'll use Azure Logic Apps to create subscription.

    To create subscription for Microsoft Graph Change Notifications, we'll need to make a http post request to https://graph.microsoft.com/v1.0/subscriptions. Microsoft Graph requires Azure Active Directory authentication make API calls. First, we'll need to register an app to Azure Active Directory, and then we will make the Microsoft Graph Subscription API call with Azure Logic Apps.

    1️⃣ Create an app in Azure Active Directory

    1. In the Azure Portal, go to Azure Active Directory and select App registrations from the left pane and select + New registration. Fill in the details for the new App registration form as below:
      • Name: Graph Subscription Flow Auth
      • Supported account types: Accounts in any organizational directory (Any Azure AD directory - Multitenant) and personal Microsoft accounts (e.g. Skype, Xbox)
      • Select Register.
    2. Go to newly registered app in Azure Active Directory, select API permissions:
      • Select + Add a permission and Microsoft Graph
      • Select Application permissions and add User.Read.All and Directory.Read.All.
      • Select Grant admin consent for the organization
    3. Select Certificates & secrets tab from the left pane, select + New client secret:
      • Choose desired expiry duration
      • Select Add
      • Copy the value of the secret.
    4. Go to Overview from the left pane, copy Application (client) ID and Directory (tenant) ID.

    2️⃣ Create subscription with Azure Logic Apps

    1. Go to Azure Portal and select Create a resource, type Logic apps and select click Create.

    2. Fill in the Logic Apps creation details, and then click Create.

    3. Go to the newly created Logic Apps page, select Workflows tab from the left pane and select + Add:

      • Give a name to the new workflow as graph-subscription-flow
      • Select Stateful as a state type
      • Click Create.
    4. Go to graph-subscription-flow, and then select Designer tab.

    5. In the Choose an operation section, search for Schedule and select Recurrence as a trigger. Fill in the parameters as below:

      • Interval: 61
      • Frequency: Minute
      • Time zone: Select your own time zone
      • Start time: Set a start time
    6. Select + button in the flow and select add an action. Search for HTTP and select HTTP as an action. Fill in the parameters as below:

      • Method: POST
      • URI: https://graph.microsoft.com/v1.0/subscriptions
      • Headers:
        • Key: Content-type
        • Value: application/json
      • Body:
      {
      "changeType": "created, updated",
      "clientState": "secretClientValue",
      "expirationDateTime": "@{addHours(utcNow(), 1)}",
      "notificationUrl": "EventHub:https://<YOUR-VAULT-URI>/secrets/<YOUR-KEY-VAULT-SECRET-NAME>?tenantId=72f988bf-86f1-41af-91ab-2d7cd011db47",
      "resource": "users"
      }

      In notificationUrl, make sure to replace <YOUR-VAULT-URI> with the vault uri and <YOUR-KEY-VAULT-SECRET-NAME> with the secret name that you copied from the Key Vault.

      In resource, define the resource type you'd like to track changes. For our example, we will track changes for users resource.

      • Authentication:
        • Authentication type: Active Directory OAuth
        • Authority: https://login.microsoft.com
        • Tenant: Directory (tenant) ID copied from AAD app
        • Audience: https://graph.microsoft.com
        • Client ID: Application (client) ID copied from AAD app
        • Credential Type: Secret
        • Secret: value of the secret copied from AAD app
    7. Select Save and run your workflow from the Overview tab.

      Check your subscription in Graph Explorer: If you'd like to make sure that your subscription is created successfully by Logic Apps, you can go to Graph Explorer, login with your Microsoft 365 account and make GET request to https://graph.microsoft.com/v1.0/subscriptions. Your subscription should appear in the response after it's created successfully.

    Subscription workflow success

    After subscription is created successfully by Logic Apps, Azure Event Hubs will receive notifications whenever there is a new user created in Azure Active Directory.


    Create Onboarding workflow in Logic Apps

    We'll create a second workflow in the Logic Apps to receive change notifications from Event Hubs when there is a new user created in the Azure Active Directory and add new user in Onboarding team on Microsoft Teams.

    1. Go to the Logic Apps you created in the previous steps, select Workflows tab and create a new workflow by selecting + Add:
      • Give a name to the new workflow as teams-onboarding-flow
      • Select Stateful as a state type
      • Click Create.
    2. Go to teams-onboarding-flow, and then select Designer tab.
    3. In the Choose an operation section, search for Event Hub, select When events are available in Event Hub as a trigger. Setup Event Hub connection as below:
      • Create Connection:
        • Connection name: Connection
        • Authentication Type: Connection String
        • Connection String: Go to Event Hubs > Shared Access Policies > RootManageSharedAccessKey and copy Connection string–primary key
        • Select Create.
      • Parameters:
        • Event Hub Name: Event Hub
        • Consumer Group Name: onboarding
    4. Select + in the flow and add an action, search for Control and add For each as an action. Fill in For each action as below:
      • Select output from previous steps: Events
    5. Inside For each, select + in the flow and add an action, search for Data operations and select Parse JSON. Fill in Parse JSON action as below:
      • Content: Events Content
      • Schema: Copy the json content from schema-parse.json and paste as a schema
    6. Select + in the flow and add an action, search for Control and add For each as an action. Fill in For each action as below:
      • Select output from previous steps: value
      1. Inside For each, select + in the flow and add an action, search for Microsoft Teams and select Add a member to a team. Login with your Microsoft 365 account to create a connection and fill in Add a member to a team action as below:
      • Team: Create an Onboarding team on Microsoft Teams and select
      • A user AAD ID for the user to add to a team: id
    7. Select Save.

    🚀 Debug your onboarding experience

    To debug our onboarding experience, we'll need to create a new user in Azure Active Directory and see if it's added in Microsoft Teams Onboarding team automatically.

    1. Go to Azure Portal and select Azure Active Directory from the left pane and go to Users. Select + New user and Create new user. Fill in the details as below:

      • User name: JaneDoe
      • Name: Jane Doe

      new user in Azure Active Directory

    2. When you added Jane Doe as a new user, it should trigger the teams-onboarding-flow to run. teams onboarding flow success

    3. Once the teams-onboarding-flow runs successfully, you should be able to see Jane Doe as a member of the Onboarding team on Microsoft Teams! 🥳 new member in Onboarding team on Microsoft Teams

    Congratulations! 🎉

    You just built an onboarding experience using Azure Logic Apps, Azure Event Hubs and Azure Key Vault.


    📚 Resources

    - - + + \ No newline at end of file diff --git a/blog/tags/openapi/index.html b/blog/tags/openapi/index.html index bfea7a428c..e62698f452 100644 --- a/blog/tags/openapi/index.html +++ b/blog/tags/openapi/index.html @@ -14,13 +14,13 @@ - - + +

    One post tagged with "openapi"

    View All Tags

    · 14 min read
    Justin Yoo

    Welcome to Day 28 of #30DaysOfServerless!

    Since it's the serverless end-to-end week, I'm going to discuss how to use a serverless application Azure Functions with OpenAPI extension to be seamlessly integrated with Power Platform custom connector through Azure API Management - in a post I call "Where am I? My GPS Location with Serverless Power Platform Custom Connector"

    OK. Are you ready? Let's get started!


    What We'll Cover

    • What is Power Platform custom connector?
    • Proxy app to Google Maps and Naver Map API
    • API Management integration
    • Two ways of building custom connector
    • Where am I? Power Apps app
    • Exercise: Try this yourself!
    • Resources: For self-study!


    SAMPLE REPO

    Want to follow along? Check out the sample app on GitHub repository used in this post.

    What is Power Platform custom connector?

    Power Platform is a low-code/no-code application development tool for fusion teams that consist of a group of people. Those people come from various disciplines, including field experts (domain experts), IT professionals and professional developers, to draw business values successfully. Within the fusion team, the domain experts become citizen developers or low-code developers by Power Platform. In addition, Making Power Platform more powerful is that it offers hundreds of connectors to other Microsoft 365 and third-party services like SAP, ServiceNow, Salesforce, Google, etc.

    However, what if you want to use your internal APIs or APIs not yet offering their official connectors? Here's an example. If your company has an inventory management system, and you want to use it within your Power Apps or Power Automate. That point is exactly where Power Platform custom connectors is necessary.

    Inventory Management System for Power Apps

    Therefore, Power Platform custom connectors enrich those citizen developers' capabilities because those connectors can connect any API applications for the citizen developers to use.

    In this post, let's build a custom connector that provides a static map image generated by Google Maps API and Naver Map API using your GPS location.

    Proxy app to Google Maps and Naver Map API

    First, let's build an Azure Functions app that connects to Google Maps and Naver Map. Suppose that you've already got the API keys for both services. If you haven't yet, get the keys first by visiting here for Google and here for Naver. Then, store them to local.settings.json within your Azure Functions app.

    {
    "Values": {
    ...
    "Maps__Google__ApiKey": "<GOOGLE_MAPS_API_KEY>",
    "Maps__Naver__ClientId": "<NAVER_MAP_API_CLIENT_ID>",
    "Maps__Naver__ClientSecret": "<NAVER_MAP_API_CLIENT_SECRET>"
    }
    }

    Here's the sample logic to get the static image from Google Maps API. It takes the latitude and longitude of your current location and image zoom level, then returns the static map image. There are a few hard-coded assumptions, though:

    • The image size should be 400x400.
    • The image should be in .png format.
    • The marker should show be red and show my location.
    public class GoogleMapService : IMapService
    {
    public async Task<byte[]> GetMapAsync(HttpRequest req)
    {
    var latitude = req.Query["lat"];
    var longitude = req.Query["long"];
    var zoom = (string)req.Query["zoom"] ?? "14";

    var sb = new StringBuilder();
    sb.Append("https://maps.googleapis.com/maps/api/staticmap")
    .Append($"?center={latitude},{longitude}")
    .Append("&size=400x400")
    .Append($"&zoom={zoom}")
    .Append($"&markers=color:red|{latitude},{longitude}")
    .Append("&format=png32")
    .Append($"&key={this._settings.Google.ApiKey}");
    var requestUri = new Uri(sb.ToString());

    var bytes = await this._http.GetByteArrayAsync(requestUri).ConfigureAwait(false);

    return bytes;
    }
    }

    The NaverMapService class has a similar logic with the same input and assumptions. Here's the code:

    public class NaverMapService : IMapService
    {
    public async Task<byte[]> GetMapAsync(HttpRequest req)
    {
    var latitude = req.Query["lat"];
    var longitude = req.Query["long"];
    var zoom = (string)req.Query["zoom"] ?? "13";

    var sb = new StringBuilder();
    sb.Append("https://naveropenapi.apigw.ntruss.com/map-static/v2/raster")
    .Append($"?center={longitude},{latitude}")
    .Append("&w=400")
    .Append("&h=400")
    .Append($"&level={zoom}")
    .Append($"&markers=color:blue|pos:{longitude}%20{latitude}")
    .Append("&format=png")
    .Append("&lang=en");
    var requestUri = new Uri(sb.ToString());

    this._http.DefaultRequestHeaders.Clear();
    this._http.DefaultRequestHeaders.Add("X-NCP-APIGW-API-KEY-ID", this._settings.Naver.ClientId);
    this._http.DefaultRequestHeaders.Add("X-NCP-APIGW-API-KEY", this._settings.Naver.ClientSecret);

    var bytes = await this._http.GetByteArrayAsync(requestUri).ConfigureAwait(false);

    return bytes;
    }
    }

    Let's take a look at the function endpoints. Here's for the Google Maps and Naver Map. As the GetMapAsync(req) method returns a byte array value, you need to transform it as FileContentResult, with the content type of image/png.

    // Google Maps
    public class GoogleMapsTrigger
    {
    [FunctionName(nameof(GoogleMapsTrigger.GetGoogleMapImage))]
    public async Task<IActionResult> GetGoogleMapImage(
    [HttpTrigger(AuthorizationLevel.Anonymous, "GET", Route = "google/image")] HttpRequest req)
    {
    this._logger.LogInformation("C# HTTP trigger function processed a request.");

    var bytes = await this._service.GetMapAsync(req).ConfigureAwait(false);

    return new FileContentResult(bytes, "image/png");
    }
    }

    // Naver Map
    public class NaverMapsTrigger
    {
    [FunctionName(nameof(NaverMapsTrigger.GetNaverMapImage))]
    public async Task<IActionResult> GetNaverMapImage(
    [HttpTrigger(AuthorizationLevel.Anonymous, "GET", Route = "naver/image")] HttpRequest req)
    {
    this._logger.LogInformation("C# HTTP trigger function processed a request.");

    var bytes = await this._service.GetMapAsync(req).ConfigureAwait(false);

    return new FileContentResult(bytes, "image/png");
    }
    }

    Then, add the OpenAPI capability to each function endpoint. Here's the example:

    // Google Maps
    public class GoogleMapsTrigger
    {
    [FunctionName(nameof(GoogleMapsTrigger.GetGoogleMapImage))]
    // ⬇️⬇️⬇️ Add decorators provided by the OpenAPI extension ⬇️⬇️⬇️
    [OpenApiOperation(operationId: nameof(GoogleMapsTrigger.GetGoogleMapImage), tags: new[] { "google" })]
    [OpenApiParameter(name: "lat", In = ParameterLocation.Query, Required = true, Type = typeof(string), Description = "The **latitude** parameter")]
    [OpenApiParameter(name: "long", In = ParameterLocation.Query, Required = true, Type = typeof(string), Description = "The **longitude** parameter")]
    [OpenApiParameter(name: "zoom", In = ParameterLocation.Query, Required = false, Type = typeof(string), Description = "The **zoom level** parameter &ndash; Default value is `14`")]
    [OpenApiResponseWithBody(statusCode: HttpStatusCode.OK, contentType: "image/png", bodyType: typeof(byte[]), Description = "The map image as an OK response")]
    // ⬆️⬆️⬆️ Add decorators provided by the OpenAPI extension ⬆️⬆️⬆️
    public async Task<IActionResult> GetGoogleMapImage(
    [HttpTrigger(AuthorizationLevel.Anonymous, "GET", Route = "google/image")] HttpRequest req)
    {
    ...
    }
    }

    // Naver Map
    public class NaverMapsTrigger
    {
    [FunctionName(nameof(NaverMapsTrigger.GetNaverMapImage))]
    // ⬇️⬇️⬇️ Add decorators provided by the OpenAPI extension ⬇️⬇️⬇️
    [OpenApiOperation(operationId: nameof(NaverMapsTrigger.GetNaverMapImage), tags: new[] { "naver" })]
    [OpenApiParameter(name: "lat", In = ParameterLocation.Query, Required = true, Type = typeof(string), Description = "The **latitude** parameter")]
    [OpenApiParameter(name: "long", In = ParameterLocation.Query, Required = true, Type = typeof(string), Description = "The **longitude** parameter")]
    [OpenApiParameter(name: "zoom", In = ParameterLocation.Query, Required = false, Type = typeof(string), Description = "The **zoom level** parameter &ndash; Default value is `13`")]
    [OpenApiResponseWithBody(statusCode: HttpStatusCode.OK, contentType: "image/png", bodyType: typeof(byte[]), Description = "The map image as an OK response")]
    // ⬆️⬆️⬆️ Add decorators provided by the OpenAPI extension ⬆️⬆️⬆️
    public async Task<IActionResult> GetNaverMapImage(
    [HttpTrigger(AuthorizationLevel.Anonymous, "GET", Route = "naver/image")] HttpRequest req)
    {
    ...
    }
    }

    Run the function app in the local. Here are the latitude and longitude values for Seoul, Korea.

    • latitude: 37.574703
    • longitude: 126.978519

    Google Map for Seoul

    It seems to be working! Let's deploy it to Azure.

    API Management integration

    Visual Studio 2022 provides a built-in deployment tool for Azure Functions app onto Azure. In addition, the deployment tool supports seamless integration with Azure API Management as long as your Azure Functions app enables the OpenAPI capability. In this post, I'm going to use this feature. Right-mouse click on the Azure Functions project and select the "Publish" menu.

    Visual Studio context menu for publish

    Then, you will see the publish screen. Click the "➕ New" button to create a new publish profile.

    Create a new publish profile

    Choose "Azure" and click the "Next" button.

    Choose the target platform for publish

    Select the app instance. This time simply pick up the "Azure Function App (Windows)" option, then click "Next".

    Choose the target OS for publish

    If you already provision an Azure Function app instance, you will see it on the screen. Otherwise, create a new one. Then, click "Next".

    Choose the target instance for publish

    In the next step, you are asked to choose the Azure API Management instance for integration. Choose one, or create a new one. Then, click "Next".

    Choose the APIM instance for integration

    Finally, select the publish method either local publish or GitHub Actions workflow. Let's pick up the local publish method for now. Then, click "Finish".

    Choose the deployment type

    The publish profile has been created. Click "Close" to move on.

    Publish profile created

    Now the function app is ready for deployment. Click the "Publish" button and see how it goes.

    Publish function app

    The Azure function app has been deployed and integrated with the Azure API Management instance.

    Function app published

    Go to the published function app site, and everything looks OK.

    Function app on Azure

    And API Management shows the function app integrated perfectly.

    Function app integrated with APIM

    Now, you are ready to create a custom connector. Let's move on.

    Two ways of building custom connector

    There are two ways to create a custom connector.

    Export custom connector from API Management

    First, you can directly use the built-in API Management feature. Then, click the ellipsis icon and select the "Create Power Connector" menu.

    Create Power Connector menu

    Then, you are redirected to this screen. While the "API" and "API display name" fields are pre-populated, you need to choose the Power Platform environment tied to your tenant. Choose an environment, click "Authenticate", and click "Create".

    Create custom connector screen

    Check your custom connector on Power Apps or Power Automate side.

    Custom connector created on Power Apps

    However, there's a caveat to this approach. Because it's tied to your tenant, you should use the second approach if you want to use this custom connector on the other tenant.

    Import custom connector from OpenAPI document or URL

    Click the ellipsis icon again and select the "Export" menu.

    Export menu

    On the Export API screen, choose the "OpenAPI v2 (JSON)" panel because Power Platform custom connector currently accepts version 2 of the OpenAPI document.

    Select OpenAPI v2

    Download the OpenAPI document to your local computer and move to your Power Apps or Power Automate page under your desired environment. I'm going to use the Power Automate page. First, go to the "Data" ➡️ "Custom connectors" page. Then, click the "➕ New custom connector" ➡️ "Import an OpenAPI file" at the top right corner.

    New custom connector

    When a modal pops up, give the custom connector name and import the OpenAPI document exported above. Then, click "Continue".

    Import custom connector

    Actually, that's it! Next, click the "✔️ Create connector" button to create the connector.

    Create custom connector

    Go back to the custom connector page, and you will see the "Maps API" custom connector you just created.

    Custom connector imported

    So, you are ready to create a Power Apps app to display your location on Google Maps or Naver Map! Let's move on.

    Where am I? Power Apps app

    Open the Power Apps Studio, and create an empty canvas app, named Who am I with a phone layout.

    Custom connector integration

    To use the custom connector created above, you need to add it to the Power App. Click the cylinder icon on the left and click the "Add data" button.

    Add custom connector to data pane

    Search the custom connector name, "Maps API", and click the custom connector to add.

    Search custom connector

    To use the custom connector, you also need to create a connection to it. Click the "Connect" button and move on.

    Create connection to custom connector

    Now, you've got the connection to the custom connector.

    Connection to custom connector ready

    Controls

    Let's build the Power Apps app. First of all, put three controls Image, Slider and Button onto the canvas.

    Power Apps control added

    Click the "Screen1" control and change the value on the property "OnVisible" to the formula below. The formula stores the current slider value in the zoomlevel collection.

    ClearCollect(
    zoomlevel,
    Slider1.Value
    )

    Click the "Botton1" control and change the value on the property "OnSelected" to the formula below. It passes the current latitude, longitude and zoom level to the custom connector and receives the image data. The received image data is stored in the result collection.

    ClearCollect(
    result,
    MAPS.GetGoogleMapImage(
    Location.Latitude,
    Location.Longitude,
    { zoom: First(zoomlevel).Value }
    )
    )

    Click the "Image1" control and change the value on the property "Image" to the formula below. It gets the image data from the result collection.

    First(result).Url

    Click the "Slider1" control and change the value on the property "OnChange" to the formula below. It stores the current slider value to the zoomlevel collection, followed by calling the custom connector to get the image data against the current location.

    ClearCollect(
    zoomlevel,
    Slider1.Value
    );
    ClearCollect(
    result,
    MAPS.GetGoogleMapImage(
    Location.Latitude,
    Location.Longitude,
    { zoom: First(zoomlevel).Value }
    )
    )

    That seems to be OK. Let's click the "Where am I?" button. But it doesn't show the image. The First(result).Url value is actually similar to this:

    appres://blobmanager/1090a86393a843adbfcf428f0b90e91b/1

    It's the image reference value somewhere you can't get there.

    Workaround Power Automate workflow

    Therefore, you need a workaround using a Power Automate workflow to sort out this issue. Open the Power Automate Studio, create an instant cloud flow with the Power App trigger, and give it the "Where am I" name. Then add input parameters of lat, long and zoom.

    Power Apps trigger on Power Automate workflow

    Add custom connector action to get the map image.

    Select action to get the Google Maps image

    In the action, pass the appropriate parameters to the action.

    Pass parameters to the custom connector action

    Add a "Response" action and put the following values into each field.

    • "Body" field:

      {
      "base64Image": <power_automate_expression>
      }

      The <power_automate_expression> should be concat('data:', body('GetGoogleMapImage')?['$content-type'], ';base64,', body('GetGoogleMapImage')?['$content']).

    • "Response Body JSON Schema" field:

      {
      "type": "object",
      "properties": {
      "base64Image": {
      "type": "string"
      }
      }
      }

    Format the Response action

    Let's return to the Power Apps Studio and add the Power Automate workflow you created.

    Add Power Automate workflow

    Select "Button1" and change the value on the property "OnSelect" below. It replaces the direct call to the custom connector with the Power Automate workflow.

    ClearCollect(
    result,
    WhereamI.Run(
    Location.Latitude,
    Location.Longitude,
    First(zoomlevel).Value
    )
    )

    Also, change the value on the property "OnChange" of the "Slider1" control below, replacing the custom connector call with the Power Automate workflow call.

    ClearCollect(
    zoomlevel,
    Slider1.Value
    );
    ClearCollect(
    result,
    WhereamI.Run(
    Location.Latitude,
    Location.Longitude,
    First(zoomlevel).Value
    )
    )

    And finally, change the "Image1" control's "Image" property value below.

    First(result).base64Image

    The workaround has been applied. Click the "Where am I?" button to see your current location from Google Maps.

    Run Power Apps app #1

    If you change the slider left or right, you will see either the zoomed-in image or the zoomed-out image.

    Run Power Apps app #2

    Now, you've created a Power Apps app to show your current location using:

    • Google Maps API through the custom connector, and
    • Custom connector written in Azure Functions with OpenAPI extension!

    Exercise: Try this yourself!

    You can fork this GitHub repository to your account and play around with it to see how the custom connector works. After forking the repository, make sure that you create all the necessary secrets to your repository documented in the README file.

    Then, click the "Deploy to Azure" button, and it will provision all necessary Azure resources and deploy an Azure Functions app for a custom connector.

    Deploy To Azure

    Once everything is deployed successfully, try to create a Power Apps app and Power Automate workflow to see your current location in real-time!

    Resources: For self-study!

    Want to know more about Power Platform custom connector and Azure Functions OpenAPI extension? Here are several resources you can take a look at:

    - - + + \ No newline at end of file diff --git a/blog/tags/power-platform/index.html b/blog/tags/power-platform/index.html index a710eddc25..42e30efbb2 100644 --- a/blog/tags/power-platform/index.html +++ b/blog/tags/power-platform/index.html @@ -14,13 +14,13 @@ - - + +

    One post tagged with "power-platform"

    View All Tags

    · 14 min read
    Justin Yoo

    Welcome to Day 28 of #30DaysOfServerless!

    Since it's the serverless end-to-end week, I'm going to discuss how to use a serverless application Azure Functions with OpenAPI extension to be seamlessly integrated with Power Platform custom connector through Azure API Management - in a post I call "Where am I? My GPS Location with Serverless Power Platform Custom Connector"

    OK. Are you ready? Let's get started!


    What We'll Cover

    • What is Power Platform custom connector?
    • Proxy app to Google Maps and Naver Map API
    • API Management integration
    • Two ways of building custom connector
    • Where am I? Power Apps app
    • Exercise: Try this yourself!
    • Resources: For self-study!


    SAMPLE REPO

    Want to follow along? Check out the sample app on GitHub repository used in this post.

    What is Power Platform custom connector?

    Power Platform is a low-code/no-code application development tool for fusion teams that consist of a group of people. Those people come from various disciplines, including field experts (domain experts), IT professionals and professional developers, to draw business values successfully. Within the fusion team, the domain experts become citizen developers or low-code developers by Power Platform. In addition, Making Power Platform more powerful is that it offers hundreds of connectors to other Microsoft 365 and third-party services like SAP, ServiceNow, Salesforce, Google, etc.

    However, what if you want to use your internal APIs or APIs not yet offering their official connectors? Here's an example. If your company has an inventory management system, and you want to use it within your Power Apps or Power Automate. That point is exactly where Power Platform custom connectors is necessary.

    Inventory Management System for Power Apps

    Therefore, Power Platform custom connectors enrich those citizen developers' capabilities because those connectors can connect any API applications for the citizen developers to use.

    In this post, let's build a custom connector that provides a static map image generated by Google Maps API and Naver Map API using your GPS location.

    Proxy app to Google Maps and Naver Map API

    First, let's build an Azure Functions app that connects to Google Maps and Naver Map. Suppose that you've already got the API keys for both services. If you haven't yet, get the keys first by visiting here for Google and here for Naver. Then, store them to local.settings.json within your Azure Functions app.

    {
    "Values": {
    ...
    "Maps__Google__ApiKey": "<GOOGLE_MAPS_API_KEY>",
    "Maps__Naver__ClientId": "<NAVER_MAP_API_CLIENT_ID>",
    "Maps__Naver__ClientSecret": "<NAVER_MAP_API_CLIENT_SECRET>"
    }
    }

    Here's the sample logic to get the static image from Google Maps API. It takes the latitude and longitude of your current location and image zoom level, then returns the static map image. There are a few hard-coded assumptions, though:

    • The image size should be 400x400.
    • The image should be in .png format.
    • The marker should show be red and show my location.
    public class GoogleMapService : IMapService
    {
    public async Task<byte[]> GetMapAsync(HttpRequest req)
    {
    var latitude = req.Query["lat"];
    var longitude = req.Query["long"];
    var zoom = (string)req.Query["zoom"] ?? "14";

    var sb = new StringBuilder();
    sb.Append("https://maps.googleapis.com/maps/api/staticmap")
    .Append($"?center={latitude},{longitude}")
    .Append("&size=400x400")
    .Append($"&zoom={zoom}")
    .Append($"&markers=color:red|{latitude},{longitude}")
    .Append("&format=png32")
    .Append($"&key={this._settings.Google.ApiKey}");
    var requestUri = new Uri(sb.ToString());

    var bytes = await this._http.GetByteArrayAsync(requestUri).ConfigureAwait(false);

    return bytes;
    }
    }

    The NaverMapService class has a similar logic with the same input and assumptions. Here's the code:

    public class NaverMapService : IMapService
    {
    public async Task<byte[]> GetMapAsync(HttpRequest req)
    {
    var latitude = req.Query["lat"];
    var longitude = req.Query["long"];
    var zoom = (string)req.Query["zoom"] ?? "13";

    var sb = new StringBuilder();
    sb.Append("https://naveropenapi.apigw.ntruss.com/map-static/v2/raster")
    .Append($"?center={longitude},{latitude}")
    .Append("&w=400")
    .Append("&h=400")
    .Append($"&level={zoom}")
    .Append($"&markers=color:blue|pos:{longitude}%20{latitude}")
    .Append("&format=png")
    .Append("&lang=en");
    var requestUri = new Uri(sb.ToString());

    this._http.DefaultRequestHeaders.Clear();
    this._http.DefaultRequestHeaders.Add("X-NCP-APIGW-API-KEY-ID", this._settings.Naver.ClientId);
    this._http.DefaultRequestHeaders.Add("X-NCP-APIGW-API-KEY", this._settings.Naver.ClientSecret);

    var bytes = await this._http.GetByteArrayAsync(requestUri).ConfigureAwait(false);

    return bytes;
    }
    }

    Let's take a look at the function endpoints. Here's for the Google Maps and Naver Map. As the GetMapAsync(req) method returns a byte array value, you need to transform it as FileContentResult, with the content type of image/png.

    // Google Maps
    public class GoogleMapsTrigger
    {
    [FunctionName(nameof(GoogleMapsTrigger.GetGoogleMapImage))]
    public async Task<IActionResult> GetGoogleMapImage(
    [HttpTrigger(AuthorizationLevel.Anonymous, "GET", Route = "google/image")] HttpRequest req)
    {
    this._logger.LogInformation("C# HTTP trigger function processed a request.");

    var bytes = await this._service.GetMapAsync(req).ConfigureAwait(false);

    return new FileContentResult(bytes, "image/png");
    }
    }

    // Naver Map
    public class NaverMapsTrigger
    {
    [FunctionName(nameof(NaverMapsTrigger.GetNaverMapImage))]
    public async Task<IActionResult> GetNaverMapImage(
    [HttpTrigger(AuthorizationLevel.Anonymous, "GET", Route = "naver/image")] HttpRequest req)
    {
    this._logger.LogInformation("C# HTTP trigger function processed a request.");

    var bytes = await this._service.GetMapAsync(req).ConfigureAwait(false);

    return new FileContentResult(bytes, "image/png");
    }
    }

    Then, add the OpenAPI capability to each function endpoint. Here's the example:

    // Google Maps
    public class GoogleMapsTrigger
    {
    [FunctionName(nameof(GoogleMapsTrigger.GetGoogleMapImage))]
    // ⬇️⬇️⬇️ Add decorators provided by the OpenAPI extension ⬇️⬇️⬇️
    [OpenApiOperation(operationId: nameof(GoogleMapsTrigger.GetGoogleMapImage), tags: new[] { "google" })]
    [OpenApiParameter(name: "lat", In = ParameterLocation.Query, Required = true, Type = typeof(string), Description = "The **latitude** parameter")]
    [OpenApiParameter(name: "long", In = ParameterLocation.Query, Required = true, Type = typeof(string), Description = "The **longitude** parameter")]
    [OpenApiParameter(name: "zoom", In = ParameterLocation.Query, Required = false, Type = typeof(string), Description = "The **zoom level** parameter &ndash; Default value is `14`")]
    [OpenApiResponseWithBody(statusCode: HttpStatusCode.OK, contentType: "image/png", bodyType: typeof(byte[]), Description = "The map image as an OK response")]
    // ⬆️⬆️⬆️ Add decorators provided by the OpenAPI extension ⬆️⬆️⬆️
    public async Task<IActionResult> GetGoogleMapImage(
    [HttpTrigger(AuthorizationLevel.Anonymous, "GET", Route = "google/image")] HttpRequest req)
    {
    ...
    }
    }

    // Naver Map
    public class NaverMapsTrigger
    {
    [FunctionName(nameof(NaverMapsTrigger.GetNaverMapImage))]
    // ⬇️⬇️⬇️ Add decorators provided by the OpenAPI extension ⬇️⬇️⬇️
    [OpenApiOperation(operationId: nameof(NaverMapsTrigger.GetNaverMapImage), tags: new[] { "naver" })]
    [OpenApiParameter(name: "lat", In = ParameterLocation.Query, Required = true, Type = typeof(string), Description = "The **latitude** parameter")]
    [OpenApiParameter(name: "long", In = ParameterLocation.Query, Required = true, Type = typeof(string), Description = "The **longitude** parameter")]
    [OpenApiParameter(name: "zoom", In = ParameterLocation.Query, Required = false, Type = typeof(string), Description = "The **zoom level** parameter &ndash; Default value is `13`")]
    [OpenApiResponseWithBody(statusCode: HttpStatusCode.OK, contentType: "image/png", bodyType: typeof(byte[]), Description = "The map image as an OK response")]
    // ⬆️⬆️⬆️ Add decorators provided by the OpenAPI extension ⬆️⬆️⬆️
    public async Task<IActionResult> GetNaverMapImage(
    [HttpTrigger(AuthorizationLevel.Anonymous, "GET", Route = "naver/image")] HttpRequest req)
    {
    ...
    }
    }

    Run the function app in the local. Here are the latitude and longitude values for Seoul, Korea.

    • latitude: 37.574703
    • longitude: 126.978519

    Google Map for Seoul

    It seems to be working! Let's deploy it to Azure.

    API Management integration

    Visual Studio 2022 provides a built-in deployment tool for Azure Functions app onto Azure. In addition, the deployment tool supports seamless integration with Azure API Management as long as your Azure Functions app enables the OpenAPI capability. In this post, I'm going to use this feature. Right-mouse click on the Azure Functions project and select the "Publish" menu.

    Visual Studio context menu for publish

    Then, you will see the publish screen. Click the "➕ New" button to create a new publish profile.

    Create a new publish profile

    Choose "Azure" and click the "Next" button.

    Choose the target platform for publish

    Select the app instance. This time simply pick up the "Azure Function App (Windows)" option, then click "Next".

    Choose the target OS for publish

    If you already provision an Azure Function app instance, you will see it on the screen. Otherwise, create a new one. Then, click "Next".

    Choose the target instance for publish

    In the next step, you are asked to choose the Azure API Management instance for integration. Choose one, or create a new one. Then, click "Next".

    Choose the APIM instance for integration

    Finally, select the publish method either local publish or GitHub Actions workflow. Let's pick up the local publish method for now. Then, click "Finish".

    Choose the deployment type

    The publish profile has been created. Click "Close" to move on.

    Publish profile created

    Now the function app is ready for deployment. Click the "Publish" button and see how it goes.

    Publish function app

    The Azure function app has been deployed and integrated with the Azure API Management instance.

    Function app published

    Go to the published function app site, and everything looks OK.

    Function app on Azure

    And API Management shows the function app integrated perfectly.

    Function app integrated with APIM

    Now, you are ready to create a custom connector. Let's move on.

    Two ways of building custom connector

    There are two ways to create a custom connector.

    Export custom connector from API Management

    First, you can directly use the built-in API Management feature. Then, click the ellipsis icon and select the "Create Power Connector" menu.

    Create Power Connector menu

    Then, you are redirected to this screen. While the "API" and "API display name" fields are pre-populated, you need to choose the Power Platform environment tied to your tenant. Choose an environment, click "Authenticate", and click "Create".

    Create custom connector screen

    Check your custom connector on Power Apps or Power Automate side.

    Custom connector created on Power Apps

    However, there's a caveat to this approach. Because it's tied to your tenant, you should use the second approach if you want to use this custom connector on the other tenant.

    Import custom connector from OpenAPI document or URL

    Click the ellipsis icon again and select the "Export" menu.

    Export menu

    On the Export API screen, choose the "OpenAPI v2 (JSON)" panel because Power Platform custom connector currently accepts version 2 of the OpenAPI document.

    Select OpenAPI v2

    Download the OpenAPI document to your local computer and move to your Power Apps or Power Automate page under your desired environment. I'm going to use the Power Automate page. First, go to the "Data" ➡️ "Custom connectors" page. Then, click the "➕ New custom connector" ➡️ "Import an OpenAPI file" at the top right corner.

    New custom connector

    When a modal pops up, give the custom connector name and import the OpenAPI document exported above. Then, click "Continue".

    Import custom connector

    Actually, that's it! Next, click the "✔️ Create connector" button to create the connector.

    Create custom connector

    Go back to the custom connector page, and you will see the "Maps API" custom connector you just created.

    Custom connector imported

    So, you are ready to create a Power Apps app to display your location on Google Maps or Naver Map! Let's move on.

    Where am I? Power Apps app

    Open the Power Apps Studio, and create an empty canvas app, named Who am I with a phone layout.

    Custom connector integration

    To use the custom connector created above, you need to add it to the Power App. Click the cylinder icon on the left and click the "Add data" button.

    Add custom connector to data pane

    Search the custom connector name, "Maps API", and click the custom connector to add.

    Search custom connector

    To use the custom connector, you also need to create a connection to it. Click the "Connect" button and move on.

    Create connection to custom connector

    Now, you've got the connection to the custom connector.

    Connection to custom connector ready

    Controls

    Let's build the Power Apps app. First of all, put three controls Image, Slider and Button onto the canvas.

    Power Apps control added

    Click the "Screen1" control and change the value on the property "OnVisible" to the formula below. The formula stores the current slider value in the zoomlevel collection.

    ClearCollect(
    zoomlevel,
    Slider1.Value
    )

    Click the "Botton1" control and change the value on the property "OnSelected" to the formula below. It passes the current latitude, longitude and zoom level to the custom connector and receives the image data. The received image data is stored in the result collection.

    ClearCollect(
    result,
    MAPS.GetGoogleMapImage(
    Location.Latitude,
    Location.Longitude,
    { zoom: First(zoomlevel).Value }
    )
    )

    Click the "Image1" control and change the value on the property "Image" to the formula below. It gets the image data from the result collection.

    First(result).Url

    Click the "Slider1" control and change the value on the property "OnChange" to the formula below. It stores the current slider value to the zoomlevel collection, followed by calling the custom connector to get the image data against the current location.

    ClearCollect(
    zoomlevel,
    Slider1.Value
    );
    ClearCollect(
    result,
    MAPS.GetGoogleMapImage(
    Location.Latitude,
    Location.Longitude,
    { zoom: First(zoomlevel).Value }
    )
    )

    That seems to be OK. Let's click the "Where am I?" button. But it doesn't show the image. The First(result).Url value is actually similar to this:

    appres://blobmanager/1090a86393a843adbfcf428f0b90e91b/1

    It's the image reference value somewhere you can't get there.

    Workaround Power Automate workflow

    Therefore, you need a workaround using a Power Automate workflow to sort out this issue. Open the Power Automate Studio, create an instant cloud flow with the Power App trigger, and give it the "Where am I" name. Then add input parameters of lat, long and zoom.

    Power Apps trigger on Power Automate workflow

    Add custom connector action to get the map image.

    Select action to get the Google Maps image

    In the action, pass the appropriate parameters to the action.

    Pass parameters to the custom connector action

    Add a "Response" action and put the following values into each field.

    • "Body" field:

      {
      "base64Image": <power_automate_expression>
      }

      The <power_automate_expression> should be concat('data:', body('GetGoogleMapImage')?['$content-type'], ';base64,', body('GetGoogleMapImage')?['$content']).

    • "Response Body JSON Schema" field:

      {
      "type": "object",
      "properties": {
      "base64Image": {
      "type": "string"
      }
      }
      }

    Format the Response action

    Let's return to the Power Apps Studio and add the Power Automate workflow you created.

    Add Power Automate workflow

    Select "Button1" and change the value on the property "OnSelect" below. It replaces the direct call to the custom connector with the Power Automate workflow.

    ClearCollect(
    result,
    WhereamI.Run(
    Location.Latitude,
    Location.Longitude,
    First(zoomlevel).Value
    )
    )

    Also, change the value on the property "OnChange" of the "Slider1" control below, replacing the custom connector call with the Power Automate workflow call.

    ClearCollect(
    zoomlevel,
    Slider1.Value
    );
    ClearCollect(
    result,
    WhereamI.Run(
    Location.Latitude,
    Location.Longitude,
    First(zoomlevel).Value
    )
    )

    And finally, change the "Image1" control's "Image" property value below.

    First(result).base64Image

    The workaround has been applied. Click the "Where am I?" button to see your current location from Google Maps.

    Run Power Apps app #1

    If you change the slider left or right, you will see either the zoomed-in image or the zoomed-out image.

    Run Power Apps app #2

    Now, you've created a Power Apps app to show your current location using:

    • Google Maps API through the custom connector, and
    • Custom connector written in Azure Functions with OpenAPI extension!

    Exercise: Try this yourself!

    You can fork this GitHub repository to your account and play around with it to see how the custom connector works. After forking the repository, make sure that you create all the necessary secrets to your repository documented in the README file.

    Then, click the "Deploy to Azure" button, and it will provision all necessary Azure resources and deploy an Azure Functions app for a custom connector.

    Deploy To Azure

    Once everything is deployed successfully, try to create a Power Apps app and Power Automate workflow to see your current location in real-time!

    Resources: For self-study!

    Want to know more about Power Platform custom connector and Azure Functions OpenAPI extension? Here are several resources you can take a look at:

    - - + + \ No newline at end of file diff --git a/blog/tags/python/index.html b/blog/tags/python/index.html index f83ad86016..46147ff6b1 100644 --- a/blog/tags/python/index.html +++ b/blog/tags/python/index.html @@ -14,15 +14,15 @@ - - + +

    One post tagged with "python"

    View All Tags

    · 7 min read
    Jay Miller

    Welcome to Day 7 of #30DaysOfServerless!

    Over the past couple of days, we've explored Azure Functions from the perspective of specific programming languages. Today we'll continue that trend by looking at Python - exploring the Timer Trigger and CosmosDB binding, and showcasing integration with a FastAPI-implemented web app.

    Ready? Let's go.


    What We'll Cover

    • Developer Guidance: Azure Functions On Python
    • Build & Deploy: Wildfire Detection Apps with Timer Trigger + CosmosDB
    • Demo: My Fire Map App: Using FastAPI and Azure Maps to visualize data
    • Next Steps: Explore Azure Samples
    • Exercise: Try this yourself!
    • Resources: For self-study!


    Developer Guidance

    If you're a Python developer new to serverless on Azure, start with the Azure Functions Python Developer Guide. It covers:

    • Quickstarts with Visual Studio Code and Azure CLI
    • Adopting best practices for hosting, reliability and efficiency.
    • Tutorials showcasing Azure automation, image classification and more
    • Samples showcasing Azure Functions features for Python developers

    Now let's dive in and build our first Python-based Azure Functions app.


    Detecting Wildfires Around the World?

    I live in California which is known for lots of wildfires. I wanted to create a proof of concept for developing an application that could let me know if there was a wildfire detected near my home.

    NASA has a few satelites orbiting the Earth that can detect wildfires. These satelites take scans of the radiative heat in and use that to determine the likelihood of a wildfire. NASA updates their information about every 30 minutes and it can take about four hours for to scan and process information.

    Fire Point Near Austin, TX

    I want to get the information but I don't want to ping NASA or another service every time I check.

    What if I occaisionally download all the data I need? Then I can ping that as much as I like.

    I can create a script that does just that. Any time I say I can create a script that is a verbal queue for me to consider using an Azure function. With the function being ran in the cloud, I can ensure the script runs even when I'm not at my computer.

    How the Timer Trigger Works

    This function will utilize the Timer Trigger. This means Azure will call this function to run at a scheduled interval. This isn't the only way to keep the data in sync, but we know that arcgis, the service that we're using says that data is only updated every 30 minutes or so.

    To learn more about the TimerTrigger as a concept, check out the Azure Functions documentation around Timers.

    When we create the function we tell it a few things like where the script will live (in our case in __init__.py) the type and direction and notably often it should run. We specify the timer using schedule": <The CRON INTERVAL>. For us we're using 0 0,30 * * * which means every 30 minutes at the hour and half-hour.

    {
    "scriptFile": "__init__.py",
    "bindings": [
    {
    "name": "reqTimer",
    "type": "timerTrigger",
    "direction": "in",
    "schedule": "0 0,30 * * * *"
    }
    ]
    }

    Next, we create the code that runs when the function is called.

    Connecting to the Database and our Source

    Disclaimer: The data that we're pulling is for educational purposes only. This is not meant to be a production level application. You're welcome play with this project but ensure that you're using the data in compliance with Esri.

    Our function does two important things.

    1. It pulls data from ArcGIS that meets the parameters
    2. It stores that pulled data into our database

    If you want to check out the code in its entirety, check out the GitHub repository.

    Pulling the data from ArcGIS is easy. We can use the ArcGIS Python API. Then, we need to load the service layer. Finally we query that layer for the specific data.

    def write_new_file_data(gis_id:str, layer:int=0) -> FeatureSet:
    """Returns a JSON String of the Dataframe"""
    fire_data = g.content.get(gis_id)
    feature = fire_data.layers[layer] # Loading Featured Layer from ArcGIS
    q = feature.query(
    where="confidence >= 65 AND hours_old <= 4", #The filter for the query
    return_distince_values=True,
    out_fields="confidence, hours_old", # The data we want to store with our points
    out_sr=4326, # The spatial reference of the data
    )
    return q

    Then we need to store the data in our database.

    We're using Cosmos DB for this. COSMOSDB is a NoSQL database, which means that the data looks a lot like a python dictionary as it's JSON. This means that we don't need to worry about converting the data into a format that can be stored in a relational database.

    The second reason is that Cosmos DB is tied into the Azure ecosystem so that if we want to create functions Azure events around it, we can.

    Our script grabs the information that we pulled from ArcGIS and stores it in our database.

    async with CosmosClient.from_connection_string(COSMOS_CONNECTION_STRING) as client:
    container = database.get_container_client(container=CONTAINER)
    for record in data:
    await container.create_item(
    record,
    enable_automatic_id_generation=True,
    )

    In our code each of these functions live in their own space. So in the main function we focus solely on what azure functions will be doing. The script that gets called is __init__.py. There we'll have the function call the other functions running.

    We created another function called load_and_write that does all the work outlined above. __init__.py will call that.

    async def main(reqTimer: func.TimerRequest) -> None:
    database=database
    container=container
    await update_db.load_and_write(gis_id=GIS_LAYER_ID, database=database, container=container)

    Then we deploy the function to Azure. I like to use VS Code's Azure Extension but you can also deploy it a few other ways.

    Deploying the function via VS Code

    Once the function is deployed we can load the Azure portal and see a ping whenever the function is called. The pings correspond to the Function being ran

    We can also see the data now living in the datastore. Document in Cosmos DB

    It's in the Database, Now What?

    Now the real fun begins. We just loaded the last bit of fire data into a database. We can now query that data and serve it to others.

    As I mentioned before, our Cosmos DB data is also stored in Azure, which means that we can deploy Azure Functions to trigger when new data is added. Perhaps you can use this to check for fires near you and use a Logic App to send an alert to your phone or email.

    Another option is to create a web application that talks to the database and displays the data. I've created an example of this using FastAPI – https://jm-func-us-fire-notify.azurewebsites.net.

    Website that Checks for Fires


    Next Steps

    This article showcased the Timer Trigger and the HTTP Trigger for Azure Functions in Python. Now try exploring other triggers and bindings by browsing Bindings code samples for Python and Azure Functions samples for Python

    Once you've tried out the samples, you may want to explore more advanced integrations or extensions for serverless Python scenarios. Here are some suggestions:

    And check out the resources for more tutorials to build up your Azure Functions skills.

    Exercise

    I encourage you to fork the repository and try building and deploying it yourself! You can see the TimerTrigger and a HTTPTrigger building the website.

    Then try extending it. Perhaps if wildfires are a big thing in your area, you can use some of the data available in Planetary Computer to check out some other datasets.

    Resources

    - - + + \ No newline at end of file diff --git a/blog/tags/serverless-e-2-e/index.html b/blog/tags/serverless-e-2-e/index.html index d749e5cd29..db19f2aa8a 100644 --- a/blog/tags/serverless-e-2-e/index.html +++ b/blog/tags/serverless-e-2-e/index.html @@ -14,13 +14,13 @@ - - + +

    One post tagged with "serverless-e2e"

    View All Tags

    · 5 min read
    Nitya Narasimhan
    Devanshi Joshi

    SEP 08: CHANGE IN PUBLISHING SCHEDULE

    Starting from Week 2 (Sep 8), we'll be publishing blog posts in batches rather than on a daily basis, so you can read a series of related posts together. Don't want to miss updates? Just subscribe to the feed


    Welcome to Day 8 of #30DaysOfServerless!

    This marks the end of our Week 1 Roadmap focused on Azure Functions!! Today, we'll do a quick recap of all #ServerlessSeptember activities in Week 1, set the stage for Week 2 - and leave you with some excellent tutorials you should explore to build more advanced scenarios with Azure Functions.

    Ready? Let's go.


    What We'll Cover

    • Azure Functions: Week 1 Recap
    • Advanced Functions: Explore Samples
    • End-to-End: Serverless Hacks & Cloud Skills
    • What's Next: Hello, Containers & Microservices
    • Challenge: Complete the Learning Path


    Week 1 Recap: #30Days & Functions

    Congratulations!! We made it to the end of Week 1 of #ServerlessSeptember. Let's recap what we learned so far:

    • In Core Concepts we looked at where Azure Functions fits into the serverless options available on Azure. And we learned about key concepts like Triggers, Bindings, Custom Handlers and Durable Functions.
    • In Build Your First Function we looked at the tooling options for creating Functions apps, testing them locally, and deploying them to Azure - as we built and deployed our first Functions app.
    • In the next 4 posts, we explored new Triggers, Integrations, and Scenarios - as we looked at building Functions Apps in Java, JavaScript, .NET and Python.
    • And in the Zero-To-Hero series, we learned about Durable Entities - and how we can use them to create stateful serverless solutions using a Chirper Sample as an example scenario.

    The illustrated roadmap below summarizes what we covered each day this week, as we bring our Functions-as-a-Service exploration to a close.


    Advanced Functions: Code Samples

    So, now that we've got our first Functions app under our belt, and validated our local development setup for tooling, where can we go next? A good next step is to explore different triggers and bindings, that drive richer end-to-end scenarios. For example:

    • Integrate Functions with Azure Logic Apps - we'll discuss Azure Logic Apps in Week 3. For now, think of it as a workflow automation tool that lets you integrate seamlessly with other supported Azure services to drive an end-to-end scenario. In this tutorial, we set up a workflow connecting Twitter (get tweet) to Azure Cognitive Services (analyze sentiment) - and use that to trigger an Azure Functions app to send email about the result.
    • Integrate Functions with Event Grid - we'll discuss Azure Event Grid in Week 3. For now, think of it as an eventing service connecting event sources (publishers) to event handlers (subscribers) at cloud scale. In this tutorial, we handle a common use case - a workflow where loading an image to Blob Storage triggers an Azure Functions app that implements a resize function, helping automatically generate thumbnails for the uploaded image.
    • Integrate Functions with CosmosDB and SignalR to bring real-time push-based notifications to your web app. It achieves this by using a Functions app that is triggered by changes in a CosmosDB backend, causing it to broadcast that update (push notification to connected web clients over SignalR, in real time.

    Want more ideas? Check out the Azure Samples for Functions for implementations, and browse the Azure Architecture Center for reference architectures from real-world scenarios that involve Azure Functions usage.


    E2E Scenarios: Hacks & Cloud Skills

    Want to systematically work your way through a single End-to-End scenario involving Azure Functions alongside other serverless support technologies? Check out the Serverless Hacks activity happening during #ServerlessSeptember, and learn to build this "Serverless Tollbooth Application" in a series of 10 challenges. Check out the video series for a reference solution in .NET and sign up for weekly office hours to join peers and discuss your solutions or challenges.

    Or perhaps you prefer to learn core concepts with code in a structured learning path? We have that covered. Check out the 12-module "Create Serverless Applications" course from Microsoft Learn which walks your through concepts, one at a time, with code. Even better - sign up for the free Cloud Skills Challenge and complete the same path (in under 30 days) but this time, with the added fun of competing against your peers for a spot on a leaderboard, and swag.


    What's Next? Hello, Cloud-Native!

    So where to next? In Week 2 we turn our attention from Functions-as-a-Service to building more complex backends using Containers and Microservices. We'll focus on two core technologies - Azure Container Apps and Dapr (Distributed Application Runtime) - both key components of a broader vision around Building Cloud-Native Applications in Azure.

    What is Cloud-Native you ask?

    Fortunately for you, we have an excellent introduction in our Zero-to-Hero article on Go Cloud-Native with Azure Container Apps - that explains the 5 pillars of Cloud-Native and highlights the value of Azure Container Apps (scenarios) and Dapr (sidecar architecture) for simplified microservices-based solution with auto-scale capability. Prefer a visual summary? Here's an illustrate guide to that article for convenience.

    Go Cloud-Native Download a higher resolution version of the image


    Take The Challenge

    We typically end each post with an exercise or activity to reinforce what you learned. For Week 1, we encourage you to take the Cloud Skills Challenge and work your way through at least a subset of the modules, for hands-on experience with the different Azure Functions concepts, integrations, and usage.

    See you in Week 2!

    - - + + \ No newline at end of file diff --git a/blog/tags/serverless-hacks/index.html b/blog/tags/serverless-hacks/index.html index 6f9eb73f82..4b5e0d14be 100644 --- a/blog/tags/serverless-hacks/index.html +++ b/blog/tags/serverless-hacks/index.html @@ -14,13 +14,13 @@ - - + +

    One post tagged with "serverless-hacks"

    View All Tags

    · 5 min read
    Nitya Narasimhan
    Devanshi Joshi

    SEP 08: CHANGE IN PUBLISHING SCHEDULE

    Starting from Week 2 (Sep 8), we'll be publishing blog posts in batches rather than on a daily basis, so you can read a series of related posts together. Don't want to miss updates? Just subscribe to the feed


    Welcome to Day 8 of #30DaysOfServerless!

    This marks the end of our Week 1 Roadmap focused on Azure Functions!! Today, we'll do a quick recap of all #ServerlessSeptember activities in Week 1, set the stage for Week 2 - and leave you with some excellent tutorials you should explore to build more advanced scenarios with Azure Functions.

    Ready? Let's go.


    What We'll Cover

    • Azure Functions: Week 1 Recap
    • Advanced Functions: Explore Samples
    • End-to-End: Serverless Hacks & Cloud Skills
    • What's Next: Hello, Containers & Microservices
    • Challenge: Complete the Learning Path


    Week 1 Recap: #30Days & Functions

    Congratulations!! We made it to the end of Week 1 of #ServerlessSeptember. Let's recap what we learned so far:

    • In Core Concepts we looked at where Azure Functions fits into the serverless options available on Azure. And we learned about key concepts like Triggers, Bindings, Custom Handlers and Durable Functions.
    • In Build Your First Function we looked at the tooling options for creating Functions apps, testing them locally, and deploying them to Azure - as we built and deployed our first Functions app.
    • In the next 4 posts, we explored new Triggers, Integrations, and Scenarios - as we looked at building Functions Apps in Java, JavaScript, .NET and Python.
    • And in the Zero-To-Hero series, we learned about Durable Entities - and how we can use them to create stateful serverless solutions using a Chirper Sample as an example scenario.

    The illustrated roadmap below summarizes what we covered each day this week, as we bring our Functions-as-a-Service exploration to a close.


    Advanced Functions: Code Samples

    So, now that we've got our first Functions app under our belt, and validated our local development setup for tooling, where can we go next? A good next step is to explore different triggers and bindings, that drive richer end-to-end scenarios. For example:

    • Integrate Functions with Azure Logic Apps - we'll discuss Azure Logic Apps in Week 3. For now, think of it as a workflow automation tool that lets you integrate seamlessly with other supported Azure services to drive an end-to-end scenario. In this tutorial, we set up a workflow connecting Twitter (get tweet) to Azure Cognitive Services (analyze sentiment) - and use that to trigger an Azure Functions app to send email about the result.
    • Integrate Functions with Event Grid - we'll discuss Azure Event Grid in Week 3. For now, think of it as an eventing service connecting event sources (publishers) to event handlers (subscribers) at cloud scale. In this tutorial, we handle a common use case - a workflow where loading an image to Blob Storage triggers an Azure Functions app that implements a resize function, helping automatically generate thumbnails for the uploaded image.
    • Integrate Functions with CosmosDB and SignalR to bring real-time push-based notifications to your web app. It achieves this by using a Functions app that is triggered by changes in a CosmosDB backend, causing it to broadcast that update (push notification to connected web clients over SignalR, in real time.

    Want more ideas? Check out the Azure Samples for Functions for implementations, and browse the Azure Architecture Center for reference architectures from real-world scenarios that involve Azure Functions usage.


    E2E Scenarios: Hacks & Cloud Skills

    Want to systematically work your way through a single End-to-End scenario involving Azure Functions alongside other serverless support technologies? Check out the Serverless Hacks activity happening during #ServerlessSeptember, and learn to build this "Serverless Tollbooth Application" in a series of 10 challenges. Check out the video series for a reference solution in .NET and sign up for weekly office hours to join peers and discuss your solutions or challenges.

    Or perhaps you prefer to learn core concepts with code in a structured learning path? We have that covered. Check out the 12-module "Create Serverless Applications" course from Microsoft Learn which walks your through concepts, one at a time, with code. Even better - sign up for the free Cloud Skills Challenge and complete the same path (in under 30 days) but this time, with the added fun of competing against your peers for a spot on a leaderboard, and swag.


    What's Next? Hello, Cloud-Native!

    So where to next? In Week 2 we turn our attention from Functions-as-a-Service to building more complex backends using Containers and Microservices. We'll focus on two core technologies - Azure Container Apps and Dapr (Distributed Application Runtime) - both key components of a broader vision around Building Cloud-Native Applications in Azure.

    What is Cloud-Native you ask?

    Fortunately for you, we have an excellent introduction in our Zero-to-Hero article on Go Cloud-Native with Azure Container Apps - that explains the 5 pillars of Cloud-Native and highlights the value of Azure Container Apps (scenarios) and Dapr (sidecar architecture) for simplified microservices-based solution with auto-scale capability. Prefer a visual summary? Here's an illustrate guide to that article for convenience.

    Go Cloud-Native Download a higher resolution version of the image


    Take The Challenge

    We typically end each post with an exercise or activity to reinforce what you learned. For Week 1, we encourage you to take the Cloud Skills Challenge and work your way through at least a subset of the modules, for hands-on experience with the different Azure Functions concepts, integrations, and usage.

    See you in Week 2!

    - - + + \ No newline at end of file diff --git a/blog/tags/serverless-september/index.html b/blog/tags/serverless-september/index.html index f376563c9f..8ff3e114b3 100644 --- a/blog/tags/serverless-september/index.html +++ b/blog/tags/serverless-september/index.html @@ -14,14 +14,14 @@ - - + +

    33 posts tagged with "serverless-september"

    View All Tags

    · 7 min read
    Devanshi Joshi

    It's Serverless September in a Nutshell! Join us as we unpack our month-long learning journey exploring the core technology pillars for Serverless architectures on Azure. Then end with a look at next steps to build your Cloud-native applications on Azure.


    What We'll Cover

    • Functions-as-a-Service (FaaS)
    • Microservices and Containers
    • Serverless Integrations
    • End-to-End Solutions
    • Developer Tools & #Hacktoberfest

    Banner for Serverless September


    Building Cloud-native Apps

    By definition, cloud-native technologies empower organizations to build and run scalable applications in modern, dynamic environments such as public, private, and hybrid clouds. You can learn more about cloud-native in Kendall Roden's #ServerlessSeptember post on Going Cloud-native with Azure Container Apps.

    Serveless technologies accelerate productivity and minimize costs for deploying applications at cloud scale. So, what can we build with serverless technologies in cloud-native on Azure? Anything that is event-driven - examples include:

    • Microservices - scaled by KEDA-compliant triggers
    • Public API Endpoints - scaled by #concurrent HTTP requests
    • Event-Driven Applications - scaled by length of message queue
    • Web Applications - scaled by #concurrent HTTP requests
    • Background Process - scaled by CPU and Memory usage

    Great - but as developers, we really want to know how we can get started building and deploying serverless solutions on Azure. That was the focus of our #ServerlessSeptember journey. Let's take a quick look at the four key themes.

    Functions-as-a-Service (FaaS)

    Functions-as-a-Service (FaaS) is the epitome of developer productivity for full-stack modern apps. As developers, you don't manage infrastructure and focus only on business logic and application code. And, with Serverless Compute you only pay for when your code runs - making this the simplest first step to begin migrating your application to cloud-native.

    In Azure, FaaS is provided by Azure Functions. Check out our Functions + Serverless on Azure to go from learning core concepts, to building your first Functions app in your programming language of choice. Azure functions support multiple programming languages including C#, F#, Java, JavaScript, Python, Typescript, and PowerShell.

    Want to get extended language support for languages like Go, and Rust? You can Use Custom Handlers to make this happen! But what if you want to have long-running functions, or create complex workflows involving more than one function? Read our post on Durable Entities to learn how you can orchestrate this with Azure Functions.

    Check out this recent AskTheExpert Q&A session with the Azure Functions team to get answers to popular community questions on Azure Functions features and usage.

    Microservices and Containers

    Functions-as-a-Service is an ideal first step towards serverless development. But Functions are just one of the 5 pillars of cloud-native. This week we'll look at two of the other pillars: microservices and containers - with specific focus on two core technologies: Azure Container Apps and Dapr (Distributed Application Runtime).

    In this 6-part series of posts, we walk through each technology independently, before looking at the value of building Azure Container Apps with Dapr.

    • In Hello Container Apps we learned core concepts & deployed our first ACA.
    • In Microservices Communication we learned about ACA environments and virtual networks, and how microservices communicate in ACA with a hands-on tutorial.
    • In Scaling Your Container Apps we learned about KEDA (Kubernetes Event-Driven Autoscaler) and configuring ACA for autoscaling with KEDA-compliant triggers.
    • In Build with Dapr we introduced the Distributed Application Runtime (Dapr), exploring its Building Block APIs and sidecar architecture for working with ACA.
    • In Secure ACA Access we learned how to secure ACA access to external services with - and without - Dapr, covering Secret Stores and Managed Identity.
    • Finally, Build ACA with Dapr tied it all together with a enterprise app scenario where an orders processor (ACA) uses Dapr APIs (PubSub, State Management) to receive and store order messages from Azure Service Bus.

    Build ACA with Dapr

    Check out this recent AskTheExpert Q&A session with the Azure Container Apps team for answers to popular community questions on core features and usage.

    Serverless Integrations

    In the first half of the month we looked at compute resources for building and deploying serverless applications. In the second half, we look at integration tools and resources that automate developer workflows to streamline the end-to-end developer experience.

    In Azure, this is enabled by services like Azure Logic Apps and Azure Event Grid. Azure Logic Apps provides a visual designer to create and automate workflows with little or no code involved. Azure Event Grid provides a highly-scable event broker with support for pub/sub communications to drive async event-driven architectures.

    • In Tracking Weather Data Changes With Logic Apps we look at how you can use Logic Apps to integrate the MSN weather service with Azure CosmosDB, allowing automated collection of weather data on changes.

    • In Teach the Cloud to Read & Categorize Mail we take it a step further, using Logic Apps to automate a workflow that includes a Computer Vision service to "read" images and store the results to CosmosDB.

    • In Integrate with Microsoft Graph we explore a multi-cloud scenario (Azure + M365) where change notifications from Microsoft Graph can be integrated using Logic Apps and Event Hubs to power an onboarding workflow.

    • In Cloud Events with Event Grid we learn about the CloudEvents specification (for consistently describing event data) - and learn how Event Grid brokers events in this format. Azure Logic Apps can be an Event handler (subscriber) that uses the event to trigger an automated workflow on receipt.

      Azure Event Grid And Logic Apps

    Want to explore other such integrations? Browse Azure Architectures and filter by selected Azure services for more real-world scenarios.


    End-to-End Solutions

    We've covered serverless compute solutions (for building your serverless applications) and serverless integration services to automate end-to-end workflows in synchronous or asynchronous event-driven architectures. In this final week, we want to leave you with a sense of end-to-end development tools and use cases that can be enabled by Serverless on Azure. Here are some key examples:

    ArticleDescription
    In this tutorial, you'll learn to deploy a containerized ASP.NET Core 6.0 application to Azure Container Apps - with a Blazor front-end and two Web API projects
    Deploy Java containers to cloudIn this tutorial you learn to build and deploy a Java application running on Spring Boot, by publishing it in a container to Azure Container Registry, then deploying to Azure Container Apps,, from ACR, via the Azure Portal.
    **Where am I? My GPS Location with Serverless Power Platform Custom Connector**In this step-by-step tutorial you learn to integrate a serverless application (built on Azure Functions and OpenAPI) with Power Platforms custom connectors via Azure API Management (API-M).This pattern can empower a new ecosystem of fusion apps for cases like inventory management.
    And in our Serverless Hacks initiative, we walked through an 8-step hack to build a serverless tollbooth. Check out this 12-part video walkthrough of a reference solution using .NET.

    Developer Tools

    But wait - there's more. Those are a sample of the end-to-end application scenarios that are built on serverless on Azure. But what about the developer experience? In this article, we say hello to the Azure Developer CLI - an open-source tool that streamlines your develop-deploy workflow, with simple commands that map to core stages of your development journey. Go from code to cloud with one CLI

    And watch this space for more such tutorials and content through October, including a special #Hacktoberfest focused initiative to encourage and support first-time contributors to open-source. Here's a sneak peek at the project we plan to share - the new awesome-azd templates gallery.


    Join us at Microsoft Ignite!

    Want to continue your learning journey, and learn about what's next for Serverless on Azure? Microsoft Ignite happens Oct 12-14 this year and has multiple sessions on relevant technologies and tools. Check out the Session Catalog and register here to attend online.

    - - + + \ No newline at end of file diff --git a/blog/tags/serverless-september/page/10/index.html b/blog/tags/serverless-september/page/10/index.html index 39186e29a9..b760dabf69 100644 --- a/blog/tags/serverless-september/page/10/index.html +++ b/blog/tags/serverless-september/page/10/index.html @@ -14,14 +14,14 @@ - - + +

    33 posts tagged with "serverless-september"

    View All Tags

    · 5 min read
    Mike Morton

    Welcome to Day 19 of #30DaysOfServerless!

    Today, we have a special set of posts from our Zero To Hero 🚀 initiative, featuring blog posts authored by our Product Engineering teams for #ServerlessSeptember. Posts were originally published on the Apps on Azure blog on Microsoft Tech Community.


    What We'll Cover

    • Log Streaming - in Azure Portal
    • Console Connect - in Azure Portal
    • Metrics - using Azure Monitor
    • Log Analytics - using Azure Monitor
    • Metric Alerts and Log Alerts - using Azure Monitor


    In past weeks, @kendallroden wrote about what it means to be Cloud-Native and @Anthony Chu the various ways to get your apps running on Azure Container Apps. Today, we will talk about the observability tools you can use to observe, debug, and diagnose your Azure Container Apps.

    Azure Container Apps provides several observability features to help you debug and diagnose your apps. There are both Azure portal and CLI options you can use to help understand the health of your apps and help identify when issues arise.

    While these features are helpful throughout your container app’s lifetime, there are two that are especially helpful. Log streaming and console connect can be a huge help in the initial stages when issues often rear their ugly head. Let's dig into both of these a little.

    Log Streaming

    Log streaming allows you to use the Azure portal to view the streaming logs from your app. You’ll see the logs written from the app to the container’s console (stderr and stdout). If your app is running multiple revisions, you can choose from which revision to view logs. You can also select a specific replica if your app is configured to scale. Lastly, you can choose from which container to view the log output. This is useful when you are running a custom or Dapr sidecar container. view streaming logs

    Here’s an example CLI command to view the logs of a container app.

    az containerapp logs show -n MyContainerapp -g MyResourceGroup

    You can find more information about the different options in our CLI docs.

    Console Connect

    In the Azure portal, you can connect to the console of a container in your app. Like log streaming, you can select the revision, replica, and container if applicable. After connecting to the console of the container, you can execute shell commands and utilities that you have installed in your container. You can view files and their contents, monitor processes, and perform other debugging tasks.

    This can be great for checking configuration files or even modifying a setting or library your container is using. Of course, updating a container in this fashion is not something you should do to a production app, but tweaking and re-testing an app in a non-production environment can speed up development.

    Here’s an example CLI command to connect to the console of a container app.

    az containerapp exec -n MyContainerapp -g MyResourceGroup

    You can find more information about the different options in our CLI docs.

    Metrics

    Azure Monitor collects metric data from your container app at regular intervals to help you gain insights into the performance and health of your container app. Container apps provide these metrics:

    • CPU usage
    • Memory working set bytes
    • Network in bytes
    • Network out bytes
    • Requests
    • Replica count
    • Replica restart count

    Here you can see the metrics explorer showing the replica count for an app as it scaled from one replica to fifteen, and then back down to one.

    You can also retrieve metric data through the Azure CLI.

    Log Analytics

    Azure Monitor Log Analytics is great for viewing your historical logs emitted from your container apps. There are two custom tables of interest, the ContainerAppConsoleLogs_CL which contains all the log messages written by your app (stdout and stderr), and the ContainerAppSystemLogs_CL which contain the system messages from the Azure Container Apps service.

    You can also query Log Analytics through the Azure CLI.

    Alerts

    Azure Monitor alerts notify you so that you can respond quickly to critical issues. There are two types of alerts that you can define:

    You can create alert rules from metric charts in the metric explorer and from queries in Log Analytics. You can also define and manage alerts from the Monitor|Alerts page.

    Here is what creating an alert looks like in the Azure portal. In this case we are setting an alert rule from the metric explorer to trigger an alert if the replica restart count for a specific container app is greater than two within the last fifteen minutes.

    To learn more about alerts, refer to Overview of alerts in Microsoft Azure.

    Conclusion

    In this article, we looked at the several ways to observe, debug, and diagnose your Azure Container Apps. As you can see there are rich portal tools and a complete set of CLI commands to use. All the tools are helpful throughout the lifecycle of your app, be sure to take advantage of them when having an issue and/or to prevent issues.

    To learn more, visit Azure Container Apps | Microsoft Azure today!

    ASK THE EXPERT: LIVE Q&A

    The Azure Container Apps team will answer questions live on September 29.

    - - + + \ No newline at end of file diff --git a/blog/tags/serverless-september/page/11/index.html b/blog/tags/serverless-september/page/11/index.html index 2ce5c8b3d9..aced1d2e6d 100644 --- a/blog/tags/serverless-september/page/11/index.html +++ b/blog/tags/serverless-september/page/11/index.html @@ -14,14 +14,14 @@ - - + +

    33 posts tagged with "serverless-september"

    View All Tags

    · 10 min read
    Brian Benz

    Welcome to Day 18 of #30DaysOfServerless!

    Yesterday my Serverless September post introduced you to making Azure Logic Apps and Azure Cosmos DB work together with a sample application that collects weather data. Today I'm sharing a more robust solution that actually reads my mail. Let's learn about Teaching the cloud to read your mail!

    Ready? Let's go!


    What We'll Cover

    • Introduction to the ReadMail solution
    • Setting up Azure storage, Cosmos DB and Computer Vision
    • Connecting it all together with a Logic App
    • Resources: For self-study!


    Introducing the ReadMail solution

    The US Postal system offers a subscription service that sends you images of mail it will be delivering to your home. I decided it would be cool to try getting Azure to collect data based on these images, so that I could categorize my mail and track the types of mail that I received.

    To do this, I used Azure storage, Cosmos DB, Logic Apps, and computer vision. When a new email comes in from the US Postal service (USPS), it triggers a logic app that:

    • Posts attachments to Azure storage
    • Triggers Azure Computer vision to perform an OCR function on attachments
    • Extracts any results into a JSON document
    • Writes the JSON document to Cosmos DB

    workflow for the readmail solution

    In this post I'll walk you through setting up the solution for yourself.

    Prerequisites

    Setup Azure Services

    First, we'll create all of the target environments we need to be used by our Logic App, then we;ll create the Logic App.

    1. Azure Storage

    We'll be using Azure storage to collect attached images from emails as they arrive. Adding images to Azure storage will also trigger a workflow that performs OCR on new attached images and stores the OCR data in Cosmos DB.

    To create a new Azure storage account from the portal dashboard, Select Create a resource > Storage account > Create.

    The Basics tab covers all of the features and information that we will need for this solution:

    SectionFieldRequired or optionalDescription
    Project detailsSubscriptionRequiredSelect the subscription for the new storage account.
    Project detailsResource groupRequiredCreate a new resource group that you will use for storage, Cosmos DB, Computer Vision and the Logic App.
    Instance detailsStorage account nameRequiredChoose a unique name for your storage account. Storage account names must be between 3 and 24 characters in length and may contain numbers and lowercase letters only.
    Instance detailsRegionRequiredSelect the appropriate region for your storage account.
    Instance detailsPerformanceRequiredSelect Standard performance for general-purpose v2 storage accounts (default).
    Instance detailsRedundancyRequiredSelect locally-redundant Storage (LRS) for this example.

    Select Review + create to accept the remaining default options, then validate and create the account.

    2. Azure CosmosDB

    CosmosDB will be used to store the JSON documents returned by the COmputer Vision OCR process.

    See more details and screen shots for setting up CosmosDB in yesterday's Serverless September post - Using Logic Apps with Cosmos DB

    To get started with Cosmos DB, you create an account, then a database, then a container to store JSON documents. To create a new Cosmos DB account from the portal dashboard, Select Create a resource > Azure Cosmos DB > Create. Choose core SQL for the API.

    Select your subscription, then for simplicity use the same resource group you created when you set up storage. Enter an account name and choose a location, select provisioned throughput capacity mode and apply the free tier discount. From here you can select Review and Create, then Create

    Next, create a new database and container. Go to the Data Explorer in your new Cosmos DB account, and choose New Container. Name the database, and keep all the other defaults except:

    SettingAction
    Container IDid
    Container partition/id

    Press OK to create a database and container

    3. Azure Computer Vision

    Azure Cognitive Services' Computer Vision will perform an OCR process on each image attachment that is stored in Azure storage.

    From the portal dashboard, Select Create a resource > AI + Machine Learning > Computer Vision > Create.

    The Basics and Identity tabs cover all of the features and information that we will need for this solution:

    Basics Tab

    SectionFieldRequired or optionalDescription
    Project detailsSubscriptionRequiredSelect the subscription for the new service.
    Project detailsResource groupRequiredUse the same resource group that you used for Azure storage and Cosmos DB.
    Instance detailsRegionRequiredSelect the appropriate region for your Computer Vision service.
    Instance detailsNameRequiredChoose a unique name for your Computer Vision service.
    Instance detailsPricingRequiredSelect the free tier for this example.

    Identity Tab

    SectionFieldRequired or optionalDescription
    System assigned managed identityStatusRequiredEnable system assigned identity to grant the resource access to other existing resources.

    Select Review + create to accept the remaining default options, then validate and create the account.


    Connect it all with a Logic App

    Now we're ready to put this all together in a Logic App workflow!

    1. Create Logic App

    From the portal dashboard, Select Create a resource > Integration > Logic App > Create. Name your Logic App and select a location, the rest of the settings can be left at their defaults.

    2. Create Workflow: Add Trigger

    Once the Logic App is created, select Create a workflow from designer.

    A workflow is a series of steps that defines a task or process. Each workflow starts with a single trigger, after which you must add one or more actions.

    When in designer, search for outlook.com on the right under Add a trigger. Choose outlook.com. Choose When a new email arrives as the trigger.

    A trigger is always the first step in any workflow and specifies the condition for running any further steps in that workflow.

    Set the following values:

    ParameterValue
    FolderInbox
    ImportanceAny
    Only With AttachmentsYes
    Include AttachmentsYes

    Then add a new parameter:

    ParameterValue
    FromAdd the email address that sends you the email with attachments
    3. Create Workflow: Add Action (for Trigger)

    Choose add an action and choose control > for-each.

    logic app for each

    Inside the for-each action, in Select an output from previous steps, choose attachments. Then, again inside the for-each action, add the create blob action:

    Set the following values:

    ParameterValue
    Folder Path/mailreaderinbox
    Blob NameAttachments Name
    Blob ContentAttachments Content

    This extracts attachments from the email and created a new blob for each attachment.

    Next, inside the same for-each action, add the get blob content action.

    Set the following values:

    ParameterValue
    Blobid
    Infer content typeYes

    We create and read from a blob for each attachment because Computer Vision needs a non-virtual source to read from when performing an OCR process. Because we enabled system assigned identity to grant Computer Vision to other existing resources, it can access the blob but not the outlook.com attachment. Also, we pass the ID of the blob to use as a unique ID when writing to Cosmos DB.

    create blob from attachments

    Next, inside the same for-each action, choose add an action and choose control > condition. Set the value to Media Type > is equal to > image/JPEG

    The USPS sends attachments of multiple types, but we only want to scan attachments that have images of our mail, which are always JPEG images. If the condition is true, we will process the image with Computer Vision OCR and write the results to a JSON document in CosmosDB.

    In the True section of the condition, add an action and choose Computer Vision API > Optical Character Recognition (OCR) to JSON.

    Set the following values:

    ParameterValue
    Image SourceImage Content
    Image contentFile Content

    In the same True section of the condition, choose add an action and choose Cosmos DB. Choose Create or Update Document from the actions. Select Access Key, and provide the primary read-write key (found under keys in Cosmos DB), and the Cosmos DB account ID (without 'documents.azure.com').

    Next, fill in your Cosmos DB Database ID and Collection ID. Create a JSON document by selecting dynamic content elements and wrapping JSON formatting around them.

    Be sure to use the ID passed from blob storage as your unique ID for CosmosDB. That way you can troubleshoot and JSON or OCR issues by tracing back the JSON document in Cosmos Db to the blob in Azure storage. Also, include the Computer Vision JSON response, as it contains the results of the Computer Vision OCR scan. all other elements are optional.

    4. TEST WORKFLOW

    When complete, you should have an action the Logic App designer that looks something like this:

    Logic App workflow create or update document in cosmosdb

    Save the workflow and test the connections by clicking Run Trigger > Run. If connections are working, you should see documents flowing into Cosmos DB each time that an email arrives with image attachments.

    Check the data in Cosmos Db by opening the Data explorer, then choosing the container you created and selecting items. You should see documents similar to this:

    Logic App workflow with trigger and action

    1. Congratulations

    You just built your personal ReadMail solution with Logic Apps! 🎉


    Resources: For self-study!

    Once you have an understanding of the basics in ths post, there is so much more to learn!

    Thanks for stopping by!

    - - + + \ No newline at end of file diff --git a/blog/tags/serverless-september/page/12/index.html b/blog/tags/serverless-september/page/12/index.html index 714e8cd447..732c5e298f 100644 --- a/blog/tags/serverless-september/page/12/index.html +++ b/blog/tags/serverless-september/page/12/index.html @@ -14,14 +14,14 @@ - - + +

    33 posts tagged with "serverless-september"

    View All Tags

    · 6 min read
    Brian Benz

    Welcome to Day 17 of #30DaysOfServerless!

    In past weeks, we've covered serverless technologies that provide core capabilities (functions, containers, microservices) for building serverless solutions. This week we're looking at technologies that make service integrations more seamless, starting with Logic Apps. Let's look at one usage example today!

    Ready? Let's Go!


    What We'll Cover

    • Introduction to Logic Apps
    • Settng up Cosmos DB for Logic Apps
    • Setting up a Logic App connection and event
    • Writing data to Cosmos DB from a Logic app
    • Resources: For self-study!


    Introduction to Logic Apps

    Previously in Serverless September, we've covered Azure Functions, where the event triggers code. In Logic Apps, the event triggers a workflow that you design. Logic Apps enable serverless applications to connect to external sources for data then automate business processes via workflows.

    In this post I'll walk you through setting up a Logic App that works with Cosmos DB. For this example, we'll connect to the MSN weather service, an design a logic app workflow that collects data when weather changes, and writes the data to Cosmos DB.

    PREREQUISITES

    Setup Cosmos DB for Logic Apps

    Cosmos DB has many APIs to choose from, but to use the default Logic App connection, we need to choose the a Cosmos DB SQL API. We'll set this up via the Azure Portal.

    To get started with Cosmos DB, you create an account, then a database, then a container to store JSON documents. To create a new Cosmos DB account from the portal dashboard, Select Create a resource > Azure Cosmos DB > Create. Choose core SQL for the API.

    Select your subscription, then create a new resource group called CosmosWeather. Enter an account name and choose a location, select provisioned throughput capacity mode and apply the free tier discount. From here you can select Review and Create, then Create

    Azure Cosmos DB is available in two different capacity modes: provisioned throughput and serverless. You can perform the same database operations in both modes, but the way you get billed for these operations is different. We wil be using provisioned throughput and the free tier for this example.

    Setup the CosmosDB account

    Next, create a new database and container. Go to the Data Explorer in your new Cosmos DB account, and choose New Container. Name the database, and keep all the orher defaults except:

    SettingAction
    Container IDid
    Container partition/id

    Press OK to create a database and container

    A database is analogous to a traditional DBMS namespace. It's used to organize one or more containers.

    Setup the CosmosDB Container

    Now we're ready to set up our logic app an write to Cosmos DB!

    Setup Logic App connection + event

    Once the Cosmos DB SQL API account is created, we can set up our Logic App. From the portal dashboard, Select Create a resource > Integration > Logic App > Create. Name your Logic App and select a location, the rest fo the settings can be left at their defaults. Once you new Logic App is created, select Create a workflow from designer to get started.

    A workflow is a series of steps that defines a task or process. Each workflow starts with a single trigger, after which you must add one or more actions.

    When in designer, search for weather on the right under Add a trigger. Choose MSN Weather. Choose When the current conditions change as the trigger.

    A trigger is always the first step in any workflow and specifies the condition for running any further steps in that workflow.

    Add a location. Valid locations are City, Region, State, Country, Landmark, Postal Code, latitude and longitude. This triggers a new workflow when the conditions change for a location.

    Write data from Logic App to Cosmos DB

    Now we are ready to set up the action to write data to Cosmos DB. Choose add an action and choose Cosmos DB.

    An action is each step in a workflow after the trigger. Every action runs some operation in a workflow.

    In this case, we will be writing a JSON document to the Cosmos DB container we created earlier. Choose Create or Update Document from the actions. At this point you should have a workflow in designer that looks something like this:

    Logic App workflow with trigger

    Start wth the connection for set up the Cosmos DB action. Select Access Key, and provide the primary read-write key (found under keys in Cosmos DB), and the Cosmos DB account ID (without 'documents.azure.com').

    Next, fill in your Cosmos DB Database ID and Collection ID. Create a JSON document bt selecting dynamic content elements and wrapping JSON formatting around them.

    You will need a unique ID for each document that you write to Cosmos DB, for that you can use an expression. Because we declared id to be our unique ID in Cosmos DB, we will use use that for the name. Under expressions, type guid() and press enter to add a unique ID to the JSON document. When complete, you should have a workflow in designer that looks something like this:

    Logic App workflow with trigger and action

    Save the workflow and test the connections by clicking Run Trigger > Run. If connections are working, you should see documents flowing into Cosmos DB over the next few minutes.

    Check the data in Cosmos Db by opening the Data explorer, then choosing the container you created and selecting items. You should see documents similar to this:

    Logic App workflow with trigger and action

    Resources: For self-study!

    Once you've grasped the basics in ths post, there is so much more to learn!

    Thanks for stopping by!

    - - + + \ No newline at end of file diff --git a/blog/tags/serverless-september/page/13/index.html b/blog/tags/serverless-september/page/13/index.html index d8ce46eb4b..a014ea71dd 100644 --- a/blog/tags/serverless-september/page/13/index.html +++ b/blog/tags/serverless-september/page/13/index.html @@ -14,13 +14,13 @@ - - + +

    33 posts tagged with "serverless-september"

    View All Tags

    · 4 min read
    Nitya Narasimhan
    Devanshi Joshi

    Welcome to Day 15 of #30DaysOfServerless!

    This post marks the midpoint of our Serverless on Azure journey! Our Week 2 Roadmap showcased two key technologies - Azure Container Apps (ACA) and Dapr - for building serverless microservices. We'll also look at what happened elsewhere in #ServerlessSeptember, then set the stage for our next week's focus: Serverless Integrations.

    Ready? Let's Go!


    What We'll Cover

    • ICYMI: This Week on #ServerlessSeptember
    • Recap: Microservices, Azure Container Apps & Dapr
    • Coming Next: Serverless Integrations
    • Exercise: Take the Cloud Skills Challenge
    • Resources: For self-study!

    This Week In Events

    We had a number of activities happen this week - here's a quick summary:

    This Week in #30Days

    In our #30Days series we focused on Azure Container Apps and Dapr.

    • In Hello Container Apps we learned how Azure Container Apps helps you run microservices and containerized apps on serverless platforms. And we build and deployed our first ACA.
    • In Microservices Communication we explored concepts like environments and virtual networking, with a hands-on example to show how two microservices communicate in a deployed ACA.
    • In Scaling Your Container Apps we learned about KEDA (Kubernetes Event-Driven Autoscaler) and how to configure autoscaling for your ACA based on KEDA-supported triggers.
    • In Build with Dapr we introduced the Distributed Application Runtime (Dapr) and learned how its Building Block APIs and sidecar architecture make it easier to develop microservices with ACA.
    • In Secure ACA Access we learned how to secure ACA access to external services with - and without - Dapr, covering Secret Stores and Managed Identity.
    • Finally, Build ACA with Dapr tied it all together with a enterprise app scenario where an orders processor (ACA) uses Dapr APIs (PubSub, State Management) to receive and store order messages from Azure Service Bus.

    Here's a visual recap:

    Self Study: Code Samples & Tutorials

    There's no better way to get familiar with the concepts, than to dive in and play with code samples and hands-on tutorials. Here are 4 resources to bookmark and try out:

    1. Dapr Quickstarts - these walk you through samples showcasing individual Building Block APIs - with multiple language options available.
    2. Dapr Tutorials provides more complex examples of microservices applications and tools usage, including a Distributed Calculator polyglot app.
    3. Next, try to Deploy a Dapr application to Azure Container Apps to get familiar with the process of setting up the environment, then deploying the app.
    4. Or, explore the many Azure Container Apps samples showcasing various features and more complex architectures tied to real world scenarios.

    What's Next: Serverless Integrations!

    So far we've talked about core technologies (Azure Functions, Azure Container Apps, Dapr) that provide foundational support for your serverless solution. Next, we'll look at Serverless Integrations - specifically at technologies like Azure Logic Apps and Azure Event Grid that automate workflows and create seamless end-to-end solutions that integrate other Azure services in serverless-friendly ways.

    Take the Challenge!

    The Cloud Skills Challenge is still going on, and we've already had hundreds of participants join and complete the learning modules to skill up on Serverless.

    There's still time to join and get yourself on the leaderboard. Get familiar with Azure Functions, SignalR, Logic Apps, Azure SQL and more - in serverless contexts!!


    - - + + \ No newline at end of file diff --git a/blog/tags/serverless-september/page/14/index.html b/blog/tags/serverless-september/page/14/index.html index 414811fd8a..6483723270 100644 --- a/blog/tags/serverless-september/page/14/index.html +++ b/blog/tags/serverless-september/page/14/index.html @@ -14,8 +14,8 @@ - - + +
    @@ -24,7 +24,7 @@ Image showing container apps role assignment

  • Lastly, we need to restart the container app revision, to do so run the command below:

     ##Get revision name and assign it to a variable
    $REVISION_NAME = (az containerapp revision list `
    --name $BACKEND_SVC_NAME `
    --resource-group $RESOURCE_GROUP `
    --query [0].name)

    ##Restart revision by name
    az containerapp revision restart `
    --resource-group $RESOURCE_GROUP `
    --name $BACKEND_SVC_NAME `
    --revision $REVISION_NAME
  • Run end-to-end Test on Azure

    From the Azure Portal, select the Azure Container App orders-processor and navigate to Log stream under Monitoring tab, leave the stream connected and opened. From the Azure Portal, select the Azure Service Bus Namespace ordersservices, select the topic orderreceivedtopic, select the subscription named orders-processor-subscription, then click on Service Bus Explorer (preview). From there we need to publish/send a message. Use the JSON payload below

    ```json
    {
    "data": {
    "reference": "Order 150",
    "quantity": 150,
    "createdOn": "2022-05-10T12:45:22.0983978Z"
    }
    }
    ```

    If all is configured correctly, you should start seeing the information logs in Container Apps Log stream, similar to the images below Image showing publishing messages from Azure Service

    Information logs on the Log stream of the deployed Azure Container App Image showing ACA Log Stream

    🎉 CONGRATULATIONS

    You have successfully deployed to the cloud an Azure Container App and configured Dapr Pub/Sub API with Azure Service Bus.

    9. Clean up

    If you are done with the tutorial, use the following command to delete the resource group and all its contained resources to avoid incurring further costs.

    az group delete --name $RESOURCE_GROUP

    Exercise

    I left for you the configuration of the Dapr State Store API with Azure Cosmos DB :)

    When you look at the action method OrderReceived in controller ExternalOrdersController, you will see that I left a line with ToDo: note, this line is responsible to save the received message (OrderModel) into Azure Cosmos DB.

    There is no need to change anything on the code base (other than removing this commented line), that's the beauty of Dapr Building Blocks and how easy it allows us to plug components to our microservice application without any plumping and brining external SDKs.

    For sure you need to work on the configuration part of Dapr State Store by creating a new component file like what we have done with the Pub/Sub API, things that you need to work on are:

    • Provision Azure Cosmos DB Account and obtain its masterKey.
    • Create a Dapr Component file adhering to Dapr Specs.
    • Create an Azure Container Apps component file adhering to ACA component specs.
    • Test locally on your dev machine using Dapr Component file.
    • Register the new Dapr State Store component with Azure Container Apps Environment and set the Cosmos Db masterKey from the Azure Portal. If you want to challenge yourself more, use the Managed Identity approach as done in this post! The right way to protect your keys and you will not worry about managing CosmosDb keys anymore!
    • Build a new image of the application and push it to Azure Container Registry.
    • Update Azure Container Apps and create a new revision which contains the updated code.
    • Verify the results by checking Azure Cosmos DB, you should see the Order Model stored in Cosmos DB.

    If you need help, you can always refer to my blog post Azure Container Apps State Store With Dapr State Management API which contains exactly what you need to implement here, so I'm very confident you will be able to complete this exercise with no issues, happy coding :)

    What's Next?

    If you enjoyed working with Dapr and Azure Container Apps, and you want to have a deep dive with more complex scenarios (Dapr bindings, service discovery, auto scaling with KEDA, sync services communication, distributed tracing, health probes, etc...) where multiple services deployed to a single Container App Environment; I have created a detailed tutorial which should walk you through step by step with through details to build the application.

    So far, the published posts below, and I'm publishing more posts on weekly basis, so stay tuned :)

    Resources

    - - + + \ No newline at end of file diff --git a/blog/tags/serverless-september/page/15/index.html b/blog/tags/serverless-september/page/15/index.html index 97d173bfc1..f793d7a81b 100644 --- a/blog/tags/serverless-september/page/15/index.html +++ b/blog/tags/serverless-september/page/15/index.html @@ -14,14 +14,14 @@ - - + +

    33 posts tagged with "serverless-september"

    View All Tags

    · 11 min read
    Kendall Roden

    Welcome to Day 13 of #30DaysOfServerless!

    In the previous post, we learned about all things Distributed Application Runtime (Dapr) and highlighted the capabilities you can unlock through managed Dapr in Azure Container Apps! Today, we'll dive into how we can make use of Container Apps secrets and managed identities to securely access cloud-hosted resources that your Container Apps depend on!

    Ready? Let's go.


    What We'll Cover

    • Secure access to external services overview
    • Using Container Apps Secrets
    • Using Managed Identity for connecting to Azure resources
    • Using Dapr secret store component references (Dapr-only)
    • Conclusion
    • Resources: For self-study!


    Securing access to external services

    In most, if not all, microservice-based applications, one or more services in the system will rely on other cloud-hosted resources; Think external services like databases, secret stores, message brokers, event sources, etc. To interact with these services, an application must have the ability to establish a secure connection. Traditionally, an application will authenticate to these backing resources using some type of connection string or password.

    I'm not sure if it was just me, but one of the first things I learned as a developer was to ensure credentials and other sensitive information were never checked into the codebase. The ability to inject these values at runtime is a non-negotiable.

    In Azure Container Apps, applications can securely leverage connection information via Container Apps Secrets. If the resource is Azure-based, a more ideal solution that removes the dependence on secrets altogether is using Managed Identity.

    Specifically for Dapr-enabled container apps, users can now tap into the power of the Dapr secrets API! With this new capability unlocked in Container Apps, users can call the Dapr secrets API from application code to securely access secrets from Key Vault or other backing secret stores. In addition, customers can also make use of a secret store component reference when wiring up Dapr state store components and more!

    ALSO, I'm excited to share that support for Dapr + Managed Identity is now available!!. What does this mean? It means that you can enable Managed Identity for your container app - and when establishing connections via Dapr, the Dapr sidecar can use this identity! This means simplified components without the need for secrets when connecting to Azure services!

    Let's dive a bit deeper into the following three topics:

    1. Using Container Apps secrets in your container apps
    2. Using Managed Identity to connect to Azure services
    3. Connecting to services securely for Dapr-enabled apps

    Secure access to external services without Dapr

    Leveraging Container Apps secrets at runtime

    Users can leverage this approach for any values which need to be securely stored, however, it is recommended to use Managed Identity where possible when connecting to Azure-specific resources.

    First, let's establish a few important points regarding secrets in container apps:

    • Secrets are scoped at the container app level, meaning secrets cannot be shared across container apps today
    • When running in multiple-revision mode,
      • changes to secrets do not generate a new revision
      • running revisions will not be automatically restarted to reflect changes. If you want to force-update existing container app revisions to reflect the changed secrets values, you will need to perform revision restarts.
    STEP 1

    Provide the secure value as a secret parameter when creating your container app using the syntax "SECRET_NAME=SECRET_VALUE"

    az containerapp create \
    --resource-group "my-resource-group" \
    --name queuereader \
    --environment "my-environment-name" \
    --image demos/queuereader:v1 \
    --secrets "queue-connection-string=$CONNECTION_STRING"
    STEP 2

    Create an environment variable which references the value of the secret created in step 1 using the syntax "ENV_VARIABLE_NAME=secretref:SECRET_NAME"

    az containerapp create \
    --resource-group "my-resource-group" \
    --name myQueueApp \
    --environment "my-environment-name" \
    --image demos/myQueueApp:v1 \
    --secrets "queue-connection-string=$CONNECTIONSTRING" \
    --env-vars "QueueName=myqueue" "ConnectionString=secretref:queue-connection-string"

    This ConnectionString environment variable can be used within your application code to securely access the connection string value at runtime.

    Using Managed Identity to connect to Azure services

    A managed identity from Azure Active Directory (Azure AD) allows your container app to access other Azure AD-protected resources. This approach is recommended where possible as it eliminates the need for managing secret credentials in your container apps and allows you to properly scope the permissions needed for a given container app using role-based access control. Both system-assigned and user-assigned identities are available in container apps. For more background on managed identities in Azure AD, see Managed identities for Azure resources.

    To configure your app with a system-assigned managed identity you will follow similar steps to the following:

    STEP 1

    Run the following command to create a system-assigned identity for your container app

    az containerapp identity assign \
    --name "myQueueApp" \
    --resource-group "my-resource-group" \
    --system-assigned
    STEP 2

    Retrieve the identity details for your container app and store the Principal ID for the identity in a variable "PRINCIPAL_ID"

    az containerapp identity show \
    --name "myQueueApp" \
    --resource-group "my-resource-group"
    STEP 3

    Assign the appropriate roles and permissions to your container app's managed identity using the Principal ID in step 2 based on the resources you need to access (example below)

    az role assignment create \
    --role "Storage Queue Data Contributor" \
    --assignee $PRINCIPAL_ID \
    --scope "/subscriptions/<subscription>/resourceGroups/<resource-group>/providers/Microsoft.Storage/storageAccounts/<storage-account>/queueServices/default/queues/<queue>"

    After running the above commands, your container app will be able to access your Azure Store Queue because it's managed identity has been assigned the "Store Queue Data Contributor" role. The role assignments you create will be contingent solely on the resources your container app needs to access. To instrument your code to use this managed identity, see more details here.

    In addition to using managed identity to access services from your container app, you can also use managed identity to pull your container images from Azure Container Registry.

    Secure access to external services with Dapr

    For Dapr-enabled apps, there are a few ways to connect to the resources your solutions depend on. In this section, we will discuss when to use each approach.

    1. Using Container Apps secrets in your Dapr components
    2. Using Managed Identity with Dapr Components
    3. Using Dapr Secret Stores for runtime secrets and component references

    Using Container Apps secrets in Dapr components

    Prior to providing support for the Dapr Secret's Management building block, this was the only approach available for securely storing sensitive values for use in Dapr components.

    In Dapr OSS, when no secret store reference is provided in a Dapr component file, the default secret store is set to "Kubernetes secrets". In Container Apps, we do not expose the ability to use this default store. Rather, Container Apps secrets can be used in it's place.

    With the introduction of the Secrets API and the ability to use Dapr + Managed Identity, this approach is useful for a limited number of scenarios:

    • Quick demos and dev/test scenarios using the Container Apps CLI
    • Securing values when a secret store is not configured or available for use
    • Using service principal credentials to configure an Azure Key Vault secret store component (Using Managed Identity is recommend)
    • Securing access credentials which may be required when creating a non-Azure secret store component
    STEP 1

    Create a Dapr component which can be used by one or more services in the container apps environment. In the below example, you will create a secret to store the storage account key and reference this secret from the appropriate Dapr metadata property.

       componentType: state.azure.blobstorage
    version: v1
    metadata:
    - name: accountName
    value: testStorage
    - name: accountKey
    secretRef: account-key
    - name: containerName
    value: myContainer
    secrets:
    - name: account-key
    value: "<STORAGE_ACCOUNT_KEY>"
    scopes:
    - myApp
    STEP 2

    Deploy the Dapr component using the below command with the appropriate arguments.

     az containerapp env dapr-component set \
    --name "my-environment" \
    --resource-group "my-resource-group" \
    --dapr-component-name statestore \
    --yaml "./statestore.yaml"

    Using Managed Identity with Dapr Components

    Dapr-enabled container apps can now make use of managed identities within Dapr components. This is the most ideal path for connecting to Azure services securely, and allows for the removal of sensitive values in the component itself.

    The Dapr sidecar makes use of the existing identities available within a given container app; Dapr itself does not have it's own identity. Therefore, the steps to enable Dapr + MI are similar to those in the section regarding managed identity for non-Dapr apps. See example steps below specifically for using a system-assigned identity:

    1. Create a system-assigned identity for your container app

    2. Retrieve the identity details for your container app and store the Principal ID for the identity in a variable "PRINCIPAL_ID"

    3. Assign the appropriate roles and permissions (for accessing resources backing your Dapr components) to your ACA's managed identity using the Principal ID

    4. Create a simplified Dapr component without any secrets required

          componentType: state.azure.blobstorage
      version: v1
      metadata:
      - name: accountName
      value: testStorage
      - name: containerName
      value: myContainer
      scopes:
      - myApp
    5. Deploy the component to test the connection from your container app via Dapr!

    Keep in mind, all Dapr components will be loaded by each Dapr-enabled container app in an environment by default. In order to avoid apps without the appropriate permissions from loading a component unsuccessfully, use scopes. This will ensure that only applications with the appropriate identities to access the backing resource load the component.

    Using Dapr Secret Stores for runtime secrets and component references

    Dapr integrates with secret stores to provide apps and other components with secure storage and access to secrets such as access keys and passwords. The Dapr Secrets API is now available for use in Container Apps.

    Using Dapr’s secret store building block typically involves the following:

    • Setting up a component for a specific secret store solution.
    • Retrieving secrets using the Dapr secrets API in the application code.
    • Optionally, referencing secrets in Dapr component files.

    Let's walk through a couple sample workflows involving the use of Dapr's Secrets Management capabilities!

    Setting up a component for a specific secret store solution

    1. Create an Azure Key Vault instance for hosting the secrets required by your application.

      az keyvault create --name "<your-unique-keyvault-name>" --resource-group "my-resource-group" --location "<your-location>"
    2. Create an Azure Key Vault component in your environment without the secrets values, as the connection will be established to Azure Key Vault via Managed Identity.

          componentType: secretstores.azure.keyvault
      version: v1
      metadata:
      - name: vaultName
      value: "[your_keyvault_name]"
      scopes:
      - myApp
      az containerapp env dapr-component set \
      --name "my-environment" \
      --resource-group "my-resource-group" \
      --dapr-component-name secretstore \
      --yaml "./secretstore.yaml"
    3. Run the following command to create a system-assigned identity for your container app

      az containerapp identity assign \
      --name "myApp" \
      --resource-group "my-resource-group" \
      --system-assigned
    4. Retrieve the identity details for your container app and store the Principal ID for the identity in a variable "PRINCIPAL_ID"

      az containerapp identity show \
      --name "myApp" \
      --resource-group "my-resource-group"
    5. Assign the appropriate roles and permissions to your container app's managed identity to access Azure Key Vault

      az role assignment create \
      --role "Key Vault Secrets Officer" \
      --assignee $PRINCIPAL_ID \
      --scope /subscriptions/{subscriptionid}/resourcegroups/{resource-group-name}/providers/Microsoft.KeyVault/vaults/{key-vault-name}
    6. Begin using the Dapr Secrets API in your application code to retrieve secrets! See additional details here.

    Referencing secrets in Dapr component files

    Once a Dapr secret store component is available in the environment, it can be used to retrieve secrets for use in other components. For example, when creating a state store component, you can add a reference to the Dapr secret store from which you would like to source connection information. You will no longer use secrets directly in the component spec, but rather will instruct the Dapr sidecar to retrieve the secrets from the specified store.

          componentType: state.azure.blobstorage
    version: v1
    metadata:
    - name: accountName
    value: testStorage
    - name: accountKey
    secretRef: account-key
    - name: containerName
    value: myContainer
    secretStoreComponent: "<SECRET_STORE_COMPONENT_NAME>"
    scopes:
    - myApp

    Summary

    In this post, we have covered the high-level details on how to work with secret values in Azure Container Apps for both Dapr and Non-Dapr apps. In the next article, we will walk through a complex Dapr example from end-to-end which makes use of the new support for Dapr + Managed Identity. Stayed tuned for additional documentation around Dapr secrets as it will be release in the next two weeks!

    Resources

    Here are the main resources to explore for self-study:

    - - + + \ No newline at end of file diff --git a/blog/tags/serverless-september/page/16/index.html b/blog/tags/serverless-september/page/16/index.html index 62cd15b9d5..0331c27e60 100644 --- a/blog/tags/serverless-september/page/16/index.html +++ b/blog/tags/serverless-september/page/16/index.html @@ -14,13 +14,13 @@ - - + +

    33 posts tagged with "serverless-september"

    View All Tags

    · 8 min read
    Nitya Narasimhan

    Welcome to Day 12 of #30DaysOfServerless!

    So far we've looked at Azure Container Apps - what it is, how it enables microservices communication, and how it enables auto-scaling with KEDA compliant scalers. Today we'll shift gears and talk about Dapr - the Distributed Application Runtime - and how it makes microservices development with ACA easier with core building blocks and a sidecar architecture!

    Ready? Let's go!


    What We'll Cover

    • What is Dapr and why use it?
    • Building Block APIs
    • Dapr Quickstart and Tutorials
    • Dapr-enabled ACA: A Sidecar Approach
    • Exercise: Build & Deploy a Dapr-enabled ACA.
    • Resources: For self-study!


    Hello, Dapr!

    Building distributed applications is hard. Building reliable and portable microservces means having middleware that deals with challenges like service discovery, sync and async communications, state management, secure information sharing and more. Integrating these support services into your application can be challenging from both development and maintenance perspectives, adding complexity that is independent of the core application logic you want to focus on.

    This is where Dapr (Distributed Application Runtime) shines - it's defined as::

    a portable, event-driven runtime that makes it easy for any developer to build resilient, stateless and stateful applications that run on the cloud and edge and embraces the diversity of languages and developer frameworks.

    But what does this actually mean to me as an app developer?


    Dapr + Apps: A Sidecar Approach

    The strength of Dapr lies in its ability to:

    • abstract complexities of distributed systems middleware - with Building Block APIs that implement components using best practices to tackle key challenges.
    • implement a Sidecar Pattern with interactions via APIs - allowing applications to keep their codebase clean and focus on app logic.
    • be Incrementally Adoptable - allowing developers to start by integrating one API, then evolving to use more as and when needed.
    • be Platform Agnostic - allowing applications to be developed in a preferred language or framework without impacting integration capabilities.

    The application-dapr sidecar interaction is illustrated below. The API abstraction allows applications to get the desired functionality without having to know how it was implemented, or without having to integrate Dapr-specific code into their codebase. Note how the sidecar process listens on port 3500 and the API provides clear routes for the specific building blocks supported by Dapr (e.g, /secrets, /state etc.)


    Dapr Building Blocks: API Interactions

    Dapr Building Blocks refers to HTTP and gRPC endpoints exposed by Dapr API endpoints exposed by the Dapr sidecar, providing key capabilities like state management, observability, service-to-service invocation, pub/sub messaging and more to the associated application.

    Building Blocks: Under the Hood
    The Dapr API is implemented by modular components that codify best practices for tackling the specific challenge that they represent. The API abstraction allows component implementations to evolve, or alternatives to be used , without requiring changes to the application codebase.

    The latest Dapr release has the building blocks shown in the above figure. Not all capabilities are available to Azure Container Apps by default - check the documentation for the latest updates on this. For now, Azure Container Apps + Dapr integration provides the following capabilities to the application:

    In the next section, we'll dive into Dapr-enabled Azure Container Apps. Before we do that, here are a couple of resources to help you explore the Dapr platform by itself, and get more hands-on experience with the concepts and capabilities:

    • Dapr Quickstarts - build your first Dapr app, then explore quickstarts for a core APIs including service-to-service invocation, pub/sub, state mangement, bindings and secrets management.
    • Dapr Tutorials - go beyond the basic quickstart and explore more realistic service integrations and usage scenarios. Try the distributed calculator example!

    Integrate Dapr & Azure Container Apps

    Dapr currently has a v1.9 (preview) version, but Azure Container Apps supports Dapr v1.8. In this section, we'll look at what it takes to enable, configure, and use, Dapr integration with Azure Container Apps. It involves 3 steps: enabling Dapr using settings, configuring Dapr components (API) for use, then invoking the APIs.

    Here's a simple a publisher-subscriber scenario from the documentation. We have two Container apps identified as publisher-app and subscriber-app deployed in a single environment. Each ACA has an activated daprd sidecar, allowing them to use the Pub/Sub API to communicate asynchronously with each other - without having to write the underlying pub/sub implementation themselves. Rather, we can see that the Dapr API uses a pubsub,azure.servicebus component to implement that capability.

    Pub/sub example

    Let's look at how this is setup.

    1. Enable Dapr in ACA: Settings

    We can enable Dapr integration in the Azure Container App during creation by specifying settings in one of two ways, based on your development preference:

    • Using Azure CLI: use custom commandline options for each setting
    • Using Infrastructure-as-Code (IaC): using properties for Bicep, ARM templates

    Once enabled, Dapr will run in the same environment as the Azure Container App, and listen on port 3500 for API requests. The Dapr sidecar can be shared my multiple Container Apps deployed in the same environment.

    There are four main settings we will focus on for this demo - the example below shows the ARM template properties, but you can find the equivalent CLI parameters here for comparison.

    • dapr.enabled - enable Dapr for Azure Container App
    • dapr.appPort - specify port on which app is listening
    • dapr.appProtocol - specify if using http (default) or gRPC for API
    • dapr.appId - specify unique application ID for service discovery, usage

    These are defined under the properties.configuration section for your resource. Changing Dapr settings does not update the revision but it will restart ACA revisions and replicas. Here is what the relevant section of the ARM template looks like for the publisher-app ACA in the scenario shown above.

    "dapr": {
    "enabled": true,
    "appId": "publisher-app",
    "appProcotol": "http",
    "appPort": 80
    }

    2. Configure Dapr in ACA: Components

    The next step after activating the Dapr sidecar, is to define the APIs that you want to use and potentially specify the Dapr components (specific implementations of that API) that you prefer. These components are created at environment-level and by default, Dapr-enabled containers apps in an environment will load the complete set of deployed components -- use the scopes property to ensure only components needed by a given app are loaded at runtime. Here's what the ARM template resources section looks like for the example above. This tells us that the environment has a dapr-pubsub component of type pubsub.azure.servicebus deployed - where that component is loaded by container apps with dapr ids (publisher-app, subscriber-app).

    USING MANAGED IDENTITY + DAPR

    The secrets approach used here is idea for demo purposes. However, we recommend using Managed Identity with Dapr in production. For more details on secrets, check out tomorrow's post on Secrets and Managed Identity in Azure Container Apps

    {
    "resources": [
    {
    "type": "daprComponents",
    "name": "dapr-pubsub",
    "properties": {
    "componentType": "pubsub.azure.servicebus",
    "version": "v1",
    "secrets": [
    {
    "name": "sb-root-connectionstring",
    "value": "value"
    }
    ],
    "metadata": [
    {
    "name": "connectionString",
    "secretRef": "sb-root-connectionstring"
    }
    ],
    // Application scopes
    "scopes": ["publisher-app", "subscriber-app"]

    }
    }
    ]
    }

    With this configuration, the ACA is now set to use pub/sub capabilities from the Dapr sidecar, using standard HTTP requests to the exposed API endpoint for this service.

    Exercise: Deploy Dapr-enabled ACA

    In the next couple posts in this series, we'll be discussing how you can use the Dapr secrets API and doing a walkthrough of a more complex example, to show how Dapr-enabled Azure Container Apps are created and deployed.

    However, you can get hands-on experience with these concepts by walking through one of these two tutorials, each providing an alternative approach to configure and setup the application describe in the scenario below:

    Resources

    Here are the main resources to explore for self-study:

    - - + + \ No newline at end of file diff --git a/blog/tags/serverless-september/page/17/index.html b/blog/tags/serverless-september/page/17/index.html index 44138980e1..d319a2e8f7 100644 --- a/blog/tags/serverless-september/page/17/index.html +++ b/blog/tags/serverless-september/page/17/index.html @@ -14,13 +14,13 @@ - - + +

    33 posts tagged with "serverless-september"

    View All Tags

    · 6 min read
    Melony Qin

    Welcome to Day 12 of #30DaysOfServerless!

    Today, we have a special set of posts from our Zero To Hero 🚀 initiative, featuring blog posts authored by our Product Engineering teams for #ServerlessSeptember. Posts were originally published on the Apps on Azure blog on Microsoft Tech Community.


    What We'll Cover

    • What are Custom Handlers, and why use them?
    • How Custom Handler Works
    • Message Processing With Azure Custom Handler
    • Azure Portal Monitoring


    If you have been working with Azure Functions for a while, you may know Azure Functions is a serverless FaaS (Function as a Service) offered provided by Microsoft Azure, which is built for your key scenarios, including building web APIs, processing file uploads, responding to database changes, processing IoT data streams, managing message queues, and more.

    Custom Handlers: What and Why

    Azure functions support multiple programming languages including C#, F#, Java, JavaScript, Python, typescript, and PowerShell. If you want to get extended language support with Azure functions for other languages such as Go, and Rust, that’s where custom handler comes in.

    An Azure function custom handler allows the use of any language that supports HTTP primitives and author Azure functions. With custom handlers, you can use triggers and input and output bindings via extension bundles, hence it supports all the triggers and bindings you're used to with Azure functions.

    How a Custom Handler Works

    Let’s take a look at custom handlers and how it works.

    • A request is sent to the function host when an event is triggered. It’s up to the function host to issue a request payload to the custom handler, which holds the trigger and inputs binding data as well as other metadata for the function.
    • The custom handler is a local HTTP web server. It executes the function code and returns a response payload to the Functions host.
    • The Functions host passes data from the response to the function's output bindings which will be passed to the downstream stream services for data processing.

    Check out this article to know more about Azure functions custom handlers.


    Message processing with Custom Handlers

    Message processing is one of the key scenarios that Azure functions are trying to address. In the message-processing scenario, events are often collected in queues. These events can trigger Azure functions to execute a piece of business logic.

    You can use the Service Bus trigger to respond to messages from an Azure Service Bus queue - it's then up to the Azure functions custom handlers to take further actions to process the messages. The process is described in the following diagram:

    Building Serverless Go Applications with Azure functions custom handlers

    In Azure function, the function.json defines the function's trigger, input and output bindings, and other configuration settings. Note that every function can have multiple bindings, but it can only have one trigger. The following is an example of setting up the Service Bus queue trigger in the function.json file :

    {
    "bindings": [
    {
    "name": "queueItem",
    "type": "serviceBusTrigger",
    "direction": "in",
    "queueName": "functionqueue",
    "connection": "ServiceBusConnection"
    }
    ]
    }

    You can add a binding definition in the function.json to write the output to a database or other locations of your desire. Supported bindings can be found here.

    As we’re programming in Go, so we need to set the value of defaultExecutablePath to handler in the customHandler.description section in the host.json file.

    Assume we’re programming in Windows OS, and we have named our go application as server.go, after we executed go build server.go command, it produces an executable called server.exe. So here we set server.exe in the host.json as the following example :

      "customHandler": {
    "description": {
    "defaultExecutablePath": "./server.exe",
    "workingDirectory": "",
    "arguments": []
    }
    }

    We’re showcasing a simple Go application here with Azure functions custom handlers where we print out the messages received from the function host. The following is the full code of server.go application :

    package main

    import (
    "encoding/json"
    "fmt"
    "log"
    "net/http"
    "os"
    )

    type InvokeRequest struct {
    Data map[string]json.RawMessage
    Metadata map[string]interface{}
    }

    func queueHandler(w http.ResponseWriter, r *http.Request) {
    var invokeRequest InvokeRequest

    d := json.NewDecoder(r.Body)
    d.Decode(&invokeRequest)

    var parsedMessage string
    json.Unmarshal(invokeRequest.Data["queueItem"], &parsedMessage)

    fmt.Println(parsedMessage)
    }

    func main() {
    customHandlerPort, exists := os.LookupEnv("FUNCTIONS_CUSTOMHANDLER_PORT")
    if !exists {
    customHandlerPort = "8080"
    }
    mux := http.NewServeMux()
    mux.HandleFunc("/MessageProcessorFunction", queueHandler)
    fmt.Println("Go server Listening on: ", customHandlerPort)
    log.Fatal(http.ListenAndServe(":"+customHandlerPort, mux))

    }

    Ensure you have Azure functions core tools installed, then we can use func start command to start our function. Then we’ll have have a C#-based Message Sender application on Github to send out 3000 messages to the Azure service bus queue. You’ll see Azure functions instantly start to process the messages and print out the message as the following:

    Monitoring Serverless Go Applications with Azure functions custom handlers


    Azure portal monitoring

    Let’s go back to Azure portal portal the events see how those messages in Azure Service Bus queue were being processed. There was 3000 messages were queued in the Service Bus queue ( the Blue line stands for incoming Messages ). The outgoing messages (the red line in smaller wave shape ) showing there are progressively being read by Azure functions as the following :

    Monitoring Serverless Go Applications with Azure functions custom handlers

    Check out this article about monitoring Azure Service bus for further information.

    Next steps

    Thanks for following along, we’re looking forward to hearing your feedback. Also, if you discover potential issues, please record them on Azure Functions host GitHub repository or tag us @AzureFunctions on Twitter.

    RESOURCES

    Start to build your serverless applications with custom handlers, check out the official documentation:

    Life is a journey of learning. Let’s stay tuned!

    - - + + \ No newline at end of file diff --git a/blog/tags/serverless-september/page/18/index.html b/blog/tags/serverless-september/page/18/index.html index 7b7c5e30fc..9364644d58 100644 --- a/blog/tags/serverless-september/page/18/index.html +++ b/blog/tags/serverless-september/page/18/index.html @@ -14,13 +14,13 @@ - - + +

    33 posts tagged with "serverless-september"

    View All Tags

    · 5 min read
    Anthony Chu

    Welcome to Day 12 of #30DaysOfServerless!

    Today, we have a special set of posts from our Zero To Hero 🚀 initiative, featuring blog posts authored by our Product Engineering teams for #ServerlessSeptember. Posts were originally published on the Apps on Azure blog on Microsoft Tech Community.


    What We'll Cover

    • Using Visual Studio
    • Using Visual Studio Code: Docker, ACA extensions
    • Using Azure CLI
    • Using CI/CD Pipelines


    Last week, @kendallroden wrote about what it means to be Cloud-Native and how Azure Container Apps provides a serverless containers platform for hosting all of your Cloud-Native applications. Today, we’ll walk through a few ways to get your apps up and running on Azure Container Apps.

    Depending on where you are in your Cloud-Native app development journey, you might choose to use different tools to deploy your apps.

    • “Right-click, publish” – Deploying an app directly from an IDE or code editor is often seen as a bad practice, but it’s one of the quickest ways to test out an app in a cloud environment.
    • Command line interface – CLIs are useful for deploying apps from a terminal. Commands can be run manually or in a script.
    • Continuous integration/deployment – To deploy production apps, the recommended approach is to automate the process in a robust CI/CD pipeline.

    Let's explore some of these options in more depth.

    Visual Studio

    Visual Studio 2022 has built-in support for deploying .NET applications to Azure Container Apps. You can use the familiar publish dialog to provision Container Apps resources and deploy to them directly. This helps you prototype an app and see it run in Azure Container Apps with the least amount of effort.

    Journey to the cloud with Azure Container Apps

    Once you’re happy with the app and it’s ready for production, Visual Studio allows you to push your code to GitHub and set up a GitHub Actions workflow to build and deploy your app every time you push changes. You can do this by checking a box.

    Journey to the cloud with Azure Container Apps

    Visual Studio Code

    There are a couple of valuable extensions that you’ll want to install if you’re working in VS Code.

    Docker extension

    The Docker extension provides commands for building a container image for your app and pushing it to a container registry. It can even do this without requiring Docker Desktop on your local machine --- the “Build image in Azure” command remotely builds and pushes a container image to Azure Container Registry.

    Journey to the cloud with Azure Container Apps

    And if your app doesn’t have a dockerfile, the extension will generate one for you.

    Journey to the cloud with Azure Container Apps

    Azure Container Apps extension

    Once you’ve built your container image and pushed it to a registry, the Azure Container Apps VS Code extension provides commands for creating a container app and deploying revisions using the image you’ve built.

    Journey to the cloud with Azure Container Apps

    Azure CLI

    The Azure CLI can be used to manage pretty much anything in Azure. For Azure Container Apps, you’ll find commands for creating, updating, and managing your Container Apps resources.

    Just like in VS Code, with a few commands in the Azure CLI, you can create your Azure resources, build and push your container image, and then deploy it to a container app.

    To make things as simple as possible, the Azure CLI also has an “az containerapp up” command. This single command takes care of everything that’s needed to turn your source code from your local machine to a cloud-hosted application in Azure Container Apps.

    az containerapp up --name myapp --source ./src

    We saw earlier that Visual Studio can generate a GitHub Actions workflow to automatically build and deploy your app on every commit. “az containerapp up” can do this too. The following adds a workflow to a repo.

    az containerapp up --name myapp --repo https://github.com/myorg/myproject

    CI/CD pipelines

    When it’s time to take your app to production, it’s strongly recommended to set up a CI/CD pipeline to automatically and repeatably build, test, and deploy it. We’ve already seen that tools such as Visual Studio and Azure CLI can automatically generate a workflow for GitHub Actions. You can set up a pipeline in Azure DevOps too. This is an example Azure DevOps pipeline.

    trigger:
    branches:
    include:
    - main

    pool:
    vmImage: ubuntu-latest

    stages:

    - stage: Build

    jobs:
    - job: build
    displayName: Build app

    steps:
    - task: Docker@2
    inputs:
    containerRegistry: 'myregistry'
    repository: 'hello-aca'
    command: 'buildAndPush'
    Dockerfile: 'hello-container-apps/Dockerfile'
    tags: '$(Build.BuildId)'

    - stage: Deploy

    jobs:
    - job: deploy
    displayName: Deploy app

    steps:
    - task: AzureCLI@2
    inputs:
    azureSubscription: 'my-subscription(5361b9d6-46ea-43c3-a898-15f14afb0db6)'
    scriptType: 'bash'
    scriptLocation: 'inlineScript'
    inlineScript: |
    # automatically install Container Apps CLI extension
    az config set extension.use_dynamic_install=yes_without_prompt

    # ensure registry is configured in container app
    az containerapp registry set \
    --name hello-aca \
    --resource-group mygroup \
    --server myregistry.azurecr.io \
    --identity system

    # update container app
    az containerapp update \
    --name hello-aca \
    --resource-group mygroup \
    --image myregistry.azurecr.io/hello-aca:$(Build.BuildId)

    Conclusion

    In this article, we looked at a few ways to deploy your Cloud-Native applications to Azure Container Apps and how to decide which one to use based on where you are in your app’s journey to the cloud.

    To learn more, visit Azure Container Apps | Microsoft Azure today!

    ASK THE EXPERT: LIVE Q&A

    The Azure Container Apps team will answer questions live on September 29.

    - - + + \ No newline at end of file diff --git a/blog/tags/serverless-september/page/19/index.html b/blog/tags/serverless-september/page/19/index.html index e93e80e736..009c633d31 100644 --- a/blog/tags/serverless-september/page/19/index.html +++ b/blog/tags/serverless-september/page/19/index.html @@ -14,13 +14,13 @@ - - + +

    33 posts tagged with "serverless-september"

    View All Tags

    · 7 min read
    Paul Yu

    Welcome to Day 11 of #30DaysOfServerless!

    Yesterday we explored Azure Container Concepts related to environments, networking and microservices communication - and illustrated these with a deployment example. Today, we turn our attention to scaling your container apps with demand.


    What We'll Cover

    • What makes ACA Serverless?
    • What is Keda?
    • Scaling Your ACA
    • ACA Scaling In Action
    • Exercise: Explore azure-opensource-labs examples
    • Resources: For self-study!


    So, what makes Azure Container Apps "serverless"?

    Today we are going to focus on what makes Azure Container Apps (ACA) a "serverless" offering. But what does the term "serverless" really mean? As much as we'd like to think there aren't any servers involved, that is certainly not the case. In general, "serverless" means that most (if not all) server maintenance has been abstracted away from you.

    With serverless, you don't spend any time managing and patching servers. This concern is offloaded to Azure and you simply focus on adding business value through application delivery. In addition to operational efficiency, cost efficiency can be achieved with serverless on-demand pricing models. Your workload horizontally scales out based on need and you only pay for what you use. To me, this is serverless, and my teammate @StevenMurawski said it best... "being able to scale to zero is what gives ACA it's serverless magic."

    Scaling your Container Apps

    If you don't know by now, ACA is built on a solid open-source foundation. Behind the scenes, it runs on a managed Kubernetes cluster and includes several open-source components out-of-the box including Dapr to help you build and run microservices, Envoy Proxy for ingress capabilities, and KEDA for event-driven autoscaling. Again, you do not need to install these components yourself. All you need to be concerned with is enabling and/or configuring your container app to leverage these components.

    Let's take a closer look at autoscaling in ACA to help you optimize your container app.

    What is KEDA?

    KEDA stands for Kubernetes Event-Driven Autoscaler. It is an open-source project initially started by Microsoft and Red Hat and has been donated to the Cloud-Native Computing Foundation (CNCF). It is being maintained by a community of 200+ contributors and adopted by many large organizations. In terms of its status as a CNCF project it is currently in the Incubating Stage which means the project has gone through significant due diligence and on its way towards the Graduation Stage.

    Prior to KEDA, horizontally scaling your Kubernetes deployment was achieved through the Horizontal Pod Autoscaler (HPA) which relies on resource metrics such as CPU and memory to determine when additional replicas should be deployed. Being limited to CPU and memory falls a bit short for certain workloads. This is especially true for apps that need to processes messages from a queue or HTTP-based apps that can handle a specific amount of incoming HTTP requests at a time. KEDA aims to fill that gap and provides a much more robust framework for scaling by working in conjunction with HPA. It offers many scalers for you to implement and even allows your deployments to scale to zero! 🥳

    KEDA architecture

    Configuring ACA scale rules

    As I mentioned above, ACA's autoscaling feature leverages KEDA and gives you the ability to configure the number of replicas to deploy based on rules (event triggers). The number of replicas can be configured as a static number or a range (minimum and maximum). So if you need your containers to run 24/7, set the min and max to be the same value. By default, when you deploy a container app, it is set to scale from 0 to 10 replicas. The default scaling rule uses HTTP scaling and defaults to a minimum of 10 concurrent requests per second. Once the threshold of 10 concurrent request per second is met, another replica will be deployed until it reaches the maximum number of replicas.

    At the time of this writing, a container app can have up to 30 replicas.

    Default autoscaler

    As a best practice, if you have a Min / max replicas range configured, you should configure a scaling rule even if it is just explicitly setting the default values.

    Adding HTTP scaling rule

    In addition to HTTP scaling, you can also configure an Azure queue rule, which allows you to use Azure Storage Queues as an event data source.

    Adding Azure Queue scaling rule

    The most flexibility comes with the Custom rule type. This opens up a LOT more options for scaling. All of KEDA's event-based scalers are supported with this option 🚀

    Adding Custom scaling rule

    Translating KEDA templates to Azure templates

    When you implement Custom rules, you need to become familiar with translating KEDA templates to Azure Resource Manager templates or ACA YAML manifests. The KEDA scaler documentation is great and it should be simple to translate KEDA template metadata to an ACA rule metadata.

    The images below shows how to translated a scaling rule which uses Azure Service Bus as an event data source. The custom rule type is set to azure-servicebus and details of the service bus is added to the Metadata section. One important thing to note here is that the connection string to the service bus was added as a secret on the container app and the trigger parameter must be set to connection.

    Azure Container App custom rule metadata

    Azure Container App custom rule metadata

    Additional examples of KEDA scaler conversion can be found in the resources section and example video below.

    See Container App scaling in action

    Now that we've built up some foundational knowledge on how ACA autoscaling is implemented and configured, let's look at a few examples.

    Autoscaling based on HTTP traffic load

    Autoscaling based on Azure Service Bus message queues

    Summary

    ACA brings you a true serverless experience and gives you the ability to configure autoscaling rules based on KEDA scaler templates. This gives you flexibility to scale based on a wide variety of data sources in an event-driven manner. With the amount built-in scalers currently available, there is probably a scaler out there for all your use cases. If not, I encourage you to get involved with the KEDA community and help make it better!

    Exercise

    By now, you've probably read and seen enough and now ready to give this autoscaling thing a try. The example I walked through in the videos above can be found at the azure-opensource-labs repo. I highly encourage you to head over to the containerapps-terraform folder and try the lab out. There you'll find instructions which will cover all the steps and tools you'll need implement autoscaling container apps within your own Azure subscription.

    If you have any questions or feedback, please let us know in the comments below or reach out on Twitter @pauldotyu

    Have fun scaling your containers!

    Resources

    - - + + \ No newline at end of file diff --git a/blog/tags/serverless-september/page/2/index.html b/blog/tags/serverless-september/page/2/index.html index 9b8fdcc9f2..0773cf2d88 100644 --- a/blog/tags/serverless-september/page/2/index.html +++ b/blog/tags/serverless-september/page/2/index.html @@ -14,8 +14,8 @@ - - + +
    @@ -26,7 +26,7 @@

    ...and that's it! We've successfully deployed our application on Azure!

    But there's more!

    Best practices: Monitoring and CI/CD!

    In my opinion, it's not enough to just set up the application on Azure! I want to know that my web app is performant and serving my users reliably! I also want to make sure that I'm not inadvertently breaking my application as I continue to make changes to it. Thankfully, the Azure Developer CLI also handles all of this via two additional commands - azd monitor and azd pipeline config.

    Application Monitoring

    When we provisioned all of our infrastructure, we also set up application monitoring via a Bicep file in our .infra/ directory that spec'd out an Application Insights dashboard. By running azd monitor we can see the dashboard with live metrics that was configured for the application.

    We can also navigate to the Application Dashboard by clicking on the resource group name, where you can set a specific refresh rate for the dashboard, and see usage, reliability, and performance metrics over time.

    I don't know about everyone else but I have spent a ton of time building out similar dashboards. It can be super time-consuming to write all the queries and create the visualizations so this feels like a real time saver.

    CI/CD

    Finally let's talk about setting up CI/CD! This might be my favorite azd feature. As I mentioned before, the Azure Developer CLI has a command, azd pipeline config, which uses the files in the .github/ directory to set up a GitHub Action. More than that, if there is no upstream repo, the Developer CLI will actually help you create one. But what does this mean exactly? Because our GitHub Action is using the same commands you'd run in the CLI under the hood, we're actually going to have CI/CD set up to run on every commit into the repo, against real Azure resources. What a sweet collaboration feature!

    That's it! We've gone end-to-end with the Azure Developer CLI - initialized a project, provisioned the resources on Azure, deployed our code on Azure, set up monitoring logs and dashboards, and set up a CI/CD pipeline with GitHub Actions to run on every commit into the repo (on real Azure resources!).

    Exercise: Try it yourself or create your own template!

    As an exercise, try out the workflow above with any template on GitHub!

    Or, try turning your own project into an Azure Developer CLI-enabled template by following this guidance. If you create your own template, don't forget to tag the repo with the azd-templates topic on GitHub to help others find it (unfamiliar with GitHub topics? Learn how to add topics to your repo)! We'd also love to chat with you about your experience creating an azd template - if you're open to providing feedback around this, please fill out this form!

    Resources

    - - + + \ No newline at end of file diff --git a/blog/tags/serverless-september/page/20/index.html b/blog/tags/serverless-september/page/20/index.html index 35ff5659cc..141da9b9bb 100644 --- a/blog/tags/serverless-september/page/20/index.html +++ b/blog/tags/serverless-september/page/20/index.html @@ -14,13 +14,13 @@ - - + +

    33 posts tagged with "serverless-september"

    View All Tags

    · 8 min read
    Paul Yu

    Welcome to Day 10 of #30DaysOfServerless!

    We continue our exploraton into Azure Container Apps, with today's focus being communication between microservices, and how to configure your Azure Container Apps environment in the context of a deployment example.


    What We'll Cover

    • ACA Environments & Virtual Networking
    • Basic Microservices Communications
    • Walkthrough: ACA Deployment Example
    • Summary and Next Steps
    • Exercise: Try this yourself!
    • Resources: For self-study!


    Introduction

    In yesterday's post, we learned what the Azure Container Apps (ACA) service is and the problems it aims to solve. It is considered to be a Container-as-a-Service platform since much of the complex implementation details of running a Kubernetes cluster is managed for you.

    Some of the use cases for ACA include event-driven processing jobs and background tasks, but this article will focus on hosting microservices, and how they can communicate with each other within the ACA service. At the end of this article, you will have a solid understanding of how networking and communication is handled and will leave you with a few tutorials to try.

    Environments and virtual networking in ACA

    Before we jump into microservices communication, we should review how networking works within ACA. With ACA being a managed service, Azure will take care of most of your underlying infrastructure concerns. As you provision an ACA resource, Azure provisions an Environment to deploy Container Apps into. This environment is your isolation boundary.

    Azure Container Apps Environment

    By default, Azure creates and manages a new Virtual Network (VNET) for you and the VNET is associated with the environment. As you deploy container apps, they are deployed into the same VNET and the environment is assigned a static public IP address which allows your apps to be accessible over the internet. This VNET is not visible or manageable.

    If you need control of the networking flows within the VNET, you can pre-provision one and tell Azure to deploy an environment within it. This "bring-your-own" VNET model allows you to deploy an environment in either External or Internal modes. Deploying an environment in External mode gives you the flexibility of managing your own VNET, while still allowing your containers to be accessible from outside the environment; a static public IP address is assigned to the environment. When deploying in Internal mode, your containers are accessible within the environment and/or VNET but not accessible from the internet.

    Bringing your own VNET will require some planning and you will need dedicate an empty subnet which will be used exclusively by the ACA environment. The size of your subnet will be dependant on how many containers you plan on deploying and your scaling requirements and one requirement to know is that the subnet address range must have have a /23 CIDR prefix at minimum. You will also need to think about your deployment strategy since ACA has the concept of Revisions which will also consume IPs from your subnet.

    Some additional restrictions to consider when planning your subnet address space is listed in the Resources section below and can be addressed in future posts, so be sure to follow us on dev.to and bookmark the ServerlessSeptember site.

    Basic microservices communication in ACA

    When it comes to communications between containers, ACA addresses this concern with its Ingress capabilities. With HTTP Ingress enabled on your container app, you can expose your app on a HTTPS endpoint.

    If your environment is deployed using default networking and your containers needs to be accessible from outside the environment, you will need to set the Ingress traffic option to Accepting traffic from anywhere. This will generate a Full-Qualified Domain Name (FQDN) which you can use to access your app right away. The ingress feature also generates and assigns a Secure Socket Layer (SSL) certificate for the FQDN.

    External ingress on Container App

    If your environment is deployed using default networking and your containers only need to communicate with other containers in the environment, you'll need to set the Ingress traffic option to Limited to Container Apps Environment. You get a FQDN here as well, but in the section below we'll see how that changes.

    Internal ingress on Container App

    As mentioned in the networking section above, if you deploy your ACA environment into a VNET in internal mode, your options will be Limited to Container Apps Environment or Limited to VNet.

    Ingress on internal virtual network

    Note how the Accepting traffic from anywhere option is greyed out. If your VNET is deployed in external mode, then the option will be available.

    Let's walk though an example ACA deployment

    The diagram below illustrates a simple microservices application that I deployed to ACA. The three container apps all have ingress enabled. The greeting-service app calls two backend services; a hello-service that returns the text Hello (in random casing) and a world-service that returns the text World (in a few random languages). The greeting-service concatenates the two strings together and returns Hello World to the browser. The greeting-service is the only service accessible via external ingress while two backend services are only accessible via internal ingress.

    Greeting Service overview

    With ingress enabled, let's take a quick look at the FQDN structures. Here is the FQDN of the external greeting-service.

    https://greeting-service.victoriouswave-3749d046.eastus.azurecontainerapps.io

    We can break it down into these components:

    https://[YOUR-CONTAINER-APP-NAME].[RANDOM-NAME]-[RANDOM-CHARACTERS].[AZURE-REGION].containerapps.io

    And here is the FQDN of the internal hello-service.

    https://hello-service.internal.victoriouswave-3749d046.eastus.azurecontainerapps.io

    Can you spot the difference between FQDNs?

    That was too easy 😉... the word internal is added as a subdomain in the FQDN between your container app name and the random name for all internal ingress endpoints.

    https://[YOUR-CONTAINER-APP-NAME].internal.[RANDOM-NAME]-[RANDOM-CHARACTERS].[AZURE-REGION].containerapps.io

    Now that we know the internal service FQDNs, we use them in the greeting-service app to achieve basic service-to-service communications.

    So we can inject FQDNs of downstream APIs to upstream apps using environment variables, but the downside to this approach is that need to deploy downstream containers ahead of time and this dependency will need to be planned for during your deployment process. There are ways around this and one option is to leverage the auto-injected environment variables within your app code.

    If I use the Console blade for the hello-service container app and run the env command, you will see environment variables named CONTAINER_APP_NAME and CONTAINER_APP_ENV_DNS_SUFFIX. You can use these values to determine FQDNs within your upstream app.

    hello-service environment variables

    Back in the greeting-service container I can invoke the hello-service container's sayhello method. I know the container app name is hello-service and this service is exposed over an internal ingress, therefore, if I add the internal subdomain to the CONTAINER_APP_ENV_DNS_SUFFIX I can invoke a HTTP request to the hello-service from my greeting-service container.

    Invoke the sayHello method from the greeting-service container

    As you can see, the ingress feature enables communications to other container apps over HTTP/S and ACA will inject environment variables into our container to help determine what the ingress FQDNs would be. All we need now is a little bit of code modification in the greeting-service app and build the FQDNs of our backend APIs by retrieving these environment variables.

    Greeting service code

    ... and now we have a working microservices app on ACA! 🎉

    Hello World

    Summary and next steps

    We've covered Container Apps networking and the basics of how containers communicate with one another. However, there is a better way to address service-to-service invocation using Dapr, which is an open-source framework for building microservices. It is natively integrated into the ACA service and in a future post, you'll learn how to enable it in your Container App to address microservices concerns and more. So stay tuned!

    Exercises

    As a takeaway for today's post, I encourage you to complete this tutorial and if you'd like to deploy the sample app that was presented in this article, my teammate @StevenMurawski is hosting a docker-compose-examples repo which includes samples for deploying to ACA using Docker Compose files. To learn more about the az containerapp compose command, a link to his blog articles are listed in the Resources section below.

    If you have any questions or feedback, please let us know in the comments below or reach out on Twitter @pauldotyu

    Have fun packing and shipping containers! See you in the next post!

    Resources

    The sample app presented here was inspired by services demonstrated in the book Introducing Distributed Application Runtime (Dapr): Simplifying Microservices Applications Development Through Proven and Reusable Patterns and Practices. Go check it out to leran more about Dapr!

    - - + + \ No newline at end of file diff --git a/blog/tags/serverless-september/page/21/index.html b/blog/tags/serverless-september/page/21/index.html index 87f1f82451..cdb62ee368 100644 --- a/blog/tags/serverless-september/page/21/index.html +++ b/blog/tags/serverless-september/page/21/index.html @@ -14,13 +14,13 @@ - - + +

    33 posts tagged with "serverless-september"

    View All Tags

    · 12 min read
    Nitya Narasimhan

    Welcome to Day 9 of #30DaysOfServerless!


    What We'll Cover

    • The Week Ahead
    • Hello, Container Apps!
    • Quickstart: Build Your First ACA!
    • Under The Hood: Core ACA Concepts
    • Exercise: Try this yourself!
    • Resources: For self-study!


    The Week Ahead

    Welcome to Week 2 of #ServerlessSeptember, where we put the focus on Microservices and building Cloud-Native applications that are optimized for serverless solutions on Azure. One week is not enough to do this complex topic justice so consider this a 7-part jumpstart to the longer journey.

    1. Hello, Container Apps (ACA) - Learn about Azure Container Apps, a key service that helps you run microservices and containerized apps on a serverless platform. Know the core concepts. (Tutorial 1: First ACA)
    2. Communication with Microservices - Dive deeper into two key concepts: environments and virtual networking. Learn how microservices communicate in ACA, and walkthrough an example. (Tutorial 2: ACA with 3 Microservices)
    3. Scaling Your Container Apps - Learn about KEDA. Understand how to configure your ACA for auto-scaling with KEDA-supported triggers. Put this into action by walking through a tutorial. (Tutorial 3: Configure Autoscaling)
    4. Hello, Distributed Application Runtime (Dapr) - Learn about Dapr and how its Building Block APIs simplify microservices development with ACA. Know how the sidecar pattern enables incremental adoption of Dapr APIs without requiring any Dapr code integration in app. (Tutorial 4: Setup & Explore Dapr)
    5. Building ACA with Dapr - See how Dapr works with ACA by building a Dapr-enabled Azure Container App. Walk through a .NET tutorial using Pub/Sub and State Management APIs in an enterprise scenario. (Tutorial 5: Build ACA with Dapr)
    6. Managing Secrets With Dapr - We'll look at the Secrets API (a key Building Block of Dapr) and learn how it simplifies management of sensitive information in ACA.
    7. Microservices + Serverless On Azure - We recap Week 2 (Microservices) and set the stage for Week 3 ( Integrations) of Serverless September. Plus, self-study resources including ACA development tutorials in different languages.

    Ready? Let's go!


    Azure Container Apps!

    When building your application, your first decision is about where you host your application. The Azure Architecture Center has a handy chart to help you decide between choices like Azure Functions, Azure App Service, Azure Container Instances, Azure Container Apps and more. But if you are new to this space, you'll need a good understanding of the terms and concepts behind the services Today, we'll focus on Azure Container Apps (ACA) - so let's start with the fundamentals.

    Containerized App Defined

    A containerized app is one where the application components, dependencies, and configuration, are packaged into a single file (container image), which can be instantiated in an isolated runtime environment (container) that is portable across hosts (OS). This makes containers lightweight and scalable - and ensures that applications behave consistently on different host platforms.

    Container images can be shared via container registries (public or private) helping developers discover and deploy related apps with less effort. Scaling a containerized app can be as simple as activating more instances of its container image. However, this requires container orchestrators to automate the management of container apps for efficiency. Orchestrators use technologies like Kubernetes to support capabilities like workload scheduling, self-healing and auto-scaling on demand.

    Cloud-Native & Microservices

    Containers are seen as one of the 5 pillars of Cloud-Native app development, an approach where applications are designed explicitly to take advantage of the unique benefits of modern dynamic environments (involving public, private and hybrid clouds). Containers are particularly suited to serverless solutions based on microservices.

    • With serverless - developers use managed services instead of managing their own infrastructure. Services are typically event-driven and can be configured for autoscaling with rules tied to event triggers. Serverless is cost-effective, with developers paying only for the compute cycles and resources they use.
    • With microservices - developers compose their applications from independent components. Each component can be deployed in its own container, and scaled at that granularity. This simplifies component reuse (across apps) and maintainability (over time) - with developers evolving functionality at microservice (vs. app) levels.

    Hello, Azure Container Apps!

    Azure Container Apps is the managed service that helps you run containerized apps and microservices as a serverless compute solution, on Azure. You can:

    • deploy serverless API endpoints - autoscaled by HTTP request traffic
    • host background processing apps - autoscaled by CPU or memory load
    • handle event-driven processing - autoscaled by #messages in queue
    • run microservices - autoscaled by any KEDA-supported scaler.

    Want a quick intro to the topic? Start by watching the short video below - then read these two posts from our ZeroToHero series:


    Deploy Your First ACA

    Dev Options

    We typically have three options for development:

    • Use the Azure Portal - provision and deploy from a browser.
    • Use Visual Studio Code (with relevant extensions) - if you prefer an IDE
    • Using Azure CLI - if you prefer to build and deploy from command line.

    The documentation site has quickstarts for three contexts:

    For this quickstart, we'll go with the first option (sample image) so we can move quickly to core concepts. We'll leave the others as an exercise for you to explore.

    1. Setup Resources

    PRE-REQUISITES

    You need:

    • An Azure account with an active subscription
    • An installed Azure CLI

    Start by logging into Azure from the CLI. The command should launch a browser to complete the auth flow (or give you an option to take an alternative path).

    $ az login

    Successful authentication will result in extensive command-line output detailing the status of your subscription.

    Next, install the Azure Container Apps extension for the CLI

    $ az extension add --name containerapp --upgrade
    ...
    The installed extension 'containerapp' is in preview.

    Once successfully installed, register the Microsoft.App namespace.

    $ az provider register --namespace Microsoft.App

    Then set local environment variables in that terminal - and verify they are set correctly:

    $ RESOURCE_GROUP="my-container-apps"
    $ LOCATION="canadacentral"
    $ CONTAINERAPPS_ENVIRONMENT="my-environment"

    $ echo $LOCATION $RESOURCE_GROUP $CONTAINERAPPS_ENVIRONMENT
    canadacentral my-container-apps my-environment

    Now you can use Azure CLI to provision a resource group for this tutorial. Creating a resource group also makes it easier for us to delete/reclaim all resources used at the end of this tutorial.

    az group create \
    --name $RESOURCE_GROUP \
    --location $LOCATION
    Congratulations

    You completed the Setup step!

    On completion, the console should print out the details of the newly created resource group. You should also be able to visit the Azure Portal and find the newly-active my-container-apps resource group under your active subscription.

    2. Create Environment

    An environment is like the picket fence around your property. It creates a secure boundary that contains a group of container apps - such that all apps deployed to it share the same virtual network and logging resources.

    $ az containerapp env create \
    --name $CONTAINERAPPS_ENVIRONMENT \
    --resource-group $RESOURCE_GROUP \
    --location $LOCATION

    No Log Analytics workspace provided.
    Generating a Log Analytics workspace with name ...

    This can take a few minutes. When done, you will see the terminal display more details. You can also check the resource group in the portal and see that a Container Apps Environment and a Log Analytics Workspace are created for you as part of this step.

    You've got the fence set up. Now it's time to build your home - er, container app!

    3. Create Container App

    Here's the command we'll use to create our first Azure Container App. Note that the --image argument provides the link to a pre-existing containerapps-helloworld image.

    az containerapp create \
    --name my-container-app \
    --resource-group $RESOURCE_GROUP \
    --environment $CONTAINERAPPS_ENVIRONMENT \
    --image mcr.microsoft.com/azuredocs/containerapps-helloworld:latest \
    --target-port 80 \
    --ingress 'external' \
    --query properties.configuration.ingress.fqdn
    ...
    ...

    Container app created. Access your app at <URL>

    The --ingress property shows that the app is open to external requests; in other words, it is publicly visible at the <URL> that is printed out on the terminal on successsful completion of this step.

    4. Verify Deployment

    Let's see if this works. You can verify that your container app by visitng the URL returned above in your browser. You should see something like this!

    Container App Hello World

    You can also visit the Azure Portal and look under the created Resource Group. You should see a new Container App type of resource was created after this step.

    Congratulations

    You just created and deployed your first "Hello World" Azure Container App! This validates your local development environment setup and existence of a valid Azure subscription.

    5. Clean Up Your Resources

    It's good practice to clean up resources once you are done with a tutorial.

    THIS ACTION IS IRREVERSIBLE

    This command deletes the resource group we created above - and all resources in it. So make sure you specified the right name, then confirm deletion.

    $ az group delete --name $RESOURCE_GROUP
    Are you sure you want to perform this operation? (y/n):

    Note that you can also delete the resource group from the Azure Portal interface if that feels more comfortable. For now, we'll just use the Portal to verify that deletion occurred. If you had previously opened the Resource Group page for the created resource, just refresh it. You should see something like this:

    Resource Not Found


    Core Concepts

    COMING SOON

    An illustrated guide summarizing these concepts in a single sketchnote.

    We covered a lot today - we'll stop with a quick overview of core concepts behind Azure Container Apps, each linked to documentation for self-study. We'll dive into more details on some of these concepts in upcoming articles:

    • Environments - are the secure boundary around a group of container apps that are deployed in the same virtual network. They write logs to a shared Log Analytics workspace and can communicate seamlessly using Dapr, if used.
    • Containers refer to the container image deployed in the Azure Container App. They can use any runtime, programming language, or development stack - and be discovered using any public or private container registry. A container app can support multiple containers.
    • Revisions are immutable snapshots of an Azure Container App. The first revision is created when the ACA is first deployed, with new revisions created when redeployment occurs with revision-scope changes. Multiple revisions can run concurrently in an environment.
    • Application Lifecycle Management revolves around these revisions, with a container app having three phases: deployment, update and deactivation.
    • Microservices are independent units of functionality in Cloud-Native architectures. A single container app typically represents a single microservice, and can be composed from one or more containers. Microservices can now be scaled and upgraded indepedently, giving your application more flexbility and control.
    • Networking architecture consist of a virtual network (VNET) associated with the environment. Unless you provide a custom VNET at environment creation time, a default VNET is automatically created. The VNET configuration determines access (ingress, internal vs. external) and can influence auto-scaling choices (e.g., use HTTP Edge Proxy and scale based on number of HTTP requests).
    • Observability is about monitoring the health of your application and diagnosing it to improve reliability or performance. Azure Container Apps has a number of features - from Log streaming and Container console to integration with Azure Monitor - to provide a holistic view of application status over time.
    • Easy Auth is possible with built-in support for authentication and authorization including support for popular identity providers like Facebook, Google, Twitter and GitHub - alongside the Microsoft Identity Platform.

    Keep these terms in mind as we walk through more tutorials this week, to see how they find application in real examples. Finally, a note on Dapr, the Distributed Application Runtime that abstracts away many of the challenges posed by distributed systems - and lets you focus on your application logic.

    DAPR INTEGRATION MADE EASY

    Dapr uses a sidecar architecture, allowing Azure Container Apps to communicate with Dapr Building Block APIs over either gRPC or HTTP. Your ACA can be built to run with or without Dapr - giving you the flexibility to incrementally adopt specific APIs and unlock related capabilities as the need arises.

    In later articles this week, we'll do a deeper dive into Dapr and build our first Dapr-enable Azure Container App to get a better understanding of this integration.

    Exercise

    Congratulations! You made it! By now you should have a good idea of what Cloud-Native development means, why Microservices and Containers are important to that vision - and how Azure Container Apps helps simplify the building and deployment of microservices based applications using serverless architectures on Azure.

    Now it's your turn to reinforce learning by doing.

    Resources

    Three key resources to bookmark and explore:

    - - + + \ No newline at end of file diff --git a/blog/tags/serverless-september/page/22/index.html b/blog/tags/serverless-september/page/22/index.html index 7bd49d8eae..9c0dda21db 100644 --- a/blog/tags/serverless-september/page/22/index.html +++ b/blog/tags/serverless-september/page/22/index.html @@ -14,13 +14,13 @@ - - + +

    33 posts tagged with "serverless-september"

    View All Tags

    · 5 min read
    Nitya Narasimhan
    Devanshi Joshi

    SEP 08: CHANGE IN PUBLISHING SCHEDULE

    Starting from Week 2 (Sep 8), we'll be publishing blog posts in batches rather than on a daily basis, so you can read a series of related posts together. Don't want to miss updates? Just subscribe to the feed


    Welcome to Day 8 of #30DaysOfServerless!

    This marks the end of our Week 1 Roadmap focused on Azure Functions!! Today, we'll do a quick recap of all #ServerlessSeptember activities in Week 1, set the stage for Week 2 - and leave you with some excellent tutorials you should explore to build more advanced scenarios with Azure Functions.

    Ready? Let's go.


    What We'll Cover

    • Azure Functions: Week 1 Recap
    • Advanced Functions: Explore Samples
    • End-to-End: Serverless Hacks & Cloud Skills
    • What's Next: Hello, Containers & Microservices
    • Challenge: Complete the Learning Path


    Week 1 Recap: #30Days & Functions

    Congratulations!! We made it to the end of Week 1 of #ServerlessSeptember. Let's recap what we learned so far:

    • In Core Concepts we looked at where Azure Functions fits into the serverless options available on Azure. And we learned about key concepts like Triggers, Bindings, Custom Handlers and Durable Functions.
    • In Build Your First Function we looked at the tooling options for creating Functions apps, testing them locally, and deploying them to Azure - as we built and deployed our first Functions app.
    • In the next 4 posts, we explored new Triggers, Integrations, and Scenarios - as we looked at building Functions Apps in Java, JavaScript, .NET and Python.
    • And in the Zero-To-Hero series, we learned about Durable Entities - and how we can use them to create stateful serverless solutions using a Chirper Sample as an example scenario.

    The illustrated roadmap below summarizes what we covered each day this week, as we bring our Functions-as-a-Service exploration to a close.


    Advanced Functions: Code Samples

    So, now that we've got our first Functions app under our belt, and validated our local development setup for tooling, where can we go next? A good next step is to explore different triggers and bindings, that drive richer end-to-end scenarios. For example:

    • Integrate Functions with Azure Logic Apps - we'll discuss Azure Logic Apps in Week 3. For now, think of it as a workflow automation tool that lets you integrate seamlessly with other supported Azure services to drive an end-to-end scenario. In this tutorial, we set up a workflow connecting Twitter (get tweet) to Azure Cognitive Services (analyze sentiment) - and use that to trigger an Azure Functions app to send email about the result.
    • Integrate Functions with Event Grid - we'll discuss Azure Event Grid in Week 3. For now, think of it as an eventing service connecting event sources (publishers) to event handlers (subscribers) at cloud scale. In this tutorial, we handle a common use case - a workflow where loading an image to Blob Storage triggers an Azure Functions app that implements a resize function, helping automatically generate thumbnails for the uploaded image.
    • Integrate Functions with CosmosDB and SignalR to bring real-time push-based notifications to your web app. It achieves this by using a Functions app that is triggered by changes in a CosmosDB backend, causing it to broadcast that update (push notification to connected web clients over SignalR, in real time.

    Want more ideas? Check out the Azure Samples for Functions for implementations, and browse the Azure Architecture Center for reference architectures from real-world scenarios that involve Azure Functions usage.


    E2E Scenarios: Hacks & Cloud Skills

    Want to systematically work your way through a single End-to-End scenario involving Azure Functions alongside other serverless support technologies? Check out the Serverless Hacks activity happening during #ServerlessSeptember, and learn to build this "Serverless Tollbooth Application" in a series of 10 challenges. Check out the video series for a reference solution in .NET and sign up for weekly office hours to join peers and discuss your solutions or challenges.

    Or perhaps you prefer to learn core concepts with code in a structured learning path? We have that covered. Check out the 12-module "Create Serverless Applications" course from Microsoft Learn which walks your through concepts, one at a time, with code. Even better - sign up for the free Cloud Skills Challenge and complete the same path (in under 30 days) but this time, with the added fun of competing against your peers for a spot on a leaderboard, and swag.


    What's Next? Hello, Cloud-Native!

    So where to next? In Week 2 we turn our attention from Functions-as-a-Service to building more complex backends using Containers and Microservices. We'll focus on two core technologies - Azure Container Apps and Dapr (Distributed Application Runtime) - both key components of a broader vision around Building Cloud-Native Applications in Azure.

    What is Cloud-Native you ask?

    Fortunately for you, we have an excellent introduction in our Zero-to-Hero article on Go Cloud-Native with Azure Container Apps - that explains the 5 pillars of Cloud-Native and highlights the value of Azure Container Apps (scenarios) and Dapr (sidecar architecture) for simplified microservices-based solution with auto-scale capability. Prefer a visual summary? Here's an illustrate guide to that article for convenience.

    Go Cloud-Native Download a higher resolution version of the image


    Take The Challenge

    We typically end each post with an exercise or activity to reinforce what you learned. For Week 1, we encourage you to take the Cloud Skills Challenge and work your way through at least a subset of the modules, for hands-on experience with the different Azure Functions concepts, integrations, and usage.

    See you in Week 2!

    - - + + \ No newline at end of file diff --git a/blog/tags/serverless-september/page/23/index.html b/blog/tags/serverless-september/page/23/index.html index a32d5b2c3e..0506db4194 100644 --- a/blog/tags/serverless-september/page/23/index.html +++ b/blog/tags/serverless-september/page/23/index.html @@ -14,15 +14,15 @@ - - + +

    33 posts tagged with "serverless-september"

    View All Tags

    · 7 min read
    Jay Miller

    Welcome to Day 7 of #30DaysOfServerless!

    Over the past couple of days, we've explored Azure Functions from the perspective of specific programming languages. Today we'll continue that trend by looking at Python - exploring the Timer Trigger and CosmosDB binding, and showcasing integration with a FastAPI-implemented web app.

    Ready? Let's go.


    What We'll Cover

    • Developer Guidance: Azure Functions On Python
    • Build & Deploy: Wildfire Detection Apps with Timer Trigger + CosmosDB
    • Demo: My Fire Map App: Using FastAPI and Azure Maps to visualize data
    • Next Steps: Explore Azure Samples
    • Exercise: Try this yourself!
    • Resources: For self-study!


    Developer Guidance

    If you're a Python developer new to serverless on Azure, start with the Azure Functions Python Developer Guide. It covers:

    • Quickstarts with Visual Studio Code and Azure CLI
    • Adopting best practices for hosting, reliability and efficiency.
    • Tutorials showcasing Azure automation, image classification and more
    • Samples showcasing Azure Functions features for Python developers

    Now let's dive in and build our first Python-based Azure Functions app.


    Detecting Wildfires Around the World?

    I live in California which is known for lots of wildfires. I wanted to create a proof of concept for developing an application that could let me know if there was a wildfire detected near my home.

    NASA has a few satelites orbiting the Earth that can detect wildfires. These satelites take scans of the radiative heat in and use that to determine the likelihood of a wildfire. NASA updates their information about every 30 minutes and it can take about four hours for to scan and process information.

    Fire Point Near Austin, TX

    I want to get the information but I don't want to ping NASA or another service every time I check.

    What if I occaisionally download all the data I need? Then I can ping that as much as I like.

    I can create a script that does just that. Any time I say I can create a script that is a verbal queue for me to consider using an Azure function. With the function being ran in the cloud, I can ensure the script runs even when I'm not at my computer.

    How the Timer Trigger Works

    This function will utilize the Timer Trigger. This means Azure will call this function to run at a scheduled interval. This isn't the only way to keep the data in sync, but we know that arcgis, the service that we're using says that data is only updated every 30 minutes or so.

    To learn more about the TimerTrigger as a concept, check out the Azure Functions documentation around Timers.

    When we create the function we tell it a few things like where the script will live (in our case in __init__.py) the type and direction and notably often it should run. We specify the timer using schedule": <The CRON INTERVAL>. For us we're using 0 0,30 * * * which means every 30 minutes at the hour and half-hour.

    {
    "scriptFile": "__init__.py",
    "bindings": [
    {
    "name": "reqTimer",
    "type": "timerTrigger",
    "direction": "in",
    "schedule": "0 0,30 * * * *"
    }
    ]
    }

    Next, we create the code that runs when the function is called.

    Connecting to the Database and our Source

    Disclaimer: The data that we're pulling is for educational purposes only. This is not meant to be a production level application. You're welcome play with this project but ensure that you're using the data in compliance with Esri.

    Our function does two important things.

    1. It pulls data from ArcGIS that meets the parameters
    2. It stores that pulled data into our database

    If you want to check out the code in its entirety, check out the GitHub repository.

    Pulling the data from ArcGIS is easy. We can use the ArcGIS Python API. Then, we need to load the service layer. Finally we query that layer for the specific data.

    def write_new_file_data(gis_id:str, layer:int=0) -> FeatureSet:
    """Returns a JSON String of the Dataframe"""
    fire_data = g.content.get(gis_id)
    feature = fire_data.layers[layer] # Loading Featured Layer from ArcGIS
    q = feature.query(
    where="confidence >= 65 AND hours_old <= 4", #The filter for the query
    return_distince_values=True,
    out_fields="confidence, hours_old", # The data we want to store with our points
    out_sr=4326, # The spatial reference of the data
    )
    return q

    Then we need to store the data in our database.

    We're using Cosmos DB for this. COSMOSDB is a NoSQL database, which means that the data looks a lot like a python dictionary as it's JSON. This means that we don't need to worry about converting the data into a format that can be stored in a relational database.

    The second reason is that Cosmos DB is tied into the Azure ecosystem so that if we want to create functions Azure events around it, we can.

    Our script grabs the information that we pulled from ArcGIS and stores it in our database.

    async with CosmosClient.from_connection_string(COSMOS_CONNECTION_STRING) as client:
    container = database.get_container_client(container=CONTAINER)
    for record in data:
    await container.create_item(
    record,
    enable_automatic_id_generation=True,
    )

    In our code each of these functions live in their own space. So in the main function we focus solely on what azure functions will be doing. The script that gets called is __init__.py. There we'll have the function call the other functions running.

    We created another function called load_and_write that does all the work outlined above. __init__.py will call that.

    async def main(reqTimer: func.TimerRequest) -> None:
    database=database
    container=container
    await update_db.load_and_write(gis_id=GIS_LAYER_ID, database=database, container=container)

    Then we deploy the function to Azure. I like to use VS Code's Azure Extension but you can also deploy it a few other ways.

    Deploying the function via VS Code

    Once the function is deployed we can load the Azure portal and see a ping whenever the function is called. The pings correspond to the Function being ran

    We can also see the data now living in the datastore. Document in Cosmos DB

    It's in the Database, Now What?

    Now the real fun begins. We just loaded the last bit of fire data into a database. We can now query that data and serve it to others.

    As I mentioned before, our Cosmos DB data is also stored in Azure, which means that we can deploy Azure Functions to trigger when new data is added. Perhaps you can use this to check for fires near you and use a Logic App to send an alert to your phone or email.

    Another option is to create a web application that talks to the database and displays the data. I've created an example of this using FastAPI – https://jm-func-us-fire-notify.azurewebsites.net.

    Website that Checks for Fires


    Next Steps

    This article showcased the Timer Trigger and the HTTP Trigger for Azure Functions in Python. Now try exploring other triggers and bindings by browsing Bindings code samples for Python and Azure Functions samples for Python

    Once you've tried out the samples, you may want to explore more advanced integrations or extensions for serverless Python scenarios. Here are some suggestions:

    And check out the resources for more tutorials to build up your Azure Functions skills.

    Exercise

    I encourage you to fork the repository and try building and deploying it yourself! You can see the TimerTrigger and a HTTPTrigger building the website.

    Then try extending it. Perhaps if wildfires are a big thing in your area, you can use some of the data available in Planetary Computer to check out some other datasets.

    Resources

    - - + + \ No newline at end of file diff --git a/blog/tags/serverless-september/page/24/index.html b/blog/tags/serverless-september/page/24/index.html index 2a6dfe4f37..b19ab3e311 100644 --- a/blog/tags/serverless-september/page/24/index.html +++ b/blog/tags/serverless-september/page/24/index.html @@ -14,13 +14,13 @@ - - + +

    33 posts tagged with "serverless-september"

    View All Tags

    · 10 min read
    Mike James
    Matt Soucoup

    Welcome to Day 6 of #30DaysOfServerless!

    The theme for this week is Azure Functions. Today we're going to talk about why Azure Functions are a great fit for .NET developers.


    What We'll Cover

    • What is serverless computing?
    • How does Azure Functions fit in?
    • Let's build a simple Azure Function in .NET
    • Developer Guide, Samples & Scenarios
    • Exercise: Explore the Create Serverless Applications path.
    • Resources: For self-study!

    A banner image that has the title of this article with the author&#39;s photo and a drawing that summarizes the demo application.


    The leaves are changing colors and there's a chill in the air, or for those lucky folks in the Southern Hemisphere, the leaves are budding and a warmth is in the air. Either way, that can only mean one thing - it's Serverless September!🍂 So today, we're going to take a look at Azure Functions - what they are, and why they're a great fit for .NET developers.

    What is serverless computing?

    For developers, serverless computing means you write highly compact individual functions that do one thing - and run in the cloud. These functions are triggered by some external event. That event could be a record being inserted into a database, a file uploaded into BLOB storage, a timer interval elapsed, or even a simple HTTP request.

    But... servers are still definitely involved! What has changed from other types of cloud computing is that the idea and ownership of the server has been abstracted away.

    A lot of the time you'll hear folks refer to this as Functions as a Service or FaaS. The defining characteristic is all you need to do is put together your application logic. Your code is going to be invoked in response to events - and the cloud provider takes care of everything else. You literally get to focus on only the business logic you need to run in response to something of interest - no worries about hosting.

    You do not need to worry about wiring up the plumbing between the service that originates the event and the serverless runtime environment. The cloud provider will handle the mechanism to call your function in response to whatever event you chose to have the function react to. And it passes along any data that is relevant to the event to your code.

    And here's a really neat thing. You only pay for the time the serverless function is running. So, if you have a function that is triggered by an HTTP request, and you rarely get requests to your function, you would rarely pay.

    How does Azure Functions fit in?

    Microsoft's Azure Functions is a modern serverless architecture, offering event-driven cloud computing that is easy for developers to use. It provides a way to run small pieces of code or Functions in the cloud without developers having to worry themselves about the infrastructure or platform the Function is running on.

    That means we're only concerned about writing the logic of the Function. And we can write that logic in our choice of languages... like C#. We are also able to add packages from NuGet to Azure Functions—this way, we don't have to reinvent the wheel and can use well-tested libraries.

    And the Azure Functions runtime takes care of a ton of neat stuff for us, like passing in information about the event that caused it to kick off - in a strongly typed variable. It also "binds" to other services, like Azure Storage, we can easily access those services from our code without having to worry about new'ing them up.

    Let's build an Azure Function!

    Scaffold the Function

    Don't worry about having an Azure subscription or even being connected to the internet—we can develop and debug Azure Functions locally using either Visual Studio or Visual Studio Code!

    For this example, I'm going to use Visual Studio Code to build up a Function that responds to an HTTP trigger and then writes a message to an Azure Storage Queue.

    Diagram of the how the Azure Function will use the HTTP trigger and the Azure Storage Queue Binding

    The incoming HTTP call is the trigger and the message queue the Function writes to is an output binding. Let's have at it!

    info

    You do need to have some tools downloaded and installed to get started. First and foremost, you'll need Visual Studio Code. Then you'll need the Azure Functions extension for VS Code to do the development with. Finally, you'll need the Azurite Emulator installed as well—this will allow us to write to a message queue locally.

    Oh! And of course, .NET 6!

    Now with all of the tooling out of the way, let's write a Function!

    1. Fire up Visual Studio Code. Then, from the command palette, type: Azure Functions: Create New Project

      Screenshot of create a new function dialog in VS Code

    2. Follow the steps as to which directory you want to create the project in and which .NET runtime and language you want to use.

      Screenshot of VS Code prompting which directory and language to use

    3. Pick .NET 6 and C#.

      It will then prompt you to pick the folder in which your Function app resides and then select a template.

      Screenshot of VS Code prompting you to pick the Function trigger template

      Pick the HTTP trigger template. When prompted for a name, call it: PostToAQueue.

    Execute the Function Locally

    1. After giving it a namespace, it prompts for an authorization level—pick Anonymous. Now we have a Function! Let's go ahead and hit F5 and see it run!
    info

    After the templates have finished installing, you may get a prompt to download additional components—these are NuGet packages. Go ahead and do that.

    When it runs, you'll see the Azure Functions logo appear in the Terminal window with the URL the Function is located at. Copy that link.

    Screenshot of the Azure Functions local runtime starting up

    1. Type the link into a browser, adding a name parameter as shown in this example: http://localhost:7071/api/PostToAQueue?name=Matt. The Function will respond with a message. You can even set breakpoints in Visual Studio Code and step through the code!

    Write To Azure Storage Queue

    Next, we'll get this HTTP trigger Function to write to a local Azure Storage Queue. First we need to add the Storage NuGet package to our project. In the terminal, type:

    dotnet add package Microsoft.Azure.WebJobs.Extensions.Storage

    Then set a configuration setting to tell the Function runtime where to find the Storage. Open up local.settings.json and set "AzureWebJobsStorage" to "UseDevelopmentStorage=true". The full file will look like:

    {
    "IsEncrypted": false,
    "Values": {
    "AzureWebJobsStorage": "UseDevelopmentStorage=true",
    "AzureWebJobsDashboard": ""
    }
    }

    Then create a new class within your project. This class will hold nothing but properties. Call it whatever you want and add whatever properties you want to it. I called mine TheMessage and added an Id and Name properties to it.

    public class TheMessage
    {
    public string Id { get; set; }
    public string Name { get; set; }
    }

    Finally, change your PostToAQueue Function, so it looks like the following:


    public static class PostToAQueue
    {
    [FunctionName("PostToAQueue")]
    public static async Task<IActionResult> Run(
    [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req,
    [Queue("demoqueue", Connection = "AzureWebJobsStorage")] IAsyncCollector<TheMessage> messages,
    ILogger log)
    {
    string name = req.Query["name"];

    await messages.AddAsync(new TheMessage { Id = System.Guid.NewGuid().ToString(), Name = name });

    return new OkResult();
    }
    }

    Note the addition of the messages variable. This is telling the Function to use the storage connection we specified before via the Connection property. And it is also specifying which queue to use in that storage account, in this case demoqueue.

    All the code is doing is pulling out the name from the query string, new'ing up a new TheMessage class and adding that to the IAsyncCollector variable.

    That will add the new message to the queue!

    Make sure Azurite is started within VS Code (both the queue and blob emulators). Run the app and send the same GET request as before: http://localhost:7071/api/PostToAQueue?name=Matt.

    If you have the Azure Storage Explorer installed, you can browse your local Queue and see the new message in there!

    Screenshot of Azure Storage Explorer with the new message in the queue

    Summing Up

    We had a quick look at what Microsoft's serverless offering, Azure Functions, is comprised of. It's a full-featured FaaS offering that enables you to write functions in your language of choice, including reusing packages such as those from NuGet.

    A highlight of Azure Functions is the way they are triggered and bound. The triggers define how a Function starts, and bindings are akin to input and output parameters on it that correspond to external services. The best part is that the Azure Function runtime takes care of maintaining the connection to the external services so you don't have to worry about new'ing up or disposing of the connections yourself.

    We then wrote a quick Function that gets triggered off an HTTP request and then writes a query string parameters from that request into a local Azure Storage Queue.

    What's Next

    So, where can you go from here?

    Think about how you can build real-world scenarios by integrating other Azure services. For example, you could use serverless integrations to build a workflow where the input payload received using an HTTP Trigger, is now stored in Blob Storage (output binding), which in turn triggers another service (e.g., Cognitive Services) that processes the blob and returns an enhanced result.

    Keep an eye out for an update to this post where we walk through a scenario like this with code. Check out the resources below to help you get started on your own.

    Exercise

    This brings us close to the end of Week 1 with Azure Functions. We've learned core concepts, built and deployed our first Functions app, and explored quickstarts and scenarios for different programming languages. So, what can you do to explore this topic on your own?

    • Explore the Create Serverless Applications learning path which has several modules that explore Azure Functions integrations with various services.
    • Take up the Cloud Skills Challenge and complete those modules in a fun setting where you compete with peers for a spot on the leaderboard!

    Then come back tomorrow as we wrap up the week with a discussion on end-to-end scenarios, a recap of what we covered this week, and a look at what's ahead next week.

    Resources

    Start here for developer guidance in getting started with Azure Functions as a .NET/C# developer:

    Then learn about supported Triggers and Bindings for C#, with code snippets to show how they are used.

    Finally, explore Azure Functions samples for C# and learn to implement serverless solutions. Examples include:

    - - + + \ No newline at end of file diff --git a/blog/tags/serverless-september/page/25/index.html b/blog/tags/serverless-september/page/25/index.html index 14c3b365be..32b56327c0 100644 --- a/blog/tags/serverless-september/page/25/index.html +++ b/blog/tags/serverless-september/page/25/index.html @@ -14,15 +14,15 @@ - - + +

    33 posts tagged with "serverless-september"

    View All Tags

    · 8 min read
    David Justo

    Welcome to Day 6 of #30DaysOfServerless!

    Today, we have a special set of posts from our Zero To Hero 🚀 initiative, featuring blog posts authored by our Product Engineering teams for #ServerlessSeptember. Posts were originally published on the Apps on Azure blog on Microsoft Tech Community.


    What We'll Cover

    • What are Durable Entities
    • Some Background
    • A Programming Model
    • Entities for a Micro-Blogging Platform


    Durable Entities are a special type of Azure Function that allow you to implement stateful objects in a serverless environment. They make it easy to introduce stateful components to your app without needing to manually persist data to external storage, so you can focus on your business logic. We’ll demonstrate their power with a real-life example in the last section.

    Entities 101: Some Background

    Programming Durable Entities feels a lot like object-oriented programming, except that these “objects” exist in a distributed system. Like objects, each Entity instance has a unique identifier, i.e. an entity ID that can be used to read and manipulate their internal state. Entities define a list of operations that constrain how their internal state is managed, like an object interface.

    Some experienced readers may realize that Entities sound a lot like an implementation of the Actor Pattern. For a discussion of the relationship between Entities and Actors, please refer to this documentation.

    Entities are a part of the Durable Functions Extension, an extension of Azure Functions that empowers programmers with stateful abstractions for serverless, such as Orchestrations (i.e. workflows).

    Durable Functions is available in most Azure Functions runtime environments: .NET, Node.js, Python, PowerShell, and Java (preview). For this article, we’ll focus on the C# experience, but note that Entities are also available in Node.js and Python; their availability in other languages is underway.

    Entities 102: The programming model

    Imagine you want to implement a simple Entity that just counts things. Its interface allows you to get the current count, add to the current count, and to reset the count to zero.

    If you implement this in an object-oriented way, you’d probably define a class (say “Counter”) with a method to get the current count (say “Counter.Get”), another to add to the count (say “Counter.Add”), and another to reset the count (say “Counter.Reset”). Well, the implementation of an Entity in C# is not that different from this sketch:

    [JsonObject(MemberSerialization.OptIn)] 
    public class Counter
    {
    [JsonProperty("value")]
    public int Value { get; set; }

    public void Add(int amount)
    {
    this.Value += amount;
    }

    public Task Reset()
    {
    this.Value = 0;
    return Task.CompletedTask;
    }

    public Task<int> Get()
    {
    return Task.FromResult(this.Value);
    }
    [FunctionName(nameof(Counter))]
    public static Task Run([EntityTrigger] IDurableEntityContext ctx)
    => ctx.DispatchAsync<Counter>();

    }

    We’ve defined a class named Counter, with an internal count stored in the variable “Value” which is manipulated through the “Add” and “Reset” methods, and which can be read via “Get”.

    The “Run” method is simply boilerplate required for the Azure Functions framework to interact with the object we’ve defined – it’s the method that the framework calls internally when it needs to load the Entity object. When DispatchAsync is called, the Entity and its corresponded state (the last count in “Value”) is loaded from storage. Again, this is mostly just boilerplate: your Entity’s business logic lies in the rest of the class.

    Finally, the Json annotation on top of the class and the Value field tells the Durable Functions framework that the “Value” field is to be durably persisted as part of the durable state on each Entity invocation. If you were to annotate other class variables with JsonProperty, they would also become part of the managed state.

    Entities for a micro-blogging platform

    We’ll try to implement a simple micro-blogging platform, a la Twitter. Let’s call it “Chirper”. In Chirper, users write chirps (i.e tweets), they can follow, and unfollow other users, and they can read the chirps of users they follow.

    Defining Entity

    Just like in OOP, it’s useful to begin by identifying what are the stateful agents of this scenario. In this case, users have state (who they follow and their chirps), and chirps have state in the form of their content. So, we could model these stateful agents as Entities!

    Below is a potential way to implement a User for Chirper as an Entity:

      [JsonObject(MemberSerialization = MemberSerialization.OptIn)] 
    public class User: IUser
    {
    [JsonProperty]
    public List<string> FollowedUsers { get; set; } = new List<string>();

    public void Add(string user)
    {
    FollowedUsers.Add(user);
    }

    public void Remove(string user)
    {
    FollowedUsers.Remove(user);
    }

    public Task<List<string>> Get()
    {
    return Task.FromResult(FollowedUsers);
    }
    // note: removed boilerplate “Run” method, for conciseness.
    }

    In this case, our Entity’s internal state is stored in “FollowedUsers” which is an array of accounts followed by this user. The operations exposed by this entity allow clients to read and modify this data: it can be read by “Get”, a new follower can be added via “Add”, and a user can be unfollowed via “Remove”.

    With that, we’ve modeled a Chirper’s user as an Entity! Recall that Entity instances each has a unique ID, so we can consider that unique ID to correspond to a specific user account.

    What about chirps? Should we represent them as Entities as well? That would certainly be valid. However, we would then need to create a mapping between an entity ID and every chirp entity ID that this user wrote.

    For demonstration purposes, a simpler approach would be to create an Entity that stores the list of all chirps authored by a given user; call it UserChirps. Then, we could fix each User Entity to share the same entity ID as its corresponding UserChirps Entity, making client operations easier.

    Below is a simple implementation of UserChirps:

      [JsonObject(MemberSerialization = MemberSerialization.OptIn)] 
    public class UserChirps : IUserChirps
    {
    [JsonProperty]
    public List<Chirp> Chirps { get; set; } = new List<Chirp>();

    public void Add(Chirp chirp)
    {
    Chirps.Add(chirp);
    }

    public void Remove(DateTime timestamp)
    {
    Chirps.RemoveAll(chirp => chirp.Timestamp == timestamp);
    }

    public Task<List<Chirp>> Get()
    {
    return Task.FromResult(Chirps);
    }

    // Omitted boilerplate “Run” function
    }

    Here, our state is stored in Chirps, a list of user posts. Our operations are the same as before: Get, Read, and Add. It’s the same pattern as before, but we’re representing different data.

    To put it all together, let’s set up Entity clients to generate and manipulate these Entities according to some REST API.

    Interacting with Entity

    Before going there, let’s talk briefly about how you can interact with an Entity. Entity interactions take one of two forms -- calls and signals:

    Calling an entity is a two-way communication. You send an operation message to the entity and then wait for the response message before you continue. The response can be a result value or an error. Signaling an entity is a one-way (fire-and-forget) communication. You send an operation message but don’t wait for a response. You have the reassurance that the message will be delivered eventually, but you don’t know when and don’t know what the response is. For example, when you read the state of an Entity, you are performing a “call” interaction. When you record that a user has followed another, you may choose to simply signal it.

    Now say user with a given userId (say “durableFan99” ) wants to post a chirp. For this, you can write an HTTP endpoint to signal the UserChips entity to record that chirp. We can leverage the HTTP Trigger functionality from Azure Functions and pair it with an entity client binding that signals the Add operation of our Chirp Entity:

    [FunctionName("UserChirpsPost")] 
    public static async Task<HttpResponseMessage> UserChirpsPost(
    [HttpTrigger(AuthorizationLevel.Function, "post", Route = "user/{userId}/chirps")]
    HttpRequestMessage req,
    DurableClient] IDurableClient client,
    ILogger log,
    string userId)
    {
    Authenticate(req, userId);
    var chirp = new Chirp()
    {
    UserId = userId,
    Timestamp = DateTime.UtcNow,
    Content = await req.Content.ReadAsStringAsync(),
    };
    await client.SignalEntityAsync<IUserChirps>(userId, x => x.Add(chirp));
    return req.CreateResponse(HttpStatusCode.Accepted, chirp);
    }

    Following the same pattern as above, to get all the chirps from a user, you could read the status of your Entity via ReadEntityStateAsync, which follows the call-interaction pattern as your client expects a response:

    [FunctionName("UserChirpsGet")] 
    public static async Task<HttpResponseMessage> UserChirpsGet(
    [HttpTrigger(AuthorizationLevel.Function, "get", Route = "user/{userId}/chirps")] HttpRequestMessage req,
    [DurableClient] IDurableClient client,
    ILogger log,
    string userId)
    {

    Authenticate(req, userId);
    var target = new EntityId(nameof(UserChirps), userId);
    var chirps = await client.ReadEntityStateAsync<UserChirps>(target);
    return chirps.EntityExists
    ? req.CreateResponse(HttpStatusCode.OK, chirps.EntityState.Chirps)
    : req.CreateResponse(HttpStatusCode.NotFound);
    }

    And there you have it! To play with a complete implementation of Chirper, you can try out our sample in the Durable Functions extension repo.

    Thank you!

    info

    Thanks for following along, and we hope you find Entities as useful as we do! If you have questions or feedback, please file issues in the repo above or tag us @AzureFunctions on Twitter

    - - + + \ No newline at end of file diff --git a/blog/tags/serverless-september/page/26/index.html b/blog/tags/serverless-september/page/26/index.html index 7cf850e19a..e2354824d6 100644 --- a/blog/tags/serverless-september/page/26/index.html +++ b/blog/tags/serverless-september/page/26/index.html @@ -14,13 +14,13 @@ - - + +

    33 posts tagged with "serverless-september"

    View All Tags

    · 8 min read
    Kendall Roden

    Welcome to Day 6 of #30DaysOfServerless!

    Today, we have a special set of posts from our Zero To Hero 🚀 initiative, featuring blog posts authored by our Product Engineering teams for #ServerlessSeptember. Posts were originally published on the Apps on Azure blog on Microsoft Tech Community.


    What We'll Cover

    • Defining Cloud-Native
    • Introduction to Azure Container Apps
    • Dapr In Azure Container Apps
    • Conclusion


    Defining Cloud-Native

    While I’m positive I’m not the first person to ask this, I think it’s an appropriate way for us to kick off this article: “How many developers does it take to define Cloud-Native?” I hope you aren’t waiting for a punch line because I seriously want to know your thoughts (drop your perspectives in the comments..) but if you ask me, the limit does not exist!

    A quick online search of the topic returns a laundry list of articles, e-books, twitter threads, etc. all trying to nail down the one true definition. While diving into the rabbit hole of Cloud-Native, you will inevitably find yourself on the Cloud-Native Computing Foundation (CNCF) site. The CNCF is part of the Linux Foundation and aims to make "Cloud-Native computing ubiquitous" through deep open source project and community involvement. The CNCF has also published arguably the most popularized definition of Cloud-Native which begins with the following statement:

    “Cloud-Native technologies empower organizations to build and run scalable applications in modern, dynamic environments such as public, private, and hybrid clouds."

    Over the past four years, my day-to-day work has been driven primarily by the surging demand for application containerization and the drastic adoption of Kubernetes as the de-facto container orchestrator. Customers are eager to learn and leverage patterns, practices and technologies that enable building "loosely coupled systems that are resilient, manageable, and observable". Enterprise developers at these organizations are being tasked with rapidly deploying event-driven, horizontally-scalable, polyglot services via repeatable, code-to-cloud pipelines.

    While building Cloud-Native solutions can enable rapid innovation, the transition to adopting a Cloud-Native architectural approach comes with a steep learning curve and a new set of considerations. In a document published by Microsoft called What is Cloud-Native?, there are a few key areas highlighted to aid customers in the adoption of best practices for building modern, portable applications which I will summarize below:

    Cloud infrastructure

    • Cloud-Native applications leverage cloud infrastructure and make use of Platform-as-a-service offerings
    • Cloud-Native applications depend on highly-elastic infrastructure with automatic scaling, self-healing, and monitoring capabilities

    Modern application design

    • Cloud-Native applications should be constructed using principles outlined in the 12 factor methodology

    Microservices

    • Cloud-Native applications are typically composed of microservices where each core function, or service, is built and deployed independently

    Containers

    • Cloud-Native applications are typically deployed using containers as a packaging mechanism where an application's code and dependencies are bundled together for consistency of deployment
    • Cloud-Native applications leverage container orchestration technologies- primarily Kubernetes- for achieving capabilities such as workload scheduling, self-healing, auto-scale, etc.

    Backing services

    • Cloud-Native applications are ideally stateless workloads which retrieve and store data in data stores external to the application hosting infrastructure. Cloud providers like Azure provide an array of backing data services which can be securely accessed from application code and provide capabilities for ensuring application data is highly-available

    Automation

    • Cloud-Native solutions should use deployment automation for backing cloud infrastructure via versioned, parameterized Infrastructure as Code (IaC) templates which provide a consistent, repeatable process for provisioning cloud resources.
    • Cloud-Native solutions should make use of modern CI/CD practices and pipelines to ensure successful, reliable infrastructure and application deployment.

    Azure Container Apps

    In many of the conversations I've had with customers that involve talk of Kubernetes and containers, the topics of cost-optimization, security, networking, and reducing infrastructure and operations inevitably arise. I personally have yet to meet with any customers eager to have their developers get more involved with infrastructure concerns.

    One of my former colleagues, Jeff Hollan, made a statement while appearing on a 2019 episode of The Cloud-Native Show where he shared his perspective on Cloud-Native:

    "When I think about Cloud-Native... it's writing applications in a way where you are specifically thinking about the benefits the cloud can provide... to me, serverless is the perfect realization of that because the only reason you can write serverless applications is because the cloud exists."

    I must say that I agree with Jeff's perspective. In addition to optimizing development practices for the Cloud-Native world, reducing infrastructure exposure and operations is equally as important to many organizations and can be achieved as a result of cloud platform innovation.

    In May of 2022, Microsoft announced the general availability of Azure Container Apps. Azure Container Apps provides customers with the ability to run microservices and containerized applications on a serverless, consumption-based platform.

    For those interested in taking advantage of the open source ecosystem while reaping the benefits of a managed platform experience, Container Apps run on Kubernetes and provides a set of managed open source projects embedded directly into the platform including the Kubernetes Event Driven Autoscaler (KEDA), the Distributed Application Runtime (Dapr) and Envoy.

    Azure Kubernetes Service vs. Azure Container Apps

    Container apps provides other Cloud-Native features and capabilities in addition to those above including, but not limited to:

    The ability to dynamically scale and support growing numbers of users, events, and requests is one of the core requirements for most Cloud-Native, distributed applications. Azure Container Apps is purpose-built with this and other Cloud-Native tenants in mind.

    What can you build with Azure Container Apps?

    Dapr in Azure Container Apps

    As a quick personal note before we dive into this section I will say I am a bit bias about Dapr. When Dapr was first released, I had an opportunity to immediately get involved and became an early advocate for the project. It is created by developers for developers, and solves tangible problems customers architecting distributed systems face:

    HOW DO I
    • integrate with external systems that my app has to react and respond to?
    • create event driven apps which reliably send events from one service to another?
    • observe the calls and events between my services to diagnose issues in production?
    • access secrets securely from within my application?
    • discover other services and call methods on them?
    • prevent committing to a technology early and have the flexibility to swap out an alternative based on project or environment changes?

    While existing solutions were in the market which could be used to address some of the concerns above, there was not a lightweight, CNCF-backed project which could provide a unified approach to solve the more fundamental ask from customers: "How do I make it easy for developers to build microservices based on Cloud-Native best practices?"

    Enter Dapr!

    The Distributed Application Runtime (Dapr) provides APIs that simplify microservice connectivity. Whether your communication pattern is service to service invocation or pub/sub messaging, Dapr helps you write resilient and secured microservices. By letting Dapr’s sidecar take care of the complex challenges such as service discovery, message broker integration, encryption, observability, and secret management, you can focus on business logic and keep your code simple."

    The Container Apps platform provides a managed and supported Dapr integration which eliminates the need for deploying and managing the Dapr OSS project. In addition to providing managed upgrades, the platform also exposes a simplified Dapr interaction model to increase developer productivity and reduce the friction required to leverage Dapr capabilities. While the Dapr integration makes it easier for customers to adopt Cloud-Native best practices in container apps it is not required to make use of the container apps platform.

    Image on Dapr

    For additional insight into the dapr integration visit aka.ms/aca-dapr.

    Conclusion

    Backed by and integrated with powerful Cloud-Native technologies, Azure Container Apps strives to make developers productive, while reducing the operational overhead and learning curve that typically accompanies adopting a cloud-native strategy.

    If you are interested in building resilient, portable and highly-scalable apps visit Azure Container Apps | Microsoft Azure today!

    ASK THE EXPERT: LIVE Q&A

    The Azure Container Apps team will answer questions live on September 29.

    - - + + \ No newline at end of file diff --git a/blog/tags/serverless-september/page/27/index.html b/blog/tags/serverless-september/page/27/index.html index 83f3757296..cf88b23cca 100644 --- a/blog/tags/serverless-september/page/27/index.html +++ b/blog/tags/serverless-september/page/27/index.html @@ -14,13 +14,13 @@ - - + +

    33 posts tagged with "serverless-september"

    View All Tags

    · 7 min read
    Aaron Powell

    Welcome to Day 5 of #30DaysOfServerless!

    Yesterday we looked at Azure Functions from the perspective of a Java developer. Today, we'll do a similar walkthrough from the perspective of a JavaScript developer.

    And, we'll use this to explore another popular usage scenario for Azure Functions: building a serverless HTTP API using JavaScript.

    Ready? Let's go.


    What We'll Cover

    • Developer Guidance
    • Create Azure Function with CLI
    • Calling an external API
    • Azure Samples & Scenarios for JS
    • Exercise: Support searching
    • Resources: For self-study!


    Developer Guidance

    If you're a JavaScript developer new to serverless on Azure, start by exploring the Azure Functions JavaScript Developers Guide. It covers:

    • Quickstarts for Node.js - using Visual Code, CLI or Azure Portal
    • Guidance on hosting options and performance considerations
    • Azure Functions bindings and (code samples) for JavaScript
    • Scenario examples - integrations with other Azure Services

    Node.js 18 Support

    Node.js 18 Support (Public Preview)

    Azure Functions support for Node.js 18 entered Public Preview on Aug 31, 2022 and is supported by the Azure Functions v.4.x runtime!

    As we continue to explore how we can use Azure Functions, today we're going to look at using JavaScript to create one, and we're going to be using the newly released Node.js 18 support for Azure Functions to make the most out of the platform.

    Ensure you have Node.js 18 and Azure Functions v4.x versions installed, along with a text editor (I'll use VS Code in this post), and a terminal, then we're ready to go.

    Scenario: Calling The GitHub API

    The application we're going to be building today will use the GitHub API to return a random commit message, so that we don't need to come up with one ourselves! After all, naming things can be really hard! 🤣

    Creating the Azure Function

    To create our Azure Function, we're going to use the Azure Functions CLI, which we can install using npm:

    npm install --global azure-function-core-tools

    Once that's installed, we can use the new func command to initalise our project:

    func init --worker-runtime node --language javascript

    When running func init we can either provide the worker-runtime and language as arguments, or use the menu system that the tool will provide us. For brevity's stake, I've used the arguments here, specifying that we want node as the runtime and javascript as the language, but you could change that to typescript if you'd prefer to use TypeScript.

    Once the init command is completed, you should have a .vscode folder, and the files .gitignore, host.json, local.settings.json, and package.json.

    Files generated by func initFiles generated by func init

    Adding a HTTP Trigger

    We have an empty Functions app so far, what we need to do next is create a Function that it will run, and we're going to make a HTTP Trigger Function, which is a Function that responds to HTTP requests. We'll use the func new command to create that:

    func new --template "HTTP Trigger" --name "get-commit-message"

    When this completes, we'll have a folder for the Function, using the name we provided, that contains the filesfunction.json and index.js. Let's open the function.json to understand it a little bit:

    {
    "bindings": [
    {
    "authLevel": "function",
    "type": "httpTrigger",
    "direction": "in",
    "name": "req",
    "methods": [
    "get",
    "post"
    ]
    },
    {
    "type": "http",
    "direction": "out",
    "name": "res"
    }
    ]
    }

    This file is used to tell Functions about the Function that we've created and what it does, so it knows to handle the appropriate events. We have a bindings node which contains the event bindings for our Azure Function. The first binding is using the type httpTrigger, which indicates that it'll be executed, or triggered, by a HTTP event, and the methods indicates that it's listening to both GET and POST (you can change this for the right HTTP methods that you want to support). The HTTP request information will be bound to a property in the Functions context called req, so we can access query strings, the request body, etc.

    The other binding we have has the direction of out, meaning that it's something that the Function will return to the called, and since this is a HTTP API, the type is http, indicating that we'll return a HTTP response, and that response will be on a property called res that we add to the Functions context.

    Let's go ahead and start the Function and call it:

    func start

    Starting the FunctionStarting the Function

    With the Function started, access the endpoint http://localhost:7071/api/get-commit-message via a browser or using cURL:

    curl http://localhost:7071/api/get-commit-message\?name\=ServerlessSeptember

    Hello from Azure FunctionsHello from Azure Functions

    🎉 CONGRATULATIONS

    You created and ran a JavaScript function app locally!

    Calling an external API

    It's time to update the Function to do what we want to do - call the GitHub Search API and get some commit messages. The endpoint that we'll be calling is https://api.github.com/search/commits?q=language:javascript.

    Note: The GitHub API is rate limited and this sample will call it unauthenticated, so be aware of that in your own testing.

    To call this API, we'll leverage the newly released fetch support in Node 18 and async/await, to make for a very clean Function.

    Open up the index.js file, and delete the contents of the existing Function, so we have a empty one:

    module.exports = async function (context, req) {

    }

    The default template uses CommonJS, but you can use ES Modules with Azure Functions if you prefer.

    Now we'll use fetch to call the API, and unpack the JSON response:

    module.exports = async function (context, req) {
    const res = await fetch("https://api.github.com/search/commits?q=language:javascript");
    const json = await res.json();
    const messages = json.items.map(item => item.commit.message);
    context.res = {
    body: {
    messages
    }
    };
    }

    To send a response to the client, we're setting the context.res property, where res is the name of the output binding in our function.json, and giving it a body that contains the commit messages.

    Run func start again, and call the endpoint:

    curl http://localhost:7071/api/get-commit-message

    The you'll get some commit messages:

    A series of commit messages from the GitHub Search APIA series of commit messages from the GitHub Search API

    🎉 CONGRATULATIONS

    There we go, we've created an Azure Function which is used as a proxy to another API, that we call (using native fetch in Node.js 18) and from which we return a subset of the JSON payload.

    Next Steps

    Other Triggers, Bindings

    This article focused on using the HTTPTrigger and relevant bindings, to build a serverless API using Azure Functions. How can you explore other supported bindings, with code samples to illustrate usage?

    Scenarios with Integrations

    Once you've tried out the samples, try building an end-to-end scenario by using these triggers to integrate seamlessly with other services. Here are some suggestions:

    Exercise: Support searching

    The GitHub Search API allows you to provide search parameters via the q query string. In this sample, we hard-coded it to be language:javascript, but as a follow-on exercise, expand the Function to allow the caller to provide the search terms as a query string to the Azure Function, which is passed to the GitHub Search API. Hint - have a look at the req argument.

    Resources

    - - + + \ No newline at end of file diff --git a/blog/tags/serverless-september/page/28/index.html b/blog/tags/serverless-september/page/28/index.html index ea7b32ef55..31e402c813 100644 --- a/blog/tags/serverless-september/page/28/index.html +++ b/blog/tags/serverless-september/page/28/index.html @@ -14,13 +14,13 @@ - - + +

    33 posts tagged with "serverless-september"

    View All Tags

    · 8 min read
    Rory Preddy

    Welcome to Day 4 of #30DaysOfServerless!

    Yesterday we walked through an Azure Functions Quickstart with JavaScript, and used it to understand the general Functions App structure, tooling and developer experience.

    Today we'll look at developing Functions app with a different programming language - namely, Java - and explore developer guidance, tools and resources to build serverless Java solutions on Azure.


    What We'll Cover


    Developer Guidance

    If you're a Java developer new to serverless on Azure, start by exploring the Azure Functions Java Developer Guide. It covers:

    In this blog post, we'll dive into one quickstart, and discuss other resources briefly, for awareness! Do check out the recommended exercises and resources for self-study!


    My First Java Functions App

    In today's post, we'll walk through the Quickstart: Azure Functions tutorial using Visual Studio Code. In the process, we'll setup our development environment with the relevant command-line tools and VS Code extensions to make building Functions app simpler.

    Note: Completing this exercise may incur a a cost of a few USD cents based on your Azure subscription. Explore pricing details to learn more.

    First, make sure you have your development environment setup and configured.

    PRE-REQUISITES
    1. An Azure account with an active subscription - Create an account for free
    2. The Java Development Kit, version 11 or 8. - Install
    3. Apache Maven, version 3.0 or above. - Install
    4. Visual Studio Code. - Install
    5. The Java extension pack - Install
    6. The Azure Functions extension for Visual Studio Code - Install

    VS Code Setup

    NEW TO VISUAL STUDIO CODE?

    Start with the Java in Visual Studio Code tutorial to jumpstart your learning!

    Install the Extension Pack for Java (shown below) to install 6 popular extensions to help development workflow from creation to testing, debugging, and deployment.

    Extension Pack for Java

    Now, it's time to get started on our first Java-based Functions app.

    1. Create App

    1. Open a command-line terminal and create a folder for your project. Use the code command to launch Visual Studio Code from that directory as shown:

      $ mkdir java-function-resource-group-api
      $ cd java-function-resource-group-api
      $ code .
    2. Open the Visual Studio Command Palette (Ctrl + Shift + p) and select Azure Functions: create new project to kickstart the create workflow. Alternatively, you can click the Azure icon (on activity sidebar), to get the Workspace window, click "+" and pick the "Create Function" option as shown below.

      Screenshot of creating function in Azure from Visual Studio Code.

    3. This triggers a multi-step workflow. Fill in the information for each step as shown in the following prompts. Important: Start this process from an empty folder - the workflow will populate it with the scaffold for your Java-based Functions app.

      PromptValue
      Choose the directory location.You should either create a new folder or choose an empty folder for the project workspace. Don't choose a project folder that is already part of a workspace.
      Select a languageChoose Java.
      Select a version of JavaChoose Java 11 or Java 8, the Java version on which your functions run in Azure. Choose a Java version that you've verified locally.
      Provide a group IDChoose com.function.
      Provide an artifact IDEnter myFunction.
      Provide a versionChoose 1.0-SNAPSHOT.
      Provide a package nameChoose com.function.
      Provide an app nameEnter HttpExample.
      Select the build tool for Java projectChoose Maven.

    Visual Studio Code uses the provided information and generates an Azure Functions project. You can view the local project files in the Explorer - it should look like this:

    Azure Functions Scaffold For Java

    2. Preview App

    Visual Studio Code integrates with the Azure Functions Core tools to let you run this project on your local development computer before you publish to Azure.

    1. To build and run the application, use the following Maven command. You should see output similar to that shown below.

      $ mvn clean package azure-functions:run
      ..
      ..
      Now listening on: http://0.0.0.0:7071
      Application started. Press Ctrl+C to shut down.

      Http Functions:

      HttpExample: [GET,POST] http://localhost:7071/api/HttpExample
      ...
    2. Copy the URL of your HttpExample function from this output to a browser and append the query string ?name=<YOUR_NAME>, making the full URL something like http://localhost:7071/api/HttpExample?name=Functions. The browser should display a message that echoes back your query string value. The terminal in which you started your project also shows log output as you make requests.

    🎉 CONGRATULATIONS

    You created and ran a function app locally!

    With the Terminal panel focused, press Ctrl + C to stop Core Tools and disconnect the debugger. After you've verified that the function runs correctly on your local computer, it's time to use Visual Studio Code and Maven to publish and test the project on Azure.

    3. Sign into Azure

    Before you can deploy, sign in to your Azure subscription.

    az login

    The az login command signs you into your Azure account.

    Use the following command to deploy your project to a new function app.

    mvn clean package azure-functions:deploy

    When the creation is complete, the following Azure resources are created in your subscription:

    • Resource group. Named as java-functions-group.
    • Storage account. Required by Functions. The name is generated randomly based on Storage account name requirements.
    • Hosting plan. Serverless hosting for your function app.The name is java-functions-app-service-plan.
    • Function app. A function app is the deployment and execution unit for your functions. The name is randomly generated based on your artifactId, appended with a randomly generated number.

    4. Deploy App

    1. Back in the Resources area in the side bar, expand your subscription, your new function app, and Functions. Right-click (Windows) or Ctrl - click (macOS) the HttpExample function and choose Execute Function Now....

      Screenshot of executing function in Azure from Visual Studio Code.

    2. In Enter request body you see the request message body value of { "name": "Azure" }. Press Enter to send this request message to your function.

    3. When the function executes in Azure and returns a response, a notification is raised in Visual Studio Code.

    You can also copy the complete Invoke URL shown in the output of the publish command into a browser address bar, appending the query parameter ?name=Functions. The browser should display similar output as when you ran the function locally.

    🎉 CONGRATULATIONS

    You deployed your function app to Azure, and invoked it!

    5. Clean up

    Use the following command to delete the resource group and all its contained resources to avoid incurring further costs.

    az group delete --name java-functions-group

    Next Steps

    So, where can you go from here? The example above used a familiar HTTP Trigger scenario with a single Azure service (Azure Functions). Now, think about how you can build richer workflows by using other triggers and integrating with other Azure or third-party services.

    Other Triggers, Bindings

    Check out Azure Functions Samples In Java for samples (and short use cases) that highlight other triggers - with code! This includes triggers to integrate with CosmosDB, Blob Storage, Event Grid, Event Hub, Kafka and more.

    Scenario with Integrations

    Once you've tried out the samples, try building an end-to-end scenario by using these triggers to integrate seamlessly with other Services. Here are a couple of useful tutorials:

    Exercise

    Time to put this into action and validate your development workflow:

    Resources

    - - + + \ No newline at end of file diff --git a/blog/tags/serverless-september/page/29/index.html b/blog/tags/serverless-september/page/29/index.html index a8cac36ac0..c47564122f 100644 --- a/blog/tags/serverless-september/page/29/index.html +++ b/blog/tags/serverless-september/page/29/index.html @@ -14,13 +14,13 @@ - - + +

    33 posts tagged with "serverless-september"

    View All Tags

    · 9 min read
    Nitya Narasimhan

    Welcome to Day 3 of #30DaysOfServerless!

    Yesterday we learned core concepts and terminology for Azure Functions, the signature Functions-as-a-Service option on Azure. Today we take our first steps into building and deploying an Azure Functions app, and validate local development setup.

    Ready? Let's go.


    What We'll Cover


    Developer Guidance

    Before we jump into development, let's familiarize ourselves with language-specific guidance from the Azure Functions Developer Guide. We'll review the JavaScript version but guides for F#, Java, Python, C# and PowerShell are also available.

    1. A function is defined by two things: code (written in a supported programming language) and configuration (specified in a functions.json file, declaring the triggers, bindings and other context for execution).

    2. A function app is the unit of deployment for your functions, and is associated with a single execution context or runtime. It can contain multiple functions, but they must be in the same language.

    3. A host configuration is runtime-specific configuration that affects all functions running in a given function app instance. It is defined in a host.json file.

    4. A recommended folder structure is defined for the function app, but may vary based on the programming language used. Check the documentation on folder structures to learn the default for your preferred language.

    Here's an example of the JavaScript folder structure for a function app containing two functions with some shared dependencies. Note that host.json (runtime configuration) is defined once, in the root directory. And function.json is defined separately for each function.

    FunctionsProject
    | - MyFirstFunction
    | | - index.js
    | | - function.json
    | - MySecondFunction
    | | - index.js
    | | - function.json
    | - SharedCode
    | | - myFirstHelperFunction.js
    | | - mySecondHelperFunction.js
    | - node_modules
    | - host.json
    | - package.json
    | - local.settings.json

    We'll dive into what the contents of these files look like, when we build and deploy the first function. We'll cover local.settings.json in the About Local Testing section at the end.


    My First Function App

    The documentation provides quickstart options for all supported languages. We'll walk through the JavaScript versions in this article. You have two options for development:

    I'm a huge fan of VS Code - so I'll be working through that tutorial today.

    PRE-REQUISITES

    Don't forget to validate your setup by checking the versions of installed software.

    Install VSCode Extension

    Installing the Visual Studio Code extension should automatically open this page in your IDE with similar quickstart instructions, but potentially more recent screenshots.

    Visual Studio Code Extension for VS Code

    Note that it may make sense to install the Azure tools for Visual Studio Code extensions pack if you plan on working through the many projects in Serverless September. This includes the Azure Functions extension by default.

    Create First Function App

    Walk through the Create local [project] steps of the quickstart. The process is quick and painless and scaffolds out this folder structure and files. Note the existence (and locations) of functions.json and host.json files.

    Final screenshot for VS Code workflow

    Explore the Code

    Check out the functions.json configuration file. It shows that the function is activated by an httpTrigger with an input binding (tied to req payload) and an output binding (tied to res payload). And it supports both GET and POST requests on the exposed URL.

    {
    "bindings": [
    {
    "authLevel": "anonymous",
    "type": "httpTrigger",
    "direction": "in",
    "name": "req",
    "methods": [
    "get",
    "post"
    ]
    },
    {
    "type": "http",
    "direction": "out",
    "name": "res"
    }
    ]
    }

    Check out index.js - the function implementation. We see it logs a message to the console when invoked. It then extracts a name value from the input payload (req) and crafts a different responseMessage based on the presence/absence of a valid name. It returns this response in the output payload (res).

    module.exports = async function (context, req) {
    context.log('JavaScript HTTP trigger function processed a request.');

    const name = (req.query.name || (req.body && req.body.name));
    const responseMessage = name
    ? "Hello, " + name + ". This HTTP triggered function executed successfully."
    : "This HTTP triggered function executed successfully. Pass a name in the query string or in the request body for a personalized response.";

    context.res = {
    // status: 200, /* Defaults to 200 */
    body: responseMessage
    };
    }

    Preview Function App Locally

    You can now run this function app locally using Azure Functions Core Tools. VS Code integrates seamlessly with this CLI-based tool, making it possible for you to exploit all its capabilities without leaving the IDE. In fact, the workflow will even prompt you to install those tools if they didn't already exist in your local dev environment.

    Now run the function app locally by clicking on the "Run and Debug" icon in the activity bar (highlighted, left) and pressing the "▶️" (Attach to Node Functions) to start execution. On success, your console output should show something like this.

    Final screenshot for VS Code workflow

    You can test the function locally by visiting the Function Url shown (http://localhost:7071/api/HttpTrigger1) or by opening the Workspace region of the Azure extension, and selecting the Execute Function now menu item as shown.

    Final screenshot for VS Code workflow

    In the latter case, the Enter request body popup will show a pre-populated request of {"name":"Azure"} that you can submit.

    Final screenshot for VS Code workflow

    On successful execution, your VS Code window will show a notification as follows. Take note of the console output - it shows the message encoded in index.js.

    Final screenshot for VS Code workflow

    You can also visit the deployed function URL directly in a local browser - testing the case for a request made with no name payload attached. Note how the response in the browser now shows the non-personalized version of the message!

    Final screenshot for VS Code workflow

    🎉 Congratulations

    You created and ran a function app locally!

    (Re)Deploy to Azure

    Now, just follow the creating a function app in Azure steps to deploy it to Azure, using an active subscription! The deployed app resource should now show up under the Function App Resources where you can click Execute Function Now to test the Azure-deployed version instead. You can also look up the function URL in the portal and visit that link in your local browser to trigger the function without the name context.

    🎉 Congratulations

    You have an Azure-hosted serverless function app!

    Challenge yourself and try to change the code and redeploy to Azure to return something different. You have effectively created a serverless API endpoint!


    About Core Tools

    That was a lot to cover! In the next few days we'll have more examples for Azure Functions app development - focused on different programming languages. So let's wrap today's post by reviewing two helpful resources.

    First, let's talk about Azure Functions Core Tools - the command-line tool that lets you develop, manage, and deploy, Azure Functions projects from your local development environment. It is used transparently by the VS Code extension - but you can use it directly from a terminal for a powerful command-line end-to-end developer experience! The Core Tools commands are organized into the following contexts:

    Learn how to work with Azure Functions Core Tools. Not only can it help with quick command execution, it can also be invaluable for debugging issues that may not always be visible or understandable in an IDE.

    About Local Testing

    You might have noticed that the scaffold also produced a local.settings.json file. What is that and why is it useful? By definition, the local.settings.json file "stores app settings and settings used by local development tools. Settings in the local.settings.json file are used only when you're running your project locally."

    Read the guidance on Code and test Azure Functions Locally to learn more about how to configure development environments locally, for your preferred programming language, to support testing and debugging on the local Functions runtime.

    Exercise

    We made it! Now it's your turn!! Here are a few things you can try to apply what you learned and reinforce your understanding:

    Resources

    Bookmark and visit the #30DaysOfServerless Collection. It's the one-stop collection of resources we will keep updated with links to relevant documentation and learning resources.

    - - + + \ No newline at end of file diff --git a/blog/tags/serverless-september/page/3/index.html b/blog/tags/serverless-september/page/3/index.html index 89e9d13af9..325081dd53 100644 --- a/blog/tags/serverless-september/page/3/index.html +++ b/blog/tags/serverless-september/page/3/index.html @@ -14,13 +14,13 @@ - - + +

    33 posts tagged with "serverless-september"

    View All Tags

    · 14 min read
    Justin Yoo

    Welcome to Day 28 of #30DaysOfServerless!

    Since it's the serverless end-to-end week, I'm going to discuss how to use a serverless application Azure Functions with OpenAPI extension to be seamlessly integrated with Power Platform custom connector through Azure API Management - in a post I call "Where am I? My GPS Location with Serverless Power Platform Custom Connector"

    OK. Are you ready? Let's get started!


    What We'll Cover

    • What is Power Platform custom connector?
    • Proxy app to Google Maps and Naver Map API
    • API Management integration
    • Two ways of building custom connector
    • Where am I? Power Apps app
    • Exercise: Try this yourself!
    • Resources: For self-study!


    SAMPLE REPO

    Want to follow along? Check out the sample app on GitHub repository used in this post.

    What is Power Platform custom connector?

    Power Platform is a low-code/no-code application development tool for fusion teams that consist of a group of people. Those people come from various disciplines, including field experts (domain experts), IT professionals and professional developers, to draw business values successfully. Within the fusion team, the domain experts become citizen developers or low-code developers by Power Platform. In addition, Making Power Platform more powerful is that it offers hundreds of connectors to other Microsoft 365 and third-party services like SAP, ServiceNow, Salesforce, Google, etc.

    However, what if you want to use your internal APIs or APIs not yet offering their official connectors? Here's an example. If your company has an inventory management system, and you want to use it within your Power Apps or Power Automate. That point is exactly where Power Platform custom connectors is necessary.

    Inventory Management System for Power Apps

    Therefore, Power Platform custom connectors enrich those citizen developers' capabilities because those connectors can connect any API applications for the citizen developers to use.

    In this post, let's build a custom connector that provides a static map image generated by Google Maps API and Naver Map API using your GPS location.

    Proxy app to Google Maps and Naver Map API

    First, let's build an Azure Functions app that connects to Google Maps and Naver Map. Suppose that you've already got the API keys for both services. If you haven't yet, get the keys first by visiting here for Google and here for Naver. Then, store them to local.settings.json within your Azure Functions app.

    {
    "Values": {
    ...
    "Maps__Google__ApiKey": "<GOOGLE_MAPS_API_KEY>",
    "Maps__Naver__ClientId": "<NAVER_MAP_API_CLIENT_ID>",
    "Maps__Naver__ClientSecret": "<NAVER_MAP_API_CLIENT_SECRET>"
    }
    }

    Here's the sample logic to get the static image from Google Maps API. It takes the latitude and longitude of your current location and image zoom level, then returns the static map image. There are a few hard-coded assumptions, though:

    • The image size should be 400x400.
    • The image should be in .png format.
    • The marker should show be red and show my location.
    public class GoogleMapService : IMapService
    {
    public async Task<byte[]> GetMapAsync(HttpRequest req)
    {
    var latitude = req.Query["lat"];
    var longitude = req.Query["long"];
    var zoom = (string)req.Query["zoom"] ?? "14";

    var sb = new StringBuilder();
    sb.Append("https://maps.googleapis.com/maps/api/staticmap")
    .Append($"?center={latitude},{longitude}")
    .Append("&size=400x400")
    .Append($"&zoom={zoom}")
    .Append($"&markers=color:red|{latitude},{longitude}")
    .Append("&format=png32")
    .Append($"&key={this._settings.Google.ApiKey}");
    var requestUri = new Uri(sb.ToString());

    var bytes = await this._http.GetByteArrayAsync(requestUri).ConfigureAwait(false);

    return bytes;
    }
    }

    The NaverMapService class has a similar logic with the same input and assumptions. Here's the code:

    public class NaverMapService : IMapService
    {
    public async Task<byte[]> GetMapAsync(HttpRequest req)
    {
    var latitude = req.Query["lat"];
    var longitude = req.Query["long"];
    var zoom = (string)req.Query["zoom"] ?? "13";

    var sb = new StringBuilder();
    sb.Append("https://naveropenapi.apigw.ntruss.com/map-static/v2/raster")
    .Append($"?center={longitude},{latitude}")
    .Append("&w=400")
    .Append("&h=400")
    .Append($"&level={zoom}")
    .Append($"&markers=color:blue|pos:{longitude}%20{latitude}")
    .Append("&format=png")
    .Append("&lang=en");
    var requestUri = new Uri(sb.ToString());

    this._http.DefaultRequestHeaders.Clear();
    this._http.DefaultRequestHeaders.Add("X-NCP-APIGW-API-KEY-ID", this._settings.Naver.ClientId);
    this._http.DefaultRequestHeaders.Add("X-NCP-APIGW-API-KEY", this._settings.Naver.ClientSecret);

    var bytes = await this._http.GetByteArrayAsync(requestUri).ConfigureAwait(false);

    return bytes;
    }
    }

    Let's take a look at the function endpoints. Here's for the Google Maps and Naver Map. As the GetMapAsync(req) method returns a byte array value, you need to transform it as FileContentResult, with the content type of image/png.

    // Google Maps
    public class GoogleMapsTrigger
    {
    [FunctionName(nameof(GoogleMapsTrigger.GetGoogleMapImage))]
    public async Task<IActionResult> GetGoogleMapImage(
    [HttpTrigger(AuthorizationLevel.Anonymous, "GET", Route = "google/image")] HttpRequest req)
    {
    this._logger.LogInformation("C# HTTP trigger function processed a request.");

    var bytes = await this._service.GetMapAsync(req).ConfigureAwait(false);

    return new FileContentResult(bytes, "image/png");
    }
    }

    // Naver Map
    public class NaverMapsTrigger
    {
    [FunctionName(nameof(NaverMapsTrigger.GetNaverMapImage))]
    public async Task<IActionResult> GetNaverMapImage(
    [HttpTrigger(AuthorizationLevel.Anonymous, "GET", Route = "naver/image")] HttpRequest req)
    {
    this._logger.LogInformation("C# HTTP trigger function processed a request.");

    var bytes = await this._service.GetMapAsync(req).ConfigureAwait(false);

    return new FileContentResult(bytes, "image/png");
    }
    }

    Then, add the OpenAPI capability to each function endpoint. Here's the example:

    // Google Maps
    public class GoogleMapsTrigger
    {
    [FunctionName(nameof(GoogleMapsTrigger.GetGoogleMapImage))]
    // ⬇️⬇️⬇️ Add decorators provided by the OpenAPI extension ⬇️⬇️⬇️
    [OpenApiOperation(operationId: nameof(GoogleMapsTrigger.GetGoogleMapImage), tags: new[] { "google" })]
    [OpenApiParameter(name: "lat", In = ParameterLocation.Query, Required = true, Type = typeof(string), Description = "The **latitude** parameter")]
    [OpenApiParameter(name: "long", In = ParameterLocation.Query, Required = true, Type = typeof(string), Description = "The **longitude** parameter")]
    [OpenApiParameter(name: "zoom", In = ParameterLocation.Query, Required = false, Type = typeof(string), Description = "The **zoom level** parameter &ndash; Default value is `14`")]
    [OpenApiResponseWithBody(statusCode: HttpStatusCode.OK, contentType: "image/png", bodyType: typeof(byte[]), Description = "The map image as an OK response")]
    // ⬆️⬆️⬆️ Add decorators provided by the OpenAPI extension ⬆️⬆️⬆️
    public async Task<IActionResult> GetGoogleMapImage(
    [HttpTrigger(AuthorizationLevel.Anonymous, "GET", Route = "google/image")] HttpRequest req)
    {
    ...
    }
    }

    // Naver Map
    public class NaverMapsTrigger
    {
    [FunctionName(nameof(NaverMapsTrigger.GetNaverMapImage))]
    // ⬇️⬇️⬇️ Add decorators provided by the OpenAPI extension ⬇️⬇️⬇️
    [OpenApiOperation(operationId: nameof(NaverMapsTrigger.GetNaverMapImage), tags: new[] { "naver" })]
    [OpenApiParameter(name: "lat", In = ParameterLocation.Query, Required = true, Type = typeof(string), Description = "The **latitude** parameter")]
    [OpenApiParameter(name: "long", In = ParameterLocation.Query, Required = true, Type = typeof(string), Description = "The **longitude** parameter")]
    [OpenApiParameter(name: "zoom", In = ParameterLocation.Query, Required = false, Type = typeof(string), Description = "The **zoom level** parameter &ndash; Default value is `13`")]
    [OpenApiResponseWithBody(statusCode: HttpStatusCode.OK, contentType: "image/png", bodyType: typeof(byte[]), Description = "The map image as an OK response")]
    // ⬆️⬆️⬆️ Add decorators provided by the OpenAPI extension ⬆️⬆️⬆️
    public async Task<IActionResult> GetNaverMapImage(
    [HttpTrigger(AuthorizationLevel.Anonymous, "GET", Route = "naver/image")] HttpRequest req)
    {
    ...
    }
    }

    Run the function app in the local. Here are the latitude and longitude values for Seoul, Korea.

    • latitude: 37.574703
    • longitude: 126.978519

    Google Map for Seoul

    It seems to be working! Let's deploy it to Azure.

    API Management integration

    Visual Studio 2022 provides a built-in deployment tool for Azure Functions app onto Azure. In addition, the deployment tool supports seamless integration with Azure API Management as long as your Azure Functions app enables the OpenAPI capability. In this post, I'm going to use this feature. Right-mouse click on the Azure Functions project and select the "Publish" menu.

    Visual Studio context menu for publish

    Then, you will see the publish screen. Click the "➕ New" button to create a new publish profile.

    Create a new publish profile

    Choose "Azure" and click the "Next" button.

    Choose the target platform for publish

    Select the app instance. This time simply pick up the "Azure Function App (Windows)" option, then click "Next".

    Choose the target OS for publish

    If you already provision an Azure Function app instance, you will see it on the screen. Otherwise, create a new one. Then, click "Next".

    Choose the target instance for publish

    In the next step, you are asked to choose the Azure API Management instance for integration. Choose one, or create a new one. Then, click "Next".

    Choose the APIM instance for integration

    Finally, select the publish method either local publish or GitHub Actions workflow. Let's pick up the local publish method for now. Then, click "Finish".

    Choose the deployment type

    The publish profile has been created. Click "Close" to move on.

    Publish profile created

    Now the function app is ready for deployment. Click the "Publish" button and see how it goes.

    Publish function app

    The Azure function app has been deployed and integrated with the Azure API Management instance.

    Function app published

    Go to the published function app site, and everything looks OK.

    Function app on Azure

    And API Management shows the function app integrated perfectly.

    Function app integrated with APIM

    Now, you are ready to create a custom connector. Let's move on.

    Two ways of building custom connector

    There are two ways to create a custom connector.

    Export custom connector from API Management

    First, you can directly use the built-in API Management feature. Then, click the ellipsis icon and select the "Create Power Connector" menu.

    Create Power Connector menu

    Then, you are redirected to this screen. While the "API" and "API display name" fields are pre-populated, you need to choose the Power Platform environment tied to your tenant. Choose an environment, click "Authenticate", and click "Create".

    Create custom connector screen

    Check your custom connector on Power Apps or Power Automate side.

    Custom connector created on Power Apps

    However, there's a caveat to this approach. Because it's tied to your tenant, you should use the second approach if you want to use this custom connector on the other tenant.

    Import custom connector from OpenAPI document or URL

    Click the ellipsis icon again and select the "Export" menu.

    Export menu

    On the Export API screen, choose the "OpenAPI v2 (JSON)" panel because Power Platform custom connector currently accepts version 2 of the OpenAPI document.

    Select OpenAPI v2

    Download the OpenAPI document to your local computer and move to your Power Apps or Power Automate page under your desired environment. I'm going to use the Power Automate page. First, go to the "Data" ➡️ "Custom connectors" page. Then, click the "➕ New custom connector" ➡️ "Import an OpenAPI file" at the top right corner.

    New custom connector

    When a modal pops up, give the custom connector name and import the OpenAPI document exported above. Then, click "Continue".

    Import custom connector

    Actually, that's it! Next, click the "✔️ Create connector" button to create the connector.

    Create custom connector

    Go back to the custom connector page, and you will see the "Maps API" custom connector you just created.

    Custom connector imported

    So, you are ready to create a Power Apps app to display your location on Google Maps or Naver Map! Let's move on.

    Where am I? Power Apps app

    Open the Power Apps Studio, and create an empty canvas app, named Who am I with a phone layout.

    Custom connector integration

    To use the custom connector created above, you need to add it to the Power App. Click the cylinder icon on the left and click the "Add data" button.

    Add custom connector to data pane

    Search the custom connector name, "Maps API", and click the custom connector to add.

    Search custom connector

    To use the custom connector, you also need to create a connection to it. Click the "Connect" button and move on.

    Create connection to custom connector

    Now, you've got the connection to the custom connector.

    Connection to custom connector ready

    Controls

    Let's build the Power Apps app. First of all, put three controls Image, Slider and Button onto the canvas.

    Power Apps control added

    Click the "Screen1" control and change the value on the property "OnVisible" to the formula below. The formula stores the current slider value in the zoomlevel collection.

    ClearCollect(
    zoomlevel,
    Slider1.Value
    )

    Click the "Botton1" control and change the value on the property "OnSelected" to the formula below. It passes the current latitude, longitude and zoom level to the custom connector and receives the image data. The received image data is stored in the result collection.

    ClearCollect(
    result,
    MAPS.GetGoogleMapImage(
    Location.Latitude,
    Location.Longitude,
    { zoom: First(zoomlevel).Value }
    )
    )

    Click the "Image1" control and change the value on the property "Image" to the formula below. It gets the image data from the result collection.

    First(result).Url

    Click the "Slider1" control and change the value on the property "OnChange" to the formula below. It stores the current slider value to the zoomlevel collection, followed by calling the custom connector to get the image data against the current location.

    ClearCollect(
    zoomlevel,
    Slider1.Value
    );
    ClearCollect(
    result,
    MAPS.GetGoogleMapImage(
    Location.Latitude,
    Location.Longitude,
    { zoom: First(zoomlevel).Value }
    )
    )

    That seems to be OK. Let's click the "Where am I?" button. But it doesn't show the image. The First(result).Url value is actually similar to this:

    appres://blobmanager/1090a86393a843adbfcf428f0b90e91b/1

    It's the image reference value somewhere you can't get there.

    Workaround Power Automate workflow

    Therefore, you need a workaround using a Power Automate workflow to sort out this issue. Open the Power Automate Studio, create an instant cloud flow with the Power App trigger, and give it the "Where am I" name. Then add input parameters of lat, long and zoom.

    Power Apps trigger on Power Automate workflow

    Add custom connector action to get the map image.

    Select action to get the Google Maps image

    In the action, pass the appropriate parameters to the action.

    Pass parameters to the custom connector action

    Add a "Response" action and put the following values into each field.

    • "Body" field:

      {
      "base64Image": <power_automate_expression>
      }

      The <power_automate_expression> should be concat('data:', body('GetGoogleMapImage')?['$content-type'], ';base64,', body('GetGoogleMapImage')?['$content']).

    • "Response Body JSON Schema" field:

      {
      "type": "object",
      "properties": {
      "base64Image": {
      "type": "string"
      }
      }
      }

    Format the Response action

    Let's return to the Power Apps Studio and add the Power Automate workflow you created.

    Add Power Automate workflow

    Select "Button1" and change the value on the property "OnSelect" below. It replaces the direct call to the custom connector with the Power Automate workflow.

    ClearCollect(
    result,
    WhereamI.Run(
    Location.Latitude,
    Location.Longitude,
    First(zoomlevel).Value
    )
    )

    Also, change the value on the property "OnChange" of the "Slider1" control below, replacing the custom connector call with the Power Automate workflow call.

    ClearCollect(
    zoomlevel,
    Slider1.Value
    );
    ClearCollect(
    result,
    WhereamI.Run(
    Location.Latitude,
    Location.Longitude,
    First(zoomlevel).Value
    )
    )

    And finally, change the "Image1" control's "Image" property value below.

    First(result).base64Image

    The workaround has been applied. Click the "Where am I?" button to see your current location from Google Maps.

    Run Power Apps app #1

    If you change the slider left or right, you will see either the zoomed-in image or the zoomed-out image.

    Run Power Apps app #2

    Now, you've created a Power Apps app to show your current location using:

    • Google Maps API through the custom connector, and
    • Custom connector written in Azure Functions with OpenAPI extension!

    Exercise: Try this yourself!

    You can fork this GitHub repository to your account and play around with it to see how the custom connector works. After forking the repository, make sure that you create all the necessary secrets to your repository documented in the README file.

    Then, click the "Deploy to Azure" button, and it will provision all necessary Azure resources and deploy an Azure Functions app for a custom connector.

    Deploy To Azure

    Once everything is deployed successfully, try to create a Power Apps app and Power Automate workflow to see your current location in real-time!

    Resources: For self-study!

    Want to know more about Power Platform custom connector and Azure Functions OpenAPI extension? Here are several resources you can take a look at:

    - - + + \ No newline at end of file diff --git a/blog/tags/serverless-september/page/30/index.html b/blog/tags/serverless-september/page/30/index.html index a14f29776a..ff188227ce 100644 --- a/blog/tags/serverless-september/page/30/index.html +++ b/blog/tags/serverless-september/page/30/index.html @@ -14,15 +14,15 @@ - - + +

    33 posts tagged with "serverless-september"

    View All Tags

    · 9 min read
    Nitya Narasimhan

    Welcome to Day 2️⃣ of #30DaysOfServerless!

    Today, we kickstart our journey into serveless on Azure with a look at Functions As a Service. We'll explore Azure Functions - from core concepts to usage patterns.

    Ready? Let's Go!


    What We'll Cover

    • What is Functions-as-a-Service? (FaaS)
    • What is Azure Functions?
    • Triggers, Bindings and Custom Handlers
    • What is Durable Functions?
    • Orchestrators, Entity Functions and Application Patterns
    • Exercise: Take the Cloud Skills Challenge!
    • Resources: #30DaysOfServerless Collection.


    1. What is FaaS?

    Faas stands for Functions As a Service (FaaS). But what does that mean for us as application developers? We know that building and deploying modern applications at scale can get complicated and it starts with us needing to take decisions on Compute. In other words, we need to answer this question: "where should I host my application given my resource dependencies and scaling requirements?"

    this useful flowchart

    Azure has this useful flowchart (shown below) to guide your decision-making. You'll see that hosting options generally fall into three categories:

    • Infrastructure as a Service (IaaS) - where you provision and manage Virtual Machines yourself (cloud provider manages infra).
    • Platform as a Service (PaaS) - where you use a provider-managed hosting environment like Azure Container Apps.
    • Functions as a Service (FaaS) - where you forget about hosting environments and simply deploy your code for the provider to run.

    Here, "serverless" compute refers to hosting options where we (as developers) can focus on building apps without having to manage the infrastructure. See serverless compute options on Azure for more information.


    2. Azure Functions

    Azure Functions is the Functions-as-a-Service (FaaS) option on Azure. It is the ideal serverless solution if your application is event-driven with short-lived workloads. With Azure Functions, we develop applications as modular blocks of code (functions) that are executed on demand, in response to configured events (triggers). This approach brings us two advantages:

    • It saves us money. We only pay for the time the function runs.
    • It scales with demand. We have 3 hosting plans for flexible scaling behaviors.

    Azure Functions can be programmed in many popular languages (C#, F#, Java, JavaScript, TypeScript, PowerShell or Python), with Azure providing language-specific handlers and default runtimes to execute them.

    Concept: Custom Handlers
    • What if we wanted to program in a non-supported language?
    • Or we wanted to use a different runtime for a supported language?

    Custom Handlers have you covered! These are lightweight webservers that can receive and process input events from the Functions host - and return responses that can be delivered to any output targets. By this definition, custom handlers can be implemented by any language that supports receiving HTTP events. Check out the quickstart for writing a custom handler in Rust or Go.

    Custom Handlers

    Concept: Trigger and Bindings

    We talked about what functions are (code blocks). But when are they invoked or executed? And how do we provide inputs (arguments) and retrieve outputs (results) from this execution?

    This is where triggers and bindings come in.

    • Triggers define how a function is invoked and what associated data it will provide. A function must have exactly one trigger.
    • Bindings declaratively define how a resource is connected to the function. The resource or binding can be of type input, output, or both. Bindings are optional. A Function can have multiple input, output bindings.

    Azure Functions comes with a number of supported bindings that can be used to integrate relevant services to power a specific scenario. For instance:

    • HTTP Triggers - invokes the function in response to an HTTP request. Use this to implement serverless APIs for your application.
    • Event Grid Triggers invokes the function on receiving events from an Event Grid. Use this to process events reactively, and potentially publish responses back to custom Event Grid topics.
    • SignalR Service Trigger invokes the function in response to messages from Azure SignalR, allowing your application to take actions with real-time contexts.

    Triggers and bindings help you abstract your function's interfaces to other components it interacts with, eliminating hardcoded integrations. They are configured differently based on the programming language you use. For example - JavaScript functions are configured in the functions.json file. Here's an example of what that looks like.

    {
    "disabled":false,
    "bindings":[
    // ... bindings here
    {
    "type": "bindingType",
    "direction": "in",
    "name": "myParamName",
    // ... more depending on binding
    }
    ]
    }

    The key thing to remember is that triggers and bindings have a direction property - triggers are always in, input bindings are in and output bindings are out. Some bindings can support a special inout direction.

    The documentation has code examples for bindings to popular Azure services. Here's an example of the bindings and trigger configuration for a BlobStorage use case.

    // function.json configuration

    {
    "bindings": [
    {
    "queueName": "myqueue-items",
    "connection": "MyStorageConnectionAppSetting",
    "name": "myQueueItem",
    "type": "queueTrigger",
    "direction": "in"
    },
    {
    "name": "myInputBlob",
    "type": "blob",
    "path": "samples-workitems/{queueTrigger}",
    "connection": "MyStorageConnectionAppSetting",
    "direction": "in"
    },
    {
    "name": "myOutputBlob",
    "type": "blob",
    "path": "samples-workitems/{queueTrigger}-Copy",
    "connection": "MyStorageConnectionAppSetting",
    "direction": "out"
    }
    ],
    "disabled": false
    }

    The code below shows the function implementation. In this scenario, the function is triggered by a queue message carrying an input payload with a blob name. In response, it copies that data to the resource associated with the output binding.

    // function implementation

    module.exports = async function(context) {
    context.log('Node.js Queue trigger function processed', context.bindings.myQueueItem);
    context.bindings.myOutputBlob = context.bindings.myInputBlob;
    };
    Concept: Custom Bindings

    What if we have a more complex scenario that requires bindings for non-supported resources?

    There is an option create custom bindings if necessary. We don't have time to dive into details here but definitely check out the documentation


    3. Durable Functions

    This sounds great, right?. But now, let's talk about one challenge for Azure Functions. In the use cases so far, the functions are stateless - they take inputs at runtime if necessary, and return output results if required. But they are otherwise self-contained, which is great for scalability!

    But what if I needed to build more complex workflows that need to store and transfer state, and complete operations in a reliable manner? Durable Functions are an extension of Azure Functions that makes stateful workflows possible.

    Concept: Orchestrator Functions

    How can I create workflows that coordinate functions?

    Durable Functions use orchestrator functions to coordinate execution of other Durable functions within a given Functions app. These functions are durable and reliable. Later in this post, we'll talk briefly about some application patterns that showcase popular orchestration scenarios.

    Concept: Entity Functions

    How do I persist and manage state across workflows?

    Entity Functions provide explicit state mangement for Durable Functions, defining operations to read and write state to durable entities. They are associated with a special entity trigger for invocation. These are currently available only for a subset of programming languages so check to see if they are supported for your programming language of choice.

    USAGE: Application Patterns

    Durable Functions are a fascinating topic that would require a separate, longer post, to do justice. For now, let's look at some application patterns that showcase the value of these starting with the simplest one - Function Chaining as shown below:

    Function Chaining

    Here, we want to execute a sequence of named functions in a specific order. As shown in the snippet below, the orchestrator function coordinates invocations on the given functions in the desired sequence - "chaining" inputs and outputs to establish the workflow. Take note of the yield keyword. This triggers a checkpoint, preserving the current state of the function for reliable operation.

    const df = require("durable-functions");

    module.exports = df.orchestrator(function*(context) {
    try {
    const x = yield context.df.callActivity("F1");
    const y = yield context.df.callActivity("F2", x);
    const z = yield context.df.callActivity("F3", y);
    return yield context.df.callActivity("F4", z);
    } catch (error) {
    // Error handling or compensation goes here.
    }
    });

    Other application patterns for durable functions include:

    There's a lot more to explore but we won't have time to do that today. Definitely check the documentation and take a minute to read the comparison with Azure Logic Apps to understand what each technology provides for serverless workflow automation.


    4. Exercise

    That was a lot of information to absorb! Thankfully, there are a lot of examples in the documentation that can help put these in context. Here are a couple of exercises you can do, to reinforce your understanding of these concepts.


    5. What's Next?

    The goal for today was to give you a quick tour of key terminology and concepts related to Azure Functions. Tomorrow, we dive into the developer experience, starting with core tools for local development and ending by deploying our first Functions app.

    Want to do some prep work? Here are a few useful links:


    6. Resources


    - - + + \ No newline at end of file diff --git a/blog/tags/serverless-september/page/31/index.html b/blog/tags/serverless-september/page/31/index.html index 99b1212152..60088ca526 100644 --- a/blog/tags/serverless-september/page/31/index.html +++ b/blog/tags/serverless-september/page/31/index.html @@ -14,13 +14,13 @@ - - + +

    33 posts tagged with "serverless-september"

    View All Tags

    · 5 min read
    Nitya Narasimhan
    Devanshi Joshi

    What We'll Cover

    • What is Serverless September? (6 initiatives)
    • How can I participate? (3 actions)
    • How can I skill up (30 days)
    • Who is behind this? (Team Contributors)
    • How can you contribute? (Custom Issues)
    • Exercise: Take the Cloud Skills Challenge!
    • Resources: #30DaysOfServerless Collection.

    Serverless September

    Welcome to Day 01 of 🍂 #ServerlessSeptember! Today, we kick off a full month of content and activities to skill you up on all things Serverless on Azure with content, events, and community interactions! Read on to learn about what we have planned!


    Explore our initiatives

    We have a number of initiatives planned for the month to help you learn and skill up on relevant technologies. Click on the links to visit the relevant pages for each.

    We'll go into more details about #30DaysOfServerless in this post - don't forget to subscribe to the blog to get daily posts delivered directly to your preferred feed reader!


    Register for events!

    What are 3 things you can do today, to jumpstart your learning journey?

    Serverless Hacks


    #30DaysOfServerless

    #30DaysOfServerless is a month-long series of daily blog posts grouped into 4 themed weeks - taking you from core concepts to end-to-end solution examples in 30 days. Each article will be short (5-8 mins reading time) and provide exercises and resources to help you reinforce learnings and take next steps.

    This series focuses on the Serverless On Azure learning journey in four stages, each building on the previous week to help you skill up in a beginner-friendly way:

    We have a tentative roadmap for the topics we hope to cover and will keep this updated as we go with links to actual articles as they get published.

    Week 1: FOCUS ON FUNCTIONS ⚡️

    Here's a sneak peek at what we have planned for week 1. We'll start with a broad look at fundamentals, walkthrough examples for each targeted programming language, then wrap with a post that showcases the role of Azure Functions in powering different serverless scenarios.

    • Sep 02: Learn Core Concepts for Azure Functions
    • Sep 03: Build and deploy your first Function
    • Sep 04: Azure Functions - for Java Developers!
    • Sep 05: Azure Functions - for JavaScript Developers!
    • Sep 06: Azure Functions - for .NET Developers!
    • Sep 07: Azure Functions - for Python Developers!
    • Sep 08: Wrap: Azure Functions + Serverless on Azure

    Ways to Participate..

    We hope you are as excited as we are, to jumpstart this journey. We want to make this a useful, beginner-friendly journey and we need your help!

    Here are the many ways you can participate:

    • Follow Azure on dev.to - we'll republish posts under this series page and welcome comments and feedback there!
    • Discussions on GitHub - Use this if you have feedback for us (on how we can improve these resources), or want to chat with your peers about serverless topics.
    • Custom Issues - just pick a template, create a new issue by filling in the requested details, and submit. You can use these to:
      • submit questions for AskTheExpert (live Q&A) ahead of time
      • submit your own articles or projects for community to learn from
      • share your ServerlessHack and get listed in our Hall Of Fame!
      • report bugs or share ideas for improvements

    Here's the list of custom issues currently defined.

    Community Buzz

    Let's Get Started!

    Now you know everything! We hope you are as excited as we are to dive into a full month of active learning and doing! Don't forget to subscribe for updates in your favorite feed reader! And look out for our first Azure Functions post tomorrow!


    - - + + \ No newline at end of file diff --git a/blog/tags/serverless-september/page/32/index.html b/blog/tags/serverless-september/page/32/index.html index 03678a9e9c..4ef9859908 100644 --- a/blog/tags/serverless-september/page/32/index.html +++ b/blog/tags/serverless-september/page/32/index.html @@ -14,13 +14,13 @@ - - + +

    33 posts tagged with "serverless-september"

    View All Tags

    · 3 min read
    Sara Gibbons

    ✨ Serverless September For Students

    My love for the tech industry grows as it evolves. Not just for the new technologies to play with, but seeing how paths into a tech career continue to expand. Allowing so many new voices, ideas and perspectives to our industry. With serverless computing removing barriers of entry for so many.

    It's a reason I enjoy working with universities and students. I get to hear the excitement of learning, fresh ideas and perspectives from our student community. All you students are incredible! How you view serverless, and what it can do, so cool!

    This year for Serverless September we want to hear all the amazing ways our student community is learning and working with Azure Serverless, and have all new ways for you to participate.

    Getting Started

    If you don't already have an Azure for Students account you can easily get your FREE account created at Azure for Students Sign up.

    If you are new to serverless, here are a couple links to get you started:

    No Experience, No problem

    For Serverless September we have planned beginner friendly content all month long. Covering such services as:

    You can follow #30DaysOfServerles here on the blog for daily posts covering concepts, scenarios, and how to create end-to-end solutions.

    Join the Cloud Skills Challenge where we have selected a list of Learn Modules for you to go through at your own pace, including deploying a full stack application with Azure Static Web Apps.

    Have A Question

    We want to hear it! All month long we will have Ask The Expert sessions. Submit your questions at any time and will be be sure to get one of our Azure Serverless experts to get you an answer.

    Share What You've Created

    If you have written a blog post, recorded a video, have an open source Azure Serverless project, we'd love to see it! Here is some links for you to share your creations

    🧭 Explore Student Resources

    ⚡️ Join us!

    Multiple teams across Microsoft are working to create Serverless September! They all want to hear from our incredible student community. We can't wait to share all the Serverless September resources and hear what you have learned and created. Here are some ways to keep up to date on all Serverless September activity:

    - - + + \ No newline at end of file diff --git a/blog/tags/serverless-september/page/33/index.html b/blog/tags/serverless-september/page/33/index.html index 3d877c3f7d..b7971be636 100644 --- a/blog/tags/serverless-september/page/33/index.html +++ b/blog/tags/serverless-september/page/33/index.html @@ -14,13 +14,13 @@ - - + +

    33 posts tagged with "serverless-september"

    View All Tags

    · 3 min read
    Nitya Narasimhan
    Devanshi Joshi

    🍂 It's September?

    Well, almost! September 1 is a few days away and I'm excited! Why? Because it's the perfect time to revisit #Serverless September, a month of

    ".. content-driven learning where experts and practitioners share their insights and tutorials on how to use serverless technologies effectively in today's ecosystems"

    If the words look familiar, it's because I actually wrote them 2 years ago when we launched the 2020 edition of this series. You might even recall this whimsical image I drew to capture the concept of September (fall) and Serverless (event-driven on-demand compute). Since then, a lot has happened in the serverless ecosystem!

    You can still browse the 2020 Content Collection to find great talks, articles and code samples to get started using Serverless on Azure. But read on to learn what's new!

    🧐 What's New?

    Well - quite a few things actually. This year, Devanshi Joshi and I expanded the original concept in a number of ways. Here's just a few of them that come to mind.

    New Website

    This year, we created this website (shortcut: https://aka.ms/serverless-september) to serve as a permanent home for content in 2022 and beyond - making it a canonical source for the #serverless posts we publish to tech communities like dev.to, Azure Developer Community and Apps On Azure. We hope this also makes it easier for you to search for, or discover, current and past articles that support your learning journey!

    Start by bookmarking these two sites:

    More Options

    Previous years focused on curating and sharing content authored by Microsoft and community contributors, showcasing serverless examples and best practices. This was perfect for those who already had experience with the core devtools and concepts.

    This year, we wanted to combine beginner-friendly options (for those just starting their serverless journey) with more advanced insights (for those looking to skill up further). Here's a sneak peek at some of the initiatives we've got planned!

    We'll also explore the full spectrum of serverless - from Functions-as-a-Service (for granularity) to Containerization (for deployment) and Microservices (for scalability). Here are a few services and technologies you'll get to learn more about:

    ⚡️ Join us!

    This has been a labor of love from multiple teams at Microsoft! We can't wait to share all the resources that we hope will help you skill up on all things Serverless this September! Here are a couple of ways to participate:

    - - + + \ No newline at end of file diff --git a/blog/tags/serverless-september/page/4/index.html b/blog/tags/serverless-september/page/4/index.html index 16e3ac8ee9..617fab4cea 100644 --- a/blog/tags/serverless-september/page/4/index.html +++ b/blog/tags/serverless-september/page/4/index.html @@ -14,14 +14,14 @@ - - + +

    33 posts tagged with "serverless-september"

    View All Tags

    · 5 min read
    Madhura Bharadwaj

    Welcome to Day 26 of #30DaysOfServerless!

    Today, we have a special set of posts from our Zero To Hero 🚀 initiative, featuring blog posts authored by our Product Engineering teams for #ServerlessSeptember. Posts were originally published on the Apps on Azure blog on Microsoft Tech Community.


    What We'll Cover

    • Monitoring your Azure Functions
    • Built-in log streaming
    • Live Metrics stream
    • Troubleshooting Azure Functions


    Monitoring your Azure Functions:

    Azure Functions uses Application Insights to collect and analyze log data from individual function executions in your function app.

    Using Application Insights

    Application Insights collects log, performance, and error data. By automatically detecting performance anomalies and featuring powerful analytics tools, you can more easily diagnose issues and better understand how your functions are used. These tools are designed to help you continuously improve performance and usability of your functions. You can even use Application Insights during local function app project development.

    Typically, you create an Application Insights instance when you create your function app. In this case, the instrumentation key required for the integration is already set as an application setting named APPINSIGHTS_INSTRUMENTATIONKEY. With Application Insights integration enabled, telemetry data is sent to your connected Application Insights instance. This data includes logs generated by the Functions host, traces written from your functions code, and performance data. In addition to data from your functions and the Functions host, you can also collect data from the Functions scale controller.

    By default, the data collected from your function app is stored in Application Insights. In the Azure portal, Application Insights provides an extensive set of visualizations of your telemetry data. You can drill into error logs and query events and metrics. To learn more, including basic examples of how to view and query your collected data, see Analyze Azure Functions telemetry in Application Insights.

    Using Log Streaming

    In addition to this, you can have a smoother debugging experience through log streaming. There are two ways to view a stream of log files being generated by your function executions.

    • Built-in log streaming: the App Service platform lets you view a stream of your application log files. This is equivalent to the output seen when you debug your functions during local development and when you use the Test tab in the portal. All log-based information is displayed. For more information, see Stream logs. This streaming method supports only a single instance and can't be used with an app running on Linux in a Consumption plan.
    • Live Metrics Stream: when your function app is connected to Application Insights, you can view log data and other metrics in near real-time in the Azure portal using Live Metrics Stream. Use this method when monitoring functions running on multiple-instances or on Linux in a Consumption plan. This method uses sampled data. Log streams can be viewed both in the portal and in most local development environments.
    Monitoring Azure Functions

    Learn how to configure monitoring for your Azure Functions. See Monitoring Azure Functions data reference for detailed information on the metrics and logs metrics created by Azure Functions.

    In addition to this, Azure Functions uses Azure Monitor to monitor the health of your function apps. Azure Functions collects the same kinds of monitoring data as other Azure resources that are described in Azure Monitor data collection. See Monitoring Azure Functions data reference for detailed information on the metrics and logs metrics created by Azure Functions.

    Troubleshooting your Azure Functions:

    When you do run into issues with your function app, Azure Functions diagnostics points out what’s wrong. It guides you to the right information to troubleshoot and resolve the issue more easily and quickly.

    Let’s explore how to use Azure Functions diagnostics to diagnose and solve common function app issues.

    1. Navigate to your function app in the Azure portal.
    2. Select Diagnose and solve problems to open Azure Functions diagnostics.
    3. Once you’re here, there are multiple ways to retrieve the information you’re looking for. Choose a category that best describes the issue of your function app by using the keywords in the homepage tile. You can also type a keyword that best describes your issue in the search bar. There’s also a section at the bottom of the page that will directly take you to some of the more popular troubleshooting tools. For example, you could type execution to see a list of diagnostic reports related to your function app execution and open them directly from the homepage.

    Monitoring and troubleshooting apps in Azure Functions

    1. For example, click on the Function App Down or Reporting Errors link under Popular troubleshooting tools section. You will find detailed analysis, insights and next steps for the issues that were detected. On the left you’ll see a list of detectors. Click on them to explore more, or if there’s a particular keyword you want to look for, type it Into the search bar on the top.

    Monitoring and troubleshooting apps in Azure Functions

    TROUBLESHOOTING TIP

    Here are some general troubleshooting tips that you can follow if you find your Function App throwing Azure Functions Runtime unreachable error.

    Also be sure to check out the recommended best practices to ensure your Azure Functions are highly reliable. This article details some best practices for designing and deploying efficient function apps that remain healthy and perform well in a cloud-based environment.

    Bonus tip:

    - - + + \ No newline at end of file diff --git a/blog/tags/serverless-september/page/5/index.html b/blog/tags/serverless-september/page/5/index.html index 88638b2c6b..461ff04922 100644 --- a/blog/tags/serverless-september/page/5/index.html +++ b/blog/tags/serverless-september/page/5/index.html @@ -14,13 +14,13 @@ - - + +

    33 posts tagged with "serverless-september"

    View All Tags

    · 7 min read
    Brian Benz

    Welcome to Day 25 of #30DaysOfServerless!

    Azure Container Apps enable application code packaged in containers to run and scale without the overhead of managing cloud infrastructure and container orchestration. In this post I'll show you how to deploy a Java application running on Spring Boot in a container to Azure Container Registry and Azure Container Apps.


    What We'll Cover

    • Introduction to Deploying Java containers in the cloud
    • Step-by-step: Deploying to Azure Container Registry
    • Step-by-step: Deploying and running on Azure Container Apps
    • Resources: For self-study!


    Deploy Java containers to cloud

    We'll deploy a Java application running on Spring Boot in a container to Azure Container Registry and Azure Container Apps. Here are the main steps:

    • Create Azure Container Registry (ACR) on Azure portal
    • Create Azure Container App (ACA) on Azure portal.
    • Deploy code to Azure Container Registry from the Azure CLI.
    • Deploy container from ACR to ACA using the Azure portal.
    PRE-REQUISITES

    Sign in to Azure from the CLI using the az login command, and follow the prompts in your browser to complete the authentication process. Also, ensure you're running the latest version of the CLI by using the az upgrade command.

    1. Get Sample Code

    Fork and clone the sample GitHub repo to your local machine. Navigate to the and click Fork in the top-right corner of the page.

    The example code that we're using is a very basic containerized Spring Boot example. There are a lot more details to learn about Spring boot apps in docker, for a deep dive check out this Spring Boot Guide

    2. Run Sample Locally (Optional)

    If you have docker installed locally, you can optionally test the code on your local machine. Navigate to the root directory of the forked repository and run the following commands:

    docker build -t spring-boot-docker-aca .
    docker run -p 8080:8080 spring-boot-docker-aca

    Open a browser and go to https://localhost:8080. You should see this message:

    Hello Docker World

    That indicates the the Spring Boot app is successfully running locally in a docker container.

    Next, let's set up an Azure Container Registry an an Azure Container App and deploy this container to the cloud!


    3. Step-by-step: Deploy to ACR

    To create a container registry from the portal dashboard, Select Create a resource > Containers > Container Registry.

    Navigate to container registry in portal

    In the Basics tab, enter values for Resource group and Registry name. The registry name must be unique within Azure, and contain 5-50 alphanumeric characters. Create a new resource group in the West US location named spring-boot-docker-aca. Select the 'Basic' SKU.

    Keep the default values for the remaining settings. Then select Review + create, then Create. When the Deployment succeeded message appears, select the container registry in the portal.

    Note the registry server name ending with azurecr.io. You will use this in the following steps when you push and pull images with Docker.

    3.1 Log into registry using the Azure CLI

    Before pushing and pulling container images, you must log in to the registry instance. Sign into the Azure CLI on your local machine, then run the az acr login command. For this step, use the registry name, not the server name ending with azurecr.io.

    From the command line, type:

    az acr login --name myregistryname

    The command returns Login Succeeded once completed.

    3.2 Build & deploy with az acr build

    Next, we're going to deploy the docker container we created earlier using the AZ ACR Build command. AZ ACR Build creates a docker build from local code and pushes the container to Azure Container Registry if the build is successful.

    Go to your local clone of the spring-boot-docker-aca repo in the command line, type:

    az acr build --registry myregistryname --image spring-boot-docker-aca:v1 .

    3.3 List container images

    Once the AZ ACR Build command is complete, you should be able to view the container as a repository in the registry. In the portal, open your registry and select Repositories, then select the spring-boot-docker-aca repository you created with docker push. You should also see the v1 image under Tags.

    4. Deploy on ACA

    Now that we have an image in the Azure Container Registry, we can deploy it to Azure Container Apps. For the first deployment, we'll pull the container from our ACR as part of the ACA setup.

    4.1 Create a container app

    We'll create the container app at the same place that we created the container registry in the Azure portal. From the portal, select Create a resource > Containers > Container App. In the Basics tab, set these values:

    4.2 Enter project details

    SettingAction
    SubscriptionYour Azure subscription.
    Resource groupUse the spring-boot-docker-aca resource group
    Container app nameEnter spring-boot-docker-aca.

    4.3 Create an environment

    1. In the Create Container App environment field, select Create new.

    2. In the Create Container App Environment page on the Basics tab, enter the following values:

      SettingValue
      Environment nameEnter my-environment.
      RegionSelect westus3.
    3. Select OK.

    4. Select the Create button at the bottom of the Create Container App Environment page.

    5. Select the Next: App settings button at the bottom of the page.

    5. App settings tab

    The App settings tab is where you connect to the ACR and pull the repository image:

    SettingAction
    Use quickstart imageUncheck the checkbox.
    NameEnter spring-boot-docker-aca.
    Image sourceSelect Azure Container Registry
    RegistrySelect your ACR from the list.
    ImageSelect spring-boot-docker-aca from the list.
    Image TagSelect v1 from the list.

    5.1 Application ingress settings

    SettingAction
    IngressSelect Enabled.
    Ingress visibilitySelect External to publicly expose your container app.
    Target portEnter 8080.

    5.2 Deploy the container app

    1. Select the Review and create button at the bottom of the page.
    2. Select Create.

    Once the deployment is successfully completed, you'll see the message: Your deployment is complete.

    5.3 Verify deployment

    In the portal, go to the Overview of your spring-boot-docker-aca Azure Container App, and click on the Application Url. You should see this message in the browser:

    Hello Docker World

    That indicates the the Spring Boot app is running in a docker container in your spring-boot-docker-aca Azure Container App.

    Resources: For self-study!

    Once you have an understanding of the basics in ths post, there is so much more to learn!

    Thanks for stopping by!

    - - + + \ No newline at end of file diff --git a/blog/tags/serverless-september/page/6/index.html b/blog/tags/serverless-september/page/6/index.html index 83a6da0c41..bbb0caba06 100644 --- a/blog/tags/serverless-september/page/6/index.html +++ b/blog/tags/serverless-september/page/6/index.html @@ -14,13 +14,13 @@ - - + +

    33 posts tagged with "serverless-september"

    View All Tags

    · 19 min read
    Alex Wolf

    Welcome to Day 24 of #30DaysOfServerless!

    We continue exploring E2E scenarios with this tutorial where you'll deploy a containerized ASP.NET Core 6.0 application to Azure Container Apps.

    The application consists of a front-end web app built using Blazor Server, as well as two Web API projects to manage data. These projects will exist as three separate containers inside of a shared container apps environment.


    What We'll Cover

    • Deploy ASP.NET Core 6.0 app to Azure Container Apps
    • Automate deployment workflows using GitHub Actions
    • Provision and deploy resources using Azure Bicep
    • Exercise: Try this yourself!
    • Resources: For self-study!


    Introduction

    Azure Container Apps enables you to run microservices and containerized applications on a serverless platform. With Container Apps, you enjoy the benefits of running containers while leaving behind the concerns of manually configuring cloud infrastructure and complex container orchestrators.

    In this tutorial, you'll deploy a containerized ASP.NET Core 6.0 application to Azure Container Apps. The application consists of a front-end web app built using Blazor Server, as well as two Web API projects to manage data. These projects will exist as three separate containers inside of a shared container apps environment.

    You will use GitHub Actions in combination with Bicep to deploy the application. These tools provide an approachable and sustainable solution for building CI/CD pipelines and working with Container Apps.

    PRE-REQUISITES

    Architecture

    In this tutorial, we'll setup a container app environment with a separate container for each project in the sample store app. The major components of the sample project include:

    • A Blazor Server front-end web app to display product information
    • A products API to list available products
    • An inventory API to determine how many products are in stock
    • GitHub Actions and Bicep templates to provision Azure resources and then build and deploy the sample app.

    You will explore these templates later in the tutorial.

    Public internet traffic should be proxied to the Blazor app. The back-end APIs should only be reachable via requests from the Blazor app inside the container apps environment. This setup can be achieved using container apps environment ingress configurations during deployment.

    An architecture diagram of the shopping app


    Project Sources

    Want to follow along? Fork the sample below. The tutorial can be completed with or without Dapr integration. Pick the path you feel comfortable in. Dapr provides various benefits that make working with Microservices easier - you can learn more in the docs. For this tutorial you will need GitHub and Azure CLI.

    PICK YOUR PATH

    To follow along with this tutorial, fork the relevant sample project below.

    You can run the app locally from Visual Studio:

    • Right click on the Blazor Store project and select Set as Startup Project.
    • Press the start button at the top of Visual Studio to run the app.
    • (Once running) start each API in the background by
    • right-clicking on the project node
    • selecting Debug --> Start without debugging.

    Once the Blazor app is running, you should see something like this:

    An architecture diagram of the shopping app


    Configuring Azure credentials

    In order to deploy the application to Azure through GitHub Actions, you first need to create a service principal. The service principal will allow the GitHub Actions process to authenticate to your Azure subscription to create resources and deploy code. You can learn more about Service Principals in the Azure CLI documentation. For this step you'll need to be logged into the Azure CLI.

    1) If you have not done so already, make sure to fork the sample project to your own GitHub account or organization.

    1) Once you have completed this step, create a service principal using the Azure CLI command below:

    ```azurecli
    $subscriptionId=$(az account show --query id --output tsv)
    az ad sp create-for-rbac --sdk-auth --name WebAndApiSample --role Contributor --scopes /subscriptions/$subscriptionId
    ```

    1) Copy the JSON output of the CLI command to your clipboard

    1) Under the settings tab of your forked GitHub repo, create a new secret named AzureSPN. The name is important to match the Bicep templates included in the project, which we'll review later. Paste the copied service principal values on your clipboard into the secret and save your changes. This new secret will be used by the GitHub Actions workflow to authenticate to Azure.

    :::image type="content" source="./img/dotnet/github-secrets.png" alt-text="A screenshot of adding GitHub secrets.":::

    Deploy using Github Actions

    You are now ready to deploy the application to Azure Container Apps using GitHub Actions. The sample application includes a GitHub Actions template that is configured to build and deploy any changes to a branch named deploy. The deploy branch does not exist in your forked repository by default, but you can easily create it through the GitHub user interface.

    1) Switch to the Actions tab along the top navigation of your GitHub repository. If you have not done so already, ensure that workflows are enabled by clicking the button in the center of the page.

    A screenshot showing how to enable GitHub actions

    1) Navigate to the main Code tab of your repository and select the main dropdown. Enter deploy into the branch input box, and then select Create branch: deploy from 'main'.

    A screenshot showing how to create the deploy branch

    1) On the new deploy branch, navigate down into the .github/workflows folder. You should see a file called deploy.yml, which contains the main GitHub Actions workflow script. Click on the file to view its content. You'll learn more about this file later in the tutorial.

    1) Click the pencil icon in the upper right to edit the document.

    1) Change the RESOURCE_GROUP_NAME: value to msdocswebappapis or another valid resource group name of your choosing.

    1) In the upper right of the screen, select Start commit and then Commit changes to commit your edit. This will persist the change to the file and trigger the GitHub Actions workflow to build and deploy the app.

    A screenshot showing how to commit changes

    1) Switch to the Actions tab along the top navigation again. You should see the workflow running to create the necessary resources and deploy the app. The workflow may take several minutes to run. When it completes successfully, all of the jobs should have a green checkmark icon next to them.

    The completed GitHub workflow.

    Explore the Azure resources

    Once the GitHub Actions workflow has completed successfully you can browse the created resources in the Azure portal.

    1) On the left navigation, select Resource Groups. Next,choose the msdocswebappapis resource group that was created by the GitHub Actions workflow.

    2) You should see seven resources available that match the screenshot and table descriptions below.

    The resources created in Azure.

    Resource nameTypeDescription
    inventoryContainer appThe containerized inventory API.
    msdocswebappapisacrContainer registryA registry that stores the built Container images for your apps.
    msdocswebappapisaiApplication insightsApplication insights provides advanced monitoring, logging and metrics for your apps.
    msdocswebappapisenvContainer apps environmentA container environment that manages networking, security and resource concerns. All of your containers live in this environment.
    msdocswebappapislogsLog Analytics workspaceA workspace environment for managing logging and analytics for the container apps environment
    productsContainer appThe containerized products API.
    storeContainer appThe Blazor front-end web app.

    3) You can view your running app in the browser by clicking on the store container app. On the overview page, click the Application Url link on the upper right of the screen.

    :::image type="content" source="./img/dotnet/application-url.png" alt-text="The link to browse the app.":::

    Understanding the GitHub Actions workflow

    The GitHub Actions workflow created and deployed resources to Azure using the deploy.yml file in the .github folder at the root of the project. The primary purpose of this file is to respond to events - such as commits to a branch - and run jobs to accomplish tasks. The deploy.yml file in the sample project has three main jobs:

    • Provision: Create the necessary resources in Azure, such as the container apps environment. This step leverages Bicep templates to create the Azure resources, which you'll explore in a moment.
    • Build: Create the container images for the three apps in the project and store them in the container registry.
    • Deploy: Deploy the container images to the different container apps created during the provisioning job.

    The deploy.yml file also accepts parameters to make the workflow more dynamic, such as setting the resource group name or the Azure region resources will be provisioned to.

    Below is a commented version of the deploy.yml file that highlights the essential steps.

    name: Build and deploy .NET application to Container Apps

    # Trigger the workflow on pushes to the deploy branch
    on:
    push:
    branches:
    - deploy

    env:
    # Set workflow variables
    RESOURCE_GROUP_NAME: msdocswebappapis

    REGION: eastus

    STORE_DOCKER: Store/Dockerfile
    STORE_IMAGE: store

    INVENTORY_DOCKER: Store.InventoryApi/Dockerfile
    INVENTORY_IMAGE: inventory

    PRODUCTS_DOCKER: Store.ProductApi/Dockerfile
    PRODUCTS_IMAGE: products

    jobs:
    # Create the required Azure resources
    provision:
    runs-on: ubuntu-latest

    steps:

    - name: Checkout to the branch
    uses: actions/checkout@v2

    - name: Azure Login
    uses: azure/login@v1
    with:
    creds: ${{ secrets.AzureSPN }}

    - name: Create resource group
    uses: azure/CLI@v1
    with:
    inlineScript: >
    echo "Creating resource group in Azure"
    echo "Executing 'az group create -l ${{ env.REGION }} -n ${{ env.RESOURCE_GROUP_NAME }}'"
    az group create -l ${{ env.REGION }} -n ${{ env.RESOURCE_GROUP_NAME }}

    # Use Bicep templates to create the resources in Azure
    - name: Creating resources
    uses: azure/CLI@v1
    with:
    inlineScript: >
    echo "Creating resources"
    az deployment group create --resource-group ${{ env.RESOURCE_GROUP_NAME }} --template-file '/github/workspace/Azure/main.bicep' --debug

    # Build the three app container images
    build:
    runs-on: ubuntu-latest
    needs: provision

    steps:

    - name: Checkout to the branch
    uses: actions/checkout@v2

    - name: Azure Login
    uses: azure/login@v1
    with:
    creds: ${{ secrets.AzureSPN }}

    - name: Set up Docker Buildx
    uses: docker/setup-buildx-action@v1

    - name: Login to ACR
    run: |
    set -euo pipefail
    access_token=$(az account get-access-token --query accessToken -o tsv)
    refresh_token=$(curl https://${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io/oauth2/exchange -v -d "grant_type=access_token&service=${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io&access_token=$access_token" | jq -r .refresh_token)
    docker login -u 00000000-0000-0000-0000-000000000000 --password-stdin ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io <<< "$refresh_token"

    - name: Build the products api image and push it to ACR
    uses: docker/build-push-action@v2
    with:
    push: true
    tags: ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io/${{ env.PRODUCTS_IMAGE }}:${{ github.sha }}
    file: ${{ env.PRODUCTS_DOCKER }}

    - name: Build the inventory api image and push it to ACR
    uses: docker/build-push-action@v2
    with:
    push: true
    tags: ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io/${{ env.INVENTORY_IMAGE }}:${{ github.sha }}
    file: ${{ env.INVENTORY_DOCKER }}

    - name: Build the frontend image and push it to ACR
    uses: docker/build-push-action@v2
    with:
    push: true
    tags: ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io/${{ env.STORE_IMAGE }}:${{ github.sha }}
    file: ${{ env.STORE_DOCKER }}

    # Deploy the three container images
    deploy:
    runs-on: ubuntu-latest
    needs: build

    steps:

    - name: Checkout to the branch
    uses: actions/checkout@v2

    - name: Azure Login
    uses: azure/login@v1
    with:
    creds: ${{ secrets.AzureSPN }}

    - name: Installing Container Apps extension
    uses: azure/CLI@v1
    with:
    inlineScript: >
    az config set extension.use_dynamic_install=yes_without_prompt

    az extension add --name containerapp --yes

    - name: Login to ACR
    run: |
    set -euo pipefail
    access_token=$(az account get-access-token --query accessToken -o tsv)
    refresh_token=$(curl https://${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io/oauth2/exchange -v -d "grant_type=access_token&service=${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io&access_token=$access_token" | jq -r .refresh_token)
    docker login -u 00000000-0000-0000-0000-000000000000 --password-stdin ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io <<< "$refresh_token"

    - name: Deploy Container Apps
    uses: azure/CLI@v1
    with:
    inlineScript: >
    az containerapp registry set -n products -g ${{ env.RESOURCE_GROUP_NAME }} --server ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io

    az containerapp update -n products -g ${{ env.RESOURCE_GROUP_NAME }} -i ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io/${{ env.PRODUCTS_IMAGE }}:${{ github.sha }}

    az containerapp registry set -n inventory -g ${{ env.RESOURCE_GROUP_NAME }} --server ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io

    az containerapp update -n inventory -g ${{ env.RESOURCE_GROUP_NAME }} -i ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io/${{ env.INVENTORY_IMAGE }}:${{ github.sha }}

    az containerapp registry set -n store -g ${{ env.RESOURCE_GROUP_NAME }} --server ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io

    az containerapp update -n store -g ${{ env.RESOURCE_GROUP_NAME }} -i ${{ env.RESOURCE_GROUP_NAME }}acr.azurecr.io/${{ env.STORE_IMAGE }}:${{ github.sha }}

    - name: logout
    run: >
    az logout

    Understanding the Bicep templates

    During the provisioning stage of the GitHub Actions workflow, the main.bicep file is processed. Bicep files provide a declarative way of generating resources in Azure and are ideal for managing infrastructure as code. You can learn more about Bicep in the related documentation. The main.bicep file in the sample project creates the following resources:

    • The container registry to store images of the containerized apps.
    • The container apps environment, which handles networking and resource management for the container apps.
    • Three container apps - one for the Blazor front-end and two for the back-end product and inventory APIs.
    • Configuration values to connect these services together

    main.bicep without Dapr

    param location string = resourceGroup().location

    # create the azure container registry
    resource acr 'Microsoft.ContainerRegistry/registries@2021-09-01' = {
    name: toLower('${resourceGroup().name}acr')
    location: location
    sku: {
    name: 'Basic'
    }
    properties: {
    adminUserEnabled: true
    }
    }

    # create the aca environment
    module env 'environment.bicep' = {
    name: 'containerAppEnvironment'
    params: {
    location: location
    }
    }

    # create the various configuration pairs
    var shared_config = [
    {
    name: 'ASPNETCORE_ENVIRONMENT'
    value: 'Development'
    }
    {
    name: 'APPINSIGHTS_INSTRUMENTATIONKEY'
    value: env.outputs.appInsightsInstrumentationKey
    }
    {
    name: 'APPLICATIONINSIGHTS_CONNECTION_STRING'
    value: env.outputs.appInsightsConnectionString
    }
    ]

    # create the products api container app
    module products 'container_app.bicep' = {
    name: 'products'
    params: {
    name: 'products'
    location: location
    registryPassword: acr.listCredentials().passwords[0].value
    registryUsername: acr.listCredentials().username
    containerAppEnvironmentId: env.outputs.id
    registry: acr.name
    envVars: shared_config
    externalIngress: false
    }
    }

    # create the inventory api container app
    module inventory 'container_app.bicep' = {
    name: 'inventory'
    params: {
    name: 'inventory'
    location: location
    registryPassword: acr.listCredentials().passwords[0].value
    registryUsername: acr.listCredentials().username
    containerAppEnvironmentId: env.outputs.id
    registry: acr.name
    envVars: shared_config
    externalIngress: false
    }
    }

    # create the store api container app
    var frontend_config = [
    {
    name: 'ProductsApi'
    value: 'http://${products.outputs.fqdn}'
    }
    {
    name: 'InventoryApi'
    value: 'http://${inventory.outputs.fqdn}'
    }
    ]

    module store 'container_app.bicep' = {
    name: 'store'
    params: {
    name: 'store'
    location: location
    registryPassword: acr.listCredentials().passwords[0].value
    registryUsername: acr.listCredentials().username
    containerAppEnvironmentId: env.outputs.id
    registry: acr.name
    envVars: union(shared_config, frontend_config)
    externalIngress: true
    }
    }

    main.bicep with Dapr


    param location string = resourceGroup().location

    # create the azure container registry
    resource acr 'Microsoft.ContainerRegistry/registries@2021-09-01' = {
    name: toLower('${resourceGroup().name}acr')
    location: location
    sku: {
    name: 'Basic'
    }
    properties: {
    adminUserEnabled: true
    }
    }

    # create the aca environment
    module env 'environment.bicep' = {
    name: 'containerAppEnvironment'
    params: {
    location: location
    }
    }

    # create the various config pairs
    var shared_config = [
    {
    name: 'ASPNETCORE_ENVIRONMENT'
    value: 'Development'
    }
    {
    name: 'APPINSIGHTS_INSTRUMENTATIONKEY'
    value: env.outputs.appInsightsInstrumentationKey
    }
    {
    name: 'APPLICATIONINSIGHTS_CONNECTION_STRING'
    value: env.outputs.appInsightsConnectionString
    }
    ]

    # create the products api container app
    module products 'container_app.bicep' = {
    name: 'products'
    params: {
    name: 'products'
    location: location
    registryPassword: acr.listCredentials().passwords[0].value
    registryUsername: acr.listCredentials().username
    containerAppEnvironmentId: env.outputs.id
    registry: acr.name
    envVars: shared_config
    externalIngress: false
    }
    }

    # create the inventory api container app
    module inventory 'container_app.bicep' = {
    name: 'inventory'
    params: {
    name: 'inventory'
    location: location
    registryPassword: acr.listCredentials().passwords[0].value
    registryUsername: acr.listCredentials().username
    containerAppEnvironmentId: env.outputs.id
    registry: acr.name
    envVars: shared_config
    externalIngress: false
    }
    }

    # create the store api container app
    module store 'container_app.bicep' = {
    name: 'store'
    params: {
    name: 'store'
    location: location
    registryPassword: acr.listCredentials().passwords[0].value
    registryUsername: acr.listCredentials().username
    containerAppEnvironmentId: env.outputs.id
    registry: acr.name
    envVars: shared_config
    externalIngress: true
    }
    }


    Bicep Modules

    The main.bicep file references modules to create resources, such as module products. Modules are a feature of Bicep templates that enable you to abstract resource declarations into their own files or sub-templates. As the main.bicep file is processed, the defined modules are also evaluated. Modules allow you to create resources in a more organized and reusable way. They can also define input and output parameters that are passed to and from the parent template, such as the name of a resource.

    For example, the environment.bicep module extracts the details of creating a container apps environment into a reusable template. The module defines necessary resource dependencies such as Log Analytics Workspaces and an Application Insights instance.

    environment.bicep without Dapr

    param baseName string = resourceGroup().name
    param location string = resourceGroup().location

    resource logs 'Microsoft.OperationalInsights/workspaces@2021-06-01' = {
    name: '${baseName}logs'
    location: location
    properties: any({
    retentionInDays: 30
    features: {
    searchVersion: 1
    }
    sku: {
    name: 'PerGB2018'
    }
    })
    }

    resource appInsights 'Microsoft.Insights/components@2020-02-02' = {
    name: '${baseName}ai'
    location: location
    kind: 'web'
    properties: {
    Application_Type: 'web'
    WorkspaceResourceId: logs.id
    }
    }

    resource env 'Microsoft.App/managedEnvironments@2022-01-01-preview' = {
    name: '${baseName}env'
    location: location
    properties: {
    appLogsConfiguration: {
    destination: 'log-analytics'
    logAnalyticsConfiguration: {
    customerId: logs.properties.customerId
    sharedKey: logs.listKeys().primarySharedKey
    }
    }
    }
    }

    output id string = env.id
    output appInsightsInstrumentationKey string = appInsights.properties.InstrumentationKey
    output appInsightsConnectionString string = appInsights.properties.ConnectionString

    environment.bicep with Dapr


    param baseName string = resourceGroup().name
    param location string = resourceGroup().location

    resource logs 'Microsoft.OperationalInsights/workspaces@2021-06-01' = {
    name: '${baseName}logs'
    location: location
    properties: any({
    retentionInDays: 30
    features: {
    searchVersion: 1
    }
    sku: {
    name: 'PerGB2018'
    }
    })
    }

    resource appInsights 'Microsoft.Insights/components@2020-02-02' = {
    name: '${baseName}ai'
    location: location
    kind: 'web'
    properties: {
    Application_Type: 'web'
    WorkspaceResourceId: logs.id
    }
    }

    resource env 'Microsoft.App/managedEnvironments@2022-01-01-preview' = {
    name: '${baseName}env'
    location: location
    properties: {
    appLogsConfiguration: {
    destination: 'log-analytics'
    logAnalyticsConfiguration: {
    customerId: logs.properties.customerId
    sharedKey: logs.listKeys().primarySharedKey
    }
    }
    }
    }

    output id string = env.id
    output appInsightsInstrumentationKey string = appInsights.properties.InstrumentationKey
    output appInsightsConnectionString string = appInsights.properties.ConnectionString


    The container_apps.bicep template defines numerous parameters to provide a reusable template for creating container apps. This allows the module to be used in other CI/CD pipelines as well.

    container_app.bicep without Dapr

    param name string
    param location string = resourceGroup().location
    param containerAppEnvironmentId string
    param repositoryImage string = 'mcr.microsoft.com/azuredocs/containerapps-helloworld:latest'
    param envVars array = []
    param registry string
    param minReplicas int = 1
    param maxReplicas int = 1
    param port int = 80
    param externalIngress bool = false
    param allowInsecure bool = true
    param transport string = 'http'
    param registryUsername string
    @secure()
    param registryPassword string

    resource containerApp 'Microsoft.App/containerApps@2022-01-01-preview' ={
    name: name
    location: location
    properties:{
    managedEnvironmentId: containerAppEnvironmentId
    configuration: {
    activeRevisionsMode: 'single'
    secrets: [
    {
    name: 'container-registry-password'
    value: registryPassword
    }
    ]
    registries: [
    {
    server: registry
    username: registryUsername
    passwordSecretRef: 'container-registry-password'
    }
    ]
    ingress: {
    external: externalIngress
    targetPort: port
    transport: transport
    allowInsecure: allowInsecure
    }
    }
    template: {
    containers: [
    {
    image: repositoryImage
    name: name
    env: envVars
    }
    ]
    scale: {
    minReplicas: minReplicas
    maxReplicas: maxReplicas
    }
    }
    }
    }

    output fqdn string = containerApp.properties.configuration.ingress.fqdn

    container_app.bicep with Dapr


    param name string
    param location string = resourceGroup().location
    param containerAppEnvironmentId string
    param repositoryImage string = 'mcr.microsoft.com/azuredocs/containerapps-helloworld:latest'
    param envVars array = []
    param registry string
    param minReplicas int = 1
    param maxReplicas int = 1
    param port int = 80
    param externalIngress bool = false
    param allowInsecure bool = true
    param transport string = 'http'
    param appProtocol string = 'http'
    param registryUsername string
    @secure()
    param registryPassword string

    resource containerApp 'Microsoft.App/containerApps@2022-01-01-preview' ={
    name: name
    location: location
    properties:{
    managedEnvironmentId: containerAppEnvironmentId
    configuration: {
    dapr: {
    enabled: true
    appId: name
    appPort: port
    appProtocol: appProtocol
    }
    activeRevisionsMode: 'single'
    secrets: [
    {
    name: 'container-registry-password'
    value: registryPassword
    }
    ]
    registries: [
    {
    server: registry
    username: registryUsername
    passwordSecretRef: 'container-registry-password'
    }
    ]
    ingress: {
    external: externalIngress
    targetPort: port
    transport: transport
    allowInsecure: allowInsecure
    }
    }
    template: {
    containers: [
    {
    image: repositoryImage
    name: name
    env: envVars
    }
    ]
    scale: {
    minReplicas: minReplicas
    maxReplicas: maxReplicas
    }
    }
    }
    }

    output fqdn string = containerApp.properties.configuration.ingress.fqdn


    Understanding configuration differences with Dapr

    The code for this specific sample application is largely the same whether or not Dapr is integrated. However, even with this simple app, there are a few benefits and configuration differences when using Dapr that are worth exploring.

    In this scenario most of the changes are related to communication between the container apps. However, you can explore the full range of Dapr benefits by reading the Dapr integration with Azure Container Apps article in the conceptual documentation.

    Without Dapr

    Without Dapr the main.bicep template handles wiring up the front-end store app to communicate with the back-end apis by manually managing environment variables. The bicep template retrieves the fully qualified domains (fqdn) of the API apps as output parameters when they are created. Those configurations are then set as environment variables on the store container app.


    # Retrieve environment variables from API container creation
    var frontend_config = [
    {
    name: 'ProductsApi'
    value: 'http://${products.outputs.fqdn}'
    }
    {
    name: 'InventoryApi'
    value: 'http://${inventory.outputs.fqdn}'
    }
    ]

    # create the store api container app, passing in config
    module store 'container_app.bicep' = {
    name: 'store'
    params: {
    name: 'store'
    location: location
    registryPassword: acr.listCredentials().passwords[0].value
    registryUsername: acr.listCredentials().username
    containerAppEnvironmentId: env.outputs.id
    registry: acr.name
    envVars: union(shared_config, frontend_config)
    externalIngress: true
    }
    }

    The environment variables are then retrieved inside of the program class and used to configure the base URLs of the corresponding HTTP clients.


    builder.Services.AddHttpClient("Products", (httpClient) => httpClient.BaseAddress = new Uri(builder.Configuration.GetValue<string>("ProductsApi")));
    builder.Services.AddHttpClient("Inventory", (httpClient) => httpClient.BaseAddress = new Uri(builder.Configuration.GetValue<string>("InventoryApi")));

    With Dapr

    Dapr can be enabled on a container app when it is created, as seen below. This configuration adds a Dapr sidecar to the app to streamline discovery and communication features between the different container apps in your environment.


    # Create the container app with Dapr enabled
    resource containerApp 'Microsoft.App/containerApps@2022-01-01-preview' ={
    name: name
    location: location
    properties:{
    managedEnvironmentId: containerAppEnvironmentId
    configuration: {
    dapr: {
    enabled: true
    appId: name
    appPort: port
    appProtocol: appProtocol
    }
    activeRevisionsMode: 'single'
    secrets: [
    {
    name: 'container-registry-password'
    value: registryPassword
    }
    ]

    # Rest of template omitted for brevity...
    }
    }

    Some of these Dapr features can be surfaced through the program file. You can configure your HttpClient to leverage Dapr configurations when communicating with other apps in your environment.


    // reconfigure code to make requests to Dapr sidecar
    var baseURL = (Environment.GetEnvironmentVariable("BASE_URL") ?? "http://localhost") + ":" + (Environment.GetEnvironmentVariable("DAPR_HTTP_PORT") ?? "3500");
    builder.Services.AddHttpClient("Products", (httpClient) =>
    {
    httpClient.BaseAddress = new Uri(baseURL);
    httpClient.DefaultRequestHeaders.Add("dapr-app-id", "Products");
    });

    builder.Services.AddHttpClient("Inventory", (httpClient) =>
    {
    httpClient.BaseAddress = new Uri(baseURL);
    httpClient.DefaultRequestHeaders.Add("dapr-app-id", "Inventory");
    });


    Clean up resources

    If you're not going to continue to use this application, you can delete the Azure Container Apps and all the associated services by removing the resource group.

    Follow these steps in the Azure portal to remove the resources you created:

    1. In the Azure portal, navigate to the msdocswebappsapi resource group using the left navigation or search bar.
    2. Select the Delete resource group button at the top of the resource group Overview.
    3. Enter the resource group name msdocswebappsapi in the Are you sure you want to delete "msdocswebappsapi" confirmation dialog.
    4. Select Delete.
      The process to delete the resource group may take a few minutes to complete.
    - - + + \ No newline at end of file diff --git a/blog/tags/serverless-september/page/7/index.html b/blog/tags/serverless-september/page/7/index.html index a073e84756..3d5b19adbd 100644 --- a/blog/tags/serverless-september/page/7/index.html +++ b/blog/tags/serverless-september/page/7/index.html @@ -14,13 +14,13 @@ - - + +

    33 posts tagged with "serverless-september"

    View All Tags

    · 9 min read
    Justin Yoo

    Welcome to Day 21 of #30DaysOfServerless!

    We've so far walked through what Azure Event Grid is and how it generally works. Today, let's discuss how Azure Event Grid deals with CloudEvents.


    What We'll Cover


    OK. Let's get started!

    What is CloudEvents?

    Needless to say, events are everywhere. Events come not only from event-driven systems but also from many different systems and devices, including IoT ones like Raspberry PI.

    But the problem is that every event publisher (system/device that creates events) describes their events differently, meaning there is no standard way of describing events. It has caused many issues between systems, mainly from the interoperability perspective.

    1. Consistency: No standard way of describing events resulted in developers having to write their own event handling logic for each event source.
    2. Accessibility: There were no common libraries, tooling and infrastructure to deliver events across systems.
    3. Productivity: The overall productivity decreases because of the lack of the standard format of events.

    Cloud Events Logo

    Therefore, CNCF (Cloud-Native Computing Foundation) has brought up the concept, called CloudEvents. CloudEvents is a specification that commonly describes event data. Conforming any event data to this spec will simplify the event declaration and delivery across systems and platforms and more, resulting in a huge productivity increase.

    How Azure Event Grid brokers CloudEvents

    Before CloudEvents, Azure Event Grid described events in their own way. Therefore, if you want to use Azure Event Grid, you should follow the event format/schema that Azure Event Grid declares. However, not every system/service/application follows the Azure Event Grid schema. Therefore, Azure Event Grid now supports CloudEvents spec as input and output formats.

    Azure Event Grid for Azure

    Take a look at the simple diagram below, which describes how Azure Event Grid captures events raised from various Azure services. In this diagram, Azure Key Vault takes the role of the event source or event publisher, and Azure Logic Apps takes the role of the event handler (I'll discuss Azure Logic Apps as the event handler later in this post). We use Azure Event Grid System Topic for Azure.

    Azure Event Grid for Azure

    Therefore, let's create an Azure Event Grid System Topic that captures events raised from Azure Key Vault when a new version of a secret is added.

    Azure Event Grid System Topic for Key Vault

    As Azure Event Grid makes use of the pub/sub pattern, you need to create the Azure Event Grid Subscription to consume the events. Here's the subscription that uses the Event Grid data format:

    ![Azure Event Grid System Subscription for Key Vault in Event Grid Format][./img/21-cloudevents-via-event-grid-03.png]

    Once you create the subscription, create a new version of the secret on Azure Key Vault. Then, Azure Key Vault raises an event, which is captured in the Event Grid format:

    [
    {
    "id": "6f44b9c0-d37e-40e7-89be-f70a6da291cc",
    "topic": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/rg-aegce-krc/providers/Microsoft.KeyVault/vaults/kv-xxxxxxxx",
    "subject": "hello",
    "eventType": "Microsoft.KeyVault.SecretNewVersionCreated",
    "data": {
    "Id": "https://kv-xxxxxxxx.vault.azure.net/secrets/hello/064dfc082fec463f8d4610ed6118811d",
    "VaultName": "kv-xxxxxxxx",
    "ObjectType": "Secret",
    "ObjectName": "hello",
    "Version": "064dfc082fec463f8d4610ed6118811d",
    "NBF": null,
    "EXP": null
    },
    "dataVersion": "1",
    "metadataVersion": "1",
    "eventTime": "2022-09-21T07:08:09.1234567Z"
    }
    ]

    So, how is it different from the CloudEvents format? Let's take a look. According to the spec, the JSON data in CloudEvents might look like this:

    {
    "id" : "C234-1234-1234",
    "source" : "/mycontext",
    "specversion" : "1.0",
    "type" : "com.example.someevent",
    "comexampleextension1" : "value",
    "time" : "2018-04-05T17:31:00Z",
    "datacontenttype" : "application/cloudevents+json",
    "data" : {
    "appinfoA" : "abc",
    "appinfoB" : 123,
    "appinfoC" : true
    }
    }

    This time, let's create another subscription using the CloudEvents schema. Here's how to create the subscription against the system topic:

    Azure Event Grid System Subscription for Key Vault in CloudEvents Format

    Therefore, Azure Key Vault emits the event data in the CloudEvents format:

    {
    "id": "6f44b9c0-d37e-40e7-89be-f70a6da291cc",
    "source": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/rg-aegce-krc/providers/Microsoft.KeyVault/vaults/kv-xxxxxxxx",
    "specversion": "1.0",
    "type": "Microsoft.KeyVault.SecretNewVersionCreated",
    "subject": "hello",
    "time": "2022-09-21T07:08:09.1234567Z",
    "data": {
    "Id": "https://kv-xxxxxxxx.vault.azure.net/secrets/hello/064dfc082fec463f8d4610ed6118811d",
    "VaultName": "kv-xxxxxxxx",
    "ObjectType": "Secret",
    "ObjectName": "hello",
    "Version": "064dfc082fec463f8d4610ed6118811d",
    "NBF": null,
    "EXP": null
    }
    }

    Can you identify some differences between the Event Grid format and the CloudEvents format? Fortunately, both Event Grid schema and CloudEvents schema look similar to each other. But they might be significantly different if you use a different event source outside Azure.

    Azure Event Grid for Systems outside Azure

    As mentioned above, the event data described outside Azure or your own applications within Azure might not be understandable by Azure Event Grid. In this case, we need to use Azure Event Grid Custom Topic. Here's the diagram for it:

    Azure Event Grid for Applications outside Azure

    Let's create the Azure Event Grid Custom Topic. When you create the topic, make sure that you use the CloudEvent schema during the provisioning process:

    Azure Event Grid Custom Topic

    If your application needs to publish events to Azure Event Grid Custom Topic, your application should build the event data in the CloudEvents format. If you use a .NET application, add the NuGet package first.

    dotnet add package Azure.Messaging.EventGrid

    Then, create the publisher instance. You've already got the topic endpoint URL and the access key.

    var topicEndpoint = new Uri("<Azure Event Grid Custom Topic Endpoint URL>");
    var credential = new AzureKeyCredential("<Azure Event Grid Custom Topic Access Key>");
    var publisher = new EventGridPublisherClient(topicEndpoint, credential);

    Now, build the event data like below. Make sure that you follow the CloudEvents schema that requires additional metadata like event source, event type and content type.

    var source = "/your/event/source";
    var type = "com.source.event.your/OnEventOccurs";

    var data = new MyEventData() { Hello = "World" };

    var @event = new CloudEvent(source, type, data);

    And finally, send the event to Azure Event Grid Custom Topic.

    await publisher.SendEventAsync(@event);

    The captured event data looks like the following:

    {
    "id": "cc2b2775-52b8-43b8-a7cc-c1c33c2b2e59",
    "source": "/your/event/source",
    "type": "com.source.event.my/OnEventOccurs",
    "data": {
    "Hello": "World"
    },
    "time": "2022-09-21T07:08:09.1234567+00:00",
    "specversion": "1.0"
    }

    However, due to limitations, someone might insist that their existing application doesn't or can't emit the event data in the CloudEvents format. In this case, what should we do? There's no standard way of sending the event data in the CloudEvents format to Azure Event Grid Custom Topic. One of the approaches we may be able to apply is to put a converter between the existing application and Azure Event Grid Custom Topic like below:

    Azure Event Grid for Applications outside Azure with Converter

    Once the Function app (or any converter app) receives legacy event data, it internally converts the CloudEvents format and publishes it to Azure Event Grid.

    var data = default(MyRequestData);
    using (var reader = new StreamReader(req.Body))
    {
    var serialised = await reader.ReadToEndAsync();
    data = JsonConvert.DeserializeObject<MyRequestData>(serialised);
    }

    var converted = new MyEventData() { Hello = data.Lorem };
    var @event = new CloudEvent(source, type, converted);

    The converted event data is captured like this:

    {
    "id": "df296da3-77cd-4da2-8122-91f631941610",
    "source": "/your/event/source",
    "type": "com.source.event.my/OnEventOccurs",
    "data": {
    "Hello": "ipsum"
    },
    "time": "2022-09-21T07:08:09.1234567+00:00",
    "specversion": "1.0"
    }

    This approach is beneficial in many integration scenarios to make all the event data canonicalised.

    How Azure Logic Apps consumes CloudEvents

    I put Azure Logic Apps as the event handler in the previous diagrams. According to the CloudEvents spec, each event handler must implement request validation to avoid abuse. One good thing about using Azure Logic Apps is that it has already implemented this request validation feature. It implies that we just subscribe to the topic and consume the event data.

    Create a new Logic Apps instance and add the HTTP Request trigger. Once it saves, you will get the endpoint URL.

    Azure Logic Apps with HTTP Request Trigger

    Then, create the Azure Event Grid Subscription with:

    • Endpoint type: Webhook
    • Endpoint URL: The Logic Apps URL from above.

    Azure Logic Apps with HTTP Request Trigger

    Once the subscription is ready, this Logic Apps works well as the event handler. Here's how it receives the CloudEvents data from the subscription.

    Azure Logic Apps that Received CloudEvents data

    Now you've got the CloudEvents data. It's entirely up to you to handle that event data however you want!

    Exercise: Try this yourself!

    You can fork this GitHub repository to your account and play around with it to see how Azure Event Grid with CloudEvents works. Alternatively, the "Deploy to Azure" button below will provision all necessary Azure resources and deploy an Azure Functions app to mimic the event publisher.

    Deploy To Azure

    Resources: For self-study!

    Want to know more about CloudEvents in real-life examples? Here are several resources you can take a look at:

    - - + + \ No newline at end of file diff --git a/blog/tags/serverless-september/page/8/index.html b/blog/tags/serverless-september/page/8/index.html index c583c67b57..379e8b639d 100644 --- a/blog/tags/serverless-september/page/8/index.html +++ b/blog/tags/serverless-september/page/8/index.html @@ -14,15 +14,15 @@ - - + +

    33 posts tagged with "serverless-september"

    View All Tags

    · 10 min read
    Ayca Bas

    Welcome to Day 20 of #30DaysOfServerless!

    Every day millions of people spend their precious time in productivity tools. What if you use data and intelligence behind the Microsoft applications (Microsoft Teams, Outlook, and many other Office apps) to build seamless automations and custom apps to boost productivity?

    In this post, we'll learn how to build a seamless onboarding experience for new employees joining a company with the power of Microsoft Graph, integrated with Event Hubs and Logic Apps!


    What We'll Cover

    • ✨ The power of Microsoft Graph
    • 🖇️ How do Microsoft Graph and Event Hubs work together?
    • 🛠 Let's Build an Onboarding Workflow!
      • 1️⃣ Setup Azure Event Hubs + Key Vault
      • 2️⃣ Subscribe to users, receive change notifications from Logic Apps
      • 3️⃣ Create Onboarding workflow in the Logic Apps
    • 🚀 Debug: Your onboarding experience
    • ✋ Exercise: Try this tutorial out yourself!
    • 📚 Resources: For Self-Study


    ✨ The Power of Microsoft Graph

    Microsoft Graph is the gateway to data and intelligence in Microsoft 365 platform. Microsoft Graph exploses Rest APIs and client libraries to access data across Microsoft 365 core services such as Calendar, Teams, To Do, Outlook, People, Planner, OneDrive, OneNote and more.

    Overview of Microsoft Graph

    You can build custom experiences by using Microsoft Graph such as automating the onboarding process for new employees. When new employees are created in the Azure Active Directory, they will be automatically added in the Onboarding team on Microsoft Teams.

    Solution architecture


    🖇️ Microsoft Graph with Event Hubs

    Microsoft Graph uses a webhook mechanism to track changes in resources and deliver change notifications to the clients. For example, with Microsoft Graph Change Notifications, you can receive change notifications when:

    • a new task is added in the to-do list
    • a user changes the presence status from busy to available
    • an event is deleted/cancelled from the calendar

    If you'd like to track a large set of resources at a high frequency, use Azure Events Hubs instead of traditional webhooks to receive change notifications. Azure Event Hubs is a popular real-time events ingestion and distribution service built for scale.

    EVENT GRID - PARTNER EVENTS

    Microsoft Graph Change Notifications can be also received by using Azure Event Grid -- currently available for Microsoft Partners! Read the Partner Events Overview documentation for details.

    Setup Azure Event Hubs + Key Vault.

    To get Microsoft Graph Change Notifications delivered to Azure Event Hubs, we'll have to setup Azure Event Hubs and Azure Key Vault. We'll use Azure Key Vault to access to Event Hubs connection string.

    1️⃣ Create Azure Event Hubs

    1. Go to Azure Portal and select Create a resource, type Event Hubs and select click Create.
    2. Fill in the Event Hubs namespace creation details, and then click Create.
    3. Go to the newly created Event Hubs namespace page, select Event Hubs tab from the left pane and + Event Hub:
      • Name your Event Hub as Event Hub
      • Click Create.
    4. Click the name of the Event Hub, and then select Shared access policies and + Add to add a new policy:
      • Give a name to the policy
      • Check Send and Listen
      • Click Create.
    5. After the policy has been created, click the name of the policy to open the details panel, and then copy the Connection string-primary key value. Write it down; you'll need it for the next step.
    6. Go to Consumer groups tab in the left pane and select + Consumer group, give a name for your consumer group as onboarding and select Create.

    2️⃣ Create Azure Key Vault

    1. Go to Azure Portal and select Create a resource, type Key Vault and select Create.
    2. Fill in the Key Vault creation details, and then click Review + Create.
    3. Go to newly created Key Vault and select Secrets tab from the left pane and click + Generate/Import:
      • Give a name to the secret
      • For the value, paste in the connection string you generated at the Event Hubs step
      • Click Create
      • Copy the name of the secret.
    4. Select Access Policies from the left pane and + Add Access Policy:
      • For Secret permissions, select Get
      • For Principal, select Microsoft Graph Change Tracking
      • Click Add.
    5. Select Overview tab from the left pane and copy the Vault URI.

    Subscribe for Logic Apps change notifications

    To start receiving Microsoft Graph Change Notifications, we'll need to create subscription to the resource that we'd like to track - here, 'users'. We'll use Azure Logic Apps to create subscription.

    To create subscription for Microsoft Graph Change Notifications, we'll need to make a http post request to https://graph.microsoft.com/v1.0/subscriptions. Microsoft Graph requires Azure Active Directory authentication make API calls. First, we'll need to register an app to Azure Active Directory, and then we will make the Microsoft Graph Subscription API call with Azure Logic Apps.

    1️⃣ Create an app in Azure Active Directory

    1. In the Azure Portal, go to Azure Active Directory and select App registrations from the left pane and select + New registration. Fill in the details for the new App registration form as below:
      • Name: Graph Subscription Flow Auth
      • Supported account types: Accounts in any organizational directory (Any Azure AD directory - Multitenant) and personal Microsoft accounts (e.g. Skype, Xbox)
      • Select Register.
    2. Go to newly registered app in Azure Active Directory, select API permissions:
      • Select + Add a permission and Microsoft Graph
      • Select Application permissions and add User.Read.All and Directory.Read.All.
      • Select Grant admin consent for the organization
    3. Select Certificates & secrets tab from the left pane, select + New client secret:
      • Choose desired expiry duration
      • Select Add
      • Copy the value of the secret.
    4. Go to Overview from the left pane, copy Application (client) ID and Directory (tenant) ID.

    2️⃣ Create subscription with Azure Logic Apps

    1. Go to Azure Portal and select Create a resource, type Logic apps and select click Create.

    2. Fill in the Logic Apps creation details, and then click Create.

    3. Go to the newly created Logic Apps page, select Workflows tab from the left pane and select + Add:

      • Give a name to the new workflow as graph-subscription-flow
      • Select Stateful as a state type
      • Click Create.
    4. Go to graph-subscription-flow, and then select Designer tab.

    5. In the Choose an operation section, search for Schedule and select Recurrence as a trigger. Fill in the parameters as below:

      • Interval: 61
      • Frequency: Minute
      • Time zone: Select your own time zone
      • Start time: Set a start time
    6. Select + button in the flow and select add an action. Search for HTTP and select HTTP as an action. Fill in the parameters as below:

      • Method: POST
      • URI: https://graph.microsoft.com/v1.0/subscriptions
      • Headers:
        • Key: Content-type
        • Value: application/json
      • Body:
      {
      "changeType": "created, updated",
      "clientState": "secretClientValue",
      "expirationDateTime": "@{addHours(utcNow(), 1)}",
      "notificationUrl": "EventHub:https://<YOUR-VAULT-URI>/secrets/<YOUR-KEY-VAULT-SECRET-NAME>?tenantId=72f988bf-86f1-41af-91ab-2d7cd011db47",
      "resource": "users"
      }

      In notificationUrl, make sure to replace <YOUR-VAULT-URI> with the vault uri and <YOUR-KEY-VAULT-SECRET-NAME> with the secret name that you copied from the Key Vault.

      In resource, define the resource type you'd like to track changes. For our example, we will track changes for users resource.

      • Authentication:
        • Authentication type: Active Directory OAuth
        • Authority: https://login.microsoft.com
        • Tenant: Directory (tenant) ID copied from AAD app
        • Audience: https://graph.microsoft.com
        • Client ID: Application (client) ID copied from AAD app
        • Credential Type: Secret
        • Secret: value of the secret copied from AAD app
    7. Select Save and run your workflow from the Overview tab.

      Check your subscription in Graph Explorer: If you'd like to make sure that your subscription is created successfully by Logic Apps, you can go to Graph Explorer, login with your Microsoft 365 account and make GET request to https://graph.microsoft.com/v1.0/subscriptions. Your subscription should appear in the response after it's created successfully.

    Subscription workflow success

    After subscription is created successfully by Logic Apps, Azure Event Hubs will receive notifications whenever there is a new user created in Azure Active Directory.


    Create Onboarding workflow in Logic Apps

    We'll create a second workflow in the Logic Apps to receive change notifications from Event Hubs when there is a new user created in the Azure Active Directory and add new user in Onboarding team on Microsoft Teams.

    1. Go to the Logic Apps you created in the previous steps, select Workflows tab and create a new workflow by selecting + Add:
      • Give a name to the new workflow as teams-onboarding-flow
      • Select Stateful as a state type
      • Click Create.
    2. Go to teams-onboarding-flow, and then select Designer tab.
    3. In the Choose an operation section, search for Event Hub, select When events are available in Event Hub as a trigger. Setup Event Hub connection as below:
      • Create Connection:
        • Connection name: Connection
        • Authentication Type: Connection String
        • Connection String: Go to Event Hubs > Shared Access Policies > RootManageSharedAccessKey and copy Connection string–primary key
        • Select Create.
      • Parameters:
        • Event Hub Name: Event Hub
        • Consumer Group Name: onboarding
    4. Select + in the flow and add an action, search for Control and add For each as an action. Fill in For each action as below:
      • Select output from previous steps: Events
    5. Inside For each, select + in the flow and add an action, search for Data operations and select Parse JSON. Fill in Parse JSON action as below:
      • Content: Events Content
      • Schema: Copy the json content from schema-parse.json and paste as a schema
    6. Select + in the flow and add an action, search for Control and add For each as an action. Fill in For each action as below:
      • Select output from previous steps: value
      1. Inside For each, select + in the flow and add an action, search for Microsoft Teams and select Add a member to a team. Login with your Microsoft 365 account to create a connection and fill in Add a member to a team action as below:
      • Team: Create an Onboarding team on Microsoft Teams and select
      • A user AAD ID for the user to add to a team: id
    7. Select Save.

    🚀 Debug your onboarding experience

    To debug our onboarding experience, we'll need to create a new user in Azure Active Directory and see if it's added in Microsoft Teams Onboarding team automatically.

    1. Go to Azure Portal and select Azure Active Directory from the left pane and go to Users. Select + New user and Create new user. Fill in the details as below:

      • User name: JaneDoe
      • Name: Jane Doe

      new user in Azure Active Directory

    2. When you added Jane Doe as a new user, it should trigger the teams-onboarding-flow to run. teams onboarding flow success

    3. Once the teams-onboarding-flow runs successfully, you should be able to see Jane Doe as a member of the Onboarding team on Microsoft Teams! 🥳 new member in Onboarding team on Microsoft Teams

    Congratulations! 🎉

    You just built an onboarding experience using Azure Logic Apps, Azure Event Hubs and Azure Key Vault.


    📚 Resources

    - - + + \ No newline at end of file diff --git a/blog/tags/serverless-september/page/9/index.html b/blog/tags/serverless-september/page/9/index.html index e3966d92dd..3313fed755 100644 --- a/blog/tags/serverless-september/page/9/index.html +++ b/blog/tags/serverless-september/page/9/index.html @@ -14,13 +14,13 @@ - - + +

    33 posts tagged with "serverless-september"

    View All Tags

    · 6 min read
    Ramya Oruganti

    Welcome to Day 19 of #30DaysOfServerless!

    Today, we have a special set of posts from our Zero To Hero 🚀 initiative, featuring blog posts authored by our Product Engineering teams for #ServerlessSeptember. Posts were originally published on the Apps on Azure blog on Microsoft Tech Community.


    What We'll Cover

    • Retry Policy Support - in Apache Kafka Extension
    • AutoOffsetReset property - in Apache Kafka Extension
    • Key support for Kafka messages - in Apache Kafka Extension
    • References: Apache Kafka Extension for Azure Functions


    Recently we launched the Apache Kafka extension for Azure functions in GA with some cool new features like deserialization of Avro Generic records and Kafka headers support. We received great responses - so we're back with more updates!

    Retry Policy support

    Handling errors in Azure Functions is important to avoid data loss or miss events or monitor the health of an application. Apache Kafka Extension for Azure Functions supports retry policy which tells the runtime to rerun a failed execution until either successful completion occurs or the maximum number of retries is reached.

    A retry policy is evaluated when a trigger function raises an uncaught exception. As a best practice, you should catch all exceptions in your code and rethrow any errors that you want to result in a retry.

    There are two retry strategies supported by policy that you can configure :- fixed delay and exponential backoff

    1. Fixed Delay - A specified amount of time is allowed to elapse between each retry.
    2. Exponential Backoff - The first retry waits for the minimum delay. On subsequent retries, time is added exponentially to the initial duration for each retry, until the maximum delay is reached. Exponential back-off adds some small randomization to delays to stagger retries in high-throughput scenarios.
    Please Note

    Retry Policy for Kafka extension is NOT supported for C# (in proc and out proc) trigger and output binding. This is supported for languages like Java, Node (JS , TypeScript), PowerShell and Python trigger and output bindings.

    Here is the sample code view of exponential backoff retry strategy

    Error Handling with Apache Kafka extension for Azure Functions

    AutoOffsetReset property

    AutoOffsetReset property enables customers to configure the behaviour in the absence of an initial offset. Imagine a scenario when there is a need to change consumer group name. The consumer connected using a new consumer group had to reprocess all events starting from the oldest (earliest) one, as this was the default one and this setting wasn’t exposed as configurable option in the Apache Kafka extension for Azure Functions(previously). With the help of this kafka setting you can configure on how to start processing events for newly created consumer groups.

    Due to lack of the ability to configure this setting, offset commit errors were causing topics to restart from earliest offset· Users were looking to be able to set offset setting to either latest or earliest based on their requirements.

    We are happy to share that we have enabled the AutoOffsetReset setting as a configurable one to either - Earliest(Default) and Latest. Setting the value to Earliest configures the consumption of the messages from the the earliest/smallest offset or beginning of the topic partition. Setting the property to Latest configures the consumption of the messages from the latest/largest offset or from the end of the topic partition. This is supported for all the Azure Functions supported languages (C# (in & out), Java, Node (JS and TypeScript), PowerShell and python) and can be used for both triggers and output binding

    Error Handling with Apache Kafka extension for Azure Functions

    Key support for Kafka messages

    With keys the producer/output binding can be mapped to broker and partition to write based on the message. So alongside the message value, we can choose to send a message key and that key can be whatever you want it could be a string, it could be a number . In case you don’t send the key, the key is set to null then the data will be sent in a Round Robin fashion to make it very simple. But in case you send a key with your message, all the messages that share the same key will always go to the same partition and thus you can enable grouping of similar messages into partitions

    Previously while consuming a Kafka event message using the Azure Function kafka extension, the event key was always none although the key was present in the event message.

    Key support was implemented in the extension which enables customers to set/view key in the Kafka event messages coming in to the kafka trigger and set keys to the messages going in to kafka topics (with keys set) through output binding. Therefore key support was enabled in the extension to support both trigger and output binding for all Azure Functions supported languages ( (C# (in & out), Java, Node (JS and TypeScript), PowerShell and python)

    Here is the view of an output binding producer code where Kafka messages are being set with key

    Error Handling with Apache Kafka extension for Azure Functions

    Conclusion:

    In this article you have learnt about the latest additions to the Apache Kafka extension for Azure Functions. Incase you have been waiting for these features to get released or need them you are all set and please go head and try them out!! They are available in the latest extension bundles

    Want to learn more?

    Please refer to Apache Kafka bindings for Azure Functions | Microsoft Docs for detail documentation, samples on the Azure function supported languages and more!

    References

    FEEDBACK WELCOME

    Keep in touch with us on Twitter via @AzureFunctions.

    - - + + \ No newline at end of file diff --git a/blog/tags/serverless/index.html b/blog/tags/serverless/index.html index b29a3a9429..6bf8a60fe3 100644 --- a/blog/tags/serverless/index.html +++ b/blog/tags/serverless/index.html @@ -14,13 +14,13 @@ - - + +

    One post tagged with "serverless"

    View All Tags

    · 8 min read
    Rory Preddy

    Welcome to Day 4 of #30DaysOfServerless!

    Yesterday we walked through an Azure Functions Quickstart with JavaScript, and used it to understand the general Functions App structure, tooling and developer experience.

    Today we'll look at developing Functions app with a different programming language - namely, Java - and explore developer guidance, tools and resources to build serverless Java solutions on Azure.


    What We'll Cover


    Developer Guidance

    If you're a Java developer new to serverless on Azure, start by exploring the Azure Functions Java Developer Guide. It covers:

    In this blog post, we'll dive into one quickstart, and discuss other resources briefly, for awareness! Do check out the recommended exercises and resources for self-study!


    My First Java Functions App

    In today's post, we'll walk through the Quickstart: Azure Functions tutorial using Visual Studio Code. In the process, we'll setup our development environment with the relevant command-line tools and VS Code extensions to make building Functions app simpler.

    Note: Completing this exercise may incur a a cost of a few USD cents based on your Azure subscription. Explore pricing details to learn more.

    First, make sure you have your development environment setup and configured.

    PRE-REQUISITES
    1. An Azure account with an active subscription - Create an account for free
    2. The Java Development Kit, version 11 or 8. - Install
    3. Apache Maven, version 3.0 or above. - Install
    4. Visual Studio Code. - Install
    5. The Java extension pack - Install
    6. The Azure Functions extension for Visual Studio Code - Install

    VS Code Setup

    NEW TO VISUAL STUDIO CODE?

    Start with the Java in Visual Studio Code tutorial to jumpstart your learning!

    Install the Extension Pack for Java (shown below) to install 6 popular extensions to help development workflow from creation to testing, debugging, and deployment.

    Extension Pack for Java

    Now, it's time to get started on our first Java-based Functions app.

    1. Create App

    1. Open a command-line terminal and create a folder for your project. Use the code command to launch Visual Studio Code from that directory as shown:

      $ mkdir java-function-resource-group-api
      $ cd java-function-resource-group-api
      $ code .
    2. Open the Visual Studio Command Palette (Ctrl + Shift + p) and select Azure Functions: create new project to kickstart the create workflow. Alternatively, you can click the Azure icon (on activity sidebar), to get the Workspace window, click "+" and pick the "Create Function" option as shown below.

      Screenshot of creating function in Azure from Visual Studio Code.

    3. This triggers a multi-step workflow. Fill in the information for each step as shown in the following prompts. Important: Start this process from an empty folder - the workflow will populate it with the scaffold for your Java-based Functions app.

      PromptValue
      Choose the directory location.You should either create a new folder or choose an empty folder for the project workspace. Don't choose a project folder that is already part of a workspace.
      Select a languageChoose Java.
      Select a version of JavaChoose Java 11 or Java 8, the Java version on which your functions run in Azure. Choose a Java version that you've verified locally.
      Provide a group IDChoose com.function.
      Provide an artifact IDEnter myFunction.
      Provide a versionChoose 1.0-SNAPSHOT.
      Provide a package nameChoose com.function.
      Provide an app nameEnter HttpExample.
      Select the build tool for Java projectChoose Maven.

    Visual Studio Code uses the provided information and generates an Azure Functions project. You can view the local project files in the Explorer - it should look like this:

    Azure Functions Scaffold For Java

    2. Preview App

    Visual Studio Code integrates with the Azure Functions Core tools to let you run this project on your local development computer before you publish to Azure.

    1. To build and run the application, use the following Maven command. You should see output similar to that shown below.

      $ mvn clean package azure-functions:run
      ..
      ..
      Now listening on: http://0.0.0.0:7071
      Application started. Press Ctrl+C to shut down.

      Http Functions:

      HttpExample: [GET,POST] http://localhost:7071/api/HttpExample
      ...
    2. Copy the URL of your HttpExample function from this output to a browser and append the query string ?name=<YOUR_NAME>, making the full URL something like http://localhost:7071/api/HttpExample?name=Functions. The browser should display a message that echoes back your query string value. The terminal in which you started your project also shows log output as you make requests.

    🎉 CONGRATULATIONS

    You created and ran a function app locally!

    With the Terminal panel focused, press Ctrl + C to stop Core Tools and disconnect the debugger. After you've verified that the function runs correctly on your local computer, it's time to use Visual Studio Code and Maven to publish and test the project on Azure.

    3. Sign into Azure

    Before you can deploy, sign in to your Azure subscription.

    az login

    The az login command signs you into your Azure account.

    Use the following command to deploy your project to a new function app.

    mvn clean package azure-functions:deploy

    When the creation is complete, the following Azure resources are created in your subscription:

    • Resource group. Named as java-functions-group.
    • Storage account. Required by Functions. The name is generated randomly based on Storage account name requirements.
    • Hosting plan. Serverless hosting for your function app.The name is java-functions-app-service-plan.
    • Function app. A function app is the deployment and execution unit for your functions. The name is randomly generated based on your artifactId, appended with a randomly generated number.

    4. Deploy App

    1. Back in the Resources area in the side bar, expand your subscription, your new function app, and Functions. Right-click (Windows) or Ctrl - click (macOS) the HttpExample function and choose Execute Function Now....

      Screenshot of executing function in Azure from Visual Studio Code.

    2. In Enter request body you see the request message body value of { "name": "Azure" }. Press Enter to send this request message to your function.

    3. When the function executes in Azure and returns a response, a notification is raised in Visual Studio Code.

    You can also copy the complete Invoke URL shown in the output of the publish command into a browser address bar, appending the query parameter ?name=Functions. The browser should display similar output as when you ran the function locally.

    🎉 CONGRATULATIONS

    You deployed your function app to Azure, and invoked it!

    5. Clean up

    Use the following command to delete the resource group and all its contained resources to avoid incurring further costs.

    az group delete --name java-functions-group

    Next Steps

    So, where can you go from here? The example above used a familiar HTTP Trigger scenario with a single Azure service (Azure Functions). Now, think about how you can build richer workflows by using other triggers and integrating with other Azure or third-party services.

    Other Triggers, Bindings

    Check out Azure Functions Samples In Java for samples (and short use cases) that highlight other triggers - with code! This includes triggers to integrate with CosmosDB, Blob Storage, Event Grid, Event Hub, Kafka and more.

    Scenario with Integrations

    Once you've tried out the samples, try building an end-to-end scenario by using these triggers to integrate seamlessly with other Services. Here are a couple of useful tutorials:

    Exercise

    Time to put this into action and validate your development workflow:

    Resources

    - - + + \ No newline at end of file diff --git a/blog/tags/students/index.html b/blog/tags/students/index.html index 3d318bcf1e..1526f5be8b 100644 --- a/blog/tags/students/index.html +++ b/blog/tags/students/index.html @@ -14,13 +14,13 @@ - - + +

    One post tagged with "students"

    View All Tags

    · 3 min read
    Sara Gibbons

    ✨ Serverless September For Students

    My love for the tech industry grows as it evolves. Not just for the new technologies to play with, but seeing how paths into a tech career continue to expand. Allowing so many new voices, ideas and perspectives to our industry. With serverless computing removing barriers of entry for so many.

    It's a reason I enjoy working with universities and students. I get to hear the excitement of learning, fresh ideas and perspectives from our student community. All you students are incredible! How you view serverless, and what it can do, so cool!

    This year for Serverless September we want to hear all the amazing ways our student community is learning and working with Azure Serverless, and have all new ways for you to participate.

    Getting Started

    If you don't already have an Azure for Students account you can easily get your FREE account created at Azure for Students Sign up.

    If you are new to serverless, here are a couple links to get you started:

    No Experience, No problem

    For Serverless September we have planned beginner friendly content all month long. Covering such services as:

    You can follow #30DaysOfServerles here on the blog for daily posts covering concepts, scenarios, and how to create end-to-end solutions.

    Join the Cloud Skills Challenge where we have selected a list of Learn Modules for you to go through at your own pace, including deploying a full stack application with Azure Static Web Apps.

    Have A Question

    We want to hear it! All month long we will have Ask The Expert sessions. Submit your questions at any time and will be be sure to get one of our Azure Serverless experts to get you an answer.

    Share What You've Created

    If you have written a blog post, recorded a video, have an open source Azure Serverless project, we'd love to see it! Here is some links for you to share your creations

    🧭 Explore Student Resources

    ⚡️ Join us!

    Multiple teams across Microsoft are working to create Serverless September! They all want to hear from our incredible student community. We can't wait to share all the Serverless September resources and hear what you have learned and created. Here are some ways to keep up to date on all Serverless September activity:

    - - + + \ No newline at end of file diff --git a/blog/tags/vscode/index.html b/blog/tags/vscode/index.html index 2a1d5dc87c..c18cc00d6a 100644 --- a/blog/tags/vscode/index.html +++ b/blog/tags/vscode/index.html @@ -14,13 +14,13 @@ - - + +

    One post tagged with "vscode"

    View All Tags

    · 9 min read
    Nitya Narasimhan

    Welcome to Day 3 of #30DaysOfServerless!

    Yesterday we learned core concepts and terminology for Azure Functions, the signature Functions-as-a-Service option on Azure. Today we take our first steps into building and deploying an Azure Functions app, and validate local development setup.

    Ready? Let's go.


    What We'll Cover


    Developer Guidance

    Before we jump into development, let's familiarize ourselves with language-specific guidance from the Azure Functions Developer Guide. We'll review the JavaScript version but guides for F#, Java, Python, C# and PowerShell are also available.

    1. A function is defined by two things: code (written in a supported programming language) and configuration (specified in a functions.json file, declaring the triggers, bindings and other context for execution).

    2. A function app is the unit of deployment for your functions, and is associated with a single execution context or runtime. It can contain multiple functions, but they must be in the same language.

    3. A host configuration is runtime-specific configuration that affects all functions running in a given function app instance. It is defined in a host.json file.

    4. A recommended folder structure is defined for the function app, but may vary based on the programming language used. Check the documentation on folder structures to learn the default for your preferred language.

    Here's an example of the JavaScript folder structure for a function app containing two functions with some shared dependencies. Note that host.json (runtime configuration) is defined once, in the root directory. And function.json is defined separately for each function.

    FunctionsProject
    | - MyFirstFunction
    | | - index.js
    | | - function.json
    | - MySecondFunction
    | | - index.js
    | | - function.json
    | - SharedCode
    | | - myFirstHelperFunction.js
    | | - mySecondHelperFunction.js
    | - node_modules
    | - host.json
    | - package.json
    | - local.settings.json

    We'll dive into what the contents of these files look like, when we build and deploy the first function. We'll cover local.settings.json in the About Local Testing section at the end.


    My First Function App

    The documentation provides quickstart options for all supported languages. We'll walk through the JavaScript versions in this article. You have two options for development:

    I'm a huge fan of VS Code - so I'll be working through that tutorial today.

    PRE-REQUISITES

    Don't forget to validate your setup by checking the versions of installed software.

    Install VSCode Extension

    Installing the Visual Studio Code extension should automatically open this page in your IDE with similar quickstart instructions, but potentially more recent screenshots.

    Visual Studio Code Extension for VS Code

    Note that it may make sense to install the Azure tools for Visual Studio Code extensions pack if you plan on working through the many projects in Serverless September. This includes the Azure Functions extension by default.

    Create First Function App

    Walk through the Create local [project] steps of the quickstart. The process is quick and painless and scaffolds out this folder structure and files. Note the existence (and locations) of functions.json and host.json files.

    Final screenshot for VS Code workflow

    Explore the Code

    Check out the functions.json configuration file. It shows that the function is activated by an httpTrigger with an input binding (tied to req payload) and an output binding (tied to res payload). And it supports both GET and POST requests on the exposed URL.

    {
    "bindings": [
    {
    "authLevel": "anonymous",
    "type": "httpTrigger",
    "direction": "in",
    "name": "req",
    "methods": [
    "get",
    "post"
    ]
    },
    {
    "type": "http",
    "direction": "out",
    "name": "res"
    }
    ]
    }

    Check out index.js - the function implementation. We see it logs a message to the console when invoked. It then extracts a name value from the input payload (req) and crafts a different responseMessage based on the presence/absence of a valid name. It returns this response in the output payload (res).

    module.exports = async function (context, req) {
    context.log('JavaScript HTTP trigger function processed a request.');

    const name = (req.query.name || (req.body && req.body.name));
    const responseMessage = name
    ? "Hello, " + name + ". This HTTP triggered function executed successfully."
    : "This HTTP triggered function executed successfully. Pass a name in the query string or in the request body for a personalized response.";

    context.res = {
    // status: 200, /* Defaults to 200 */
    body: responseMessage
    };
    }

    Preview Function App Locally

    You can now run this function app locally using Azure Functions Core Tools. VS Code integrates seamlessly with this CLI-based tool, making it possible for you to exploit all its capabilities without leaving the IDE. In fact, the workflow will even prompt you to install those tools if they didn't already exist in your local dev environment.

    Now run the function app locally by clicking on the "Run and Debug" icon in the activity bar (highlighted, left) and pressing the "▶️" (Attach to Node Functions) to start execution. On success, your console output should show something like this.

    Final screenshot for VS Code workflow

    You can test the function locally by visiting the Function Url shown (http://localhost:7071/api/HttpTrigger1) or by opening the Workspace region of the Azure extension, and selecting the Execute Function now menu item as shown.

    Final screenshot for VS Code workflow

    In the latter case, the Enter request body popup will show a pre-populated request of {"name":"Azure"} that you can submit.

    Final screenshot for VS Code workflow

    On successful execution, your VS Code window will show a notification as follows. Take note of the console output - it shows the message encoded in index.js.

    Final screenshot for VS Code workflow

    You can also visit the deployed function URL directly in a local browser - testing the case for a request made with no name payload attached. Note how the response in the browser now shows the non-personalized version of the message!

    Final screenshot for VS Code workflow

    🎉 Congratulations

    You created and ran a function app locally!

    (Re)Deploy to Azure

    Now, just follow the creating a function app in Azure steps to deploy it to Azure, using an active subscription! The deployed app resource should now show up under the Function App Resources where you can click Execute Function Now to test the Azure-deployed version instead. You can also look up the function URL in the portal and visit that link in your local browser to trigger the function without the name context.

    🎉 Congratulations

    You have an Azure-hosted serverless function app!

    Challenge yourself and try to change the code and redeploy to Azure to return something different. You have effectively created a serverless API endpoint!


    About Core Tools

    That was a lot to cover! In the next few days we'll have more examples for Azure Functions app development - focused on different programming languages. So let's wrap today's post by reviewing two helpful resources.

    First, let's talk about Azure Functions Core Tools - the command-line tool that lets you develop, manage, and deploy, Azure Functions projects from your local development environment. It is used transparently by the VS Code extension - but you can use it directly from a terminal for a powerful command-line end-to-end developer experience! The Core Tools commands are organized into the following contexts:

    Learn how to work with Azure Functions Core Tools. Not only can it help with quick command execution, it can also be invaluable for debugging issues that may not always be visible or understandable in an IDE.

    About Local Testing

    You might have noticed that the scaffold also produced a local.settings.json file. What is that and why is it useful? By definition, the local.settings.json file "stores app settings and settings used by local development tools. Settings in the local.settings.json file are used only when you're running your project locally."

    Read the guidance on Code and test Azure Functions Locally to learn more about how to configure development environments locally, for your preferred programming language, to support testing and debugging on the local Functions runtime.

    Exercise

    We made it! Now it's your turn!! Here are a few things you can try to apply what you learned and reinforce your understanding:

    Resources

    Bookmark and visit the #30DaysOfServerless Collection. It's the one-stop collection of resources we will keep updated with links to relevant documentation and learning resources.

    - - + + \ No newline at end of file diff --git a/blog/tags/zero-to-hero/index.html b/blog/tags/zero-to-hero/index.html index c49b0dfed5..dce9f91e8a 100644 --- a/blog/tags/zero-to-hero/index.html +++ b/blog/tags/zero-to-hero/index.html @@ -14,14 +14,14 @@ - - + +

    8 posts tagged with "zero-to-hero"

    View All Tags

    · 5 min read
    Madhura Bharadwaj

    Welcome to Day 26 of #30DaysOfServerless!

    Today, we have a special set of posts from our Zero To Hero 🚀 initiative, featuring blog posts authored by our Product Engineering teams for #ServerlessSeptember. Posts were originally published on the Apps on Azure blog on Microsoft Tech Community.


    What We'll Cover

    • Monitoring your Azure Functions
    • Built-in log streaming
    • Live Metrics stream
    • Troubleshooting Azure Functions


    Monitoring your Azure Functions:

    Azure Functions uses Application Insights to collect and analyze log data from individual function executions in your function app.

    Using Application Insights

    Application Insights collects log, performance, and error data. By automatically detecting performance anomalies and featuring powerful analytics tools, you can more easily diagnose issues and better understand how your functions are used. These tools are designed to help you continuously improve performance and usability of your functions. You can even use Application Insights during local function app project development.

    Typically, you create an Application Insights instance when you create your function app. In this case, the instrumentation key required for the integration is already set as an application setting named APPINSIGHTS_INSTRUMENTATIONKEY. With Application Insights integration enabled, telemetry data is sent to your connected Application Insights instance. This data includes logs generated by the Functions host, traces written from your functions code, and performance data. In addition to data from your functions and the Functions host, you can also collect data from the Functions scale controller.

    By default, the data collected from your function app is stored in Application Insights. In the Azure portal, Application Insights provides an extensive set of visualizations of your telemetry data. You can drill into error logs and query events and metrics. To learn more, including basic examples of how to view and query your collected data, see Analyze Azure Functions telemetry in Application Insights.

    Using Log Streaming

    In addition to this, you can have a smoother debugging experience through log streaming. There are two ways to view a stream of log files being generated by your function executions.

    • Built-in log streaming: the App Service platform lets you view a stream of your application log files. This is equivalent to the output seen when you debug your functions during local development and when you use the Test tab in the portal. All log-based information is displayed. For more information, see Stream logs. This streaming method supports only a single instance and can't be used with an app running on Linux in a Consumption plan.
    • Live Metrics Stream: when your function app is connected to Application Insights, you can view log data and other metrics in near real-time in the Azure portal using Live Metrics Stream. Use this method when monitoring functions running on multiple-instances or on Linux in a Consumption plan. This method uses sampled data. Log streams can be viewed both in the portal and in most local development environments.
    Monitoring Azure Functions

    Learn how to configure monitoring for your Azure Functions. See Monitoring Azure Functions data reference for detailed information on the metrics and logs metrics created by Azure Functions.

    In addition to this, Azure Functions uses Azure Monitor to monitor the health of your function apps. Azure Functions collects the same kinds of monitoring data as other Azure resources that are described in Azure Monitor data collection. See Monitoring Azure Functions data reference for detailed information on the metrics and logs metrics created by Azure Functions.

    Troubleshooting your Azure Functions:

    When you do run into issues with your function app, Azure Functions diagnostics points out what’s wrong. It guides you to the right information to troubleshoot and resolve the issue more easily and quickly.

    Let’s explore how to use Azure Functions diagnostics to diagnose and solve common function app issues.

    1. Navigate to your function app in the Azure portal.
    2. Select Diagnose and solve problems to open Azure Functions diagnostics.
    3. Once you’re here, there are multiple ways to retrieve the information you’re looking for. Choose a category that best describes the issue of your function app by using the keywords in the homepage tile. You can also type a keyword that best describes your issue in the search bar. There’s also a section at the bottom of the page that will directly take you to some of the more popular troubleshooting tools. For example, you could type execution to see a list of diagnostic reports related to your function app execution and open them directly from the homepage.

    Monitoring and troubleshooting apps in Azure Functions

    1. For example, click on the Function App Down or Reporting Errors link under Popular troubleshooting tools section. You will find detailed analysis, insights and next steps for the issues that were detected. On the left you’ll see a list of detectors. Click on them to explore more, or if there’s a particular keyword you want to look for, type it Into the search bar on the top.

    Monitoring and troubleshooting apps in Azure Functions

    TROUBLESHOOTING TIP

    Here are some general troubleshooting tips that you can follow if you find your Function App throwing Azure Functions Runtime unreachable error.

    Also be sure to check out the recommended best practices to ensure your Azure Functions are highly reliable. This article details some best practices for designing and deploying efficient function apps that remain healthy and perform well in a cloud-based environment.

    Bonus tip:

    - - + + \ No newline at end of file diff --git a/blog/tags/zero-to-hero/page/2/index.html b/blog/tags/zero-to-hero/page/2/index.html index d61ad9b7c4..c15c0d3127 100644 --- a/blog/tags/zero-to-hero/page/2/index.html +++ b/blog/tags/zero-to-hero/page/2/index.html @@ -14,13 +14,13 @@ - - + +

    8 posts tagged with "zero-to-hero"

    View All Tags

    · 6 min read
    Ramya Oruganti

    Welcome to Day 19 of #30DaysOfServerless!

    Today, we have a special set of posts from our Zero To Hero 🚀 initiative, featuring blog posts authored by our Product Engineering teams for #ServerlessSeptember. Posts were originally published on the Apps on Azure blog on Microsoft Tech Community.


    What We'll Cover

    • Retry Policy Support - in Apache Kafka Extension
    • AutoOffsetReset property - in Apache Kafka Extension
    • Key support for Kafka messages - in Apache Kafka Extension
    • References: Apache Kafka Extension for Azure Functions


    Recently we launched the Apache Kafka extension for Azure functions in GA with some cool new features like deserialization of Avro Generic records and Kafka headers support. We received great responses - so we're back with more updates!

    Retry Policy support

    Handling errors in Azure Functions is important to avoid data loss or miss events or monitor the health of an application. Apache Kafka Extension for Azure Functions supports retry policy which tells the runtime to rerun a failed execution until either successful completion occurs or the maximum number of retries is reached.

    A retry policy is evaluated when a trigger function raises an uncaught exception. As a best practice, you should catch all exceptions in your code and rethrow any errors that you want to result in a retry.

    There are two retry strategies supported by policy that you can configure :- fixed delay and exponential backoff

    1. Fixed Delay - A specified amount of time is allowed to elapse between each retry.
    2. Exponential Backoff - The first retry waits for the minimum delay. On subsequent retries, time is added exponentially to the initial duration for each retry, until the maximum delay is reached. Exponential back-off adds some small randomization to delays to stagger retries in high-throughput scenarios.
    Please Note

    Retry Policy for Kafka extension is NOT supported for C# (in proc and out proc) trigger and output binding. This is supported for languages like Java, Node (JS , TypeScript), PowerShell and Python trigger and output bindings.

    Here is the sample code view of exponential backoff retry strategy

    Error Handling with Apache Kafka extension for Azure Functions

    AutoOffsetReset property

    AutoOffsetReset property enables customers to configure the behaviour in the absence of an initial offset. Imagine a scenario when there is a need to change consumer group name. The consumer connected using a new consumer group had to reprocess all events starting from the oldest (earliest) one, as this was the default one and this setting wasn’t exposed as configurable option in the Apache Kafka extension for Azure Functions(previously). With the help of this kafka setting you can configure on how to start processing events for newly created consumer groups.

    Due to lack of the ability to configure this setting, offset commit errors were causing topics to restart from earliest offset· Users were looking to be able to set offset setting to either latest or earliest based on their requirements.

    We are happy to share that we have enabled the AutoOffsetReset setting as a configurable one to either - Earliest(Default) and Latest. Setting the value to Earliest configures the consumption of the messages from the the earliest/smallest offset or beginning of the topic partition. Setting the property to Latest configures the consumption of the messages from the latest/largest offset or from the end of the topic partition. This is supported for all the Azure Functions supported languages (C# (in & out), Java, Node (JS and TypeScript), PowerShell and python) and can be used for both triggers and output binding

    Error Handling with Apache Kafka extension for Azure Functions

    Key support for Kafka messages

    With keys the producer/output binding can be mapped to broker and partition to write based on the message. So alongside the message value, we can choose to send a message key and that key can be whatever you want it could be a string, it could be a number . In case you don’t send the key, the key is set to null then the data will be sent in a Round Robin fashion to make it very simple. But in case you send a key with your message, all the messages that share the same key will always go to the same partition and thus you can enable grouping of similar messages into partitions

    Previously while consuming a Kafka event message using the Azure Function kafka extension, the event key was always none although the key was present in the event message.

    Key support was implemented in the extension which enables customers to set/view key in the Kafka event messages coming in to the kafka trigger and set keys to the messages going in to kafka topics (with keys set) through output binding. Therefore key support was enabled in the extension to support both trigger and output binding for all Azure Functions supported languages ( (C# (in & out), Java, Node (JS and TypeScript), PowerShell and python)

    Here is the view of an output binding producer code where Kafka messages are being set with key

    Error Handling with Apache Kafka extension for Azure Functions

    Conclusion:

    In this article you have learnt about the latest additions to the Apache Kafka extension for Azure Functions. Incase you have been waiting for these features to get released or need them you are all set and please go head and try them out!! They are available in the latest extension bundles

    Want to learn more?

    Please refer to Apache Kafka bindings for Azure Functions | Microsoft Docs for detail documentation, samples on the Azure function supported languages and more!

    References

    FEEDBACK WELCOME

    Keep in touch with us on Twitter via @AzureFunctions.

    - - + + \ No newline at end of file diff --git a/blog/tags/zero-to-hero/page/3/index.html b/blog/tags/zero-to-hero/page/3/index.html index 6527b6c297..c2e74d78f3 100644 --- a/blog/tags/zero-to-hero/page/3/index.html +++ b/blog/tags/zero-to-hero/page/3/index.html @@ -14,14 +14,14 @@ - - + +

    8 posts tagged with "zero-to-hero"

    View All Tags

    · 5 min read
    Mike Morton

    Welcome to Day 19 of #30DaysOfServerless!

    Today, we have a special set of posts from our Zero To Hero 🚀 initiative, featuring blog posts authored by our Product Engineering teams for #ServerlessSeptember. Posts were originally published on the Apps on Azure blog on Microsoft Tech Community.


    What We'll Cover

    • Log Streaming - in Azure Portal
    • Console Connect - in Azure Portal
    • Metrics - using Azure Monitor
    • Log Analytics - using Azure Monitor
    • Metric Alerts and Log Alerts - using Azure Monitor


    In past weeks, @kendallroden wrote about what it means to be Cloud-Native and @Anthony Chu the various ways to get your apps running on Azure Container Apps. Today, we will talk about the observability tools you can use to observe, debug, and diagnose your Azure Container Apps.

    Azure Container Apps provides several observability features to help you debug and diagnose your apps. There are both Azure portal and CLI options you can use to help understand the health of your apps and help identify when issues arise.

    While these features are helpful throughout your container app’s lifetime, there are two that are especially helpful. Log streaming and console connect can be a huge help in the initial stages when issues often rear their ugly head. Let's dig into both of these a little.

    Log Streaming

    Log streaming allows you to use the Azure portal to view the streaming logs from your app. You’ll see the logs written from the app to the container’s console (stderr and stdout). If your app is running multiple revisions, you can choose from which revision to view logs. You can also select a specific replica if your app is configured to scale. Lastly, you can choose from which container to view the log output. This is useful when you are running a custom or Dapr sidecar container. view streaming logs

    Here’s an example CLI command to view the logs of a container app.

    az containerapp logs show -n MyContainerapp -g MyResourceGroup

    You can find more information about the different options in our CLI docs.

    Console Connect

    In the Azure portal, you can connect to the console of a container in your app. Like log streaming, you can select the revision, replica, and container if applicable. After connecting to the console of the container, you can execute shell commands and utilities that you have installed in your container. You can view files and their contents, monitor processes, and perform other debugging tasks.

    This can be great for checking configuration files or even modifying a setting or library your container is using. Of course, updating a container in this fashion is not something you should do to a production app, but tweaking and re-testing an app in a non-production environment can speed up development.

    Here’s an example CLI command to connect to the console of a container app.

    az containerapp exec -n MyContainerapp -g MyResourceGroup

    You can find more information about the different options in our CLI docs.

    Metrics

    Azure Monitor collects metric data from your container app at regular intervals to help you gain insights into the performance and health of your container app. Container apps provide these metrics:

    • CPU usage
    • Memory working set bytes
    • Network in bytes
    • Network out bytes
    • Requests
    • Replica count
    • Replica restart count

    Here you can see the metrics explorer showing the replica count for an app as it scaled from one replica to fifteen, and then back down to one.

    You can also retrieve metric data through the Azure CLI.

    Log Analytics

    Azure Monitor Log Analytics is great for viewing your historical logs emitted from your container apps. There are two custom tables of interest, the ContainerAppConsoleLogs_CL which contains all the log messages written by your app (stdout and stderr), and the ContainerAppSystemLogs_CL which contain the system messages from the Azure Container Apps service.

    You can also query Log Analytics through the Azure CLI.

    Alerts

    Azure Monitor alerts notify you so that you can respond quickly to critical issues. There are two types of alerts that you can define:

    You can create alert rules from metric charts in the metric explorer and from queries in Log Analytics. You can also define and manage alerts from the Monitor|Alerts page.

    Here is what creating an alert looks like in the Azure portal. In this case we are setting an alert rule from the metric explorer to trigger an alert if the replica restart count for a specific container app is greater than two within the last fifteen minutes.

    To learn more about alerts, refer to Overview of alerts in Microsoft Azure.

    Conclusion

    In this article, we looked at the several ways to observe, debug, and diagnose your Azure Container Apps. As you can see there are rich portal tools and a complete set of CLI commands to use. All the tools are helpful throughout the lifecycle of your app, be sure to take advantage of them when having an issue and/or to prevent issues.

    To learn more, visit Azure Container Apps | Microsoft Azure today!

    ASK THE EXPERT: LIVE Q&A

    The Azure Container Apps team will answer questions live on September 29.

    - - + + \ No newline at end of file diff --git a/blog/tags/zero-to-hero/page/4/index.html b/blog/tags/zero-to-hero/page/4/index.html index 6786490fcf..1eeac139d5 100644 --- a/blog/tags/zero-to-hero/page/4/index.html +++ b/blog/tags/zero-to-hero/page/4/index.html @@ -14,13 +14,13 @@ - - + +

    8 posts tagged with "zero-to-hero"

    View All Tags

    · 6 min read
    Melony Qin

    Welcome to Day 12 of #30DaysOfServerless!

    Today, we have a special set of posts from our Zero To Hero 🚀 initiative, featuring blog posts authored by our Product Engineering teams for #ServerlessSeptember. Posts were originally published on the Apps on Azure blog on Microsoft Tech Community.


    What We'll Cover

    • What are Custom Handlers, and why use them?
    • How Custom Handler Works
    • Message Processing With Azure Custom Handler
    • Azure Portal Monitoring


    If you have been working with Azure Functions for a while, you may know Azure Functions is a serverless FaaS (Function as a Service) offered provided by Microsoft Azure, which is built for your key scenarios, including building web APIs, processing file uploads, responding to database changes, processing IoT data streams, managing message queues, and more.

    Custom Handlers: What and Why

    Azure functions support multiple programming languages including C#, F#, Java, JavaScript, Python, typescript, and PowerShell. If you want to get extended language support with Azure functions for other languages such as Go, and Rust, that’s where custom handler comes in.

    An Azure function custom handler allows the use of any language that supports HTTP primitives and author Azure functions. With custom handlers, you can use triggers and input and output bindings via extension bundles, hence it supports all the triggers and bindings you're used to with Azure functions.

    How a Custom Handler Works

    Let’s take a look at custom handlers and how it works.

    • A request is sent to the function host when an event is triggered. It’s up to the function host to issue a request payload to the custom handler, which holds the trigger and inputs binding data as well as other metadata for the function.
    • The custom handler is a local HTTP web server. It executes the function code and returns a response payload to the Functions host.
    • The Functions host passes data from the response to the function's output bindings which will be passed to the downstream stream services for data processing.

    Check out this article to know more about Azure functions custom handlers.


    Message processing with Custom Handlers

    Message processing is one of the key scenarios that Azure functions are trying to address. In the message-processing scenario, events are often collected in queues. These events can trigger Azure functions to execute a piece of business logic.

    You can use the Service Bus trigger to respond to messages from an Azure Service Bus queue - it's then up to the Azure functions custom handlers to take further actions to process the messages. The process is described in the following diagram:

    Building Serverless Go Applications with Azure functions custom handlers

    In Azure function, the function.json defines the function's trigger, input and output bindings, and other configuration settings. Note that every function can have multiple bindings, but it can only have one trigger. The following is an example of setting up the Service Bus queue trigger in the function.json file :

    {
    "bindings": [
    {
    "name": "queueItem",
    "type": "serviceBusTrigger",
    "direction": "in",
    "queueName": "functionqueue",
    "connection": "ServiceBusConnection"
    }
    ]
    }

    You can add a binding definition in the function.json to write the output to a database or other locations of your desire. Supported bindings can be found here.

    As we’re programming in Go, so we need to set the value of defaultExecutablePath to handler in the customHandler.description section in the host.json file.

    Assume we’re programming in Windows OS, and we have named our go application as server.go, after we executed go build server.go command, it produces an executable called server.exe. So here we set server.exe in the host.json as the following example :

      "customHandler": {
    "description": {
    "defaultExecutablePath": "./server.exe",
    "workingDirectory": "",
    "arguments": []
    }
    }

    We’re showcasing a simple Go application here with Azure functions custom handlers where we print out the messages received from the function host. The following is the full code of server.go application :

    package main

    import (
    "encoding/json"
    "fmt"
    "log"
    "net/http"
    "os"
    )

    type InvokeRequest struct {
    Data map[string]json.RawMessage
    Metadata map[string]interface{}
    }

    func queueHandler(w http.ResponseWriter, r *http.Request) {
    var invokeRequest InvokeRequest

    d := json.NewDecoder(r.Body)
    d.Decode(&invokeRequest)

    var parsedMessage string
    json.Unmarshal(invokeRequest.Data["queueItem"], &parsedMessage)

    fmt.Println(parsedMessage)
    }

    func main() {
    customHandlerPort, exists := os.LookupEnv("FUNCTIONS_CUSTOMHANDLER_PORT")
    if !exists {
    customHandlerPort = "8080"
    }
    mux := http.NewServeMux()
    mux.HandleFunc("/MessageProcessorFunction", queueHandler)
    fmt.Println("Go server Listening on: ", customHandlerPort)
    log.Fatal(http.ListenAndServe(":"+customHandlerPort, mux))

    }

    Ensure you have Azure functions core tools installed, then we can use func start command to start our function. Then we’ll have have a C#-based Message Sender application on Github to send out 3000 messages to the Azure service bus queue. You’ll see Azure functions instantly start to process the messages and print out the message as the following:

    Monitoring Serverless Go Applications with Azure functions custom handlers


    Azure portal monitoring

    Let’s go back to Azure portal portal the events see how those messages in Azure Service Bus queue were being processed. There was 3000 messages were queued in the Service Bus queue ( the Blue line stands for incoming Messages ). The outgoing messages (the red line in smaller wave shape ) showing there are progressively being read by Azure functions as the following :

    Monitoring Serverless Go Applications with Azure functions custom handlers

    Check out this article about monitoring Azure Service bus for further information.

    Next steps

    Thanks for following along, we’re looking forward to hearing your feedback. Also, if you discover potential issues, please record them on Azure Functions host GitHub repository or tag us @AzureFunctions on Twitter.

    RESOURCES

    Start to build your serverless applications with custom handlers, check out the official documentation:

    Life is a journey of learning. Let’s stay tuned!

    - - + + \ No newline at end of file diff --git a/blog/tags/zero-to-hero/page/5/index.html b/blog/tags/zero-to-hero/page/5/index.html index 81c52e7ef6..53677dc90f 100644 --- a/blog/tags/zero-to-hero/page/5/index.html +++ b/blog/tags/zero-to-hero/page/5/index.html @@ -14,13 +14,13 @@ - - + +

    8 posts tagged with "zero-to-hero"

    View All Tags

    · 5 min read
    Anthony Chu

    Welcome to Day 12 of #30DaysOfServerless!

    Today, we have a special set of posts from our Zero To Hero 🚀 initiative, featuring blog posts authored by our Product Engineering teams for #ServerlessSeptember. Posts were originally published on the Apps on Azure blog on Microsoft Tech Community.


    What We'll Cover

    • Using Visual Studio
    • Using Visual Studio Code: Docker, ACA extensions
    • Using Azure CLI
    • Using CI/CD Pipelines


    Last week, @kendallroden wrote about what it means to be Cloud-Native and how Azure Container Apps provides a serverless containers platform for hosting all of your Cloud-Native applications. Today, we’ll walk through a few ways to get your apps up and running on Azure Container Apps.

    Depending on where you are in your Cloud-Native app development journey, you might choose to use different tools to deploy your apps.

    • “Right-click, publish” – Deploying an app directly from an IDE or code editor is often seen as a bad practice, but it’s one of the quickest ways to test out an app in a cloud environment.
    • Command line interface – CLIs are useful for deploying apps from a terminal. Commands can be run manually or in a script.
    • Continuous integration/deployment – To deploy production apps, the recommended approach is to automate the process in a robust CI/CD pipeline.

    Let's explore some of these options in more depth.

    Visual Studio

    Visual Studio 2022 has built-in support for deploying .NET applications to Azure Container Apps. You can use the familiar publish dialog to provision Container Apps resources and deploy to them directly. This helps you prototype an app and see it run in Azure Container Apps with the least amount of effort.

    Journey to the cloud with Azure Container Apps

    Once you’re happy with the app and it’s ready for production, Visual Studio allows you to push your code to GitHub and set up a GitHub Actions workflow to build and deploy your app every time you push changes. You can do this by checking a box.

    Journey to the cloud with Azure Container Apps

    Visual Studio Code

    There are a couple of valuable extensions that you’ll want to install if you’re working in VS Code.

    Docker extension

    The Docker extension provides commands for building a container image for your app and pushing it to a container registry. It can even do this without requiring Docker Desktop on your local machine --- the “Build image in Azure” command remotely builds and pushes a container image to Azure Container Registry.

    Journey to the cloud with Azure Container Apps

    And if your app doesn’t have a dockerfile, the extension will generate one for you.

    Journey to the cloud with Azure Container Apps

    Azure Container Apps extension

    Once you’ve built your container image and pushed it to a registry, the Azure Container Apps VS Code extension provides commands for creating a container app and deploying revisions using the image you’ve built.

    Journey to the cloud with Azure Container Apps

    Azure CLI

    The Azure CLI can be used to manage pretty much anything in Azure. For Azure Container Apps, you’ll find commands for creating, updating, and managing your Container Apps resources.

    Just like in VS Code, with a few commands in the Azure CLI, you can create your Azure resources, build and push your container image, and then deploy it to a container app.

    To make things as simple as possible, the Azure CLI also has an “az containerapp up” command. This single command takes care of everything that’s needed to turn your source code from your local machine to a cloud-hosted application in Azure Container Apps.

    az containerapp up --name myapp --source ./src

    We saw earlier that Visual Studio can generate a GitHub Actions workflow to automatically build and deploy your app on every commit. “az containerapp up” can do this too. The following adds a workflow to a repo.

    az containerapp up --name myapp --repo https://github.com/myorg/myproject

    CI/CD pipelines

    When it’s time to take your app to production, it’s strongly recommended to set up a CI/CD pipeline to automatically and repeatably build, test, and deploy it. We’ve already seen that tools such as Visual Studio and Azure CLI can automatically generate a workflow for GitHub Actions. You can set up a pipeline in Azure DevOps too. This is an example Azure DevOps pipeline.

    trigger:
    branches:
    include:
    - main

    pool:
    vmImage: ubuntu-latest

    stages:

    - stage: Build

    jobs:
    - job: build
    displayName: Build app

    steps:
    - task: Docker@2
    inputs:
    containerRegistry: 'myregistry'
    repository: 'hello-aca'
    command: 'buildAndPush'
    Dockerfile: 'hello-container-apps/Dockerfile'
    tags: '$(Build.BuildId)'

    - stage: Deploy

    jobs:
    - job: deploy
    displayName: Deploy app

    steps:
    - task: AzureCLI@2
    inputs:
    azureSubscription: 'my-subscription(5361b9d6-46ea-43c3-a898-15f14afb0db6)'
    scriptType: 'bash'
    scriptLocation: 'inlineScript'
    inlineScript: |
    # automatically install Container Apps CLI extension
    az config set extension.use_dynamic_install=yes_without_prompt

    # ensure registry is configured in container app
    az containerapp registry set \
    --name hello-aca \
    --resource-group mygroup \
    --server myregistry.azurecr.io \
    --identity system

    # update container app
    az containerapp update \
    --name hello-aca \
    --resource-group mygroup \
    --image myregistry.azurecr.io/hello-aca:$(Build.BuildId)

    Conclusion

    In this article, we looked at a few ways to deploy your Cloud-Native applications to Azure Container Apps and how to decide which one to use based on where you are in your app’s journey to the cloud.

    To learn more, visit Azure Container Apps | Microsoft Azure today!

    ASK THE EXPERT: LIVE Q&A

    The Azure Container Apps team will answer questions live on September 29.

    - - + + \ No newline at end of file diff --git a/blog/tags/zero-to-hero/page/6/index.html b/blog/tags/zero-to-hero/page/6/index.html index e0ac823bdf..2ca7f80497 100644 --- a/blog/tags/zero-to-hero/page/6/index.html +++ b/blog/tags/zero-to-hero/page/6/index.html @@ -14,13 +14,13 @@ - - + +

    8 posts tagged with "zero-to-hero"

    View All Tags

    · 5 min read
    Nitya Narasimhan
    Devanshi Joshi

    SEP 08: CHANGE IN PUBLISHING SCHEDULE

    Starting from Week 2 (Sep 8), we'll be publishing blog posts in batches rather than on a daily basis, so you can read a series of related posts together. Don't want to miss updates? Just subscribe to the feed


    Welcome to Day 8 of #30DaysOfServerless!

    This marks the end of our Week 1 Roadmap focused on Azure Functions!! Today, we'll do a quick recap of all #ServerlessSeptember activities in Week 1, set the stage for Week 2 - and leave you with some excellent tutorials you should explore to build more advanced scenarios with Azure Functions.

    Ready? Let's go.


    What We'll Cover

    • Azure Functions: Week 1 Recap
    • Advanced Functions: Explore Samples
    • End-to-End: Serverless Hacks & Cloud Skills
    • What's Next: Hello, Containers & Microservices
    • Challenge: Complete the Learning Path


    Week 1 Recap: #30Days & Functions

    Congratulations!! We made it to the end of Week 1 of #ServerlessSeptember. Let's recap what we learned so far:

    • In Core Concepts we looked at where Azure Functions fits into the serverless options available on Azure. And we learned about key concepts like Triggers, Bindings, Custom Handlers and Durable Functions.
    • In Build Your First Function we looked at the tooling options for creating Functions apps, testing them locally, and deploying them to Azure - as we built and deployed our first Functions app.
    • In the next 4 posts, we explored new Triggers, Integrations, and Scenarios - as we looked at building Functions Apps in Java, JavaScript, .NET and Python.
    • And in the Zero-To-Hero series, we learned about Durable Entities - and how we can use them to create stateful serverless solutions using a Chirper Sample as an example scenario.

    The illustrated roadmap below summarizes what we covered each day this week, as we bring our Functions-as-a-Service exploration to a close.


    Advanced Functions: Code Samples

    So, now that we've got our first Functions app under our belt, and validated our local development setup for tooling, where can we go next? A good next step is to explore different triggers and bindings, that drive richer end-to-end scenarios. For example:

    • Integrate Functions with Azure Logic Apps - we'll discuss Azure Logic Apps in Week 3. For now, think of it as a workflow automation tool that lets you integrate seamlessly with other supported Azure services to drive an end-to-end scenario. In this tutorial, we set up a workflow connecting Twitter (get tweet) to Azure Cognitive Services (analyze sentiment) - and use that to trigger an Azure Functions app to send email about the result.
    • Integrate Functions with Event Grid - we'll discuss Azure Event Grid in Week 3. For now, think of it as an eventing service connecting event sources (publishers) to event handlers (subscribers) at cloud scale. In this tutorial, we handle a common use case - a workflow where loading an image to Blob Storage triggers an Azure Functions app that implements a resize function, helping automatically generate thumbnails for the uploaded image.
    • Integrate Functions with CosmosDB and SignalR to bring real-time push-based notifications to your web app. It achieves this by using a Functions app that is triggered by changes in a CosmosDB backend, causing it to broadcast that update (push notification to connected web clients over SignalR, in real time.

    Want more ideas? Check out the Azure Samples for Functions for implementations, and browse the Azure Architecture Center for reference architectures from real-world scenarios that involve Azure Functions usage.


    E2E Scenarios: Hacks & Cloud Skills

    Want to systematically work your way through a single End-to-End scenario involving Azure Functions alongside other serverless support technologies? Check out the Serverless Hacks activity happening during #ServerlessSeptember, and learn to build this "Serverless Tollbooth Application" in a series of 10 challenges. Check out the video series for a reference solution in .NET and sign up for weekly office hours to join peers and discuss your solutions or challenges.

    Or perhaps you prefer to learn core concepts with code in a structured learning path? We have that covered. Check out the 12-module "Create Serverless Applications" course from Microsoft Learn which walks your through concepts, one at a time, with code. Even better - sign up for the free Cloud Skills Challenge and complete the same path (in under 30 days) but this time, with the added fun of competing against your peers for a spot on a leaderboard, and swag.


    What's Next? Hello, Cloud-Native!

    So where to next? In Week 2 we turn our attention from Functions-as-a-Service to building more complex backends using Containers and Microservices. We'll focus on two core technologies - Azure Container Apps and Dapr (Distributed Application Runtime) - both key components of a broader vision around Building Cloud-Native Applications in Azure.

    What is Cloud-Native you ask?

    Fortunately for you, we have an excellent introduction in our Zero-to-Hero article on Go Cloud-Native with Azure Container Apps - that explains the 5 pillars of Cloud-Native and highlights the value of Azure Container Apps (scenarios) and Dapr (sidecar architecture) for simplified microservices-based solution with auto-scale capability. Prefer a visual summary? Here's an illustrate guide to that article for convenience.

    Go Cloud-Native Download a higher resolution version of the image


    Take The Challenge

    We typically end each post with an exercise or activity to reinforce what you learned. For Week 1, we encourage you to take the Cloud Skills Challenge and work your way through at least a subset of the modules, for hands-on experience with the different Azure Functions concepts, integrations, and usage.

    See you in Week 2!

    - - + + \ No newline at end of file diff --git a/blog/tags/zero-to-hero/page/7/index.html b/blog/tags/zero-to-hero/page/7/index.html index 75e997fe81..5e64f349f6 100644 --- a/blog/tags/zero-to-hero/page/7/index.html +++ b/blog/tags/zero-to-hero/page/7/index.html @@ -14,15 +14,15 @@ - - + +

    8 posts tagged with "zero-to-hero"

    View All Tags

    · 8 min read
    David Justo

    Welcome to Day 6 of #30DaysOfServerless!

    Today, we have a special set of posts from our Zero To Hero 🚀 initiative, featuring blog posts authored by our Product Engineering teams for #ServerlessSeptember. Posts were originally published on the Apps on Azure blog on Microsoft Tech Community.


    What We'll Cover

    • What are Durable Entities
    • Some Background
    • A Programming Model
    • Entities for a Micro-Blogging Platform


    Durable Entities are a special type of Azure Function that allow you to implement stateful objects in a serverless environment. They make it easy to introduce stateful components to your app without needing to manually persist data to external storage, so you can focus on your business logic. We’ll demonstrate their power with a real-life example in the last section.

    Entities 101: Some Background

    Programming Durable Entities feels a lot like object-oriented programming, except that these “objects” exist in a distributed system. Like objects, each Entity instance has a unique identifier, i.e. an entity ID that can be used to read and manipulate their internal state. Entities define a list of operations that constrain how their internal state is managed, like an object interface.

    Some experienced readers may realize that Entities sound a lot like an implementation of the Actor Pattern. For a discussion of the relationship between Entities and Actors, please refer to this documentation.

    Entities are a part of the Durable Functions Extension, an extension of Azure Functions that empowers programmers with stateful abstractions for serverless, such as Orchestrations (i.e. workflows).

    Durable Functions is available in most Azure Functions runtime environments: .NET, Node.js, Python, PowerShell, and Java (preview). For this article, we’ll focus on the C# experience, but note that Entities are also available in Node.js and Python; their availability in other languages is underway.

    Entities 102: The programming model

    Imagine you want to implement a simple Entity that just counts things. Its interface allows you to get the current count, add to the current count, and to reset the count to zero.

    If you implement this in an object-oriented way, you’d probably define a class (say “Counter”) with a method to get the current count (say “Counter.Get”), another to add to the count (say “Counter.Add”), and another to reset the count (say “Counter.Reset”). Well, the implementation of an Entity in C# is not that different from this sketch:

    [JsonObject(MemberSerialization.OptIn)] 
    public class Counter
    {
    [JsonProperty("value")]
    public int Value { get; set; }

    public void Add(int amount)
    {
    this.Value += amount;
    }

    public Task Reset()
    {
    this.Value = 0;
    return Task.CompletedTask;
    }

    public Task<int> Get()
    {
    return Task.FromResult(this.Value);
    }
    [FunctionName(nameof(Counter))]
    public static Task Run([EntityTrigger] IDurableEntityContext ctx)
    => ctx.DispatchAsync<Counter>();

    }

    We’ve defined a class named Counter, with an internal count stored in the variable “Value” which is manipulated through the “Add” and “Reset” methods, and which can be read via “Get”.

    The “Run” method is simply boilerplate required for the Azure Functions framework to interact with the object we’ve defined – it’s the method that the framework calls internally when it needs to load the Entity object. When DispatchAsync is called, the Entity and its corresponded state (the last count in “Value”) is loaded from storage. Again, this is mostly just boilerplate: your Entity’s business logic lies in the rest of the class.

    Finally, the Json annotation on top of the class and the Value field tells the Durable Functions framework that the “Value” field is to be durably persisted as part of the durable state on each Entity invocation. If you were to annotate other class variables with JsonProperty, they would also become part of the managed state.

    Entities for a micro-blogging platform

    We’ll try to implement a simple micro-blogging platform, a la Twitter. Let’s call it “Chirper”. In Chirper, users write chirps (i.e tweets), they can follow, and unfollow other users, and they can read the chirps of users they follow.

    Defining Entity

    Just like in OOP, it’s useful to begin by identifying what are the stateful agents of this scenario. In this case, users have state (who they follow and their chirps), and chirps have state in the form of their content. So, we could model these stateful agents as Entities!

    Below is a potential way to implement a User for Chirper as an Entity:

      [JsonObject(MemberSerialization = MemberSerialization.OptIn)] 
    public class User: IUser
    {
    [JsonProperty]
    public List<string> FollowedUsers { get; set; } = new List<string>();

    public void Add(string user)
    {
    FollowedUsers.Add(user);
    }

    public void Remove(string user)
    {
    FollowedUsers.Remove(user);
    }

    public Task<List<string>> Get()
    {
    return Task.FromResult(FollowedUsers);
    }
    // note: removed boilerplate “Run” method, for conciseness.
    }

    In this case, our Entity’s internal state is stored in “FollowedUsers” which is an array of accounts followed by this user. The operations exposed by this entity allow clients to read and modify this data: it can be read by “Get”, a new follower can be added via “Add”, and a user can be unfollowed via “Remove”.

    With that, we’ve modeled a Chirper’s user as an Entity! Recall that Entity instances each has a unique ID, so we can consider that unique ID to correspond to a specific user account.

    What about chirps? Should we represent them as Entities as well? That would certainly be valid. However, we would then need to create a mapping between an entity ID and every chirp entity ID that this user wrote.

    For demonstration purposes, a simpler approach would be to create an Entity that stores the list of all chirps authored by a given user; call it UserChirps. Then, we could fix each User Entity to share the same entity ID as its corresponding UserChirps Entity, making client operations easier.

    Below is a simple implementation of UserChirps:

      [JsonObject(MemberSerialization = MemberSerialization.OptIn)] 
    public class UserChirps : IUserChirps
    {
    [JsonProperty]
    public List<Chirp> Chirps { get; set; } = new List<Chirp>();

    public void Add(Chirp chirp)
    {
    Chirps.Add(chirp);
    }

    public void Remove(DateTime timestamp)
    {
    Chirps.RemoveAll(chirp => chirp.Timestamp == timestamp);
    }

    public Task<List<Chirp>> Get()
    {
    return Task.FromResult(Chirps);
    }

    // Omitted boilerplate “Run” function
    }

    Here, our state is stored in Chirps, a list of user posts. Our operations are the same as before: Get, Read, and Add. It’s the same pattern as before, but we’re representing different data.

    To put it all together, let’s set up Entity clients to generate and manipulate these Entities according to some REST API.

    Interacting with Entity

    Before going there, let’s talk briefly about how you can interact with an Entity. Entity interactions take one of two forms -- calls and signals:

    Calling an entity is a two-way communication. You send an operation message to the entity and then wait for the response message before you continue. The response can be a result value or an error. Signaling an entity is a one-way (fire-and-forget) communication. You send an operation message but don’t wait for a response. You have the reassurance that the message will be delivered eventually, but you don’t know when and don’t know what the response is. For example, when you read the state of an Entity, you are performing a “call” interaction. When you record that a user has followed another, you may choose to simply signal it.

    Now say user with a given userId (say “durableFan99” ) wants to post a chirp. For this, you can write an HTTP endpoint to signal the UserChips entity to record that chirp. We can leverage the HTTP Trigger functionality from Azure Functions and pair it with an entity client binding that signals the Add operation of our Chirp Entity:

    [FunctionName("UserChirpsPost")] 
    public static async Task<HttpResponseMessage> UserChirpsPost(
    [HttpTrigger(AuthorizationLevel.Function, "post", Route = "user/{userId}/chirps")]
    HttpRequestMessage req,
    DurableClient] IDurableClient client,
    ILogger log,
    string userId)
    {
    Authenticate(req, userId);
    var chirp = new Chirp()
    {
    UserId = userId,
    Timestamp = DateTime.UtcNow,
    Content = await req.Content.ReadAsStringAsync(),
    };
    await client.SignalEntityAsync<IUserChirps>(userId, x => x.Add(chirp));
    return req.CreateResponse(HttpStatusCode.Accepted, chirp);
    }

    Following the same pattern as above, to get all the chirps from a user, you could read the status of your Entity via ReadEntityStateAsync, which follows the call-interaction pattern as your client expects a response:

    [FunctionName("UserChirpsGet")] 
    public static async Task<HttpResponseMessage> UserChirpsGet(
    [HttpTrigger(AuthorizationLevel.Function, "get", Route = "user/{userId}/chirps")] HttpRequestMessage req,
    [DurableClient] IDurableClient client,
    ILogger log,
    string userId)
    {

    Authenticate(req, userId);
    var target = new EntityId(nameof(UserChirps), userId);
    var chirps = await client.ReadEntityStateAsync<UserChirps>(target);
    return chirps.EntityExists
    ? req.CreateResponse(HttpStatusCode.OK, chirps.EntityState.Chirps)
    : req.CreateResponse(HttpStatusCode.NotFound);
    }

    And there you have it! To play with a complete implementation of Chirper, you can try out our sample in the Durable Functions extension repo.

    Thank you!

    info

    Thanks for following along, and we hope you find Entities as useful as we do! If you have questions or feedback, please file issues in the repo above or tag us @AzureFunctions on Twitter

    - - + + \ No newline at end of file diff --git a/blog/tags/zero-to-hero/page/8/index.html b/blog/tags/zero-to-hero/page/8/index.html index f15c7c1030..44a1d5d45e 100644 --- a/blog/tags/zero-to-hero/page/8/index.html +++ b/blog/tags/zero-to-hero/page/8/index.html @@ -14,13 +14,13 @@ - - + +

    8 posts tagged with "zero-to-hero"

    View All Tags

    · 8 min read
    Kendall Roden

    Welcome to Day 6 of #30DaysOfServerless!

    Today, we have a special set of posts from our Zero To Hero 🚀 initiative, featuring blog posts authored by our Product Engineering teams for #ServerlessSeptember. Posts were originally published on the Apps on Azure blog on Microsoft Tech Community.


    What We'll Cover

    • Defining Cloud-Native
    • Introduction to Azure Container Apps
    • Dapr In Azure Container Apps
    • Conclusion


    Defining Cloud-Native

    While I’m positive I’m not the first person to ask this, I think it’s an appropriate way for us to kick off this article: “How many developers does it take to define Cloud-Native?” I hope you aren’t waiting for a punch line because I seriously want to know your thoughts (drop your perspectives in the comments..) but if you ask me, the limit does not exist!

    A quick online search of the topic returns a laundry list of articles, e-books, twitter threads, etc. all trying to nail down the one true definition. While diving into the rabbit hole of Cloud-Native, you will inevitably find yourself on the Cloud-Native Computing Foundation (CNCF) site. The CNCF is part of the Linux Foundation and aims to make "Cloud-Native computing ubiquitous" through deep open source project and community involvement. The CNCF has also published arguably the most popularized definition of Cloud-Native which begins with the following statement:

    “Cloud-Native technologies empower organizations to build and run scalable applications in modern, dynamic environments such as public, private, and hybrid clouds."

    Over the past four years, my day-to-day work has been driven primarily by the surging demand for application containerization and the drastic adoption of Kubernetes as the de-facto container orchestrator. Customers are eager to learn and leverage patterns, practices and technologies that enable building "loosely coupled systems that are resilient, manageable, and observable". Enterprise developers at these organizations are being tasked with rapidly deploying event-driven, horizontally-scalable, polyglot services via repeatable, code-to-cloud pipelines.

    While building Cloud-Native solutions can enable rapid innovation, the transition to adopting a Cloud-Native architectural approach comes with a steep learning curve and a new set of considerations. In a document published by Microsoft called What is Cloud-Native?, there are a few key areas highlighted to aid customers in the adoption of best practices for building modern, portable applications which I will summarize below:

    Cloud infrastructure

    • Cloud-Native applications leverage cloud infrastructure and make use of Platform-as-a-service offerings
    • Cloud-Native applications depend on highly-elastic infrastructure with automatic scaling, self-healing, and monitoring capabilities

    Modern application design

    • Cloud-Native applications should be constructed using principles outlined in the 12 factor methodology

    Microservices

    • Cloud-Native applications are typically composed of microservices where each core function, or service, is built and deployed independently

    Containers

    • Cloud-Native applications are typically deployed using containers as a packaging mechanism where an application's code and dependencies are bundled together for consistency of deployment
    • Cloud-Native applications leverage container orchestration technologies- primarily Kubernetes- for achieving capabilities such as workload scheduling, self-healing, auto-scale, etc.

    Backing services

    • Cloud-Native applications are ideally stateless workloads which retrieve and store data in data stores external to the application hosting infrastructure. Cloud providers like Azure provide an array of backing data services which can be securely accessed from application code and provide capabilities for ensuring application data is highly-available

    Automation

    • Cloud-Native solutions should use deployment automation for backing cloud infrastructure via versioned, parameterized Infrastructure as Code (IaC) templates which provide a consistent, repeatable process for provisioning cloud resources.
    • Cloud-Native solutions should make use of modern CI/CD practices and pipelines to ensure successful, reliable infrastructure and application deployment.

    Azure Container Apps

    In many of the conversations I've had with customers that involve talk of Kubernetes and containers, the topics of cost-optimization, security, networking, and reducing infrastructure and operations inevitably arise. I personally have yet to meet with any customers eager to have their developers get more involved with infrastructure concerns.

    One of my former colleagues, Jeff Hollan, made a statement while appearing on a 2019 episode of The Cloud-Native Show where he shared his perspective on Cloud-Native:

    "When I think about Cloud-Native... it's writing applications in a way where you are specifically thinking about the benefits the cloud can provide... to me, serverless is the perfect realization of that because the only reason you can write serverless applications is because the cloud exists."

    I must say that I agree with Jeff's perspective. In addition to optimizing development practices for the Cloud-Native world, reducing infrastructure exposure and operations is equally as important to many organizations and can be achieved as a result of cloud platform innovation.

    In May of 2022, Microsoft announced the general availability of Azure Container Apps. Azure Container Apps provides customers with the ability to run microservices and containerized applications on a serverless, consumption-based platform.

    For those interested in taking advantage of the open source ecosystem while reaping the benefits of a managed platform experience, Container Apps run on Kubernetes and provides a set of managed open source projects embedded directly into the platform including the Kubernetes Event Driven Autoscaler (KEDA), the Distributed Application Runtime (Dapr) and Envoy.

    Azure Kubernetes Service vs. Azure Container Apps

    Container apps provides other Cloud-Native features and capabilities in addition to those above including, but not limited to:

    The ability to dynamically scale and support growing numbers of users, events, and requests is one of the core requirements for most Cloud-Native, distributed applications. Azure Container Apps is purpose-built with this and other Cloud-Native tenants in mind.

    What can you build with Azure Container Apps?

    Dapr in Azure Container Apps

    As a quick personal note before we dive into this section I will say I am a bit bias about Dapr. When Dapr was first released, I had an opportunity to immediately get involved and became an early advocate for the project. It is created by developers for developers, and solves tangible problems customers architecting distributed systems face:

    HOW DO I
    • integrate with external systems that my app has to react and respond to?
    • create event driven apps which reliably send events from one service to another?
    • observe the calls and events between my services to diagnose issues in production?
    • access secrets securely from within my application?
    • discover other services and call methods on them?
    • prevent committing to a technology early and have the flexibility to swap out an alternative based on project or environment changes?

    While existing solutions were in the market which could be used to address some of the concerns above, there was not a lightweight, CNCF-backed project which could provide a unified approach to solve the more fundamental ask from customers: "How do I make it easy for developers to build microservices based on Cloud-Native best practices?"

    Enter Dapr!

    The Distributed Application Runtime (Dapr) provides APIs that simplify microservice connectivity. Whether your communication pattern is service to service invocation or pub/sub messaging, Dapr helps you write resilient and secured microservices. By letting Dapr’s sidecar take care of the complex challenges such as service discovery, message broker integration, encryption, observability, and secret management, you can focus on business logic and keep your code simple."

    The Container Apps platform provides a managed and supported Dapr integration which eliminates the need for deploying and managing the Dapr OSS project. In addition to providing managed upgrades, the platform also exposes a simplified Dapr interaction model to increase developer productivity and reduce the friction required to leverage Dapr capabilities. While the Dapr integration makes it easier for customers to adopt Cloud-Native best practices in container apps it is not required to make use of the container apps platform.

    Image on Dapr

    For additional insight into the dapr integration visit aka.ms/aca-dapr.

    Conclusion

    Backed by and integrated with powerful Cloud-Native technologies, Azure Container Apps strives to make developers productive, while reducing the operational overhead and learning curve that typically accompanies adopting a cloud-native strategy.

    If you are interested in building resilient, portable and highly-scalable apps visit Azure Container Apps | Microsoft Azure today!

    ASK THE EXPERT: LIVE Q&A

    The Azure Container Apps team will answer questions live on September 29.

    - - + + \ No newline at end of file diff --git a/blog/welcome/index.html b/blog/welcome/index.html index 7ba5f89f54..7f9f5e9d8a 100644 --- a/blog/welcome/index.html +++ b/blog/welcome/index.html @@ -14,13 +14,13 @@ - - + +

    Hello, ServerlessSeptember

    · 3 min read
    Nitya Narasimhan
    Devanshi Joshi

    🍂 It's September?

    Well, almost! September 1 is a few days away and I'm excited! Why? Because it's the perfect time to revisit #Serverless September, a month of

    ".. content-driven learning where experts and practitioners share their insights and tutorials on how to use serverless technologies effectively in today's ecosystems"

    If the words look familiar, it's because I actually wrote them 2 years ago when we launched the 2020 edition of this series. You might even recall this whimsical image I drew to capture the concept of September (fall) and Serverless (event-driven on-demand compute). Since then, a lot has happened in the serverless ecosystem!

    You can still browse the 2020 Content Collection to find great talks, articles and code samples to get started using Serverless on Azure. But read on to learn what's new!

    🧐 What's New?

    Well - quite a few things actually. This year, Devanshi Joshi and I expanded the original concept in a number of ways. Here's just a few of them that come to mind.

    New Website

    This year, we created this website (shortcut: https://aka.ms/serverless-september) to serve as a permanent home for content in 2022 and beyond - making it a canonical source for the #serverless posts we publish to tech communities like dev.to, Azure Developer Community and Apps On Azure. We hope this also makes it easier for you to search for, or discover, current and past articles that support your learning journey!

    Start by bookmarking these two sites:

    More Options

    Previous years focused on curating and sharing content authored by Microsoft and community contributors, showcasing serverless examples and best practices. This was perfect for those who already had experience with the core devtools and concepts.

    This year, we wanted to combine beginner-friendly options (for those just starting their serverless journey) with more advanced insights (for those looking to skill up further). Here's a sneak peek at some of the initiatives we've got planned!

    We'll also explore the full spectrum of serverless - from Functions-as-a-Service (for granularity) to Containerization (for deployment) and Microservices (for scalability). Here are a few services and technologies you'll get to learn more about:

    ⚡️ Join us!

    This has been a labor of love from multiple teams at Microsoft! We can't wait to share all the resources that we hope will help you skill up on all things Serverless this September! Here are a couple of ways to participate:

    - - + + \ No newline at end of file diff --git a/blog/zero2hero-aca-01/index.html b/blog/zero2hero-aca-01/index.html index 217d08672b..5f69843ac7 100644 --- a/blog/zero2hero-aca-01/index.html +++ b/blog/zero2hero-aca-01/index.html @@ -14,13 +14,13 @@ - - + +

    🚀 | Go Cloud-Native with ACA

    · 8 min read
    Kendall Roden

    Welcome to Day 6 of #30DaysOfServerless!

    Today, we have a special set of posts from our Zero To Hero 🚀 initiative, featuring blog posts authored by our Product Engineering teams for #ServerlessSeptember. Posts were originally published on the Apps on Azure blog on Microsoft Tech Community.


    What We'll Cover

    • Defining Cloud-Native
    • Introduction to Azure Container Apps
    • Dapr In Azure Container Apps
    • Conclusion


    Defining Cloud-Native

    While I’m positive I’m not the first person to ask this, I think it’s an appropriate way for us to kick off this article: “How many developers does it take to define Cloud-Native?” I hope you aren’t waiting for a punch line because I seriously want to know your thoughts (drop your perspectives in the comments..) but if you ask me, the limit does not exist!

    A quick online search of the topic returns a laundry list of articles, e-books, twitter threads, etc. all trying to nail down the one true definition. While diving into the rabbit hole of Cloud-Native, you will inevitably find yourself on the Cloud-Native Computing Foundation (CNCF) site. The CNCF is part of the Linux Foundation and aims to make "Cloud-Native computing ubiquitous" through deep open source project and community involvement. The CNCF has also published arguably the most popularized definition of Cloud-Native which begins with the following statement:

    “Cloud-Native technologies empower organizations to build and run scalable applications in modern, dynamic environments such as public, private, and hybrid clouds."

    Over the past four years, my day-to-day work has been driven primarily by the surging demand for application containerization and the drastic adoption of Kubernetes as the de-facto container orchestrator. Customers are eager to learn and leverage patterns, practices and technologies that enable building "loosely coupled systems that are resilient, manageable, and observable". Enterprise developers at these organizations are being tasked with rapidly deploying event-driven, horizontally-scalable, polyglot services via repeatable, code-to-cloud pipelines.

    While building Cloud-Native solutions can enable rapid innovation, the transition to adopting a Cloud-Native architectural approach comes with a steep learning curve and a new set of considerations. In a document published by Microsoft called What is Cloud-Native?, there are a few key areas highlighted to aid customers in the adoption of best practices for building modern, portable applications which I will summarize below:

    Cloud infrastructure

    • Cloud-Native applications leverage cloud infrastructure and make use of Platform-as-a-service offerings
    • Cloud-Native applications depend on highly-elastic infrastructure with automatic scaling, self-healing, and monitoring capabilities

    Modern application design

    • Cloud-Native applications should be constructed using principles outlined in the 12 factor methodology

    Microservices

    • Cloud-Native applications are typically composed of microservices where each core function, or service, is built and deployed independently

    Containers

    • Cloud-Native applications are typically deployed using containers as a packaging mechanism where an application's code and dependencies are bundled together for consistency of deployment
    • Cloud-Native applications leverage container orchestration technologies- primarily Kubernetes- for achieving capabilities such as workload scheduling, self-healing, auto-scale, etc.

    Backing services

    • Cloud-Native applications are ideally stateless workloads which retrieve and store data in data stores external to the application hosting infrastructure. Cloud providers like Azure provide an array of backing data services which can be securely accessed from application code and provide capabilities for ensuring application data is highly-available

    Automation

    • Cloud-Native solutions should use deployment automation for backing cloud infrastructure via versioned, parameterized Infrastructure as Code (IaC) templates which provide a consistent, repeatable process for provisioning cloud resources.
    • Cloud-Native solutions should make use of modern CI/CD practices and pipelines to ensure successful, reliable infrastructure and application deployment.

    Azure Container Apps

    In many of the conversations I've had with customers that involve talk of Kubernetes and containers, the topics of cost-optimization, security, networking, and reducing infrastructure and operations inevitably arise. I personally have yet to meet with any customers eager to have their developers get more involved with infrastructure concerns.

    One of my former colleagues, Jeff Hollan, made a statement while appearing on a 2019 episode of The Cloud-Native Show where he shared his perspective on Cloud-Native:

    "When I think about Cloud-Native... it's writing applications in a way where you are specifically thinking about the benefits the cloud can provide... to me, serverless is the perfect realization of that because the only reason you can write serverless applications is because the cloud exists."

    I must say that I agree with Jeff's perspective. In addition to optimizing development practices for the Cloud-Native world, reducing infrastructure exposure and operations is equally as important to many organizations and can be achieved as a result of cloud platform innovation.

    In May of 2022, Microsoft announced the general availability of Azure Container Apps. Azure Container Apps provides customers with the ability to run microservices and containerized applications on a serverless, consumption-based platform.

    For those interested in taking advantage of the open source ecosystem while reaping the benefits of a managed platform experience, Container Apps run on Kubernetes and provides a set of managed open source projects embedded directly into the platform including the Kubernetes Event Driven Autoscaler (KEDA), the Distributed Application Runtime (Dapr) and Envoy.

    Azure Kubernetes Service vs. Azure Container Apps

    Container apps provides other Cloud-Native features and capabilities in addition to those above including, but not limited to:

    The ability to dynamically scale and support growing numbers of users, events, and requests is one of the core requirements for most Cloud-Native, distributed applications. Azure Container Apps is purpose-built with this and other Cloud-Native tenants in mind.

    What can you build with Azure Container Apps?

    Dapr in Azure Container Apps

    As a quick personal note before we dive into this section I will say I am a bit bias about Dapr. When Dapr was first released, I had an opportunity to immediately get involved and became an early advocate for the project. It is created by developers for developers, and solves tangible problems customers architecting distributed systems face:

    HOW DO I
    • integrate with external systems that my app has to react and respond to?
    • create event driven apps which reliably send events from one service to another?
    • observe the calls and events between my services to diagnose issues in production?
    • access secrets securely from within my application?
    • discover other services and call methods on them?
    • prevent committing to a technology early and have the flexibility to swap out an alternative based on project or environment changes?

    While existing solutions were in the market which could be used to address some of the concerns above, there was not a lightweight, CNCF-backed project which could provide a unified approach to solve the more fundamental ask from customers: "How do I make it easy for developers to build microservices based on Cloud-Native best practices?"

    Enter Dapr!

    The Distributed Application Runtime (Dapr) provides APIs that simplify microservice connectivity. Whether your communication pattern is service to service invocation or pub/sub messaging, Dapr helps you write resilient and secured microservices. By letting Dapr’s sidecar take care of the complex challenges such as service discovery, message broker integration, encryption, observability, and secret management, you can focus on business logic and keep your code simple."

    The Container Apps platform provides a managed and supported Dapr integration which eliminates the need for deploying and managing the Dapr OSS project. In addition to providing managed upgrades, the platform also exposes a simplified Dapr interaction model to increase developer productivity and reduce the friction required to leverage Dapr capabilities. While the Dapr integration makes it easier for customers to adopt Cloud-Native best practices in container apps it is not required to make use of the container apps platform.

    Image on Dapr

    For additional insight into the dapr integration visit aka.ms/aca-dapr.

    Conclusion

    Backed by and integrated with powerful Cloud-Native technologies, Azure Container Apps strives to make developers productive, while reducing the operational overhead and learning curve that typically accompanies adopting a cloud-native strategy.

    If you are interested in building resilient, portable and highly-scalable apps visit Azure Container Apps | Microsoft Azure today!

    ASK THE EXPERT: LIVE Q&A

    The Azure Container Apps team will answer questions live on September 29.

    - - + + \ No newline at end of file diff --git a/blog/zero2hero-aca-04/index.html b/blog/zero2hero-aca-04/index.html index 40a2f1edf5..b91e5e2434 100644 --- a/blog/zero2hero-aca-04/index.html +++ b/blog/zero2hero-aca-04/index.html @@ -14,13 +14,13 @@ - - + +

    🚀 | Journey to the Cloud With ACA

    · 5 min read
    Anthony Chu

    Welcome to Day 12 of #30DaysOfServerless!

    Today, we have a special set of posts from our Zero To Hero 🚀 initiative, featuring blog posts authored by our Product Engineering teams for #ServerlessSeptember. Posts were originally published on the Apps on Azure blog on Microsoft Tech Community.


    What We'll Cover

    • Using Visual Studio
    • Using Visual Studio Code: Docker, ACA extensions
    • Using Azure CLI
    • Using CI/CD Pipelines


    Last week, @kendallroden wrote about what it means to be Cloud-Native and how Azure Container Apps provides a serverless containers platform for hosting all of your Cloud-Native applications. Today, we’ll walk through a few ways to get your apps up and running on Azure Container Apps.

    Depending on where you are in your Cloud-Native app development journey, you might choose to use different tools to deploy your apps.

    • “Right-click, publish” – Deploying an app directly from an IDE or code editor is often seen as a bad practice, but it’s one of the quickest ways to test out an app in a cloud environment.
    • Command line interface – CLIs are useful for deploying apps from a terminal. Commands can be run manually or in a script.
    • Continuous integration/deployment – To deploy production apps, the recommended approach is to automate the process in a robust CI/CD pipeline.

    Let's explore some of these options in more depth.

    Visual Studio

    Visual Studio 2022 has built-in support for deploying .NET applications to Azure Container Apps. You can use the familiar publish dialog to provision Container Apps resources and deploy to them directly. This helps you prototype an app and see it run in Azure Container Apps with the least amount of effort.

    Journey to the cloud with Azure Container Apps

    Once you’re happy with the app and it’s ready for production, Visual Studio allows you to push your code to GitHub and set up a GitHub Actions workflow to build and deploy your app every time you push changes. You can do this by checking a box.

    Journey to the cloud with Azure Container Apps

    Visual Studio Code

    There are a couple of valuable extensions that you’ll want to install if you’re working in VS Code.

    Docker extension

    The Docker extension provides commands for building a container image for your app and pushing it to a container registry. It can even do this without requiring Docker Desktop on your local machine --- the “Build image in Azure” command remotely builds and pushes a container image to Azure Container Registry.

    Journey to the cloud with Azure Container Apps

    And if your app doesn’t have a dockerfile, the extension will generate one for you.

    Journey to the cloud with Azure Container Apps

    Azure Container Apps extension

    Once you’ve built your container image and pushed it to a registry, the Azure Container Apps VS Code extension provides commands for creating a container app and deploying revisions using the image you’ve built.

    Journey to the cloud with Azure Container Apps

    Azure CLI

    The Azure CLI can be used to manage pretty much anything in Azure. For Azure Container Apps, you’ll find commands for creating, updating, and managing your Container Apps resources.

    Just like in VS Code, with a few commands in the Azure CLI, you can create your Azure resources, build and push your container image, and then deploy it to a container app.

    To make things as simple as possible, the Azure CLI also has an “az containerapp up” command. This single command takes care of everything that’s needed to turn your source code from your local machine to a cloud-hosted application in Azure Container Apps.

    az containerapp up --name myapp --source ./src

    We saw earlier that Visual Studio can generate a GitHub Actions workflow to automatically build and deploy your app on every commit. “az containerapp up” can do this too. The following adds a workflow to a repo.

    az containerapp up --name myapp --repo https://github.com/myorg/myproject

    CI/CD pipelines

    When it’s time to take your app to production, it’s strongly recommended to set up a CI/CD pipeline to automatically and repeatably build, test, and deploy it. We’ve already seen that tools such as Visual Studio and Azure CLI can automatically generate a workflow for GitHub Actions. You can set up a pipeline in Azure DevOps too. This is an example Azure DevOps pipeline.

    trigger:
    branches:
    include:
    - main

    pool:
    vmImage: ubuntu-latest

    stages:

    - stage: Build

    jobs:
    - job: build
    displayName: Build app

    steps:
    - task: Docker@2
    inputs:
    containerRegistry: 'myregistry'
    repository: 'hello-aca'
    command: 'buildAndPush'
    Dockerfile: 'hello-container-apps/Dockerfile'
    tags: '$(Build.BuildId)'

    - stage: Deploy

    jobs:
    - job: deploy
    displayName: Deploy app

    steps:
    - task: AzureCLI@2
    inputs:
    azureSubscription: 'my-subscription(5361b9d6-46ea-43c3-a898-15f14afb0db6)'
    scriptType: 'bash'
    scriptLocation: 'inlineScript'
    inlineScript: |
    # automatically install Container Apps CLI extension
    az config set extension.use_dynamic_install=yes_without_prompt

    # ensure registry is configured in container app
    az containerapp registry set \
    --name hello-aca \
    --resource-group mygroup \
    --server myregistry.azurecr.io \
    --identity system

    # update container app
    az containerapp update \
    --name hello-aca \
    --resource-group mygroup \
    --image myregistry.azurecr.io/hello-aca:$(Build.BuildId)

    Conclusion

    In this article, we looked at a few ways to deploy your Cloud-Native applications to Azure Container Apps and how to decide which one to use based on where you are in your app’s journey to the cloud.

    To learn more, visit Azure Container Apps | Microsoft Azure today!

    ASK THE EXPERT: LIVE Q&A

    The Azure Container Apps team will answer questions live on September 29.

    - - + + \ No newline at end of file diff --git a/blog/zero2hero-aca-06/index.html b/blog/zero2hero-aca-06/index.html index 31bb98faad..72f1870c1b 100644 --- a/blog/zero2hero-aca-06/index.html +++ b/blog/zero2hero-aca-06/index.html @@ -14,14 +14,14 @@ - - + +

    🚀 | Observability with ACA

    · 5 min read
    Mike Morton

    Welcome to Day 19 of #30DaysOfServerless!

    Today, we have a special set of posts from our Zero To Hero 🚀 initiative, featuring blog posts authored by our Product Engineering teams for #ServerlessSeptember. Posts were originally published on the Apps on Azure blog on Microsoft Tech Community.


    What We'll Cover

    • Log Streaming - in Azure Portal
    • Console Connect - in Azure Portal
    • Metrics - using Azure Monitor
    • Log Analytics - using Azure Monitor
    • Metric Alerts and Log Alerts - using Azure Monitor


    In past weeks, @kendallroden wrote about what it means to be Cloud-Native and @Anthony Chu the various ways to get your apps running on Azure Container Apps. Today, we will talk about the observability tools you can use to observe, debug, and diagnose your Azure Container Apps.

    Azure Container Apps provides several observability features to help you debug and diagnose your apps. There are both Azure portal and CLI options you can use to help understand the health of your apps and help identify when issues arise.

    While these features are helpful throughout your container app’s lifetime, there are two that are especially helpful. Log streaming and console connect can be a huge help in the initial stages when issues often rear their ugly head. Let's dig into both of these a little.

    Log Streaming

    Log streaming allows you to use the Azure portal to view the streaming logs from your app. You’ll see the logs written from the app to the container’s console (stderr and stdout). If your app is running multiple revisions, you can choose from which revision to view logs. You can also select a specific replica if your app is configured to scale. Lastly, you can choose from which container to view the log output. This is useful when you are running a custom or Dapr sidecar container. view streaming logs

    Here’s an example CLI command to view the logs of a container app.

    az containerapp logs show -n MyContainerapp -g MyResourceGroup

    You can find more information about the different options in our CLI docs.

    Console Connect

    In the Azure portal, you can connect to the console of a container in your app. Like log streaming, you can select the revision, replica, and container if applicable. After connecting to the console of the container, you can execute shell commands and utilities that you have installed in your container. You can view files and their contents, monitor processes, and perform other debugging tasks.

    This can be great for checking configuration files or even modifying a setting or library your container is using. Of course, updating a container in this fashion is not something you should do to a production app, but tweaking and re-testing an app in a non-production environment can speed up development.

    Here’s an example CLI command to connect to the console of a container app.

    az containerapp exec -n MyContainerapp -g MyResourceGroup

    You can find more information about the different options in our CLI docs.

    Metrics

    Azure Monitor collects metric data from your container app at regular intervals to help you gain insights into the performance and health of your container app. Container apps provide these metrics:

    • CPU usage
    • Memory working set bytes
    • Network in bytes
    • Network out bytes
    • Requests
    • Replica count
    • Replica restart count

    Here you can see the metrics explorer showing the replica count for an app as it scaled from one replica to fifteen, and then back down to one.

    You can also retrieve metric data through the Azure CLI.

    Log Analytics

    Azure Monitor Log Analytics is great for viewing your historical logs emitted from your container apps. There are two custom tables of interest, the ContainerAppConsoleLogs_CL which contains all the log messages written by your app (stdout and stderr), and the ContainerAppSystemLogs_CL which contain the system messages from the Azure Container Apps service.

    You can also query Log Analytics through the Azure CLI.

    Alerts

    Azure Monitor alerts notify you so that you can respond quickly to critical issues. There are two types of alerts that you can define:

    You can create alert rules from metric charts in the metric explorer and from queries in Log Analytics. You can also define and manage alerts from the Monitor|Alerts page.

    Here is what creating an alert looks like in the Azure portal. In this case we are setting an alert rule from the metric explorer to trigger an alert if the replica restart count for a specific container app is greater than two within the last fifteen minutes.

    To learn more about alerts, refer to Overview of alerts in Microsoft Azure.

    Conclusion

    In this article, we looked at the several ways to observe, debug, and diagnose your Azure Container Apps. As you can see there are rich portal tools and a complete set of CLI commands to use. All the tools are helpful throughout the lifecycle of your app, be sure to take advantage of them when having an issue and/or to prevent issues.

    To learn more, visit Azure Container Apps | Microsoft Azure today!

    ASK THE EXPERT: LIVE Q&A

    The Azure Container Apps team will answer questions live on September 29.

    - - + + \ No newline at end of file diff --git a/blog/zero2hero-func-02/index.html b/blog/zero2hero-func-02/index.html index 9a8420b825..32b80f0fe3 100644 --- a/blog/zero2hero-func-02/index.html +++ b/blog/zero2hero-func-02/index.html @@ -14,15 +14,15 @@ - - + +

    🚀 | Durable Entities Walkthrough

    · 8 min read
    David Justo

    Welcome to Day 6 of #30DaysOfServerless!

    Today, we have a special set of posts from our Zero To Hero 🚀 initiative, featuring blog posts authored by our Product Engineering teams for #ServerlessSeptember. Posts were originally published on the Apps on Azure blog on Microsoft Tech Community.


    What We'll Cover

    • What are Durable Entities
    • Some Background
    • A Programming Model
    • Entities for a Micro-Blogging Platform


    Durable Entities are a special type of Azure Function that allow you to implement stateful objects in a serverless environment. They make it easy to introduce stateful components to your app without needing to manually persist data to external storage, so you can focus on your business logic. We’ll demonstrate their power with a real-life example in the last section.

    Entities 101: Some Background

    Programming Durable Entities feels a lot like object-oriented programming, except that these “objects” exist in a distributed system. Like objects, each Entity instance has a unique identifier, i.e. an entity ID that can be used to read and manipulate their internal state. Entities define a list of operations that constrain how their internal state is managed, like an object interface.

    Some experienced readers may realize that Entities sound a lot like an implementation of the Actor Pattern. For a discussion of the relationship between Entities and Actors, please refer to this documentation.

    Entities are a part of the Durable Functions Extension, an extension of Azure Functions that empowers programmers with stateful abstractions for serverless, such as Orchestrations (i.e. workflows).

    Durable Functions is available in most Azure Functions runtime environments: .NET, Node.js, Python, PowerShell, and Java (preview). For this article, we’ll focus on the C# experience, but note that Entities are also available in Node.js and Python; their availability in other languages is underway.

    Entities 102: The programming model

    Imagine you want to implement a simple Entity that just counts things. Its interface allows you to get the current count, add to the current count, and to reset the count to zero.

    If you implement this in an object-oriented way, you’d probably define a class (say “Counter”) with a method to get the current count (say “Counter.Get”), another to add to the count (say “Counter.Add”), and another to reset the count (say “Counter.Reset”). Well, the implementation of an Entity in C# is not that different from this sketch:

    [JsonObject(MemberSerialization.OptIn)] 
    public class Counter
    {
    [JsonProperty("value")]
    public int Value { get; set; }

    public void Add(int amount)
    {
    this.Value += amount;
    }

    public Task Reset()
    {
    this.Value = 0;
    return Task.CompletedTask;
    }

    public Task<int> Get()
    {
    return Task.FromResult(this.Value);
    }
    [FunctionName(nameof(Counter))]
    public static Task Run([EntityTrigger] IDurableEntityContext ctx)
    => ctx.DispatchAsync<Counter>();

    }

    We’ve defined a class named Counter, with an internal count stored in the variable “Value” which is manipulated through the “Add” and “Reset” methods, and which can be read via “Get”.

    The “Run” method is simply boilerplate required for the Azure Functions framework to interact with the object we’ve defined – it’s the method that the framework calls internally when it needs to load the Entity object. When DispatchAsync is called, the Entity and its corresponded state (the last count in “Value”) is loaded from storage. Again, this is mostly just boilerplate: your Entity’s business logic lies in the rest of the class.

    Finally, the Json annotation on top of the class and the Value field tells the Durable Functions framework that the “Value” field is to be durably persisted as part of the durable state on each Entity invocation. If you were to annotate other class variables with JsonProperty, they would also become part of the managed state.

    Entities for a micro-blogging platform

    We’ll try to implement a simple micro-blogging platform, a la Twitter. Let’s call it “Chirper”. In Chirper, users write chirps (i.e tweets), they can follow, and unfollow other users, and they can read the chirps of users they follow.

    Defining Entity

    Just like in OOP, it’s useful to begin by identifying what are the stateful agents of this scenario. In this case, users have state (who they follow and their chirps), and chirps have state in the form of their content. So, we could model these stateful agents as Entities!

    Below is a potential way to implement a User for Chirper as an Entity:

      [JsonObject(MemberSerialization = MemberSerialization.OptIn)] 
    public class User: IUser
    {
    [JsonProperty]
    public List<string> FollowedUsers { get; set; } = new List<string>();

    public void Add(string user)
    {
    FollowedUsers.Add(user);
    }

    public void Remove(string user)
    {
    FollowedUsers.Remove(user);
    }

    public Task<List<string>> Get()
    {
    return Task.FromResult(FollowedUsers);
    }
    // note: removed boilerplate “Run” method, for conciseness.
    }

    In this case, our Entity’s internal state is stored in “FollowedUsers” which is an array of accounts followed by this user. The operations exposed by this entity allow clients to read and modify this data: it can be read by “Get”, a new follower can be added via “Add”, and a user can be unfollowed via “Remove”.

    With that, we’ve modeled a Chirper’s user as an Entity! Recall that Entity instances each has a unique ID, so we can consider that unique ID to correspond to a specific user account.

    What about chirps? Should we represent them as Entities as well? That would certainly be valid. However, we would then need to create a mapping between an entity ID and every chirp entity ID that this user wrote.

    For demonstration purposes, a simpler approach would be to create an Entity that stores the list of all chirps authored by a given user; call it UserChirps. Then, we could fix each User Entity to share the same entity ID as its corresponding UserChirps Entity, making client operations easier.

    Below is a simple implementation of UserChirps:

      [JsonObject(MemberSerialization = MemberSerialization.OptIn)] 
    public class UserChirps : IUserChirps
    {
    [JsonProperty]
    public List<Chirp> Chirps { get; set; } = new List<Chirp>();

    public void Add(Chirp chirp)
    {
    Chirps.Add(chirp);
    }

    public void Remove(DateTime timestamp)
    {
    Chirps.RemoveAll(chirp => chirp.Timestamp == timestamp);
    }

    public Task<List<Chirp>> Get()
    {
    return Task.FromResult(Chirps);
    }

    // Omitted boilerplate “Run” function
    }

    Here, our state is stored in Chirps, a list of user posts. Our operations are the same as before: Get, Read, and Add. It’s the same pattern as before, but we’re representing different data.

    To put it all together, let’s set up Entity clients to generate and manipulate these Entities according to some REST API.

    Interacting with Entity

    Before going there, let’s talk briefly about how you can interact with an Entity. Entity interactions take one of two forms -- calls and signals:

    Calling an entity is a two-way communication. You send an operation message to the entity and then wait for the response message before you continue. The response can be a result value or an error. Signaling an entity is a one-way (fire-and-forget) communication. You send an operation message but don’t wait for a response. You have the reassurance that the message will be delivered eventually, but you don’t know when and don’t know what the response is. For example, when you read the state of an Entity, you are performing a “call” interaction. When you record that a user has followed another, you may choose to simply signal it.

    Now say user with a given userId (say “durableFan99” ) wants to post a chirp. For this, you can write an HTTP endpoint to signal the UserChips entity to record that chirp. We can leverage the HTTP Trigger functionality from Azure Functions and pair it with an entity client binding that signals the Add operation of our Chirp Entity:

    [FunctionName("UserChirpsPost")] 
    public static async Task<HttpResponseMessage> UserChirpsPost(
    [HttpTrigger(AuthorizationLevel.Function, "post", Route = "user/{userId}/chirps")]
    HttpRequestMessage req,
    DurableClient] IDurableClient client,
    ILogger log,
    string userId)
    {
    Authenticate(req, userId);
    var chirp = new Chirp()
    {
    UserId = userId,
    Timestamp = DateTime.UtcNow,
    Content = await req.Content.ReadAsStringAsync(),
    };
    await client.SignalEntityAsync<IUserChirps>(userId, x => x.Add(chirp));
    return req.CreateResponse(HttpStatusCode.Accepted, chirp);
    }

    Following the same pattern as above, to get all the chirps from a user, you could read the status of your Entity via ReadEntityStateAsync, which follows the call-interaction pattern as your client expects a response:

    [FunctionName("UserChirpsGet")] 
    public static async Task<HttpResponseMessage> UserChirpsGet(
    [HttpTrigger(AuthorizationLevel.Function, "get", Route = "user/{userId}/chirps")] HttpRequestMessage req,
    [DurableClient] IDurableClient client,
    ILogger log,
    string userId)
    {

    Authenticate(req, userId);
    var target = new EntityId(nameof(UserChirps), userId);
    var chirps = await client.ReadEntityStateAsync<UserChirps>(target);
    return chirps.EntityExists
    ? req.CreateResponse(HttpStatusCode.OK, chirps.EntityState.Chirps)
    : req.CreateResponse(HttpStatusCode.NotFound);
    }

    And there you have it! To play with a complete implementation of Chirper, you can try out our sample in the Durable Functions extension repo.

    Thank you!

    info

    Thanks for following along, and we hope you find Entities as useful as we do! If you have questions or feedback, please file issues in the repo above or tag us @AzureFunctions on Twitter

    - - + + \ No newline at end of file diff --git a/blog/zero2hero-func-03/index.html b/blog/zero2hero-func-03/index.html index 4a27eed52b..f79f2b9fc1 100644 --- a/blog/zero2hero-func-03/index.html +++ b/blog/zero2hero-func-03/index.html @@ -14,13 +14,13 @@ - - + +

    🚀 | Use Custom Handlers For Go

    · 6 min read
    Melony Qin

    Welcome to Day 12 of #30DaysOfServerless!

    Today, we have a special set of posts from our Zero To Hero 🚀 initiative, featuring blog posts authored by our Product Engineering teams for #ServerlessSeptember. Posts were originally published on the Apps on Azure blog on Microsoft Tech Community.


    What We'll Cover

    • What are Custom Handlers, and why use them?
    • How Custom Handler Works
    • Message Processing With Azure Custom Handler
    • Azure Portal Monitoring


    If you have been working with Azure Functions for a while, you may know Azure Functions is a serverless FaaS (Function as a Service) offered provided by Microsoft Azure, which is built for your key scenarios, including building web APIs, processing file uploads, responding to database changes, processing IoT data streams, managing message queues, and more.

    Custom Handlers: What and Why

    Azure functions support multiple programming languages including C#, F#, Java, JavaScript, Python, typescript, and PowerShell. If you want to get extended language support with Azure functions for other languages such as Go, and Rust, that’s where custom handler comes in.

    An Azure function custom handler allows the use of any language that supports HTTP primitives and author Azure functions. With custom handlers, you can use triggers and input and output bindings via extension bundles, hence it supports all the triggers and bindings you're used to with Azure functions.

    How a Custom Handler Works

    Let’s take a look at custom handlers and how it works.

    • A request is sent to the function host when an event is triggered. It’s up to the function host to issue a request payload to the custom handler, which holds the trigger and inputs binding data as well as other metadata for the function.
    • The custom handler is a local HTTP web server. It executes the function code and returns a response payload to the Functions host.
    • The Functions host passes data from the response to the function's output bindings which will be passed to the downstream stream services for data processing.

    Check out this article to know more about Azure functions custom handlers.


    Message processing with Custom Handlers

    Message processing is one of the key scenarios that Azure functions are trying to address. In the message-processing scenario, events are often collected in queues. These events can trigger Azure functions to execute a piece of business logic.

    You can use the Service Bus trigger to respond to messages from an Azure Service Bus queue - it's then up to the Azure functions custom handlers to take further actions to process the messages. The process is described in the following diagram:

    Building Serverless Go Applications with Azure functions custom handlers

    In Azure function, the function.json defines the function's trigger, input and output bindings, and other configuration settings. Note that every function can have multiple bindings, but it can only have one trigger. The following is an example of setting up the Service Bus queue trigger in the function.json file :

    {
    "bindings": [
    {
    "name": "queueItem",
    "type": "serviceBusTrigger",
    "direction": "in",
    "queueName": "functionqueue",
    "connection": "ServiceBusConnection"
    }
    ]
    }

    You can add a binding definition in the function.json to write the output to a database or other locations of your desire. Supported bindings can be found here.

    As we’re programming in Go, so we need to set the value of defaultExecutablePath to handler in the customHandler.description section in the host.json file.

    Assume we’re programming in Windows OS, and we have named our go application as server.go, after we executed go build server.go command, it produces an executable called server.exe. So here we set server.exe in the host.json as the following example :

      "customHandler": {
    "description": {
    "defaultExecutablePath": "./server.exe",
    "workingDirectory": "",
    "arguments": []
    }
    }

    We’re showcasing a simple Go application here with Azure functions custom handlers where we print out the messages received from the function host. The following is the full code of server.go application :

    package main

    import (
    "encoding/json"
    "fmt"
    "log"
    "net/http"
    "os"
    )

    type InvokeRequest struct {
    Data map[string]json.RawMessage
    Metadata map[string]interface{}
    }

    func queueHandler(w http.ResponseWriter, r *http.Request) {
    var invokeRequest InvokeRequest

    d := json.NewDecoder(r.Body)
    d.Decode(&invokeRequest)

    var parsedMessage string
    json.Unmarshal(invokeRequest.Data["queueItem"], &parsedMessage)

    fmt.Println(parsedMessage)
    }

    func main() {
    customHandlerPort, exists := os.LookupEnv("FUNCTIONS_CUSTOMHANDLER_PORT")
    if !exists {
    customHandlerPort = "8080"
    }
    mux := http.NewServeMux()
    mux.HandleFunc("/MessageProcessorFunction", queueHandler)
    fmt.Println("Go server Listening on: ", customHandlerPort)
    log.Fatal(http.ListenAndServe(":"+customHandlerPort, mux))

    }

    Ensure you have Azure functions core tools installed, then we can use func start command to start our function. Then we’ll have have a C#-based Message Sender application on Github to send out 3000 messages to the Azure service bus queue. You’ll see Azure functions instantly start to process the messages and print out the message as the following:

    Monitoring Serverless Go Applications with Azure functions custom handlers


    Azure portal monitoring

    Let’s go back to Azure portal portal the events see how those messages in Azure Service Bus queue were being processed. There was 3000 messages were queued in the Service Bus queue ( the Blue line stands for incoming Messages ). The outgoing messages (the red line in smaller wave shape ) showing there are progressively being read by Azure functions as the following :

    Monitoring Serverless Go Applications with Azure functions custom handlers

    Check out this article about monitoring Azure Service bus for further information.

    Next steps

    Thanks for following along, we’re looking forward to hearing your feedback. Also, if you discover potential issues, please record them on Azure Functions host GitHub repository or tag us @AzureFunctions on Twitter.

    RESOURCES

    Start to build your serverless applications with custom handlers, check out the official documentation:

    Life is a journey of learning. Let’s stay tuned!

    - - + + \ No newline at end of file diff --git a/blog/zero2hero-func-05/index.html b/blog/zero2hero-func-05/index.html index 1d6fff7156..a0ecfc6ee6 100644 --- a/blog/zero2hero-func-05/index.html +++ b/blog/zero2hero-func-05/index.html @@ -14,13 +14,13 @@ - - + +

    🚀 | Error Handling w/ Apache Kafka

    · 6 min read
    Ramya Oruganti

    Welcome to Day 19 of #30DaysOfServerless!

    Today, we have a special set of posts from our Zero To Hero 🚀 initiative, featuring blog posts authored by our Product Engineering teams for #ServerlessSeptember. Posts were originally published on the Apps on Azure blog on Microsoft Tech Community.


    What We'll Cover

    • Retry Policy Support - in Apache Kafka Extension
    • AutoOffsetReset property - in Apache Kafka Extension
    • Key support for Kafka messages - in Apache Kafka Extension
    • References: Apache Kafka Extension for Azure Functions


    Recently we launched the Apache Kafka extension for Azure functions in GA with some cool new features like deserialization of Avro Generic records and Kafka headers support. We received great responses - so we're back with more updates!

    Retry Policy support

    Handling errors in Azure Functions is important to avoid data loss or miss events or monitor the health of an application. Apache Kafka Extension for Azure Functions supports retry policy which tells the runtime to rerun a failed execution until either successful completion occurs or the maximum number of retries is reached.

    A retry policy is evaluated when a trigger function raises an uncaught exception. As a best practice, you should catch all exceptions in your code and rethrow any errors that you want to result in a retry.

    There are two retry strategies supported by policy that you can configure :- fixed delay and exponential backoff

    1. Fixed Delay - A specified amount of time is allowed to elapse between each retry.
    2. Exponential Backoff - The first retry waits for the minimum delay. On subsequent retries, time is added exponentially to the initial duration for each retry, until the maximum delay is reached. Exponential back-off adds some small randomization to delays to stagger retries in high-throughput scenarios.
    Please Note

    Retry Policy for Kafka extension is NOT supported for C# (in proc and out proc) trigger and output binding. This is supported for languages like Java, Node (JS , TypeScript), PowerShell and Python trigger and output bindings.

    Here is the sample code view of exponential backoff retry strategy

    Error Handling with Apache Kafka extension for Azure Functions

    AutoOffsetReset property

    AutoOffsetReset property enables customers to configure the behaviour in the absence of an initial offset. Imagine a scenario when there is a need to change consumer group name. The consumer connected using a new consumer group had to reprocess all events starting from the oldest (earliest) one, as this was the default one and this setting wasn’t exposed as configurable option in the Apache Kafka extension for Azure Functions(previously). With the help of this kafka setting you can configure on how to start processing events for newly created consumer groups.

    Due to lack of the ability to configure this setting, offset commit errors were causing topics to restart from earliest offset· Users were looking to be able to set offset setting to either latest or earliest based on their requirements.

    We are happy to share that we have enabled the AutoOffsetReset setting as a configurable one to either - Earliest(Default) and Latest. Setting the value to Earliest configures the consumption of the messages from the the earliest/smallest offset or beginning of the topic partition. Setting the property to Latest configures the consumption of the messages from the latest/largest offset or from the end of the topic partition. This is supported for all the Azure Functions supported languages (C# (in & out), Java, Node (JS and TypeScript), PowerShell and python) and can be used for both triggers and output binding

    Error Handling with Apache Kafka extension for Azure Functions

    Key support for Kafka messages

    With keys the producer/output binding can be mapped to broker and partition to write based on the message. So alongside the message value, we can choose to send a message key and that key can be whatever you want it could be a string, it could be a number . In case you don’t send the key, the key is set to null then the data will be sent in a Round Robin fashion to make it very simple. But in case you send a key with your message, all the messages that share the same key will always go to the same partition and thus you can enable grouping of similar messages into partitions

    Previously while consuming a Kafka event message using the Azure Function kafka extension, the event key was always none although the key was present in the event message.

    Key support was implemented in the extension which enables customers to set/view key in the Kafka event messages coming in to the kafka trigger and set keys to the messages going in to kafka topics (with keys set) through output binding. Therefore key support was enabled in the extension to support both trigger and output binding for all Azure Functions supported languages ( (C# (in & out), Java, Node (JS and TypeScript), PowerShell and python)

    Here is the view of an output binding producer code where Kafka messages are being set with key

    Error Handling with Apache Kafka extension for Azure Functions

    Conclusion:

    In this article you have learnt about the latest additions to the Apache Kafka extension for Azure Functions. Incase you have been waiting for these features to get released or need them you are all set and please go head and try them out!! They are available in the latest extension bundles

    Want to learn more?

    Please refer to Apache Kafka bindings for Azure Functions | Microsoft Docs for detail documentation, samples on the Azure function supported languages and more!

    References

    FEEDBACK WELCOME

    Keep in touch with us on Twitter via @AzureFunctions.

    - - + + \ No newline at end of file diff --git a/blog/zero2hero-func-07/index.html b/blog/zero2hero-func-07/index.html index d4a5dc8d25..c2fc05a1dc 100644 --- a/blog/zero2hero-func-07/index.html +++ b/blog/zero2hero-func-07/index.html @@ -14,14 +14,14 @@ - - + +

    🚀 | Monitor + Troubleshoot Apps

    · 5 min read
    Madhura Bharadwaj

    Welcome to Day 26 of #30DaysOfServerless!

    Today, we have a special set of posts from our Zero To Hero 🚀 initiative, featuring blog posts authored by our Product Engineering teams for #ServerlessSeptember. Posts were originally published on the Apps on Azure blog on Microsoft Tech Community.


    What We'll Cover

    • Monitoring your Azure Functions
    • Built-in log streaming
    • Live Metrics stream
    • Troubleshooting Azure Functions


    Monitoring your Azure Functions:

    Azure Functions uses Application Insights to collect and analyze log data from individual function executions in your function app.

    Using Application Insights

    Application Insights collects log, performance, and error data. By automatically detecting performance anomalies and featuring powerful analytics tools, you can more easily diagnose issues and better understand how your functions are used. These tools are designed to help you continuously improve performance and usability of your functions. You can even use Application Insights during local function app project development.

    Typically, you create an Application Insights instance when you create your function app. In this case, the instrumentation key required for the integration is already set as an application setting named APPINSIGHTS_INSTRUMENTATIONKEY. With Application Insights integration enabled, telemetry data is sent to your connected Application Insights instance. This data includes logs generated by the Functions host, traces written from your functions code, and performance data. In addition to data from your functions and the Functions host, you can also collect data from the Functions scale controller.

    By default, the data collected from your function app is stored in Application Insights. In the Azure portal, Application Insights provides an extensive set of visualizations of your telemetry data. You can drill into error logs and query events and metrics. To learn more, including basic examples of how to view and query your collected data, see Analyze Azure Functions telemetry in Application Insights.

    Using Log Streaming

    In addition to this, you can have a smoother debugging experience through log streaming. There are two ways to view a stream of log files being generated by your function executions.

    • Built-in log streaming: the App Service platform lets you view a stream of your application log files. This is equivalent to the output seen when you debug your functions during local development and when you use the Test tab in the portal. All log-based information is displayed. For more information, see Stream logs. This streaming method supports only a single instance and can't be used with an app running on Linux in a Consumption plan.
    • Live Metrics Stream: when your function app is connected to Application Insights, you can view log data and other metrics in near real-time in the Azure portal using Live Metrics Stream. Use this method when monitoring functions running on multiple-instances or on Linux in a Consumption plan. This method uses sampled data. Log streams can be viewed both in the portal and in most local development environments.
    Monitoring Azure Functions

    Learn how to configure monitoring for your Azure Functions. See Monitoring Azure Functions data reference for detailed information on the metrics and logs metrics created by Azure Functions.

    In addition to this, Azure Functions uses Azure Monitor to monitor the health of your function apps. Azure Functions collects the same kinds of monitoring data as other Azure resources that are described in Azure Monitor data collection. See Monitoring Azure Functions data reference for detailed information on the metrics and logs metrics created by Azure Functions.

    Troubleshooting your Azure Functions:

    When you do run into issues with your function app, Azure Functions diagnostics points out what’s wrong. It guides you to the right information to troubleshoot and resolve the issue more easily and quickly.

    Let’s explore how to use Azure Functions diagnostics to diagnose and solve common function app issues.

    1. Navigate to your function app in the Azure portal.
    2. Select Diagnose and solve problems to open Azure Functions diagnostics.
    3. Once you’re here, there are multiple ways to retrieve the information you’re looking for. Choose a category that best describes the issue of your function app by using the keywords in the homepage tile. You can also type a keyword that best describes your issue in the search bar. There’s also a section at the bottom of the page that will directly take you to some of the more popular troubleshooting tools. For example, you could type execution to see a list of diagnostic reports related to your function app execution and open them directly from the homepage.

    Monitoring and troubleshooting apps in Azure Functions

    1. For example, click on the Function App Down or Reporting Errors link under Popular troubleshooting tools section. You will find detailed analysis, insights and next steps for the issues that were detected. On the left you’ll see a list of detectors. Click on them to explore more, or if there’s a particular keyword you want to look for, type it Into the search bar on the top.

    Monitoring and troubleshooting apps in Azure Functions

    TROUBLESHOOTING TIP

    Here are some general troubleshooting tips that you can follow if you find your Function App throwing Azure Functions Runtime unreachable error.

    Also be sure to check out the recommended best practices to ensure your Azure Functions are highly reliable. This article details some best practices for designing and deploying efficient function apps that remain healthy and perform well in a cloud-based environment.

    Bonus tip:

    - - + + \ No newline at end of file diff --git a/calendar/index.html b/calendar/index.html index 92b925e097..174739de5e 100644 --- a/calendar/index.html +++ b/calendar/index.html @@ -14,13 +14,13 @@ - - + +

    #ServerlessSeptember Calendar

    Look for these icons, for signature activities.


    Upcoming

    Check this section for links to upcoming activities for #Serverless September.

    WhenWhatWhere
    Sep 01✍🏽 Kickoff: #30DaysOfServerless, 4 Themed WeeksWebsite
    Sep 01🎯 Cloud Skills Challenge: Register NowMicrosoft Learn
    Sep 02✍🏽 Week 1: Functions-As-A-ServiceWebsite
    Sep 05🚀 A walkthrough of Durable EntitiesApps On Azure
    Sep 05🚀 Go Cloud-Native With Azure Container AppsApps On Azure
    Sep 07🏆 Hacks: How to get into Tech And Serverless YouTube
    Sep 09✍🏽 Week 2: Containers & MicroservicesWebsite
    Sep 12🚀 Journey to the cloud with Azure Container AppsApps On Azure
    Sep 12🚀 Building Serverless Go Applications with Azure functions custom handlersApps On Azure
    Sep 14🏆 Hacks: How to DevOps and Serverless the Right Way🌟 Register Now
    Sep 15🎤 ATE: Azure Functions Live Q&A with Product Team🌟 Register Now
    Sep 15🚀 Error Handling with Kafka extension - and what's new with the Kafka triggerApps On Azure
    Sep 16Containers, Serverless & IoT Meetup - In-Person, Online🌟 Register Now
    Sep 16✍🏽 Week 3: Serverless IntegrationsWebsite
    Sep 19🚀 Azure Container Apps observabilityApps On Azure
    Sep 21🏆 Hacks: The Serverless Project that Got Me Promoted!🌟 Register Now
    Sep 23✍🏽 Week 4: Serverless End-to-EndWebsite
    Sep 23#Learnathon - Azure Functions - In Person, Online🌟 Register Now
    Sep 26🚀 How to monitor and troubleshoot applications in Azure FunctionsApps On Azure
    Sep 26🚀 End-to-End solution development with codeApps on Azure
    Sep 28Webinar: Java Azure Functions with Kafka🌟 Register Now
    Sep 28🏆 Hacks: So you want to migrate your project to Serverless?🌟 Register Now
    Sep 29🎤 ATE: Azure Container Apps Live Q&A with Product Team🌟 Register Here
    Sep 29Serverless Meetup: #SamosaChai.NET = Livestream🌟 Register Now
    Sep 30🎯 Cloud Skills Challenge: Last Day To Complete It!Microsoft Learn

    Archive

    Check this section once events or content deadlines are past, to get links to published posts or recordings, to catch up on what you missed.

    - - + + \ No newline at end of file diff --git a/cnny-2023/Kubernetes-101/index.html b/cnny-2023/Kubernetes-101/index.html index 02b29acc0a..ba72a68d46 100644 --- a/cnny-2023/Kubernetes-101/index.html +++ b/cnny-2023/Kubernetes-101/index.html @@ -14,14 +14,14 @@ - - + +

    1-3. Kubernetes 101

    · 3 min read
    Steven Murawski

    Welcome to Day 3 of Week 1 of #CloudNativeNewYear!

    This week we'll focus on what Kubernetes is.

    What We'll Cover

    • Introduction
    • What is Kubernetes? (Video)
    • How does Kubernetes Work? (Video)
    • Conclusion


    REGISTER & LEARN: KUBERNETES 101

    Interested in a dive into Kubernetes and a chance to talk to experts?

    🎙: Join us Jan 26 @1pm PST by registering here

    Here's what you will learn:

    • Key concepts and core principles of Kubernetes.
    • How to deploy, scale and manage containerized workloads.
    • Live Demo of the concepts explained
    • How to get started with Azure Kubernetes Service for free.

    Start your free Azure Kubernetes Trial Today!!: aka.ms/TryAKS

    Introduction

    Kubernetes is an open source container orchestration engine that can help with automated deployment, scaling, and management of our applications.

    Kubernetes takes physical (or virtual) resources and provides a consistent API over them, bringing a consistency to the management and runtime experience for our applications. Kubernetes provides us with a number of capabilities such as:

    • Container scheduling
    • Service discovery and load balancing
    • Storage orchestration
    • Automated rollouts and rollbacks
    • Automatic bin packing
    • Self-healing
    • Secret and configuration management

    We'll learn more about most of these topics as we progress through Cloud Native New Year.

    What is Kubernetes?

    Let's hear from Brendan Burns, one of the founders of Kubernetes as to what Kubernetes actually is.

    How does Kubernetes Work?

    And Brendan shares a bit more with us about how Kubernetes works.

    Conclusion

    Kubernetes allows us to deploy and manage our applications effectively and consistently.

    By providing a consistent API across many of the concerns our applications have, like load balancing, networking, storage, and compute, Kubernetes improves both our ability to build and ship new software.

    There are standards for the applications to depend on for resources needed. Deployments, metrics, and logs are provided in a standardized fashion allowing more effecient operations across our application environments.

    And since Kubernetes is an open source platform, it can be found in just about every type of operating environment - cloud, virtual machines, physical hardware, shared data centers, even small devices like Rasberry Pi's!

    Want to learn more? Join us for a webinar on Kubernetes Concepts (or catch the playback) on Thursday, January 26th at 1 PM PST and watch for the rest of this series right here!

    - - + + \ No newline at end of file diff --git a/cnny-2023/aks-extensions-addons/index.html b/cnny-2023/aks-extensions-addons/index.html index 1e3c9a76de..3039ce71ab 100644 --- a/cnny-2023/aks-extensions-addons/index.html +++ b/cnny-2023/aks-extensions-addons/index.html @@ -14,13 +14,13 @@ - - + +

    4-4. Azure Kubernetes Services Add-ons and Extensions

    · 4 min read
    Jorge Arteiro

    Welcome to Day 4 of Week 4 of #CloudNativeNewYear!

    The theme for this week is going further with Cloud Native. Yesterday we talked about Windows Containers. Today we'll explore addons and extensions available to Azure Kubernetes Services (AKS).

    What We'll Cover

    • Introduction
    • Add-ons
    • Extensions
    • Add-ons vs Extensions
    • Resources

    Introduction

    Azure Kubernetes Service (AKS) is a fully managed container orchestration service that makes it easy to deploy and manage containerized applications on Azure. AKS offers a number of features and capabilities, including the ability to extend its supported functionality through the use of add-ons and extensions.

    There are also integrations available from open-source projects and third parties, but they are not covered by the AKS support policy.

    Add-ons

    Add-ons provide a supported way to extend AKS. Installation, configuration and lifecycle are managed by AKS following pre-determine updates rules.

    As an example, let's enable Container Insights with the monitoring addon. on an existing AKS cluster using az aks enable-addons --addons CLI command

    az aks enable-addons \
    --name MyManagedCluster \
    --resource-group MyResourceGroup \
    --addons monitoring

    or you can use az aks create --enable-addons when creating new clusters

    az aks create \
    --name MyManagedCluster \
    --resource-group MyResourceGroup \
    --enable-addons monitoring

    The current available add-ons are:

    1. http_application_routing - Configure ingress with automatic public DNS name creation. Only recommended for development.
    2. monitoring - Container Insights monitoring.
    3. virtual-node - CNCF virtual nodes open source project.
    4. azure-policy - Azure Policy for AKS.
    5. ingress-appgw - Application Gateway Ingress Controller (AGIC).
    6. open-service-mesh - CNCF Open Service Mesh project.
    7. azure-keyvault-secrets-provider - Azure Key Vault Secrets Provider for Secret Store CSI Driver.
    8. web_application_routing - Managed NGINX ingress Controller.
    9. keda - CNCF Event-driven autoscaling project.

    For more details, get the updated list of AKS Add-ons here

    Extensions

    Cluster Extensions uses Helm charts and integrates with Azure Resource Manager (ARM) to provide installation and lifecycle management of capabilities on top of AKS.

    Extensions can be auto upgraded using minor versions, but it requires extra management and configuration. Using Scope parameter, it can be installed on the whole cluster or per namespace.

    AKS Extensions requires an Azure CLI extension to be installed. To add or update this CLI extension use the following commands:

    az extension add --name k8s-extension

    and to update an existing extension

    az extension update --name k8s-extension

    There are only 3 available extensions:

    1. Dapr - CNCF Dapr project.
    2. Azure ML - Integrate Azure Machine Learning with AKS to train, inference and manage ML models.
    3. Flux (GitOps) - CNCF Flux project integrated with AKS to enable cluster configuration and application deployment using GitOps.

    As an example, you can install Azure ML using the following command:

    az k8s-extension create \
    --name aml-compute --extension-type Microsoft.AzureML.Kubernetes \
    --scope cluster --cluster-name <clusterName> \
    --resource-group <resourceGroupName> \
    --cluster-type managedClusters \
    --configuration-settings enableInference=True allowInsecureConnections=True

    For more details, get the updated list of AKS Extensions here

    Add-ons vs Extensions

    AKS Add-ons brings an advantage of been fully managed by AKS itself, and AKS Extensions are more flexible and configurable but requires extra level of management.

    Add-ons are part of the AKS resource provider in the Azure API, and AKS Extensions are a separate resource provider on the Azure API.

    Resources

    It's not too late to sign up for and complete the Cloud Skills Challenge!
    - - + + \ No newline at end of file diff --git a/cnny-2023/archive/index.html b/cnny-2023/archive/index.html index d8a5cbcf01..32b891d4ba 100644 --- a/cnny-2023/archive/index.html +++ b/cnny-2023/archive/index.html @@ -14,13 +14,13 @@ - - + +
    - - + + \ No newline at end of file diff --git a/cnny-2023/bring-your-app-day-1/index.html b/cnny-2023/bring-your-app-day-1/index.html index 5b32fde4e0..fbeca260c2 100644 --- a/cnny-2023/bring-your-app-day-1/index.html +++ b/cnny-2023/bring-your-app-day-1/index.html @@ -14,15 +14,15 @@ - - + +

    3-1. Bringing Your Application to Kubernetes - CI/CD

    · 14 min read
    Steven Murawski

    Welcome to Day 1 of Week 3 of #CloudNativeNewYear!

    The theme for this week is Bringing Your Application to Kubernetes. Last we talked about Kubernetes Fundamentals. Today we'll explore getting an existing application running in Kubernetes with a full pipeline in GitHub Actions.

    Ask the Experts Thursday, February 9th at 9 AM PST
    Friday, February 10th at 11 AM PST

    Watch the recorded demo and conversation about this week's topics.

    We were live on YouTube walking through today's (and the rest of this week's) demos. Join us Friday, February 10th and bring your questions!

    What We'll Cover

    • Our Application
    • Adding Some Infrastructure as Code
    • Building and Publishing a Container Image
    • Deploying to Kubernetes
    • Summary
    • Resources

    Our Application

    This week we'll be taking an exisiting application - something similar to a typical line of business application - and setting it up to run in Kubernetes. Over the course of the week, we'll address different concerns. Today we'll focus on updating our CI/CD process to handle standing up (or validating that we have) an Azure Kubernetes Service (AKS) environment, building and publishing container images for our web site and API server, and getting those services running in Kubernetes.

    The application we'll be starting with is eShopOnWeb. This application has a web site and API which are backed by a SQL Server instance. It's built in .NET 7, so it's cross-platform.

    info

    For the enterprising among you, you may notice that there are a number of different eShopOn* variants on GitHub, including eShopOnContainers. We aren't using that example as it's more of an end state than a starting place. Afterwards, feel free to check out that example as what this solution could look like as a series of microservices.

    Adding Some Infrastructure as Code

    Just like last week, we need to stand up an AKS environment. This week, however, rather than running commands in our own shell, we'll set up GitHub Actions to do that for us.

    There is a LOT of plumbing in this section, but once it's set up, it'll make our lives a lot easier. This section ensures that we have an environment to deploy our application into configured the way we want. We can easily extend this to accomodate multiple environments or add additional microservices with minimal new effort.

    Federated Identity

    Setting up a federated identity will allow us a more securable and auditable way of accessing Azure from GitHub Actions. For more about setting up a federated identity, Microsoft Learn has the details on connecting GitHub Actions to Azure.

    Here, we'll just walk through the setup of the identity and configure GitHub to use that idenity to deploy our AKS environment and interact with our Azure Container Registry.

    The examples will use PowerShell, but a Bash version of the setup commands is available in the week3/day1 branch.

    Prerequisites

    To follow along, you'll need:

    • a GitHub account
    • an Azure Subscription
    • the Azure CLI
    • and the Git CLI.

    You'll need to fork the source repository under your GitHub user or organization where you can manage secrets and GitHub Actions.

    It would be helpful to have the GitHub CLI, but it's not required.

    Set Up Some Defaults

    You will need to update one or more of the variables (your user or organization, what branch you want to work off of, and possibly the Azure AD application name if there is a conflict).

    # Replace the gitHubOrganizationName value
    # with the user or organization you forked
    # the repository under.

    $githubOrganizationName = 'Azure-Samples'
    $githubRepositoryName = 'eShopOnAKS'
    $branchName = 'week3/day1'
    $applicationName = 'cnny-week3-day1'

    Create an Azure AD Application

    Next, we need to create an Azure AD application.

    # Create an Azure AD application
    $aksDeploymentApplication = New-AzADApplication -DisplayName $applicationName

    Set Up Federation for that Azure AD Application

    And configure that application to allow federated credential requests from our GitHub repository for a particular branch.

    # Create a federated identity credential for the application
    New-AzADAppFederatedCredential `
    -Name $applicationName `
    -ApplicationObjectId $aksDeploymentApplication.Id `
    -Issuer 'https://token.actions.githubusercontent.com' `
    -Audience 'api://AzureADTokenExchange' `
    -Subject "repo:$($githubOrganizationName)/$($githubRepositoryName):ref:refs/heads/$branchName"

    Create a Service Principal for the Azure AD Application

    Once the application has been created, we need a service principal tied to that application. The service principal can be granted rights in Azure.

    # Create a service principal for the application
    New-AzADServicePrincipal -AppId $($aksDeploymentApplication.AppId)

    Give that Service Principal Rights to Azure Resources

    Because our Bicep deployment exists at the subscription level and we are creating role assignments, we need to give it Owner rights. If we changed the scope of the deployment to just a resource group, we could apply more scoped permissions.

    $azureContext = Get-AzContext
    New-AzRoleAssignment `
    -ApplicationId $($aksDeploymentApplication.AppId) `
    -RoleDefinitionName Owner `
    -Scope $azureContext.Subscription.Id

    Add Secrets to GitHub Repository

    If you have the GitHub CLI, you can use that right from your shell to set the secrets needed.

    gh secret set AZURE_CLIENT_ID --body $aksDeploymentApplication.AppId
    gh secret set AZURE_TENANT_ID --body $azureContext.Tenant.Id
    gh secret set AZURE_SUBSCRIPTION_ID --body $azureContext.Subscription.Id

    Otherwise, you can create them through the web interface like I did in the Learn Live event below.

    info

    It may look like the whole video will play, but it'll stop after configuring the secrets in GitHub (after about 9 minutes)

    The video shows creating the Azure AD application, service principals, and configuring the federated identity in Azure AD and GitHub.

    Creating a Bicep Deployment

    Resuable Workflows

    We'll create our Bicep deployment in a reusable workflows. What are they? The previous link has the documentation or the video below has my colleague Brandon Martinez and I talking about them.

    This workflow is basically the same deployment we did in last week's series, just in GitHub Actions.

    Start by creating a file called deploy_aks.yml in the .github/workflows directory with the below contents.

    name: deploy

    on:
    workflow_call:
    inputs:
    resourceGroupName:
    required: true
    type: string
    secrets:
    AZURE_CLIENT_ID:
    required: true
    AZURE_TENANT_ID:
    required: true
    AZURE_SUBSCRIPTION_ID:
    required: true
    outputs:
    containerRegistryName:
    description: Container Registry Name
    value: ${{ jobs.deploy.outputs.containerRegistryName }}
    containerRegistryUrl:
    description: Container Registry Login Url
    value: ${{ jobs.deploy.outputs.containerRegistryUrl }}
    resourceGroupName:
    description: Resource Group Name
    value: ${{ jobs.deploy.outputs.resourceGroupName }}
    aksName:
    description: Azure Kubernetes Service Cluster Name
    value: ${{ jobs.deploy.outputs.aksName }}

    permissions:
    id-token: write
    contents: read

    jobs:
    validate:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    - uses: azure/login@v1
    name: Sign in to Azure
    with:
    client-id: ${{ secrets.AZURE_CLIENT_ID }}
    tenant-id: ${{ secrets.AZURE_TENANT_ID }}
    subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
    - uses: azure/arm-deploy@v1
    name: Run preflight validation
    with:
    deploymentName: ${{ github.run_number }}
    scope: subscription
    region: eastus
    template: ./deploy/main.bicep
    parameters: >
    resourceGroup=${{ inputs.resourceGroupName }}
    deploymentMode: Validate

    deploy:
    needs: validate
    runs-on: ubuntu-latest
    outputs:
    containerRegistryName: ${{ steps.deploy.outputs.acr_name }}
    containerRegistryUrl: ${{ steps.deploy.outputs.acr_login_server_url }}
    resourceGroupName: ${{ steps.deploy.outputs.resource_group_name }}
    aksName: ${{ steps.deploy.outputs.aks_name }}
    steps:
    - uses: actions/checkout@v2
    - uses: azure/login@v1
    name: Sign in to Azure
    with:
    client-id: ${{ secrets.AZURE_CLIENT_ID }}
    tenant-id: ${{ secrets.AZURE_TENANT_ID }}
    subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
    - uses: azure/arm-deploy@v1
    id: deploy
    name: Deploy Bicep file
    with:
    failOnStdErr: false
    deploymentName: ${{ github.run_number }}
    scope: subscription
    region: eastus
    template: ./deploy/main.bicep
    parameters: >
    resourceGroup=${{ inputs.resourceGroupName }}

    Adding the Bicep Deployment

    Once we have the Bicep deployment workflow, we can add it to the primary build definition in .github/workflows/dotnetcore.yml

    Permissions

    First, we need to add a permissions block to let the workflow request our Azure AD token. This can go towards the top of the YAML file (I started it on line 5).

    permissions:
    id-token: write
    contents: read

    Deploy AKS Job

    Next, we'll add a reference to our reusable workflow. This will go after the build job.

      deploy_aks:
    needs: [build]
    uses: ./.github/workflows/deploy_aks.yml
    with:
    resourceGroupName: 'cnny-week3'
    secrets:
    AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
    AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
    AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }}

    Building and Publishing a Container Image

    Now that we have our target environment in place and an Azure Container Registry, we can build and publish our container images.

    Add a Reusable Workflow

    First, we'll create a new file for our reusable workflow at .github/workflows/publish_container_image.yml.

    We'll start the file with a name, the parameters it needs to run, and the permissions requirements for the federated identity request.

    name: Publish Container Images

    on:
    workflow_call:
    inputs:
    containerRegistryName:
    required: true
    type: string
    containerRegistryUrl:
    required: true
    type: string
    githubSha:
    required: true
    type: string
    secrets:
    AZURE_CLIENT_ID:
    required: true
    AZURE_TENANT_ID:
    required: true
    AZURE_SUBSCRIPTION_ID:
    required: true

    permissions:
    id-token: write
    contents: read

    Build the Container Images

    Our next step is to build the two container images we'll need for the application, the website and the API. We'll build the container images on our build worker and tag it with the git SHA, so there'll be a direct tie between the point in time in our codebase and the container images that represent it.

    jobs:
    publish_container_image:
    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v2
    - name: docker build
    run: |
    docker build . -f src/Web/Dockerfile -t ${{ inputs.containerRegistryUrl }}/web:${{ inputs.githubSha }}
    docker build . -f src/PublicApi/Dockerfile -t ${{ inputs.containerRegistryUrl }}/api:${{ inputs.githubSha}}

    Scan the Container Images

    Before we publish those container images, we'll scan them for vulnerabilities and best practice violations. We can add these two steps (one scan for each image).

        - name: scan web container image
    uses: Azure/container-scan@v0
    with:
    image-name: ${{ inputs.containerRegistryUrl }}/web:${{ inputs.githubSha}}
    - name: scan api container image
    uses: Azure/container-scan@v0
    with:
    image-name: ${{ inputs.containerRegistryUrl }}/web:${{ inputs.githubSha}}

    The container images provided have a few items that'll be found. We can create an allowed list at .github/containerscan/allowedlist.yaml to define vulnerabilities or best practice violations that we'll explictly allow to not fail our build.

    general:
    vulnerabilities:
    - CVE-2022-29458
    - CVE-2022-3715
    - CVE-2022-1304
    - CVE-2021-33560
    - CVE-2020-16156
    - CVE-2019-8457
    - CVE-2018-8292
    bestPracticeViolations:
    - CIS-DI-0001
    - CIS-DI-0005
    - CIS-DI-0006
    - CIS-DI-0008

    Publish the Container Images

    Finally, we'll log in to Azure, then log in to our Azure Container Registry, and push our images.

        - uses: azure/login@v1
    name: Sign in to Azure
    with:
    client-id: ${{ secrets.AZURE_CLIENT_ID }}
    tenant-id: ${{ secrets.AZURE_TENANT_ID }}
    subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
    - name: acr login
    run: az acr login --name ${{ inputs.containerRegistryName }}
    - name: docker push
    run: |
    docker push ${{ inputs.containerRegistryUrl }}/web:${{ inputs.githubSha}}
    docker push ${{ inputs.containerRegistryUrl }}/api:${{ inputs.githubSha}}

    Update the Build With the Image Build and Publish

    Now that we have our reusable workflow to create and publish our container images, we can include that in our primary build defnition at .github/workflows/dotnetcore.yml.

      publish_container_image:
    needs: [deploy_aks]
    uses: ./.github/workflows/publish_container_image.yml
    with:
    containerRegistryName: ${{ needs.deploy_aks.outputs.containerRegistryName }}
    containerRegistryUrl: ${{ needs.deploy_aks.outputs.containerRegistryUrl }}
    githubSha: ${{ github.sha }}
    secrets:
    AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
    AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
    AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }}

    Deploying to Kubernetes

    Finally, we've gotten enough set up that a commit to the target branch will:

    • build and test our application code
    • set up (or validate) our AKS and ACR environment
    • and create, scan, and publish our container images to ACR

    Our last step will be to deploy our application to Kubernetes. We'll use the basic building blocks we worked with last week, deployments and services.

    Starting the Reusable Workflow to Deploy to AKS

    We'll start our workflow with our parameters that we need, as well as the permissions to access the token to log in to Azure.

    We'll check out our code, then log in to Azure, and use the az CLI to get credentials for our AKS cluster.

    name: deploy_to_aks

    on:
    workflow_call:
    inputs:
    aksName:
    required: true
    type: string
    resourceGroupName:
    required: true
    type: string
    containerRegistryUrl:
    required: true
    type: string
    githubSha:
    required: true
    type: string
    secrets:
    AZURE_CLIENT_ID:
    required: true
    AZURE_TENANT_ID:
    required: true
    AZURE_SUBSCRIPTION_ID:
    required: true

    permissions:
    id-token: write
    contents: read

    jobs:
    deploy:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    - uses: azure/login@v1
    name: Sign in to Azure
    with:
    client-id: ${{ secrets.AZURE_CLIENT_ID }}
    tenant-id: ${{ secrets.AZURE_TENANT_ID }}
    subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
    - name: Get AKS Credentials
    run: |
    az aks get-credentials --resource-group ${{ inputs.resourceGroupName }} --name ${{ inputs.aksName }}

    Edit the Deployment For Our Current Image Tag

    Let's add the Kubernetes manifests to our repo. This post is long enough, so you can find the content for the manifests folder in the manifests folder in the source repo under the week3/day1 branch.

    tip

    If you only forked the main branch of the source repo, you can easily get the updated manifests by using the following git commands:

    git remote add upstream https://github.com/Azure-Samples/eShopOnAks
    git fetch upstream week3/day1
    git checkout upstream/week3/day1 manifests

    This will make the week3/day1 branch available locally and then we can update the manifests directory to match the state of that branch.

    The deployments and the service defintions should be familiar from last week's content (but not the same). This week, however, there's a new file in the manifests - ./manifests/kustomization.yaml

    This file helps us more dynamically edit our kubernetes manifests and support is baked right in to the kubectl command.

    Kustomize Definition

    Kustomize allows us to specify specific resource manifests and areas of that manifest to replace. We've put some placeholders in our file as well, so we can replace those for each run of our CI/CD system.

    In ./manifests/kustomization.yaml you will see:

    resources:
    - deployment-api.yaml
    - deployment-web.yaml

    # Change the image name and version
    images:
    - name: notavalidregistry.azurecr.io/api:v0.1.0
    newName: <YOUR_ACR_SERVER>/api
    newTag: <YOUR_IMAGE_TAG>
    - name: notavalidregistry.azurecr.io/web:v0.1.0
    newName: <YOUR_ACR_SERVER>/web
    newTag: <YOUR_IMAGE_TAG>

    Replacing Values in our Build

    Now, we encounter a little problem - our deployment files need to know the tag and ACR server. We can do a bit of sed magic to edit the file on the fly.

    In .github/workflows/deploy_to_aks.yml, we'll add:

          - name: replace_placeholders_with_current_run
    run: |
    sed -i "s/<YOUR_ACR_SERVER>/${{ inputs.containerRegistryUrl }}/g" ./manifests/kustomization.yaml
    sed -i "s/<YOUR_IMAGE_TAG>/${{ inputs.githubSha }}/g" ./manifests/kustomization.yaml

    Deploying the Manifests

    We have our manifests in place and our kustomization.yaml file (with commands to update it at runtime) ready to go, we can deploy our manifests.

    First, we'll deploy our database (deployment and service). Next, we'll use the -k parameter on kubectl to tell it to look for a kustomize configuration, transform the requested manifests and apply those. Finally, we apply the service defintions for the web and API deployments.

            run: |
    kubectl apply -f ./manifests/deployment-db.yaml \
    -f ./manifests/service-db.yaml
    kubectl apply -k ./manifests
    kubectl apply -f ./manifests/service-api.yaml \
    -f ./manifests/service-web.yaml

    Summary

    We've covered a lot of ground in today's post. We set up federated credentials with GitHub. Then we added reusable workflows to deploy an AKS environment and build/scan/publish our container images, and then to deploy them into our AKS environment.

    This sets us up to start making changes to our application and Kubernetes configuration and have those changes automatically validated and deployed by our CI/CD system. Tomorrow, we'll look at updating our application environment with runtime configuration, persistent storage, and more.

    Resources

    Take the Cloud Skills Challenge!

    Enroll in the Cloud Skills Challenge!

    Don't miss out on this opportunity to level up your skills and stay ahead of the curve in the world of cloud native.

    - - + + \ No newline at end of file diff --git a/cnny-2023/bring-your-app-day-2/index.html b/cnny-2023/bring-your-app-day-2/index.html index b7575eeac4..6a59ae5b58 100644 --- a/cnny-2023/bring-your-app-day-2/index.html +++ b/cnny-2023/bring-your-app-day-2/index.html @@ -14,13 +14,13 @@ - - + +

    3-2. Bringing Your Application to Kubernetes - Adapting Storage, Secrets, and Configuration

    · 12 min read
    Paul Yu

    Welcome to Day 2 of Week 3 of #CloudNativeNewYear!

    The theme for this week is Bringing Your Application to Kubernetes. Yesterday we talked about getting an existing application running in Kubernetes with a full pipeline in GitHub Actions. Today we'll evaluate our sample application's configuration, storage, and networking requirements and implement using Kubernetes and Azure resources.

    Ask the Experts Thursday, February 9th at 9 AM PST
    Friday, February 10th at 11 AM PST

    Watch the recorded demo and conversation about this week's topics.

    We were live on YouTube walking through today's (and the rest of this week's) demos. Join us Friday, February 10th and bring your questions!

    What We'll Cover

    • Gather requirements
    • Implement environment variables using ConfigMaps
    • Implement persistent volumes using Azure Files
    • Implement secrets using Azure Key Vault
    • Re-package deployments
    • Conclusion
    • Resources
    caution

    Before you begin, make sure you've gone through yesterday's post to set up your AKS cluster.

    Gather requirements

    The eShopOnWeb application is written in .NET 7 and has two major pieces of functionality. The web UI is where customers can browse and shop. The web UI also includes an admin portal for managing the product catalog. This admin portal, is packaged as a WebAssembly application and relies on a separate REST API service. Both the web UI and the REST API connect to the same SQL Server container.

    Looking through the source code which can be found here we can identify requirements for configs, persistent storage, and secrets.

    Database server

    • Need to store the password for the sa account as a secure secret
    • Need persistent storage volume for data directory
    • Need to inject environment variables for SQL Server license type and EULA acceptance

    Web UI and REST API service

    • Need to store database connection string as a secure secret
    • Need to inject ASP.NET environment variables to override app settings
    • Need persistent storage volume for ASP.NET key storage

    Implement environment variables using ConfigMaps

    ConfigMaps are relatively straight-forward to create. If you were following along with the examples last week, this should be review 😉

    Create a ConfigMap to store database environment variables.

    kubectl apply -f - <<EOF
    apiVersion: v1
    kind: ConfigMap
    metadata:
    name: mssql-settings
    data:
    MSSQL_PID: Developer
    ACCEPT_EULA: "Y"
    EOF

    Create another ConfigMap to store ASP.NET environment variables.

    kubectl apply -f - <<EOF
    apiVersion: v1
    kind: ConfigMap
    metadata:
    name: aspnet-settings
    data:
    ASPNETCORE_ENVIRONMENT: Development
    EOF

    Implement persistent volumes using Azure Files

    Similar to last week, we'll take advantage of storage classes built into AKS. For our SQL Server data, we'll use the azurefile-csi-premium storage class and leverage an Azure Files resource as our PersistentVolume.

    Create a PersistentVolumeClaim (PVC) for persisting SQL Server data.

    kubectl apply -f - <<EOF
    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
    name: mssql-data
    spec:
    accessModes:
    - ReadWriteMany
    storageClassName: azurefile-csi-premium
    resources:
    requests:
    storage: 5Gi
    EOF

    Create another PVC for persisting ASP.NET data.

    kubectl apply -f - <<EOF
    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
    name: aspnet-data
    spec:
    accessModes:
    - ReadWriteMany
    storageClassName: azurefile-csi-premium
    resources:
    requests:
    storage: 5Gi
    EOF

    Implement secrets using Azure Key Vault

    It's a well known fact that Kubernetes secretes are not really secrets. They're just base64-encoded values and not secure, especially if malicious users have access to your Kubernetes cluster.

    In a production scenario, you will want to leverage an external vault like Azure Key Vault or HashiCorp Vault to encrypt and store secrets.

    With AKS, we can enable the Secrets Store CSI driver add-on which will allow us to leverage Azure Key Vault.

    # Set some variables
    RG_NAME=<YOUR_RESOURCE_GROUP_NAME>
    AKS_NAME=<YOUR_AKS_CLUSTER_NAME>
    ACR_NAME=<YOUR_ACR_NAME>

    az aks enable-addons \
    --addons azure-keyvault-secrets-provider \
    --name $AKS_NAME \
    --resource-group $RG_NAME

    With the add-on enabled, you should see aks-secrets-store-csi-driver and aks-secrets-store-provider-azure resources installed on each node in your Kubernetes cluster.

    Run the command below to verify.

    kubectl get pods \
    --namespace kube-system \
    --selector 'app in (secrets-store-csi-driver, secrets-store-provider-azure)'

    The Secrets Store CSI driver allows us to use secret stores via Container Storage Interface (CSI) volumes. This provider offers capabilities such as mounting and syncing between the secure vault and Kubernetes Secrets. On AKS, the Azure Key Vault Provider for Secrets Store CSI Driver enables integration with Azure Key Vault.

    You may not have an Azure Key Vault created yet, so let's create one and add some secrets to it.

    AKV_NAME=$(az keyvault create \
    --name akv-eshop$RANDOM \
    --resource-group $RG_NAME \
    --query name -o tsv)

    # Database server password
    az keyvault secret set \
    --vault-name $AKV_NAME \
    --name mssql-password \
    --value "@someThingComplicated1234"

    # Catalog database connection string
    az keyvault secret set \
    --vault-name $AKV_NAME \
    --name mssql-connection-catalog \
    --value "Server=db;Database=Microsoft.eShopOnWeb.CatalogDb;User Id=sa;Password=@someThingComplicated1234;TrustServerCertificate=True;"

    # Identity database connection string
    az keyvault secret set \
    --vault-name $AKV_NAME \
    --name mssql-connection-identity \
    --value "Server=db;Database=Microsoft.eShopOnWeb.Identity;User Id=sa;Password=@someThingComplicated1234;TrustServerCertificate=True;"

    Pods authentication using Azure Workload Identity

    In order for our Pods to retrieve secrets from Azure Key Vault, we'll need to set up a way for the Pod to authenticate against Azure AD. This can be achieved by implementing the new Azure Workload Identity feature of AKS.

    info

    At the time of this writing, the workload identity feature of AKS is in Preview.

    The workload identity feature within AKS allows us to leverage native Kubernetes resources and link a Kubernetes ServiceAccount to an Azure Managed Identity to authenticate against Azure AD.

    For the authentication flow, our Kubernetes cluster will act as an Open ID Connect (OIDC) issuer and will be able issue identity tokens to ServiceAccounts which will be assigned to our Pods.

    The Azure Managed Identity will be granted permission to access secrets in our Azure Key Vault and with the ServiceAccount being assigned to our Pods, they will be able to retrieve our secrets.

    For more information on how the authentication mechanism all works, check out this doc.

    To implement all this, start by enabling the new preview feature for AKS.

    az feature register \
    --namespace "Microsoft.ContainerService" \
    --name "EnableWorkloadIdentityPreview"
    caution

    This can take several minutes to complete.

    Check the status and ensure the state shows Regestered before moving forward.

    az feature show \
    --namespace "Microsoft.ContainerService" \
    --name "EnableWorkloadIdentityPreview"

    Update your AKS cluster to enable the workload identity feature and enable the OIDC issuer endpoint.

    az aks update \
    --name $AKS_NAME \
    --resource-group $RG_NAME \
    --enable-workload-identity \
    --enable-oidc-issuer

    Create an Azure Managed Identity and retrieve its client ID.

    MANAGED_IDENTITY_CLIENT_ID=$(az identity create \
    --name aks-workload-identity \
    --resource-group $RG_NAME \
    --subscription $(az account show --query id -o tsv) \
    --query 'clientId' -o tsv)

    Create the Kubernetes ServiceAccount.

    # Set namespace (this must align with the namespace that your app is deployed into)
    SERVICE_ACCOUNT_NAMESPACE=default

    # Set the service account name
    SERVICE_ACCOUNT_NAME=eshop-serviceaccount

    # Create the service account
    kubectl apply -f - <<EOF
    apiVersion: v1
    kind: ServiceAccount
    metadata:
    annotations:
    azure.workload.identity/client-id: ${MANAGED_IDENTITY_CLIENT_ID}
    labels:
    azure.workload.identity/use: "true"
    name: ${SERVICE_ACCOUNT_NAME}
    namespace: ${SERVICE_ACCOUNT_NAMESPACE}
    EOF
    info

    Note to enable this ServiceAccount to work with Azure Workload Identity, you must annotate the resource with azure.workload.identity/client-id, and add a label of azure.workload.identity/use: "true"

    That was a lot... Let's review what we just did.

    We have an Azure Managed Identity (object in Azure AD), an OIDC issuer URL (endpoint in our Kubernetes cluster), and a Kubernetes ServiceAccount.

    The next step is to "tie" these components together and establish a Federated Identity Credential so that Azure AD can trust authentication requests from your Kubernetes cluster.

    info

    This identity federation can be established between Azure AD any Kubernetes cluster; not just AKS 🤗

    To establish the federated credential, we'll need the OIDC issuer URL, and a subject which points to your Kubernetes ServiceAccount.

    # Get the OIDC issuer URL
    OIDC_ISSUER_URL=$(az aks show \
    --name $AKS_NAME \
    --resource-group $RG_NAME \
    --query "oidcIssuerProfile.issuerUrl" -o tsv)

    # Set the subject name using this format: `system:serviceaccount:<YOUR_SERVICE_ACCOUNT_NAMESPACE>:<YOUR_SERVICE_ACCOUNT_NAME>`
    SUBJECT=system:serviceaccount:$SERVICE_ACCOUNT_NAMESPACE:$SERVICE_ACCOUNT_NAME

    az identity federated-credential create \
    --name aks-federated-credential \
    --identity-name aks-workload-identity \
    --resource-group $RG_NAME \
    --issuer $OIDC_ISSUER_URL \
    --subject $SUBJECT

    With the authentication components set, we can now create a SecretProviderClass which includes details about the Azure Key Vault, the secrets to pull out from the vault, and identity used to access the vault.

    # Get the tenant id for the key vault
    TENANT_ID=$(az keyvault show \
    --name $AKV_NAME \
    --resource-group $RG_NAME \
    --query properties.tenantId -o tsv)

    # Create the secret provider for azure key vault
    kubectl apply -f - <<EOF
    apiVersion: secrets-store.csi.x-k8s.io/v1
    kind: SecretProviderClass
    metadata:
    name: eshop-azure-keyvault
    spec:
    provider: azure
    parameters:
    usePodIdentity: "false"
    useVMManagedIdentity: "false"
    clientID: "${MANAGED_IDENTITY_CLIENT_ID}"
    keyvaultName: "${AKV_NAME}"
    cloudName: ""
    objects: |
    array:
    - |
    objectName: mssql-password
    objectType: secret
    objectVersion: ""
    - |
    objectName: mssql-connection-catalog
    objectType: secret
    objectVersion: ""
    - |
    objectName: mssql-connection-identity
    objectType: secret
    objectVersion: ""
    tenantId: "${TENANT_ID}"
    secretObjects:
    - secretName: eshop-secrets
    type: Opaque
    data:
    - objectName: mssql-password
    key: mssql-password
    - objectName: mssql-connection-catalog
    key: mssql-connection-catalog
    - objectName: mssql-connection-identity
    key: mssql-connection-identity
    EOF

    Finally, lets grant the Azure Managed Identity permissions to retrieve secrets from the Azure Key Vault.

    az keyvault set-policy \
    --name $AKV_NAME \
    --secret-permissions get \
    --spn $MANAGED_IDENTITY_CLIENT_ID

    Re-package deployments

    Update your database deployment to load environment variables from our ConfigMap, attach the PVC and SecretProviderClass as volumes, mount the volumes into the Pod, and use the ServiceAccount to retrieve secrets.

    Additionally, you may notice the database Pod is set to use fsGroup:10001 as part of the securityContext. This is required as the MSSQL container runs using a non-root account called mssql and this account has the proper permissions to read/write data at the /var/opt/mssql mount path.

    kubectl apply -f - <<EOF
    apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: db
    labels:
    app: db
    spec:
    replicas: 1
    selector:
    matchLabels:
    app: db
    template:
    metadata:
    labels:
    app: db
    spec:
    securityContext:
    fsGroup: 10001
    serviceAccountName: ${SERVICE_ACCOUNT_NAME}
    containers:
    - name: db
    image: mcr.microsoft.com/mssql/server:2019-latest
    ports:
    - containerPort: 1433
    envFrom:
    - configMapRef:
    name: mssql-settings
    env:
    - name: MSSQL_SA_PASSWORD
    valueFrom:
    secretKeyRef:
    name: eshop-secrets
    key: mssql-password
    resources: {}
    volumeMounts:
    - name: mssqldb
    mountPath: /var/opt/mssql
    - name: eshop-secrets
    mountPath: "/mnt/secrets-store"
    readOnly: true
    volumes:
    - name: mssqldb
    persistentVolumeClaim:
    claimName: mssql-data
    - name: eshop-secrets
    csi:
    driver: secrets-store.csi.k8s.io
    readOnly: true
    volumeAttributes:
    secretProviderClass: eshop-azure-keyvault
    EOF

    We'll update the API and Web deployments in a similar way.

    # Set the image tag
    IMAGE_TAG=<YOUR_IMAGE_TAG>

    # API deployment
    kubectl apply -f - <<EOF
    apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: api
    labels:
    app: api
    spec:
    replicas: 1
    selector:
    matchLabels:
    app: api
    template:
    metadata:
    labels:
    app: api
    spec:
    serviceAccount: ${SERVICE_ACCOUNT_NAME}
    containers:
    - name: api
    image: ${ACR_NAME}.azurecr.io/api:${IMAGE_TAG}
    ports:
    - containerPort: 80
    envFrom:
    - configMapRef:
    name: aspnet-settings
    env:
    - name: ConnectionStrings__CatalogConnection
    valueFrom:
    secretKeyRef:
    name: eshop-secrets
    key: mssql-connection-catalog
    - name: ConnectionStrings__IdentityConnection
    valueFrom:
    secretKeyRef:
    name: eshop-secrets
    key: mssql-connection-identity
    resources: {}
    volumeMounts:
    - name: aspnet
    mountPath: ~/.aspnet/https:/root/.aspnet/https:ro
    - name: eshop-secrets
    mountPath: "/mnt/secrets-store"
    readOnly: true
    volumes:
    - name: aspnet
    persistentVolumeClaim:
    claimName: aspnet-data
    - name: eshop-secrets
    csi:
    driver: secrets-store.csi.k8s.io
    readOnly: true
    volumeAttributes:
    secretProviderClass: eshop-azure-keyvault
    EOF

    ## Web deployment
    kubectl apply -f - <<EOF
    apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: web
    labels:
    app: web
    spec:
    replicas: 1
    selector:
    matchLabels:
    app: web
    template:
    metadata:
    labels:
    app: web
    spec:
    serviceAccount: ${SERVICE_ACCOUNT_NAME}
    containers:
    - name: web
    image: ${ACR_NAME}.azurecr.io/web:${IMAGE_TAG}
    ports:
    - containerPort: 80
    envFrom:
    - configMapRef:
    name: aspnet-settings
    env:
    - name: ConnectionStrings__CatalogConnection
    valueFrom:
    secretKeyRef:
    name: eshop-secrets
    key: mssql-connection-catalog
    - name: ConnectionStrings__IdentityConnection
    valueFrom:
    secretKeyRef:
    name: eshop-secrets
    key: mssql-connection-identity
    resources: {}
    volumeMounts:
    - name: aspnet
    mountPath: ~/.aspnet/https:/root/.aspnet/https:ro
    - name: eshop-secrets
    mountPath: "/mnt/secrets-store"
    readOnly: true
    volumes:
    - name: aspnet
    persistentVolumeClaim:
    claimName: aspnet-data
    - name: eshop-secrets
    csi:
    driver: secrets-store.csi.k8s.io
    readOnly: true
    volumeAttributes:
    secretProviderClass: eshop-azure-keyvault
    EOF

    If all went well with your deployment updates, you should be able to browse to your website and buy some merchandise again 🥳

    echo "http://$(kubectl get service web -o jsonpath='{.status.loadBalancer.ingress[0].ip}')"

    Conclusion

    Although there is no visible changes on with our website, we've made a ton of changes on the Kubernetes backend to make this application much more secure and resilient.

    We used a combination of Kubernetes resources and AKS-specific features to achieve our goal of securing our secrets and ensuring data is not lost on container crashes and restarts.

    To learn more about the components we leveraged here today, checkout the resources and additional tutorials listed below.

    You can also find manifests with all the changes made in today's post in the Azure-Samples/eShopOnAKS repository.

    See you in the next post!

    Resources

    Take the Cloud Skills Challenge!

    Enroll in the Cloud Skills Challenge!

    Don't miss out on this opportunity to level up your skills and stay ahead of the curve in the world of cloud native.

    - - + + \ No newline at end of file diff --git a/cnny-2023/bring-your-app-day-3/index.html b/cnny-2023/bring-your-app-day-3/index.html index db8e046624..ddefcf5ad8 100644 --- a/cnny-2023/bring-your-app-day-3/index.html +++ b/cnny-2023/bring-your-app-day-3/index.html @@ -14,13 +14,13 @@ - - + +

    3-3. Bringing Your Application to Kubernetes - Opening your Application with Ingress

    · 10 min read
    Paul Yu

    Welcome to Day 3 of Week 3 of #CloudNativeNewYear!

    The theme for this week is Bringing Your Application to Kubernetes. Yesterday we added configuration, secrets, and storage to our app. Today we'll explore how to expose the eShopOnWeb app so that customers can reach it over the internet using a custom domain name and TLS.

    Ask the Experts Thursday, February 9th at 9 AM PST
    Friday, February 10th at 11 AM PST

    Watch the recorded demo and conversation about this week's topics.

    We were live on YouTube walking through today's (and the rest of this week's) demos. Join us Friday, February 10th and bring your questions!

    What We'll Cover

    • Gather requirements
    • Generate TLS certificate and store in Azure Key Vault
    • Implement custom DNS using Azure DNS
    • Enable Web Application Routing add-on for AKS
    • Implement Ingress for the web application
    • Conclusion
    • Resources

    Gather requirements

    Currently, our eShopOnWeb app has three Kubernetes services deployed:

    1. db exposed internally via ClusterIP
    2. api exposed externally via LoadBalancer
    3. web exposed externally via LoadBalancer

    As mentioned in my post last week, Services allow applications to communicate with each other using DNS names. Kubernetes has service discovery capabilities built-in that allows Pods to resolve Services simply by using their names.

    In the case of our api and web deployments, they can simply reach the database by calling its name. The service type of ClusterIP for the db can remain as-is since it only needs to be accessed by the api and web apps.

    On the other hand, api and web both need to be accessed over the public internet. Currently, these services are using service type LoadBalancer which tells AKS to provision an Azure Load Balancer with a public IP address. No one is going to remember the IP addresses, so we need to make the app more accessible by adding a custom domain name and securing it with a TLS certificate.

    Here's what we're going to need:

    • Custom domain name for our app
    • TLS certificate for the custom domain name
    • Routing rule to ensure requests with /api/ in the URL is routed to the backend REST API
    • Routing rule to ensure requests without /api/ in the URL is routing to the web UI

    Just like last week, we will use the Web Application Routing add-on for AKS. But this time, we'll integrate it with Azure DNS and Azure Key Vault to satisfy all of our requirements above.

    info

    At the time of this writing the add-on is still in Public Preview

    Generate TLS certificate and store in Azure Key Vault

    We deployed an Azure Key Vault yesterday to store secrets. We'll use it again to store a TLS certificate too.

    Let's create and export a self-signed certificate for the custom domain.

    DNS_NAME=eshoponweb$RANDOM.com
    openssl req -new -x509 -nodes -out web-tls.crt -keyout web-tls.key -subj "/CN=${DNS_NAME}" -addext "subjectAltName=DNS:${DNS_NAME}"
    openssl pkcs12 -export -in web-tls.crt -inkey web-tls.key -out web-tls.pfx -password pass:
    info

    For learning purposes we'll use a self-signed certificate and a fake custom domain name.

    To browse to the site using the fake domain, we'll mimic a DNS lookup by adding an entry to your host file which maps the public IP address assigned to the ingress controller to the custom domain.

    In a production scenario, you will need to have a real domain delegated to Azure DNS and a valid TLS certificate for the domain.

    Grab your Azure Key Vault name and set the value in a variable for later use.

    RESOURCE_GROUP=cnny-week3

    AKV_NAME=$(az resource list \
    --resource-group $RESOURCE_GROUP \
    --resource-type Microsoft.KeyVault/vaults \
    --query "[0].name" -o tsv)

    Grant yourself permissions to get, list, and import certificates.

    MY_USER_NAME=$(az account show --query user.name -o tsv)
    MY_USER_OBJECT_ID=$(az ad user show --id $MY_USER_NAME --query id -o tsv)

    az keyvault set-policy \
    --name $AKV_NAME \
    --object-id $MY_USER_OBJECT_ID \
    --certificate-permissions get list import

    Upload the TLS certificate to Azure Key Vault and grab its certificate URI.

    WEB_TLS_CERT_ID=$(az keyvault certificate import \
    --vault-name $AKV_NAME \
    --name web-tls \
    --file web-tls.pfx \
    --query id \
    --output tsv)

    Implement custom DNS with Azure DNS

    Create a custom domain for our application and grab its Azure resource id.

    DNS_ZONE_ID=$(az network dns zone create \
    --name $DNS_NAME \
    --resource-group $RESOURCE_GROUP \
    --query id \
    --output tsv)

    Enable Web Application Routing add-on for AKS

    As we enable the Web Application Routing add-on, we'll also pass in the Azure DNS Zone resource id which triggers the installation of the external-dns controller in your Kubernetes cluster. This controller will be able to write Azure DNS zone entries on your behalf as you deploy Ingress manifests.

    AKS_NAME=$(az resource list \
    --resource-group $RESOURCE_GROUP \
    --resource-type Microsoft.ContainerService/managedClusters \
    --query "[0].name" -o tsv)

    az aks enable-addons \
    --name $AKS_NAME \
    --resource-group $RESOURCE_GROUP \
    --addons web_application_routing \
    --dns-zone-resource-id=$DNS_ZONE_ID \
    --enable-secret-rotation

    The add-on will also deploy a new Azure Managed Identity which is used by the external-dns controller when writing Azure DNS zone entries. Currently, it does not have permission to do that, so let's grant it permission.

    # This is where resources are automatically deployed by AKS
    NODE_RESOURCE_GROUP=$(az aks show \
    --name $AKS_NAME \
    --resource-group $RESOURCE_GROUP \
    --query nodeResourceGroup -o tsv)

    # This is the managed identity created by the Web Application Routing add-on
    MANAGED_IDENTTIY_OBJECT_ID=$(az resource show \
    --name webapprouting-${AKS_NAME} \
    --resource-group $NODE_RESOURCE_GROUP \
    --resource-type Microsoft.ManagedIdentity/userAssignedIdentities \
    --query properties.principalId \
    --output tsv)

    # Grant the managed identity permissions to write DNS entries
    az role assignment create \
    --role "DNS Zone Contributor" \
    --assignee $MANAGED_IDENTTIY_OBJECT_ID \
    --scope $DNS_ZONE_ID

    The Azure Managed Identity will also be used to retrieve and rotate TLS certificates from Azure Key Vault. So we'll need to grant it permission for that too.

    az keyvault set-policy \
    --name $AKV_NAME \
    --object-id $MANAGED_IDENTTIY_OBJECT_ID \
    --secret-permissions get \
    --certificate-permissions get

    Implement Ingress for the web application

    Before we create a new Ingress manifest, let's update the existing services to use ClusterIP instead of LoadBalancer. With an Ingress in place, there is no reason why we need the Service resources to be accessible from outside the cluster. The new Ingress will be the only entrypoint for external users.

    We can use the kubectl patch command to update the services

    kubectl patch service api -p '{"spec": {"type": "ClusterIP"}}'
    kubectl patch service web -p '{"spec": {"type": "ClusterIP"}}'

    Deploy a new Ingress to place in front of the web Service. Notice there is a special annotations entry for kubernetes.azure.com/tls-cert-keyvault-uri which points back to our self-signed certificate that was uploaded to Azure Key Vault.

    kubectl apply -f - <<EOF
    apiVersion: networking.k8s.io/v1
    kind: Ingress
    metadata:
    annotations:
    kubernetes.azure.com/tls-cert-keyvault-uri: ${WEB_TLS_CERT_ID}
    name: web
    spec:
    ingressClassName: webapprouting.kubernetes.azure.com
    rules:
    - host: ${DNS_NAME}
    http:
    paths:
    - backend:
    service:
    name: web
    port:
    number: 80
    path: /
    pathType: Prefix
    - backend:
    service:
    name: api
    port:
    number: 80
    path: /api
    pathType: Prefix
    tls:
    - hosts:
    - ${DNS_NAME}
    secretName: web-tls
    EOF

    In our manifest above, we've also configured the Ingress route the traffic to either the web or api services based on the URL path requested. If the request URL includes /api/ then it will send traffic to the api backend service. Otherwise, it will send traffic to the web service.

    Within a few minutes, the external-dns controller will add an A record to Azure DNS which points to the Ingress resource's public IP. With the custom domain in place, we can simply browse using this domain name.

    info

    As mentioned above, since this is not a real domain name, we need to modify our host file to make it seem like our custom domain is resolving to the Ingress' public IP address.

    To get the ingress public IP, run the following:

    # Get the IP
    kubectl get ingress web -o jsonpath="{.status.loadBalancer.ingress[0].ip}"

    # Get the hostname
    kubectl get ingress web -o jsonpath="{.spec.tls[0].hosts[0]}"

    Next, open your host file and add an entry using the format <YOUR_PUBLIC_IP> <YOUR_CUSTOM_DOMAIN>. Below is an example of what it should look like.

    20.237.116.224 eshoponweb11265.com

    See this doc for more info on how to do this.

    When browsing to the website, you may be presented with a warning about the connection not being private. This is due to the fact that we are using a self-signed certificate. This is expected, so go ahead and proceed anyway to load up the page.

    Why is the Admin page broken?

    If you log in using the admin@microsoft.com account and browse to the Admin page, you'll notice no products are loaded on the page.

    This is because the admin page is built using Blazor and compiled as a WebAssembly application that runs in your browser. When the application was compiled, it packed the appsettings.Development.json file as an embedded resource. This file contains the base URL for the public API and it currently points to https://localhost:5099. Now that we have a domain name, we can update the base URL and point it to our custom domain.

    From the root of the eShopOnWeb repo, update the configuration file using a sed command.

    sed -i -e "s/localhost:5099/${DNS_NAME}/g" ./src/BlazorAdmin/wwwroot/appsettings.Development.json

    Rebuild and push the container to Azure Container Registry.

    # Grab the name of your Azure Container Registry
    ACR_NAME=$(az resource list \
    --resource-group $RESOURCE_GROUP \
    --resource-type Microsoft.ContainerRegistry/registries \
    --query "[0].name" -o tsv)

    # Invoke a build and publish job
    az acr build \
    --registry $ACR_NAME \
    --image $ACR_NAME.azurecr.io/web:v0.1.0 \
    --file ./src/Web/Dockerfile .

    Once the container build has completed, we can issue a kubectl patch command to quickly update the web deployment to test our change.

    kubectl patch deployment web -p "$(cat <<EOF
    {
    "spec": {
    "template": {
    "spec": {
    "containers": [
    {
    "name": "web",
    "image": "${ACR_NAME}.azurecr.io/web:v0.1.0"
    }
    ]
    }
    }
    }
    }
    EOF
    )"

    If all went well, you will be able to browse the admin page again and confirm product data is being loaded 🥳

    Conclusion

    The Web Application Routing add-on for AKS aims to streamline the process of exposing it to the public using the open-source NGINX Ingress Controller. With the add-on being managed by Azure, it natively integrates with other Azure services like Azure DNS and eliminates the need to manually create DNS entries. It can also integrate with Azure Key Vault to automatically pull in TLS certificates and rotate them as needed to further reduce operational overhead.

    We are one step closer to production and in the upcoming posts we'll further operationalize and secure our deployment, so stay tuned!

    In the meantime, check out the resources listed below for further reading.

    You can also find manifests with all the changes made in today's post in the Azure-Samples/eShopOnAKS repository.

    Resources

    Take the Cloud Skills Challenge!

    Enroll in the Cloud Skills Challenge!

    Don't miss out on this opportunity to level up your skills and stay ahead of the curve in the world of cloud native.

    - - + + \ No newline at end of file diff --git a/cnny-2023/bring-your-app-day-4/index.html b/cnny-2023/bring-your-app-day-4/index.html index 1916eb0aa6..c0579443ba 100644 --- a/cnny-2023/bring-your-app-day-4/index.html +++ b/cnny-2023/bring-your-app-day-4/index.html @@ -14,13 +14,13 @@ - - + +

    3-4. Bringing Your Application to Kubernetes - Debugging and Instrumentation

    · 9 min read
    Steven Murawski

    Welcome to Day 4 of Week 3 of #CloudNativeNewYear!

    The theme for this week is Bringing Your Application to Kubernetes. Yesterday we exposed the eShopOnWeb app so that customers can reach it over the internet using a custom domain name and TLS. Today we'll explore the topic of debugging and instrumentation.

    Ask the Experts Thursday, February 9th at 9 AM PST
    Friday, February 10th at 11 AM PST

    Watch the recorded demo and conversation about this week's topics.

    We were live on YouTube walking through today's (and the rest of this week's) demos. Join us Friday, February 10th and bring your questions!

    What We'll Cover

    • Debugging
    • Bridge To Kubernetes
    • Instrumentation
    • Resources: For self-study!

    Debugging

    Debugging applications in a Kubernetes cluster can be challenging for several reasons:

    • Complexity: Kubernetes is a complex system with many moving parts, including pods, nodes, services, and config maps, all of which can interact in unexpected ways and cause issues.
    • Distributed Environment: Applications running in a Kubernetes cluster are often distributed across multiple nodes, which makes it harder to determine the root cause of an issue.
    • Logging and Monitoring: Debugging an application in a Kubernetes cluster requires access to logs and performance metrics, which can be difficult to obtain in a large and dynamic environment.
    • Resource Management: Kubernetes manages resources such as CPU and memory, which can impact the performance and behavior of applications. Debugging resource-related issues requires a deep understanding of the Kubernetes resource model and the underlying infrastructure.
    • Dynamic Nature: Kubernetes is designed to be dynamic, with the ability to add and remove resources as needed. This dynamic nature can make it difficult to reproduce issues and debug problems.

    However, there are many tools and practices that can help make debugging applications in a Kubernetes cluster easier, such as using centralized logging, monitoring, and tracing solutions, and following best practices for managing resources and deployment configurations.

    There's also another great tool in our toolbox - Bridge to Kubernetes.

    Bridge to Kubernetes

    Bridge to Kubernetes is a great tool for microservice development and debugging applications without having to locally replicate all the required microservices.

    Bridge to Kubernetes works with Visual Studio or Visual Studio Code.

    We'll walk through using it with Visual Studio Code.

    Connecting Bridge to Kubernetes to Our Cluster

    Ensure your AKS cluster is the default for kubectl

    If you've recently spun up a new AKS cluster or you have been working with a different cluster, you may need to change what cluster credentials you have configured.

    If it's a new cluster, we can use:

    RESOURCE_GROUP=<YOUR RESOURCE GROUP NAME>
    CLUSTER_NAME=<YOUR AKS CLUSTER NAME>
    az aks get-credentials az aks get-credentials --resource-group $RESOURCE_GROUP --name $CLUSTER_NAME

    Open the command palette

    Open the command palette and find Bridge to Kubernetes: Configure. You may need to start typing the name to get it to show up.

    The command palette for Visual Studio Code is open and the first item is Bridge to Kubernetes: Configure

    Pick the service you want to debug

    Bridge to Kubernetes will redirect a service for you. Pick the service you want to redirect, in this case we'll pick web.

    Selecting the `web` service to redirect in Visual Studio Code

    Identify the port your application runs on

    Next, we'll be prompted to identify what port our application will run on locally. For this application it'll be 5001, but that's just specific to this application (and the default for ASP.NET 7, I believe).

    Setting port 5001 as the port to redirect to the `web` Kubernetes service in Visual Studio Code

    Pick a debug configuration to extend

    Bridge to Kubernetes has a couple of ways to run - it can inject it's setup and teardown to your existing debug configurations. We'll pick .NET Core Launch (web).

    Telling Bridge to Kubernetes to use the .NET Core Launch (web) debug configuration in Visual Studio Code

    Forward Traffic for All Requests

    The last prompt you'll get in the configuration is about how you want Bridge to Kubernetes to handle re-routing traffic. The default is that all requests into the service will get your local version.

    You can also redirect specific traffic. Bridge to Kubernetes will set up a subdomain and route specific traffic to your local service, while allowing other traffic to the deployed service.

    Allowing the launch of Endpoint Manager on Windows

    Using Bridge to Kubernetes to Debug Our Service

    Now that we've configured Bridge to Kubernetes, we see that tasks and a new launch configuration have been added.

    Added to .vscode/tasks.json:

            {
    "label": "bridge-to-kubernetes.resource",
    "type": "bridge-to-kubernetes.resource",
    "resource": "web",
    "resourceType": "service",
    "ports": [
    5001
    ],
    "targetCluster": "aks1",
    "targetNamespace": "default",
    "useKubernetesServiceEnvironmentVariables": false
    },
    {
    "label": "bridge-to-kubernetes.compound",
    "dependsOn": [
    "bridge-to-kubernetes.resource",
    "build"
    ],
    "dependsOrder": "sequence"
    }

    And added to .vscode/launch.json:

    {
    "name": ".NET Core Launch (web) with Kubernetes",
    "type": "coreclr",
    "request": "launch",
    "preLaunchTask": "bridge-to-kubernetes.compound",
    "program": "${workspaceFolder}/src/Web/bin/Debug/net7.0/Web.dll",
    "args": [],
    "cwd": "${workspaceFolder}/src/Web",
    "stopAtEntry": false,
    "env": {
    "ASPNETCORE_ENVIRONMENT": "Development",
    "ASPNETCORE_URLS": "http://+:5001"
    },
    "sourceFileMap": {
    "/Views": "${workspaceFolder}/Views"
    }
    }

    Launch the debug configuration

    We can start the process with the .NET Core Launch (web) with Kubernetes launch configuration in the Debug pane in Visual Studio Code.

    Launch the `.NET Core Launch (web) with Kubernetes` from the Debug pane in Visual Studio Code

    Enable the Endpoint Manager

    Part of this process includes a local service to help manage the traffic routing and your hosts file. This will require admin or sudo privileges. On Windows, you'll get a prompt like:

    Prompt to launch the endpoint manager.

    Access your Kubernetes cluster "locally"

    Bridge to Kubernetes will set up a tunnel (thanks to port forwarding) to your local workstation and create local endpoints for the other Kubernetes hosted services in your cluster, as well as pretending to be a pod in that cluster (for the application you are debugging).

    Output from Bridge To Kubernetes setup task.

    After making the connection to your Kubernetes cluster, the launch configuration will continue. In this case, we'll make a debug build of the application and attach the debugger. (This process may cause the terminal in VS Code to scroll with build output. You can find the Bridge to Kubernetes output with the local IP addresses and ports in the Output pane for Bridge to Kubernetes.)

    You can set breakpoints, use your debug console, set watches, run tests against your local version of the service.

    Exploring the Running Application Environment

    One of the cool things that Bridge to Kubernetes does for our debugging experience is bring the environment configuration that our deployed pod would inherit. When we launch our app, it'll see configuration and secrets that we'd expect our pod to be running with.

    To test this, we'll set a breakpoint in our application's start up to see what SQL Server is being used. We'll set a breakpoint at src/Infrastructure/Dependencies.cs on line 32.

    Then, we will start debugging the application with Bridge to Kubernetes. When it hits the breakpoint, we'll open the Debug pane and type configuration.GetConnectionString("CatalogConnection").

    When we run locally (not with Bridge to Kubernetes), we'd see:

    configuration.GetConnectionString("CatalogConnection")
    "Server=(localdb)\\mssqllocaldb;Integrated Security=true;Initial Catalog=Microsoft.eShopOnWeb.CatalogDb;"

    But, with Bridge to Kubernetes we see something more like (yours will vary based on the password ):

    configuration.GetConnectionString("CatalogConnection")
    "Server=db;Database=Microsoft.eShopOnWeb.CatalogDb;User Id=sa;Password=*****************;TrustServerCertificate=True;"

    Debugging our local application connected to Kubernetes.

    We can see that the database server configured is based on our db service and the password is pulled from our secret in Azure KeyVault (via AKS).

    This helps us run our local application just like it was actually in our cluster.

    Going Further

    Bridge to Kubernetes also supports more advanced scenarios and, as you need to start routing traffic around inside your cluster, may require you to modify your application to pass along a kubernetes-route-as header to help ensure that traffic for your debugging workloads is properly handled. The docs go into much greater detail about that.

    Instrumentation

    Now that we've figured out our debugging story, we'll need to ensure that we have the right context clues to find where we need to debug or to give us a better idea of how well our microservices are running.

    Logging and Tracing

    Logging and tracing become even more critical in Kubernetes, where your application could be running in a number of pods across different nodes. When you have an issue, in addition to the normal application data, you'll want to know what pod and what node had the issue, what the state of those resources were (were you resource constrained or were shared resources unavailable?), and if autoscaling is enabled, you'll want to know if a scale event has been triggered. There are a multitude of other concerns based on your application and the environment you maintain.

    Given these informational needs, it's crucial to revisit your existing logging and instrumentation. Most frameworks and languages have extensible logging, tracing, and instrumentation libraries that you can iteratively add information to, such as pod and node states, and ensuring that requests can be traced across your microservices. This will pay you back time and time again when you have to troubleshoot issues in your existing environment.

    Centralized Logging

    To enhance the troubleshooting process further, it's important to implement centralized logging to consolidate logs from all your microservices into a single location. This makes it easier to search and analyze logs when you're troubleshooting an issue.

    Automated Alerting

    Additionally, implementing automated alerting, such as sending notifications when specific conditions occur in the logs, can help you detect issues before they escalate.

    End to end Visibility

    End-to-end visibility is also essential in understanding the flow of requests and responses between microservices in a distributed system. With end-to-end visibility, you can quickly identify bottlenecks and slowdowns in the system, helping you to resolve issues more efficiently.

    Resources

    Take the Cloud Skills Challenge!

    Enroll in the Cloud Skills Challenge!

    Don't miss out on this opportunity to level up your skills and stay ahead of the curve in the world of cloud native.

    - - + + \ No newline at end of file diff --git a/cnny-2023/bring-your-app-day-5/index.html b/cnny-2023/bring-your-app-day-5/index.html index 33771eaa43..102ab2ef1b 100644 --- a/cnny-2023/bring-your-app-day-5/index.html +++ b/cnny-2023/bring-your-app-day-5/index.html @@ -14,13 +14,13 @@ - - + +

    3-5. Bringing Your Application to Kubernetes - CI/CD Secure Supply Chain

    · 6 min read
    Josh Duffney

    Welcome to Day 5 of Week 3 of #CloudNativeNewYear!

    The theme for this week is Bringing Your Application to Kubernetes. Yesterday we talked about debugging and instrumenting our application. Today we'll explore the topic of container image signing and secure supply chain.

    Ask the Experts Thursday, February 9th at 9 AM PST
    Friday, February 10th at 11 AM PST

    Watch the recorded demo and conversation about this week's topics.

    We were live on YouTube walking through today's (and the rest of this week's) demos. Join us Friday, February 10th and bring your questions!

    What We'll Cover

    • Introduction
    • Prerequisites
    • Create a digital signing certificate
    • Generate an Azure Container Registry Token
    • Set up Notation
    • Install the Notation Azure Key Vault Plugin
    • Add the signing Certificate to Notation
    • Sign Container Images
    • Conclusion

    Introduction

    The secure supply chain is a crucial aspect of software development, delivery, and deployment, and digital signing plays a critical role in this process.

    By using digital signatures to verify the authenticity and integrity of container images, organizations can improve the security of your software supply chain and reduce the risk of security breaches and data compromise.

    In this article, you'll learn how to use Notary, an open-source project hosted by the Cloud Native Computing Foundation (CNCF) to digitally sign container images stored on Azure Container Registry.

    Prerequisites

    To follow along, you'll need an instance of:

    Create a digital signing certificate

    A digital signing certificate is a certificate that is used to digitally sign and verify the authenticity and integrity of digital artifacts. Such documents, software, and of course container images.

    Before you can implement digital signatures, you must first create a digital signing certificate.

    Run the following command to generate the certificate:

    1. Create the policy file

      cat <<EOF > ./my_policy.json
      {
      "issuerParameters": {
      "certificateTransparency": null,
      "name": "Self"
      },
      "x509CertificateProperties": {
      "ekus": [
      "1.3.6.1.5.5.7.3.3"
      ],
      "key_usage": [
      "digitalSignature"
      ],
      "subject": "CN=${keySubjectName}",
      "validityInMonths": 12
      }
      }
      EOF

      The ekus and key usage of this certificate policy dictate that the certificate can only be used for digital signatures.

    2. Create the certificate in Azure Key Vault

      az keyvault certificate create --name $keyName --vault-name $keyVaultName --policy @my_policy.json

      Replace $keyName and $keyVaultName with your desired certificate name and Azure Key Vault instance name.

    Generate a Azure Container Registry token

    Azure Container Registry tokens are used to grant access to the contents of the registry. Tokens can be used for a variety of things such as pulling images, pushing images, or managing the registry.

    As part of the container image signing workflow, you'll need a token to authenticate the Notation CLI with your Azure Container Registry.

    Run the following command to generate an ACR token:

    az acr token create \
    --name $tokenName \
    --registry $registry \
    --scope-map _repositories_admin \
    --query 'credentials.passwords[0].value' \
    --only-show-errors \
    --output tsv

    Replace $tokenName with your name for the ACR token and $registry with the name of your ACR instance.

    Setup Notation

    Notation is the command-line interface for the CNCF Notary project. You'll use it to digitally sign the api and web container images for the eShopOnWeb application.

    Run the following commands to download and install the NotationCli:

    1. Open a terminal or command prompt window

    2. Download the Notary notation release

      curl -Lo notation.tar.gz https://github.com/notaryproject/notation/releases/download/v1.0.0-rc.1/notation_1.0.0-rc.1_linux_amd64.tar.gz > /dev/null 2>&1

      If you're not using Linux, you can find the releases here.

    3. Extract the contents of the notation.tar.gz

      tar xvzf notation.tar.gz > /dev/null 2>&1
    4. Copy the notation binary to the $HOME/bin directory

      cp ./notation $HOME/bin
    5. Add the $HOME/bin directory to the PATH environment variable

      export PATH="$HOME/bin:$PATH"
    6. Remove the downloaded files

      rm notation.tar.gz LICENSE
    7. Check the notation version

      notation --version

    Install the Notation Azure Key Vault plugin

    By design the NotationCli supports plugins that extend its digital signing capabilities to remote registries. And in order to sign your container images stored in Azure Container Registry, you'll need to install the Azure Key Vault plugin for Notation.

    Run the following commands to install the azure-kv plugin:

    1. Download the plugin

      curl -Lo notation-azure-kv.tar.gz \
      https://github.com/Azure/notation-azure-kv/releases/download/v0.5.0-rc.1/notation-azure-kv_0.5.0-rc.1_linux_amd64.tar.gz > /dev/null 2>&1

      Non-Linux releases can be found here.

    2. Extract to the plugin directory & delete download files

      tar xvzf notation-azure-kv.tar.gz -C ~/.config/notation/plugins/azure-kv notation-azure-kv > /dev/null 2>&

      rm -rf notation-azure-kv.tar.gz
    3. Verify the plugin was installed

      notation plugin ls

    Add the signing certificate to Notation

    Now that you have Notation and the Azure Key Vault plugin installed, add the certificate's keyId created above to Notation.

    1. Get the Certificate Key ID from Azure Key Vault

      az keyvault certificate show \
      --vault-name $keyVaultName \
      --name $keyName \
      --query "kid" --only-show-errors --output tsv

      Replace $keyVaultName and $keyName with the appropriate information.

    2. Add the Key ID to KMS using Notation

      notation key add --plugin azure-kv --id $keyID $keyName
    3. Check the key list

      notation key ls

    Sign Container Images

    At this point, all that's left is to sign the container images.

    Run the notation sign command to sign the api and web container images:

    notation sign $registry.azurecr.io/web:$tag \
    --username $tokenName \
    --password $tokenPassword

    notation sign $registry.azurecr.io/api:$tag \
    --username $tokenName \
    --password $tokenPassword

    Replace $registry, $tag, $tokenName, and $tokenPassword with the appropriate values. To improve security, use a SHA hash for the tag.

    NOTE: If you didn't take note of the token password, you can rerun the az acr token create command to generate a new password.

    Conclusion

    Digital signing plays a critical role in ensuring the security of software supply chains.

    By signing software components, organizations can verify the authenticity and integrity of software, helping to prevent unauthorized modifications, tampering, and malware.

    And if you want to take digital signing to a whole new level by using them to prevent the deployment of unsigned container images, check out the Ratify project on GitHub!

    Take the Cloud Skills Challenge!

    Enroll in the Cloud Skills Challenge!

    Don't miss out on this opportunity to level up your skills and stay ahead of the curve in the world of cloud native.

    - - + + \ No newline at end of file diff --git a/cnny-2023/building-with-draft/index.html b/cnny-2023/building-with-draft/index.html index d86a8f912b..128d843454 100644 --- a/cnny-2023/building-with-draft/index.html +++ b/cnny-2023/building-with-draft/index.html @@ -14,13 +14,13 @@ - - + +

    4-2. Jumpstart your applications with Draft

    · 3 min read
    Cory Skimming

    It's the final week of #CloudNativeNewYear! This week we'll go further with Cloud-native by exploring advanced topics and best practices for the Cloud-native practitioner. In today's post, we will introduce you to the basics of the open-source project Draft and how it can be used to easily create and deploy applications to Kubernetes.

    It's not too late to sign up for and complete the Cloud Skills Challenge!

    What We'll Cover

    • What is Draft?
    • Draft basics
    • Demo: Developing to AKS with Draft
    • Resources


    What is Draft?

    Draft is an open-source tool that can be used to streamline the development and deployment of applications on Kubernetes clusters. It provides a simple and easy-to-use workflow for creating and deploying applications, making it easier for developers to focus on writing code and building features, rather than worrying about the underlying infrastructure. This is great for users who are just getting started with Kubernetes, or those who are just looking to simplify their experience.

    New to Kubernetes?

    Draft basics

    Draft streamlines Kubernetes development by taking a non-containerized application and generating the Dockerfiles, K8s manifests, Helm charts, and other artifacts associated with a containerized application. Draft can also create a GitHub Action workflow file to quickly build and deploy your application onto any Kubernetes cluster.

    1. 'draft create'': Create a new Draft project by simply running the 'draft create' command - this command will walk you through a series of questions on your application specification (such as the application language) and create a Dockerfile, Helm char, and Kubernetes
    2. 'draft generate-workflow'': Automatically build out a GitHub Action using the 'draft generate-workflow' command
    3. 'draft setup-gh'': If you are using Azure, use this command to automate the GitHub OIDC set up process to ensure that you will be able to deploy your application using your GitHub Action.

    At this point, you will have all the files needed to deploy your app onto a Kubernetes cluster (we told you it was easy!).

    You can also use the 'draft info' command if you are looking for information on supported languages and deployment types. Let's see it in action, shall we?


    Developing to AKS with Draft

    In this Microsoft Reactor session below, we'll briefly introduce Kubernetes and the Azure Kubernetes Service (AKS) and then demo how enable your applications for Kubernetes using the open-source tool Draft. We'll show how Draft can help you create the boilerplate code to containerize your applications and add routing and scaling behaviours.

    ##Conclusion

    Overall, Draft simplifies the process of building, deploying, and managing applications on Kubernetes, and can make the overall journey from code to Kubernetes significantly easier.


    Resources


    - - + + \ No newline at end of file diff --git a/cnny-2023/cloud-native-fundamentals/index.html b/cnny-2023/cloud-native-fundamentals/index.html index a5716bdb35..41a6c0f991 100644 --- a/cnny-2023/cloud-native-fundamentals/index.html +++ b/cnny-2023/cloud-native-fundamentals/index.html @@ -14,14 +14,14 @@ - - + +

    1-1. Cloud-native Fundamentals

    · 5 min read
    Cory Skimming

    Welcome to Week 1 of #CloudNativeNewYear!

    Cloud-native New Year

    You will often hear the term "cloud-native" when discussing modern application development, but even a quick online search will return a huge number of articles, tweets, and web pages with a variety of definitions. So, what does cloud-native actually mean? Also, what makes an application a cloud-native application versus a "regular" application?

    Today, we will address these questions and more as we kickstart our learning journey (and our new year!) with an introductory dive into the wonderful world of cloud-native.


    What We'll Cover

    • What is cloud-native?
    • What is a cloud-native application?
    • The benefits of cloud-native
    • The five pillars of cloud-native
    • Exercise: Take the Cloud Skills Challenge!

    1. What is cloud-native?

    The term "cloud-native" can seem pretty self-evident (yes, hello, native to the cloud?), and in a way, it is. While there are lots of definitions of cloud-native floating around, at it's core, cloud-native simply refers to a modern approach to building software that takes advantage of cloud services and environments. This includes using cloud-native technologies, such as containers, microservices, and serverless, and following best practices for deploying, scaling, and managing applications in a cloud environment.

    Official definition from the Cloud Native Computing Foundation:

    Cloud-native technologies empower organizations to build and run scalable applications in modern, dynamic environments such as public, private, and hybrid clouds. Containers, service meshes, microservices, immutable infrastructure, and declarative APIs exemplify this approach.

    These techniques enable loosely coupled systems that are resilient, manageable, and observable. Combined with robust automation, they allow engineers to make high-impact changes frequently and predictably with minimal toil. Source


    2. So, what exactly is a cloud-native application?

    Cloud-native applications are specifically designed to take advantage of the scalability, resiliency, and distributed nature of modern cloud infrastructure. But how does this differ from a "traditional" application?

    Traditional applications are generally been built, tested, and deployed as a single, monolithic unit. The monolithic nature of this type of architecture creates close dependencies between components. This complexity and interweaving only increases as an application grows and can make it difficult to evolve (not to mention troubleshoot) and challenging to operate over time.

    To contrast, in cloud-native architectures the application components are decomposed into loosely coupled services, rather than built and deployed as one block of code. This decomposition into multiple self-contained services enables teams to manage complexity and improve the speed, agility, and scale of software delivery. Many small parts enables teams to make targeted updates, deliver new features, and fix any issues without leading to broader service disruption.


    3. The benefits of cloud-native

    Cloud-native architectures can bring many benefits to an organization, including:

    1. Scalability: easily scale up or down based on demand, allowing organizations to adjust their resource usage and costs as needed.
    2. Flexibility: deploy and run on any cloud platform, and easily move between clouds and on-premises environments.
    3. High-availability: techniques such as redundancy, self-healing, and automatic failover help ensure that cloud-native applications are designed to be highly-available and fault tolerant.
    4. Reduced costs: take advantage of the pay-as-you-go model of cloud computing, reducing the need for expensive infrastructure investments.
    5. Improved security: tap in to cloud security features, such as encryption and identity management, to improve the security of the application.
    6. Increased agility: easily add new features or services to your applications to meet changing business needs and market demand.

    4. The pillars of cloud-native

    There are five areas that are generally cited as the core building blocks of cloud-native architecture:

    1. Microservices: Breaking down monolithic applications into smaller, independent, and loosely-coupled services that can be developed, deployed, and scaled independently.
    2. Containers: Packaging software in lightweight, portable, and self-sufficient containers that can run consistently across different environments.
    3. Automation: Using automation tools and DevOps processes to manage and operate the cloud-native infrastructure and applications, including deployment, scaling, monitoring, and self-healing.
    4. Service discovery: Using service discovery mechanisms, such as APIs & service meshes, to enable services to discover and communicate with each other.
    5. Observability: Collecting and analyzing data from the infrastructure and applications to understand and optimize the performance, behavior, and health of the system.

    These can (and should!) be used in combination to deliver cloud-native solutions that are highly scalable, flexible, and available.

    WHAT'S NEXT

    Stay tuned, as we will be diving deeper into these topics in the coming weeks:

    • Jan 24: Containers 101
    • Jan 25: Adopting Microservices with Kubernetes
    • Jan 26: Kubernetes 101
    • Jan 27: Exploring your Cloud-native Options

    Resources


    Don't forget to subscribe to the blog to get daily posts delivered directly to your favorite feed reader!


    - - + + \ No newline at end of file diff --git a/cnny-2023/cnny-kickoff/index.html b/cnny-2023/cnny-kickoff/index.html index 070438bd1b..8f7f03e5ab 100644 --- a/cnny-2023/cnny-kickoff/index.html +++ b/cnny-2023/cnny-kickoff/index.html @@ -14,13 +14,13 @@ - - + +

    Kicking Off 30DaysOfCloudNative!

    · 4 min read
    Cory Skimming
    Devanshi Joshi
    Steven Murawski
    Nitya Narasimhan

    Welcome to the Kick-off Post for #30DaysOfCloudNative - one of the core initiatives within #CloudNativeNewYear! Over the next four weeks, join us as we take you from fundamentals to functional usage of Cloud-native technologies, one blog post at a time! Read on to learn a little bit about this initiative and what you can expect to learn from this journey!

    What We'll Cover


    Cloud-native New Year

    Welcome to Week 01 of 🥳 #CloudNativeNewYear ! Today, we kick off a full month of content and activities to skill you up on all things Cloud-native on Azure with content, events, and community interactions! Read on to learn about what we have planned!


    Explore our initiatives

    We have a number of initiatives planned for the month to help you learn and skill up on relevant technologies. Click on the links to visit the relevant pages for each.

    We'll go into more details about #30DaysOfCloudNative in this post - don't forget to subscribe to the blog to get daily posts delivered directly to your preferred feed reader!


    Register for events!

    What are 3 things you can do today, to jumpstart your learning journey?


    #30DaysOfCloudNative

    #30DaysOfCloudNative is a month-long series of daily blog posts grouped into 4 themed weeks - taking you from core concepts to end-to-end solution examples in 30 days. Each article will be short (5-8 mins reading time) and provide exercises and resources to help you reinforce learnings and take next steps.

    This series focuses on the Cloud-native On Azure learning journey in four stages, each building on the previous week to help you skill up in a beginner-friendly way:

    We have a tentative weekly-themed roadmap for the topics we hope to cover and will keep this updated as we go with links to actual articles as they get published.

    Week 1: FOCUS ON CLOUD-NATIVE FUNDAMENTALS

    Here's a sneak peek at the week 1 schedule. We'll start with a broad review of cloud-native fundamentals and walkthrough the core concepts of microservices, containers and Kubernetes.

    • Jan 23: Learn Core Concepts for Cloud-native
    • Jan 24: Container 101
    • Jan 25: Adopting Microservices with Kubernetes
    • Jan 26: Kubernetes 101
    • Jan 27: Exploring your Cloud Native Options

    Let's Get Started!

    Now you know everything! We hope you are as excited as we are to dive into a full month of active learning and doing! Don't forget to subscribe for updates in your favorite feed reader! And look out for our first Cloud-native Fundamentals post on January 23rd!


    - - + + \ No newline at end of file diff --git a/cnny-2023/cnny-wrap-up/index.html b/cnny-2023/cnny-wrap-up/index.html index 2b662fb13d..46801f0722 100644 --- a/cnny-2023/cnny-wrap-up/index.html +++ b/cnny-2023/cnny-wrap-up/index.html @@ -14,13 +14,13 @@ - - + +

    4-5. Cloud Native New Year Wrap Up

    · 6 min read
    Cory Skimming
    Steven Murawski
    Paul Yu
    Josh Duffney
    Nitya Narasimhan
    Vinicius Apolinario
    Jorge Arteiro
    Devanshi Joshi

    And that's a wrap on the inaugural #CloudNativeNewYear! Thank you for joining us to kick off the new year with this learning journey into cloud-native! In this final post of the 2023 celebration of all things cloud-native, we'll do two things:

    • Look Back - with a quick retrospective of what was covered.
    • Look Ahead - with resources and suggestions for how you can continue your skilling journey!

    We appreciate your time and attention and we hope you found this curated learning valuable. Feedback and suggestions are always welcome. From our entire team, we wish you good luck with the learning journey - now go build some apps and share your knowledge! 🎉


    What We'll Cover

    • Cloud-native fundamentals
    • Kubernetes fundamentals
    • Bringing your applications to Kubernetes
    • Go further with cloud-native
    • Resources to keep the celebration going!

    Week 1: Cloud-native Fundamentals

    In Week 1, we took a tour through the fundamentals of cloud-native technologies, including a walkthrough of the core concepts of containers, microservices, and Kubernetes.

    • Jan 23 - Cloud-native Fundamentals: The answers to life and all the universe - what is cloud-native? What makes an application cloud-native? What are the benefits? (yes, we all know it's 42, but hey, gotta start somewhere!)
    • Jan 24 - Containers 101: Containers are an essential component of cloud-native development. In this intro post, we cover how containers work and why they have become so popular.
    • Jan 25 - Kubernetes 101: Kuber-what-now? Learn the basics of Kubernetes and how it enables us to deploy and manage our applications effectively and consistently.
    A QUICKSTART GUIDE TO KUBERNETES CONCEPTS

    Missed it Live? Tune in to A Quickstart Guide to Kubernetes Concepts on demand, now!

    • Jan 26 - Microservices 101: What is a microservices architecture and how can we go about designing one?
    • Jan 27 - Exploring your Cloud Native Options: Cloud-native, while catchy, can be a very broad term. What technologies should you use? Learn some basic guidelines for when it is optimal to use different technologies for your project.

    Week 2: Kubernetes Fundamentals

    In Week 2, we took a deeper dive into the Fundamentals of Kubernetes. The posts and live demo from this week took us through how to build a simple application on Kubernetes, covering everything from deployment to networking and scaling. Note: for our samples and demo we have used Azure Kubernetes Service, but the principles apply to any Kubernetes!

    • Jan 30 - Pods and Deployments: how to use pods and deployments in Kubernetes.
    • Jan 31 - Services and Ingress: how to use services and ingress and a walk through the steps of making our containers accessible internally and externally!
    • Feb 1 - ConfigMaps and Secrets: how to of passing configuration and secrets to our applications in Kubernetes with ConfigMaps and Secrets.
    • Feb 2 - Volumes, Mounts, and Claims: how to use persistent storage on Kubernetes (and ensure your data can survive container restarts!).
    • Feb 3 - Scaling Pods and Nodes: how to scale pods and nodes in our Kubernetes cluster.
    ASK THE EXPERTS: AZURE KUBERNETES SERVICE

    Missed it Live? Tune in to Ask the Expert with Azure Kubernetes Service on demand, now!


    Week 3: Bringing your applications to Kubernetes

    So, you have learned how to build an application on Kubernetes. What about your existing applications? In Week 3, we explored how to take an existing application and set it up to run in Kubernetes:

    • Feb 6 - CI/CD: learn how to get an existing application running in Kubernetes with a full pipeline in GitHub Actions.
    • Feb 7 - Adapting Storage, Secrets, and Configuration: how to evaluate our sample application's configuration, storage, and networking requirements and implement using Kubernetes.
    • Feb 8 - Opening your Application with Ingress: how to expose the eShopOnWeb app so that customers can reach it over the internet using a custom domain name and TLS.
    • Feb 9 - Debugging and Instrumentation: how to debug and instrument your application now that it is on Kubernetes.
    • Feb 10 - CI/CD Secure Supply Chain: now that we have set up our application on Kubernetes, let's talk about container image signing and how to set up a secure supply change.

    Week 4: Go Further with Cloud-Native

    This week we have gone further with Cloud-native by exploring advanced topics and best practices for the Cloud-native practitioner.

    And today, February 17th, with this one post to rule (er, collect) them all!


    Keep the Learning Going!

    Learning is great, so why stop here? We have a host of great resources and samples for you to continue your cloud-native journey with Azure below:


    - - + + \ No newline at end of file diff --git a/cnny-2023/containers-101/index.html b/cnny-2023/containers-101/index.html index 056beeccf0..15c79fae75 100644 --- a/cnny-2023/containers-101/index.html +++ b/cnny-2023/containers-101/index.html @@ -14,14 +14,14 @@ - - + +

    1-2. Containers 101

    · 4 min read
    Steven Murawski
    Paul Yu
    Josh Duffney

    Welcome to Day 2 of Week 1 of #CloudNativeNewYear!

    Today, we'll focus on building an understanding of containers.

    What We'll Cover

    • Introduction
    • How do Containers Work?
    • Why are Containers Becoming so Popular?
    • Conclusion
    • Resources
    • Learning Path

    REGISTER & LEARN: KUBERNETES 101

    Interested in a dive into Kubernetes and a chance to talk to experts?

    🎙: Join us Jan 26 @1pm PST by registering here

    Here's what you will learn:

    • Key concepts and core principles of Kubernetes.
    • How to deploy, scale and manage containerized workloads.
    • Live Demo of the concepts explained
    • How to get started with Azure Kubernetes Service for free.

    Start your free Azure Kubernetes Trial Today!!: aka.ms/TryAKS

    Introduction

    In the beginning, we deployed our applications onto physical servers. We only had a certain number of those servers, so often they hosted multiple applications. This led to some problems when those applications shared dependencies. Upgrading one application could break another application on the same server.

    Enter virtualization. Virtualization allowed us to run our applications in an isolated operating system instance. This removed much of the risk of updating shared dependencies. However, it increased our overhead since we had to run a full operating system for each application environment.

    To address the challenges created by virtualization, containerization was created to improve isolation without duplicating kernel level resources. Containers provide efficient and consistent deployment and runtime experiences for our applications and have become very popular as a way of packaging and distributing applications.

    How do Containers Work?

    Containers build on two capabilities in the Linux operating system, namespaces and cgroups. These constructs allow the operating system to provide isolation to a process or group of processes, keeping their access to filesystem resources separate and providing controls on resource utilization. This, combined with tooling to help package, deploy, and run container images has led to their popularity in today’s operating environment. This provides us our isolation without the overhead of additional operating system resources.

    When a container host is deployed on an operating system, it works at scheduling the access to the OS (operating systems) components. This is done by providing a logical isolated group that can contain processes for a given application, called a namespace. The container host then manages /schedules access from the namespace to the host OS. The container host then uses cgroups to allocate compute resources. Together, the container host with the help of cgroups and namespaces can schedule multiple applications to access host OS resources.

    Overall, this gives the illusion of virtualizing the host OS, where each application gets its own OS. In actuality, all the applications are running on the same operating system and sharing the same kernel as the container host.

    Containers are popular in the software development industry because they provide several benefits over traditional virtualization methods. Some of these benefits include:

    • Portability: Containers make it easy to move an application from one environment to another without having to worry about compatibility issues or missing dependencies.
    • Isolation: Containers provide a level of isolation between the application and the host system, which means that the application running in the container cannot access the host system's resources.
    • Scalability: Containers make it easy to scale an application up or down as needed, which is useful for applications that experience a lot of traffic or need to handle a lot of data.
    • Resource Efficiency: Containers are more resource-efficient than traditional virtualization methods because they don't require a full operating system to be running on each virtual machine.
    • Cost-Effective: Containers are more cost-effective than traditional virtualization methods because they don't require expensive hardware or licensing fees.

    Conclusion

    Containers are a powerful technology that allows developers to package and deploy applications in a portable and isolated environment. This technology is becoming increasingly popular in the world of software development and is being used by many companies and organizations to improve their application deployment and management processes. With the benefits of portability, isolation, scalability, resource efficiency, and cost-effectiveness, containers are definitely worth considering for your next application development project.

    Containerizing applications is a key step in modernizing them, and there are many other patterns that can be adopted to achieve cloud-native architectures, including using serverless platforms, Kubernetes, and implementing DevOps practices.

    Resources

    Learning Path

    - - + + \ No newline at end of file diff --git a/cnny-2023/explore-options/index.html b/cnny-2023/explore-options/index.html index d7f2320f94..0e0291e106 100644 --- a/cnny-2023/explore-options/index.html +++ b/cnny-2023/explore-options/index.html @@ -14,14 +14,14 @@ - - + +

    1-5. Exploring Cloud-Native Options

    · 6 min read
    Cory Skimming

    We are excited to be wrapping up our first week of #CloudNativeNewYear! This week, we have tried to set the stage by covering the fundamentals of cloud-native practices and technologies, including primers on containerization, microservices, and Kubernetes.

    Don't forget to sign up for the the Cloud Skills Challenge!

    Today, we will do a brief recap of some of these technologies and provide some basic guidelines for when it is optimal to use each.


    What We'll Cover

    • To Containerize or not to Containerize?
    • The power of Kubernetes
    • Where does Serverless fit?
    • Resources
    • What's coming next!


    Just joining us now? Check out these other Week 1 posts:

    To Containerize or not to Containerize?

    As mentioned in our Containers 101 post earlier this week, containers can provide several benefits over traditional virtualization methods, which has made them popular within the software development community. Containers provide a consistent and predictable runtime environment, which can help reduce the risk of compatibility issues and simplify the deployment process. Additionally, containers can improve resource efficiency by allowing multiple applications to run on the same host while isolating their dependencies.

    Some types of apps that are a particularly good fit for containerization include:

    1. Microservices: Containers are particularly well-suited for microservices-based applications, as they can be used to isolate and deploy individual components of the system. This allows for more flexibility and scalability in the deployment process.
    2. Stateless applications: Applications that do not maintain state across multiple sessions, such as web applications, are well-suited for containers. Containers can be easily scaled up or down as needed and replaced with new instances, without losing data.
    3. Portable applications: Applications that need to be deployed in different environments, such as on-premises, in the cloud, or on edge devices, can benefit from containerization. The consistent and portable runtime environment of containers can make it easier to move the application between different environments.
    4. Legacy applications: Applications that are built using older technologies or that have compatibility issues can be containerized to run in an isolated environment, without impacting other applications or the host system.
    5. Dev and testing environments: Containerization can be used to create isolated development and testing environments, which can be easily created and destroyed as needed.

    While there are many types of applications that can benefit from a containerized approach, it's worth noting that containerization is not always the best option, and it's important to weigh the benefits and trade-offs before deciding to containerize an application. Additionally, some types of applications may not be a good fit for containers including:

    • Apps that require full access to host resources: Containers are isolated from the host system, so if an application needs direct access to hardware resources such as GPUs or specialized devices, it might not work well in a containerized environment.
    • Apps that require low-level system access: If an application requires deep access to the underlying operating system, it may not be suitable for running in a container.
    • Applications that have specific OS dependencies: Apps that have specific dependencies on a certain version of an operating system or libraries may not be able to run in a container.
    • Stateful applications: Apps that maintain state across multiple sessions, such as databases, may not be well suited for containers. Containers are ephemeral by design, so the data stored inside a container may not persist between restarts.

    The good news is that some of these limitations can be overcome with the use of specialized containerization technologies such as Kubernetes, and by carefully designing the architecture of the application.


    The power of Kubernetes

    Speaking of Kubernetes...

    Kubernetes is a powerful tool for managing and deploying containerized applications in production environments, particularly for applications that need to scale, handle large numbers of requests, or run in multi-cloud or hybrid environments.

    Kubernetes is well-suited for a wide variety of applications, but it is particularly well-suited for the following types of applications:

    1. Microservices-based applications: Kubernetes provides a powerful set of tools for managing and deploying microservices-based applications, making it easy to scale, update, and manage the individual components of the application.
    2. Stateful applications: Kubernetes provides support for stateful applications through the use of Persistent Volumes and StatefulSets, allowing for applications that need to maintain state across multiple instances.
    3. Large-scale, highly-available systems: Kubernetes provides built-in support for scaling, self-healing, and rolling updates, making it an ideal choice for large-scale, highly-available systems that need to handle large numbers of users and requests.
    4. Multi-cloud and hybrid environments: Kubernetes can be used to deploy and manage applications across multiple cloud providers and on-premises environments, making it a good choice for organizations that want to take advantage of the benefits of multiple cloud providers or that need to deploy applications in a hybrid environment.
    New to Kubernetes?

    Where does Serverless fit in?

    Serverless is a cloud computing model where the cloud provider (like Azure) is responsible for executing a piece of code by dynamically allocating the resources. With serverless, you only pay for the exact amount of compute time that you use, rather than paying for a fixed amount of resources. This can lead to significant cost savings, particularly for applications with variable or unpredictable workloads.

    Serverless is commonly used for building applications like web or mobile apps, IoT, data processing, and real-time streaming - apps where the workloads are variable and high scalability is required. It's important to note that serverless is not a replacement for all types of workloads - it's best suited for stateless, short-lived and small-scale workloads.

    For a detailed look into the world of Serverless and lots of great learning content, revisit #30DaysofServerless.


    Resources


    What's up next in #CloudNativeNewYear?

    Week 1 has been all about the fundamentals of cloud-native. Next week, the team will be diving in to application deployment with Azure Kubernetes Service. Don't forget to subscribe to the blog to get daily posts delivered directly to your favorite feed reader!


    - - + + \ No newline at end of file diff --git a/cnny-2023/fundamentals-day-1/index.html b/cnny-2023/fundamentals-day-1/index.html index f7a5b22718..01d4649653 100644 --- a/cnny-2023/fundamentals-day-1/index.html +++ b/cnny-2023/fundamentals-day-1/index.html @@ -14,13 +14,13 @@ - - + +

    2-1. Kubernetes Fundamentals - Pods and Deployments

    · 14 min read
    Steven Murawski

    Welcome to Day #1 of Week 2 of #CloudNativeNewYear!

    The theme for this week is Kubernetes fundamentals. Last week we talked about Cloud Native architectures and the Cloud Native landscape. Today we'll explore the topic of Pods and Deployments in Kubernetes.

    Ask the Experts Thursday, February 9th at 9 AM PST
    Catch the Replay of the Live Demo

    Watch the recorded demo and conversation about this week's topics.

    We were live on YouTube walking through today's (and the rest of this week's) demos.

    What We'll Cover

    • Setting Up A Kubernetes Environment in Azure
    • Running Containers in Kubernetes Pods
    • Making the Pods Resilient with Deployments
    • Exercise
    • Resources

    Setting Up A Kubernetes Environment in Azure

    For this week, we'll be working with a simple app - the Azure Voting App. My teammate Paul Yu ported the app to Rust and we tweaked it a bit to let us highlight some of the basic features of Kubernetes.

    You should be able to replicate this in just about any Kubernetes environment, but we'll use Azure Kubernetes Service (AKS) as our working environment for this week.

    To make it easier to get started, there's a Bicep template to deploy an AKS cluster, an Azure Container Registry (ACR) (to host our container image), and connect the two so that we can easily deploy our application.

    Step 0 - Prerequisites

    There are a few things you'll need if you want to work through this and the following examples this week.

    Required:

    • Git (and probably a GitHub account if you want to persist your work outside of your computer)
    • Azure CLI
    • An Azure subscription (if you want to follow along with the Azure steps)
    • Kubectl (the command line tool for managing Kubernetes)

    Helpful:

    • Visual Studio Code (or equivalent editor)

    Step 1 - Clone the application repository

    First, I forked the source repository to my account.

    $GitHubOrg = 'smurawski' # Replace this with your GitHub account name or org name
    git clone "https://github.com/$GitHubOrg/azure-voting-app-rust"
    cd azure-voting-app-rust

    Leave your shell opened with your current location inside the application repository.

    Step 2 - Set up AKS

    Running the template deployment from the demo script (I'm using the PowerShell example in cnny23-week2-day1.ps1, but there's a Bash variant at cnny23-week2-day1.sh) stands up the environment. The second, third, and fourth commands take some of the output from the Bicep deployment to set up for later commands, so don't close out your shell after you run these commands.

    az deployment sub create --template-file ./deploy/main.bicep --location eastus --parameters 'resourceGroup=cnny-week2'
    $AcrName = az deployment sub show --name main --query 'properties.outputs.acr_name.value' -o tsv
    $AksName = az deployment sub show --name main --query 'properties.outputs.aks_name.value' -o tsv
    $ResourceGroup = az deployment sub show --name main --query 'properties.outputs.resource_group_name.value' -o tsv

    az aks get-credentials --resource-group $ResourceGroup --name $AksName

    Step 3 - Build our application container

    Since we have an Azure Container Registry set up, I'll use ACR Build Tasks to build and store my container image.

    az acr build --registry $AcrName --% --image cnny2023/azure-voting-app-rust:{{.Run.ID}} .
    $BuildTag = az acr repository show-tags `
    --name $AcrName `
    --repository cnny2023/azure-voting-app-rust `
    --orderby time_desc `
    --query '[0]' -o tsv
    tip

    Wondering what the --% is in the first command line? That tells the PowerShell interpreter to pass the input after it "as is" to the command without parsing/evaluating it. Otherwise, PowerShell messes a bit with the templated {{.Run.ID}} bit.

    Running Containers in Kubernetes Pods

    Now that we have our AKS cluster and application image ready to go, let's look into how Kubernetes runs containers.

    If you've been in tech for any length of time, you've seen that every framework, runtime, orchestrator, etc.. can have their own naming scheme for their concepts. So let's get into some of what Kubernetes calls things.

    The Pod

    A container running in Kubernetes is called a Pod. A Pod is basically a running container on a Node or VM. It can be more. For example you can run multiple containers and specify some funky configuration, but we'll keep it simple for now - add the complexity when you need it.

    Our Pod definition can be created via the kubectl command imperatively from arguments or declaratively from a configuration file. We'll do a little of both. We'll use the kubectl command to help us write our configuration files. Kubernetes configuration files are YAML, so having an editor that supports and can help you syntax check YAML is really helpful.

    Creating a Pod Definition

    Let's create a few Pod definitions. Our application requires two containers to get working - the application and a database.

    Let's create the database Pod first. And before you comment, the configuration isn't secure nor best practice. We'll fix that later this week. For now, let's focus on getting up and running.

    This is a trick I learned from one of my teammates - Paul. By using the --output yaml and --dry-run=client options, we can have the command help us write our YAML. And with a bit of output redirection, we can stash it safely in a file for later use.

    kubectl run azure-voting-db `
    --image "postgres:15.0-alpine" `
    --env "POSTGRES_PASSWORD=mypassword" `
    --output yaml `
    --dry-run=client > manifests/pod-db.yaml

    This creates a file that looks like:

    apiVersion: v1
    kind: Pod
    metadata:
    creationTimestamp: null
    labels:
    run: azure-voting-db
    name: azure-voting-db
    spec:
    containers:
    - env:
    - name: POSTGRES_PASSWORD
    value: mypassword
    image: postgres:15.0-alpine
    name: azure-voting-db
    resources: {}
    dnsPolicy: ClusterFirst
    restartPolicy: Always
    status: {}

    The file, when supplied to the Kubernetes API, will identify what kind of resource to create, the API version to use, and the details of the container (as well as an environment variable to be supplied).

    We'll get that container image started with the kubectl command. Because the details of what to create are in the file, we don't need to specify much else to the kubectl command but the path to the file.

    kubectl apply -f ./manifests/pod-db.yaml

    I'm going to need the IP address of the Pod, so that my application can connect to it, so we can use kubectl to get some information about our pod. By default, kubectl get pod only displays certain information but it retrieves a lot more. We can use the JSONPath syntax to index into the response and get the information you want.

    tip

    To see what you can get, I usually run the kubectl command with the output type (-o JSON) of JSON and then I can find where the data I want is and create my JSONPath query to get it.

    $DB_IP = kubectl get pod azure-voting-db -o jsonpath='{.status.podIP}'

    Now, let's create our Pod definition for our application. We'll use the same technique as before.

    kubectl run azure-voting-app `
    --image "$AcrName.azurecr.io/cnny2023/azure-voting-app-rust:$BuildTag" `
    --env "DATABASE_SERVER=$DB_IP" `
    --env "DATABASE_PASSWORD=mypassword`
    --output yaml `
    --dry-run=client > manifests/pod-app.yaml

    That command gets us a similar YAML file to the database container - you can see the full file here

    Let's get our application container running.

    kubectl apply -f ./manifests/pod-app.yaml

    Now that the Application is Running

    We can check the status of our Pods with:

    kubectl get pods

    And we should see something like:

    azure-voting-app-rust ❯  kubectl get pods
    NAME READY STATUS RESTARTS AGE
    azure-voting-app 1/1 Running 0 36s
    azure-voting-db 1/1 Running 0 84s

    Once our pod is running, we can check to make sure everything is working by letting kubectl proxy network connections to our Pod running the application. If we get the voting web page, we'll know the application found the database and we can start voting!

    kubectl port-forward pod/azure-voting-app 8080:8080

    Azure voting website in a browser with three buttons, one for Dogs, one for Cats, and one for Reset.  The counter is Dogs - 0 and Cats - 0.

    When you are done voting, you can stop the port forwarding by using Control-C to break the command.

    Clean Up

    Let's clean up after ourselves and see if we can't get Kubernetes to help us keep our application running. We can use the same configuration files to ensure that Kubernetes only removes what we want removed.

    kubectl delete -f ./manifests/pod-app.yaml
    kubectl delete -f ./manifests/pod-db.yaml

    Summary - Pods

    A Pod is the most basic unit of work inside Kubernetes. Once the Pod is deleted, it's gone. That leads us to our next topic (and final topic for today.)

    Making the Pods Resilient with Deployments

    We've seen how easy it is to deploy a Pod and get our containers running on Nodes in our Kubernetes cluster. But there's a problem with that. Let's illustrate it.

    Breaking Stuff

    Setting Back Up

    First, let's redeploy our application environment. We'll start with our application container.

    kubectl apply -f ./manifests/pod-db.yaml
    kubectl get pod azure-voting-db -o jsonpath='{.status.podIP}'

    The second command will report out the new IP Address for our database container. Let's open ./manifests/pod-app.yaml and update the container IP to our new one.

    - name: DATABASE_SERVER
    value: YOUR_NEW_IP_HERE

    Then we can deploy the application with the information it needs to find its database. We'll also list out our pods to see what is running.

    kubectl apply -f ./manifests/pod-app.yaml
    kubectl get pods

    Feel free to look back and use the port forwarding trick to make sure your app is running if you'd like.

    Knocking It Down

    The first thing we'll try to break is our application pod. Let's delete it.

    kubectl delete pod azure-voting-app

    Then, we'll check our pod's status:

    kubectl get pods

    Which should show something like:

    azure-voting-app-rust ❯  kubectl get pods
    NAME READY STATUS RESTARTS AGE
    azure-voting-db 1/1 Running 0 50s

    We should be able to recreate our application pod deployment with no problem, since it has the current database IP address and nothing else depends on it.

    kubectl apply -f ./manifests/pod-app.yaml

    Again, feel free to do some fun port forwarding and check your site is running.

    Uncomfortable Truths

    Here's where it gets a bit stickier, what if we delete the database container?

    If we delete our database container and recreate it, it'll likely have a new IP address, which would force us to update our application configuration. We'll look at some solutions for these problems in the next three posts this week.

    Because our database problem is a bit tricky, we'll primarily focus on making our application layer more resilient and prepare our database layer for those other techniques over the next few days.

    Let's clean back up and look into making things more resilient.

    kubectl delete -f ./manifests/pod-app.yaml
    kubectl delete -f ./manifests/pod-db.yaml

    The Deployment

    One of the reasons you may want to use Kubernetes is it's ability to orchestrate workloads. Part of that orchestration includes being able to ensure that certain workloads are running (regardless of what Node they might be on).

    We saw that we could delete our application pod and then restart it from the manifest with little problem. It just meant that we had to run a command to restart it. We can use the Deployment in Kubernetes to tell the orchestrator to ensure we have our application pod running.

    The Deployment also can encompass a lot of extra configuration - controlling how many containers of a particular type should be running, how upgrades of container images should proceed, and more.

    Creating the Deployment

    First, we'll create a Deployment for our database. We'll use a technique similar to what we did for the Pod, with just a bit of difference.

    kubectl create deployment azure-voting-db `
    --image "postgres:15.0-alpine" `
    --port 5432 `
    --output yaml `
    --dry-run=client > manifests/deployment-db.yaml

    Unlike our Pod definition creation, we can't pass in environment variable configuration from the command line. We'll have to edit the YAML file to add that.

    So, let's open ./manifests/deployment-db.yaml in our editor and add the following in the spec/containers configuration.

            env:
    - name: POSTGRES_PASSWORD
    value: "mypassword"

    Your file should look like this deployment-db.yaml.

    Once we have our configuration file updated, we can deploy our database container image.

    kubectl apply -f ./manifests/deployment-db.yaml

    For our application, we'll use the same technique.

    kubectl create deployment azure-voting-app `
    --image "$AcrName.azurecr.io/cnny2023/azure-voting-app-rust:$BuildTag" `
    --port 8080 `
    --output yaml `
    --dry-run=client > manifests/deployment-app.yaml

    Next, we'll need to add an environment variable to the generated configuration. We'll also need the new IP address for the database deployment.

    Previously, we named the pod and were able to ask for the IP address with kubectl and a bit of JSONPath. Now, the deployment created the pod for us, so there's a bit of random in the naming. Check out:

    kubectl get pods

    Should return something like:

    azure-voting-app-rust ❯  kubectl get pods
    NAME READY STATUS RESTARTS AGE
    azure-voting-db-686d758fbf-8jnq8 1/1 Running 0 7s

    We can either ask for the IP with the new pod name, or we can use a selector to find our desired pod.

    kubectl get pod --selector app=azure-voting-db -o jsonpath='{.items[0].status.podIP}'

    Now, we can update our application deployment configuration file with:

            env:
    - name: DATABASE_SERVER
    value: YOUR_NEW_IP_HERE
    - name: DATABASE_PASSWORD
    value: mypassword

    Your file should look like this deployment-app.yaml (but with IPs and image names matching your environment).

    After we save those changes, we can deploy our application.

    kubectl apply -f ./manifests/deployment-app.yaml

    Let's test the resilience of our app now. First, we'll delete the pod running our application, then we'll check to make sure Kubernetes restarted our application pod.

    kubectl get pods
    azure-voting-app-rust ❯  kubectl get pods
    NAME READY STATUS RESTARTS AGE
    azure-voting-app-56c9ccc89d-skv7x 1/1 Running 0 71s
    azure-voting-db-686d758fbf-8jnq8 1/1 Running 0 12m
    kubectl delete pod azure-voting-app-56c9ccc89d-skv7x
    kubectl get pods
    azure-voting-app-rust ❯  kubectl delete pod azure-voting-app-56c9ccc89d-skv7x
    >> kubectl get pods
    pod "azure-voting-app-56c9ccc89d-skv7x" deleted
    NAME READY STATUS RESTARTS AGE
    azure-voting-app-56c9ccc89d-2b5mx 1/1 Running 0 2s
    azure-voting-db-686d758fbf-8jnq8 1/1 Running 0 14m
    info

    Your Pods will likely have different identifiers at the end, so adjust your commands to match the names in your environment.

    As you can see, by the time the kubectl get pods command was run, Kubernetes had already spun up a new pod for the application container image. Thanks Kubernetes!

    Clean up

    Since we can't just delete the pods, we have to delete the deployments.

    kubectl delete -f ./manifests/deployment-app.yaml
    kubectl delete -f ./manifests/deployment-db.yaml

    Summary - Deployments

    Deployments allow us to create more durable configuration for the workloads we deploy into Kubernetes. As we dig deeper, we'll discover more capabilities the deployments offer. Check out the Resources below for more.

    Exercise

    If you want to try these steps, head over to the source repository, fork it, clone it locally, and give it a spin!

    You can check your manifests against the manifests in the week2/day1 branch of the source repository.

    Resources

    Take the Cloud Skills Challenge!

    Enroll in the Cloud Skills Challenge!

    Don't miss out on this opportunity to level up your skills and stay ahead of the curve in the world of cloud native.

    Documentation

    Training

    - - + + \ No newline at end of file diff --git a/cnny-2023/fundamentals-day-2/index.html b/cnny-2023/fundamentals-day-2/index.html index 71113cd466..e9400c317b 100644 --- a/cnny-2023/fundamentals-day-2/index.html +++ b/cnny-2023/fundamentals-day-2/index.html @@ -14,13 +14,13 @@ - - + +

    2-2. Kubernetes Fundamentals - Services and Ingress

    · 11 min read
    Paul Yu

    Welcome to Day 2 of Week 2 of #CloudNativeNewYear!

    The theme for this week is #Kubernetes fundamentals. Yesterday we talked about how to deploy a containerized web app workload to Azure Kubernetes Service (AKS). Today we'll explore the topic of services and ingress and walk through the steps of making our containers accessible both internally as well as over the internet so that you can share it with the world 😊

    Ask the Experts Thursday, February 9th at 9 AM PST
    Catch the Replay of the Live Demo

    Watch the recorded demo and conversation about this week's topics.

    We were live on YouTube walking through today's (and the rest of this week's) demos.

    What We'll Cover

    • Exposing Pods via Service
    • Exposing Services via Ingress
    • Takeaways
    • Resources

    Exposing Pods via Service

    There are a few ways to expose your pod in Kubernetes. One way is to take an imperative approach and use the kubectl expose command. This is probably the quickest way to achieve your goal but it isn't the best way. A better way to expose your pod by taking a declarative approach by creating a services manifest file and deploying it using the kubectl apply command.

    Don't worry if you are unsure of how to make this manifest, we'll use kubectl to help generate it.

    First, let's ensure we have the database deployed on our AKS cluster.

    📝 NOTE: If you don't have an AKS cluster deployed, please head over to Azure-Samples/azure-voting-app-rust, clone the repo, and follow the instructions in the README.md to execute the Azure deployment and setup your kubectl context. Check out the first post this week for more on the environment setup.

    kubectl apply -f ./manifests/deployment-db.yaml

    Next, let's deploy the application. If you are following along from yesterday's content, there isn't anything you need to change; however, if you are deploy the app from scratch, you'll need to modify the deployment-app.yaml manifest and update it with your image tag and database pod's IP address.

    kubectl apply -f ./manifests/deployment-app.yaml

    Now, let's expose the database using a service so that we can leverage Kubernetes' built-in service discovery to be able to reference it by name; not pod IP. Run the following command.

    kubectl expose deployment azure-voting-db \
    --port=5432 \
    --target-port=5432

    With the database exposed using service, we can update the app deployment manifest to use the service name instead of pod IP. This way, if the pod ever gets assigned a new IP, we don't have to worry about updating the IP each time and redeploying our web application. Kubernetes has internal service discovery mechanism in place that allows us to reference a service by its name.

    Let's make an update to the manifest. Replace the environment variable for DATABASE_SERVER with the following:

    - name: DATABASE_SERVER
    value: azure-voting-db

    Re-deploy the app with the updated configuration.

    kubectl apply -f ./manifests/deployment-app.yaml

    One service down, one to go. Run the following command to expose the web application.

    kubectl expose deployment azure-voting-app \
    --type=LoadBalancer \
    --port=80 \
    --target-port=8080

    Notice the --type argument has a value of LoadBalancer. This service type is implemented by the cloud-controller-manager which is part of the Kubernetes control plane. When using a managed Kubernetes cluster such as Azure Kubernetes Service, a public standard load balancer will be able to provisioned when the service type is set to LoadBalancer. The load balancer will also have a public IP assigned which will make your deployment publicly available.

    Kubernetes supports four service types:

    • ClusterIP: this is the default and limits service access to internal traffic within the cluster
    • NodePort: this assigns a port mapping on the node's IP address and allows traffic from the virtual network (outside the cluster)
    • LoadBalancer: as mentioned above, this creates a cloud-based load balancer
    • ExternalName: this is used in special case scenarios where you want to map a service to an external DNS name

    📝 NOTE: When exposing a web application to the internet, allowing external users to connect to your Service directly is not the best approach. Instead, you should use an Ingress, which we'll cover in the next section.

    Now, let's confirm you can reach the web app from the internet. You can use the following command to print the URL to your terminal.

    echo "http://$(kubectl get service azure-voting-app -o jsonpath='{.status.loadBalancer.ingress[0].ip}')"

    Great! The kubectl expose command gets the job done, but as mentioned above, it is not the best method of exposing deployments. It is better to expose deployments declaratively using a service manifest, so let's delete the services and redeploy using manifests.

    kubectl delete service azure-voting-db azure-voting-app

    To use kubectl to generate our manifest file, we can use the same kubectl expose command that we ran earlier but this time, we'll include --output=yaml and --dry-run=client. This will instruct the command to output the manifest that would be sent to the kube-api server in YAML format to the terminal.

    Generate the manifest for the database service.

    kubectl expose deployment azure-voting-db \
    --type=ClusterIP \
    --port=5432 \
    --target-port=5432 \
    --output=yaml \
    --dry-run=client > ./manifests/service-db.yaml

    Generate the manifest for the application service.

    kubectl expose deployment azure-voting-app \
    --type=LoadBalancer \
    --port=80 \
    --target-port=8080 \
    --output=yaml \
    --dry-run=client > ./manifests/service-app.yaml

    The command above redirected the YAML output to your manifests directory. Here is what the web application service looks like.

    apiVersion: v1
    kind: Service
    metadata:
    creationTimestamp: null
    labels:
    app: azure-voting-app
    name: azure-voting-app
    spec:
    ports:
    - port: 80
    protocol: TCP
    targetPort: 8080
    selector:
    app: azure-voting-app
    type: LoadBalancer
    status:
    loadBalancer: {}

    💡 TIP: To view the schema of any api-resource in Kubernetes, you can use the kubectl explain command. In this case the kubectl explain service command will tell us exactly what each of these fields do.

    Re-deploy the services using the new service manifests.

    kubectl apply -f ./manifests/service-db.yaml -f ./manifests/service-app.yaml

    # You should see TYPE is set to LoadBalancer and the EXTERNAL-IP is set
    kubectl get service azure-voting-db azure-voting-app

    Confirm again that our application is accessible again. Run the following command to print the URL to the terminal.

    echo "http://$(kubectl get service azure-voting-app -o jsonpath='{.status.loadBalancer.ingress[0].ip}')"

    That was easy, right? We just exposed both of our pods using Kubernetes services. The database only needs to be accessible from within the cluster so ClusterIP is perfect for that. For the web application, we specified the type to be LoadBalancer so that we can access the application over the public internet.

    But wait... remember that if you want to expose web applications over the public internet, a Service with a public IP is not the best way; the better approach is to use an Ingress resource.

    Exposing Services via Ingress

    If you read through the Kubernetes documentation on Ingress you will see a diagram that depicts the Ingress sitting in front of the Service resource with a routing rule between it. In order to use Ingress, you need to deploy an Ingress Controller and it can be configured with many routing rules to forward traffic to one or many backend services. So effectively, an Ingress is a load balancer for your Services.

    With that said, we no longer need a service type of LoadBalancer since the service does not need to be accessible from the internet. It only needs to be accessible from the Ingress Controller (internal to the cluster) so we can change the service type to ClusterIP.

    Update your service.yaml file to look like this:

    apiVersion: v1
    kind: Service
    metadata:
    creationTimestamp: null
    labels:
    app: azure-voting-app
    name: azure-voting-app
    spec:
    ports:
    - port: 80
    protocol: TCP
    targetPort: 8080
    selector:
    app: azure-voting-app

    📝 NOTE: The default service type is ClusterIP so we can omit the type altogether.

    Re-apply the app service manifest.

    kubectl apply -f ./manifests/service-app.yaml

    # You should see TYPE set to ClusterIP and EXTERNAL-IP set to <none>
    kubectl get service azure-voting-app

    Next, we need to install an Ingress Controller. There are quite a few options, and the Kubernetes-maintained NGINX Ingress Controller is commonly deployed.

    You could install this manually by following these instructions, but if you do that you'll be responsible for maintaining and supporting the resource.

    I like to take advantage of free maintenance and support when I can get it, so I'll opt to use the Web Application Routing add-on for AKS.

    💡 TIP: Whenever you install an AKS add-on, it will be maintained and fully supported by Azure Support.

    Enable the web application routing add-on in our AKS cluster with the following command.

    az aks addon enable \
    --name <YOUR_AKS_NAME> \
    --resource-group <YOUR_AKS_RESOURCE_GROUP>
    --addon web_application_routing

    ⚠️ WARNING: This command can take a few minutes to complete

    Now, let's use the same approach we took in creating our service to create our Ingress resource. Run the following command to generate the Ingress manifest.

    kubectl create ingress azure-voting-app \
    --class=webapprouting.kubernetes.azure.com \
    --rule="/*=azure-voting-app:80" \
    --output yaml \
    --dry-run=client > ./manifests/ingress.yaml

    The --class=webapprouting.kubernetes.azure.com option activates the AKS web application routing add-on. This AKS add-on can also integrate with other Azure services such as Azure DNS and Azure Key Vault for TLS certificate management and this special class makes it all work.

    The --rule="/*=azure-voting-app:80" option looks confusing but we can use kubectl again to help us understand how to format the value for the option.

    kubectl create ingress --help

    In the output you will see the following:

    --rule=[]:
    Rule in format host/path=service:port[,tls=secretname]. Paths containing the leading character '*' are
    considered pathType=Prefix. tls argument is optional.

    It expects a host and path separated by a forward-slash, then expects the backend service name and port separated by a colon. We're not using a hostname for this demo so we can omit it. For the path, an asterisk is used to specify a wildcard path prefix.

    So, the value of /*=azure-voting-app:80 creates a routing rule for all paths following the domain (or in our case since we don't have a hostname specified, the IP) to route traffic to our azure-voting-app backend service on port 80.

    📝 NOTE: Configuring the hostname and TLS is outside the scope of this demo but please visit this URL https://bit.ly/aks-webapp-routing for an in-depth hands-on lab centered around Web Application Routing on AKS.

    Your ingress.yaml file should look like this:

    apiVersion: networking.k8s.io/v1
    kind: Ingress
    metadata:
    creationTimestamp: null
    name: azure-voting-app
    spec:
    ingressClassName: webapprouting.kubernetes.azure.com
    rules:
    - http:
    paths:
    - backend:
    service:
    name: azure-voting-app
    port:
    number: 80
    path: /
    pathType: Prefix
    status:
    loadBalancer: {}

    Apply the app ingress manifest.

    kubectl apply -f ./manifests/ingress.yaml

    Validate the web application is available from the internet again. You can run the following command to print the URL to the terminal.

    echo "http://$(kubectl get ingress azure-voting-app -o jsonpath='{.status.loadBalancer.ingress[0].ip}')"

    Takeaways

    Exposing your applications both internally and externally can be easily achieved using Service and Ingress resources respectively. If your service is HTTP or HTTPS based and needs to be accessible from outsie the cluster, use Ingress with an internal Service (i.e., ClusterIP or NodePort); otherwise, use the Service resource. If your TCP-based Service needs to be publicly accessible, you set the type to LoadBalancer to expose a public IP for it. To learn more about these resources, please visit the links listed below.

    Lastly, if you are unsure how to begin writing your service manifest, you can use kubectl and have it do most of the work for you 🥳

    Resources

    Take the Cloud Skills Challenge!

    Enroll in the Cloud Skills Challenge!

    Don't miss out on this opportunity to level up your skills and stay ahead of the curve in the world of cloud native.

    - - + + \ No newline at end of file diff --git a/cnny-2023/fundamentals-day-3/index.html b/cnny-2023/fundamentals-day-3/index.html index 4ab0f61ca9..99d64763d9 100644 --- a/cnny-2023/fundamentals-day-3/index.html +++ b/cnny-2023/fundamentals-day-3/index.html @@ -14,14 +14,14 @@ - - + +

    2-3. Kubernetes Fundamentals - ConfigMaps and Secrets

    · 6 min read
    Josh Duffney

    Welcome to Day 3 of Week 2 of #CloudNativeNewYear!

    The theme for this week is Kubernetes fundamentals. Yesterday we talked about Services and Ingress. Today we'll explore the topic of passing configuration and secrets to our applications in Kubernetes with ConfigMaps and Secrets.

    Ask the Experts Thursday, February 9th at 9 AM PST
    Catch the Replay of the Live Demo

    Watch the recorded demo and conversation about this week's topics.

    We were live on YouTube walking through today's (and the rest of this week's) demos.

    What We'll Cover

    • Decouple configurations with ConfigMaps and Secerts
    • Passing Environment Data with ConfigMaps and Secrets
    • Conclusion

    Decouple configurations with ConfigMaps and Secerts

    A ConfigMap is a Kubernetes object that decouples configuration data from pod definitions. Kubernetes secerts are similar, but were designed to decouple senstive information.

    Separating the configuration and secerts from your application promotes better organization and security of your Kubernetes environment. It also enables you to share the same configuration and different secerts across multiple pods and deployments which can simplify scaling and management. Using ConfigMaps and Secerts in Kubernetes is a best practice that can help to improve the scalability, security, and maintainability of your cluster.

    By the end of this tutorial, you'll have added a Kubernetes ConfigMap and Secret to the Azure Voting deployment.

    Passing Environment Data with ConfigMaps and Secrets

    📝 NOTE: If you don't have an AKS cluster deployed, please head over to Azure-Samples/azure-voting-app-rust, clone the repo, and follow the instructions in the README.md to execute the Azure deployment and setup your kubectl context. Check out the first post this week for more on the environment setup.

    Create the ConfigMap

    ConfigMaps can be used in one of two ways; as environment variables or volumes.

    For this tutorial you'll use a ConfigMap to create three environment variables inside the pod; DATABASE_SERVER, FISRT_VALUE, and SECOND_VALUE. The DATABASE_SERVER provides part of connection string to a Postgres. FIRST_VALUE and SECOND_VALUE are configuration options that change what voting options the application presents to the users.

    Follow the below steps to create a new ConfigMap:

    1. Create a YAML file named 'config-map.yaml'. In this file, specify the environment variables for the application.

      apiVersion: v1
      kind: ConfigMap
      metadata:
      name: azure-voting-config
      data:
      DATABASE_SERVER: azure-voting-db
      FIRST_VALUE: "Go"
      SECOND_VALUE: "Rust"
    2. Create the config map in your Kubernetes cluster by running the following command:

      kubectl create -f config-map.yaml

    Create the Secret

    The deployment-db.yaml and deployment-app.yaml are Kubernetes manifests that deploy the Azure Voting App. Currently, those deployment manifests contain the environment variables POSTGRES_PASSWORD and DATABASE_PASSWORD with the value stored as plain text. Your task is to replace that environment variable with a Kubernetes Secret.

    Create a Secret running the following commands:

    1. Encode mypassword.

      echo -n "mypassword" | base64
    2. Create a YAML file named secret.yaml. In this file, add POSTGRES_PASSWORD as the key and the encoded value returned above under as the value in the data section.

      apiVersion: v1
      kind: Secret
      metadata:
      name: azure-voting-secret
      type: Opaque
      data:
      POSTGRES_PASSWORD: bXlwYXNzd29yZA==
    3. Create the Secret in your Kubernetes cluster by running the following command:

      kubectl create -f secret.yaml

    [!WARNING] base64 encoding is a simple and widely supported way to obscure plaintext data, it is not secure, as it can easily be decoded. If you want to store sensitive data like password, you should use a more secure method like encrypting with a Key Management Service (KMS) before storing it in the Secret.

    Modify the app deployment manifest

    With the ConfigMap and Secert both created the next step is to replace the environment variables provided in the application deployment manuscript with the values stored in the ConfigMap and the Secert.

    Complete the following steps to add the ConfigMap and Secert to the deployment mainifest:

    1. Open the Kubernetes manifest file deployment-app.yaml.

    2. In the containers section, add an envFrom section and upate the env section.

      envFrom:
      - configMapRef:
      name: azure-voting-config
      env:
      - name: DATABASE_PASSWORD
      valueFrom:
      secretKeyRef:
      name: azure-voting-secret
      key: POSTGRES_PASSWORD

      Using envFrom exposes all the values witin the ConfigMap as environment variables. Making it so you don't have to list them individually.

    3. Save the changes to the deployment manifest file.

    4. Apply the changes to the deployment by running the following command:

      kubectl apply -f deployment-app.yaml

    Modify the database deployment manifest

    Next, update the database deployment manifest and replace the plain text environment variable with the Kubernetes Secert.

    1. Open the deployment-db.yaml.

    2. To add the secret to the deployment, replace the env section with the following code:

      env:
      - name: POSTGRES_PASSWORD
      valueFrom:
      secretKeyRef:
      name: azure-voting-secret
      key: POSTGRES_PASSWORD
    3. Apply the updated manifest.

      kubectl apply -f deployment-db.yaml

    Verify the ConfigMap and output environment variables

    Verify that the ConfigMap was added to your deploy by running the following command:

    ```bash
    kubectl describe deployment azure-voting-app
    ```

    Browse the output until you find the envFrom section with the config map reference.

    You can also verify that the environment variables from the config map are being passed to the container by running the command kubectl exec -it <pod-name> -- printenv. This command will show you all the environment variables passed to the pod including the one from configmap.

    By following these steps, you will have successfully added a config map to the Azure Voting App Kubernetes deployment, and the environment variables defined in the config map will be passed to the container running in the pod.

    Verify the Secret and describe the deployment

    Once the secret has been created you can verify it exists by running the following command:

    kubectl get secrets

    You can view additional information, such as labels, annotations, type, and the Data by running kubectl describe:

    kubectl describe secret azure-voting-secret

    By default, the describe command doesn't output the encoded value, but if you output the results as JSON or YAML you'll be able to see the secret's encoded value.

     kubectl get secret azure-voting-secret -o json

    Conclusion

    In conclusion, using ConfigMaps and Secrets in Kubernetes can help to improve the scalability, security, and maintainability of your cluster. By decoupling configuration data and sensitive information from pod definitions, you can promote better organization and security in your Kubernetes environment. Additionally, separating these elements allows for sharing the same configuration and different secrets across multiple pods and deployments, simplifying scaling and management.

    Take the Cloud Skills Challenge!

    Enroll in the Cloud Skills Challenge!

    Don't miss out on this opportunity to level up your skills and stay ahead of the curve in the world of cloud native.

    - - + + \ No newline at end of file diff --git a/cnny-2023/fundamentals-day-4/index.html b/cnny-2023/fundamentals-day-4/index.html index 1c944c35a7..93156c2ae0 100644 --- a/cnny-2023/fundamentals-day-4/index.html +++ b/cnny-2023/fundamentals-day-4/index.html @@ -14,13 +14,13 @@ - - + +

    2-4. Kubernetes Fundamentals - Volumes, Mounts, and Claims

    · 8 min read
    Paul Yu

    Welcome to Day 4 of Week 2 of #CloudNativeNewYear!

    The theme for this week is Kubernetes fundamentals. Yesterday we talked about how to set app configurations and secrets at runtime using Kubernetes ConfigMaps and Secrets. Today we'll explore the topic of persistent storage on Kubernetes and show you can leverage Persistent Volumes and Persistent Volume Claims to ensure your PostgreSQL data can survive container restarts.

    Ask the Experts Thursday, February 9th at 9 AM PST
    Catch the Replay of the Live Demo

    Watch the recorded demo and conversation about this week's topics.

    We were live on YouTube walking through today's (and the rest of this week's) demos.

    What We'll Cover

    • Containers are ephemeral
    • Persistent storage on Kubernetes
    • Persistent storage on AKS
    • Takeaways
    • Resources

    Containers are ephemeral

    In our sample application, the frontend UI writes vote values to a backend PostgreSQL database. By default the database container stores its data on the container's local file system, so there will be data loss when the pod is re-deployed or crashes as containers are meant to start with a clean slate each time.

    Let's re-deploy our sample app and experience the problem first hand.

    📝 NOTE: If you don't have an AKS cluster deployed, please head over to Azure-Samples/azure-voting-app-rust, clone the repo, and follow the instructions in the README.md to execute the Azure deployment and setup your kubectl context. Check out the first post this week for more on the environment setup.

    kubectl apply -f ./manifests

    Wait for the azure-voting-app service to be assigned a public IP then browse to the website and submit some votes. Use the command below to print the URL to the terminal.

    echo "http://$(kubectl get ingress azure-voting-app -o jsonpath='{.status.loadBalancer.ingress[0].ip}')"

    Now, let's delete the pods and watch Kubernetes do what it does best... that is, re-schedule pods.

    # wait for the pod to come up then ctrl+c to stop watching
    kubectl delete --all pod --wait=false && kubectl get po -w

    Once the pods have been recovered, reload the website and confirm the vote tally has been reset to zero.

    We need to fix this so that the data outlives the container.

    Persistent storage on Kubernetes

    In order for application data to survive crashes and restarts, you must implement Persistent Volumes and Persistent Volume Claims.

    A persistent volume represents storage that is available to the cluster. Storage volumes can be provisioned manually by an administrator or dynamically using Container Storage Interface (CSI) and storage classes, which includes information on how to provision CSI volumes.

    When a user needs to add persistent storage to their application, a persistent volume claim is made to allocate chunks of storage from the volume. This "claim" includes things like volume mode (e.g., file system or block storage), the amount of storage to allocate, the access mode, and optionally a storage class. Once a persistent volume claim has been deployed, users can add the volume to the pod and mount it in a container.

    In the next section, we'll demonstrate how to enable persistent storage on AKS.

    Persistent storage on AKS

    With AKS, CSI drivers and storage classes are pre-deployed into your cluster. This allows you to natively use Azure Disks, Azure Files, and Azure Blob Storage as persistent volumes. You can either bring your own Azure storage account and use it with AKS or have AKS provision an Azure storage account for you.

    To view the Storage CSI drivers that have been enabled in your AKS cluster, run the following command.

    az aks show \
    --name <YOUR_AKS_NAME> \
    --resource-group <YOUR_AKS_RESOURCE_GROUP> \
    --query storageProfile

    You should see output that looks like this.

    {
    "blobCsiDriver": null,
    "diskCsiDriver": {
    "enabled": true,
    "version": "v1"
    },
    "fileCsiDriver": {
    "enabled": true
    },
    "snapshotController": {
    "enabled": true
    }
    }

    To view the storage classes that have been installed in your cluster, run the following command.

    kubectl get storageclass

    Workload requirements will dictate which CSI driver and storage class you will need to use.

    If you need block storage, then you should use the blobCsiDriver. The driver may not be enabled by default but you can enable it by following instructions which can be found in the Resources section below.

    If you need file storage you should leverage either diskCsiDriver or fileCsiDriver. The decision between these two boils down to whether or not you need to have the underlying storage accessible by one pod or multiple pods. It is important to note that diskCsiDriver currently supports access from a single pod only. Therefore, if you need data to be accessible by multiple pods at the same time, then you should opt for fileCsiDriver.

    For our PostgreSQL deployment, we'll use the diskCsiDriver and have AKS create an Azure Disk resource for us. There is no need to create a PV resource, all we need to do to is create a PVC using the managed-csi-premium storage class.

    Run the following command to create the PVC.

    kubectl apply -f - <<EOF            
    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
    name: pvc-azuredisk
    spec:
    accessModes:
    - ReadWriteOnce
    resources:
    requests:
    storage: 10Gi
    storageClassName: managed-csi-premium
    EOF

    When you check the PVC resource, you'll notice the STATUS is set to Pending. It will be set to Bound once the volume is mounted in the PostgreSQL container.

    kubectl get persistentvolumeclaim

    Let's delete the azure-voting-db deployment.

    kubectl delete deploy azure-voting-db

    Next, we need to apply an updated deployment manifest which includes our PVC.

    kubectl apply -f - <<EOF
    apiVersion: apps/v1
    kind: Deployment
    metadata:
    creationTimestamp: null
    labels:
    app: azure-voting-db
    name: azure-voting-db
    spec:
    replicas: 1
    selector:
    matchLabels:
    app: azure-voting-db
    strategy: {}
    template:
    metadata:
    creationTimestamp: null
    labels:
    app: azure-voting-db
    spec:
    containers:
    - image: postgres:15.0-alpine
    name: postgres
    ports:
    - containerPort: 5432
    env:
    - name: POSTGRES_PASSWORD
    valueFrom:
    secretKeyRef:
    name: azure-voting-secret
    key: POSTGRES_PASSWORD
    resources: {}
    volumeMounts:
    - name: mypvc
    mountPath: "/var/lib/postgresql/data"
    subPath: "data"
    volumes:
    - name: mypvc
    persistentVolumeClaim:
    claimName: pvc-azuredisk
    EOF

    In the manifest above, you'll see that we are mounting a new volume called mypvc (the name can be whatever you want) in the pod which points to a PVC named pvc-azuredisk. With the volume in place, we can mount it in the container by referencing the name of the volume mypvc and setting the mount path to /var/lib/postgresql/data (which is the default path).

    💡 IMPORTANT: When mounting a volume into a non-empty subdirectory, you must add subPath to the volume mount and point it to a subdirectory in the volume rather than mounting at root. In our case, when Azure Disk is formatted, it leaves a lost+found directory as documented here.

    Watch the pods and wait for the STATUS to show Running and the pod's READY status shows 1/1.

    # wait for the pod to come up then ctrl+c to stop watching
    kubectl get po -w

    Verify that the STATUS of the PVC is now set to Bound

    kubectl get persistentvolumeclaim

    With the new database container running, let's restart the application pod, wait for the pod's READY status to show 1/1, then head back over to our web browser and submit a few votes.

    kubectl delete pod -lapp=azure-voting-app --wait=false && kubectl get po -lapp=azure-voting-app -w

    Now the moment of truth... let's rip out the pods again, wait for the pods to be re-scheduled, and confirm our vote counts remain in tact.

    kubectl delete --all pod --wait=false && kubectl get po -w

    If you navigate back to the website, you'll find the vote are still there 🎉

    Takeaways

    By design, containers are meant to be ephemeral and stateless workloads are ideal on Kubernetes. However, there will come a time when your data needs to outlive the container. To persist data in your Kubernetes workloads, you need to leverage PV, PVC, and optionally storage classes. In our demo scenario, we leveraged CSI drivers built into AKS and created a PVC using pre-installed storage classes. From there, we updated the database deployment to mount the PVC in the container and AKS did the rest of the work in provisioning the underlying Azure Disk. If the built-in storage classes does not fit your needs; for example, you need to change the ReclaimPolicy or change the SKU for the Azure resource, then you can create your own custom storage class and configure it just the way you need it 😊

    We'll revisit this topic again next week but in the meantime, check out some of the resources listed below to learn more.

    See you in the next post!

    Resources

    Take the Cloud Skills Challenge!

    Enroll in the Cloud Skills Challenge!

    Don't miss out on this opportunity to level up your skills and stay ahead of the curve in the world of cloud native.

    - - + + \ No newline at end of file diff --git a/cnny-2023/fundamentals-day-5/index.html b/cnny-2023/fundamentals-day-5/index.html index edbfc90998..494702d3d0 100644 --- a/cnny-2023/fundamentals-day-5/index.html +++ b/cnny-2023/fundamentals-day-5/index.html @@ -14,13 +14,13 @@ - - + +

    2-5. Kubernetes Fundamentals - Scaling Pods and Nodes

    · 10 min read
    Steven Murawski

    Welcome to Day 5 of Week 2 of #CloudNativeNewYear!

    The theme for this week is Kubernetes fundamentals. Yesterday we talked about adding persistent storage to our deployment. Today we'll explore the topic of scaling pods and nodes in our Kubernetes cluster.

    Ask the Experts Thursday, February 9th at 9 AM PST
    Catch the Replay of the Live Demo

    Watch the recorded demo and conversation about this week's topics.

    We were live on YouTube walking through today's (and the rest of this week's) demos.

    What We'll Cover

    • Scaling Our Application
    • Scaling Pods
    • Scaling Nodes
    • Exercise
    • Resources

    Scaling Our Application

    One of our primary reasons to use a service like Kubernetes to orchestrate our workloads is the ability to scale. We've approached scaling in a multitude of ways over the years, taking advantage of the ever-evolving levels of hardware and software. Kubernetes allows us to scale our units of work, Pods, and the Nodes they run on. This allows us to take advantage of both hardware and software scaling abilities. Kubernetes can help improve the utilization of existing hardware (by scheduling Pods on Nodes that have resource capacity). And, with the capabilities of virtualization and/or cloud hosting (or a bit more work, if you have a pool of physical machines), Kubernetes can expand (or contract) the number of Nodes capable of hosting Pods. Scaling is primarily driven by resource utilization, but can be triggered by a variety of other sources thanks to projects like Kubernetes Event-driven Autoscaling (KEDA).

    Scaling Pods

    Our first level of scaling is with our Pods. Earlier, when we worked on our deployment, we talked about how the Kubernetes would use the deployment configuration to ensure that we had the desired workloads running. One thing we didn't explore was running more than one instance of a pod. We can define a number of replicas of a pod in our Deployment.

    Manually Scale Pods

    So, if we wanted to define more pods right at the start (or at any point really), we could update our deployment configuration file with the number of replicas and apply that configuration file.

    spec:
    replicas: 5

    Or we could use the kubectl scale command to update the deployment with a number of pods to create.

    kubectl scale --replicas=5 deployment/azure-voting-app

    Both of these approaches modify the running configuration of our Kubernetes cluster and request that it ensure that we have that set number of replicas running. Because this was a manual change, the Kubernetes cluster won't automatically increase or decrease the number of pods. It'll just ensure that there are always the specified number of pods running.

    Autoscale Pods with the Horizontal Pod Autoscaler

    Another approach to scaling our pods is to allow the Horizontal Pod Autoscaler to help us scale in response to resources being used by the pod. This requires a bit more configuration up front. When we define our pod in our deployment, we need to include resource requests and limits. The requests help Kubernetes determine what nodes may have capacity for a new instance of a pod. The limit tells us where the node should cap utilization for a particular instance of a pod. For example, we'll update our deployment to request 0.25 CPU and set a limit of 0.5 CPU.

        spec:
    containers:
    - image: acrudavoz.azurecr.io/cnny2023/azure-voting-app-rust:ca4
    name: azure-voting-app-rust
    ports:
    - containerPort: 8080
    env:
    - name: DATABASE_URL
    value: postgres://postgres:mypassword@10.244.0.29
    resources:
    requests:
    cpu: 250m
    limits:
    cpu: 500m

    Now that we've given Kubernetes an allowed range and an idea of what free resources a node should have to place new pods, we can set up autoscaling. Because autoscaling is a persistent configuration, I like to define it in a configuration file that I'll be able to keep with the rest of my cluster configuration. We'll use the kubectl command to help us write the configuration file. We'll request that Kubernetes watch our pods and when the average CPU utilization if 50% of the requested usage (in our case if it's using more than 0.375 CPU across the current number of pods), it can grow the number of pods serving requests up to 10. If the utilization drops, Kubernetes will have the permission to deprovision pods down to the minimum (three in our example).

    kubectl autoscale deployment azure-voting-app --cpu-percent=50 --min=3 --max=10 -o YAML --dry-run=client

    Which would give us:

    apiVersion: autoscaling/v1
    kind: HorizontalPodAutoscaler
    metadata:
    creationTimestamp: null
    name: azure-voting-app
    spec:
    maxReplicas: 10
    minReplicas: 3
    scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: azure-voting-app
    targetCPUUtilizationPercentage: 50
    status:
    currentReplicas: 0
    desiredReplicas: 0

    So, how often does the autoscaler check the metrics being monitored? The autoscaler checks the Metrics API every 15 seconds, however the pods stats are only updated every 60 seconds. This means that an autoscale event may be evaluated about once a minute. Once an autoscale down event happens however, Kubernetes has a cooldown period to give the new pods a chance to distribute the workload and let the new metrics accumulate. There is no delay on scale up events.

    Application Architecture Considerations

    We've focused in this example on our front end, which is an easier scaling story. When we start talking about scaling our database layers or anything that deals with persistent storage or has primary/replica configuration requirements things get a bit more complicated. Some of these applications may have built-in leader election or could use sidecars to help use existing features in Kubernetes to perform that function. For shared storage scenarios, persistent volumes (or persistent volumes with Azure) can be of help, if the application knows how to play well with shared file access.

    Ultimately, you know your application architecture and, while Kubernetes may not have an exact match to how you are doing things today, the underlying capability is probably there under a different name. This abstraction allows you to more effectively use Kubernetes to operate a variety of workloads with the levels of controls you need.

    Scaling Nodes

    We've looked at how to scale our pods, but that assumes we have enough resources in our existing pool of nodes to accomodate those scaling requests. Kubernetes can also help scale our available nodes to ensure that our applications have the necessary resources to meet their performance requirements.

    Manually Scale Nodes

    Manually scaling nodes isn't a direct function of Kubernetes, so your operating environment instructions may vary. On Azure, it's pretty straight forward. Using the Azure CLI (or other tools), we can tell our AKS cluster to scale up or scale down the number of nodes in our node pool.

    First, we'll check out how many nodes we currently have in our working environment.

    kubectl get nodes

    This will show us

    azure-voting-app-rust ❯  kubectl get nodes
    NAME STATUS ROLES AGE VERSION
    aks-pool0-37917684-vmss000000 Ready agent 5d21h v1.24.6

    Then, we'll scale it up to three nodes.

    az aks scale --resource-group $ResourceGroup --name $AksName --node-count 3

    Then, we'll check out how many nodes we now have in our working environment.

    kubectl get nodes

    Which returns:

    azure-voting-app-rust ❯  kubectl get nodes
    NAME STATUS ROLES AGE VERSION
    aks-pool0-37917684-vmss000000 Ready agent 5d21h v1.24.6
    aks-pool0-37917684-vmss000001 Ready agent 5m27s v1.24.6
    aks-pool0-37917684-vmss000002 Ready agent 5m10s v1.24.6

    Autoscale Nodes with the Cluster Autoscaler

    Things get more interesting when we start working with the Cluster Autoscaler. The Cluster Autoscaler watches for the inability of Kubernetes to schedule the required number of pods due to resource constraints (and a few other criteria like affinity/anti-affinity). If there are insufficient resources available on the existing nodes, the autoscaler can provision new nodes into the nodepool. Likewise, the autoscaler watches to see if the existing pods could be consolidated to a smaller set of nodes and can remove excess nodes.

    Enabling the autoscaler is likewise an update that can be dependent on where and how your Kubernetes cluster is hosted. Azure makes it easy with a simple Azure CLI command.

    az aks update `
    --resource-group $ResourceGroup `
    --name $AksName `
    --update-cluster-autoscaler `
    --min-count 1 `
    --max-count 5

    There are a variety of settings that can be configured to tune how the autoscaler works.

    Scaling on Different Events

    CPU and memory utilization are the primary drivers for the Horizontal Pod Autoscaler, but those might not be the best measures as to when you might want to scale workloads. There are other options for scaling triggers and one of the more common plugins to help with that is the Kubernetes Event-driven Autoscaling (KEDA) project. The KEDA project makes it easy to plug in different event sources to help drive scaling. Find more information about using KEDA on AKS here.

    Exercise

    Let's try out the scaling configurations that we just walked through using our sample application. If you still have your environment from Day 1, you can use that.

    📝 NOTE: If you don't have an AKS cluster deployed, please head over to Azure-Samples/azure-voting-app-rust, clone the repo, and follow the instructions in the README.md to execute the Azure deployment and setup your kubectl context. Check out the first post this week for more on the environment setup.

    Configure Horizontal Pod Autoscaler

    • Edit ./manifests/deployment-app.yaml to include resource requests and limits.
            resources:
    requests:
    cpu: 250m
    limits:
    cpu: 500m
    • Apply the updated deployment configuration.
    kubectl apply -f ./manifests/deployment-app.yaml
    • Create the horizontal pod autoscaler configuration and apply it
    kubectl autoscale deployment azure-voting-app --cpu-percent=50 --min=3 --max=10 -o YAML --dry-run=client > ./manifests/scaler-app.yaml
    kubectl apply -f ./manifests/scaler-app.yaml
    • Check to see your pods scale out to the minimum.
    kubectl get pods

    Configure Cluster Autoscaler

    Configuring the basic behavior of the Cluster Autoscaler is a bit simpler. We just need to run the Azure CLI command to enable the autoscaler and define our lower and upper limits.

    • Check the current nodes available (should be 1).
    kubectl get nodes
    • Update the cluster to enable the autoscaler
    az aks update `
    --resource-group $ResourceGroup `
    --name $AksName `
    --update-cluster-autoscaler `
    --min-count 2 `
    --max-count 5
    • Check to see the current number of nodes (should be 2 now).
    kubectl get nodes

    Resources

    Take the Cloud Skills Challenge!

    Enroll in the Cloud Skills Challenge!

    Don't miss out on this opportunity to level up your skills and stay ahead of the curve in the world of cloud native.

    Documentation

    Training

    - - + + \ No newline at end of file diff --git a/cnny-2023/index.html b/cnny-2023/index.html index 541b00194e..0fe0712991 100644 --- a/cnny-2023/index.html +++ b/cnny-2023/index.html @@ -14,13 +14,13 @@ - - + +

    · 4 min read
    Cory Skimming
    Devanshi Joshi
    Steven Murawski
    Nitya Narasimhan

    Welcome to the Kick-off Post for #30DaysOfCloudNative - one of the core initiatives within #CloudNativeNewYear! Over the next four weeks, join us as we take you from fundamentals to functional usage of Cloud-native technologies, one blog post at a time! Read on to learn a little bit about this initiative and what you can expect to learn from this journey!

    What We'll Cover


    Cloud-native New Year

    Welcome to Week 01 of 🥳 #CloudNativeNewYear ! Today, we kick off a full month of content and activities to skill you up on all things Cloud-native on Azure with content, events, and community interactions! Read on to learn about what we have planned!


    Explore our initiatives

    We have a number of initiatives planned for the month to help you learn and skill up on relevant technologies. Click on the links to visit the relevant pages for each.

    We'll go into more details about #30DaysOfCloudNative in this post - don't forget to subscribe to the blog to get daily posts delivered directly to your preferred feed reader!


    Register for events!

    What are 3 things you can do today, to jumpstart your learning journey?


    #30DaysOfCloudNative

    #30DaysOfCloudNative is a month-long series of daily blog posts grouped into 4 themed weeks - taking you from core concepts to end-to-end solution examples in 30 days. Each article will be short (5-8 mins reading time) and provide exercises and resources to help you reinforce learnings and take next steps.

    This series focuses on the Cloud-native On Azure learning journey in four stages, each building on the previous week to help you skill up in a beginner-friendly way:

    We have a tentative weekly-themed roadmap for the topics we hope to cover and will keep this updated as we go with links to actual articles as they get published.

    Week 1: FOCUS ON CLOUD-NATIVE FUNDAMENTALS

    Here's a sneak peek at the week 1 schedule. We'll start with a broad review of cloud-native fundamentals and walkthrough the core concepts of microservices, containers and Kubernetes.

    • Jan 23: Learn Core Concepts for Cloud-native
    • Jan 24: Container 101
    • Jan 25: Adopting Microservices with Kubernetes
    • Jan 26: Kubernetes 101
    • Jan 27: Exploring your Cloud Native Options

    Let's Get Started!

    Now you know everything! We hope you are as excited as we are to dive into a full month of active learning and doing! Don't forget to subscribe for updates in your favorite feed reader! And look out for our first Cloud-native Fundamentals post on January 23rd!


    - - + + \ No newline at end of file diff --git a/cnny-2023/microservices-101/index.html b/cnny-2023/microservices-101/index.html index 54c9d4667c..83c7be30f1 100644 --- a/cnny-2023/microservices-101/index.html +++ b/cnny-2023/microservices-101/index.html @@ -14,13 +14,13 @@ - - + +

    1-4. Microservices 101

    · 6 min read
    Josh Duffney

    Welcome to Day 4 of Week 1 of #CloudNativeNewYear!

    This week we'll focus on advanced topics and best practices for Cloud-Native practitioners, kicking off with this post on Serverless Container Options with Azure. We'll look at technologies, tools and best practices that range from managed services like Azure Kubernetes Service, to options allowing finer granularity of control and oversight.

    What We'll Cover

    • What is Microservice Architecture?
    • How do you design a Microservice?
    • What challenges do Microservices introduce?
    • Conclusion
    • Resources


    Microservices are a modern way of designing and building software that increases deployment velocity by decomposing an application into small autonomous services that can be deployed independently.

    By deploying loosely coupled microservices your applications can be developed, deployed, and scaled independently. Because each service is independent, it can be updated or replaced without having to worry about the impact on the rest of the application. This means that if a bug is found in one service, it can be fixed without having to redeploy the entire application. All of which gives an organization the ability to deliver value to their customers faster.

    In this article, we will explore the basics of microservices architecture, its benefits and challenges, and how it can help improve the development, deployment, and maintenance of software applications.

    What is Microservice Architecture?

    Before explaining what Microservice architecture is, it’s important to understand what problems microservices aim to address.

    Traditional software development is centered around building monolithic applications. Monolithic applications are built as a single, large codebase. Meaning your code is tightly coupled causing the monolithic app to suffer from the following:

    Too much Complexity: Monolithic applications can become complex and difficult to understand and maintain as they grow. This can make it hard to identify and fix bugs and add new features.

    Difficult to Scale: Monolithic applications can be difficult to scale as they often have a single point of failure, which can cause the whole application to crash if a service fails.

    Slow Deployment: Deploying a monolithic application can be risky and time-consuming, as a small change in one part of the codebase can affect the entire application.

    Microservice architecture (often called microservices) is an architecture style that addresses the challenges created by Monolithic applications. Microservices architecture is a way of designing and building software applications as a collection of small, independent services that communicate with each other through APIs. This allows for faster development and deployment cycles, as well as easier scaling and maintenance than is possible with a monolithic application.

    How do you design a Microservice?

    Building applications with Microservices architecture requires a different approach. Microservices architecture focuses on business capabilities rather than technical layers, such as data access or messaging. Doing so requires that you shift your focus away from the technical stack and model your applications based upon the various domains that exist within the business.

    Domain-driven design (DDD) is a way to design software by focusing on the business needs. You can use Domain-driven design as a framework that guides the development of well-designed microservices by building services that encapsulate knowledge in each domain and abstract that knowledge from clients.

    In Domain-driven design you start by modeling the business domain and creating a domain model. A domain model is an abstract model of the business model that distills and organizes a domain of knowledge and provides a common language for developers and domain experts. It’s the resulting domain model that microservices a best suited to be built around because it helps establish a well-defined boundary between external systems and other internal applications.

    In short, before you begin designing microservices, start by mapping the functions of the business and their connections to create a domain model for the microservice(s) to be built around.

    What challenges do Microservices introduce?

    Microservices solve a lot of problems and have several advantages, but the grass isn’t always greener on the other side.

    One of the key challenges of microservices is managing communication between services. Because services are independent, they need to communicate with each other through APIs. This can be complex and difficult to manage, especially as the number of services grows. To address this challenge, it is important to have a clear API design, with well-defined inputs and outputs for each service. It is also important to have a system for managing and monitoring communication between services, to ensure that everything is running smoothly.

    Another challenge of microservices is managing the deployment and scaling of services. Because each service is independent, it needs to be deployed and scaled separately from the rest of the application. This can be complex and difficult to manage, especially as the number of services grows. To address this challenge, it is important to have a clear and consistent deployment process, with well-defined steps for deploying and scaling each service. Furthermore, it is advisable to host them on a system with self-healing capabilities to reduce operational burden.

    It is also important to have a system for monitoring and managing the deployment and scaling of services, to ensure optimal performance.

    Each of these challenges has created fertile ground for tooling and process that exists in the cloud-native ecosystem. Kubernetes, CI CD, and other DevOps practices are part of the package of adopting the microservices architecture.

    Conclusion

    In summary, microservices architecture focuses on software applications as a collection of small, independent services that communicate with each other over well-defined APIs.

    The main advantages of microservices include:

    • increased flexibility and scalability per microservice,
    • efficient resource utilization (with help from a container orchestrator like Kubernetes),
    • and faster development cycles.

    Continue following along with this series to see how you can use Kubernetes to help adopt microservices patterns in your own environments!

    Resources

    - - + + \ No newline at end of file diff --git a/cnny-2023/page/10/index.html b/cnny-2023/page/10/index.html index 830263bd7d..92d4b0fd20 100644 --- a/cnny-2023/page/10/index.html +++ b/cnny-2023/page/10/index.html @@ -14,13 +14,13 @@ - - + +

    · 8 min read
    Paul Yu

    Welcome to Day 4 of Week 2 of #CloudNativeNewYear!

    The theme for this week is Kubernetes fundamentals. Yesterday we talked about how to set app configurations and secrets at runtime using Kubernetes ConfigMaps and Secrets. Today we'll explore the topic of persistent storage on Kubernetes and show you can leverage Persistent Volumes and Persistent Volume Claims to ensure your PostgreSQL data can survive container restarts.

    Ask the Experts Thursday, February 9th at 9 AM PST
    Catch the Replay of the Live Demo

    Watch the recorded demo and conversation about this week's topics.

    We were live on YouTube walking through today's (and the rest of this week's) demos.

    What We'll Cover

    • Containers are ephemeral
    • Persistent storage on Kubernetes
    • Persistent storage on AKS
    • Takeaways
    • Resources

    Containers are ephemeral

    In our sample application, the frontend UI writes vote values to a backend PostgreSQL database. By default the database container stores its data on the container's local file system, so there will be data loss when the pod is re-deployed or crashes as containers are meant to start with a clean slate each time.

    Let's re-deploy our sample app and experience the problem first hand.

    📝 NOTE: If you don't have an AKS cluster deployed, please head over to Azure-Samples/azure-voting-app-rust, clone the repo, and follow the instructions in the README.md to execute the Azure deployment and setup your kubectl context. Check out the first post this week for more on the environment setup.

    kubectl apply -f ./manifests

    Wait for the azure-voting-app service to be assigned a public IP then browse to the website and submit some votes. Use the command below to print the URL to the terminal.

    echo "http://$(kubectl get ingress azure-voting-app -o jsonpath='{.status.loadBalancer.ingress[0].ip}')"

    Now, let's delete the pods and watch Kubernetes do what it does best... that is, re-schedule pods.

    # wait for the pod to come up then ctrl+c to stop watching
    kubectl delete --all pod --wait=false && kubectl get po -w

    Once the pods have been recovered, reload the website and confirm the vote tally has been reset to zero.

    We need to fix this so that the data outlives the container.

    Persistent storage on Kubernetes

    In order for application data to survive crashes and restarts, you must implement Persistent Volumes and Persistent Volume Claims.

    A persistent volume represents storage that is available to the cluster. Storage volumes can be provisioned manually by an administrator or dynamically using Container Storage Interface (CSI) and storage classes, which includes information on how to provision CSI volumes.

    When a user needs to add persistent storage to their application, a persistent volume claim is made to allocate chunks of storage from the volume. This "claim" includes things like volume mode (e.g., file system or block storage), the amount of storage to allocate, the access mode, and optionally a storage class. Once a persistent volume claim has been deployed, users can add the volume to the pod and mount it in a container.

    In the next section, we'll demonstrate how to enable persistent storage on AKS.

    Persistent storage on AKS

    With AKS, CSI drivers and storage classes are pre-deployed into your cluster. This allows you to natively use Azure Disks, Azure Files, and Azure Blob Storage as persistent volumes. You can either bring your own Azure storage account and use it with AKS or have AKS provision an Azure storage account for you.

    To view the Storage CSI drivers that have been enabled in your AKS cluster, run the following command.

    az aks show \
    --name <YOUR_AKS_NAME> \
    --resource-group <YOUR_AKS_RESOURCE_GROUP> \
    --query storageProfile

    You should see output that looks like this.

    {
    "blobCsiDriver": null,
    "diskCsiDriver": {
    "enabled": true,
    "version": "v1"
    },
    "fileCsiDriver": {
    "enabled": true
    },
    "snapshotController": {
    "enabled": true
    }
    }

    To view the storage classes that have been installed in your cluster, run the following command.

    kubectl get storageclass

    Workload requirements will dictate which CSI driver and storage class you will need to use.

    If you need block storage, then you should use the blobCsiDriver. The driver may not be enabled by default but you can enable it by following instructions which can be found in the Resources section below.

    If you need file storage you should leverage either diskCsiDriver or fileCsiDriver. The decision between these two boils down to whether or not you need to have the underlying storage accessible by one pod or multiple pods. It is important to note that diskCsiDriver currently supports access from a single pod only. Therefore, if you need data to be accessible by multiple pods at the same time, then you should opt for fileCsiDriver.

    For our PostgreSQL deployment, we'll use the diskCsiDriver and have AKS create an Azure Disk resource for us. There is no need to create a PV resource, all we need to do to is create a PVC using the managed-csi-premium storage class.

    Run the following command to create the PVC.

    kubectl apply -f - <<EOF            
    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
    name: pvc-azuredisk
    spec:
    accessModes:
    - ReadWriteOnce
    resources:
    requests:
    storage: 10Gi
    storageClassName: managed-csi-premium
    EOF

    When you check the PVC resource, you'll notice the STATUS is set to Pending. It will be set to Bound once the volume is mounted in the PostgreSQL container.

    kubectl get persistentvolumeclaim

    Let's delete the azure-voting-db deployment.

    kubectl delete deploy azure-voting-db

    Next, we need to apply an updated deployment manifest which includes our PVC.

    kubectl apply -f - <<EOF
    apiVersion: apps/v1
    kind: Deployment
    metadata:
    creationTimestamp: null
    labels:
    app: azure-voting-db
    name: azure-voting-db
    spec:
    replicas: 1
    selector:
    matchLabels:
    app: azure-voting-db
    strategy: {}
    template:
    metadata:
    creationTimestamp: null
    labels:
    app: azure-voting-db
    spec:
    containers:
    - image: postgres:15.0-alpine
    name: postgres
    ports:
    - containerPort: 5432
    env:
    - name: POSTGRES_PASSWORD
    valueFrom:
    secretKeyRef:
    name: azure-voting-secret
    key: POSTGRES_PASSWORD
    resources: {}
    volumeMounts:
    - name: mypvc
    mountPath: "/var/lib/postgresql/data"
    subPath: "data"
    volumes:
    - name: mypvc
    persistentVolumeClaim:
    claimName: pvc-azuredisk
    EOF

    In the manifest above, you'll see that we are mounting a new volume called mypvc (the name can be whatever you want) in the pod which points to a PVC named pvc-azuredisk. With the volume in place, we can mount it in the container by referencing the name of the volume mypvc and setting the mount path to /var/lib/postgresql/data (which is the default path).

    💡 IMPORTANT: When mounting a volume into a non-empty subdirectory, you must add subPath to the volume mount and point it to a subdirectory in the volume rather than mounting at root. In our case, when Azure Disk is formatted, it leaves a lost+found directory as documented here.

    Watch the pods and wait for the STATUS to show Running and the pod's READY status shows 1/1.

    # wait for the pod to come up then ctrl+c to stop watching
    kubectl get po -w

    Verify that the STATUS of the PVC is now set to Bound

    kubectl get persistentvolumeclaim

    With the new database container running, let's restart the application pod, wait for the pod's READY status to show 1/1, then head back over to our web browser and submit a few votes.

    kubectl delete pod -lapp=azure-voting-app --wait=false && kubectl get po -lapp=azure-voting-app -w

    Now the moment of truth... let's rip out the pods again, wait for the pods to be re-scheduled, and confirm our vote counts remain in tact.

    kubectl delete --all pod --wait=false && kubectl get po -w

    If you navigate back to the website, you'll find the vote are still there 🎉

    Takeaways

    By design, containers are meant to be ephemeral and stateless workloads are ideal on Kubernetes. However, there will come a time when your data needs to outlive the container. To persist data in your Kubernetes workloads, you need to leverage PV, PVC, and optionally storage classes. In our demo scenario, we leveraged CSI drivers built into AKS and created a PVC using pre-installed storage classes. From there, we updated the database deployment to mount the PVC in the container and AKS did the rest of the work in provisioning the underlying Azure Disk. If the built-in storage classes does not fit your needs; for example, you need to change the ReclaimPolicy or change the SKU for the Azure resource, then you can create your own custom storage class and configure it just the way you need it 😊

    We'll revisit this topic again next week but in the meantime, check out some of the resources listed below to learn more.

    See you in the next post!

    Resources

    Take the Cloud Skills Challenge!

    Enroll in the Cloud Skills Challenge!

    Don't miss out on this opportunity to level up your skills and stay ahead of the curve in the world of cloud native.

    - - + + \ No newline at end of file diff --git a/cnny-2023/page/11/index.html b/cnny-2023/page/11/index.html index 95bfdb4fe5..cbd42f4284 100644 --- a/cnny-2023/page/11/index.html +++ b/cnny-2023/page/11/index.html @@ -14,13 +14,13 @@ - - + +

    · 10 min read
    Steven Murawski

    Welcome to Day 5 of Week 2 of #CloudNativeNewYear!

    The theme for this week is Kubernetes fundamentals. Yesterday we talked about adding persistent storage to our deployment. Today we'll explore the topic of scaling pods and nodes in our Kubernetes cluster.

    Ask the Experts Thursday, February 9th at 9 AM PST
    Catch the Replay of the Live Demo

    Watch the recorded demo and conversation about this week's topics.

    We were live on YouTube walking through today's (and the rest of this week's) demos.

    What We'll Cover

    • Scaling Our Application
    • Scaling Pods
    • Scaling Nodes
    • Exercise
    • Resources

    Scaling Our Application

    One of our primary reasons to use a service like Kubernetes to orchestrate our workloads is the ability to scale. We've approached scaling in a multitude of ways over the years, taking advantage of the ever-evolving levels of hardware and software. Kubernetes allows us to scale our units of work, Pods, and the Nodes they run on. This allows us to take advantage of both hardware and software scaling abilities. Kubernetes can help improve the utilization of existing hardware (by scheduling Pods on Nodes that have resource capacity). And, with the capabilities of virtualization and/or cloud hosting (or a bit more work, if you have a pool of physical machines), Kubernetes can expand (or contract) the number of Nodes capable of hosting Pods. Scaling is primarily driven by resource utilization, but can be triggered by a variety of other sources thanks to projects like Kubernetes Event-driven Autoscaling (KEDA).

    Scaling Pods

    Our first level of scaling is with our Pods. Earlier, when we worked on our deployment, we talked about how the Kubernetes would use the deployment configuration to ensure that we had the desired workloads running. One thing we didn't explore was running more than one instance of a pod. We can define a number of replicas of a pod in our Deployment.

    Manually Scale Pods

    So, if we wanted to define more pods right at the start (or at any point really), we could update our deployment configuration file with the number of replicas and apply that configuration file.

    spec:
    replicas: 5

    Or we could use the kubectl scale command to update the deployment with a number of pods to create.

    kubectl scale --replicas=5 deployment/azure-voting-app

    Both of these approaches modify the running configuration of our Kubernetes cluster and request that it ensure that we have that set number of replicas running. Because this was a manual change, the Kubernetes cluster won't automatically increase or decrease the number of pods. It'll just ensure that there are always the specified number of pods running.

    Autoscale Pods with the Horizontal Pod Autoscaler

    Another approach to scaling our pods is to allow the Horizontal Pod Autoscaler to help us scale in response to resources being used by the pod. This requires a bit more configuration up front. When we define our pod in our deployment, we need to include resource requests and limits. The requests help Kubernetes determine what nodes may have capacity for a new instance of a pod. The limit tells us where the node should cap utilization for a particular instance of a pod. For example, we'll update our deployment to request 0.25 CPU and set a limit of 0.5 CPU.

        spec:
    containers:
    - image: acrudavoz.azurecr.io/cnny2023/azure-voting-app-rust:ca4
    name: azure-voting-app-rust
    ports:
    - containerPort: 8080
    env:
    - name: DATABASE_URL
    value: postgres://postgres:mypassword@10.244.0.29
    resources:
    requests:
    cpu: 250m
    limits:
    cpu: 500m

    Now that we've given Kubernetes an allowed range and an idea of what free resources a node should have to place new pods, we can set up autoscaling. Because autoscaling is a persistent configuration, I like to define it in a configuration file that I'll be able to keep with the rest of my cluster configuration. We'll use the kubectl command to help us write the configuration file. We'll request that Kubernetes watch our pods and when the average CPU utilization if 50% of the requested usage (in our case if it's using more than 0.375 CPU across the current number of pods), it can grow the number of pods serving requests up to 10. If the utilization drops, Kubernetes will have the permission to deprovision pods down to the minimum (three in our example).

    kubectl autoscale deployment azure-voting-app --cpu-percent=50 --min=3 --max=10 -o YAML --dry-run=client

    Which would give us:

    apiVersion: autoscaling/v1
    kind: HorizontalPodAutoscaler
    metadata:
    creationTimestamp: null
    name: azure-voting-app
    spec:
    maxReplicas: 10
    minReplicas: 3
    scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: azure-voting-app
    targetCPUUtilizationPercentage: 50
    status:
    currentReplicas: 0
    desiredReplicas: 0

    So, how often does the autoscaler check the metrics being monitored? The autoscaler checks the Metrics API every 15 seconds, however the pods stats are only updated every 60 seconds. This means that an autoscale event may be evaluated about once a minute. Once an autoscale down event happens however, Kubernetes has a cooldown period to give the new pods a chance to distribute the workload and let the new metrics accumulate. There is no delay on scale up events.

    Application Architecture Considerations

    We've focused in this example on our front end, which is an easier scaling story. When we start talking about scaling our database layers or anything that deals with persistent storage or has primary/replica configuration requirements things get a bit more complicated. Some of these applications may have built-in leader election or could use sidecars to help use existing features in Kubernetes to perform that function. For shared storage scenarios, persistent volumes (or persistent volumes with Azure) can be of help, if the application knows how to play well with shared file access.

    Ultimately, you know your application architecture and, while Kubernetes may not have an exact match to how you are doing things today, the underlying capability is probably there under a different name. This abstraction allows you to more effectively use Kubernetes to operate a variety of workloads with the levels of controls you need.

    Scaling Nodes

    We've looked at how to scale our pods, but that assumes we have enough resources in our existing pool of nodes to accomodate those scaling requests. Kubernetes can also help scale our available nodes to ensure that our applications have the necessary resources to meet their performance requirements.

    Manually Scale Nodes

    Manually scaling nodes isn't a direct function of Kubernetes, so your operating environment instructions may vary. On Azure, it's pretty straight forward. Using the Azure CLI (or other tools), we can tell our AKS cluster to scale up or scale down the number of nodes in our node pool.

    First, we'll check out how many nodes we currently have in our working environment.

    kubectl get nodes

    This will show us

    azure-voting-app-rust ❯  kubectl get nodes
    NAME STATUS ROLES AGE VERSION
    aks-pool0-37917684-vmss000000 Ready agent 5d21h v1.24.6

    Then, we'll scale it up to three nodes.

    az aks scale --resource-group $ResourceGroup --name $AksName --node-count 3

    Then, we'll check out how many nodes we now have in our working environment.

    kubectl get nodes

    Which returns:

    azure-voting-app-rust ❯  kubectl get nodes
    NAME STATUS ROLES AGE VERSION
    aks-pool0-37917684-vmss000000 Ready agent 5d21h v1.24.6
    aks-pool0-37917684-vmss000001 Ready agent 5m27s v1.24.6
    aks-pool0-37917684-vmss000002 Ready agent 5m10s v1.24.6

    Autoscale Nodes with the Cluster Autoscaler

    Things get more interesting when we start working with the Cluster Autoscaler. The Cluster Autoscaler watches for the inability of Kubernetes to schedule the required number of pods due to resource constraints (and a few other criteria like affinity/anti-affinity). If there are insufficient resources available on the existing nodes, the autoscaler can provision new nodes into the nodepool. Likewise, the autoscaler watches to see if the existing pods could be consolidated to a smaller set of nodes and can remove excess nodes.

    Enabling the autoscaler is likewise an update that can be dependent on where and how your Kubernetes cluster is hosted. Azure makes it easy with a simple Azure CLI command.

    az aks update `
    --resource-group $ResourceGroup `
    --name $AksName `
    --update-cluster-autoscaler `
    --min-count 1 `
    --max-count 5

    There are a variety of settings that can be configured to tune how the autoscaler works.

    Scaling on Different Events

    CPU and memory utilization are the primary drivers for the Horizontal Pod Autoscaler, but those might not be the best measures as to when you might want to scale workloads. There are other options for scaling triggers and one of the more common plugins to help with that is the Kubernetes Event-driven Autoscaling (KEDA) project. The KEDA project makes it easy to plug in different event sources to help drive scaling. Find more information about using KEDA on AKS here.

    Exercise

    Let's try out the scaling configurations that we just walked through using our sample application. If you still have your environment from Day 1, you can use that.

    📝 NOTE: If you don't have an AKS cluster deployed, please head over to Azure-Samples/azure-voting-app-rust, clone the repo, and follow the instructions in the README.md to execute the Azure deployment and setup your kubectl context. Check out the first post this week for more on the environment setup.

    Configure Horizontal Pod Autoscaler

    • Edit ./manifests/deployment-app.yaml to include resource requests and limits.
            resources:
    requests:
    cpu: 250m
    limits:
    cpu: 500m
    • Apply the updated deployment configuration.
    kubectl apply -f ./manifests/deployment-app.yaml
    • Create the horizontal pod autoscaler configuration and apply it
    kubectl autoscale deployment azure-voting-app --cpu-percent=50 --min=3 --max=10 -o YAML --dry-run=client > ./manifests/scaler-app.yaml
    kubectl apply -f ./manifests/scaler-app.yaml
    • Check to see your pods scale out to the minimum.
    kubectl get pods

    Configure Cluster Autoscaler

    Configuring the basic behavior of the Cluster Autoscaler is a bit simpler. We just need to run the Azure CLI command to enable the autoscaler and define our lower and upper limits.

    • Check the current nodes available (should be 1).
    kubectl get nodes
    • Update the cluster to enable the autoscaler
    az aks update `
    --resource-group $ResourceGroup `
    --name $AksName `
    --update-cluster-autoscaler `
    --min-count 2 `
    --max-count 5
    • Check to see the current number of nodes (should be 2 now).
    kubectl get nodes

    Resources

    Take the Cloud Skills Challenge!

    Enroll in the Cloud Skills Challenge!

    Don't miss out on this opportunity to level up your skills and stay ahead of the curve in the world of cloud native.

    Documentation

    Training

    - - + + \ No newline at end of file diff --git a/cnny-2023/page/12/index.html b/cnny-2023/page/12/index.html index 3a83188929..dee9e5d838 100644 --- a/cnny-2023/page/12/index.html +++ b/cnny-2023/page/12/index.html @@ -14,15 +14,15 @@ - - + +

    · 14 min read
    Steven Murawski

    Welcome to Day 1 of Week 3 of #CloudNativeNewYear!

    The theme for this week is Bringing Your Application to Kubernetes. Last we talked about Kubernetes Fundamentals. Today we'll explore getting an existing application running in Kubernetes with a full pipeline in GitHub Actions.

    Ask the Experts Thursday, February 9th at 9 AM PST
    Friday, February 10th at 11 AM PST

    Watch the recorded demo and conversation about this week's topics.

    We were live on YouTube walking through today's (and the rest of this week's) demos. Join us Friday, February 10th and bring your questions!

    What We'll Cover

    • Our Application
    • Adding Some Infrastructure as Code
    • Building and Publishing a Container Image
    • Deploying to Kubernetes
    • Summary
    • Resources

    Our Application

    This week we'll be taking an exisiting application - something similar to a typical line of business application - and setting it up to run in Kubernetes. Over the course of the week, we'll address different concerns. Today we'll focus on updating our CI/CD process to handle standing up (or validating that we have) an Azure Kubernetes Service (AKS) environment, building and publishing container images for our web site and API server, and getting those services running in Kubernetes.

    The application we'll be starting with is eShopOnWeb. This application has a web site and API which are backed by a SQL Server instance. It's built in .NET 7, so it's cross-platform.

    info

    For the enterprising among you, you may notice that there are a number of different eShopOn* variants on GitHub, including eShopOnContainers. We aren't using that example as it's more of an end state than a starting place. Afterwards, feel free to check out that example as what this solution could look like as a series of microservices.

    Adding Some Infrastructure as Code

    Just like last week, we need to stand up an AKS environment. This week, however, rather than running commands in our own shell, we'll set up GitHub Actions to do that for us.

    There is a LOT of plumbing in this section, but once it's set up, it'll make our lives a lot easier. This section ensures that we have an environment to deploy our application into configured the way we want. We can easily extend this to accomodate multiple environments or add additional microservices with minimal new effort.

    Federated Identity

    Setting up a federated identity will allow us a more securable and auditable way of accessing Azure from GitHub Actions. For more about setting up a federated identity, Microsoft Learn has the details on connecting GitHub Actions to Azure.

    Here, we'll just walk through the setup of the identity and configure GitHub to use that idenity to deploy our AKS environment and interact with our Azure Container Registry.

    The examples will use PowerShell, but a Bash version of the setup commands is available in the week3/day1 branch.

    Prerequisites

    To follow along, you'll need:

    • a GitHub account
    • an Azure Subscription
    • the Azure CLI
    • and the Git CLI.

    You'll need to fork the source repository under your GitHub user or organization where you can manage secrets and GitHub Actions.

    It would be helpful to have the GitHub CLI, but it's not required.

    Set Up Some Defaults

    You will need to update one or more of the variables (your user or organization, what branch you want to work off of, and possibly the Azure AD application name if there is a conflict).

    # Replace the gitHubOrganizationName value
    # with the user or organization you forked
    # the repository under.

    $githubOrganizationName = 'Azure-Samples'
    $githubRepositoryName = 'eShopOnAKS'
    $branchName = 'week3/day1'
    $applicationName = 'cnny-week3-day1'

    Create an Azure AD Application

    Next, we need to create an Azure AD application.

    # Create an Azure AD application
    $aksDeploymentApplication = New-AzADApplication -DisplayName $applicationName

    Set Up Federation for that Azure AD Application

    And configure that application to allow federated credential requests from our GitHub repository for a particular branch.

    # Create a federated identity credential for the application
    New-AzADAppFederatedCredential `
    -Name $applicationName `
    -ApplicationObjectId $aksDeploymentApplication.Id `
    -Issuer 'https://token.actions.githubusercontent.com' `
    -Audience 'api://AzureADTokenExchange' `
    -Subject "repo:$($githubOrganizationName)/$($githubRepositoryName):ref:refs/heads/$branchName"

    Create a Service Principal for the Azure AD Application

    Once the application has been created, we need a service principal tied to that application. The service principal can be granted rights in Azure.

    # Create a service principal for the application
    New-AzADServicePrincipal -AppId $($aksDeploymentApplication.AppId)

    Give that Service Principal Rights to Azure Resources

    Because our Bicep deployment exists at the subscription level and we are creating role assignments, we need to give it Owner rights. If we changed the scope of the deployment to just a resource group, we could apply more scoped permissions.

    $azureContext = Get-AzContext
    New-AzRoleAssignment `
    -ApplicationId $($aksDeploymentApplication.AppId) `
    -RoleDefinitionName Owner `
    -Scope $azureContext.Subscription.Id

    Add Secrets to GitHub Repository

    If you have the GitHub CLI, you can use that right from your shell to set the secrets needed.

    gh secret set AZURE_CLIENT_ID --body $aksDeploymentApplication.AppId
    gh secret set AZURE_TENANT_ID --body $azureContext.Tenant.Id
    gh secret set AZURE_SUBSCRIPTION_ID --body $azureContext.Subscription.Id

    Otherwise, you can create them through the web interface like I did in the Learn Live event below.

    info

    It may look like the whole video will play, but it'll stop after configuring the secrets in GitHub (after about 9 minutes)

    The video shows creating the Azure AD application, service principals, and configuring the federated identity in Azure AD and GitHub.

    Creating a Bicep Deployment

    Resuable Workflows

    We'll create our Bicep deployment in a reusable workflows. What are they? The previous link has the documentation or the video below has my colleague Brandon Martinez and I talking about them.

    This workflow is basically the same deployment we did in last week's series, just in GitHub Actions.

    Start by creating a file called deploy_aks.yml in the .github/workflows directory with the below contents.

    name: deploy

    on:
    workflow_call:
    inputs:
    resourceGroupName:
    required: true
    type: string
    secrets:
    AZURE_CLIENT_ID:
    required: true
    AZURE_TENANT_ID:
    required: true
    AZURE_SUBSCRIPTION_ID:
    required: true
    outputs:
    containerRegistryName:
    description: Container Registry Name
    value: ${{ jobs.deploy.outputs.containerRegistryName }}
    containerRegistryUrl:
    description: Container Registry Login Url
    value: ${{ jobs.deploy.outputs.containerRegistryUrl }}
    resourceGroupName:
    description: Resource Group Name
    value: ${{ jobs.deploy.outputs.resourceGroupName }}
    aksName:
    description: Azure Kubernetes Service Cluster Name
    value: ${{ jobs.deploy.outputs.aksName }}

    permissions:
    id-token: write
    contents: read

    jobs:
    validate:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    - uses: azure/login@v1
    name: Sign in to Azure
    with:
    client-id: ${{ secrets.AZURE_CLIENT_ID }}
    tenant-id: ${{ secrets.AZURE_TENANT_ID }}
    subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
    - uses: azure/arm-deploy@v1
    name: Run preflight validation
    with:
    deploymentName: ${{ github.run_number }}
    scope: subscription
    region: eastus
    template: ./deploy/main.bicep
    parameters: >
    resourceGroup=${{ inputs.resourceGroupName }}
    deploymentMode: Validate

    deploy:
    needs: validate
    runs-on: ubuntu-latest
    outputs:
    containerRegistryName: ${{ steps.deploy.outputs.acr_name }}
    containerRegistryUrl: ${{ steps.deploy.outputs.acr_login_server_url }}
    resourceGroupName: ${{ steps.deploy.outputs.resource_group_name }}
    aksName: ${{ steps.deploy.outputs.aks_name }}
    steps:
    - uses: actions/checkout@v2
    - uses: azure/login@v1
    name: Sign in to Azure
    with:
    client-id: ${{ secrets.AZURE_CLIENT_ID }}
    tenant-id: ${{ secrets.AZURE_TENANT_ID }}
    subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
    - uses: azure/arm-deploy@v1
    id: deploy
    name: Deploy Bicep file
    with:
    failOnStdErr: false
    deploymentName: ${{ github.run_number }}
    scope: subscription
    region: eastus
    template: ./deploy/main.bicep
    parameters: >
    resourceGroup=${{ inputs.resourceGroupName }}

    Adding the Bicep Deployment

    Once we have the Bicep deployment workflow, we can add it to the primary build definition in .github/workflows/dotnetcore.yml

    Permissions

    First, we need to add a permissions block to let the workflow request our Azure AD token. This can go towards the top of the YAML file (I started it on line 5).

    permissions:
    id-token: write
    contents: read

    Deploy AKS Job

    Next, we'll add a reference to our reusable workflow. This will go after the build job.

      deploy_aks:
    needs: [build]
    uses: ./.github/workflows/deploy_aks.yml
    with:
    resourceGroupName: 'cnny-week3'
    secrets:
    AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
    AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
    AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }}

    Building and Publishing a Container Image

    Now that we have our target environment in place and an Azure Container Registry, we can build and publish our container images.

    Add a Reusable Workflow

    First, we'll create a new file for our reusable workflow at .github/workflows/publish_container_image.yml.

    We'll start the file with a name, the parameters it needs to run, and the permissions requirements for the federated identity request.

    name: Publish Container Images

    on:
    workflow_call:
    inputs:
    containerRegistryName:
    required: true
    type: string
    containerRegistryUrl:
    required: true
    type: string
    githubSha:
    required: true
    type: string
    secrets:
    AZURE_CLIENT_ID:
    required: true
    AZURE_TENANT_ID:
    required: true
    AZURE_SUBSCRIPTION_ID:
    required: true

    permissions:
    id-token: write
    contents: read

    Build the Container Images

    Our next step is to build the two container images we'll need for the application, the website and the API. We'll build the container images on our build worker and tag it with the git SHA, so there'll be a direct tie between the point in time in our codebase and the container images that represent it.

    jobs:
    publish_container_image:
    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v2
    - name: docker build
    run: |
    docker build . -f src/Web/Dockerfile -t ${{ inputs.containerRegistryUrl }}/web:${{ inputs.githubSha }}
    docker build . -f src/PublicApi/Dockerfile -t ${{ inputs.containerRegistryUrl }}/api:${{ inputs.githubSha}}

    Scan the Container Images

    Before we publish those container images, we'll scan them for vulnerabilities and best practice violations. We can add these two steps (one scan for each image).

        - name: scan web container image
    uses: Azure/container-scan@v0
    with:
    image-name: ${{ inputs.containerRegistryUrl }}/web:${{ inputs.githubSha}}
    - name: scan api container image
    uses: Azure/container-scan@v0
    with:
    image-name: ${{ inputs.containerRegistryUrl }}/web:${{ inputs.githubSha}}

    The container images provided have a few items that'll be found. We can create an allowed list at .github/containerscan/allowedlist.yaml to define vulnerabilities or best practice violations that we'll explictly allow to not fail our build.

    general:
    vulnerabilities:
    - CVE-2022-29458
    - CVE-2022-3715
    - CVE-2022-1304
    - CVE-2021-33560
    - CVE-2020-16156
    - CVE-2019-8457
    - CVE-2018-8292
    bestPracticeViolations:
    - CIS-DI-0001
    - CIS-DI-0005
    - CIS-DI-0006
    - CIS-DI-0008

    Publish the Container Images

    Finally, we'll log in to Azure, then log in to our Azure Container Registry, and push our images.

        - uses: azure/login@v1
    name: Sign in to Azure
    with:
    client-id: ${{ secrets.AZURE_CLIENT_ID }}
    tenant-id: ${{ secrets.AZURE_TENANT_ID }}
    subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
    - name: acr login
    run: az acr login --name ${{ inputs.containerRegistryName }}
    - name: docker push
    run: |
    docker push ${{ inputs.containerRegistryUrl }}/web:${{ inputs.githubSha}}
    docker push ${{ inputs.containerRegistryUrl }}/api:${{ inputs.githubSha}}

    Update the Build With the Image Build and Publish

    Now that we have our reusable workflow to create and publish our container images, we can include that in our primary build defnition at .github/workflows/dotnetcore.yml.

      publish_container_image:
    needs: [deploy_aks]
    uses: ./.github/workflows/publish_container_image.yml
    with:
    containerRegistryName: ${{ needs.deploy_aks.outputs.containerRegistryName }}
    containerRegistryUrl: ${{ needs.deploy_aks.outputs.containerRegistryUrl }}
    githubSha: ${{ github.sha }}
    secrets:
    AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
    AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
    AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }}

    Deploying to Kubernetes

    Finally, we've gotten enough set up that a commit to the target branch will:

    • build and test our application code
    • set up (or validate) our AKS and ACR environment
    • and create, scan, and publish our container images to ACR

    Our last step will be to deploy our application to Kubernetes. We'll use the basic building blocks we worked with last week, deployments and services.

    Starting the Reusable Workflow to Deploy to AKS

    We'll start our workflow with our parameters that we need, as well as the permissions to access the token to log in to Azure.

    We'll check out our code, then log in to Azure, and use the az CLI to get credentials for our AKS cluster.

    name: deploy_to_aks

    on:
    workflow_call:
    inputs:
    aksName:
    required: true
    type: string
    resourceGroupName:
    required: true
    type: string
    containerRegistryUrl:
    required: true
    type: string
    githubSha:
    required: true
    type: string
    secrets:
    AZURE_CLIENT_ID:
    required: true
    AZURE_TENANT_ID:
    required: true
    AZURE_SUBSCRIPTION_ID:
    required: true

    permissions:
    id-token: write
    contents: read

    jobs:
    deploy:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    - uses: azure/login@v1
    name: Sign in to Azure
    with:
    client-id: ${{ secrets.AZURE_CLIENT_ID }}
    tenant-id: ${{ secrets.AZURE_TENANT_ID }}
    subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
    - name: Get AKS Credentials
    run: |
    az aks get-credentials --resource-group ${{ inputs.resourceGroupName }} --name ${{ inputs.aksName }}

    Edit the Deployment For Our Current Image Tag

    Let's add the Kubernetes manifests to our repo. This post is long enough, so you can find the content for the manifests folder in the manifests folder in the source repo under the week3/day1 branch.

    tip

    If you only forked the main branch of the source repo, you can easily get the updated manifests by using the following git commands:

    git remote add upstream https://github.com/Azure-Samples/eShopOnAks
    git fetch upstream week3/day1
    git checkout upstream/week3/day1 manifests

    This will make the week3/day1 branch available locally and then we can update the manifests directory to match the state of that branch.

    The deployments and the service defintions should be familiar from last week's content (but not the same). This week, however, there's a new file in the manifests - ./manifests/kustomization.yaml

    This file helps us more dynamically edit our kubernetes manifests and support is baked right in to the kubectl command.

    Kustomize Definition

    Kustomize allows us to specify specific resource manifests and areas of that manifest to replace. We've put some placeholders in our file as well, so we can replace those for each run of our CI/CD system.

    In ./manifests/kustomization.yaml you will see:

    resources:
    - deployment-api.yaml
    - deployment-web.yaml

    # Change the image name and version
    images:
    - name: notavalidregistry.azurecr.io/api:v0.1.0
    newName: <YOUR_ACR_SERVER>/api
    newTag: <YOUR_IMAGE_TAG>
    - name: notavalidregistry.azurecr.io/web:v0.1.0
    newName: <YOUR_ACR_SERVER>/web
    newTag: <YOUR_IMAGE_TAG>

    Replacing Values in our Build

    Now, we encounter a little problem - our deployment files need to know the tag and ACR server. We can do a bit of sed magic to edit the file on the fly.

    In .github/workflows/deploy_to_aks.yml, we'll add:

          - name: replace_placeholders_with_current_run
    run: |
    sed -i "s/<YOUR_ACR_SERVER>/${{ inputs.containerRegistryUrl }}/g" ./manifests/kustomization.yaml
    sed -i "s/<YOUR_IMAGE_TAG>/${{ inputs.githubSha }}/g" ./manifests/kustomization.yaml

    Deploying the Manifests

    We have our manifests in place and our kustomization.yaml file (with commands to update it at runtime) ready to go, we can deploy our manifests.

    First, we'll deploy our database (deployment and service). Next, we'll use the -k parameter on kubectl to tell it to look for a kustomize configuration, transform the requested manifests and apply those. Finally, we apply the service defintions for the web and API deployments.

            run: |
    kubectl apply -f ./manifests/deployment-db.yaml \
    -f ./manifests/service-db.yaml
    kubectl apply -k ./manifests
    kubectl apply -f ./manifests/service-api.yaml \
    -f ./manifests/service-web.yaml

    Summary

    We've covered a lot of ground in today's post. We set up federated credentials with GitHub. Then we added reusable workflows to deploy an AKS environment and build/scan/publish our container images, and then to deploy them into our AKS environment.

    This sets us up to start making changes to our application and Kubernetes configuration and have those changes automatically validated and deployed by our CI/CD system. Tomorrow, we'll look at updating our application environment with runtime configuration, persistent storage, and more.

    Resources

    Take the Cloud Skills Challenge!

    Enroll in the Cloud Skills Challenge!

    Don't miss out on this opportunity to level up your skills and stay ahead of the curve in the world of cloud native.

    - - + + \ No newline at end of file diff --git a/cnny-2023/page/13/index.html b/cnny-2023/page/13/index.html index e8d9f27cda..23d8ef5023 100644 --- a/cnny-2023/page/13/index.html +++ b/cnny-2023/page/13/index.html @@ -14,13 +14,13 @@ - - + +

    · 12 min read
    Paul Yu

    Welcome to Day 2 of Week 3 of #CloudNativeNewYear!

    The theme for this week is Bringing Your Application to Kubernetes. Yesterday we talked about getting an existing application running in Kubernetes with a full pipeline in GitHub Actions. Today we'll evaluate our sample application's configuration, storage, and networking requirements and implement using Kubernetes and Azure resources.

    Ask the Experts Thursday, February 9th at 9 AM PST
    Friday, February 10th at 11 AM PST

    Watch the recorded demo and conversation about this week's topics.

    We were live on YouTube walking through today's (and the rest of this week's) demos. Join us Friday, February 10th and bring your questions!

    What We'll Cover

    • Gather requirements
    • Implement environment variables using ConfigMaps
    • Implement persistent volumes using Azure Files
    • Implement secrets using Azure Key Vault
    • Re-package deployments
    • Conclusion
    • Resources
    caution

    Before you begin, make sure you've gone through yesterday's post to set up your AKS cluster.

    Gather requirements

    The eShopOnWeb application is written in .NET 7 and has two major pieces of functionality. The web UI is where customers can browse and shop. The web UI also includes an admin portal for managing the product catalog. This admin portal, is packaged as a WebAssembly application and relies on a separate REST API service. Both the web UI and the REST API connect to the same SQL Server container.

    Looking through the source code which can be found here we can identify requirements for configs, persistent storage, and secrets.

    Database server

    • Need to store the password for the sa account as a secure secret
    • Need persistent storage volume for data directory
    • Need to inject environment variables for SQL Server license type and EULA acceptance

    Web UI and REST API service

    • Need to store database connection string as a secure secret
    • Need to inject ASP.NET environment variables to override app settings
    • Need persistent storage volume for ASP.NET key storage

    Implement environment variables using ConfigMaps

    ConfigMaps are relatively straight-forward to create. If you were following along with the examples last week, this should be review 😉

    Create a ConfigMap to store database environment variables.

    kubectl apply -f - <<EOF
    apiVersion: v1
    kind: ConfigMap
    metadata:
    name: mssql-settings
    data:
    MSSQL_PID: Developer
    ACCEPT_EULA: "Y"
    EOF

    Create another ConfigMap to store ASP.NET environment variables.

    kubectl apply -f - <<EOF
    apiVersion: v1
    kind: ConfigMap
    metadata:
    name: aspnet-settings
    data:
    ASPNETCORE_ENVIRONMENT: Development
    EOF

    Implement persistent volumes using Azure Files

    Similar to last week, we'll take advantage of storage classes built into AKS. For our SQL Server data, we'll use the azurefile-csi-premium storage class and leverage an Azure Files resource as our PersistentVolume.

    Create a PersistentVolumeClaim (PVC) for persisting SQL Server data.

    kubectl apply -f - <<EOF
    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
    name: mssql-data
    spec:
    accessModes:
    - ReadWriteMany
    storageClassName: azurefile-csi-premium
    resources:
    requests:
    storage: 5Gi
    EOF

    Create another PVC for persisting ASP.NET data.

    kubectl apply -f - <<EOF
    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
    name: aspnet-data
    spec:
    accessModes:
    - ReadWriteMany
    storageClassName: azurefile-csi-premium
    resources:
    requests:
    storage: 5Gi
    EOF

    Implement secrets using Azure Key Vault

    It's a well known fact that Kubernetes secretes are not really secrets. They're just base64-encoded values and not secure, especially if malicious users have access to your Kubernetes cluster.

    In a production scenario, you will want to leverage an external vault like Azure Key Vault or HashiCorp Vault to encrypt and store secrets.

    With AKS, we can enable the Secrets Store CSI driver add-on which will allow us to leverage Azure Key Vault.

    # Set some variables
    RG_NAME=<YOUR_RESOURCE_GROUP_NAME>
    AKS_NAME=<YOUR_AKS_CLUSTER_NAME>
    ACR_NAME=<YOUR_ACR_NAME>

    az aks enable-addons \
    --addons azure-keyvault-secrets-provider \
    --name $AKS_NAME \
    --resource-group $RG_NAME

    With the add-on enabled, you should see aks-secrets-store-csi-driver and aks-secrets-store-provider-azure resources installed on each node in your Kubernetes cluster.

    Run the command below to verify.

    kubectl get pods \
    --namespace kube-system \
    --selector 'app in (secrets-store-csi-driver, secrets-store-provider-azure)'

    The Secrets Store CSI driver allows us to use secret stores via Container Storage Interface (CSI) volumes. This provider offers capabilities such as mounting and syncing between the secure vault and Kubernetes Secrets. On AKS, the Azure Key Vault Provider for Secrets Store CSI Driver enables integration with Azure Key Vault.

    You may not have an Azure Key Vault created yet, so let's create one and add some secrets to it.

    AKV_NAME=$(az keyvault create \
    --name akv-eshop$RANDOM \
    --resource-group $RG_NAME \
    --query name -o tsv)

    # Database server password
    az keyvault secret set \
    --vault-name $AKV_NAME \
    --name mssql-password \
    --value "@someThingComplicated1234"

    # Catalog database connection string
    az keyvault secret set \
    --vault-name $AKV_NAME \
    --name mssql-connection-catalog \
    --value "Server=db;Database=Microsoft.eShopOnWeb.CatalogDb;User Id=sa;Password=@someThingComplicated1234;TrustServerCertificate=True;"

    # Identity database connection string
    az keyvault secret set \
    --vault-name $AKV_NAME \
    --name mssql-connection-identity \
    --value "Server=db;Database=Microsoft.eShopOnWeb.Identity;User Id=sa;Password=@someThingComplicated1234;TrustServerCertificate=True;"

    Pods authentication using Azure Workload Identity

    In order for our Pods to retrieve secrets from Azure Key Vault, we'll need to set up a way for the Pod to authenticate against Azure AD. This can be achieved by implementing the new Azure Workload Identity feature of AKS.

    info

    At the time of this writing, the workload identity feature of AKS is in Preview.

    The workload identity feature within AKS allows us to leverage native Kubernetes resources and link a Kubernetes ServiceAccount to an Azure Managed Identity to authenticate against Azure AD.

    For the authentication flow, our Kubernetes cluster will act as an Open ID Connect (OIDC) issuer and will be able issue identity tokens to ServiceAccounts which will be assigned to our Pods.

    The Azure Managed Identity will be granted permission to access secrets in our Azure Key Vault and with the ServiceAccount being assigned to our Pods, they will be able to retrieve our secrets.

    For more information on how the authentication mechanism all works, check out this doc.

    To implement all this, start by enabling the new preview feature for AKS.

    az feature register \
    --namespace "Microsoft.ContainerService" \
    --name "EnableWorkloadIdentityPreview"
    caution

    This can take several minutes to complete.

    Check the status and ensure the state shows Regestered before moving forward.

    az feature show \
    --namespace "Microsoft.ContainerService" \
    --name "EnableWorkloadIdentityPreview"

    Update your AKS cluster to enable the workload identity feature and enable the OIDC issuer endpoint.

    az aks update \
    --name $AKS_NAME \
    --resource-group $RG_NAME \
    --enable-workload-identity \
    --enable-oidc-issuer

    Create an Azure Managed Identity and retrieve its client ID.

    MANAGED_IDENTITY_CLIENT_ID=$(az identity create \
    --name aks-workload-identity \
    --resource-group $RG_NAME \
    --subscription $(az account show --query id -o tsv) \
    --query 'clientId' -o tsv)

    Create the Kubernetes ServiceAccount.

    # Set namespace (this must align with the namespace that your app is deployed into)
    SERVICE_ACCOUNT_NAMESPACE=default

    # Set the service account name
    SERVICE_ACCOUNT_NAME=eshop-serviceaccount

    # Create the service account
    kubectl apply -f - <<EOF
    apiVersion: v1
    kind: ServiceAccount
    metadata:
    annotations:
    azure.workload.identity/client-id: ${MANAGED_IDENTITY_CLIENT_ID}
    labels:
    azure.workload.identity/use: "true"
    name: ${SERVICE_ACCOUNT_NAME}
    namespace: ${SERVICE_ACCOUNT_NAMESPACE}
    EOF
    info

    Note to enable this ServiceAccount to work with Azure Workload Identity, you must annotate the resource with azure.workload.identity/client-id, and add a label of azure.workload.identity/use: "true"

    That was a lot... Let's review what we just did.

    We have an Azure Managed Identity (object in Azure AD), an OIDC issuer URL (endpoint in our Kubernetes cluster), and a Kubernetes ServiceAccount.

    The next step is to "tie" these components together and establish a Federated Identity Credential so that Azure AD can trust authentication requests from your Kubernetes cluster.

    info

    This identity federation can be established between Azure AD any Kubernetes cluster; not just AKS 🤗

    To establish the federated credential, we'll need the OIDC issuer URL, and a subject which points to your Kubernetes ServiceAccount.

    # Get the OIDC issuer URL
    OIDC_ISSUER_URL=$(az aks show \
    --name $AKS_NAME \
    --resource-group $RG_NAME \
    --query "oidcIssuerProfile.issuerUrl" -o tsv)

    # Set the subject name using this format: `system:serviceaccount:<YOUR_SERVICE_ACCOUNT_NAMESPACE>:<YOUR_SERVICE_ACCOUNT_NAME>`
    SUBJECT=system:serviceaccount:$SERVICE_ACCOUNT_NAMESPACE:$SERVICE_ACCOUNT_NAME

    az identity federated-credential create \
    --name aks-federated-credential \
    --identity-name aks-workload-identity \
    --resource-group $RG_NAME \
    --issuer $OIDC_ISSUER_URL \
    --subject $SUBJECT

    With the authentication components set, we can now create a SecretProviderClass which includes details about the Azure Key Vault, the secrets to pull out from the vault, and identity used to access the vault.

    # Get the tenant id for the key vault
    TENANT_ID=$(az keyvault show \
    --name $AKV_NAME \
    --resource-group $RG_NAME \
    --query properties.tenantId -o tsv)

    # Create the secret provider for azure key vault
    kubectl apply -f - <<EOF
    apiVersion: secrets-store.csi.x-k8s.io/v1
    kind: SecretProviderClass
    metadata:
    name: eshop-azure-keyvault
    spec:
    provider: azure
    parameters:
    usePodIdentity: "false"
    useVMManagedIdentity: "false"
    clientID: "${MANAGED_IDENTITY_CLIENT_ID}"
    keyvaultName: "${AKV_NAME}"
    cloudName: ""
    objects: |
    array:
    - |
    objectName: mssql-password
    objectType: secret
    objectVersion: ""
    - |
    objectName: mssql-connection-catalog
    objectType: secret
    objectVersion: ""
    - |
    objectName: mssql-connection-identity
    objectType: secret
    objectVersion: ""
    tenantId: "${TENANT_ID}"
    secretObjects:
    - secretName: eshop-secrets
    type: Opaque
    data:
    - objectName: mssql-password
    key: mssql-password
    - objectName: mssql-connection-catalog
    key: mssql-connection-catalog
    - objectName: mssql-connection-identity
    key: mssql-connection-identity
    EOF

    Finally, lets grant the Azure Managed Identity permissions to retrieve secrets from the Azure Key Vault.

    az keyvault set-policy \
    --name $AKV_NAME \
    --secret-permissions get \
    --spn $MANAGED_IDENTITY_CLIENT_ID

    Re-package deployments

    Update your database deployment to load environment variables from our ConfigMap, attach the PVC and SecretProviderClass as volumes, mount the volumes into the Pod, and use the ServiceAccount to retrieve secrets.

    Additionally, you may notice the database Pod is set to use fsGroup:10001 as part of the securityContext. This is required as the MSSQL container runs using a non-root account called mssql and this account has the proper permissions to read/write data at the /var/opt/mssql mount path.

    kubectl apply -f - <<EOF
    apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: db
    labels:
    app: db
    spec:
    replicas: 1
    selector:
    matchLabels:
    app: db
    template:
    metadata:
    labels:
    app: db
    spec:
    securityContext:
    fsGroup: 10001
    serviceAccountName: ${SERVICE_ACCOUNT_NAME}
    containers:
    - name: db
    image: mcr.microsoft.com/mssql/server:2019-latest
    ports:
    - containerPort: 1433
    envFrom:
    - configMapRef:
    name: mssql-settings
    env:
    - name: MSSQL_SA_PASSWORD
    valueFrom:
    secretKeyRef:
    name: eshop-secrets
    key: mssql-password
    resources: {}
    volumeMounts:
    - name: mssqldb
    mountPath: /var/opt/mssql
    - name: eshop-secrets
    mountPath: "/mnt/secrets-store"
    readOnly: true
    volumes:
    - name: mssqldb
    persistentVolumeClaim:
    claimName: mssql-data
    - name: eshop-secrets
    csi:
    driver: secrets-store.csi.k8s.io
    readOnly: true
    volumeAttributes:
    secretProviderClass: eshop-azure-keyvault
    EOF

    We'll update the API and Web deployments in a similar way.

    # Set the image tag
    IMAGE_TAG=<YOUR_IMAGE_TAG>

    # API deployment
    kubectl apply -f - <<EOF
    apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: api
    labels:
    app: api
    spec:
    replicas: 1
    selector:
    matchLabels:
    app: api
    template:
    metadata:
    labels:
    app: api
    spec:
    serviceAccount: ${SERVICE_ACCOUNT_NAME}
    containers:
    - name: api
    image: ${ACR_NAME}.azurecr.io/api:${IMAGE_TAG}
    ports:
    - containerPort: 80
    envFrom:
    - configMapRef:
    name: aspnet-settings
    env:
    - name: ConnectionStrings__CatalogConnection
    valueFrom:
    secretKeyRef:
    name: eshop-secrets
    key: mssql-connection-catalog
    - name: ConnectionStrings__IdentityConnection
    valueFrom:
    secretKeyRef:
    name: eshop-secrets
    key: mssql-connection-identity
    resources: {}
    volumeMounts:
    - name: aspnet
    mountPath: ~/.aspnet/https:/root/.aspnet/https:ro
    - name: eshop-secrets
    mountPath: "/mnt/secrets-store"
    readOnly: true
    volumes:
    - name: aspnet
    persistentVolumeClaim:
    claimName: aspnet-data
    - name: eshop-secrets
    csi:
    driver: secrets-store.csi.k8s.io
    readOnly: true
    volumeAttributes:
    secretProviderClass: eshop-azure-keyvault
    EOF

    ## Web deployment
    kubectl apply -f - <<EOF
    apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: web
    labels:
    app: web
    spec:
    replicas: 1
    selector:
    matchLabels:
    app: web
    template:
    metadata:
    labels:
    app: web
    spec:
    serviceAccount: ${SERVICE_ACCOUNT_NAME}
    containers:
    - name: web
    image: ${ACR_NAME}.azurecr.io/web:${IMAGE_TAG}
    ports:
    - containerPort: 80
    envFrom:
    - configMapRef:
    name: aspnet-settings
    env:
    - name: ConnectionStrings__CatalogConnection
    valueFrom:
    secretKeyRef:
    name: eshop-secrets
    key: mssql-connection-catalog
    - name: ConnectionStrings__IdentityConnection
    valueFrom:
    secretKeyRef:
    name: eshop-secrets
    key: mssql-connection-identity
    resources: {}
    volumeMounts:
    - name: aspnet
    mountPath: ~/.aspnet/https:/root/.aspnet/https:ro
    - name: eshop-secrets
    mountPath: "/mnt/secrets-store"
    readOnly: true
    volumes:
    - name: aspnet
    persistentVolumeClaim:
    claimName: aspnet-data
    - name: eshop-secrets
    csi:
    driver: secrets-store.csi.k8s.io
    readOnly: true
    volumeAttributes:
    secretProviderClass: eshop-azure-keyvault
    EOF

    If all went well with your deployment updates, you should be able to browse to your website and buy some merchandise again 🥳

    echo "http://$(kubectl get service web -o jsonpath='{.status.loadBalancer.ingress[0].ip}')"

    Conclusion

    Although there is no visible changes on with our website, we've made a ton of changes on the Kubernetes backend to make this application much more secure and resilient.

    We used a combination of Kubernetes resources and AKS-specific features to achieve our goal of securing our secrets and ensuring data is not lost on container crashes and restarts.

    To learn more about the components we leveraged here today, checkout the resources and additional tutorials listed below.

    You can also find manifests with all the changes made in today's post in the Azure-Samples/eShopOnAKS repository.

    See you in the next post!

    Resources

    Take the Cloud Skills Challenge!

    Enroll in the Cloud Skills Challenge!

    Don't miss out on this opportunity to level up your skills and stay ahead of the curve in the world of cloud native.

    - - + + \ No newline at end of file diff --git a/cnny-2023/page/14/index.html b/cnny-2023/page/14/index.html index d081e014e8..1f57bf2d75 100644 --- a/cnny-2023/page/14/index.html +++ b/cnny-2023/page/14/index.html @@ -14,13 +14,13 @@ - - + +

    · 10 min read
    Paul Yu

    Welcome to Day 3 of Week 3 of #CloudNativeNewYear!

    The theme for this week is Bringing Your Application to Kubernetes. Yesterday we added configuration, secrets, and storage to our app. Today we'll explore how to expose the eShopOnWeb app so that customers can reach it over the internet using a custom domain name and TLS.

    Ask the Experts Thursday, February 9th at 9 AM PST
    Friday, February 10th at 11 AM PST

    Watch the recorded demo and conversation about this week's topics.

    We were live on YouTube walking through today's (and the rest of this week's) demos. Join us Friday, February 10th and bring your questions!

    What We'll Cover

    • Gather requirements
    • Generate TLS certificate and store in Azure Key Vault
    • Implement custom DNS using Azure DNS
    • Enable Web Application Routing add-on for AKS
    • Implement Ingress for the web application
    • Conclusion
    • Resources

    Gather requirements

    Currently, our eShopOnWeb app has three Kubernetes services deployed:

    1. db exposed internally via ClusterIP
    2. api exposed externally via LoadBalancer
    3. web exposed externally via LoadBalancer

    As mentioned in my post last week, Services allow applications to communicate with each other using DNS names. Kubernetes has service discovery capabilities built-in that allows Pods to resolve Services simply by using their names.

    In the case of our api and web deployments, they can simply reach the database by calling its name. The service type of ClusterIP for the db can remain as-is since it only needs to be accessed by the api and web apps.

    On the other hand, api and web both need to be accessed over the public internet. Currently, these services are using service type LoadBalancer which tells AKS to provision an Azure Load Balancer with a public IP address. No one is going to remember the IP addresses, so we need to make the app more accessible by adding a custom domain name and securing it with a TLS certificate.

    Here's what we're going to need:

    • Custom domain name for our app
    • TLS certificate for the custom domain name
    • Routing rule to ensure requests with /api/ in the URL is routed to the backend REST API
    • Routing rule to ensure requests without /api/ in the URL is routing to the web UI

    Just like last week, we will use the Web Application Routing add-on for AKS. But this time, we'll integrate it with Azure DNS and Azure Key Vault to satisfy all of our requirements above.

    info

    At the time of this writing the add-on is still in Public Preview

    Generate TLS certificate and store in Azure Key Vault

    We deployed an Azure Key Vault yesterday to store secrets. We'll use it again to store a TLS certificate too.

    Let's create and export a self-signed certificate for the custom domain.

    DNS_NAME=eshoponweb$RANDOM.com
    openssl req -new -x509 -nodes -out web-tls.crt -keyout web-tls.key -subj "/CN=${DNS_NAME}" -addext "subjectAltName=DNS:${DNS_NAME}"
    openssl pkcs12 -export -in web-tls.crt -inkey web-tls.key -out web-tls.pfx -password pass:
    info

    For learning purposes we'll use a self-signed certificate and a fake custom domain name.

    To browse to the site using the fake domain, we'll mimic a DNS lookup by adding an entry to your host file which maps the public IP address assigned to the ingress controller to the custom domain.

    In a production scenario, you will need to have a real domain delegated to Azure DNS and a valid TLS certificate for the domain.

    Grab your Azure Key Vault name and set the value in a variable for later use.

    RESOURCE_GROUP=cnny-week3

    AKV_NAME=$(az resource list \
    --resource-group $RESOURCE_GROUP \
    --resource-type Microsoft.KeyVault/vaults \
    --query "[0].name" -o tsv)

    Grant yourself permissions to get, list, and import certificates.

    MY_USER_NAME=$(az account show --query user.name -o tsv)
    MY_USER_OBJECT_ID=$(az ad user show --id $MY_USER_NAME --query id -o tsv)

    az keyvault set-policy \
    --name $AKV_NAME \
    --object-id $MY_USER_OBJECT_ID \
    --certificate-permissions get list import

    Upload the TLS certificate to Azure Key Vault and grab its certificate URI.

    WEB_TLS_CERT_ID=$(az keyvault certificate import \
    --vault-name $AKV_NAME \
    --name web-tls \
    --file web-tls.pfx \
    --query id \
    --output tsv)

    Implement custom DNS with Azure DNS

    Create a custom domain for our application and grab its Azure resource id.

    DNS_ZONE_ID=$(az network dns zone create \
    --name $DNS_NAME \
    --resource-group $RESOURCE_GROUP \
    --query id \
    --output tsv)

    Enable Web Application Routing add-on for AKS

    As we enable the Web Application Routing add-on, we'll also pass in the Azure DNS Zone resource id which triggers the installation of the external-dns controller in your Kubernetes cluster. This controller will be able to write Azure DNS zone entries on your behalf as you deploy Ingress manifests.

    AKS_NAME=$(az resource list \
    --resource-group $RESOURCE_GROUP \
    --resource-type Microsoft.ContainerService/managedClusters \
    --query "[0].name" -o tsv)

    az aks enable-addons \
    --name $AKS_NAME \
    --resource-group $RESOURCE_GROUP \
    --addons web_application_routing \
    --dns-zone-resource-id=$DNS_ZONE_ID \
    --enable-secret-rotation

    The add-on will also deploy a new Azure Managed Identity which is used by the external-dns controller when writing Azure DNS zone entries. Currently, it does not have permission to do that, so let's grant it permission.

    # This is where resources are automatically deployed by AKS
    NODE_RESOURCE_GROUP=$(az aks show \
    --name $AKS_NAME \
    --resource-group $RESOURCE_GROUP \
    --query nodeResourceGroup -o tsv)

    # This is the managed identity created by the Web Application Routing add-on
    MANAGED_IDENTTIY_OBJECT_ID=$(az resource show \
    --name webapprouting-${AKS_NAME} \
    --resource-group $NODE_RESOURCE_GROUP \
    --resource-type Microsoft.ManagedIdentity/userAssignedIdentities \
    --query properties.principalId \
    --output tsv)

    # Grant the managed identity permissions to write DNS entries
    az role assignment create \
    --role "DNS Zone Contributor" \
    --assignee $MANAGED_IDENTTIY_OBJECT_ID \
    --scope $DNS_ZONE_ID

    The Azure Managed Identity will also be used to retrieve and rotate TLS certificates from Azure Key Vault. So we'll need to grant it permission for that too.

    az keyvault set-policy \
    --name $AKV_NAME \
    --object-id $MANAGED_IDENTTIY_OBJECT_ID \
    --secret-permissions get \
    --certificate-permissions get

    Implement Ingress for the web application

    Before we create a new Ingress manifest, let's update the existing services to use ClusterIP instead of LoadBalancer. With an Ingress in place, there is no reason why we need the Service resources to be accessible from outside the cluster. The new Ingress will be the only entrypoint for external users.

    We can use the kubectl patch command to update the services

    kubectl patch service api -p '{"spec": {"type": "ClusterIP"}}'
    kubectl patch service web -p '{"spec": {"type": "ClusterIP"}}'

    Deploy a new Ingress to place in front of the web Service. Notice there is a special annotations entry for kubernetes.azure.com/tls-cert-keyvault-uri which points back to our self-signed certificate that was uploaded to Azure Key Vault.

    kubectl apply -f - <<EOF
    apiVersion: networking.k8s.io/v1
    kind: Ingress
    metadata:
    annotations:
    kubernetes.azure.com/tls-cert-keyvault-uri: ${WEB_TLS_CERT_ID}
    name: web
    spec:
    ingressClassName: webapprouting.kubernetes.azure.com
    rules:
    - host: ${DNS_NAME}
    http:
    paths:
    - backend:
    service:
    name: web
    port:
    number: 80
    path: /
    pathType: Prefix
    - backend:
    service:
    name: api
    port:
    number: 80
    path: /api
    pathType: Prefix
    tls:
    - hosts:
    - ${DNS_NAME}
    secretName: web-tls
    EOF

    In our manifest above, we've also configured the Ingress route the traffic to either the web or api services based on the URL path requested. If the request URL includes /api/ then it will send traffic to the api backend service. Otherwise, it will send traffic to the web service.

    Within a few minutes, the external-dns controller will add an A record to Azure DNS which points to the Ingress resource's public IP. With the custom domain in place, we can simply browse using this domain name.

    info

    As mentioned above, since this is not a real domain name, we need to modify our host file to make it seem like our custom domain is resolving to the Ingress' public IP address.

    To get the ingress public IP, run the following:

    # Get the IP
    kubectl get ingress web -o jsonpath="{.status.loadBalancer.ingress[0].ip}"

    # Get the hostname
    kubectl get ingress web -o jsonpath="{.spec.tls[0].hosts[0]}"

    Next, open your host file and add an entry using the format <YOUR_PUBLIC_IP> <YOUR_CUSTOM_DOMAIN>. Below is an example of what it should look like.

    20.237.116.224 eshoponweb11265.com

    See this doc for more info on how to do this.

    When browsing to the website, you may be presented with a warning about the connection not being private. This is due to the fact that we are using a self-signed certificate. This is expected, so go ahead and proceed anyway to load up the page.

    Why is the Admin page broken?

    If you log in using the admin@microsoft.com account and browse to the Admin page, you'll notice no products are loaded on the page.

    This is because the admin page is built using Blazor and compiled as a WebAssembly application that runs in your browser. When the application was compiled, it packed the appsettings.Development.json file as an embedded resource. This file contains the base URL for the public API and it currently points to https://localhost:5099. Now that we have a domain name, we can update the base URL and point it to our custom domain.

    From the root of the eShopOnWeb repo, update the configuration file using a sed command.

    sed -i -e "s/localhost:5099/${DNS_NAME}/g" ./src/BlazorAdmin/wwwroot/appsettings.Development.json

    Rebuild and push the container to Azure Container Registry.

    # Grab the name of your Azure Container Registry
    ACR_NAME=$(az resource list \
    --resource-group $RESOURCE_GROUP \
    --resource-type Microsoft.ContainerRegistry/registries \
    --query "[0].name" -o tsv)

    # Invoke a build and publish job
    az acr build \
    --registry $ACR_NAME \
    --image $ACR_NAME.azurecr.io/web:v0.1.0 \
    --file ./src/Web/Dockerfile .

    Once the container build has completed, we can issue a kubectl patch command to quickly update the web deployment to test our change.

    kubectl patch deployment web -p "$(cat <<EOF
    {
    "spec": {
    "template": {
    "spec": {
    "containers": [
    {
    "name": "web",
    "image": "${ACR_NAME}.azurecr.io/web:v0.1.0"
    }
    ]
    }
    }
    }
    }
    EOF
    )"

    If all went well, you will be able to browse the admin page again and confirm product data is being loaded 🥳

    Conclusion

    The Web Application Routing add-on for AKS aims to streamline the process of exposing it to the public using the open-source NGINX Ingress Controller. With the add-on being managed by Azure, it natively integrates with other Azure services like Azure DNS and eliminates the need to manually create DNS entries. It can also integrate with Azure Key Vault to automatically pull in TLS certificates and rotate them as needed to further reduce operational overhead.

    We are one step closer to production and in the upcoming posts we'll further operationalize and secure our deployment, so stay tuned!

    In the meantime, check out the resources listed below for further reading.

    You can also find manifests with all the changes made in today's post in the Azure-Samples/eShopOnAKS repository.

    Resources

    Take the Cloud Skills Challenge!

    Enroll in the Cloud Skills Challenge!

    Don't miss out on this opportunity to level up your skills and stay ahead of the curve in the world of cloud native.

    - - + + \ No newline at end of file diff --git a/cnny-2023/page/15/index.html b/cnny-2023/page/15/index.html index 626a51541e..40593029ff 100644 --- a/cnny-2023/page/15/index.html +++ b/cnny-2023/page/15/index.html @@ -14,13 +14,13 @@ - - + +

    · 9 min read
    Steven Murawski

    Welcome to Day 4 of Week 3 of #CloudNativeNewYear!

    The theme for this week is Bringing Your Application to Kubernetes. Yesterday we exposed the eShopOnWeb app so that customers can reach it over the internet using a custom domain name and TLS. Today we'll explore the topic of debugging and instrumentation.

    Ask the Experts Thursday, February 9th at 9 AM PST
    Friday, February 10th at 11 AM PST

    Watch the recorded demo and conversation about this week's topics.

    We were live on YouTube walking through today's (and the rest of this week's) demos. Join us Friday, February 10th and bring your questions!

    What We'll Cover

    • Debugging
    • Bridge To Kubernetes
    • Instrumentation
    • Resources: For self-study!

    Debugging

    Debugging applications in a Kubernetes cluster can be challenging for several reasons:

    • Complexity: Kubernetes is a complex system with many moving parts, including pods, nodes, services, and config maps, all of which can interact in unexpected ways and cause issues.
    • Distributed Environment: Applications running in a Kubernetes cluster are often distributed across multiple nodes, which makes it harder to determine the root cause of an issue.
    • Logging and Monitoring: Debugging an application in a Kubernetes cluster requires access to logs and performance metrics, which can be difficult to obtain in a large and dynamic environment.
    • Resource Management: Kubernetes manages resources such as CPU and memory, which can impact the performance and behavior of applications. Debugging resource-related issues requires a deep understanding of the Kubernetes resource model and the underlying infrastructure.
    • Dynamic Nature: Kubernetes is designed to be dynamic, with the ability to add and remove resources as needed. This dynamic nature can make it difficult to reproduce issues and debug problems.

    However, there are many tools and practices that can help make debugging applications in a Kubernetes cluster easier, such as using centralized logging, monitoring, and tracing solutions, and following best practices for managing resources and deployment configurations.

    There's also another great tool in our toolbox - Bridge to Kubernetes.

    Bridge to Kubernetes

    Bridge to Kubernetes is a great tool for microservice development and debugging applications without having to locally replicate all the required microservices.

    Bridge to Kubernetes works with Visual Studio or Visual Studio Code.

    We'll walk through using it with Visual Studio Code.

    Connecting Bridge to Kubernetes to Our Cluster

    Ensure your AKS cluster is the default for kubectl

    If you've recently spun up a new AKS cluster or you have been working with a different cluster, you may need to change what cluster credentials you have configured.

    If it's a new cluster, we can use:

    RESOURCE_GROUP=<YOUR RESOURCE GROUP NAME>
    CLUSTER_NAME=<YOUR AKS CLUSTER NAME>
    az aks get-credentials az aks get-credentials --resource-group $RESOURCE_GROUP --name $CLUSTER_NAME

    Open the command palette

    Open the command palette and find Bridge to Kubernetes: Configure. You may need to start typing the name to get it to show up.

    The command palette for Visual Studio Code is open and the first item is Bridge to Kubernetes: Configure

    Pick the service you want to debug

    Bridge to Kubernetes will redirect a service for you. Pick the service you want to redirect, in this case we'll pick web.

    Selecting the `web` service to redirect in Visual Studio Code

    Identify the port your application runs on

    Next, we'll be prompted to identify what port our application will run on locally. For this application it'll be 5001, but that's just specific to this application (and the default for ASP.NET 7, I believe).

    Setting port 5001 as the port to redirect to the `web` Kubernetes service in Visual Studio Code

    Pick a debug configuration to extend

    Bridge to Kubernetes has a couple of ways to run - it can inject it's setup and teardown to your existing debug configurations. We'll pick .NET Core Launch (web).

    Telling Bridge to Kubernetes to use the .NET Core Launch (web) debug configuration in Visual Studio Code

    Forward Traffic for All Requests

    The last prompt you'll get in the configuration is about how you want Bridge to Kubernetes to handle re-routing traffic. The default is that all requests into the service will get your local version.

    You can also redirect specific traffic. Bridge to Kubernetes will set up a subdomain and route specific traffic to your local service, while allowing other traffic to the deployed service.

    Allowing the launch of Endpoint Manager on Windows

    Using Bridge to Kubernetes to Debug Our Service

    Now that we've configured Bridge to Kubernetes, we see that tasks and a new launch configuration have been added.

    Added to .vscode/tasks.json:

            {
    "label": "bridge-to-kubernetes.resource",
    "type": "bridge-to-kubernetes.resource",
    "resource": "web",
    "resourceType": "service",
    "ports": [
    5001
    ],
    "targetCluster": "aks1",
    "targetNamespace": "default",
    "useKubernetesServiceEnvironmentVariables": false
    },
    {
    "label": "bridge-to-kubernetes.compound",
    "dependsOn": [
    "bridge-to-kubernetes.resource",
    "build"
    ],
    "dependsOrder": "sequence"
    }

    And added to .vscode/launch.json:

    {
    "name": ".NET Core Launch (web) with Kubernetes",
    "type": "coreclr",
    "request": "launch",
    "preLaunchTask": "bridge-to-kubernetes.compound",
    "program": "${workspaceFolder}/src/Web/bin/Debug/net7.0/Web.dll",
    "args": [],
    "cwd": "${workspaceFolder}/src/Web",
    "stopAtEntry": false,
    "env": {
    "ASPNETCORE_ENVIRONMENT": "Development",
    "ASPNETCORE_URLS": "http://+:5001"
    },
    "sourceFileMap": {
    "/Views": "${workspaceFolder}/Views"
    }
    }

    Launch the debug configuration

    We can start the process with the .NET Core Launch (web) with Kubernetes launch configuration in the Debug pane in Visual Studio Code.

    Launch the `.NET Core Launch (web) with Kubernetes` from the Debug pane in Visual Studio Code

    Enable the Endpoint Manager

    Part of this process includes a local service to help manage the traffic routing and your hosts file. This will require admin or sudo privileges. On Windows, you'll get a prompt like:

    Prompt to launch the endpoint manager.

    Access your Kubernetes cluster "locally"

    Bridge to Kubernetes will set up a tunnel (thanks to port forwarding) to your local workstation and create local endpoints for the other Kubernetes hosted services in your cluster, as well as pretending to be a pod in that cluster (for the application you are debugging).

    Output from Bridge To Kubernetes setup task.

    After making the connection to your Kubernetes cluster, the launch configuration will continue. In this case, we'll make a debug build of the application and attach the debugger. (This process may cause the terminal in VS Code to scroll with build output. You can find the Bridge to Kubernetes output with the local IP addresses and ports in the Output pane for Bridge to Kubernetes.)

    You can set breakpoints, use your debug console, set watches, run tests against your local version of the service.

    Exploring the Running Application Environment

    One of the cool things that Bridge to Kubernetes does for our debugging experience is bring the environment configuration that our deployed pod would inherit. When we launch our app, it'll see configuration and secrets that we'd expect our pod to be running with.

    To test this, we'll set a breakpoint in our application's start up to see what SQL Server is being used. We'll set a breakpoint at src/Infrastructure/Dependencies.cs on line 32.

    Then, we will start debugging the application with Bridge to Kubernetes. When it hits the breakpoint, we'll open the Debug pane and type configuration.GetConnectionString("CatalogConnection").

    When we run locally (not with Bridge to Kubernetes), we'd see:

    configuration.GetConnectionString("CatalogConnection")
    "Server=(localdb)\\mssqllocaldb;Integrated Security=true;Initial Catalog=Microsoft.eShopOnWeb.CatalogDb;"

    But, with Bridge to Kubernetes we see something more like (yours will vary based on the password ):

    configuration.GetConnectionString("CatalogConnection")
    "Server=db;Database=Microsoft.eShopOnWeb.CatalogDb;User Id=sa;Password=*****************;TrustServerCertificate=True;"

    Debugging our local application connected to Kubernetes.

    We can see that the database server configured is based on our db service and the password is pulled from our secret in Azure KeyVault (via AKS).

    This helps us run our local application just like it was actually in our cluster.

    Going Further

    Bridge to Kubernetes also supports more advanced scenarios and, as you need to start routing traffic around inside your cluster, may require you to modify your application to pass along a kubernetes-route-as header to help ensure that traffic for your debugging workloads is properly handled. The docs go into much greater detail about that.

    Instrumentation

    Now that we've figured out our debugging story, we'll need to ensure that we have the right context clues to find where we need to debug or to give us a better idea of how well our microservices are running.

    Logging and Tracing

    Logging and tracing become even more critical in Kubernetes, where your application could be running in a number of pods across different nodes. When you have an issue, in addition to the normal application data, you'll want to know what pod and what node had the issue, what the state of those resources were (were you resource constrained or were shared resources unavailable?), and if autoscaling is enabled, you'll want to know if a scale event has been triggered. There are a multitude of other concerns based on your application and the environment you maintain.

    Given these informational needs, it's crucial to revisit your existing logging and instrumentation. Most frameworks and languages have extensible logging, tracing, and instrumentation libraries that you can iteratively add information to, such as pod and node states, and ensuring that requests can be traced across your microservices. This will pay you back time and time again when you have to troubleshoot issues in your existing environment.

    Centralized Logging

    To enhance the troubleshooting process further, it's important to implement centralized logging to consolidate logs from all your microservices into a single location. This makes it easier to search and analyze logs when you're troubleshooting an issue.

    Automated Alerting

    Additionally, implementing automated alerting, such as sending notifications when specific conditions occur in the logs, can help you detect issues before they escalate.

    End to end Visibility

    End-to-end visibility is also essential in understanding the flow of requests and responses between microservices in a distributed system. With end-to-end visibility, you can quickly identify bottlenecks and slowdowns in the system, helping you to resolve issues more efficiently.

    Resources

    Take the Cloud Skills Challenge!

    Enroll in the Cloud Skills Challenge!

    Don't miss out on this opportunity to level up your skills and stay ahead of the curve in the world of cloud native.

    - - + + \ No newline at end of file diff --git a/cnny-2023/page/16/index.html b/cnny-2023/page/16/index.html index fea6fd29d1..0fed798e1c 100644 --- a/cnny-2023/page/16/index.html +++ b/cnny-2023/page/16/index.html @@ -14,13 +14,13 @@ - - + +

    · 6 min read
    Josh Duffney

    Welcome to Day 5 of Week 3 of #CloudNativeNewYear!

    The theme for this week is Bringing Your Application to Kubernetes. Yesterday we talked about debugging and instrumenting our application. Today we'll explore the topic of container image signing and secure supply chain.

    Ask the Experts Thursday, February 9th at 9 AM PST
    Friday, February 10th at 11 AM PST

    Watch the recorded demo and conversation about this week's topics.

    We were live on YouTube walking through today's (and the rest of this week's) demos. Join us Friday, February 10th and bring your questions!

    What We'll Cover

    • Introduction
    • Prerequisites
    • Create a digital signing certificate
    • Generate an Azure Container Registry Token
    • Set up Notation
    • Install the Notation Azure Key Vault Plugin
    • Add the signing Certificate to Notation
    • Sign Container Images
    • Conclusion

    Introduction

    The secure supply chain is a crucial aspect of software development, delivery, and deployment, and digital signing plays a critical role in this process.

    By using digital signatures to verify the authenticity and integrity of container images, organizations can improve the security of your software supply chain and reduce the risk of security breaches and data compromise.

    In this article, you'll learn how to use Notary, an open-source project hosted by the Cloud Native Computing Foundation (CNCF) to digitally sign container images stored on Azure Container Registry.

    Prerequisites

    To follow along, you'll need an instance of:

    Create a digital signing certificate

    A digital signing certificate is a certificate that is used to digitally sign and verify the authenticity and integrity of digital artifacts. Such documents, software, and of course container images.

    Before you can implement digital signatures, you must first create a digital signing certificate.

    Run the following command to generate the certificate:

    1. Create the policy file

      cat <<EOF > ./my_policy.json
      {
      "issuerParameters": {
      "certificateTransparency": null,
      "name": "Self"
      },
      "x509CertificateProperties": {
      "ekus": [
      "1.3.6.1.5.5.7.3.3"
      ],
      "key_usage": [
      "digitalSignature"
      ],
      "subject": "CN=${keySubjectName}",
      "validityInMonths": 12
      }
      }
      EOF

      The ekus and key usage of this certificate policy dictate that the certificate can only be used for digital signatures.

    2. Create the certificate in Azure Key Vault

      az keyvault certificate create --name $keyName --vault-name $keyVaultName --policy @my_policy.json

      Replace $keyName and $keyVaultName with your desired certificate name and Azure Key Vault instance name.

    Generate a Azure Container Registry token

    Azure Container Registry tokens are used to grant access to the contents of the registry. Tokens can be used for a variety of things such as pulling images, pushing images, or managing the registry.

    As part of the container image signing workflow, you'll need a token to authenticate the Notation CLI with your Azure Container Registry.

    Run the following command to generate an ACR token:

    az acr token create \
    --name $tokenName \
    --registry $registry \
    --scope-map _repositories_admin \
    --query 'credentials.passwords[0].value' \
    --only-show-errors \
    --output tsv

    Replace $tokenName with your name for the ACR token and $registry with the name of your ACR instance.

    Setup Notation

    Notation is the command-line interface for the CNCF Notary project. You'll use it to digitally sign the api and web container images for the eShopOnWeb application.

    Run the following commands to download and install the NotationCli:

    1. Open a terminal or command prompt window

    2. Download the Notary notation release

      curl -Lo notation.tar.gz https://github.com/notaryproject/notation/releases/download/v1.0.0-rc.1/notation_1.0.0-rc.1_linux_amd64.tar.gz > /dev/null 2>&1

      If you're not using Linux, you can find the releases here.

    3. Extract the contents of the notation.tar.gz

      tar xvzf notation.tar.gz > /dev/null 2>&1
    4. Copy the notation binary to the $HOME/bin directory

      cp ./notation $HOME/bin
    5. Add the $HOME/bin directory to the PATH environment variable

      export PATH="$HOME/bin:$PATH"
    6. Remove the downloaded files

      rm notation.tar.gz LICENSE
    7. Check the notation version

      notation --version

    Install the Notation Azure Key Vault plugin

    By design the NotationCli supports plugins that extend its digital signing capabilities to remote registries. And in order to sign your container images stored in Azure Container Registry, you'll need to install the Azure Key Vault plugin for Notation.

    Run the following commands to install the azure-kv plugin:

    1. Download the plugin

      curl -Lo notation-azure-kv.tar.gz \
      https://github.com/Azure/notation-azure-kv/releases/download/v0.5.0-rc.1/notation-azure-kv_0.5.0-rc.1_linux_amd64.tar.gz > /dev/null 2>&1

      Non-Linux releases can be found here.

    2. Extract to the plugin directory & delete download files

      tar xvzf notation-azure-kv.tar.gz -C ~/.config/notation/plugins/azure-kv notation-azure-kv > /dev/null 2>&

      rm -rf notation-azure-kv.tar.gz
    3. Verify the plugin was installed

      notation plugin ls

    Add the signing certificate to Notation

    Now that you have Notation and the Azure Key Vault plugin installed, add the certificate's keyId created above to Notation.

    1. Get the Certificate Key ID from Azure Key Vault

      az keyvault certificate show \
      --vault-name $keyVaultName \
      --name $keyName \
      --query "kid" --only-show-errors --output tsv

      Replace $keyVaultName and $keyName with the appropriate information.

    2. Add the Key ID to KMS using Notation

      notation key add --plugin azure-kv --id $keyID $keyName
    3. Check the key list

      notation key ls

    Sign Container Images

    At this point, all that's left is to sign the container images.

    Run the notation sign command to sign the api and web container images:

    notation sign $registry.azurecr.io/web:$tag \
    --username $tokenName \
    --password $tokenPassword

    notation sign $registry.azurecr.io/api:$tag \
    --username $tokenName \
    --password $tokenPassword

    Replace $registry, $tag, $tokenName, and $tokenPassword with the appropriate values. To improve security, use a SHA hash for the tag.

    NOTE: If you didn't take note of the token password, you can rerun the az acr token create command to generate a new password.

    Conclusion

    Digital signing plays a critical role in ensuring the security of software supply chains.

    By signing software components, organizations can verify the authenticity and integrity of software, helping to prevent unauthorized modifications, tampering, and malware.

    And if you want to take digital signing to a whole new level by using them to prevent the deployment of unsigned container images, check out the Ratify project on GitHub!

    Take the Cloud Skills Challenge!

    Enroll in the Cloud Skills Challenge!

    Don't miss out on this opportunity to level up your skills and stay ahead of the curve in the world of cloud native.

    - - + + \ No newline at end of file diff --git a/cnny-2023/page/17/index.html b/cnny-2023/page/17/index.html index 1c8df4391e..6b4cbbd226 100644 --- a/cnny-2023/page/17/index.html +++ b/cnny-2023/page/17/index.html @@ -14,13 +14,13 @@ - - + +

    · 7 min read
    Nitya Narasimhan

    Welcome to Week 4 of #CloudNativeNewYear!

    This week we'll go further with Cloud-native by exploring advanced topics and best practices for the Cloud-native practitioner. We'll start with an exploration of Serverless Container Options - ranging from managed services to Azure Kubernetes Service (AKS) and Azure Container Apps (ACA), to options that allow more granular control!

    What We'll Cover

    • The Azure Compute Landscape
    • Serverless Compute on Azure
    • Comparing Container Options On Azure
    • Other Considerations
    • Exercise: Try this yourself!
    • Resources: For self-study!


    We started this series with an introduction to core concepts:

    • In Containers 101, we learned why containerization matters. Think portability, isolation, scalability, resource-efficiency and cost-effectiveness. But not all apps can be containerized.
    • In Kubernetes 101, we learned how orchestration works. Think systems to automate container deployment, scaling, and management. But using Kubernetes directly can be complex.
    • In Exploring Cloud Native Options we asked the real questions: can we containerize - and should we?. The first depends on app characteristics, the second on your requirements.

    For example:

    • Can we containerize? The answer might be no if your app has system or OS dependencies, requires access to low-level hardware, or maintains complex state across sessions.
    • Should we containerize? The answer might be yes if your app is microservices-based, is stateless by default, requires portability, or is a legaacy app that can benefit from container isolation.

    As with every technology adoption decision process, there are no clear yes/no answers - just tradeoffs that you need to evaluate based on your architecture and application requirements. In today's post, we try to look at this from two main perspectives:

    1. Should you go serverless? Think: managed services that let you focus on app, not infra.
    2. What Azure Compute should I use? Think: best fit for my architecture & technology choices.

    Azure Compute Landscape

    Let's answer the second question first by exploring all available compute options on Azure. The illustrated decision-flow below is my favorite ways to navigate the choices, with questions like:

    • Are you migrating an existing app or building a new one?
    • Can you app be containerized?
    • Does it use a specific technology (Spring Boot, Red Hat Openshift)?
    • Do you need access to the Kubernetes API?
    • What characterizes the workload? (event-driven, web app, microservices etc.)

    Read the docs to understand how your choices can be influenced by the hosting model (IaaS, PaaS, FaaS), supported features (Networking, DevOps, Scalability, Availability, Security), architectural styles (Microservices, Event-driven, High-Performance Compute, Task Automation,Web-Queue Worker) etc.

    Compute Choices

    Now that we know all available compute options, let's address the second question: why go serverless? and what are my serverless compute options on Azure?

    Azure Serverless Compute

    Serverless gets defined many ways, but from a compute perspective, we can focus on a few key characteristics that are key to influencing this decision:

    • managed services - focus on application, let cloud provider handle infrastructure.
    • pay for what you use - get cost-effective resource utilization, flexible pricing options.
    • autoscaling on demand - take advantage of built-in features like KEDA-compliant triggers.
    • use preferred languages - write code in Java, JS, C#, Python etc. (specifics based on service)
    • cloud-native architectures - can support event-driven solutions, APIs, Microservices, DevOps!

    So what are some of the key options for Serverless Compute on Azure? The article dives into serverless support for fully-managed end-to-end serverless solutions with comprehensive support for DevOps, DevTools, AI/ML, Database, Storage, Monitoring and Analytics integrations. But we'll just focus on the 4 categories of applications when we look at Compute!

    1. Serverless Containerized Microservices using Azure Container Apps. Code in your preferred language, exploit full Dapr support, scale easily with any KEDA-compliant trigger.
    2. Serverless Application Environments using Azure App Service. Suitable for hosting monolithic apps (vs. microservices) in a managed service, with built-in support for on-demand scaling.
    3. Serverless Kubernetes using Azure Kubernetes Service (AKS). Spin up pods inside container instances and deploy Kubernetes-based applications with built-in KEDA-compliant autoscaling.
    4. Serverless Functions using Azure Functions. Execute "code at the granularity of functions" in your preferred language, and scale on demand with event-driven compute.

    We'll talk about these, and other compute comparisons, at the end of the article. But let's start with the core option you might choose if you want a managed serverless compute solution with built-in features for delivering containerized microservices at scale. Hello, Azure Container Apps!.

    Azure Container Apps

    Azure Container Apps (ACA) became generally available in May 2022 - providing customers with the ability to run microservices and containerized applications on a serverless, consumption-based platform. The figure below showcases the different types of applications that can be built with ACA. Note that it comes with built-in KEDA-compliant autoscaling triggers, and other auto-scale criteria that may be better-suited to the type of application you are building.

    About ACA

    So far in the series, we've put the spotlight on Azure Kubernetes Service (AKS) - so you're probably asking yourself: How does ACA compare to AKS?. We're glad you asked. Check out our Go Cloud-native with Azure Container Apps post from the #ServerlessSeptember series last year for a deeper-dive, or review the figure below for the main comparison points.

    The key takeaway is this. Azure Container Apps (ACA) also runs on Kubernetes but abstracts away its complexity in a managed service offering that lets you get productive quickly without requiring detailed knowledge of Kubernetes workings or APIs. However, if you want full access and control over the Kubernetes API then go with Azure Kubernetes Service (AKS) instead.

    Comparison

    Other Container Options

    Azure Container Apps is the preferred Platform As a Service (PaaS) option for a fully-managed serverless solution on Azure that is purpose-built for cloud-native microservices-based application workloads. But - there are other options that may be suitable for your specific needs, from a requirements and tradeoffs perspective. Let's review them quickly:

    1. Azure Functions is the serverless Functions-as-a-Service (FaaS) option, as opposed to ACA which supports a PaaS approach. It's optimized for running event-driven applications built at the granularity of ephemeral functions that can be deployed as code or containers.
    2. Azure App Service provides fully managed hosting for web applications that may be deployed using code or containers. It can be integrated with other services including Azure Container Apps and Azure Functions. It's optimized for deploying traditional web apps.
    3. Azure Kubernetes Service provides a fully managed Kubernetes option capable of running any Kubernetes workload, with direct access to the Kubernetes API.
    4. Azure Container Instances provides a single pod of Hyper-V isolated containers on demand, making them more of a low-level "building block" option compared to ACA.

    Based on the technology choices you made for application development you may also have more specialized options you want to consider. For instance:

    1. Azure Spring Apps is ideal if you're running Spring Boot or Spring Cloud workloads on Azure,
    2. Azure Red Hat OpenShift is ideal for integrated Kubernetes-powered OpenShift on Azure.
    3. Azure Confidential Computing is ideal if you have data/code integrity and confidentiality needs.
    4. Kubernetes At The Edge is ideal for bare-metal options that extend compute to edge devices.

    This is just the tip of the iceberg in your decision-making journey - but hopefully, it gave you a good sense of the options and criteria that influences your final choices. Let's wrap this up with a look at self-study resources for skilling up further.

    Exercise

    Want to get hands on learning related to these technologies?

    TAKE THE CLOUD SKILLS CHALLENGE

    Register today and level up your skills by completing free learning modules, while competing with your peers for a place on the leaderboards!

    Resources

    - - + + \ No newline at end of file diff --git a/cnny-2023/page/18/index.html b/cnny-2023/page/18/index.html index 897d2a7c15..97c8c41c53 100644 --- a/cnny-2023/page/18/index.html +++ b/cnny-2023/page/18/index.html @@ -14,13 +14,13 @@ - - + +

    · 3 min read
    Cory Skimming

    It's the final week of #CloudNativeNewYear! This week we'll go further with Cloud-native by exploring advanced topics and best practices for the Cloud-native practitioner. In today's post, we will introduce you to the basics of the open-source project Draft and how it can be used to easily create and deploy applications to Kubernetes.

    It's not too late to sign up for and complete the Cloud Skills Challenge!

    What We'll Cover

    • What is Draft?
    • Draft basics
    • Demo: Developing to AKS with Draft
    • Resources


    What is Draft?

    Draft is an open-source tool that can be used to streamline the development and deployment of applications on Kubernetes clusters. It provides a simple and easy-to-use workflow for creating and deploying applications, making it easier for developers to focus on writing code and building features, rather than worrying about the underlying infrastructure. This is great for users who are just getting started with Kubernetes, or those who are just looking to simplify their experience.

    New to Kubernetes?

    Draft basics

    Draft streamlines Kubernetes development by taking a non-containerized application and generating the Dockerfiles, K8s manifests, Helm charts, and other artifacts associated with a containerized application. Draft can also create a GitHub Action workflow file to quickly build and deploy your application onto any Kubernetes cluster.

    1. 'draft create'': Create a new Draft project by simply running the 'draft create' command - this command will walk you through a series of questions on your application specification (such as the application language) and create a Dockerfile, Helm char, and Kubernetes
    2. 'draft generate-workflow'': Automatically build out a GitHub Action using the 'draft generate-workflow' command
    3. 'draft setup-gh'': If you are using Azure, use this command to automate the GitHub OIDC set up process to ensure that you will be able to deploy your application using your GitHub Action.

    At this point, you will have all the files needed to deploy your app onto a Kubernetes cluster (we told you it was easy!).

    You can also use the 'draft info' command if you are looking for information on supported languages and deployment types. Let's see it in action, shall we?


    Developing to AKS with Draft

    In this Microsoft Reactor session below, we'll briefly introduce Kubernetes and the Azure Kubernetes Service (AKS) and then demo how enable your applications for Kubernetes using the open-source tool Draft. We'll show how Draft can help you create the boilerplate code to containerize your applications and add routing and scaling behaviours.

    ##Conclusion

    Overall, Draft simplifies the process of building, deploying, and managing applications on Kubernetes, and can make the overall journey from code to Kubernetes significantly easier.


    Resources


    - - + + \ No newline at end of file diff --git a/cnny-2023/page/19/index.html b/cnny-2023/page/19/index.html index ec07f138f3..d054e7c18c 100644 --- a/cnny-2023/page/19/index.html +++ b/cnny-2023/page/19/index.html @@ -14,14 +14,14 @@ - - + +

    · 7 min read
    Vinicius Apolinario

    Welcome to Day 3 of Week 4 of #CloudNativeNewYear!

    The theme for this week is going further with Cloud Native. Yesterday we talked about using Draft to accelerate your Kubernetes adoption. Today we'll explore the topic of Windows containers.

    What We'll Cover

    • Introduction
    • Windows containers overview
    • Windows base container images
    • Isolation
    • Exercise: Try this yourself!
    • Resources: For self-study!

    Introduction

    Windows containers were launched along with Windows Server 2016, and have evolved since. In its latest release, Windows Server 2022, Windows containers have reached a great level of maturity and allow for customers to run production grade workloads.

    While suitable for new developments, Windows containers also provide developers and operations with a different approach than Linux containers. It allows for existing Windows applications to be containerized with little or no code changes. It also allows for professionals that are more comfortable with the Windows platform and OS, to leverage their skill set, while taking advantage of the containers platform.

    Windows container overview

    In essence, Windows containers are very similar to Linux. Since Windows containers use the same foundation of Docker containers, you can expect that the same architecture applies - with the specific notes of the Windows OS. For example, when running a Windows container via Docker, you use the same commands, such as docker run. To pull a container image, you can use docker pull, just like on Linux. However, to run a Windows container, you also need a Windows container host. This requirement is there because, as you might remember, a container shares the OS kernel with its container host.

    On Kubernetes, Windows containers are supported since Windows Server 2019. Just like with Docker, you can manage Windows containers like any other resource on the Kubernetes ecosystem. A Windows node can be part of a Kubernetes cluster, allowing you to run Windows container based applications on services like Azure Kubernetes Service. To deploy an Windows application to a Windows pod in Kubernetes, you can author a YAML specification much like you would for Linux. The main difference is that you would point to an image that runs on Windows, and you need to specify a node selection tag to indicate said pod needs to run on a Windows node.

    Windows base container images

    On Windows containers, you will always use a base container image provided by Microsoft. This base container image contains the OS binaries for the container to run. This image can be as large as 3GB+, or small as ~300MB. The difference in the size is a consequence of the APIs and components available in each Windows container base container image. There are primarily, three images: Nano Server, Server Core, and Server.

    Nano Server is the smallest image, ranging around 300MB. It's a base container image for new developments and cloud-native scenarios. Applications need to target Nano Server as the Windows OS, so not all frameworks will work. For example, .Net works on Nano Server, but .Net Framework doesn't. Other third-party frameworks also work on Nano Server, such as Apache, NodeJS, Phyton, Tomcat, Java runtime, JBoss, Redis, among others.

    Server Core is a much larger base container image, ranging around 1.25GB. It's larger size is compensated by it's application compatibility. Simply put, any application that meets the requirements to be run on a Windows container, can be containerized with this image.

    The Server image builds on the Server Core one. It ranges around 3.1GB and has even greater application compatibility than the Server Core image. In addition to the traditional Windows APIs and components, this image allows for scenarios such as Machine Learning via DirectX with GPU access.

    The best image for your scenario is dependent on the requirements your application has on the Windows OS inside a container. However, there are some scenarios that are not supported at all on Windows containers - such as GUI or RDP dependent applications, some Windows Server infrastructure roles, such as Active Directory, among others.

    Isolation

    When running containers, the kernel of the container host is shared with the containers running on it. While extremely convenient, this poses a potential risk for multi-tenant scenarios. If one container is compromised and has access to the host, it could potentially compromise other containers in the same system.

    For enterprise customers running on-premises (or even in the cloud), this can be mitigated by using a VM as a container host and considering the VM itself a security boundary. However, if multiple workloads from different tenants need to share the same host, Windows containers offer another option: Hyper-V isolation. While the name Hyper-V is associated with VMs, its virtualization capabilities extend to other services, including containers. Hyper-V isolated containers run on a purpose built, extremely small, highly performant VM. However, you manage a container running with Hyper-V isolation the same way you do with a process isolated one. In fact, the only notable difference is that you need to append the --isolation=hyperv tag to the docker run command.

    Exercise

    Here are a few examples of how to use Windows containers:

    Run Windows containers via Docker on your machine

    To pull a Windows base container image:

    docker pull mcr.microsoft.com/windows/servercore:ltsc2022

    To run a basic IIS container:

    #This command will pull and start a IIS container. You can access it from http://<your local IP>:8080
    docker run -d -p 8080:80 mcr.microsoft.com/windows/servercore/iis:windowsservercore-ltsc2022

    Run the same IIS container with Hyper-V isolation

    #This command will pull and start a IIS container. You can access it from http://<your local IP>:8080
    docker run -d -p 8080:80 --isolation=hyperv mcr.microsoft.com/windows/servercore/iis:windowsservercore-ltsc2022

    To run a Windows container interactively:

    docker run -it mcr.microsoft.com/windows/servercore:ltsc2022 powershell

    Run Windows containers on Kubernetes

    To prepare an AKS cluster for Windows containers: Note: Replace the values on the example below with the ones from your environment.

    echo "Please enter the username to use as administrator credentials for Windows Server nodes on your cluster: " && read WINDOWS_USERNAME
    az aks create \
    --resource-group myResourceGroup \
    --name myAKSCluster \
    --node-count 2 \
    --generate-ssh-keys \
    --windows-admin-username $WINDOWS_USERNAME \
    --vm-set-type VirtualMachineScaleSets \
    --network-plugin azure

    To add a Windows node pool for Windows containers:

    az aks nodepool add \
    --resource-group myResourceGroup \
    --cluster-name myAKSCluster \
    --os-type Windows \
    --name npwin \
    --node-count 1

    Deploy a sample ASP.Net application to the AKS cluster above using the YAML file below:

    apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: sample
    labels:
    app: sample
    spec:
    replicas: 1
    template:
    metadata:
    name: sample
    labels:
    app: sample
    spec:
    nodeSelector:
    "kubernetes.io/os": windows
    containers:
    - name: sample
    image: mcr.microsoft.com/dotnet/framework/samples:aspnetapp
    resources:
    limits:
    cpu: 1
    memory: 800M
    ports:
    - containerPort: 80
    selector:
    matchLabels:
    app: sample
    ---
    apiVersion: v1
    kind: Service
    metadata:
    name: sample
    spec:
    type: LoadBalancer
    ports:
    - protocol: TCP
    port: 80
    selector:
    app: sample

    Save the file above and run the command below on your Kubernetes cluster:

    kubectl apply -f <filename> .

    Once deployed, you can access the application by getting the IP address of your service:

    kubectl get service

    Resources

    It's not too late to sign up for and complete the Cloud Skills Challenge!
    - - + + \ No newline at end of file diff --git a/cnny-2023/page/2/index.html b/cnny-2023/page/2/index.html index 20bc325a85..5314d17203 100644 --- a/cnny-2023/page/2/index.html +++ b/cnny-2023/page/2/index.html @@ -14,14 +14,14 @@ - - + +

    · 5 min read
    Cory Skimming

    Welcome to Week 1 of #CloudNativeNewYear!

    Cloud-native New Year

    You will often hear the term "cloud-native" when discussing modern application development, but even a quick online search will return a huge number of articles, tweets, and web pages with a variety of definitions. So, what does cloud-native actually mean? Also, what makes an application a cloud-native application versus a "regular" application?

    Today, we will address these questions and more as we kickstart our learning journey (and our new year!) with an introductory dive into the wonderful world of cloud-native.


    What We'll Cover

    • What is cloud-native?
    • What is a cloud-native application?
    • The benefits of cloud-native
    • The five pillars of cloud-native
    • Exercise: Take the Cloud Skills Challenge!

    1. What is cloud-native?

    The term "cloud-native" can seem pretty self-evident (yes, hello, native to the cloud?), and in a way, it is. While there are lots of definitions of cloud-native floating around, at it's core, cloud-native simply refers to a modern approach to building software that takes advantage of cloud services and environments. This includes using cloud-native technologies, such as containers, microservices, and serverless, and following best practices for deploying, scaling, and managing applications in a cloud environment.

    Official definition from the Cloud Native Computing Foundation:

    Cloud-native technologies empower organizations to build and run scalable applications in modern, dynamic environments such as public, private, and hybrid clouds. Containers, service meshes, microservices, immutable infrastructure, and declarative APIs exemplify this approach.

    These techniques enable loosely coupled systems that are resilient, manageable, and observable. Combined with robust automation, they allow engineers to make high-impact changes frequently and predictably with minimal toil. Source


    2. So, what exactly is a cloud-native application?

    Cloud-native applications are specifically designed to take advantage of the scalability, resiliency, and distributed nature of modern cloud infrastructure. But how does this differ from a "traditional" application?

    Traditional applications are generally been built, tested, and deployed as a single, monolithic unit. The monolithic nature of this type of architecture creates close dependencies between components. This complexity and interweaving only increases as an application grows and can make it difficult to evolve (not to mention troubleshoot) and challenging to operate over time.

    To contrast, in cloud-native architectures the application components are decomposed into loosely coupled services, rather than built and deployed as one block of code. This decomposition into multiple self-contained services enables teams to manage complexity and improve the speed, agility, and scale of software delivery. Many small parts enables teams to make targeted updates, deliver new features, and fix any issues without leading to broader service disruption.


    3. The benefits of cloud-native

    Cloud-native architectures can bring many benefits to an organization, including:

    1. Scalability: easily scale up or down based on demand, allowing organizations to adjust their resource usage and costs as needed.
    2. Flexibility: deploy and run on any cloud platform, and easily move between clouds and on-premises environments.
    3. High-availability: techniques such as redundancy, self-healing, and automatic failover help ensure that cloud-native applications are designed to be highly-available and fault tolerant.
    4. Reduced costs: take advantage of the pay-as-you-go model of cloud computing, reducing the need for expensive infrastructure investments.
    5. Improved security: tap in to cloud security features, such as encryption and identity management, to improve the security of the application.
    6. Increased agility: easily add new features or services to your applications to meet changing business needs and market demand.

    4. The pillars of cloud-native

    There are five areas that are generally cited as the core building blocks of cloud-native architecture:

    1. Microservices: Breaking down monolithic applications into smaller, independent, and loosely-coupled services that can be developed, deployed, and scaled independently.
    2. Containers: Packaging software in lightweight, portable, and self-sufficient containers that can run consistently across different environments.
    3. Automation: Using automation tools and DevOps processes to manage and operate the cloud-native infrastructure and applications, including deployment, scaling, monitoring, and self-healing.
    4. Service discovery: Using service discovery mechanisms, such as APIs & service meshes, to enable services to discover and communicate with each other.
    5. Observability: Collecting and analyzing data from the infrastructure and applications to understand and optimize the performance, behavior, and health of the system.

    These can (and should!) be used in combination to deliver cloud-native solutions that are highly scalable, flexible, and available.

    WHAT'S NEXT

    Stay tuned, as we will be diving deeper into these topics in the coming weeks:

    • Jan 24: Containers 101
    • Jan 25: Adopting Microservices with Kubernetes
    • Jan 26: Kubernetes 101
    • Jan 27: Exploring your Cloud-native Options

    Resources


    Don't forget to subscribe to the blog to get daily posts delivered directly to your favorite feed reader!


    - - + + \ No newline at end of file diff --git a/cnny-2023/page/20/index.html b/cnny-2023/page/20/index.html index 750376a06d..d821a4c05a 100644 --- a/cnny-2023/page/20/index.html +++ b/cnny-2023/page/20/index.html @@ -14,13 +14,13 @@ - - + +

    · 4 min read
    Jorge Arteiro

    Welcome to Day 4 of Week 4 of #CloudNativeNewYear!

    The theme for this week is going further with Cloud Native. Yesterday we talked about Windows Containers. Today we'll explore addons and extensions available to Azure Kubernetes Services (AKS).

    What We'll Cover

    • Introduction
    • Add-ons
    • Extensions
    • Add-ons vs Extensions
    • Resources

    Introduction

    Azure Kubernetes Service (AKS) is a fully managed container orchestration service that makes it easy to deploy and manage containerized applications on Azure. AKS offers a number of features and capabilities, including the ability to extend its supported functionality through the use of add-ons and extensions.

    There are also integrations available from open-source projects and third parties, but they are not covered by the AKS support policy.

    Add-ons

    Add-ons provide a supported way to extend AKS. Installation, configuration and lifecycle are managed by AKS following pre-determine updates rules.

    As an example, let's enable Container Insights with the monitoring addon. on an existing AKS cluster using az aks enable-addons --addons CLI command

    az aks enable-addons \
    --name MyManagedCluster \
    --resource-group MyResourceGroup \
    --addons monitoring

    or you can use az aks create --enable-addons when creating new clusters

    az aks create \
    --name MyManagedCluster \
    --resource-group MyResourceGroup \
    --enable-addons monitoring

    The current available add-ons are:

    1. http_application_routing - Configure ingress with automatic public DNS name creation. Only recommended for development.
    2. monitoring - Container Insights monitoring.
    3. virtual-node - CNCF virtual nodes open source project.
    4. azure-policy - Azure Policy for AKS.
    5. ingress-appgw - Application Gateway Ingress Controller (AGIC).
    6. open-service-mesh - CNCF Open Service Mesh project.
    7. azure-keyvault-secrets-provider - Azure Key Vault Secrets Provider for Secret Store CSI Driver.
    8. web_application_routing - Managed NGINX ingress Controller.
    9. keda - CNCF Event-driven autoscaling project.

    For more details, get the updated list of AKS Add-ons here

    Extensions

    Cluster Extensions uses Helm charts and integrates with Azure Resource Manager (ARM) to provide installation and lifecycle management of capabilities on top of AKS.

    Extensions can be auto upgraded using minor versions, but it requires extra management and configuration. Using Scope parameter, it can be installed on the whole cluster or per namespace.

    AKS Extensions requires an Azure CLI extension to be installed. To add or update this CLI extension use the following commands:

    az extension add --name k8s-extension

    and to update an existing extension

    az extension update --name k8s-extension

    There are only 3 available extensions:

    1. Dapr - CNCF Dapr project.
    2. Azure ML - Integrate Azure Machine Learning with AKS to train, inference and manage ML models.
    3. Flux (GitOps) - CNCF Flux project integrated with AKS to enable cluster configuration and application deployment using GitOps.

    As an example, you can install Azure ML using the following command:

    az k8s-extension create \
    --name aml-compute --extension-type Microsoft.AzureML.Kubernetes \
    --scope cluster --cluster-name <clusterName> \
    --resource-group <resourceGroupName> \
    --cluster-type managedClusters \
    --configuration-settings enableInference=True allowInsecureConnections=True

    For more details, get the updated list of AKS Extensions here

    Add-ons vs Extensions

    AKS Add-ons brings an advantage of been fully managed by AKS itself, and AKS Extensions are more flexible and configurable but requires extra level of management.

    Add-ons are part of the AKS resource provider in the Azure API, and AKS Extensions are a separate resource provider on the Azure API.

    Resources

    It's not too late to sign up for and complete the Cloud Skills Challenge!
    - - + + \ No newline at end of file diff --git a/cnny-2023/page/21/index.html b/cnny-2023/page/21/index.html index aa6d035255..827d6619ea 100644 --- a/cnny-2023/page/21/index.html +++ b/cnny-2023/page/21/index.html @@ -14,13 +14,13 @@ - - + +

    · 6 min read
    Cory Skimming
    Steven Murawski
    Paul Yu
    Josh Duffney
    Nitya Narasimhan
    Vinicius Apolinario
    Jorge Arteiro
    Devanshi Joshi

    And that's a wrap on the inaugural #CloudNativeNewYear! Thank you for joining us to kick off the new year with this learning journey into cloud-native! In this final post of the 2023 celebration of all things cloud-native, we'll do two things:

    • Look Back - with a quick retrospective of what was covered.
    • Look Ahead - with resources and suggestions for how you can continue your skilling journey!

    We appreciate your time and attention and we hope you found this curated learning valuable. Feedback and suggestions are always welcome. From our entire team, we wish you good luck with the learning journey - now go build some apps and share your knowledge! 🎉


    What We'll Cover

    • Cloud-native fundamentals
    • Kubernetes fundamentals
    • Bringing your applications to Kubernetes
    • Go further with cloud-native
    • Resources to keep the celebration going!

    Week 1: Cloud-native Fundamentals

    In Week 1, we took a tour through the fundamentals of cloud-native technologies, including a walkthrough of the core concepts of containers, microservices, and Kubernetes.

    • Jan 23 - Cloud-native Fundamentals: The answers to life and all the universe - what is cloud-native? What makes an application cloud-native? What are the benefits? (yes, we all know it's 42, but hey, gotta start somewhere!)
    • Jan 24 - Containers 101: Containers are an essential component of cloud-native development. In this intro post, we cover how containers work and why they have become so popular.
    • Jan 25 - Kubernetes 101: Kuber-what-now? Learn the basics of Kubernetes and how it enables us to deploy and manage our applications effectively and consistently.
    A QUICKSTART GUIDE TO KUBERNETES CONCEPTS

    Missed it Live? Tune in to A Quickstart Guide to Kubernetes Concepts on demand, now!

    • Jan 26 - Microservices 101: What is a microservices architecture and how can we go about designing one?
    • Jan 27 - Exploring your Cloud Native Options: Cloud-native, while catchy, can be a very broad term. What technologies should you use? Learn some basic guidelines for when it is optimal to use different technologies for your project.

    Week 2: Kubernetes Fundamentals

    In Week 2, we took a deeper dive into the Fundamentals of Kubernetes. The posts and live demo from this week took us through how to build a simple application on Kubernetes, covering everything from deployment to networking and scaling. Note: for our samples and demo we have used Azure Kubernetes Service, but the principles apply to any Kubernetes!

    • Jan 30 - Pods and Deployments: how to use pods and deployments in Kubernetes.
    • Jan 31 - Services and Ingress: how to use services and ingress and a walk through the steps of making our containers accessible internally and externally!
    • Feb 1 - ConfigMaps and Secrets: how to of passing configuration and secrets to our applications in Kubernetes with ConfigMaps and Secrets.
    • Feb 2 - Volumes, Mounts, and Claims: how to use persistent storage on Kubernetes (and ensure your data can survive container restarts!).
    • Feb 3 - Scaling Pods and Nodes: how to scale pods and nodes in our Kubernetes cluster.
    ASK THE EXPERTS: AZURE KUBERNETES SERVICE

    Missed it Live? Tune in to Ask the Expert with Azure Kubernetes Service on demand, now!


    Week 3: Bringing your applications to Kubernetes

    So, you have learned how to build an application on Kubernetes. What about your existing applications? In Week 3, we explored how to take an existing application and set it up to run in Kubernetes:

    • Feb 6 - CI/CD: learn how to get an existing application running in Kubernetes with a full pipeline in GitHub Actions.
    • Feb 7 - Adapting Storage, Secrets, and Configuration: how to evaluate our sample application's configuration, storage, and networking requirements and implement using Kubernetes.
    • Feb 8 - Opening your Application with Ingress: how to expose the eShopOnWeb app so that customers can reach it over the internet using a custom domain name and TLS.
    • Feb 9 - Debugging and Instrumentation: how to debug and instrument your application now that it is on Kubernetes.
    • Feb 10 - CI/CD Secure Supply Chain: now that we have set up our application on Kubernetes, let's talk about container image signing and how to set up a secure supply change.

    Week 4: Go Further with Cloud-Native

    This week we have gone further with Cloud-native by exploring advanced topics and best practices for the Cloud-native practitioner.

    And today, February 17th, with this one post to rule (er, collect) them all!


    Keep the Learning Going!

    Learning is great, so why stop here? We have a host of great resources and samples for you to continue your cloud-native journey with Azure below:


    - - + + \ No newline at end of file diff --git a/cnny-2023/page/3/index.html b/cnny-2023/page/3/index.html index 3526827957..41bf0f52a1 100644 --- a/cnny-2023/page/3/index.html +++ b/cnny-2023/page/3/index.html @@ -14,14 +14,14 @@ - - + +

    · 4 min read
    Steven Murawski
    Paul Yu
    Josh Duffney

    Welcome to Day 2 of Week 1 of #CloudNativeNewYear!

    Today, we'll focus on building an understanding of containers.

    What We'll Cover

    • Introduction
    • How do Containers Work?
    • Why are Containers Becoming so Popular?
    • Conclusion
    • Resources
    • Learning Path

    REGISTER & LEARN: KUBERNETES 101

    Interested in a dive into Kubernetes and a chance to talk to experts?

    🎙: Join us Jan 26 @1pm PST by registering here

    Here's what you will learn:

    • Key concepts and core principles of Kubernetes.
    • How to deploy, scale and manage containerized workloads.
    • Live Demo of the concepts explained
    • How to get started with Azure Kubernetes Service for free.

    Start your free Azure Kubernetes Trial Today!!: aka.ms/TryAKS

    Introduction

    In the beginning, we deployed our applications onto physical servers. We only had a certain number of those servers, so often they hosted multiple applications. This led to some problems when those applications shared dependencies. Upgrading one application could break another application on the same server.

    Enter virtualization. Virtualization allowed us to run our applications in an isolated operating system instance. This removed much of the risk of updating shared dependencies. However, it increased our overhead since we had to run a full operating system for each application environment.

    To address the challenges created by virtualization, containerization was created to improve isolation without duplicating kernel level resources. Containers provide efficient and consistent deployment and runtime experiences for our applications and have become very popular as a way of packaging and distributing applications.

    How do Containers Work?

    Containers build on two capabilities in the Linux operating system, namespaces and cgroups. These constructs allow the operating system to provide isolation to a process or group of processes, keeping their access to filesystem resources separate and providing controls on resource utilization. This, combined with tooling to help package, deploy, and run container images has led to their popularity in today’s operating environment. This provides us our isolation without the overhead of additional operating system resources.

    When a container host is deployed on an operating system, it works at scheduling the access to the OS (operating systems) components. This is done by providing a logical isolated group that can contain processes for a given application, called a namespace. The container host then manages /schedules access from the namespace to the host OS. The container host then uses cgroups to allocate compute resources. Together, the container host with the help of cgroups and namespaces can schedule multiple applications to access host OS resources.

    Overall, this gives the illusion of virtualizing the host OS, where each application gets its own OS. In actuality, all the applications are running on the same operating system and sharing the same kernel as the container host.

    Containers are popular in the software development industry because they provide several benefits over traditional virtualization methods. Some of these benefits include:

    • Portability: Containers make it easy to move an application from one environment to another without having to worry about compatibility issues or missing dependencies.
    • Isolation: Containers provide a level of isolation between the application and the host system, which means that the application running in the container cannot access the host system's resources.
    • Scalability: Containers make it easy to scale an application up or down as needed, which is useful for applications that experience a lot of traffic or need to handle a lot of data.
    • Resource Efficiency: Containers are more resource-efficient than traditional virtualization methods because they don't require a full operating system to be running on each virtual machine.
    • Cost-Effective: Containers are more cost-effective than traditional virtualization methods because they don't require expensive hardware or licensing fees.

    Conclusion

    Containers are a powerful technology that allows developers to package and deploy applications in a portable and isolated environment. This technology is becoming increasingly popular in the world of software development and is being used by many companies and organizations to improve their application deployment and management processes. With the benefits of portability, isolation, scalability, resource efficiency, and cost-effectiveness, containers are definitely worth considering for your next application development project.

    Containerizing applications is a key step in modernizing them, and there are many other patterns that can be adopted to achieve cloud-native architectures, including using serverless platforms, Kubernetes, and implementing DevOps practices.

    Resources

    Learning Path

    - - + + \ No newline at end of file diff --git a/cnny-2023/page/4/index.html b/cnny-2023/page/4/index.html index 800c4bff84..24c3953fe7 100644 --- a/cnny-2023/page/4/index.html +++ b/cnny-2023/page/4/index.html @@ -14,14 +14,14 @@ - - + +

    · 3 min read
    Steven Murawski

    Welcome to Day 3 of Week 1 of #CloudNativeNewYear!

    This week we'll focus on what Kubernetes is.

    What We'll Cover

    • Introduction
    • What is Kubernetes? (Video)
    • How does Kubernetes Work? (Video)
    • Conclusion


    REGISTER & LEARN: KUBERNETES 101

    Interested in a dive into Kubernetes and a chance to talk to experts?

    🎙: Join us Jan 26 @1pm PST by registering here

    Here's what you will learn:

    • Key concepts and core principles of Kubernetes.
    • How to deploy, scale and manage containerized workloads.
    • Live Demo of the concepts explained
    • How to get started with Azure Kubernetes Service for free.

    Start your free Azure Kubernetes Trial Today!!: aka.ms/TryAKS

    Introduction

    Kubernetes is an open source container orchestration engine that can help with automated deployment, scaling, and management of our applications.

    Kubernetes takes physical (or virtual) resources and provides a consistent API over them, bringing a consistency to the management and runtime experience for our applications. Kubernetes provides us with a number of capabilities such as:

    • Container scheduling
    • Service discovery and load balancing
    • Storage orchestration
    • Automated rollouts and rollbacks
    • Automatic bin packing
    • Self-healing
    • Secret and configuration management

    We'll learn more about most of these topics as we progress through Cloud Native New Year.

    What is Kubernetes?

    Let's hear from Brendan Burns, one of the founders of Kubernetes as to what Kubernetes actually is.

    How does Kubernetes Work?

    And Brendan shares a bit more with us about how Kubernetes works.

    Conclusion

    Kubernetes allows us to deploy and manage our applications effectively and consistently.

    By providing a consistent API across many of the concerns our applications have, like load balancing, networking, storage, and compute, Kubernetes improves both our ability to build and ship new software.

    There are standards for the applications to depend on for resources needed. Deployments, metrics, and logs are provided in a standardized fashion allowing more effecient operations across our application environments.

    And since Kubernetes is an open source platform, it can be found in just about every type of operating environment - cloud, virtual machines, physical hardware, shared data centers, even small devices like Rasberry Pi's!

    Want to learn more? Join us for a webinar on Kubernetes Concepts (or catch the playback) on Thursday, January 26th at 1 PM PST and watch for the rest of this series right here!

    - - + + \ No newline at end of file diff --git a/cnny-2023/page/5/index.html b/cnny-2023/page/5/index.html index 5c75e31287..7ab1ccaf0b 100644 --- a/cnny-2023/page/5/index.html +++ b/cnny-2023/page/5/index.html @@ -14,13 +14,13 @@ - - + +

    · 6 min read
    Josh Duffney

    Welcome to Day 4 of Week 1 of #CloudNativeNewYear!

    This week we'll focus on advanced topics and best practices for Cloud-Native practitioners, kicking off with this post on Serverless Container Options with Azure. We'll look at technologies, tools and best practices that range from managed services like Azure Kubernetes Service, to options allowing finer granularity of control and oversight.

    What We'll Cover

    • What is Microservice Architecture?
    • How do you design a Microservice?
    • What challenges do Microservices introduce?
    • Conclusion
    • Resources


    Microservices are a modern way of designing and building software that increases deployment velocity by decomposing an application into small autonomous services that can be deployed independently.

    By deploying loosely coupled microservices your applications can be developed, deployed, and scaled independently. Because each service is independent, it can be updated or replaced without having to worry about the impact on the rest of the application. This means that if a bug is found in one service, it can be fixed without having to redeploy the entire application. All of which gives an organization the ability to deliver value to their customers faster.

    In this article, we will explore the basics of microservices architecture, its benefits and challenges, and how it can help improve the development, deployment, and maintenance of software applications.

    What is Microservice Architecture?

    Before explaining what Microservice architecture is, it’s important to understand what problems microservices aim to address.

    Traditional software development is centered around building monolithic applications. Monolithic applications are built as a single, large codebase. Meaning your code is tightly coupled causing the monolithic app to suffer from the following:

    Too much Complexity: Monolithic applications can become complex and difficult to understand and maintain as they grow. This can make it hard to identify and fix bugs and add new features.

    Difficult to Scale: Monolithic applications can be difficult to scale as they often have a single point of failure, which can cause the whole application to crash if a service fails.

    Slow Deployment: Deploying a monolithic application can be risky and time-consuming, as a small change in one part of the codebase can affect the entire application.

    Microservice architecture (often called microservices) is an architecture style that addresses the challenges created by Monolithic applications. Microservices architecture is a way of designing and building software applications as a collection of small, independent services that communicate with each other through APIs. This allows for faster development and deployment cycles, as well as easier scaling and maintenance than is possible with a monolithic application.

    How do you design a Microservice?

    Building applications with Microservices architecture requires a different approach. Microservices architecture focuses on business capabilities rather than technical layers, such as data access or messaging. Doing so requires that you shift your focus away from the technical stack and model your applications based upon the various domains that exist within the business.

    Domain-driven design (DDD) is a way to design software by focusing on the business needs. You can use Domain-driven design as a framework that guides the development of well-designed microservices by building services that encapsulate knowledge in each domain and abstract that knowledge from clients.

    In Domain-driven design you start by modeling the business domain and creating a domain model. A domain model is an abstract model of the business model that distills and organizes a domain of knowledge and provides a common language for developers and domain experts. It’s the resulting domain model that microservices a best suited to be built around because it helps establish a well-defined boundary between external systems and other internal applications.

    In short, before you begin designing microservices, start by mapping the functions of the business and their connections to create a domain model for the microservice(s) to be built around.

    What challenges do Microservices introduce?

    Microservices solve a lot of problems and have several advantages, but the grass isn’t always greener on the other side.

    One of the key challenges of microservices is managing communication between services. Because services are independent, they need to communicate with each other through APIs. This can be complex and difficult to manage, especially as the number of services grows. To address this challenge, it is important to have a clear API design, with well-defined inputs and outputs for each service. It is also important to have a system for managing and monitoring communication between services, to ensure that everything is running smoothly.

    Another challenge of microservices is managing the deployment and scaling of services. Because each service is independent, it needs to be deployed and scaled separately from the rest of the application. This can be complex and difficult to manage, especially as the number of services grows. To address this challenge, it is important to have a clear and consistent deployment process, with well-defined steps for deploying and scaling each service. Furthermore, it is advisable to host them on a system with self-healing capabilities to reduce operational burden.

    It is also important to have a system for monitoring and managing the deployment and scaling of services, to ensure optimal performance.

    Each of these challenges has created fertile ground for tooling and process that exists in the cloud-native ecosystem. Kubernetes, CI CD, and other DevOps practices are part of the package of adopting the microservices architecture.

    Conclusion

    In summary, microservices architecture focuses on software applications as a collection of small, independent services that communicate with each other over well-defined APIs.

    The main advantages of microservices include:

    • increased flexibility and scalability per microservice,
    • efficient resource utilization (with help from a container orchestrator like Kubernetes),
    • and faster development cycles.

    Continue following along with this series to see how you can use Kubernetes to help adopt microservices patterns in your own environments!

    Resources

    - - + + \ No newline at end of file diff --git a/cnny-2023/page/6/index.html b/cnny-2023/page/6/index.html index 58c28d9b12..af043a1410 100644 --- a/cnny-2023/page/6/index.html +++ b/cnny-2023/page/6/index.html @@ -14,14 +14,14 @@ - - + +

    · 6 min read
    Cory Skimming

    We are excited to be wrapping up our first week of #CloudNativeNewYear! This week, we have tried to set the stage by covering the fundamentals of cloud-native practices and technologies, including primers on containerization, microservices, and Kubernetes.

    Don't forget to sign up for the the Cloud Skills Challenge!

    Today, we will do a brief recap of some of these technologies and provide some basic guidelines for when it is optimal to use each.


    What We'll Cover

    • To Containerize or not to Containerize?
    • The power of Kubernetes
    • Where does Serverless fit?
    • Resources
    • What's coming next!


    Just joining us now? Check out these other Week 1 posts:

    To Containerize or not to Containerize?

    As mentioned in our Containers 101 post earlier this week, containers can provide several benefits over traditional virtualization methods, which has made them popular within the software development community. Containers provide a consistent and predictable runtime environment, which can help reduce the risk of compatibility issues and simplify the deployment process. Additionally, containers can improve resource efficiency by allowing multiple applications to run on the same host while isolating their dependencies.

    Some types of apps that are a particularly good fit for containerization include:

    1. Microservices: Containers are particularly well-suited for microservices-based applications, as they can be used to isolate and deploy individual components of the system. This allows for more flexibility and scalability in the deployment process.
    2. Stateless applications: Applications that do not maintain state across multiple sessions, such as web applications, are well-suited for containers. Containers can be easily scaled up or down as needed and replaced with new instances, without losing data.
    3. Portable applications: Applications that need to be deployed in different environments, such as on-premises, in the cloud, or on edge devices, can benefit from containerization. The consistent and portable runtime environment of containers can make it easier to move the application between different environments.
    4. Legacy applications: Applications that are built using older technologies or that have compatibility issues can be containerized to run in an isolated environment, without impacting other applications or the host system.
    5. Dev and testing environments: Containerization can be used to create isolated development and testing environments, which can be easily created and destroyed as needed.

    While there are many types of applications that can benefit from a containerized approach, it's worth noting that containerization is not always the best option, and it's important to weigh the benefits and trade-offs before deciding to containerize an application. Additionally, some types of applications may not be a good fit for containers including:

    • Apps that require full access to host resources: Containers are isolated from the host system, so if an application needs direct access to hardware resources such as GPUs or specialized devices, it might not work well in a containerized environment.
    • Apps that require low-level system access: If an application requires deep access to the underlying operating system, it may not be suitable for running in a container.
    • Applications that have specific OS dependencies: Apps that have specific dependencies on a certain version of an operating system or libraries may not be able to run in a container.
    • Stateful applications: Apps that maintain state across multiple sessions, such as databases, may not be well suited for containers. Containers are ephemeral by design, so the data stored inside a container may not persist between restarts.

    The good news is that some of these limitations can be overcome with the use of specialized containerization technologies such as Kubernetes, and by carefully designing the architecture of the application.


    The power of Kubernetes

    Speaking of Kubernetes...

    Kubernetes is a powerful tool for managing and deploying containerized applications in production environments, particularly for applications that need to scale, handle large numbers of requests, or run in multi-cloud or hybrid environments.

    Kubernetes is well-suited for a wide variety of applications, but it is particularly well-suited for the following types of applications:

    1. Microservices-based applications: Kubernetes provides a powerful set of tools for managing and deploying microservices-based applications, making it easy to scale, update, and manage the individual components of the application.
    2. Stateful applications: Kubernetes provides support for stateful applications through the use of Persistent Volumes and StatefulSets, allowing for applications that need to maintain state across multiple instances.
    3. Large-scale, highly-available systems: Kubernetes provides built-in support for scaling, self-healing, and rolling updates, making it an ideal choice for large-scale, highly-available systems that need to handle large numbers of users and requests.
    4. Multi-cloud and hybrid environments: Kubernetes can be used to deploy and manage applications across multiple cloud providers and on-premises environments, making it a good choice for organizations that want to take advantage of the benefits of multiple cloud providers or that need to deploy applications in a hybrid environment.
    New to Kubernetes?

    Where does Serverless fit in?

    Serverless is a cloud computing model where the cloud provider (like Azure) is responsible for executing a piece of code by dynamically allocating the resources. With serverless, you only pay for the exact amount of compute time that you use, rather than paying for a fixed amount of resources. This can lead to significant cost savings, particularly for applications with variable or unpredictable workloads.

    Serverless is commonly used for building applications like web or mobile apps, IoT, data processing, and real-time streaming - apps where the workloads are variable and high scalability is required. It's important to note that serverless is not a replacement for all types of workloads - it's best suited for stateless, short-lived and small-scale workloads.

    For a detailed look into the world of Serverless and lots of great learning content, revisit #30DaysofServerless.


    Resources


    What's up next in #CloudNativeNewYear?

    Week 1 has been all about the fundamentals of cloud-native. Next week, the team will be diving in to application deployment with Azure Kubernetes Service. Don't forget to subscribe to the blog to get daily posts delivered directly to your favorite feed reader!


    - - + + \ No newline at end of file diff --git a/cnny-2023/page/7/index.html b/cnny-2023/page/7/index.html index 09c218a963..dfc44ffdec 100644 --- a/cnny-2023/page/7/index.html +++ b/cnny-2023/page/7/index.html @@ -14,13 +14,13 @@ - - + +

    · 14 min read
    Steven Murawski

    Welcome to Day #1 of Week 2 of #CloudNativeNewYear!

    The theme for this week is Kubernetes fundamentals. Last week we talked about Cloud Native architectures and the Cloud Native landscape. Today we'll explore the topic of Pods and Deployments in Kubernetes.

    Ask the Experts Thursday, February 9th at 9 AM PST
    Catch the Replay of the Live Demo

    Watch the recorded demo and conversation about this week's topics.

    We were live on YouTube walking through today's (and the rest of this week's) demos.

    What We'll Cover

    • Setting Up A Kubernetes Environment in Azure
    • Running Containers in Kubernetes Pods
    • Making the Pods Resilient with Deployments
    • Exercise
    • Resources

    Setting Up A Kubernetes Environment in Azure

    For this week, we'll be working with a simple app - the Azure Voting App. My teammate Paul Yu ported the app to Rust and we tweaked it a bit to let us highlight some of the basic features of Kubernetes.

    You should be able to replicate this in just about any Kubernetes environment, but we'll use Azure Kubernetes Service (AKS) as our working environment for this week.

    To make it easier to get started, there's a Bicep template to deploy an AKS cluster, an Azure Container Registry (ACR) (to host our container image), and connect the two so that we can easily deploy our application.

    Step 0 - Prerequisites

    There are a few things you'll need if you want to work through this and the following examples this week.

    Required:

    • Git (and probably a GitHub account if you want to persist your work outside of your computer)
    • Azure CLI
    • An Azure subscription (if you want to follow along with the Azure steps)
    • Kubectl (the command line tool for managing Kubernetes)

    Helpful:

    • Visual Studio Code (or equivalent editor)

    Step 1 - Clone the application repository

    First, I forked the source repository to my account.

    $GitHubOrg = 'smurawski' # Replace this with your GitHub account name or org name
    git clone "https://github.com/$GitHubOrg/azure-voting-app-rust"
    cd azure-voting-app-rust

    Leave your shell opened with your current location inside the application repository.

    Step 2 - Set up AKS

    Running the template deployment from the demo script (I'm using the PowerShell example in cnny23-week2-day1.ps1, but there's a Bash variant at cnny23-week2-day1.sh) stands up the environment. The second, third, and fourth commands take some of the output from the Bicep deployment to set up for later commands, so don't close out your shell after you run these commands.

    az deployment sub create --template-file ./deploy/main.bicep --location eastus --parameters 'resourceGroup=cnny-week2'
    $AcrName = az deployment sub show --name main --query 'properties.outputs.acr_name.value' -o tsv
    $AksName = az deployment sub show --name main --query 'properties.outputs.aks_name.value' -o tsv
    $ResourceGroup = az deployment sub show --name main --query 'properties.outputs.resource_group_name.value' -o tsv

    az aks get-credentials --resource-group $ResourceGroup --name $AksName

    Step 3 - Build our application container

    Since we have an Azure Container Registry set up, I'll use ACR Build Tasks to build and store my container image.

    az acr build --registry $AcrName --% --image cnny2023/azure-voting-app-rust:{{.Run.ID}} .
    $BuildTag = az acr repository show-tags `
    --name $AcrName `
    --repository cnny2023/azure-voting-app-rust `
    --orderby time_desc `
    --query '[0]' -o tsv
    tip

    Wondering what the --% is in the first command line? That tells the PowerShell interpreter to pass the input after it "as is" to the command without parsing/evaluating it. Otherwise, PowerShell messes a bit with the templated {{.Run.ID}} bit.

    Running Containers in Kubernetes Pods

    Now that we have our AKS cluster and application image ready to go, let's look into how Kubernetes runs containers.

    If you've been in tech for any length of time, you've seen that every framework, runtime, orchestrator, etc.. can have their own naming scheme for their concepts. So let's get into some of what Kubernetes calls things.

    The Pod

    A container running in Kubernetes is called a Pod. A Pod is basically a running container on a Node or VM. It can be more. For example you can run multiple containers and specify some funky configuration, but we'll keep it simple for now - add the complexity when you need it.

    Our Pod definition can be created via the kubectl command imperatively from arguments or declaratively from a configuration file. We'll do a little of both. We'll use the kubectl command to help us write our configuration files. Kubernetes configuration files are YAML, so having an editor that supports and can help you syntax check YAML is really helpful.

    Creating a Pod Definition

    Let's create a few Pod definitions. Our application requires two containers to get working - the application and a database.

    Let's create the database Pod first. And before you comment, the configuration isn't secure nor best practice. We'll fix that later this week. For now, let's focus on getting up and running.

    This is a trick I learned from one of my teammates - Paul. By using the --output yaml and --dry-run=client options, we can have the command help us write our YAML. And with a bit of output redirection, we can stash it safely in a file for later use.

    kubectl run azure-voting-db `
    --image "postgres:15.0-alpine" `
    --env "POSTGRES_PASSWORD=mypassword" `
    --output yaml `
    --dry-run=client > manifests/pod-db.yaml

    This creates a file that looks like:

    apiVersion: v1
    kind: Pod
    metadata:
    creationTimestamp: null
    labels:
    run: azure-voting-db
    name: azure-voting-db
    spec:
    containers:
    - env:
    - name: POSTGRES_PASSWORD
    value: mypassword
    image: postgres:15.0-alpine
    name: azure-voting-db
    resources: {}
    dnsPolicy: ClusterFirst
    restartPolicy: Always
    status: {}

    The file, when supplied to the Kubernetes API, will identify what kind of resource to create, the API version to use, and the details of the container (as well as an environment variable to be supplied).

    We'll get that container image started with the kubectl command. Because the details of what to create are in the file, we don't need to specify much else to the kubectl command but the path to the file.

    kubectl apply -f ./manifests/pod-db.yaml

    I'm going to need the IP address of the Pod, so that my application can connect to it, so we can use kubectl to get some information about our pod. By default, kubectl get pod only displays certain information but it retrieves a lot more. We can use the JSONPath syntax to index into the response and get the information you want.

    tip

    To see what you can get, I usually run the kubectl command with the output type (-o JSON) of JSON and then I can find where the data I want is and create my JSONPath query to get it.

    $DB_IP = kubectl get pod azure-voting-db -o jsonpath='{.status.podIP}'

    Now, let's create our Pod definition for our application. We'll use the same technique as before.

    kubectl run azure-voting-app `
    --image "$AcrName.azurecr.io/cnny2023/azure-voting-app-rust:$BuildTag" `
    --env "DATABASE_SERVER=$DB_IP" `
    --env "DATABASE_PASSWORD=mypassword`
    --output yaml `
    --dry-run=client > manifests/pod-app.yaml

    That command gets us a similar YAML file to the database container - you can see the full file here

    Let's get our application container running.

    kubectl apply -f ./manifests/pod-app.yaml

    Now that the Application is Running

    We can check the status of our Pods with:

    kubectl get pods

    And we should see something like:

    azure-voting-app-rust ❯  kubectl get pods
    NAME READY STATUS RESTARTS AGE
    azure-voting-app 1/1 Running 0 36s
    azure-voting-db 1/1 Running 0 84s

    Once our pod is running, we can check to make sure everything is working by letting kubectl proxy network connections to our Pod running the application. If we get the voting web page, we'll know the application found the database and we can start voting!

    kubectl port-forward pod/azure-voting-app 8080:8080

    Azure voting website in a browser with three buttons, one for Dogs, one for Cats, and one for Reset.  The counter is Dogs - 0 and Cats - 0.

    When you are done voting, you can stop the port forwarding by using Control-C to break the command.

    Clean Up

    Let's clean up after ourselves and see if we can't get Kubernetes to help us keep our application running. We can use the same configuration files to ensure that Kubernetes only removes what we want removed.

    kubectl delete -f ./manifests/pod-app.yaml
    kubectl delete -f ./manifests/pod-db.yaml

    Summary - Pods

    A Pod is the most basic unit of work inside Kubernetes. Once the Pod is deleted, it's gone. That leads us to our next topic (and final topic for today.)

    Making the Pods Resilient with Deployments

    We've seen how easy it is to deploy a Pod and get our containers running on Nodes in our Kubernetes cluster. But there's a problem with that. Let's illustrate it.

    Breaking Stuff

    Setting Back Up

    First, let's redeploy our application environment. We'll start with our application container.

    kubectl apply -f ./manifests/pod-db.yaml
    kubectl get pod azure-voting-db -o jsonpath='{.status.podIP}'

    The second command will report out the new IP Address for our database container. Let's open ./manifests/pod-app.yaml and update the container IP to our new one.

    - name: DATABASE_SERVER
    value: YOUR_NEW_IP_HERE

    Then we can deploy the application with the information it needs to find its database. We'll also list out our pods to see what is running.

    kubectl apply -f ./manifests/pod-app.yaml
    kubectl get pods

    Feel free to look back and use the port forwarding trick to make sure your app is running if you'd like.

    Knocking It Down

    The first thing we'll try to break is our application pod. Let's delete it.

    kubectl delete pod azure-voting-app

    Then, we'll check our pod's status:

    kubectl get pods

    Which should show something like:

    azure-voting-app-rust ❯  kubectl get pods
    NAME READY STATUS RESTARTS AGE
    azure-voting-db 1/1 Running 0 50s

    We should be able to recreate our application pod deployment with no problem, since it has the current database IP address and nothing else depends on it.

    kubectl apply -f ./manifests/pod-app.yaml

    Again, feel free to do some fun port forwarding and check your site is running.

    Uncomfortable Truths

    Here's where it gets a bit stickier, what if we delete the database container?

    If we delete our database container and recreate it, it'll likely have a new IP address, which would force us to update our application configuration. We'll look at some solutions for these problems in the next three posts this week.

    Because our database problem is a bit tricky, we'll primarily focus on making our application layer more resilient and prepare our database layer for those other techniques over the next few days.

    Let's clean back up and look into making things more resilient.

    kubectl delete -f ./manifests/pod-app.yaml
    kubectl delete -f ./manifests/pod-db.yaml

    The Deployment

    One of the reasons you may want to use Kubernetes is it's ability to orchestrate workloads. Part of that orchestration includes being able to ensure that certain workloads are running (regardless of what Node they might be on).

    We saw that we could delete our application pod and then restart it from the manifest with little problem. It just meant that we had to run a command to restart it. We can use the Deployment in Kubernetes to tell the orchestrator to ensure we have our application pod running.

    The Deployment also can encompass a lot of extra configuration - controlling how many containers of a particular type should be running, how upgrades of container images should proceed, and more.

    Creating the Deployment

    First, we'll create a Deployment for our database. We'll use a technique similar to what we did for the Pod, with just a bit of difference.

    kubectl create deployment azure-voting-db `
    --image "postgres:15.0-alpine" `
    --port 5432 `
    --output yaml `
    --dry-run=client > manifests/deployment-db.yaml

    Unlike our Pod definition creation, we can't pass in environment variable configuration from the command line. We'll have to edit the YAML file to add that.

    So, let's open ./manifests/deployment-db.yaml in our editor and add the following in the spec/containers configuration.

            env:
    - name: POSTGRES_PASSWORD
    value: "mypassword"

    Your file should look like this deployment-db.yaml.

    Once we have our configuration file updated, we can deploy our database container image.

    kubectl apply -f ./manifests/deployment-db.yaml

    For our application, we'll use the same technique.

    kubectl create deployment azure-voting-app `
    --image "$AcrName.azurecr.io/cnny2023/azure-voting-app-rust:$BuildTag" `
    --port 8080 `
    --output yaml `
    --dry-run=client > manifests/deployment-app.yaml

    Next, we'll need to add an environment variable to the generated configuration. We'll also need the new IP address for the database deployment.

    Previously, we named the pod and were able to ask for the IP address with kubectl and a bit of JSONPath. Now, the deployment created the pod for us, so there's a bit of random in the naming. Check out:

    kubectl get pods

    Should return something like:

    azure-voting-app-rust ❯  kubectl get pods
    NAME READY STATUS RESTARTS AGE
    azure-voting-db-686d758fbf-8jnq8 1/1 Running 0 7s

    We can either ask for the IP with the new pod name, or we can use a selector to find our desired pod.

    kubectl get pod --selector app=azure-voting-db -o jsonpath='{.items[0].status.podIP}'

    Now, we can update our application deployment configuration file with:

            env:
    - name: DATABASE_SERVER
    value: YOUR_NEW_IP_HERE
    - name: DATABASE_PASSWORD
    value: mypassword

    Your file should look like this deployment-app.yaml (but with IPs and image names matching your environment).

    After we save those changes, we can deploy our application.

    kubectl apply -f ./manifests/deployment-app.yaml

    Let's test the resilience of our app now. First, we'll delete the pod running our application, then we'll check to make sure Kubernetes restarted our application pod.

    kubectl get pods
    azure-voting-app-rust ❯  kubectl get pods
    NAME READY STATUS RESTARTS AGE
    azure-voting-app-56c9ccc89d-skv7x 1/1 Running 0 71s
    azure-voting-db-686d758fbf-8jnq8 1/1 Running 0 12m
    kubectl delete pod azure-voting-app-56c9ccc89d-skv7x
    kubectl get pods
    azure-voting-app-rust ❯  kubectl delete pod azure-voting-app-56c9ccc89d-skv7x
    >> kubectl get pods
    pod "azure-voting-app-56c9ccc89d-skv7x" deleted
    NAME READY STATUS RESTARTS AGE
    azure-voting-app-56c9ccc89d-2b5mx 1/1 Running 0 2s
    azure-voting-db-686d758fbf-8jnq8 1/1 Running 0 14m
    info

    Your Pods will likely have different identifiers at the end, so adjust your commands to match the names in your environment.

    As you can see, by the time the kubectl get pods command was run, Kubernetes had already spun up a new pod for the application container image. Thanks Kubernetes!

    Clean up

    Since we can't just delete the pods, we have to delete the deployments.

    kubectl delete -f ./manifests/deployment-app.yaml
    kubectl delete -f ./manifests/deployment-db.yaml

    Summary - Deployments

    Deployments allow us to create more durable configuration for the workloads we deploy into Kubernetes. As we dig deeper, we'll discover more capabilities the deployments offer. Check out the Resources below for more.

    Exercise

    If you want to try these steps, head over to the source repository, fork it, clone it locally, and give it a spin!

    You can check your manifests against the manifests in the week2/day1 branch of the source repository.

    Resources

    Take the Cloud Skills Challenge!

    Enroll in the Cloud Skills Challenge!

    Don't miss out on this opportunity to level up your skills and stay ahead of the curve in the world of cloud native.

    Documentation

    Training

    - - + + \ No newline at end of file diff --git a/cnny-2023/page/8/index.html b/cnny-2023/page/8/index.html index 0ea392c549..f8f98ec2f7 100644 --- a/cnny-2023/page/8/index.html +++ b/cnny-2023/page/8/index.html @@ -14,13 +14,13 @@ - - + +

    · 11 min read
    Paul Yu

    Welcome to Day 2 of Week 2 of #CloudNativeNewYear!

    The theme for this week is #Kubernetes fundamentals. Yesterday we talked about how to deploy a containerized web app workload to Azure Kubernetes Service (AKS). Today we'll explore the topic of services and ingress and walk through the steps of making our containers accessible both internally as well as over the internet so that you can share it with the world 😊

    Ask the Experts Thursday, February 9th at 9 AM PST
    Catch the Replay of the Live Demo

    Watch the recorded demo and conversation about this week's topics.

    We were live on YouTube walking through today's (and the rest of this week's) demos.

    What We'll Cover

    • Exposing Pods via Service
    • Exposing Services via Ingress
    • Takeaways
    • Resources

    Exposing Pods via Service

    There are a few ways to expose your pod in Kubernetes. One way is to take an imperative approach and use the kubectl expose command. This is probably the quickest way to achieve your goal but it isn't the best way. A better way to expose your pod by taking a declarative approach by creating a services manifest file and deploying it using the kubectl apply command.

    Don't worry if you are unsure of how to make this manifest, we'll use kubectl to help generate it.

    First, let's ensure we have the database deployed on our AKS cluster.

    📝 NOTE: If you don't have an AKS cluster deployed, please head over to Azure-Samples/azure-voting-app-rust, clone the repo, and follow the instructions in the README.md to execute the Azure deployment and setup your kubectl context. Check out the first post this week for more on the environment setup.

    kubectl apply -f ./manifests/deployment-db.yaml

    Next, let's deploy the application. If you are following along from yesterday's content, there isn't anything you need to change; however, if you are deploy the app from scratch, you'll need to modify the deployment-app.yaml manifest and update it with your image tag and database pod's IP address.

    kubectl apply -f ./manifests/deployment-app.yaml

    Now, let's expose the database using a service so that we can leverage Kubernetes' built-in service discovery to be able to reference it by name; not pod IP. Run the following command.

    kubectl expose deployment azure-voting-db \
    --port=5432 \
    --target-port=5432

    With the database exposed using service, we can update the app deployment manifest to use the service name instead of pod IP. This way, if the pod ever gets assigned a new IP, we don't have to worry about updating the IP each time and redeploying our web application. Kubernetes has internal service discovery mechanism in place that allows us to reference a service by its name.

    Let's make an update to the manifest. Replace the environment variable for DATABASE_SERVER with the following:

    - name: DATABASE_SERVER
    value: azure-voting-db

    Re-deploy the app with the updated configuration.

    kubectl apply -f ./manifests/deployment-app.yaml

    One service down, one to go. Run the following command to expose the web application.

    kubectl expose deployment azure-voting-app \
    --type=LoadBalancer \
    --port=80 \
    --target-port=8080

    Notice the --type argument has a value of LoadBalancer. This service type is implemented by the cloud-controller-manager which is part of the Kubernetes control plane. When using a managed Kubernetes cluster such as Azure Kubernetes Service, a public standard load balancer will be able to provisioned when the service type is set to LoadBalancer. The load balancer will also have a public IP assigned which will make your deployment publicly available.

    Kubernetes supports four service types:

    • ClusterIP: this is the default and limits service access to internal traffic within the cluster
    • NodePort: this assigns a port mapping on the node's IP address and allows traffic from the virtual network (outside the cluster)
    • LoadBalancer: as mentioned above, this creates a cloud-based load balancer
    • ExternalName: this is used in special case scenarios where you want to map a service to an external DNS name

    📝 NOTE: When exposing a web application to the internet, allowing external users to connect to your Service directly is not the best approach. Instead, you should use an Ingress, which we'll cover in the next section.

    Now, let's confirm you can reach the web app from the internet. You can use the following command to print the URL to your terminal.

    echo "http://$(kubectl get service azure-voting-app -o jsonpath='{.status.loadBalancer.ingress[0].ip}')"

    Great! The kubectl expose command gets the job done, but as mentioned above, it is not the best method of exposing deployments. It is better to expose deployments declaratively using a service manifest, so let's delete the services and redeploy using manifests.

    kubectl delete service azure-voting-db azure-voting-app

    To use kubectl to generate our manifest file, we can use the same kubectl expose command that we ran earlier but this time, we'll include --output=yaml and --dry-run=client. This will instruct the command to output the manifest that would be sent to the kube-api server in YAML format to the terminal.

    Generate the manifest for the database service.

    kubectl expose deployment azure-voting-db \
    --type=ClusterIP \
    --port=5432 \
    --target-port=5432 \
    --output=yaml \
    --dry-run=client > ./manifests/service-db.yaml

    Generate the manifest for the application service.

    kubectl expose deployment azure-voting-app \
    --type=LoadBalancer \
    --port=80 \
    --target-port=8080 \
    --output=yaml \
    --dry-run=client > ./manifests/service-app.yaml

    The command above redirected the YAML output to your manifests directory. Here is what the web application service looks like.

    apiVersion: v1
    kind: Service
    metadata:
    creationTimestamp: null
    labels:
    app: azure-voting-app
    name: azure-voting-app
    spec:
    ports:
    - port: 80
    protocol: TCP
    targetPort: 8080
    selector:
    app: azure-voting-app
    type: LoadBalancer
    status:
    loadBalancer: {}

    💡 TIP: To view the schema of any api-resource in Kubernetes, you can use the kubectl explain command. In this case the kubectl explain service command will tell us exactly what each of these fields do.

    Re-deploy the services using the new service manifests.

    kubectl apply -f ./manifests/service-db.yaml -f ./manifests/service-app.yaml

    # You should see TYPE is set to LoadBalancer and the EXTERNAL-IP is set
    kubectl get service azure-voting-db azure-voting-app

    Confirm again that our application is accessible again. Run the following command to print the URL to the terminal.

    echo "http://$(kubectl get service azure-voting-app -o jsonpath='{.status.loadBalancer.ingress[0].ip}')"

    That was easy, right? We just exposed both of our pods using Kubernetes services. The database only needs to be accessible from within the cluster so ClusterIP is perfect for that. For the web application, we specified the type to be LoadBalancer so that we can access the application over the public internet.

    But wait... remember that if you want to expose web applications over the public internet, a Service with a public IP is not the best way; the better approach is to use an Ingress resource.

    Exposing Services via Ingress

    If you read through the Kubernetes documentation on Ingress you will see a diagram that depicts the Ingress sitting in front of the Service resource with a routing rule between it. In order to use Ingress, you need to deploy an Ingress Controller and it can be configured with many routing rules to forward traffic to one or many backend services. So effectively, an Ingress is a load balancer for your Services.

    With that said, we no longer need a service type of LoadBalancer since the service does not need to be accessible from the internet. It only needs to be accessible from the Ingress Controller (internal to the cluster) so we can change the service type to ClusterIP.

    Update your service.yaml file to look like this:

    apiVersion: v1
    kind: Service
    metadata:
    creationTimestamp: null
    labels:
    app: azure-voting-app
    name: azure-voting-app
    spec:
    ports:
    - port: 80
    protocol: TCP
    targetPort: 8080
    selector:
    app: azure-voting-app

    📝 NOTE: The default service type is ClusterIP so we can omit the type altogether.

    Re-apply the app service manifest.

    kubectl apply -f ./manifests/service-app.yaml

    # You should see TYPE set to ClusterIP and EXTERNAL-IP set to <none>
    kubectl get service azure-voting-app

    Next, we need to install an Ingress Controller. There are quite a few options, and the Kubernetes-maintained NGINX Ingress Controller is commonly deployed.

    You could install this manually by following these instructions, but if you do that you'll be responsible for maintaining and supporting the resource.

    I like to take advantage of free maintenance and support when I can get it, so I'll opt to use the Web Application Routing add-on for AKS.

    💡 TIP: Whenever you install an AKS add-on, it will be maintained and fully supported by Azure Support.

    Enable the web application routing add-on in our AKS cluster with the following command.

    az aks addon enable \
    --name <YOUR_AKS_NAME> \
    --resource-group <YOUR_AKS_RESOURCE_GROUP>
    --addon web_application_routing

    ⚠️ WARNING: This command can take a few minutes to complete

    Now, let's use the same approach we took in creating our service to create our Ingress resource. Run the following command to generate the Ingress manifest.

    kubectl create ingress azure-voting-app \
    --class=webapprouting.kubernetes.azure.com \
    --rule="/*=azure-voting-app:80" \
    --output yaml \
    --dry-run=client > ./manifests/ingress.yaml

    The --class=webapprouting.kubernetes.azure.com option activates the AKS web application routing add-on. This AKS add-on can also integrate with other Azure services such as Azure DNS and Azure Key Vault for TLS certificate management and this special class makes it all work.

    The --rule="/*=azure-voting-app:80" option looks confusing but we can use kubectl again to help us understand how to format the value for the option.

    kubectl create ingress --help

    In the output you will see the following:

    --rule=[]:
    Rule in format host/path=service:port[,tls=secretname]. Paths containing the leading character '*' are
    considered pathType=Prefix. tls argument is optional.

    It expects a host and path separated by a forward-slash, then expects the backend service name and port separated by a colon. We're not using a hostname for this demo so we can omit it. For the path, an asterisk is used to specify a wildcard path prefix.

    So, the value of /*=azure-voting-app:80 creates a routing rule for all paths following the domain (or in our case since we don't have a hostname specified, the IP) to route traffic to our azure-voting-app backend service on port 80.

    📝 NOTE: Configuring the hostname and TLS is outside the scope of this demo but please visit this URL https://bit.ly/aks-webapp-routing for an in-depth hands-on lab centered around Web Application Routing on AKS.

    Your ingress.yaml file should look like this:

    apiVersion: networking.k8s.io/v1
    kind: Ingress
    metadata:
    creationTimestamp: null
    name: azure-voting-app
    spec:
    ingressClassName: webapprouting.kubernetes.azure.com
    rules:
    - http:
    paths:
    - backend:
    service:
    name: azure-voting-app
    port:
    number: 80
    path: /
    pathType: Prefix
    status:
    loadBalancer: {}

    Apply the app ingress manifest.

    kubectl apply -f ./manifests/ingress.yaml

    Validate the web application is available from the internet again. You can run the following command to print the URL to the terminal.

    echo "http://$(kubectl get ingress azure-voting-app -o jsonpath='{.status.loadBalancer.ingress[0].ip}')"

    Takeaways

    Exposing your applications both internally and externally can be easily achieved using Service and Ingress resources respectively. If your service is HTTP or HTTPS based and needs to be accessible from outsie the cluster, use Ingress with an internal Service (i.e., ClusterIP or NodePort); otherwise, use the Service resource. If your TCP-based Service needs to be publicly accessible, you set the type to LoadBalancer to expose a public IP for it. To learn more about these resources, please visit the links listed below.

    Lastly, if you are unsure how to begin writing your service manifest, you can use kubectl and have it do most of the work for you 🥳

    Resources

    Take the Cloud Skills Challenge!

    Enroll in the Cloud Skills Challenge!

    Don't miss out on this opportunity to level up your skills and stay ahead of the curve in the world of cloud native.

    - - + + \ No newline at end of file diff --git a/cnny-2023/page/9/index.html b/cnny-2023/page/9/index.html index 95a6b061f2..e97ba91404 100644 --- a/cnny-2023/page/9/index.html +++ b/cnny-2023/page/9/index.html @@ -14,14 +14,14 @@ - - + +

    · 6 min read
    Josh Duffney

    Welcome to Day 3 of Week 2 of #CloudNativeNewYear!

    The theme for this week is Kubernetes fundamentals. Yesterday we talked about Services and Ingress. Today we'll explore the topic of passing configuration and secrets to our applications in Kubernetes with ConfigMaps and Secrets.

    Ask the Experts Thursday, February 9th at 9 AM PST
    Catch the Replay of the Live Demo

    Watch the recorded demo and conversation about this week's topics.

    We were live on YouTube walking through today's (and the rest of this week's) demos.

    What We'll Cover

    • Decouple configurations with ConfigMaps and Secerts
    • Passing Environment Data with ConfigMaps and Secrets
    • Conclusion

    Decouple configurations with ConfigMaps and Secerts

    A ConfigMap is a Kubernetes object that decouples configuration data from pod definitions. Kubernetes secerts are similar, but were designed to decouple senstive information.

    Separating the configuration and secerts from your application promotes better organization and security of your Kubernetes environment. It also enables you to share the same configuration and different secerts across multiple pods and deployments which can simplify scaling and management. Using ConfigMaps and Secerts in Kubernetes is a best practice that can help to improve the scalability, security, and maintainability of your cluster.

    By the end of this tutorial, you'll have added a Kubernetes ConfigMap and Secret to the Azure Voting deployment.

    Passing Environment Data with ConfigMaps and Secrets

    📝 NOTE: If you don't have an AKS cluster deployed, please head over to Azure-Samples/azure-voting-app-rust, clone the repo, and follow the instructions in the README.md to execute the Azure deployment and setup your kubectl context. Check out the first post this week for more on the environment setup.

    Create the ConfigMap

    ConfigMaps can be used in one of two ways; as environment variables or volumes.

    For this tutorial you'll use a ConfigMap to create three environment variables inside the pod; DATABASE_SERVER, FISRT_VALUE, and SECOND_VALUE. The DATABASE_SERVER provides part of connection string to a Postgres. FIRST_VALUE and SECOND_VALUE are configuration options that change what voting options the application presents to the users.

    Follow the below steps to create a new ConfigMap:

    1. Create a YAML file named 'config-map.yaml'. In this file, specify the environment variables for the application.

      apiVersion: v1
      kind: ConfigMap
      metadata:
      name: azure-voting-config
      data:
      DATABASE_SERVER: azure-voting-db
      FIRST_VALUE: "Go"
      SECOND_VALUE: "Rust"
    2. Create the config map in your Kubernetes cluster by running the following command:

      kubectl create -f config-map.yaml

    Create the Secret

    The deployment-db.yaml and deployment-app.yaml are Kubernetes manifests that deploy the Azure Voting App. Currently, those deployment manifests contain the environment variables POSTGRES_PASSWORD and DATABASE_PASSWORD with the value stored as plain text. Your task is to replace that environment variable with a Kubernetes Secret.

    Create a Secret running the following commands:

    1. Encode mypassword.

      echo -n "mypassword" | base64
    2. Create a YAML file named secret.yaml. In this file, add POSTGRES_PASSWORD as the key and the encoded value returned above under as the value in the data section.

      apiVersion: v1
      kind: Secret
      metadata:
      name: azure-voting-secret
      type: Opaque
      data:
      POSTGRES_PASSWORD: bXlwYXNzd29yZA==
    3. Create the Secret in your Kubernetes cluster by running the following command:

      kubectl create -f secret.yaml

    [!WARNING] base64 encoding is a simple and widely supported way to obscure plaintext data, it is not secure, as it can easily be decoded. If you want to store sensitive data like password, you should use a more secure method like encrypting with a Key Management Service (KMS) before storing it in the Secret.

    Modify the app deployment manifest

    With the ConfigMap and Secert both created the next step is to replace the environment variables provided in the application deployment manuscript with the values stored in the ConfigMap and the Secert.

    Complete the following steps to add the ConfigMap and Secert to the deployment mainifest:

    1. Open the Kubernetes manifest file deployment-app.yaml.

    2. In the containers section, add an envFrom section and upate the env section.

      envFrom:
      - configMapRef:
      name: azure-voting-config
      env:
      - name: DATABASE_PASSWORD
      valueFrom:
      secretKeyRef:
      name: azure-voting-secret
      key: POSTGRES_PASSWORD

      Using envFrom exposes all the values witin the ConfigMap as environment variables. Making it so you don't have to list them individually.

    3. Save the changes to the deployment manifest file.

    4. Apply the changes to the deployment by running the following command:

      kubectl apply -f deployment-app.yaml

    Modify the database deployment manifest

    Next, update the database deployment manifest and replace the plain text environment variable with the Kubernetes Secert.

    1. Open the deployment-db.yaml.

    2. To add the secret to the deployment, replace the env section with the following code:

      env:
      - name: POSTGRES_PASSWORD
      valueFrom:
      secretKeyRef:
      name: azure-voting-secret
      key: POSTGRES_PASSWORD
    3. Apply the updated manifest.

      kubectl apply -f deployment-db.yaml

    Verify the ConfigMap and output environment variables

    Verify that the ConfigMap was added to your deploy by running the following command:

    ```bash
    kubectl describe deployment azure-voting-app
    ```

    Browse the output until you find the envFrom section with the config map reference.

    You can also verify that the environment variables from the config map are being passed to the container by running the command kubectl exec -it <pod-name> -- printenv. This command will show you all the environment variables passed to the pod including the one from configmap.

    By following these steps, you will have successfully added a config map to the Azure Voting App Kubernetes deployment, and the environment variables defined in the config map will be passed to the container running in the pod.

    Verify the Secret and describe the deployment

    Once the secret has been created you can verify it exists by running the following command:

    kubectl get secrets

    You can view additional information, such as labels, annotations, type, and the Data by running kubectl describe:

    kubectl describe secret azure-voting-secret

    By default, the describe command doesn't output the encoded value, but if you output the results as JSON or YAML you'll be able to see the secret's encoded value.

     kubectl get secret azure-voting-secret -o json

    Conclusion

    In conclusion, using ConfigMaps and Secrets in Kubernetes can help to improve the scalability, security, and maintainability of your cluster. By decoupling configuration data and sensitive information from pod definitions, you can promote better organization and security in your Kubernetes environment. Additionally, separating these elements allows for sharing the same configuration and different secrets across multiple pods and deployments, simplifying scaling and management.

    Take the Cloud Skills Challenge!

    Enroll in the Cloud Skills Challenge!

    Don't miss out on this opportunity to level up your skills and stay ahead of the curve in the world of cloud native.

    - - + + \ No newline at end of file diff --git a/cnny-2023/serverless-containers/index.html b/cnny-2023/serverless-containers/index.html index e58a8773ff..32b3bd3d0c 100644 --- a/cnny-2023/serverless-containers/index.html +++ b/cnny-2023/serverless-containers/index.html @@ -14,13 +14,13 @@ - - + +

    4-1. Serverless Container Options

    · 7 min read
    Nitya Narasimhan

    Welcome to Week 4 of #CloudNativeNewYear!

    This week we'll go further with Cloud-native by exploring advanced topics and best practices for the Cloud-native practitioner. We'll start with an exploration of Serverless Container Options - ranging from managed services to Azure Kubernetes Service (AKS) and Azure Container Apps (ACA), to options that allow more granular control!

    What We'll Cover

    • The Azure Compute Landscape
    • Serverless Compute on Azure
    • Comparing Container Options On Azure
    • Other Considerations
    • Exercise: Try this yourself!
    • Resources: For self-study!


    We started this series with an introduction to core concepts:

    • In Containers 101, we learned why containerization matters. Think portability, isolation, scalability, resource-efficiency and cost-effectiveness. But not all apps can be containerized.
    • In Kubernetes 101, we learned how orchestration works. Think systems to automate container deployment, scaling, and management. But using Kubernetes directly can be complex.
    • In Exploring Cloud Native Options we asked the real questions: can we containerize - and should we?. The first depends on app characteristics, the second on your requirements.

    For example:

    • Can we containerize? The answer might be no if your app has system or OS dependencies, requires access to low-level hardware, or maintains complex state across sessions.
    • Should we containerize? The answer might be yes if your app is microservices-based, is stateless by default, requires portability, or is a legaacy app that can benefit from container isolation.

    As with every technology adoption decision process, there are no clear yes/no answers - just tradeoffs that you need to evaluate based on your architecture and application requirements. In today's post, we try to look at this from two main perspectives:

    1. Should you go serverless? Think: managed services that let you focus on app, not infra.
    2. What Azure Compute should I use? Think: best fit for my architecture & technology choices.

    Azure Compute Landscape

    Let's answer the second question first by exploring all available compute options on Azure. The illustrated decision-flow below is my favorite ways to navigate the choices, with questions like:

    • Are you migrating an existing app or building a new one?
    • Can you app be containerized?
    • Does it use a specific technology (Spring Boot, Red Hat Openshift)?
    • Do you need access to the Kubernetes API?
    • What characterizes the workload? (event-driven, web app, microservices etc.)

    Read the docs to understand how your choices can be influenced by the hosting model (IaaS, PaaS, FaaS), supported features (Networking, DevOps, Scalability, Availability, Security), architectural styles (Microservices, Event-driven, High-Performance Compute, Task Automation,Web-Queue Worker) etc.

    Compute Choices

    Now that we know all available compute options, let's address the second question: why go serverless? and what are my serverless compute options on Azure?

    Azure Serverless Compute

    Serverless gets defined many ways, but from a compute perspective, we can focus on a few key characteristics that are key to influencing this decision:

    • managed services - focus on application, let cloud provider handle infrastructure.
    • pay for what you use - get cost-effective resource utilization, flexible pricing options.
    • autoscaling on demand - take advantage of built-in features like KEDA-compliant triggers.
    • use preferred languages - write code in Java, JS, C#, Python etc. (specifics based on service)
    • cloud-native architectures - can support event-driven solutions, APIs, Microservices, DevOps!

    So what are some of the key options for Serverless Compute on Azure? The article dives into serverless support for fully-managed end-to-end serverless solutions with comprehensive support for DevOps, DevTools, AI/ML, Database, Storage, Monitoring and Analytics integrations. But we'll just focus on the 4 categories of applications when we look at Compute!

    1. Serverless Containerized Microservices using Azure Container Apps. Code in your preferred language, exploit full Dapr support, scale easily with any KEDA-compliant trigger.
    2. Serverless Application Environments using Azure App Service. Suitable for hosting monolithic apps (vs. microservices) in a managed service, with built-in support for on-demand scaling.
    3. Serverless Kubernetes using Azure Kubernetes Service (AKS). Spin up pods inside container instances and deploy Kubernetes-based applications with built-in KEDA-compliant autoscaling.
    4. Serverless Functions using Azure Functions. Execute "code at the granularity of functions" in your preferred language, and scale on demand with event-driven compute.

    We'll talk about these, and other compute comparisons, at the end of the article. But let's start with the core option you might choose if you want a managed serverless compute solution with built-in features for delivering containerized microservices at scale. Hello, Azure Container Apps!.

    Azure Container Apps

    Azure Container Apps (ACA) became generally available in May 2022 - providing customers with the ability to run microservices and containerized applications on a serverless, consumption-based platform. The figure below showcases the different types of applications that can be built with ACA. Note that it comes with built-in KEDA-compliant autoscaling triggers, and other auto-scale criteria that may be better-suited to the type of application you are building.

    About ACA

    So far in the series, we've put the spotlight on Azure Kubernetes Service (AKS) - so you're probably asking yourself: How does ACA compare to AKS?. We're glad you asked. Check out our Go Cloud-native with Azure Container Apps post from the #ServerlessSeptember series last year for a deeper-dive, or review the figure below for the main comparison points.

    The key takeaway is this. Azure Container Apps (ACA) also runs on Kubernetes but abstracts away its complexity in a managed service offering that lets you get productive quickly without requiring detailed knowledge of Kubernetes workings or APIs. However, if you want full access and control over the Kubernetes API then go with Azure Kubernetes Service (AKS) instead.

    Comparison

    Other Container Options

    Azure Container Apps is the preferred Platform As a Service (PaaS) option for a fully-managed serverless solution on Azure that is purpose-built for cloud-native microservices-based application workloads. But - there are other options that may be suitable for your specific needs, from a requirements and tradeoffs perspective. Let's review them quickly:

    1. Azure Functions is the serverless Functions-as-a-Service (FaaS) option, as opposed to ACA which supports a PaaS approach. It's optimized for running event-driven applications built at the granularity of ephemeral functions that can be deployed as code or containers.
    2. Azure App Service provides fully managed hosting for web applications that may be deployed using code or containers. It can be integrated with other services including Azure Container Apps and Azure Functions. It's optimized for deploying traditional web apps.
    3. Azure Kubernetes Service provides a fully managed Kubernetes option capable of running any Kubernetes workload, with direct access to the Kubernetes API.
    4. Azure Container Instances provides a single pod of Hyper-V isolated containers on demand, making them more of a low-level "building block" option compared to ACA.

    Based on the technology choices you made for application development you may also have more specialized options you want to consider. For instance:

    1. Azure Spring Apps is ideal if you're running Spring Boot or Spring Cloud workloads on Azure,
    2. Azure Red Hat OpenShift is ideal for integrated Kubernetes-powered OpenShift on Azure.
    3. Azure Confidential Computing is ideal if you have data/code integrity and confidentiality needs.
    4. Kubernetes At The Edge is ideal for bare-metal options that extend compute to edge devices.

    This is just the tip of the iceberg in your decision-making journey - but hopefully, it gave you a good sense of the options and criteria that influences your final choices. Let's wrap this up with a look at self-study resources for skilling up further.

    Exercise

    Want to get hands on learning related to these technologies?

    TAKE THE CLOUD SKILLS CHALLENGE

    Register today and level up your skills by completing free learning modules, while competing with your peers for a place on the leaderboards!

    Resources

    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/30-daysofcloudnative/index.html b/cnny-2023/tags/30-daysofcloudnative/index.html index 973d8b04c5..5668647d50 100644 --- a/cnny-2023/tags/30-daysofcloudnative/index.html +++ b/cnny-2023/tags/30-daysofcloudnative/index.html @@ -14,13 +14,13 @@ - - + +

    16 posts tagged with "30daysofcloudnative"

    View All Tags

    · 4 min read
    Cory Skimming
    Devanshi Joshi
    Steven Murawski
    Nitya Narasimhan

    Welcome to the Kick-off Post for #30DaysOfCloudNative - one of the core initiatives within #CloudNativeNewYear! Over the next four weeks, join us as we take you from fundamentals to functional usage of Cloud-native technologies, one blog post at a time! Read on to learn a little bit about this initiative and what you can expect to learn from this journey!

    What We'll Cover


    Cloud-native New Year

    Welcome to Week 01 of 🥳 #CloudNativeNewYear ! Today, we kick off a full month of content and activities to skill you up on all things Cloud-native on Azure with content, events, and community interactions! Read on to learn about what we have planned!


    Explore our initiatives

    We have a number of initiatives planned for the month to help you learn and skill up on relevant technologies. Click on the links to visit the relevant pages for each.

    We'll go into more details about #30DaysOfCloudNative in this post - don't forget to subscribe to the blog to get daily posts delivered directly to your preferred feed reader!


    Register for events!

    What are 3 things you can do today, to jumpstart your learning journey?


    #30DaysOfCloudNative

    #30DaysOfCloudNative is a month-long series of daily blog posts grouped into 4 themed weeks - taking you from core concepts to end-to-end solution examples in 30 days. Each article will be short (5-8 mins reading time) and provide exercises and resources to help you reinforce learnings and take next steps.

    This series focuses on the Cloud-native On Azure learning journey in four stages, each building on the previous week to help you skill up in a beginner-friendly way:

    We have a tentative weekly-themed roadmap for the topics we hope to cover and will keep this updated as we go with links to actual articles as they get published.

    Week 1: FOCUS ON CLOUD-NATIVE FUNDAMENTALS

    Here's a sneak peek at the week 1 schedule. We'll start with a broad review of cloud-native fundamentals and walkthrough the core concepts of microservices, containers and Kubernetes.

    • Jan 23: Learn Core Concepts for Cloud-native
    • Jan 24: Container 101
    • Jan 25: Adopting Microservices with Kubernetes
    • Jan 26: Kubernetes 101
    • Jan 27: Exploring your Cloud Native Options

    Let's Get Started!

    Now you know everything! We hope you are as excited as we are to dive into a full month of active learning and doing! Don't forget to subscribe for updates in your favorite feed reader! And look out for our first Cloud-native Fundamentals post on January 23rd!


    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/30-daysofcloudnative/page/10/index.html b/cnny-2023/tags/30-daysofcloudnative/page/10/index.html index a4d2622f72..1017b76bdb 100644 --- a/cnny-2023/tags/30-daysofcloudnative/page/10/index.html +++ b/cnny-2023/tags/30-daysofcloudnative/page/10/index.html @@ -14,15 +14,15 @@ - - + +

    16 posts tagged with "30daysofcloudnative"

    View All Tags

    · 14 min read
    Steven Murawski

    Welcome to Day 1 of Week 3 of #CloudNativeNewYear!

    The theme for this week is Bringing Your Application to Kubernetes. Last we talked about Kubernetes Fundamentals. Today we'll explore getting an existing application running in Kubernetes with a full pipeline in GitHub Actions.

    Ask the Experts Thursday, February 9th at 9 AM PST
    Friday, February 10th at 11 AM PST

    Watch the recorded demo and conversation about this week's topics.

    We were live on YouTube walking through today's (and the rest of this week's) demos. Join us Friday, February 10th and bring your questions!

    What We'll Cover

    • Our Application
    • Adding Some Infrastructure as Code
    • Building and Publishing a Container Image
    • Deploying to Kubernetes
    • Summary
    • Resources

    Our Application

    This week we'll be taking an exisiting application - something similar to a typical line of business application - and setting it up to run in Kubernetes. Over the course of the week, we'll address different concerns. Today we'll focus on updating our CI/CD process to handle standing up (or validating that we have) an Azure Kubernetes Service (AKS) environment, building and publishing container images for our web site and API server, and getting those services running in Kubernetes.

    The application we'll be starting with is eShopOnWeb. This application has a web site and API which are backed by a SQL Server instance. It's built in .NET 7, so it's cross-platform.

    info

    For the enterprising among you, you may notice that there are a number of different eShopOn* variants on GitHub, including eShopOnContainers. We aren't using that example as it's more of an end state than a starting place. Afterwards, feel free to check out that example as what this solution could look like as a series of microservices.

    Adding Some Infrastructure as Code

    Just like last week, we need to stand up an AKS environment. This week, however, rather than running commands in our own shell, we'll set up GitHub Actions to do that for us.

    There is a LOT of plumbing in this section, but once it's set up, it'll make our lives a lot easier. This section ensures that we have an environment to deploy our application into configured the way we want. We can easily extend this to accomodate multiple environments or add additional microservices with minimal new effort.

    Federated Identity

    Setting up a federated identity will allow us a more securable and auditable way of accessing Azure from GitHub Actions. For more about setting up a federated identity, Microsoft Learn has the details on connecting GitHub Actions to Azure.

    Here, we'll just walk through the setup of the identity and configure GitHub to use that idenity to deploy our AKS environment and interact with our Azure Container Registry.

    The examples will use PowerShell, but a Bash version of the setup commands is available in the week3/day1 branch.

    Prerequisites

    To follow along, you'll need:

    • a GitHub account
    • an Azure Subscription
    • the Azure CLI
    • and the Git CLI.

    You'll need to fork the source repository under your GitHub user or organization where you can manage secrets and GitHub Actions.

    It would be helpful to have the GitHub CLI, but it's not required.

    Set Up Some Defaults

    You will need to update one or more of the variables (your user or organization, what branch you want to work off of, and possibly the Azure AD application name if there is a conflict).

    # Replace the gitHubOrganizationName value
    # with the user or organization you forked
    # the repository under.

    $githubOrganizationName = 'Azure-Samples'
    $githubRepositoryName = 'eShopOnAKS'
    $branchName = 'week3/day1'
    $applicationName = 'cnny-week3-day1'

    Create an Azure AD Application

    Next, we need to create an Azure AD application.

    # Create an Azure AD application
    $aksDeploymentApplication = New-AzADApplication -DisplayName $applicationName

    Set Up Federation for that Azure AD Application

    And configure that application to allow federated credential requests from our GitHub repository for a particular branch.

    # Create a federated identity credential for the application
    New-AzADAppFederatedCredential `
    -Name $applicationName `
    -ApplicationObjectId $aksDeploymentApplication.Id `
    -Issuer 'https://token.actions.githubusercontent.com' `
    -Audience 'api://AzureADTokenExchange' `
    -Subject "repo:$($githubOrganizationName)/$($githubRepositoryName):ref:refs/heads/$branchName"

    Create a Service Principal for the Azure AD Application

    Once the application has been created, we need a service principal tied to that application. The service principal can be granted rights in Azure.

    # Create a service principal for the application
    New-AzADServicePrincipal -AppId $($aksDeploymentApplication.AppId)

    Give that Service Principal Rights to Azure Resources

    Because our Bicep deployment exists at the subscription level and we are creating role assignments, we need to give it Owner rights. If we changed the scope of the deployment to just a resource group, we could apply more scoped permissions.

    $azureContext = Get-AzContext
    New-AzRoleAssignment `
    -ApplicationId $($aksDeploymentApplication.AppId) `
    -RoleDefinitionName Owner `
    -Scope $azureContext.Subscription.Id

    Add Secrets to GitHub Repository

    If you have the GitHub CLI, you can use that right from your shell to set the secrets needed.

    gh secret set AZURE_CLIENT_ID --body $aksDeploymentApplication.AppId
    gh secret set AZURE_TENANT_ID --body $azureContext.Tenant.Id
    gh secret set AZURE_SUBSCRIPTION_ID --body $azureContext.Subscription.Id

    Otherwise, you can create them through the web interface like I did in the Learn Live event below.

    info

    It may look like the whole video will play, but it'll stop after configuring the secrets in GitHub (after about 9 minutes)

    The video shows creating the Azure AD application, service principals, and configuring the federated identity in Azure AD and GitHub.

    Creating a Bicep Deployment

    Resuable Workflows

    We'll create our Bicep deployment in a reusable workflows. What are they? The previous link has the documentation or the video below has my colleague Brandon Martinez and I talking about them.

    This workflow is basically the same deployment we did in last week's series, just in GitHub Actions.

    Start by creating a file called deploy_aks.yml in the .github/workflows directory with the below contents.

    name: deploy

    on:
    workflow_call:
    inputs:
    resourceGroupName:
    required: true
    type: string
    secrets:
    AZURE_CLIENT_ID:
    required: true
    AZURE_TENANT_ID:
    required: true
    AZURE_SUBSCRIPTION_ID:
    required: true
    outputs:
    containerRegistryName:
    description: Container Registry Name
    value: ${{ jobs.deploy.outputs.containerRegistryName }}
    containerRegistryUrl:
    description: Container Registry Login Url
    value: ${{ jobs.deploy.outputs.containerRegistryUrl }}
    resourceGroupName:
    description: Resource Group Name
    value: ${{ jobs.deploy.outputs.resourceGroupName }}
    aksName:
    description: Azure Kubernetes Service Cluster Name
    value: ${{ jobs.deploy.outputs.aksName }}

    permissions:
    id-token: write
    contents: read

    jobs:
    validate:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    - uses: azure/login@v1
    name: Sign in to Azure
    with:
    client-id: ${{ secrets.AZURE_CLIENT_ID }}
    tenant-id: ${{ secrets.AZURE_TENANT_ID }}
    subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
    - uses: azure/arm-deploy@v1
    name: Run preflight validation
    with:
    deploymentName: ${{ github.run_number }}
    scope: subscription
    region: eastus
    template: ./deploy/main.bicep
    parameters: >
    resourceGroup=${{ inputs.resourceGroupName }}
    deploymentMode: Validate

    deploy:
    needs: validate
    runs-on: ubuntu-latest
    outputs:
    containerRegistryName: ${{ steps.deploy.outputs.acr_name }}
    containerRegistryUrl: ${{ steps.deploy.outputs.acr_login_server_url }}
    resourceGroupName: ${{ steps.deploy.outputs.resource_group_name }}
    aksName: ${{ steps.deploy.outputs.aks_name }}
    steps:
    - uses: actions/checkout@v2
    - uses: azure/login@v1
    name: Sign in to Azure
    with:
    client-id: ${{ secrets.AZURE_CLIENT_ID }}
    tenant-id: ${{ secrets.AZURE_TENANT_ID }}
    subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
    - uses: azure/arm-deploy@v1
    id: deploy
    name: Deploy Bicep file
    with:
    failOnStdErr: false
    deploymentName: ${{ github.run_number }}
    scope: subscription
    region: eastus
    template: ./deploy/main.bicep
    parameters: >
    resourceGroup=${{ inputs.resourceGroupName }}

    Adding the Bicep Deployment

    Once we have the Bicep deployment workflow, we can add it to the primary build definition in .github/workflows/dotnetcore.yml

    Permissions

    First, we need to add a permissions block to let the workflow request our Azure AD token. This can go towards the top of the YAML file (I started it on line 5).

    permissions:
    id-token: write
    contents: read

    Deploy AKS Job

    Next, we'll add a reference to our reusable workflow. This will go after the build job.

      deploy_aks:
    needs: [build]
    uses: ./.github/workflows/deploy_aks.yml
    with:
    resourceGroupName: 'cnny-week3'
    secrets:
    AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
    AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
    AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }}

    Building and Publishing a Container Image

    Now that we have our target environment in place and an Azure Container Registry, we can build and publish our container images.

    Add a Reusable Workflow

    First, we'll create a new file for our reusable workflow at .github/workflows/publish_container_image.yml.

    We'll start the file with a name, the parameters it needs to run, and the permissions requirements for the federated identity request.

    name: Publish Container Images

    on:
    workflow_call:
    inputs:
    containerRegistryName:
    required: true
    type: string
    containerRegistryUrl:
    required: true
    type: string
    githubSha:
    required: true
    type: string
    secrets:
    AZURE_CLIENT_ID:
    required: true
    AZURE_TENANT_ID:
    required: true
    AZURE_SUBSCRIPTION_ID:
    required: true

    permissions:
    id-token: write
    contents: read

    Build the Container Images

    Our next step is to build the two container images we'll need for the application, the website and the API. We'll build the container images on our build worker and tag it with the git SHA, so there'll be a direct tie between the point in time in our codebase and the container images that represent it.

    jobs:
    publish_container_image:
    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v2
    - name: docker build
    run: |
    docker build . -f src/Web/Dockerfile -t ${{ inputs.containerRegistryUrl }}/web:${{ inputs.githubSha }}
    docker build . -f src/PublicApi/Dockerfile -t ${{ inputs.containerRegistryUrl }}/api:${{ inputs.githubSha}}

    Scan the Container Images

    Before we publish those container images, we'll scan them for vulnerabilities and best practice violations. We can add these two steps (one scan for each image).

        - name: scan web container image
    uses: Azure/container-scan@v0
    with:
    image-name: ${{ inputs.containerRegistryUrl }}/web:${{ inputs.githubSha}}
    - name: scan api container image
    uses: Azure/container-scan@v0
    with:
    image-name: ${{ inputs.containerRegistryUrl }}/web:${{ inputs.githubSha}}

    The container images provided have a few items that'll be found. We can create an allowed list at .github/containerscan/allowedlist.yaml to define vulnerabilities or best practice violations that we'll explictly allow to not fail our build.

    general:
    vulnerabilities:
    - CVE-2022-29458
    - CVE-2022-3715
    - CVE-2022-1304
    - CVE-2021-33560
    - CVE-2020-16156
    - CVE-2019-8457
    - CVE-2018-8292
    bestPracticeViolations:
    - CIS-DI-0001
    - CIS-DI-0005
    - CIS-DI-0006
    - CIS-DI-0008

    Publish the Container Images

    Finally, we'll log in to Azure, then log in to our Azure Container Registry, and push our images.

        - uses: azure/login@v1
    name: Sign in to Azure
    with:
    client-id: ${{ secrets.AZURE_CLIENT_ID }}
    tenant-id: ${{ secrets.AZURE_TENANT_ID }}
    subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
    - name: acr login
    run: az acr login --name ${{ inputs.containerRegistryName }}
    - name: docker push
    run: |
    docker push ${{ inputs.containerRegistryUrl }}/web:${{ inputs.githubSha}}
    docker push ${{ inputs.containerRegistryUrl }}/api:${{ inputs.githubSha}}

    Update the Build With the Image Build and Publish

    Now that we have our reusable workflow to create and publish our container images, we can include that in our primary build defnition at .github/workflows/dotnetcore.yml.

      publish_container_image:
    needs: [deploy_aks]
    uses: ./.github/workflows/publish_container_image.yml
    with:
    containerRegistryName: ${{ needs.deploy_aks.outputs.containerRegistryName }}
    containerRegistryUrl: ${{ needs.deploy_aks.outputs.containerRegistryUrl }}
    githubSha: ${{ github.sha }}
    secrets:
    AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
    AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
    AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }}

    Deploying to Kubernetes

    Finally, we've gotten enough set up that a commit to the target branch will:

    • build and test our application code
    • set up (or validate) our AKS and ACR environment
    • and create, scan, and publish our container images to ACR

    Our last step will be to deploy our application to Kubernetes. We'll use the basic building blocks we worked with last week, deployments and services.

    Starting the Reusable Workflow to Deploy to AKS

    We'll start our workflow with our parameters that we need, as well as the permissions to access the token to log in to Azure.

    We'll check out our code, then log in to Azure, and use the az CLI to get credentials for our AKS cluster.

    name: deploy_to_aks

    on:
    workflow_call:
    inputs:
    aksName:
    required: true
    type: string
    resourceGroupName:
    required: true
    type: string
    containerRegistryUrl:
    required: true
    type: string
    githubSha:
    required: true
    type: string
    secrets:
    AZURE_CLIENT_ID:
    required: true
    AZURE_TENANT_ID:
    required: true
    AZURE_SUBSCRIPTION_ID:
    required: true

    permissions:
    id-token: write
    contents: read

    jobs:
    deploy:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    - uses: azure/login@v1
    name: Sign in to Azure
    with:
    client-id: ${{ secrets.AZURE_CLIENT_ID }}
    tenant-id: ${{ secrets.AZURE_TENANT_ID }}
    subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
    - name: Get AKS Credentials
    run: |
    az aks get-credentials --resource-group ${{ inputs.resourceGroupName }} --name ${{ inputs.aksName }}

    Edit the Deployment For Our Current Image Tag

    Let's add the Kubernetes manifests to our repo. This post is long enough, so you can find the content for the manifests folder in the manifests folder in the source repo under the week3/day1 branch.

    tip

    If you only forked the main branch of the source repo, you can easily get the updated manifests by using the following git commands:

    git remote add upstream https://github.com/Azure-Samples/eShopOnAks
    git fetch upstream week3/day1
    git checkout upstream/week3/day1 manifests

    This will make the week3/day1 branch available locally and then we can update the manifests directory to match the state of that branch.

    The deployments and the service defintions should be familiar from last week's content (but not the same). This week, however, there's a new file in the manifests - ./manifests/kustomization.yaml

    This file helps us more dynamically edit our kubernetes manifests and support is baked right in to the kubectl command.

    Kustomize Definition

    Kustomize allows us to specify specific resource manifests and areas of that manifest to replace. We've put some placeholders in our file as well, so we can replace those for each run of our CI/CD system.

    In ./manifests/kustomization.yaml you will see:

    resources:
    - deployment-api.yaml
    - deployment-web.yaml

    # Change the image name and version
    images:
    - name: notavalidregistry.azurecr.io/api:v0.1.0
    newName: <YOUR_ACR_SERVER>/api
    newTag: <YOUR_IMAGE_TAG>
    - name: notavalidregistry.azurecr.io/web:v0.1.0
    newName: <YOUR_ACR_SERVER>/web
    newTag: <YOUR_IMAGE_TAG>

    Replacing Values in our Build

    Now, we encounter a little problem - our deployment files need to know the tag and ACR server. We can do a bit of sed magic to edit the file on the fly.

    In .github/workflows/deploy_to_aks.yml, we'll add:

          - name: replace_placeholders_with_current_run
    run: |
    sed -i "s/<YOUR_ACR_SERVER>/${{ inputs.containerRegistryUrl }}/g" ./manifests/kustomization.yaml
    sed -i "s/<YOUR_IMAGE_TAG>/${{ inputs.githubSha }}/g" ./manifests/kustomization.yaml

    Deploying the Manifests

    We have our manifests in place and our kustomization.yaml file (with commands to update it at runtime) ready to go, we can deploy our manifests.

    First, we'll deploy our database (deployment and service). Next, we'll use the -k parameter on kubectl to tell it to look for a kustomize configuration, transform the requested manifests and apply those. Finally, we apply the service defintions for the web and API deployments.

            run: |
    kubectl apply -f ./manifests/deployment-db.yaml \
    -f ./manifests/service-db.yaml
    kubectl apply -k ./manifests
    kubectl apply -f ./manifests/service-api.yaml \
    -f ./manifests/service-web.yaml

    Summary

    We've covered a lot of ground in today's post. We set up federated credentials with GitHub. Then we added reusable workflows to deploy an AKS environment and build/scan/publish our container images, and then to deploy them into our AKS environment.

    This sets us up to start making changes to our application and Kubernetes configuration and have those changes automatically validated and deployed by our CI/CD system. Tomorrow, we'll look at updating our application environment with runtime configuration, persistent storage, and more.

    Resources

    Take the Cloud Skills Challenge!

    Enroll in the Cloud Skills Challenge!

    Don't miss out on this opportunity to level up your skills and stay ahead of the curve in the world of cloud native.

    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/30-daysofcloudnative/page/11/index.html b/cnny-2023/tags/30-daysofcloudnative/page/11/index.html index dbd8555a3e..fc43486c6c 100644 --- a/cnny-2023/tags/30-daysofcloudnative/page/11/index.html +++ b/cnny-2023/tags/30-daysofcloudnative/page/11/index.html @@ -14,13 +14,13 @@ - - + +

    16 posts tagged with "30daysofcloudnative"

    View All Tags

    · 9 min read
    Steven Murawski

    Welcome to Day 4 of Week 3 of #CloudNativeNewYear!

    The theme for this week is Bringing Your Application to Kubernetes. Yesterday we exposed the eShopOnWeb app so that customers can reach it over the internet using a custom domain name and TLS. Today we'll explore the topic of debugging and instrumentation.

    Ask the Experts Thursday, February 9th at 9 AM PST
    Friday, February 10th at 11 AM PST

    Watch the recorded demo and conversation about this week's topics.

    We were live on YouTube walking through today's (and the rest of this week's) demos. Join us Friday, February 10th and bring your questions!

    What We'll Cover

    • Debugging
    • Bridge To Kubernetes
    • Instrumentation
    • Resources: For self-study!

    Debugging

    Debugging applications in a Kubernetes cluster can be challenging for several reasons:

    • Complexity: Kubernetes is a complex system with many moving parts, including pods, nodes, services, and config maps, all of which can interact in unexpected ways and cause issues.
    • Distributed Environment: Applications running in a Kubernetes cluster are often distributed across multiple nodes, which makes it harder to determine the root cause of an issue.
    • Logging and Monitoring: Debugging an application in a Kubernetes cluster requires access to logs and performance metrics, which can be difficult to obtain in a large and dynamic environment.
    • Resource Management: Kubernetes manages resources such as CPU and memory, which can impact the performance and behavior of applications. Debugging resource-related issues requires a deep understanding of the Kubernetes resource model and the underlying infrastructure.
    • Dynamic Nature: Kubernetes is designed to be dynamic, with the ability to add and remove resources as needed. This dynamic nature can make it difficult to reproduce issues and debug problems.

    However, there are many tools and practices that can help make debugging applications in a Kubernetes cluster easier, such as using centralized logging, monitoring, and tracing solutions, and following best practices for managing resources and deployment configurations.

    There's also another great tool in our toolbox - Bridge to Kubernetes.

    Bridge to Kubernetes

    Bridge to Kubernetes is a great tool for microservice development and debugging applications without having to locally replicate all the required microservices.

    Bridge to Kubernetes works with Visual Studio or Visual Studio Code.

    We'll walk through using it with Visual Studio Code.

    Connecting Bridge to Kubernetes to Our Cluster

    Ensure your AKS cluster is the default for kubectl

    If you've recently spun up a new AKS cluster or you have been working with a different cluster, you may need to change what cluster credentials you have configured.

    If it's a new cluster, we can use:

    RESOURCE_GROUP=<YOUR RESOURCE GROUP NAME>
    CLUSTER_NAME=<YOUR AKS CLUSTER NAME>
    az aks get-credentials az aks get-credentials --resource-group $RESOURCE_GROUP --name $CLUSTER_NAME

    Open the command palette

    Open the command palette and find Bridge to Kubernetes: Configure. You may need to start typing the name to get it to show up.

    The command palette for Visual Studio Code is open and the first item is Bridge to Kubernetes: Configure

    Pick the service you want to debug

    Bridge to Kubernetes will redirect a service for you. Pick the service you want to redirect, in this case we'll pick web.

    Selecting the `web` service to redirect in Visual Studio Code

    Identify the port your application runs on

    Next, we'll be prompted to identify what port our application will run on locally. For this application it'll be 5001, but that's just specific to this application (and the default for ASP.NET 7, I believe).

    Setting port 5001 as the port to redirect to the `web` Kubernetes service in Visual Studio Code

    Pick a debug configuration to extend

    Bridge to Kubernetes has a couple of ways to run - it can inject it's setup and teardown to your existing debug configurations. We'll pick .NET Core Launch (web).

    Telling Bridge to Kubernetes to use the .NET Core Launch (web) debug configuration in Visual Studio Code

    Forward Traffic for All Requests

    The last prompt you'll get in the configuration is about how you want Bridge to Kubernetes to handle re-routing traffic. The default is that all requests into the service will get your local version.

    You can also redirect specific traffic. Bridge to Kubernetes will set up a subdomain and route specific traffic to your local service, while allowing other traffic to the deployed service.

    Allowing the launch of Endpoint Manager on Windows

    Using Bridge to Kubernetes to Debug Our Service

    Now that we've configured Bridge to Kubernetes, we see that tasks and a new launch configuration have been added.

    Added to .vscode/tasks.json:

            {
    "label": "bridge-to-kubernetes.resource",
    "type": "bridge-to-kubernetes.resource",
    "resource": "web",
    "resourceType": "service",
    "ports": [
    5001
    ],
    "targetCluster": "aks1",
    "targetNamespace": "default",
    "useKubernetesServiceEnvironmentVariables": false
    },
    {
    "label": "bridge-to-kubernetes.compound",
    "dependsOn": [
    "bridge-to-kubernetes.resource",
    "build"
    ],
    "dependsOrder": "sequence"
    }

    And added to .vscode/launch.json:

    {
    "name": ".NET Core Launch (web) with Kubernetes",
    "type": "coreclr",
    "request": "launch",
    "preLaunchTask": "bridge-to-kubernetes.compound",
    "program": "${workspaceFolder}/src/Web/bin/Debug/net7.0/Web.dll",
    "args": [],
    "cwd": "${workspaceFolder}/src/Web",
    "stopAtEntry": false,
    "env": {
    "ASPNETCORE_ENVIRONMENT": "Development",
    "ASPNETCORE_URLS": "http://+:5001"
    },
    "sourceFileMap": {
    "/Views": "${workspaceFolder}/Views"
    }
    }

    Launch the debug configuration

    We can start the process with the .NET Core Launch (web) with Kubernetes launch configuration in the Debug pane in Visual Studio Code.

    Launch the `.NET Core Launch (web) with Kubernetes` from the Debug pane in Visual Studio Code

    Enable the Endpoint Manager

    Part of this process includes a local service to help manage the traffic routing and your hosts file. This will require admin or sudo privileges. On Windows, you'll get a prompt like:

    Prompt to launch the endpoint manager.

    Access your Kubernetes cluster "locally"

    Bridge to Kubernetes will set up a tunnel (thanks to port forwarding) to your local workstation and create local endpoints for the other Kubernetes hosted services in your cluster, as well as pretending to be a pod in that cluster (for the application you are debugging).

    Output from Bridge To Kubernetes setup task.

    After making the connection to your Kubernetes cluster, the launch configuration will continue. In this case, we'll make a debug build of the application and attach the debugger. (This process may cause the terminal in VS Code to scroll with build output. You can find the Bridge to Kubernetes output with the local IP addresses and ports in the Output pane for Bridge to Kubernetes.)

    You can set breakpoints, use your debug console, set watches, run tests against your local version of the service.

    Exploring the Running Application Environment

    One of the cool things that Bridge to Kubernetes does for our debugging experience is bring the environment configuration that our deployed pod would inherit. When we launch our app, it'll see configuration and secrets that we'd expect our pod to be running with.

    To test this, we'll set a breakpoint in our application's start up to see what SQL Server is being used. We'll set a breakpoint at src/Infrastructure/Dependencies.cs on line 32.

    Then, we will start debugging the application with Bridge to Kubernetes. When it hits the breakpoint, we'll open the Debug pane and type configuration.GetConnectionString("CatalogConnection").

    When we run locally (not with Bridge to Kubernetes), we'd see:

    configuration.GetConnectionString("CatalogConnection")
    "Server=(localdb)\\mssqllocaldb;Integrated Security=true;Initial Catalog=Microsoft.eShopOnWeb.CatalogDb;"

    But, with Bridge to Kubernetes we see something more like (yours will vary based on the password ):

    configuration.GetConnectionString("CatalogConnection")
    "Server=db;Database=Microsoft.eShopOnWeb.CatalogDb;User Id=sa;Password=*****************;TrustServerCertificate=True;"

    Debugging our local application connected to Kubernetes.

    We can see that the database server configured is based on our db service and the password is pulled from our secret in Azure KeyVault (via AKS).

    This helps us run our local application just like it was actually in our cluster.

    Going Further

    Bridge to Kubernetes also supports more advanced scenarios and, as you need to start routing traffic around inside your cluster, may require you to modify your application to pass along a kubernetes-route-as header to help ensure that traffic for your debugging workloads is properly handled. The docs go into much greater detail about that.

    Instrumentation

    Now that we've figured out our debugging story, we'll need to ensure that we have the right context clues to find where we need to debug or to give us a better idea of how well our microservices are running.

    Logging and Tracing

    Logging and tracing become even more critical in Kubernetes, where your application could be running in a number of pods across different nodes. When you have an issue, in addition to the normal application data, you'll want to know what pod and what node had the issue, what the state of those resources were (were you resource constrained or were shared resources unavailable?), and if autoscaling is enabled, you'll want to know if a scale event has been triggered. There are a multitude of other concerns based on your application and the environment you maintain.

    Given these informational needs, it's crucial to revisit your existing logging and instrumentation. Most frameworks and languages have extensible logging, tracing, and instrumentation libraries that you can iteratively add information to, such as pod and node states, and ensuring that requests can be traced across your microservices. This will pay you back time and time again when you have to troubleshoot issues in your existing environment.

    Centralized Logging

    To enhance the troubleshooting process further, it's important to implement centralized logging to consolidate logs from all your microservices into a single location. This makes it easier to search and analyze logs when you're troubleshooting an issue.

    Automated Alerting

    Additionally, implementing automated alerting, such as sending notifications when specific conditions occur in the logs, can help you detect issues before they escalate.

    End to end Visibility

    End-to-end visibility is also essential in understanding the flow of requests and responses between microservices in a distributed system. With end-to-end visibility, you can quickly identify bottlenecks and slowdowns in the system, helping you to resolve issues more efficiently.

    Resources

    Take the Cloud Skills Challenge!

    Enroll in the Cloud Skills Challenge!

    Don't miss out on this opportunity to level up your skills and stay ahead of the curve in the world of cloud native.

    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/30-daysofcloudnative/page/12/index.html b/cnny-2023/tags/30-daysofcloudnative/page/12/index.html index 848a09bf1d..91349ea725 100644 --- a/cnny-2023/tags/30-daysofcloudnative/page/12/index.html +++ b/cnny-2023/tags/30-daysofcloudnative/page/12/index.html @@ -14,13 +14,13 @@ - - + +

    16 posts tagged with "30daysofcloudnative"

    View All Tags

    · 7 min read
    Nitya Narasimhan

    Welcome to Week 4 of #CloudNativeNewYear!

    This week we'll go further with Cloud-native by exploring advanced topics and best practices for the Cloud-native practitioner. We'll start with an exploration of Serverless Container Options - ranging from managed services to Azure Kubernetes Service (AKS) and Azure Container Apps (ACA), to options that allow more granular control!

    What We'll Cover

    • The Azure Compute Landscape
    • Serverless Compute on Azure
    • Comparing Container Options On Azure
    • Other Considerations
    • Exercise: Try this yourself!
    • Resources: For self-study!


    We started this series with an introduction to core concepts:

    • In Containers 101, we learned why containerization matters. Think portability, isolation, scalability, resource-efficiency and cost-effectiveness. But not all apps can be containerized.
    • In Kubernetes 101, we learned how orchestration works. Think systems to automate container deployment, scaling, and management. But using Kubernetes directly can be complex.
    • In Exploring Cloud Native Options we asked the real questions: can we containerize - and should we?. The first depends on app characteristics, the second on your requirements.

    For example:

    • Can we containerize? The answer might be no if your app has system or OS dependencies, requires access to low-level hardware, or maintains complex state across sessions.
    • Should we containerize? The answer might be yes if your app is microservices-based, is stateless by default, requires portability, or is a legaacy app that can benefit from container isolation.

    As with every technology adoption decision process, there are no clear yes/no answers - just tradeoffs that you need to evaluate based on your architecture and application requirements. In today's post, we try to look at this from two main perspectives:

    1. Should you go serverless? Think: managed services that let you focus on app, not infra.
    2. What Azure Compute should I use? Think: best fit for my architecture & technology choices.

    Azure Compute Landscape

    Let's answer the second question first by exploring all available compute options on Azure. The illustrated decision-flow below is my favorite ways to navigate the choices, with questions like:

    • Are you migrating an existing app or building a new one?
    • Can you app be containerized?
    • Does it use a specific technology (Spring Boot, Red Hat Openshift)?
    • Do you need access to the Kubernetes API?
    • What characterizes the workload? (event-driven, web app, microservices etc.)

    Read the docs to understand how your choices can be influenced by the hosting model (IaaS, PaaS, FaaS), supported features (Networking, DevOps, Scalability, Availability, Security), architectural styles (Microservices, Event-driven, High-Performance Compute, Task Automation,Web-Queue Worker) etc.

    Compute Choices

    Now that we know all available compute options, let's address the second question: why go serverless? and what are my serverless compute options on Azure?

    Azure Serverless Compute

    Serverless gets defined many ways, but from a compute perspective, we can focus on a few key characteristics that are key to influencing this decision:

    • managed services - focus on application, let cloud provider handle infrastructure.
    • pay for what you use - get cost-effective resource utilization, flexible pricing options.
    • autoscaling on demand - take advantage of built-in features like KEDA-compliant triggers.
    • use preferred languages - write code in Java, JS, C#, Python etc. (specifics based on service)
    • cloud-native architectures - can support event-driven solutions, APIs, Microservices, DevOps!

    So what are some of the key options for Serverless Compute on Azure? The article dives into serverless support for fully-managed end-to-end serverless solutions with comprehensive support for DevOps, DevTools, AI/ML, Database, Storage, Monitoring and Analytics integrations. But we'll just focus on the 4 categories of applications when we look at Compute!

    1. Serverless Containerized Microservices using Azure Container Apps. Code in your preferred language, exploit full Dapr support, scale easily with any KEDA-compliant trigger.
    2. Serverless Application Environments using Azure App Service. Suitable for hosting monolithic apps (vs. microservices) in a managed service, with built-in support for on-demand scaling.
    3. Serverless Kubernetes using Azure Kubernetes Service (AKS). Spin up pods inside container instances and deploy Kubernetes-based applications with built-in KEDA-compliant autoscaling.
    4. Serverless Functions using Azure Functions. Execute "code at the granularity of functions" in your preferred language, and scale on demand with event-driven compute.

    We'll talk about these, and other compute comparisons, at the end of the article. But let's start with the core option you might choose if you want a managed serverless compute solution with built-in features for delivering containerized microservices at scale. Hello, Azure Container Apps!.

    Azure Container Apps

    Azure Container Apps (ACA) became generally available in May 2022 - providing customers with the ability to run microservices and containerized applications on a serverless, consumption-based platform. The figure below showcases the different types of applications that can be built with ACA. Note that it comes with built-in KEDA-compliant autoscaling triggers, and other auto-scale criteria that may be better-suited to the type of application you are building.

    About ACA

    So far in the series, we've put the spotlight on Azure Kubernetes Service (AKS) - so you're probably asking yourself: How does ACA compare to AKS?. We're glad you asked. Check out our Go Cloud-native with Azure Container Apps post from the #ServerlessSeptember series last year for a deeper-dive, or review the figure below for the main comparison points.

    The key takeaway is this. Azure Container Apps (ACA) also runs on Kubernetes but abstracts away its complexity in a managed service offering that lets you get productive quickly without requiring detailed knowledge of Kubernetes workings or APIs. However, if you want full access and control over the Kubernetes API then go with Azure Kubernetes Service (AKS) instead.

    Comparison

    Other Container Options

    Azure Container Apps is the preferred Platform As a Service (PaaS) option for a fully-managed serverless solution on Azure that is purpose-built for cloud-native microservices-based application workloads. But - there are other options that may be suitable for your specific needs, from a requirements and tradeoffs perspective. Let's review them quickly:

    1. Azure Functions is the serverless Functions-as-a-Service (FaaS) option, as opposed to ACA which supports a PaaS approach. It's optimized for running event-driven applications built at the granularity of ephemeral functions that can be deployed as code or containers.
    2. Azure App Service provides fully managed hosting for web applications that may be deployed using code or containers. It can be integrated with other services including Azure Container Apps and Azure Functions. It's optimized for deploying traditional web apps.
    3. Azure Kubernetes Service provides a fully managed Kubernetes option capable of running any Kubernetes workload, with direct access to the Kubernetes API.
    4. Azure Container Instances provides a single pod of Hyper-V isolated containers on demand, making them more of a low-level "building block" option compared to ACA.

    Based on the technology choices you made for application development you may also have more specialized options you want to consider. For instance:

    1. Azure Spring Apps is ideal if you're running Spring Boot or Spring Cloud workloads on Azure,
    2. Azure Red Hat OpenShift is ideal for integrated Kubernetes-powered OpenShift on Azure.
    3. Azure Confidential Computing is ideal if you have data/code integrity and confidentiality needs.
    4. Kubernetes At The Edge is ideal for bare-metal options that extend compute to edge devices.

    This is just the tip of the iceberg in your decision-making journey - but hopefully, it gave you a good sense of the options and criteria that influences your final choices. Let's wrap this up with a look at self-study resources for skilling up further.

    Exercise

    Want to get hands on learning related to these technologies?

    TAKE THE CLOUD SKILLS CHALLENGE

    Register today and level up your skills by completing free learning modules, while competing with your peers for a place on the leaderboards!

    Resources

    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/30-daysofcloudnative/page/13/index.html b/cnny-2023/tags/30-daysofcloudnative/page/13/index.html index 83bbeadec2..14e0d69966 100644 --- a/cnny-2023/tags/30-daysofcloudnative/page/13/index.html +++ b/cnny-2023/tags/30-daysofcloudnative/page/13/index.html @@ -14,13 +14,13 @@ - - + +

    16 posts tagged with "30daysofcloudnative"

    View All Tags

    · 3 min read
    Cory Skimming

    It's the final week of #CloudNativeNewYear! This week we'll go further with Cloud-native by exploring advanced topics and best practices for the Cloud-native practitioner. In today's post, we will introduce you to the basics of the open-source project Draft and how it can be used to easily create and deploy applications to Kubernetes.

    It's not too late to sign up for and complete the Cloud Skills Challenge!

    What We'll Cover

    • What is Draft?
    • Draft basics
    • Demo: Developing to AKS with Draft
    • Resources


    What is Draft?

    Draft is an open-source tool that can be used to streamline the development and deployment of applications on Kubernetes clusters. It provides a simple and easy-to-use workflow for creating and deploying applications, making it easier for developers to focus on writing code and building features, rather than worrying about the underlying infrastructure. This is great for users who are just getting started with Kubernetes, or those who are just looking to simplify their experience.

    New to Kubernetes?

    Draft basics

    Draft streamlines Kubernetes development by taking a non-containerized application and generating the Dockerfiles, K8s manifests, Helm charts, and other artifacts associated with a containerized application. Draft can also create a GitHub Action workflow file to quickly build and deploy your application onto any Kubernetes cluster.

    1. 'draft create'': Create a new Draft project by simply running the 'draft create' command - this command will walk you through a series of questions on your application specification (such as the application language) and create a Dockerfile, Helm char, and Kubernetes
    2. 'draft generate-workflow'': Automatically build out a GitHub Action using the 'draft generate-workflow' command
    3. 'draft setup-gh'': If you are using Azure, use this command to automate the GitHub OIDC set up process to ensure that you will be able to deploy your application using your GitHub Action.

    At this point, you will have all the files needed to deploy your app onto a Kubernetes cluster (we told you it was easy!).

    You can also use the 'draft info' command if you are looking for information on supported languages and deployment types. Let's see it in action, shall we?


    Developing to AKS with Draft

    In this Microsoft Reactor session below, we'll briefly introduce Kubernetes and the Azure Kubernetes Service (AKS) and then demo how enable your applications for Kubernetes using the open-source tool Draft. We'll show how Draft can help you create the boilerplate code to containerize your applications and add routing and scaling behaviours.

    ##Conclusion

    Overall, Draft simplifies the process of building, deploying, and managing applications on Kubernetes, and can make the overall journey from code to Kubernetes significantly easier.


    Resources


    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/30-daysofcloudnative/page/14/index.html b/cnny-2023/tags/30-daysofcloudnative/page/14/index.html index 15238ff9c7..f0f6d83f3e 100644 --- a/cnny-2023/tags/30-daysofcloudnative/page/14/index.html +++ b/cnny-2023/tags/30-daysofcloudnative/page/14/index.html @@ -14,14 +14,14 @@ - - + +

    16 posts tagged with "30daysofcloudnative"

    View All Tags

    · 7 min read
    Vinicius Apolinario

    Welcome to Day 3 of Week 4 of #CloudNativeNewYear!

    The theme for this week is going further with Cloud Native. Yesterday we talked about using Draft to accelerate your Kubernetes adoption. Today we'll explore the topic of Windows containers.

    What We'll Cover

    • Introduction
    • Windows containers overview
    • Windows base container images
    • Isolation
    • Exercise: Try this yourself!
    • Resources: For self-study!

    Introduction

    Windows containers were launched along with Windows Server 2016, and have evolved since. In its latest release, Windows Server 2022, Windows containers have reached a great level of maturity and allow for customers to run production grade workloads.

    While suitable for new developments, Windows containers also provide developers and operations with a different approach than Linux containers. It allows for existing Windows applications to be containerized with little or no code changes. It also allows for professionals that are more comfortable with the Windows platform and OS, to leverage their skill set, while taking advantage of the containers platform.

    Windows container overview

    In essence, Windows containers are very similar to Linux. Since Windows containers use the same foundation of Docker containers, you can expect that the same architecture applies - with the specific notes of the Windows OS. For example, when running a Windows container via Docker, you use the same commands, such as docker run. To pull a container image, you can use docker pull, just like on Linux. However, to run a Windows container, you also need a Windows container host. This requirement is there because, as you might remember, a container shares the OS kernel with its container host.

    On Kubernetes, Windows containers are supported since Windows Server 2019. Just like with Docker, you can manage Windows containers like any other resource on the Kubernetes ecosystem. A Windows node can be part of a Kubernetes cluster, allowing you to run Windows container based applications on services like Azure Kubernetes Service. To deploy an Windows application to a Windows pod in Kubernetes, you can author a YAML specification much like you would for Linux. The main difference is that you would point to an image that runs on Windows, and you need to specify a node selection tag to indicate said pod needs to run on a Windows node.

    Windows base container images

    On Windows containers, you will always use a base container image provided by Microsoft. This base container image contains the OS binaries for the container to run. This image can be as large as 3GB+, or small as ~300MB. The difference in the size is a consequence of the APIs and components available in each Windows container base container image. There are primarily, three images: Nano Server, Server Core, and Server.

    Nano Server is the smallest image, ranging around 300MB. It's a base container image for new developments and cloud-native scenarios. Applications need to target Nano Server as the Windows OS, so not all frameworks will work. For example, .Net works on Nano Server, but .Net Framework doesn't. Other third-party frameworks also work on Nano Server, such as Apache, NodeJS, Phyton, Tomcat, Java runtime, JBoss, Redis, among others.

    Server Core is a much larger base container image, ranging around 1.25GB. It's larger size is compensated by it's application compatibility. Simply put, any application that meets the requirements to be run on a Windows container, can be containerized with this image.

    The Server image builds on the Server Core one. It ranges around 3.1GB and has even greater application compatibility than the Server Core image. In addition to the traditional Windows APIs and components, this image allows for scenarios such as Machine Learning via DirectX with GPU access.

    The best image for your scenario is dependent on the requirements your application has on the Windows OS inside a container. However, there are some scenarios that are not supported at all on Windows containers - such as GUI or RDP dependent applications, some Windows Server infrastructure roles, such as Active Directory, among others.

    Isolation

    When running containers, the kernel of the container host is shared with the containers running on it. While extremely convenient, this poses a potential risk for multi-tenant scenarios. If one container is compromised and has access to the host, it could potentially compromise other containers in the same system.

    For enterprise customers running on-premises (or even in the cloud), this can be mitigated by using a VM as a container host and considering the VM itself a security boundary. However, if multiple workloads from different tenants need to share the same host, Windows containers offer another option: Hyper-V isolation. While the name Hyper-V is associated with VMs, its virtualization capabilities extend to other services, including containers. Hyper-V isolated containers run on a purpose built, extremely small, highly performant VM. However, you manage a container running with Hyper-V isolation the same way you do with a process isolated one. In fact, the only notable difference is that you need to append the --isolation=hyperv tag to the docker run command.

    Exercise

    Here are a few examples of how to use Windows containers:

    Run Windows containers via Docker on your machine

    To pull a Windows base container image:

    docker pull mcr.microsoft.com/windows/servercore:ltsc2022

    To run a basic IIS container:

    #This command will pull and start a IIS container. You can access it from http://<your local IP>:8080
    docker run -d -p 8080:80 mcr.microsoft.com/windows/servercore/iis:windowsservercore-ltsc2022

    Run the same IIS container with Hyper-V isolation

    #This command will pull and start a IIS container. You can access it from http://<your local IP>:8080
    docker run -d -p 8080:80 --isolation=hyperv mcr.microsoft.com/windows/servercore/iis:windowsservercore-ltsc2022

    To run a Windows container interactively:

    docker run -it mcr.microsoft.com/windows/servercore:ltsc2022 powershell

    Run Windows containers on Kubernetes

    To prepare an AKS cluster for Windows containers: Note: Replace the values on the example below with the ones from your environment.

    echo "Please enter the username to use as administrator credentials for Windows Server nodes on your cluster: " && read WINDOWS_USERNAME
    az aks create \
    --resource-group myResourceGroup \
    --name myAKSCluster \
    --node-count 2 \
    --generate-ssh-keys \
    --windows-admin-username $WINDOWS_USERNAME \
    --vm-set-type VirtualMachineScaleSets \
    --network-plugin azure

    To add a Windows node pool for Windows containers:

    az aks nodepool add \
    --resource-group myResourceGroup \
    --cluster-name myAKSCluster \
    --os-type Windows \
    --name npwin \
    --node-count 1

    Deploy a sample ASP.Net application to the AKS cluster above using the YAML file below:

    apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: sample
    labels:
    app: sample
    spec:
    replicas: 1
    template:
    metadata:
    name: sample
    labels:
    app: sample
    spec:
    nodeSelector:
    "kubernetes.io/os": windows
    containers:
    - name: sample
    image: mcr.microsoft.com/dotnet/framework/samples:aspnetapp
    resources:
    limits:
    cpu: 1
    memory: 800M
    ports:
    - containerPort: 80
    selector:
    matchLabels:
    app: sample
    ---
    apiVersion: v1
    kind: Service
    metadata:
    name: sample
    spec:
    type: LoadBalancer
    ports:
    - protocol: TCP
    port: 80
    selector:
    app: sample

    Save the file above and run the command below on your Kubernetes cluster:

    kubectl apply -f <filename> .

    Once deployed, you can access the application by getting the IP address of your service:

    kubectl get service

    Resources

    It's not too late to sign up for and complete the Cloud Skills Challenge!
    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/30-daysofcloudnative/page/15/index.html b/cnny-2023/tags/30-daysofcloudnative/page/15/index.html index 98ee14b22c..2e98fa3eb5 100644 --- a/cnny-2023/tags/30-daysofcloudnative/page/15/index.html +++ b/cnny-2023/tags/30-daysofcloudnative/page/15/index.html @@ -14,13 +14,13 @@ - - + +

    16 posts tagged with "30daysofcloudnative"

    View All Tags

    · 4 min read
    Jorge Arteiro

    Welcome to Day 4 of Week 4 of #CloudNativeNewYear!

    The theme for this week is going further with Cloud Native. Yesterday we talked about Windows Containers. Today we'll explore addons and extensions available to Azure Kubernetes Services (AKS).

    What We'll Cover

    • Introduction
    • Add-ons
    • Extensions
    • Add-ons vs Extensions
    • Resources

    Introduction

    Azure Kubernetes Service (AKS) is a fully managed container orchestration service that makes it easy to deploy and manage containerized applications on Azure. AKS offers a number of features and capabilities, including the ability to extend its supported functionality through the use of add-ons and extensions.

    There are also integrations available from open-source projects and third parties, but they are not covered by the AKS support policy.

    Add-ons

    Add-ons provide a supported way to extend AKS. Installation, configuration and lifecycle are managed by AKS following pre-determine updates rules.

    As an example, let's enable Container Insights with the monitoring addon. on an existing AKS cluster using az aks enable-addons --addons CLI command

    az aks enable-addons \
    --name MyManagedCluster \
    --resource-group MyResourceGroup \
    --addons monitoring

    or you can use az aks create --enable-addons when creating new clusters

    az aks create \
    --name MyManagedCluster \
    --resource-group MyResourceGroup \
    --enable-addons monitoring

    The current available add-ons are:

    1. http_application_routing - Configure ingress with automatic public DNS name creation. Only recommended for development.
    2. monitoring - Container Insights monitoring.
    3. virtual-node - CNCF virtual nodes open source project.
    4. azure-policy - Azure Policy for AKS.
    5. ingress-appgw - Application Gateway Ingress Controller (AGIC).
    6. open-service-mesh - CNCF Open Service Mesh project.
    7. azure-keyvault-secrets-provider - Azure Key Vault Secrets Provider for Secret Store CSI Driver.
    8. web_application_routing - Managed NGINX ingress Controller.
    9. keda - CNCF Event-driven autoscaling project.

    For more details, get the updated list of AKS Add-ons here

    Extensions

    Cluster Extensions uses Helm charts and integrates with Azure Resource Manager (ARM) to provide installation and lifecycle management of capabilities on top of AKS.

    Extensions can be auto upgraded using minor versions, but it requires extra management and configuration. Using Scope parameter, it can be installed on the whole cluster or per namespace.

    AKS Extensions requires an Azure CLI extension to be installed. To add or update this CLI extension use the following commands:

    az extension add --name k8s-extension

    and to update an existing extension

    az extension update --name k8s-extension

    There are only 3 available extensions:

    1. Dapr - CNCF Dapr project.
    2. Azure ML - Integrate Azure Machine Learning with AKS to train, inference and manage ML models.
    3. Flux (GitOps) - CNCF Flux project integrated with AKS to enable cluster configuration and application deployment using GitOps.

    As an example, you can install Azure ML using the following command:

    az k8s-extension create \
    --name aml-compute --extension-type Microsoft.AzureML.Kubernetes \
    --scope cluster --cluster-name <clusterName> \
    --resource-group <resourceGroupName> \
    --cluster-type managedClusters \
    --configuration-settings enableInference=True allowInsecureConnections=True

    For more details, get the updated list of AKS Extensions here

    Add-ons vs Extensions

    AKS Add-ons brings an advantage of been fully managed by AKS itself, and AKS Extensions are more flexible and configurable but requires extra level of management.

    Add-ons are part of the AKS resource provider in the Azure API, and AKS Extensions are a separate resource provider on the Azure API.

    Resources

    It's not too late to sign up for and complete the Cloud Skills Challenge!
    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/30-daysofcloudnative/page/16/index.html b/cnny-2023/tags/30-daysofcloudnative/page/16/index.html index b06add5094..8181f8a238 100644 --- a/cnny-2023/tags/30-daysofcloudnative/page/16/index.html +++ b/cnny-2023/tags/30-daysofcloudnative/page/16/index.html @@ -14,13 +14,13 @@ - - + +

    16 posts tagged with "30daysofcloudnative"

    View All Tags

    · 6 min read
    Cory Skimming
    Steven Murawski
    Paul Yu
    Josh Duffney
    Nitya Narasimhan
    Vinicius Apolinario
    Jorge Arteiro
    Devanshi Joshi

    And that's a wrap on the inaugural #CloudNativeNewYear! Thank you for joining us to kick off the new year with this learning journey into cloud-native! In this final post of the 2023 celebration of all things cloud-native, we'll do two things:

    • Look Back - with a quick retrospective of what was covered.
    • Look Ahead - with resources and suggestions for how you can continue your skilling journey!

    We appreciate your time and attention and we hope you found this curated learning valuable. Feedback and suggestions are always welcome. From our entire team, we wish you good luck with the learning journey - now go build some apps and share your knowledge! 🎉


    What We'll Cover

    • Cloud-native fundamentals
    • Kubernetes fundamentals
    • Bringing your applications to Kubernetes
    • Go further with cloud-native
    • Resources to keep the celebration going!

    Week 1: Cloud-native Fundamentals

    In Week 1, we took a tour through the fundamentals of cloud-native technologies, including a walkthrough of the core concepts of containers, microservices, and Kubernetes.

    • Jan 23 - Cloud-native Fundamentals: The answers to life and all the universe - what is cloud-native? What makes an application cloud-native? What are the benefits? (yes, we all know it's 42, but hey, gotta start somewhere!)
    • Jan 24 - Containers 101: Containers are an essential component of cloud-native development. In this intro post, we cover how containers work and why they have become so popular.
    • Jan 25 - Kubernetes 101: Kuber-what-now? Learn the basics of Kubernetes and how it enables us to deploy and manage our applications effectively and consistently.
    A QUICKSTART GUIDE TO KUBERNETES CONCEPTS

    Missed it Live? Tune in to A Quickstart Guide to Kubernetes Concepts on demand, now!

    • Jan 26 - Microservices 101: What is a microservices architecture and how can we go about designing one?
    • Jan 27 - Exploring your Cloud Native Options: Cloud-native, while catchy, can be a very broad term. What technologies should you use? Learn some basic guidelines for when it is optimal to use different technologies for your project.

    Week 2: Kubernetes Fundamentals

    In Week 2, we took a deeper dive into the Fundamentals of Kubernetes. The posts and live demo from this week took us through how to build a simple application on Kubernetes, covering everything from deployment to networking and scaling. Note: for our samples and demo we have used Azure Kubernetes Service, but the principles apply to any Kubernetes!

    • Jan 30 - Pods and Deployments: how to use pods and deployments in Kubernetes.
    • Jan 31 - Services and Ingress: how to use services and ingress and a walk through the steps of making our containers accessible internally and externally!
    • Feb 1 - ConfigMaps and Secrets: how to of passing configuration and secrets to our applications in Kubernetes with ConfigMaps and Secrets.
    • Feb 2 - Volumes, Mounts, and Claims: how to use persistent storage on Kubernetes (and ensure your data can survive container restarts!).
    • Feb 3 - Scaling Pods and Nodes: how to scale pods and nodes in our Kubernetes cluster.
    ASK THE EXPERTS: AZURE KUBERNETES SERVICE

    Missed it Live? Tune in to Ask the Expert with Azure Kubernetes Service on demand, now!


    Week 3: Bringing your applications to Kubernetes

    So, you have learned how to build an application on Kubernetes. What about your existing applications? In Week 3, we explored how to take an existing application and set it up to run in Kubernetes:

    • Feb 6 - CI/CD: learn how to get an existing application running in Kubernetes with a full pipeline in GitHub Actions.
    • Feb 7 - Adapting Storage, Secrets, and Configuration: how to evaluate our sample application's configuration, storage, and networking requirements and implement using Kubernetes.
    • Feb 8 - Opening your Application with Ingress: how to expose the eShopOnWeb app so that customers can reach it over the internet using a custom domain name and TLS.
    • Feb 9 - Debugging and Instrumentation: how to debug and instrument your application now that it is on Kubernetes.
    • Feb 10 - CI/CD Secure Supply Chain: now that we have set up our application on Kubernetes, let's talk about container image signing and how to set up a secure supply change.

    Week 4: Go Further with Cloud-Native

    This week we have gone further with Cloud-native by exploring advanced topics and best practices for the Cloud-native practitioner.

    And today, February 17th, with this one post to rule (er, collect) them all!


    Keep the Learning Going!

    Learning is great, so why stop here? We have a host of great resources and samples for you to continue your cloud-native journey with Azure below:


    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/30-daysofcloudnative/page/2/index.html b/cnny-2023/tags/30-daysofcloudnative/page/2/index.html index 161e03d2bb..ef4f303ec0 100644 --- a/cnny-2023/tags/30-daysofcloudnative/page/2/index.html +++ b/cnny-2023/tags/30-daysofcloudnative/page/2/index.html @@ -14,14 +14,14 @@ - - + +

    16 posts tagged with "30daysofcloudnative"

    View All Tags

    · 5 min read
    Cory Skimming

    Welcome to Week 1 of #CloudNativeNewYear!

    Cloud-native New Year

    You will often hear the term "cloud-native" when discussing modern application development, but even a quick online search will return a huge number of articles, tweets, and web pages with a variety of definitions. So, what does cloud-native actually mean? Also, what makes an application a cloud-native application versus a "regular" application?

    Today, we will address these questions and more as we kickstart our learning journey (and our new year!) with an introductory dive into the wonderful world of cloud-native.


    What We'll Cover

    • What is cloud-native?
    • What is a cloud-native application?
    • The benefits of cloud-native
    • The five pillars of cloud-native
    • Exercise: Take the Cloud Skills Challenge!

    1. What is cloud-native?

    The term "cloud-native" can seem pretty self-evident (yes, hello, native to the cloud?), and in a way, it is. While there are lots of definitions of cloud-native floating around, at it's core, cloud-native simply refers to a modern approach to building software that takes advantage of cloud services and environments. This includes using cloud-native technologies, such as containers, microservices, and serverless, and following best practices for deploying, scaling, and managing applications in a cloud environment.

    Official definition from the Cloud Native Computing Foundation:

    Cloud-native technologies empower organizations to build and run scalable applications in modern, dynamic environments such as public, private, and hybrid clouds. Containers, service meshes, microservices, immutable infrastructure, and declarative APIs exemplify this approach.

    These techniques enable loosely coupled systems that are resilient, manageable, and observable. Combined with robust automation, they allow engineers to make high-impact changes frequently and predictably with minimal toil. Source


    2. So, what exactly is a cloud-native application?

    Cloud-native applications are specifically designed to take advantage of the scalability, resiliency, and distributed nature of modern cloud infrastructure. But how does this differ from a "traditional" application?

    Traditional applications are generally been built, tested, and deployed as a single, monolithic unit. The monolithic nature of this type of architecture creates close dependencies between components. This complexity and interweaving only increases as an application grows and can make it difficult to evolve (not to mention troubleshoot) and challenging to operate over time.

    To contrast, in cloud-native architectures the application components are decomposed into loosely coupled services, rather than built and deployed as one block of code. This decomposition into multiple self-contained services enables teams to manage complexity and improve the speed, agility, and scale of software delivery. Many small parts enables teams to make targeted updates, deliver new features, and fix any issues without leading to broader service disruption.


    3. The benefits of cloud-native

    Cloud-native architectures can bring many benefits to an organization, including:

    1. Scalability: easily scale up or down based on demand, allowing organizations to adjust their resource usage and costs as needed.
    2. Flexibility: deploy and run on any cloud platform, and easily move between clouds and on-premises environments.
    3. High-availability: techniques such as redundancy, self-healing, and automatic failover help ensure that cloud-native applications are designed to be highly-available and fault tolerant.
    4. Reduced costs: take advantage of the pay-as-you-go model of cloud computing, reducing the need for expensive infrastructure investments.
    5. Improved security: tap in to cloud security features, such as encryption and identity management, to improve the security of the application.
    6. Increased agility: easily add new features or services to your applications to meet changing business needs and market demand.

    4. The pillars of cloud-native

    There are five areas that are generally cited as the core building blocks of cloud-native architecture:

    1. Microservices: Breaking down monolithic applications into smaller, independent, and loosely-coupled services that can be developed, deployed, and scaled independently.
    2. Containers: Packaging software in lightweight, portable, and self-sufficient containers that can run consistently across different environments.
    3. Automation: Using automation tools and DevOps processes to manage and operate the cloud-native infrastructure and applications, including deployment, scaling, monitoring, and self-healing.
    4. Service discovery: Using service discovery mechanisms, such as APIs & service meshes, to enable services to discover and communicate with each other.
    5. Observability: Collecting and analyzing data from the infrastructure and applications to understand and optimize the performance, behavior, and health of the system.

    These can (and should!) be used in combination to deliver cloud-native solutions that are highly scalable, flexible, and available.

    WHAT'S NEXT

    Stay tuned, as we will be diving deeper into these topics in the coming weeks:

    • Jan 24: Containers 101
    • Jan 25: Adopting Microservices with Kubernetes
    • Jan 26: Kubernetes 101
    • Jan 27: Exploring your Cloud-native Options

    Resources


    Don't forget to subscribe to the blog to get daily posts delivered directly to your favorite feed reader!


    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/30-daysofcloudnative/page/3/index.html b/cnny-2023/tags/30-daysofcloudnative/page/3/index.html index 570d462b9e..fb0df055b3 100644 --- a/cnny-2023/tags/30-daysofcloudnative/page/3/index.html +++ b/cnny-2023/tags/30-daysofcloudnative/page/3/index.html @@ -14,14 +14,14 @@ - - + +

    16 posts tagged with "30daysofcloudnative"

    View All Tags

    · 4 min read
    Steven Murawski
    Paul Yu
    Josh Duffney

    Welcome to Day 2 of Week 1 of #CloudNativeNewYear!

    Today, we'll focus on building an understanding of containers.

    What We'll Cover

    • Introduction
    • How do Containers Work?
    • Why are Containers Becoming so Popular?
    • Conclusion
    • Resources
    • Learning Path

    REGISTER & LEARN: KUBERNETES 101

    Interested in a dive into Kubernetes and a chance to talk to experts?

    🎙: Join us Jan 26 @1pm PST by registering here

    Here's what you will learn:

    • Key concepts and core principles of Kubernetes.
    • How to deploy, scale and manage containerized workloads.
    • Live Demo of the concepts explained
    • How to get started with Azure Kubernetes Service for free.

    Start your free Azure Kubernetes Trial Today!!: aka.ms/TryAKS

    Introduction

    In the beginning, we deployed our applications onto physical servers. We only had a certain number of those servers, so often they hosted multiple applications. This led to some problems when those applications shared dependencies. Upgrading one application could break another application on the same server.

    Enter virtualization. Virtualization allowed us to run our applications in an isolated operating system instance. This removed much of the risk of updating shared dependencies. However, it increased our overhead since we had to run a full operating system for each application environment.

    To address the challenges created by virtualization, containerization was created to improve isolation without duplicating kernel level resources. Containers provide efficient and consistent deployment and runtime experiences for our applications and have become very popular as a way of packaging and distributing applications.

    How do Containers Work?

    Containers build on two capabilities in the Linux operating system, namespaces and cgroups. These constructs allow the operating system to provide isolation to a process or group of processes, keeping their access to filesystem resources separate and providing controls on resource utilization. This, combined with tooling to help package, deploy, and run container images has led to their popularity in today’s operating environment. This provides us our isolation without the overhead of additional operating system resources.

    When a container host is deployed on an operating system, it works at scheduling the access to the OS (operating systems) components. This is done by providing a logical isolated group that can contain processes for a given application, called a namespace. The container host then manages /schedules access from the namespace to the host OS. The container host then uses cgroups to allocate compute resources. Together, the container host with the help of cgroups and namespaces can schedule multiple applications to access host OS resources.

    Overall, this gives the illusion of virtualizing the host OS, where each application gets its own OS. In actuality, all the applications are running on the same operating system and sharing the same kernel as the container host.

    Containers are popular in the software development industry because they provide several benefits over traditional virtualization methods. Some of these benefits include:

    • Portability: Containers make it easy to move an application from one environment to another without having to worry about compatibility issues or missing dependencies.
    • Isolation: Containers provide a level of isolation between the application and the host system, which means that the application running in the container cannot access the host system's resources.
    • Scalability: Containers make it easy to scale an application up or down as needed, which is useful for applications that experience a lot of traffic or need to handle a lot of data.
    • Resource Efficiency: Containers are more resource-efficient than traditional virtualization methods because they don't require a full operating system to be running on each virtual machine.
    • Cost-Effective: Containers are more cost-effective than traditional virtualization methods because they don't require expensive hardware or licensing fees.

    Conclusion

    Containers are a powerful technology that allows developers to package and deploy applications in a portable and isolated environment. This technology is becoming increasingly popular in the world of software development and is being used by many companies and organizations to improve their application deployment and management processes. With the benefits of portability, isolation, scalability, resource efficiency, and cost-effectiveness, containers are definitely worth considering for your next application development project.

    Containerizing applications is a key step in modernizing them, and there are many other patterns that can be adopted to achieve cloud-native architectures, including using serverless platforms, Kubernetes, and implementing DevOps practices.

    Resources

    Learning Path

    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/30-daysofcloudnative/page/4/index.html b/cnny-2023/tags/30-daysofcloudnative/page/4/index.html index 17454f4939..bf1c6c7923 100644 --- a/cnny-2023/tags/30-daysofcloudnative/page/4/index.html +++ b/cnny-2023/tags/30-daysofcloudnative/page/4/index.html @@ -14,14 +14,14 @@ - - + +

    16 posts tagged with "30daysofcloudnative"

    View All Tags

    · 3 min read
    Steven Murawski

    Welcome to Day 3 of Week 1 of #CloudNativeNewYear!

    This week we'll focus on what Kubernetes is.

    What We'll Cover

    • Introduction
    • What is Kubernetes? (Video)
    • How does Kubernetes Work? (Video)
    • Conclusion


    REGISTER & LEARN: KUBERNETES 101

    Interested in a dive into Kubernetes and a chance to talk to experts?

    🎙: Join us Jan 26 @1pm PST by registering here

    Here's what you will learn:

    • Key concepts and core principles of Kubernetes.
    • How to deploy, scale and manage containerized workloads.
    • Live Demo of the concepts explained
    • How to get started with Azure Kubernetes Service for free.

    Start your free Azure Kubernetes Trial Today!!: aka.ms/TryAKS

    Introduction

    Kubernetes is an open source container orchestration engine that can help with automated deployment, scaling, and management of our applications.

    Kubernetes takes physical (or virtual) resources and provides a consistent API over them, bringing a consistency to the management and runtime experience for our applications. Kubernetes provides us with a number of capabilities such as:

    • Container scheduling
    • Service discovery and load balancing
    • Storage orchestration
    • Automated rollouts and rollbacks
    • Automatic bin packing
    • Self-healing
    • Secret and configuration management

    We'll learn more about most of these topics as we progress through Cloud Native New Year.

    What is Kubernetes?

    Let's hear from Brendan Burns, one of the founders of Kubernetes as to what Kubernetes actually is.

    How does Kubernetes Work?

    And Brendan shares a bit more with us about how Kubernetes works.

    Conclusion

    Kubernetes allows us to deploy and manage our applications effectively and consistently.

    By providing a consistent API across many of the concerns our applications have, like load balancing, networking, storage, and compute, Kubernetes improves both our ability to build and ship new software.

    There are standards for the applications to depend on for resources needed. Deployments, metrics, and logs are provided in a standardized fashion allowing more effecient operations across our application environments.

    And since Kubernetes is an open source platform, it can be found in just about every type of operating environment - cloud, virtual machines, physical hardware, shared data centers, even small devices like Rasberry Pi's!

    Want to learn more? Join us for a webinar on Kubernetes Concepts (or catch the playback) on Thursday, January 26th at 1 PM PST and watch for the rest of this series right here!

    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/30-daysofcloudnative/page/5/index.html b/cnny-2023/tags/30-daysofcloudnative/page/5/index.html index b4da80413c..dcae387cdd 100644 --- a/cnny-2023/tags/30-daysofcloudnative/page/5/index.html +++ b/cnny-2023/tags/30-daysofcloudnative/page/5/index.html @@ -14,13 +14,13 @@ - - + +

    16 posts tagged with "30daysofcloudnative"

    View All Tags

    · 6 min read
    Josh Duffney

    Welcome to Day 4 of Week 1 of #CloudNativeNewYear!

    This week we'll focus on advanced topics and best practices for Cloud-Native practitioners, kicking off with this post on Serverless Container Options with Azure. We'll look at technologies, tools and best practices that range from managed services like Azure Kubernetes Service, to options allowing finer granularity of control and oversight.

    What We'll Cover

    • What is Microservice Architecture?
    • How do you design a Microservice?
    • What challenges do Microservices introduce?
    • Conclusion
    • Resources


    Microservices are a modern way of designing and building software that increases deployment velocity by decomposing an application into small autonomous services that can be deployed independently.

    By deploying loosely coupled microservices your applications can be developed, deployed, and scaled independently. Because each service is independent, it can be updated or replaced without having to worry about the impact on the rest of the application. This means that if a bug is found in one service, it can be fixed without having to redeploy the entire application. All of which gives an organization the ability to deliver value to their customers faster.

    In this article, we will explore the basics of microservices architecture, its benefits and challenges, and how it can help improve the development, deployment, and maintenance of software applications.

    What is Microservice Architecture?

    Before explaining what Microservice architecture is, it’s important to understand what problems microservices aim to address.

    Traditional software development is centered around building monolithic applications. Monolithic applications are built as a single, large codebase. Meaning your code is tightly coupled causing the monolithic app to suffer from the following:

    Too much Complexity: Monolithic applications can become complex and difficult to understand and maintain as they grow. This can make it hard to identify and fix bugs and add new features.

    Difficult to Scale: Monolithic applications can be difficult to scale as they often have a single point of failure, which can cause the whole application to crash if a service fails.

    Slow Deployment: Deploying a monolithic application can be risky and time-consuming, as a small change in one part of the codebase can affect the entire application.

    Microservice architecture (often called microservices) is an architecture style that addresses the challenges created by Monolithic applications. Microservices architecture is a way of designing and building software applications as a collection of small, independent services that communicate with each other through APIs. This allows for faster development and deployment cycles, as well as easier scaling and maintenance than is possible with a monolithic application.

    How do you design a Microservice?

    Building applications with Microservices architecture requires a different approach. Microservices architecture focuses on business capabilities rather than technical layers, such as data access or messaging. Doing so requires that you shift your focus away from the technical stack and model your applications based upon the various domains that exist within the business.

    Domain-driven design (DDD) is a way to design software by focusing on the business needs. You can use Domain-driven design as a framework that guides the development of well-designed microservices by building services that encapsulate knowledge in each domain and abstract that knowledge from clients.

    In Domain-driven design you start by modeling the business domain and creating a domain model. A domain model is an abstract model of the business model that distills and organizes a domain of knowledge and provides a common language for developers and domain experts. It’s the resulting domain model that microservices a best suited to be built around because it helps establish a well-defined boundary between external systems and other internal applications.

    In short, before you begin designing microservices, start by mapping the functions of the business and their connections to create a domain model for the microservice(s) to be built around.

    What challenges do Microservices introduce?

    Microservices solve a lot of problems and have several advantages, but the grass isn’t always greener on the other side.

    One of the key challenges of microservices is managing communication between services. Because services are independent, they need to communicate with each other through APIs. This can be complex and difficult to manage, especially as the number of services grows. To address this challenge, it is important to have a clear API design, with well-defined inputs and outputs for each service. It is also important to have a system for managing and monitoring communication between services, to ensure that everything is running smoothly.

    Another challenge of microservices is managing the deployment and scaling of services. Because each service is independent, it needs to be deployed and scaled separately from the rest of the application. This can be complex and difficult to manage, especially as the number of services grows. To address this challenge, it is important to have a clear and consistent deployment process, with well-defined steps for deploying and scaling each service. Furthermore, it is advisable to host them on a system with self-healing capabilities to reduce operational burden.

    It is also important to have a system for monitoring and managing the deployment and scaling of services, to ensure optimal performance.

    Each of these challenges has created fertile ground for tooling and process that exists in the cloud-native ecosystem. Kubernetes, CI CD, and other DevOps practices are part of the package of adopting the microservices architecture.

    Conclusion

    In summary, microservices architecture focuses on software applications as a collection of small, independent services that communicate with each other over well-defined APIs.

    The main advantages of microservices include:

    • increased flexibility and scalability per microservice,
    • efficient resource utilization (with help from a container orchestrator like Kubernetes),
    • and faster development cycles.

    Continue following along with this series to see how you can use Kubernetes to help adopt microservices patterns in your own environments!

    Resources

    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/30-daysofcloudnative/page/6/index.html b/cnny-2023/tags/30-daysofcloudnative/page/6/index.html index 8712f3f6fa..b0b398a772 100644 --- a/cnny-2023/tags/30-daysofcloudnative/page/6/index.html +++ b/cnny-2023/tags/30-daysofcloudnative/page/6/index.html @@ -14,14 +14,14 @@ - - + +

    16 posts tagged with "30daysofcloudnative"

    View All Tags

    · 6 min read
    Cory Skimming

    We are excited to be wrapping up our first week of #CloudNativeNewYear! This week, we have tried to set the stage by covering the fundamentals of cloud-native practices and technologies, including primers on containerization, microservices, and Kubernetes.

    Don't forget to sign up for the the Cloud Skills Challenge!

    Today, we will do a brief recap of some of these technologies and provide some basic guidelines for when it is optimal to use each.


    What We'll Cover

    • To Containerize or not to Containerize?
    • The power of Kubernetes
    • Where does Serverless fit?
    • Resources
    • What's coming next!


    Just joining us now? Check out these other Week 1 posts:

    To Containerize or not to Containerize?

    As mentioned in our Containers 101 post earlier this week, containers can provide several benefits over traditional virtualization methods, which has made them popular within the software development community. Containers provide a consistent and predictable runtime environment, which can help reduce the risk of compatibility issues and simplify the deployment process. Additionally, containers can improve resource efficiency by allowing multiple applications to run on the same host while isolating their dependencies.

    Some types of apps that are a particularly good fit for containerization include:

    1. Microservices: Containers are particularly well-suited for microservices-based applications, as they can be used to isolate and deploy individual components of the system. This allows for more flexibility and scalability in the deployment process.
    2. Stateless applications: Applications that do not maintain state across multiple sessions, such as web applications, are well-suited for containers. Containers can be easily scaled up or down as needed and replaced with new instances, without losing data.
    3. Portable applications: Applications that need to be deployed in different environments, such as on-premises, in the cloud, or on edge devices, can benefit from containerization. The consistent and portable runtime environment of containers can make it easier to move the application between different environments.
    4. Legacy applications: Applications that are built using older technologies or that have compatibility issues can be containerized to run in an isolated environment, without impacting other applications or the host system.
    5. Dev and testing environments: Containerization can be used to create isolated development and testing environments, which can be easily created and destroyed as needed.

    While there are many types of applications that can benefit from a containerized approach, it's worth noting that containerization is not always the best option, and it's important to weigh the benefits and trade-offs before deciding to containerize an application. Additionally, some types of applications may not be a good fit for containers including:

    • Apps that require full access to host resources: Containers are isolated from the host system, so if an application needs direct access to hardware resources such as GPUs or specialized devices, it might not work well in a containerized environment.
    • Apps that require low-level system access: If an application requires deep access to the underlying operating system, it may not be suitable for running in a container.
    • Applications that have specific OS dependencies: Apps that have specific dependencies on a certain version of an operating system or libraries may not be able to run in a container.
    • Stateful applications: Apps that maintain state across multiple sessions, such as databases, may not be well suited for containers. Containers are ephemeral by design, so the data stored inside a container may not persist between restarts.

    The good news is that some of these limitations can be overcome with the use of specialized containerization technologies such as Kubernetes, and by carefully designing the architecture of the application.


    The power of Kubernetes

    Speaking of Kubernetes...

    Kubernetes is a powerful tool for managing and deploying containerized applications in production environments, particularly for applications that need to scale, handle large numbers of requests, or run in multi-cloud or hybrid environments.

    Kubernetes is well-suited for a wide variety of applications, but it is particularly well-suited for the following types of applications:

    1. Microservices-based applications: Kubernetes provides a powerful set of tools for managing and deploying microservices-based applications, making it easy to scale, update, and manage the individual components of the application.
    2. Stateful applications: Kubernetes provides support for stateful applications through the use of Persistent Volumes and StatefulSets, allowing for applications that need to maintain state across multiple instances.
    3. Large-scale, highly-available systems: Kubernetes provides built-in support for scaling, self-healing, and rolling updates, making it an ideal choice for large-scale, highly-available systems that need to handle large numbers of users and requests.
    4. Multi-cloud and hybrid environments: Kubernetes can be used to deploy and manage applications across multiple cloud providers and on-premises environments, making it a good choice for organizations that want to take advantage of the benefits of multiple cloud providers or that need to deploy applications in a hybrid environment.
    New to Kubernetes?

    Where does Serverless fit in?

    Serverless is a cloud computing model where the cloud provider (like Azure) is responsible for executing a piece of code by dynamically allocating the resources. With serverless, you only pay for the exact amount of compute time that you use, rather than paying for a fixed amount of resources. This can lead to significant cost savings, particularly for applications with variable or unpredictable workloads.

    Serverless is commonly used for building applications like web or mobile apps, IoT, data processing, and real-time streaming - apps where the workloads are variable and high scalability is required. It's important to note that serverless is not a replacement for all types of workloads - it's best suited for stateless, short-lived and small-scale workloads.

    For a detailed look into the world of Serverless and lots of great learning content, revisit #30DaysofServerless.


    Resources


    What's up next in #CloudNativeNewYear?

    Week 1 has been all about the fundamentals of cloud-native. Next week, the team will be diving in to application deployment with Azure Kubernetes Service. Don't forget to subscribe to the blog to get daily posts delivered directly to your favorite feed reader!


    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/30-daysofcloudnative/page/7/index.html b/cnny-2023/tags/30-daysofcloudnative/page/7/index.html index 6865f0fa6b..789843c10b 100644 --- a/cnny-2023/tags/30-daysofcloudnative/page/7/index.html +++ b/cnny-2023/tags/30-daysofcloudnative/page/7/index.html @@ -14,13 +14,13 @@ - - + +

    16 posts tagged with "30daysofcloudnative"

    View All Tags

    · 14 min read
    Steven Murawski

    Welcome to Day #1 of Week 2 of #CloudNativeNewYear!

    The theme for this week is Kubernetes fundamentals. Last week we talked about Cloud Native architectures and the Cloud Native landscape. Today we'll explore the topic of Pods and Deployments in Kubernetes.

    Ask the Experts Thursday, February 9th at 9 AM PST
    Catch the Replay of the Live Demo

    Watch the recorded demo and conversation about this week's topics.

    We were live on YouTube walking through today's (and the rest of this week's) demos.

    What We'll Cover

    • Setting Up A Kubernetes Environment in Azure
    • Running Containers in Kubernetes Pods
    • Making the Pods Resilient with Deployments
    • Exercise
    • Resources

    Setting Up A Kubernetes Environment in Azure

    For this week, we'll be working with a simple app - the Azure Voting App. My teammate Paul Yu ported the app to Rust and we tweaked it a bit to let us highlight some of the basic features of Kubernetes.

    You should be able to replicate this in just about any Kubernetes environment, but we'll use Azure Kubernetes Service (AKS) as our working environment for this week.

    To make it easier to get started, there's a Bicep template to deploy an AKS cluster, an Azure Container Registry (ACR) (to host our container image), and connect the two so that we can easily deploy our application.

    Step 0 - Prerequisites

    There are a few things you'll need if you want to work through this and the following examples this week.

    Required:

    • Git (and probably a GitHub account if you want to persist your work outside of your computer)
    • Azure CLI
    • An Azure subscription (if you want to follow along with the Azure steps)
    • Kubectl (the command line tool for managing Kubernetes)

    Helpful:

    • Visual Studio Code (or equivalent editor)

    Step 1 - Clone the application repository

    First, I forked the source repository to my account.

    $GitHubOrg = 'smurawski' # Replace this with your GitHub account name or org name
    git clone "https://github.com/$GitHubOrg/azure-voting-app-rust"
    cd azure-voting-app-rust

    Leave your shell opened with your current location inside the application repository.

    Step 2 - Set up AKS

    Running the template deployment from the demo script (I'm using the PowerShell example in cnny23-week2-day1.ps1, but there's a Bash variant at cnny23-week2-day1.sh) stands up the environment. The second, third, and fourth commands take some of the output from the Bicep deployment to set up for later commands, so don't close out your shell after you run these commands.

    az deployment sub create --template-file ./deploy/main.bicep --location eastus --parameters 'resourceGroup=cnny-week2'
    $AcrName = az deployment sub show --name main --query 'properties.outputs.acr_name.value' -o tsv
    $AksName = az deployment sub show --name main --query 'properties.outputs.aks_name.value' -o tsv
    $ResourceGroup = az deployment sub show --name main --query 'properties.outputs.resource_group_name.value' -o tsv

    az aks get-credentials --resource-group $ResourceGroup --name $AksName

    Step 3 - Build our application container

    Since we have an Azure Container Registry set up, I'll use ACR Build Tasks to build and store my container image.

    az acr build --registry $AcrName --% --image cnny2023/azure-voting-app-rust:{{.Run.ID}} .
    $BuildTag = az acr repository show-tags `
    --name $AcrName `
    --repository cnny2023/azure-voting-app-rust `
    --orderby time_desc `
    --query '[0]' -o tsv
    tip

    Wondering what the --% is in the first command line? That tells the PowerShell interpreter to pass the input after it "as is" to the command without parsing/evaluating it. Otherwise, PowerShell messes a bit with the templated {{.Run.ID}} bit.

    Running Containers in Kubernetes Pods

    Now that we have our AKS cluster and application image ready to go, let's look into how Kubernetes runs containers.

    If you've been in tech for any length of time, you've seen that every framework, runtime, orchestrator, etc.. can have their own naming scheme for their concepts. So let's get into some of what Kubernetes calls things.

    The Pod

    A container running in Kubernetes is called a Pod. A Pod is basically a running container on a Node or VM. It can be more. For example you can run multiple containers and specify some funky configuration, but we'll keep it simple for now - add the complexity when you need it.

    Our Pod definition can be created via the kubectl command imperatively from arguments or declaratively from a configuration file. We'll do a little of both. We'll use the kubectl command to help us write our configuration files. Kubernetes configuration files are YAML, so having an editor that supports and can help you syntax check YAML is really helpful.

    Creating a Pod Definition

    Let's create a few Pod definitions. Our application requires two containers to get working - the application and a database.

    Let's create the database Pod first. And before you comment, the configuration isn't secure nor best practice. We'll fix that later this week. For now, let's focus on getting up and running.

    This is a trick I learned from one of my teammates - Paul. By using the --output yaml and --dry-run=client options, we can have the command help us write our YAML. And with a bit of output redirection, we can stash it safely in a file for later use.

    kubectl run azure-voting-db `
    --image "postgres:15.0-alpine" `
    --env "POSTGRES_PASSWORD=mypassword" `
    --output yaml `
    --dry-run=client > manifests/pod-db.yaml

    This creates a file that looks like:

    apiVersion: v1
    kind: Pod
    metadata:
    creationTimestamp: null
    labels:
    run: azure-voting-db
    name: azure-voting-db
    spec:
    containers:
    - env:
    - name: POSTGRES_PASSWORD
    value: mypassword
    image: postgres:15.0-alpine
    name: azure-voting-db
    resources: {}
    dnsPolicy: ClusterFirst
    restartPolicy: Always
    status: {}

    The file, when supplied to the Kubernetes API, will identify what kind of resource to create, the API version to use, and the details of the container (as well as an environment variable to be supplied).

    We'll get that container image started with the kubectl command. Because the details of what to create are in the file, we don't need to specify much else to the kubectl command but the path to the file.

    kubectl apply -f ./manifests/pod-db.yaml

    I'm going to need the IP address of the Pod, so that my application can connect to it, so we can use kubectl to get some information about our pod. By default, kubectl get pod only displays certain information but it retrieves a lot more. We can use the JSONPath syntax to index into the response and get the information you want.

    tip

    To see what you can get, I usually run the kubectl command with the output type (-o JSON) of JSON and then I can find where the data I want is and create my JSONPath query to get it.

    $DB_IP = kubectl get pod azure-voting-db -o jsonpath='{.status.podIP}'

    Now, let's create our Pod definition for our application. We'll use the same technique as before.

    kubectl run azure-voting-app `
    --image "$AcrName.azurecr.io/cnny2023/azure-voting-app-rust:$BuildTag" `
    --env "DATABASE_SERVER=$DB_IP" `
    --env "DATABASE_PASSWORD=mypassword`
    --output yaml `
    --dry-run=client > manifests/pod-app.yaml

    That command gets us a similar YAML file to the database container - you can see the full file here

    Let's get our application container running.

    kubectl apply -f ./manifests/pod-app.yaml

    Now that the Application is Running

    We can check the status of our Pods with:

    kubectl get pods

    And we should see something like:

    azure-voting-app-rust ❯  kubectl get pods
    NAME READY STATUS RESTARTS AGE
    azure-voting-app 1/1 Running 0 36s
    azure-voting-db 1/1 Running 0 84s

    Once our pod is running, we can check to make sure everything is working by letting kubectl proxy network connections to our Pod running the application. If we get the voting web page, we'll know the application found the database and we can start voting!

    kubectl port-forward pod/azure-voting-app 8080:8080

    Azure voting website in a browser with three buttons, one for Dogs, one for Cats, and one for Reset.  The counter is Dogs - 0 and Cats - 0.

    When you are done voting, you can stop the port forwarding by using Control-C to break the command.

    Clean Up

    Let's clean up after ourselves and see if we can't get Kubernetes to help us keep our application running. We can use the same configuration files to ensure that Kubernetes only removes what we want removed.

    kubectl delete -f ./manifests/pod-app.yaml
    kubectl delete -f ./manifests/pod-db.yaml

    Summary - Pods

    A Pod is the most basic unit of work inside Kubernetes. Once the Pod is deleted, it's gone. That leads us to our next topic (and final topic for today.)

    Making the Pods Resilient with Deployments

    We've seen how easy it is to deploy a Pod and get our containers running on Nodes in our Kubernetes cluster. But there's a problem with that. Let's illustrate it.

    Breaking Stuff

    Setting Back Up

    First, let's redeploy our application environment. We'll start with our application container.

    kubectl apply -f ./manifests/pod-db.yaml
    kubectl get pod azure-voting-db -o jsonpath='{.status.podIP}'

    The second command will report out the new IP Address for our database container. Let's open ./manifests/pod-app.yaml and update the container IP to our new one.

    - name: DATABASE_SERVER
    value: YOUR_NEW_IP_HERE

    Then we can deploy the application with the information it needs to find its database. We'll also list out our pods to see what is running.

    kubectl apply -f ./manifests/pod-app.yaml
    kubectl get pods

    Feel free to look back and use the port forwarding trick to make sure your app is running if you'd like.

    Knocking It Down

    The first thing we'll try to break is our application pod. Let's delete it.

    kubectl delete pod azure-voting-app

    Then, we'll check our pod's status:

    kubectl get pods

    Which should show something like:

    azure-voting-app-rust ❯  kubectl get pods
    NAME READY STATUS RESTARTS AGE
    azure-voting-db 1/1 Running 0 50s

    We should be able to recreate our application pod deployment with no problem, since it has the current database IP address and nothing else depends on it.

    kubectl apply -f ./manifests/pod-app.yaml

    Again, feel free to do some fun port forwarding and check your site is running.

    Uncomfortable Truths

    Here's where it gets a bit stickier, what if we delete the database container?

    If we delete our database container and recreate it, it'll likely have a new IP address, which would force us to update our application configuration. We'll look at some solutions for these problems in the next three posts this week.

    Because our database problem is a bit tricky, we'll primarily focus on making our application layer more resilient and prepare our database layer for those other techniques over the next few days.

    Let's clean back up and look into making things more resilient.

    kubectl delete -f ./manifests/pod-app.yaml
    kubectl delete -f ./manifests/pod-db.yaml

    The Deployment

    One of the reasons you may want to use Kubernetes is it's ability to orchestrate workloads. Part of that orchestration includes being able to ensure that certain workloads are running (regardless of what Node they might be on).

    We saw that we could delete our application pod and then restart it from the manifest with little problem. It just meant that we had to run a command to restart it. We can use the Deployment in Kubernetes to tell the orchestrator to ensure we have our application pod running.

    The Deployment also can encompass a lot of extra configuration - controlling how many containers of a particular type should be running, how upgrades of container images should proceed, and more.

    Creating the Deployment

    First, we'll create a Deployment for our database. We'll use a technique similar to what we did for the Pod, with just a bit of difference.

    kubectl create deployment azure-voting-db `
    --image "postgres:15.0-alpine" `
    --port 5432 `
    --output yaml `
    --dry-run=client > manifests/deployment-db.yaml

    Unlike our Pod definition creation, we can't pass in environment variable configuration from the command line. We'll have to edit the YAML file to add that.

    So, let's open ./manifests/deployment-db.yaml in our editor and add the following in the spec/containers configuration.

            env:
    - name: POSTGRES_PASSWORD
    value: "mypassword"

    Your file should look like this deployment-db.yaml.

    Once we have our configuration file updated, we can deploy our database container image.

    kubectl apply -f ./manifests/deployment-db.yaml

    For our application, we'll use the same technique.

    kubectl create deployment azure-voting-app `
    --image "$AcrName.azurecr.io/cnny2023/azure-voting-app-rust:$BuildTag" `
    --port 8080 `
    --output yaml `
    --dry-run=client > manifests/deployment-app.yaml

    Next, we'll need to add an environment variable to the generated configuration. We'll also need the new IP address for the database deployment.

    Previously, we named the pod and were able to ask for the IP address with kubectl and a bit of JSONPath. Now, the deployment created the pod for us, so there's a bit of random in the naming. Check out:

    kubectl get pods

    Should return something like:

    azure-voting-app-rust ❯  kubectl get pods
    NAME READY STATUS RESTARTS AGE
    azure-voting-db-686d758fbf-8jnq8 1/1 Running 0 7s

    We can either ask for the IP with the new pod name, or we can use a selector to find our desired pod.

    kubectl get pod --selector app=azure-voting-db -o jsonpath='{.items[0].status.podIP}'

    Now, we can update our application deployment configuration file with:

            env:
    - name: DATABASE_SERVER
    value: YOUR_NEW_IP_HERE
    - name: DATABASE_PASSWORD
    value: mypassword

    Your file should look like this deployment-app.yaml (but with IPs and image names matching your environment).

    After we save those changes, we can deploy our application.

    kubectl apply -f ./manifests/deployment-app.yaml

    Let's test the resilience of our app now. First, we'll delete the pod running our application, then we'll check to make sure Kubernetes restarted our application pod.

    kubectl get pods
    azure-voting-app-rust ❯  kubectl get pods
    NAME READY STATUS RESTARTS AGE
    azure-voting-app-56c9ccc89d-skv7x 1/1 Running 0 71s
    azure-voting-db-686d758fbf-8jnq8 1/1 Running 0 12m
    kubectl delete pod azure-voting-app-56c9ccc89d-skv7x
    kubectl get pods
    azure-voting-app-rust ❯  kubectl delete pod azure-voting-app-56c9ccc89d-skv7x
    >> kubectl get pods
    pod "azure-voting-app-56c9ccc89d-skv7x" deleted
    NAME READY STATUS RESTARTS AGE
    azure-voting-app-56c9ccc89d-2b5mx 1/1 Running 0 2s
    azure-voting-db-686d758fbf-8jnq8 1/1 Running 0 14m
    info

    Your Pods will likely have different identifiers at the end, so adjust your commands to match the names in your environment.

    As you can see, by the time the kubectl get pods command was run, Kubernetes had already spun up a new pod for the application container image. Thanks Kubernetes!

    Clean up

    Since we can't just delete the pods, we have to delete the deployments.

    kubectl delete -f ./manifests/deployment-app.yaml
    kubectl delete -f ./manifests/deployment-db.yaml

    Summary - Deployments

    Deployments allow us to create more durable configuration for the workloads we deploy into Kubernetes. As we dig deeper, we'll discover more capabilities the deployments offer. Check out the Resources below for more.

    Exercise

    If you want to try these steps, head over to the source repository, fork it, clone it locally, and give it a spin!

    You can check your manifests against the manifests in the week2/day1 branch of the source repository.

    Resources

    Take the Cloud Skills Challenge!

    Enroll in the Cloud Skills Challenge!

    Don't miss out on this opportunity to level up your skills and stay ahead of the curve in the world of cloud native.

    Documentation

    Training

    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/30-daysofcloudnative/page/8/index.html b/cnny-2023/tags/30-daysofcloudnative/page/8/index.html index 48937834f0..d6d0abebdb 100644 --- a/cnny-2023/tags/30-daysofcloudnative/page/8/index.html +++ b/cnny-2023/tags/30-daysofcloudnative/page/8/index.html @@ -14,14 +14,14 @@ - - + +

    16 posts tagged with "30daysofcloudnative"

    View All Tags

    · 6 min read
    Josh Duffney

    Welcome to Day 3 of Week 2 of #CloudNativeNewYear!

    The theme for this week is Kubernetes fundamentals. Yesterday we talked about Services and Ingress. Today we'll explore the topic of passing configuration and secrets to our applications in Kubernetes with ConfigMaps and Secrets.

    Ask the Experts Thursday, February 9th at 9 AM PST
    Catch the Replay of the Live Demo

    Watch the recorded demo and conversation about this week's topics.

    We were live on YouTube walking through today's (and the rest of this week's) demos.

    What We'll Cover

    • Decouple configurations with ConfigMaps and Secerts
    • Passing Environment Data with ConfigMaps and Secrets
    • Conclusion

    Decouple configurations with ConfigMaps and Secerts

    A ConfigMap is a Kubernetes object that decouples configuration data from pod definitions. Kubernetes secerts are similar, but were designed to decouple senstive information.

    Separating the configuration and secerts from your application promotes better organization and security of your Kubernetes environment. It also enables you to share the same configuration and different secerts across multiple pods and deployments which can simplify scaling and management. Using ConfigMaps and Secerts in Kubernetes is a best practice that can help to improve the scalability, security, and maintainability of your cluster.

    By the end of this tutorial, you'll have added a Kubernetes ConfigMap and Secret to the Azure Voting deployment.

    Passing Environment Data with ConfigMaps and Secrets

    📝 NOTE: If you don't have an AKS cluster deployed, please head over to Azure-Samples/azure-voting-app-rust, clone the repo, and follow the instructions in the README.md to execute the Azure deployment and setup your kubectl context. Check out the first post this week for more on the environment setup.

    Create the ConfigMap

    ConfigMaps can be used in one of two ways; as environment variables or volumes.

    For this tutorial you'll use a ConfigMap to create three environment variables inside the pod; DATABASE_SERVER, FISRT_VALUE, and SECOND_VALUE. The DATABASE_SERVER provides part of connection string to a Postgres. FIRST_VALUE and SECOND_VALUE are configuration options that change what voting options the application presents to the users.

    Follow the below steps to create a new ConfigMap:

    1. Create a YAML file named 'config-map.yaml'. In this file, specify the environment variables for the application.

      apiVersion: v1
      kind: ConfigMap
      metadata:
      name: azure-voting-config
      data:
      DATABASE_SERVER: azure-voting-db
      FIRST_VALUE: "Go"
      SECOND_VALUE: "Rust"
    2. Create the config map in your Kubernetes cluster by running the following command:

      kubectl create -f config-map.yaml

    Create the Secret

    The deployment-db.yaml and deployment-app.yaml are Kubernetes manifests that deploy the Azure Voting App. Currently, those deployment manifests contain the environment variables POSTGRES_PASSWORD and DATABASE_PASSWORD with the value stored as plain text. Your task is to replace that environment variable with a Kubernetes Secret.

    Create a Secret running the following commands:

    1. Encode mypassword.

      echo -n "mypassword" | base64
    2. Create a YAML file named secret.yaml. In this file, add POSTGRES_PASSWORD as the key and the encoded value returned above under as the value in the data section.

      apiVersion: v1
      kind: Secret
      metadata:
      name: azure-voting-secret
      type: Opaque
      data:
      POSTGRES_PASSWORD: bXlwYXNzd29yZA==
    3. Create the Secret in your Kubernetes cluster by running the following command:

      kubectl create -f secret.yaml

    [!WARNING] base64 encoding is a simple and widely supported way to obscure plaintext data, it is not secure, as it can easily be decoded. If you want to store sensitive data like password, you should use a more secure method like encrypting with a Key Management Service (KMS) before storing it in the Secret.

    Modify the app deployment manifest

    With the ConfigMap and Secert both created the next step is to replace the environment variables provided in the application deployment manuscript with the values stored in the ConfigMap and the Secert.

    Complete the following steps to add the ConfigMap and Secert to the deployment mainifest:

    1. Open the Kubernetes manifest file deployment-app.yaml.

    2. In the containers section, add an envFrom section and upate the env section.

      envFrom:
      - configMapRef:
      name: azure-voting-config
      env:
      - name: DATABASE_PASSWORD
      valueFrom:
      secretKeyRef:
      name: azure-voting-secret
      key: POSTGRES_PASSWORD

      Using envFrom exposes all the values witin the ConfigMap as environment variables. Making it so you don't have to list them individually.

    3. Save the changes to the deployment manifest file.

    4. Apply the changes to the deployment by running the following command:

      kubectl apply -f deployment-app.yaml

    Modify the database deployment manifest

    Next, update the database deployment manifest and replace the plain text environment variable with the Kubernetes Secert.

    1. Open the deployment-db.yaml.

    2. To add the secret to the deployment, replace the env section with the following code:

      env:
      - name: POSTGRES_PASSWORD
      valueFrom:
      secretKeyRef:
      name: azure-voting-secret
      key: POSTGRES_PASSWORD
    3. Apply the updated manifest.

      kubectl apply -f deployment-db.yaml

    Verify the ConfigMap and output environment variables

    Verify that the ConfigMap was added to your deploy by running the following command:

    ```bash
    kubectl describe deployment azure-voting-app
    ```

    Browse the output until you find the envFrom section with the config map reference.

    You can also verify that the environment variables from the config map are being passed to the container by running the command kubectl exec -it <pod-name> -- printenv. This command will show you all the environment variables passed to the pod including the one from configmap.

    By following these steps, you will have successfully added a config map to the Azure Voting App Kubernetes deployment, and the environment variables defined in the config map will be passed to the container running in the pod.

    Verify the Secret and describe the deployment

    Once the secret has been created you can verify it exists by running the following command:

    kubectl get secrets

    You can view additional information, such as labels, annotations, type, and the Data by running kubectl describe:

    kubectl describe secret azure-voting-secret

    By default, the describe command doesn't output the encoded value, but if you output the results as JSON or YAML you'll be able to see the secret's encoded value.

     kubectl get secret azure-voting-secret -o json

    Conclusion

    In conclusion, using ConfigMaps and Secrets in Kubernetes can help to improve the scalability, security, and maintainability of your cluster. By decoupling configuration data and sensitive information from pod definitions, you can promote better organization and security in your Kubernetes environment. Additionally, separating these elements allows for sharing the same configuration and different secrets across multiple pods and deployments, simplifying scaling and management.

    Take the Cloud Skills Challenge!

    Enroll in the Cloud Skills Challenge!

    Don't miss out on this opportunity to level up your skills and stay ahead of the curve in the world of cloud native.

    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/30-daysofcloudnative/page/9/index.html b/cnny-2023/tags/30-daysofcloudnative/page/9/index.html index 23ec213b36..cb7d7263eb 100644 --- a/cnny-2023/tags/30-daysofcloudnative/page/9/index.html +++ b/cnny-2023/tags/30-daysofcloudnative/page/9/index.html @@ -14,13 +14,13 @@ - - + +

    16 posts tagged with "30daysofcloudnative"

    View All Tags

    · 10 min read
    Steven Murawski

    Welcome to Day 5 of Week 2 of #CloudNativeNewYear!

    The theme for this week is Kubernetes fundamentals. Yesterday we talked about adding persistent storage to our deployment. Today we'll explore the topic of scaling pods and nodes in our Kubernetes cluster.

    Ask the Experts Thursday, February 9th at 9 AM PST
    Catch the Replay of the Live Demo

    Watch the recorded demo and conversation about this week's topics.

    We were live on YouTube walking through today's (and the rest of this week's) demos.

    What We'll Cover

    • Scaling Our Application
    • Scaling Pods
    • Scaling Nodes
    • Exercise
    • Resources

    Scaling Our Application

    One of our primary reasons to use a service like Kubernetes to orchestrate our workloads is the ability to scale. We've approached scaling in a multitude of ways over the years, taking advantage of the ever-evolving levels of hardware and software. Kubernetes allows us to scale our units of work, Pods, and the Nodes they run on. This allows us to take advantage of both hardware and software scaling abilities. Kubernetes can help improve the utilization of existing hardware (by scheduling Pods on Nodes that have resource capacity). And, with the capabilities of virtualization and/or cloud hosting (or a bit more work, if you have a pool of physical machines), Kubernetes can expand (or contract) the number of Nodes capable of hosting Pods. Scaling is primarily driven by resource utilization, but can be triggered by a variety of other sources thanks to projects like Kubernetes Event-driven Autoscaling (KEDA).

    Scaling Pods

    Our first level of scaling is with our Pods. Earlier, when we worked on our deployment, we talked about how the Kubernetes would use the deployment configuration to ensure that we had the desired workloads running. One thing we didn't explore was running more than one instance of a pod. We can define a number of replicas of a pod in our Deployment.

    Manually Scale Pods

    So, if we wanted to define more pods right at the start (or at any point really), we could update our deployment configuration file with the number of replicas and apply that configuration file.

    spec:
    replicas: 5

    Or we could use the kubectl scale command to update the deployment with a number of pods to create.

    kubectl scale --replicas=5 deployment/azure-voting-app

    Both of these approaches modify the running configuration of our Kubernetes cluster and request that it ensure that we have that set number of replicas running. Because this was a manual change, the Kubernetes cluster won't automatically increase or decrease the number of pods. It'll just ensure that there are always the specified number of pods running.

    Autoscale Pods with the Horizontal Pod Autoscaler

    Another approach to scaling our pods is to allow the Horizontal Pod Autoscaler to help us scale in response to resources being used by the pod. This requires a bit more configuration up front. When we define our pod in our deployment, we need to include resource requests and limits. The requests help Kubernetes determine what nodes may have capacity for a new instance of a pod. The limit tells us where the node should cap utilization for a particular instance of a pod. For example, we'll update our deployment to request 0.25 CPU and set a limit of 0.5 CPU.

        spec:
    containers:
    - image: acrudavoz.azurecr.io/cnny2023/azure-voting-app-rust:ca4
    name: azure-voting-app-rust
    ports:
    - containerPort: 8080
    env:
    - name: DATABASE_URL
    value: postgres://postgres:mypassword@10.244.0.29
    resources:
    requests:
    cpu: 250m
    limits:
    cpu: 500m

    Now that we've given Kubernetes an allowed range and an idea of what free resources a node should have to place new pods, we can set up autoscaling. Because autoscaling is a persistent configuration, I like to define it in a configuration file that I'll be able to keep with the rest of my cluster configuration. We'll use the kubectl command to help us write the configuration file. We'll request that Kubernetes watch our pods and when the average CPU utilization if 50% of the requested usage (in our case if it's using more than 0.375 CPU across the current number of pods), it can grow the number of pods serving requests up to 10. If the utilization drops, Kubernetes will have the permission to deprovision pods down to the minimum (three in our example).

    kubectl autoscale deployment azure-voting-app --cpu-percent=50 --min=3 --max=10 -o YAML --dry-run=client

    Which would give us:

    apiVersion: autoscaling/v1
    kind: HorizontalPodAutoscaler
    metadata:
    creationTimestamp: null
    name: azure-voting-app
    spec:
    maxReplicas: 10
    minReplicas: 3
    scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: azure-voting-app
    targetCPUUtilizationPercentage: 50
    status:
    currentReplicas: 0
    desiredReplicas: 0

    So, how often does the autoscaler check the metrics being monitored? The autoscaler checks the Metrics API every 15 seconds, however the pods stats are only updated every 60 seconds. This means that an autoscale event may be evaluated about once a minute. Once an autoscale down event happens however, Kubernetes has a cooldown period to give the new pods a chance to distribute the workload and let the new metrics accumulate. There is no delay on scale up events.

    Application Architecture Considerations

    We've focused in this example on our front end, which is an easier scaling story. When we start talking about scaling our database layers or anything that deals with persistent storage or has primary/replica configuration requirements things get a bit more complicated. Some of these applications may have built-in leader election or could use sidecars to help use existing features in Kubernetes to perform that function. For shared storage scenarios, persistent volumes (or persistent volumes with Azure) can be of help, if the application knows how to play well with shared file access.

    Ultimately, you know your application architecture and, while Kubernetes may not have an exact match to how you are doing things today, the underlying capability is probably there under a different name. This abstraction allows you to more effectively use Kubernetes to operate a variety of workloads with the levels of controls you need.

    Scaling Nodes

    We've looked at how to scale our pods, but that assumes we have enough resources in our existing pool of nodes to accomodate those scaling requests. Kubernetes can also help scale our available nodes to ensure that our applications have the necessary resources to meet their performance requirements.

    Manually Scale Nodes

    Manually scaling nodes isn't a direct function of Kubernetes, so your operating environment instructions may vary. On Azure, it's pretty straight forward. Using the Azure CLI (or other tools), we can tell our AKS cluster to scale up or scale down the number of nodes in our node pool.

    First, we'll check out how many nodes we currently have in our working environment.

    kubectl get nodes

    This will show us

    azure-voting-app-rust ❯  kubectl get nodes
    NAME STATUS ROLES AGE VERSION
    aks-pool0-37917684-vmss000000 Ready agent 5d21h v1.24.6

    Then, we'll scale it up to three nodes.

    az aks scale --resource-group $ResourceGroup --name $AksName --node-count 3

    Then, we'll check out how many nodes we now have in our working environment.

    kubectl get nodes

    Which returns:

    azure-voting-app-rust ❯  kubectl get nodes
    NAME STATUS ROLES AGE VERSION
    aks-pool0-37917684-vmss000000 Ready agent 5d21h v1.24.6
    aks-pool0-37917684-vmss000001 Ready agent 5m27s v1.24.6
    aks-pool0-37917684-vmss000002 Ready agent 5m10s v1.24.6

    Autoscale Nodes with the Cluster Autoscaler

    Things get more interesting when we start working with the Cluster Autoscaler. The Cluster Autoscaler watches for the inability of Kubernetes to schedule the required number of pods due to resource constraints (and a few other criteria like affinity/anti-affinity). If there are insufficient resources available on the existing nodes, the autoscaler can provision new nodes into the nodepool. Likewise, the autoscaler watches to see if the existing pods could be consolidated to a smaller set of nodes and can remove excess nodes.

    Enabling the autoscaler is likewise an update that can be dependent on where and how your Kubernetes cluster is hosted. Azure makes it easy with a simple Azure CLI command.

    az aks update `
    --resource-group $ResourceGroup `
    --name $AksName `
    --update-cluster-autoscaler `
    --min-count 1 `
    --max-count 5

    There are a variety of settings that can be configured to tune how the autoscaler works.

    Scaling on Different Events

    CPU and memory utilization are the primary drivers for the Horizontal Pod Autoscaler, but those might not be the best measures as to when you might want to scale workloads. There are other options for scaling triggers and one of the more common plugins to help with that is the Kubernetes Event-driven Autoscaling (KEDA) project. The KEDA project makes it easy to plug in different event sources to help drive scaling. Find more information about using KEDA on AKS here.

    Exercise

    Let's try out the scaling configurations that we just walked through using our sample application. If you still have your environment from Day 1, you can use that.

    📝 NOTE: If you don't have an AKS cluster deployed, please head over to Azure-Samples/azure-voting-app-rust, clone the repo, and follow the instructions in the README.md to execute the Azure deployment and setup your kubectl context. Check out the first post this week for more on the environment setup.

    Configure Horizontal Pod Autoscaler

    • Edit ./manifests/deployment-app.yaml to include resource requests and limits.
            resources:
    requests:
    cpu: 250m
    limits:
    cpu: 500m
    • Apply the updated deployment configuration.
    kubectl apply -f ./manifests/deployment-app.yaml
    • Create the horizontal pod autoscaler configuration and apply it
    kubectl autoscale deployment azure-voting-app --cpu-percent=50 --min=3 --max=10 -o YAML --dry-run=client > ./manifests/scaler-app.yaml
    kubectl apply -f ./manifests/scaler-app.yaml
    • Check to see your pods scale out to the minimum.
    kubectl get pods

    Configure Cluster Autoscaler

    Configuring the basic behavior of the Cluster Autoscaler is a bit simpler. We just need to run the Azure CLI command to enable the autoscaler and define our lower and upper limits.

    • Check the current nodes available (should be 1).
    kubectl get nodes
    • Update the cluster to enable the autoscaler
    az aks update `
    --resource-group $ResourceGroup `
    --name $AksName `
    --update-cluster-autoscaler `
    --min-count 2 `
    --max-count 5
    • Check to see the current number of nodes (should be 2 now).
    kubectl get nodes

    Resources

    Take the Cloud Skills Challenge!

    Enroll in the Cloud Skills Challenge!

    Don't miss out on this opportunity to level up your skills and stay ahead of the curve in the world of cloud native.

    Documentation

    Training

    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/addons/index.html b/cnny-2023/tags/addons/index.html index ff419fea22..eb83376ca1 100644 --- a/cnny-2023/tags/addons/index.html +++ b/cnny-2023/tags/addons/index.html @@ -14,13 +14,13 @@ - - + +

    One post tagged with "addons"

    View All Tags

    · 4 min read
    Jorge Arteiro

    Welcome to Day 4 of Week 4 of #CloudNativeNewYear!

    The theme for this week is going further with Cloud Native. Yesterday we talked about Windows Containers. Today we'll explore addons and extensions available to Azure Kubernetes Services (AKS).

    What We'll Cover

    • Introduction
    • Add-ons
    • Extensions
    • Add-ons vs Extensions
    • Resources

    Introduction

    Azure Kubernetes Service (AKS) is a fully managed container orchestration service that makes it easy to deploy and manage containerized applications on Azure. AKS offers a number of features and capabilities, including the ability to extend its supported functionality through the use of add-ons and extensions.

    There are also integrations available from open-source projects and third parties, but they are not covered by the AKS support policy.

    Add-ons

    Add-ons provide a supported way to extend AKS. Installation, configuration and lifecycle are managed by AKS following pre-determine updates rules.

    As an example, let's enable Container Insights with the monitoring addon. on an existing AKS cluster using az aks enable-addons --addons CLI command

    az aks enable-addons \
    --name MyManagedCluster \
    --resource-group MyResourceGroup \
    --addons monitoring

    or you can use az aks create --enable-addons when creating new clusters

    az aks create \
    --name MyManagedCluster \
    --resource-group MyResourceGroup \
    --enable-addons monitoring

    The current available add-ons are:

    1. http_application_routing - Configure ingress with automatic public DNS name creation. Only recommended for development.
    2. monitoring - Container Insights monitoring.
    3. virtual-node - CNCF virtual nodes open source project.
    4. azure-policy - Azure Policy for AKS.
    5. ingress-appgw - Application Gateway Ingress Controller (AGIC).
    6. open-service-mesh - CNCF Open Service Mesh project.
    7. azure-keyvault-secrets-provider - Azure Key Vault Secrets Provider for Secret Store CSI Driver.
    8. web_application_routing - Managed NGINX ingress Controller.
    9. keda - CNCF Event-driven autoscaling project.

    For more details, get the updated list of AKS Add-ons here

    Extensions

    Cluster Extensions uses Helm charts and integrates with Azure Resource Manager (ARM) to provide installation and lifecycle management of capabilities on top of AKS.

    Extensions can be auto upgraded using minor versions, but it requires extra management and configuration. Using Scope parameter, it can be installed on the whole cluster or per namespace.

    AKS Extensions requires an Azure CLI extension to be installed. To add or update this CLI extension use the following commands:

    az extension add --name k8s-extension

    and to update an existing extension

    az extension update --name k8s-extension

    There are only 3 available extensions:

    1. Dapr - CNCF Dapr project.
    2. Azure ML - Integrate Azure Machine Learning with AKS to train, inference and manage ML models.
    3. Flux (GitOps) - CNCF Flux project integrated with AKS to enable cluster configuration and application deployment using GitOps.

    As an example, you can install Azure ML using the following command:

    az k8s-extension create \
    --name aml-compute --extension-type Microsoft.AzureML.Kubernetes \
    --scope cluster --cluster-name <clusterName> \
    --resource-group <resourceGroupName> \
    --cluster-type managedClusters \
    --configuration-settings enableInference=True allowInsecureConnections=True

    For more details, get the updated list of AKS Extensions here

    Add-ons vs Extensions

    AKS Add-ons brings an advantage of been fully managed by AKS itself, and AKS Extensions are more flexible and configurable but requires extra level of management.

    Add-ons are part of the AKS resource provider in the Azure API, and AKS Extensions are a separate resource provider on the Azure API.

    Resources

    It's not too late to sign up for and complete the Cloud Skills Challenge!
    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/aks/index.html b/cnny-2023/tags/aks/index.html index 2b3b41f765..3bee0f2d86 100644 --- a/cnny-2023/tags/aks/index.html +++ b/cnny-2023/tags/aks/index.html @@ -14,13 +14,13 @@ - - + +

    5 posts tagged with "aks"

    View All Tags

    · 11 min read
    Paul Yu

    Welcome to Day 2 of Week 2 of #CloudNativeNewYear!

    The theme for this week is #Kubernetes fundamentals. Yesterday we talked about how to deploy a containerized web app workload to Azure Kubernetes Service (AKS). Today we'll explore the topic of services and ingress and walk through the steps of making our containers accessible both internally as well as over the internet so that you can share it with the world 😊

    Ask the Experts Thursday, February 9th at 9 AM PST
    Catch the Replay of the Live Demo

    Watch the recorded demo and conversation about this week's topics.

    We were live on YouTube walking through today's (and the rest of this week's) demos.

    What We'll Cover

    • Exposing Pods via Service
    • Exposing Services via Ingress
    • Takeaways
    • Resources

    Exposing Pods via Service

    There are a few ways to expose your pod in Kubernetes. One way is to take an imperative approach and use the kubectl expose command. This is probably the quickest way to achieve your goal but it isn't the best way. A better way to expose your pod by taking a declarative approach by creating a services manifest file and deploying it using the kubectl apply command.

    Don't worry if you are unsure of how to make this manifest, we'll use kubectl to help generate it.

    First, let's ensure we have the database deployed on our AKS cluster.

    📝 NOTE: If you don't have an AKS cluster deployed, please head over to Azure-Samples/azure-voting-app-rust, clone the repo, and follow the instructions in the README.md to execute the Azure deployment and setup your kubectl context. Check out the first post this week for more on the environment setup.

    kubectl apply -f ./manifests/deployment-db.yaml

    Next, let's deploy the application. If you are following along from yesterday's content, there isn't anything you need to change; however, if you are deploy the app from scratch, you'll need to modify the deployment-app.yaml manifest and update it with your image tag and database pod's IP address.

    kubectl apply -f ./manifests/deployment-app.yaml

    Now, let's expose the database using a service so that we can leverage Kubernetes' built-in service discovery to be able to reference it by name; not pod IP. Run the following command.

    kubectl expose deployment azure-voting-db \
    --port=5432 \
    --target-port=5432

    With the database exposed using service, we can update the app deployment manifest to use the service name instead of pod IP. This way, if the pod ever gets assigned a new IP, we don't have to worry about updating the IP each time and redeploying our web application. Kubernetes has internal service discovery mechanism in place that allows us to reference a service by its name.

    Let's make an update to the manifest. Replace the environment variable for DATABASE_SERVER with the following:

    - name: DATABASE_SERVER
    value: azure-voting-db

    Re-deploy the app with the updated configuration.

    kubectl apply -f ./manifests/deployment-app.yaml

    One service down, one to go. Run the following command to expose the web application.

    kubectl expose deployment azure-voting-app \
    --type=LoadBalancer \
    --port=80 \
    --target-port=8080

    Notice the --type argument has a value of LoadBalancer. This service type is implemented by the cloud-controller-manager which is part of the Kubernetes control plane. When using a managed Kubernetes cluster such as Azure Kubernetes Service, a public standard load balancer will be able to provisioned when the service type is set to LoadBalancer. The load balancer will also have a public IP assigned which will make your deployment publicly available.

    Kubernetes supports four service types:

    • ClusterIP: this is the default and limits service access to internal traffic within the cluster
    • NodePort: this assigns a port mapping on the node's IP address and allows traffic from the virtual network (outside the cluster)
    • LoadBalancer: as mentioned above, this creates a cloud-based load balancer
    • ExternalName: this is used in special case scenarios where you want to map a service to an external DNS name

    📝 NOTE: When exposing a web application to the internet, allowing external users to connect to your Service directly is not the best approach. Instead, you should use an Ingress, which we'll cover in the next section.

    Now, let's confirm you can reach the web app from the internet. You can use the following command to print the URL to your terminal.

    echo "http://$(kubectl get service azure-voting-app -o jsonpath='{.status.loadBalancer.ingress[0].ip}')"

    Great! The kubectl expose command gets the job done, but as mentioned above, it is not the best method of exposing deployments. It is better to expose deployments declaratively using a service manifest, so let's delete the services and redeploy using manifests.

    kubectl delete service azure-voting-db azure-voting-app

    To use kubectl to generate our manifest file, we can use the same kubectl expose command that we ran earlier but this time, we'll include --output=yaml and --dry-run=client. This will instruct the command to output the manifest that would be sent to the kube-api server in YAML format to the terminal.

    Generate the manifest for the database service.

    kubectl expose deployment azure-voting-db \
    --type=ClusterIP \
    --port=5432 \
    --target-port=5432 \
    --output=yaml \
    --dry-run=client > ./manifests/service-db.yaml

    Generate the manifest for the application service.

    kubectl expose deployment azure-voting-app \
    --type=LoadBalancer \
    --port=80 \
    --target-port=8080 \
    --output=yaml \
    --dry-run=client > ./manifests/service-app.yaml

    The command above redirected the YAML output to your manifests directory. Here is what the web application service looks like.

    apiVersion: v1
    kind: Service
    metadata:
    creationTimestamp: null
    labels:
    app: azure-voting-app
    name: azure-voting-app
    spec:
    ports:
    - port: 80
    protocol: TCP
    targetPort: 8080
    selector:
    app: azure-voting-app
    type: LoadBalancer
    status:
    loadBalancer: {}

    💡 TIP: To view the schema of any api-resource in Kubernetes, you can use the kubectl explain command. In this case the kubectl explain service command will tell us exactly what each of these fields do.

    Re-deploy the services using the new service manifests.

    kubectl apply -f ./manifests/service-db.yaml -f ./manifests/service-app.yaml

    # You should see TYPE is set to LoadBalancer and the EXTERNAL-IP is set
    kubectl get service azure-voting-db azure-voting-app

    Confirm again that our application is accessible again. Run the following command to print the URL to the terminal.

    echo "http://$(kubectl get service azure-voting-app -o jsonpath='{.status.loadBalancer.ingress[0].ip}')"

    That was easy, right? We just exposed both of our pods using Kubernetes services. The database only needs to be accessible from within the cluster so ClusterIP is perfect for that. For the web application, we specified the type to be LoadBalancer so that we can access the application over the public internet.

    But wait... remember that if you want to expose web applications over the public internet, a Service with a public IP is not the best way; the better approach is to use an Ingress resource.

    Exposing Services via Ingress

    If you read through the Kubernetes documentation on Ingress you will see a diagram that depicts the Ingress sitting in front of the Service resource with a routing rule between it. In order to use Ingress, you need to deploy an Ingress Controller and it can be configured with many routing rules to forward traffic to one or many backend services. So effectively, an Ingress is a load balancer for your Services.

    With that said, we no longer need a service type of LoadBalancer since the service does not need to be accessible from the internet. It only needs to be accessible from the Ingress Controller (internal to the cluster) so we can change the service type to ClusterIP.

    Update your service.yaml file to look like this:

    apiVersion: v1
    kind: Service
    metadata:
    creationTimestamp: null
    labels:
    app: azure-voting-app
    name: azure-voting-app
    spec:
    ports:
    - port: 80
    protocol: TCP
    targetPort: 8080
    selector:
    app: azure-voting-app

    📝 NOTE: The default service type is ClusterIP so we can omit the type altogether.

    Re-apply the app service manifest.

    kubectl apply -f ./manifests/service-app.yaml

    # You should see TYPE set to ClusterIP and EXTERNAL-IP set to <none>
    kubectl get service azure-voting-app

    Next, we need to install an Ingress Controller. There are quite a few options, and the Kubernetes-maintained NGINX Ingress Controller is commonly deployed.

    You could install this manually by following these instructions, but if you do that you'll be responsible for maintaining and supporting the resource.

    I like to take advantage of free maintenance and support when I can get it, so I'll opt to use the Web Application Routing add-on for AKS.

    💡 TIP: Whenever you install an AKS add-on, it will be maintained and fully supported by Azure Support.

    Enable the web application routing add-on in our AKS cluster with the following command.

    az aks addon enable \
    --name <YOUR_AKS_NAME> \
    --resource-group <YOUR_AKS_RESOURCE_GROUP>
    --addon web_application_routing

    ⚠️ WARNING: This command can take a few minutes to complete

    Now, let's use the same approach we took in creating our service to create our Ingress resource. Run the following command to generate the Ingress manifest.

    kubectl create ingress azure-voting-app \
    --class=webapprouting.kubernetes.azure.com \
    --rule="/*=azure-voting-app:80" \
    --output yaml \
    --dry-run=client > ./manifests/ingress.yaml

    The --class=webapprouting.kubernetes.azure.com option activates the AKS web application routing add-on. This AKS add-on can also integrate with other Azure services such as Azure DNS and Azure Key Vault for TLS certificate management and this special class makes it all work.

    The --rule="/*=azure-voting-app:80" option looks confusing but we can use kubectl again to help us understand how to format the value for the option.

    kubectl create ingress --help

    In the output you will see the following:

    --rule=[]:
    Rule in format host/path=service:port[,tls=secretname]. Paths containing the leading character '*' are
    considered pathType=Prefix. tls argument is optional.

    It expects a host and path separated by a forward-slash, then expects the backend service name and port separated by a colon. We're not using a hostname for this demo so we can omit it. For the path, an asterisk is used to specify a wildcard path prefix.

    So, the value of /*=azure-voting-app:80 creates a routing rule for all paths following the domain (or in our case since we don't have a hostname specified, the IP) to route traffic to our azure-voting-app backend service on port 80.

    📝 NOTE: Configuring the hostname and TLS is outside the scope of this demo but please visit this URL https://bit.ly/aks-webapp-routing for an in-depth hands-on lab centered around Web Application Routing on AKS.

    Your ingress.yaml file should look like this:

    apiVersion: networking.k8s.io/v1
    kind: Ingress
    metadata:
    creationTimestamp: null
    name: azure-voting-app
    spec:
    ingressClassName: webapprouting.kubernetes.azure.com
    rules:
    - http:
    paths:
    - backend:
    service:
    name: azure-voting-app
    port:
    number: 80
    path: /
    pathType: Prefix
    status:
    loadBalancer: {}

    Apply the app ingress manifest.

    kubectl apply -f ./manifests/ingress.yaml

    Validate the web application is available from the internet again. You can run the following command to print the URL to the terminal.

    echo "http://$(kubectl get ingress azure-voting-app -o jsonpath='{.status.loadBalancer.ingress[0].ip}')"

    Takeaways

    Exposing your applications both internally and externally can be easily achieved using Service and Ingress resources respectively. If your service is HTTP or HTTPS based and needs to be accessible from outsie the cluster, use Ingress with an internal Service (i.e., ClusterIP or NodePort); otherwise, use the Service resource. If your TCP-based Service needs to be publicly accessible, you set the type to LoadBalancer to expose a public IP for it. To learn more about these resources, please visit the links listed below.

    Lastly, if you are unsure how to begin writing your service manifest, you can use kubectl and have it do most of the work for you 🥳

    Resources

    Take the Cloud Skills Challenge!

    Enroll in the Cloud Skills Challenge!

    Don't miss out on this opportunity to level up your skills and stay ahead of the curve in the world of cloud native.

    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/aks/page/2/index.html b/cnny-2023/tags/aks/page/2/index.html index bdfe884ab2..2297843e68 100644 --- a/cnny-2023/tags/aks/page/2/index.html +++ b/cnny-2023/tags/aks/page/2/index.html @@ -14,13 +14,13 @@ - - + +

    5 posts tagged with "aks"

    View All Tags

    · 8 min read
    Paul Yu

    Welcome to Day 4 of Week 2 of #CloudNativeNewYear!

    The theme for this week is Kubernetes fundamentals. Yesterday we talked about how to set app configurations and secrets at runtime using Kubernetes ConfigMaps and Secrets. Today we'll explore the topic of persistent storage on Kubernetes and show you can leverage Persistent Volumes and Persistent Volume Claims to ensure your PostgreSQL data can survive container restarts.

    Ask the Experts Thursday, February 9th at 9 AM PST
    Catch the Replay of the Live Demo

    Watch the recorded demo and conversation about this week's topics.

    We were live on YouTube walking through today's (and the rest of this week's) demos.

    What We'll Cover

    • Containers are ephemeral
    • Persistent storage on Kubernetes
    • Persistent storage on AKS
    • Takeaways
    • Resources

    Containers are ephemeral

    In our sample application, the frontend UI writes vote values to a backend PostgreSQL database. By default the database container stores its data on the container's local file system, so there will be data loss when the pod is re-deployed or crashes as containers are meant to start with a clean slate each time.

    Let's re-deploy our sample app and experience the problem first hand.

    📝 NOTE: If you don't have an AKS cluster deployed, please head over to Azure-Samples/azure-voting-app-rust, clone the repo, and follow the instructions in the README.md to execute the Azure deployment and setup your kubectl context. Check out the first post this week for more on the environment setup.

    kubectl apply -f ./manifests

    Wait for the azure-voting-app service to be assigned a public IP then browse to the website and submit some votes. Use the command below to print the URL to the terminal.

    echo "http://$(kubectl get ingress azure-voting-app -o jsonpath='{.status.loadBalancer.ingress[0].ip}')"

    Now, let's delete the pods and watch Kubernetes do what it does best... that is, re-schedule pods.

    # wait for the pod to come up then ctrl+c to stop watching
    kubectl delete --all pod --wait=false && kubectl get po -w

    Once the pods have been recovered, reload the website and confirm the vote tally has been reset to zero.

    We need to fix this so that the data outlives the container.

    Persistent storage on Kubernetes

    In order for application data to survive crashes and restarts, you must implement Persistent Volumes and Persistent Volume Claims.

    A persistent volume represents storage that is available to the cluster. Storage volumes can be provisioned manually by an administrator or dynamically using Container Storage Interface (CSI) and storage classes, which includes information on how to provision CSI volumes.

    When a user needs to add persistent storage to their application, a persistent volume claim is made to allocate chunks of storage from the volume. This "claim" includes things like volume mode (e.g., file system or block storage), the amount of storage to allocate, the access mode, and optionally a storage class. Once a persistent volume claim has been deployed, users can add the volume to the pod and mount it in a container.

    In the next section, we'll demonstrate how to enable persistent storage on AKS.

    Persistent storage on AKS

    With AKS, CSI drivers and storage classes are pre-deployed into your cluster. This allows you to natively use Azure Disks, Azure Files, and Azure Blob Storage as persistent volumes. You can either bring your own Azure storage account and use it with AKS or have AKS provision an Azure storage account for you.

    To view the Storage CSI drivers that have been enabled in your AKS cluster, run the following command.

    az aks show \
    --name <YOUR_AKS_NAME> \
    --resource-group <YOUR_AKS_RESOURCE_GROUP> \
    --query storageProfile

    You should see output that looks like this.

    {
    "blobCsiDriver": null,
    "diskCsiDriver": {
    "enabled": true,
    "version": "v1"
    },
    "fileCsiDriver": {
    "enabled": true
    },
    "snapshotController": {
    "enabled": true
    }
    }

    To view the storage classes that have been installed in your cluster, run the following command.

    kubectl get storageclass

    Workload requirements will dictate which CSI driver and storage class you will need to use.

    If you need block storage, then you should use the blobCsiDriver. The driver may not be enabled by default but you can enable it by following instructions which can be found in the Resources section below.

    If you need file storage you should leverage either diskCsiDriver or fileCsiDriver. The decision between these two boils down to whether or not you need to have the underlying storage accessible by one pod or multiple pods. It is important to note that diskCsiDriver currently supports access from a single pod only. Therefore, if you need data to be accessible by multiple pods at the same time, then you should opt for fileCsiDriver.

    For our PostgreSQL deployment, we'll use the diskCsiDriver and have AKS create an Azure Disk resource for us. There is no need to create a PV resource, all we need to do to is create a PVC using the managed-csi-premium storage class.

    Run the following command to create the PVC.

    kubectl apply -f - <<EOF            
    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
    name: pvc-azuredisk
    spec:
    accessModes:
    - ReadWriteOnce
    resources:
    requests:
    storage: 10Gi
    storageClassName: managed-csi-premium
    EOF

    When you check the PVC resource, you'll notice the STATUS is set to Pending. It will be set to Bound once the volume is mounted in the PostgreSQL container.

    kubectl get persistentvolumeclaim

    Let's delete the azure-voting-db deployment.

    kubectl delete deploy azure-voting-db

    Next, we need to apply an updated deployment manifest which includes our PVC.

    kubectl apply -f - <<EOF
    apiVersion: apps/v1
    kind: Deployment
    metadata:
    creationTimestamp: null
    labels:
    app: azure-voting-db
    name: azure-voting-db
    spec:
    replicas: 1
    selector:
    matchLabels:
    app: azure-voting-db
    strategy: {}
    template:
    metadata:
    creationTimestamp: null
    labels:
    app: azure-voting-db
    spec:
    containers:
    - image: postgres:15.0-alpine
    name: postgres
    ports:
    - containerPort: 5432
    env:
    - name: POSTGRES_PASSWORD
    valueFrom:
    secretKeyRef:
    name: azure-voting-secret
    key: POSTGRES_PASSWORD
    resources: {}
    volumeMounts:
    - name: mypvc
    mountPath: "/var/lib/postgresql/data"
    subPath: "data"
    volumes:
    - name: mypvc
    persistentVolumeClaim:
    claimName: pvc-azuredisk
    EOF

    In the manifest above, you'll see that we are mounting a new volume called mypvc (the name can be whatever you want) in the pod which points to a PVC named pvc-azuredisk. With the volume in place, we can mount it in the container by referencing the name of the volume mypvc and setting the mount path to /var/lib/postgresql/data (which is the default path).

    💡 IMPORTANT: When mounting a volume into a non-empty subdirectory, you must add subPath to the volume mount and point it to a subdirectory in the volume rather than mounting at root. In our case, when Azure Disk is formatted, it leaves a lost+found directory as documented here.

    Watch the pods and wait for the STATUS to show Running and the pod's READY status shows 1/1.

    # wait for the pod to come up then ctrl+c to stop watching
    kubectl get po -w

    Verify that the STATUS of the PVC is now set to Bound

    kubectl get persistentvolumeclaim

    With the new database container running, let's restart the application pod, wait for the pod's READY status to show 1/1, then head back over to our web browser and submit a few votes.

    kubectl delete pod -lapp=azure-voting-app --wait=false && kubectl get po -lapp=azure-voting-app -w

    Now the moment of truth... let's rip out the pods again, wait for the pods to be re-scheduled, and confirm our vote counts remain in tact.

    kubectl delete --all pod --wait=false && kubectl get po -w

    If you navigate back to the website, you'll find the vote are still there 🎉

    Takeaways

    By design, containers are meant to be ephemeral and stateless workloads are ideal on Kubernetes. However, there will come a time when your data needs to outlive the container. To persist data in your Kubernetes workloads, you need to leverage PV, PVC, and optionally storage classes. In our demo scenario, we leveraged CSI drivers built into AKS and created a PVC using pre-installed storage classes. From there, we updated the database deployment to mount the PVC in the container and AKS did the rest of the work in provisioning the underlying Azure Disk. If the built-in storage classes does not fit your needs; for example, you need to change the ReclaimPolicy or change the SKU for the Azure resource, then you can create your own custom storage class and configure it just the way you need it 😊

    We'll revisit this topic again next week but in the meantime, check out some of the resources listed below to learn more.

    See you in the next post!

    Resources

    Take the Cloud Skills Challenge!

    Enroll in the Cloud Skills Challenge!

    Don't miss out on this opportunity to level up your skills and stay ahead of the curve in the world of cloud native.

    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/aks/page/3/index.html b/cnny-2023/tags/aks/page/3/index.html index ff2e8052e9..6781995ae7 100644 --- a/cnny-2023/tags/aks/page/3/index.html +++ b/cnny-2023/tags/aks/page/3/index.html @@ -14,13 +14,13 @@ - - + +

    5 posts tagged with "aks"

    View All Tags

    · 12 min read
    Paul Yu

    Welcome to Day 2 of Week 3 of #CloudNativeNewYear!

    The theme for this week is Bringing Your Application to Kubernetes. Yesterday we talked about getting an existing application running in Kubernetes with a full pipeline in GitHub Actions. Today we'll evaluate our sample application's configuration, storage, and networking requirements and implement using Kubernetes and Azure resources.

    Ask the Experts Thursday, February 9th at 9 AM PST
    Friday, February 10th at 11 AM PST

    Watch the recorded demo and conversation about this week's topics.

    We were live on YouTube walking through today's (and the rest of this week's) demos. Join us Friday, February 10th and bring your questions!

    What We'll Cover

    • Gather requirements
    • Implement environment variables using ConfigMaps
    • Implement persistent volumes using Azure Files
    • Implement secrets using Azure Key Vault
    • Re-package deployments
    • Conclusion
    • Resources
    caution

    Before you begin, make sure you've gone through yesterday's post to set up your AKS cluster.

    Gather requirements

    The eShopOnWeb application is written in .NET 7 and has two major pieces of functionality. The web UI is where customers can browse and shop. The web UI also includes an admin portal for managing the product catalog. This admin portal, is packaged as a WebAssembly application and relies on a separate REST API service. Both the web UI and the REST API connect to the same SQL Server container.

    Looking through the source code which can be found here we can identify requirements for configs, persistent storage, and secrets.

    Database server

    • Need to store the password for the sa account as a secure secret
    • Need persistent storage volume for data directory
    • Need to inject environment variables for SQL Server license type and EULA acceptance

    Web UI and REST API service

    • Need to store database connection string as a secure secret
    • Need to inject ASP.NET environment variables to override app settings
    • Need persistent storage volume for ASP.NET key storage

    Implement environment variables using ConfigMaps

    ConfigMaps are relatively straight-forward to create. If you were following along with the examples last week, this should be review 😉

    Create a ConfigMap to store database environment variables.

    kubectl apply -f - <<EOF
    apiVersion: v1
    kind: ConfigMap
    metadata:
    name: mssql-settings
    data:
    MSSQL_PID: Developer
    ACCEPT_EULA: "Y"
    EOF

    Create another ConfigMap to store ASP.NET environment variables.

    kubectl apply -f - <<EOF
    apiVersion: v1
    kind: ConfigMap
    metadata:
    name: aspnet-settings
    data:
    ASPNETCORE_ENVIRONMENT: Development
    EOF

    Implement persistent volumes using Azure Files

    Similar to last week, we'll take advantage of storage classes built into AKS. For our SQL Server data, we'll use the azurefile-csi-premium storage class and leverage an Azure Files resource as our PersistentVolume.

    Create a PersistentVolumeClaim (PVC) for persisting SQL Server data.

    kubectl apply -f - <<EOF
    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
    name: mssql-data
    spec:
    accessModes:
    - ReadWriteMany
    storageClassName: azurefile-csi-premium
    resources:
    requests:
    storage: 5Gi
    EOF

    Create another PVC for persisting ASP.NET data.

    kubectl apply -f - <<EOF
    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
    name: aspnet-data
    spec:
    accessModes:
    - ReadWriteMany
    storageClassName: azurefile-csi-premium
    resources:
    requests:
    storage: 5Gi
    EOF

    Implement secrets using Azure Key Vault

    It's a well known fact that Kubernetes secretes are not really secrets. They're just base64-encoded values and not secure, especially if malicious users have access to your Kubernetes cluster.

    In a production scenario, you will want to leverage an external vault like Azure Key Vault or HashiCorp Vault to encrypt and store secrets.

    With AKS, we can enable the Secrets Store CSI driver add-on which will allow us to leverage Azure Key Vault.

    # Set some variables
    RG_NAME=<YOUR_RESOURCE_GROUP_NAME>
    AKS_NAME=<YOUR_AKS_CLUSTER_NAME>
    ACR_NAME=<YOUR_ACR_NAME>

    az aks enable-addons \
    --addons azure-keyvault-secrets-provider \
    --name $AKS_NAME \
    --resource-group $RG_NAME

    With the add-on enabled, you should see aks-secrets-store-csi-driver and aks-secrets-store-provider-azure resources installed on each node in your Kubernetes cluster.

    Run the command below to verify.

    kubectl get pods \
    --namespace kube-system \
    --selector 'app in (secrets-store-csi-driver, secrets-store-provider-azure)'

    The Secrets Store CSI driver allows us to use secret stores via Container Storage Interface (CSI) volumes. This provider offers capabilities such as mounting and syncing between the secure vault and Kubernetes Secrets. On AKS, the Azure Key Vault Provider for Secrets Store CSI Driver enables integration with Azure Key Vault.

    You may not have an Azure Key Vault created yet, so let's create one and add some secrets to it.

    AKV_NAME=$(az keyvault create \
    --name akv-eshop$RANDOM \
    --resource-group $RG_NAME \
    --query name -o tsv)

    # Database server password
    az keyvault secret set \
    --vault-name $AKV_NAME \
    --name mssql-password \
    --value "@someThingComplicated1234"

    # Catalog database connection string
    az keyvault secret set \
    --vault-name $AKV_NAME \
    --name mssql-connection-catalog \
    --value "Server=db;Database=Microsoft.eShopOnWeb.CatalogDb;User Id=sa;Password=@someThingComplicated1234;TrustServerCertificate=True;"

    # Identity database connection string
    az keyvault secret set \
    --vault-name $AKV_NAME \
    --name mssql-connection-identity \
    --value "Server=db;Database=Microsoft.eShopOnWeb.Identity;User Id=sa;Password=@someThingComplicated1234;TrustServerCertificate=True;"

    Pods authentication using Azure Workload Identity

    In order for our Pods to retrieve secrets from Azure Key Vault, we'll need to set up a way for the Pod to authenticate against Azure AD. This can be achieved by implementing the new Azure Workload Identity feature of AKS.

    info

    At the time of this writing, the workload identity feature of AKS is in Preview.

    The workload identity feature within AKS allows us to leverage native Kubernetes resources and link a Kubernetes ServiceAccount to an Azure Managed Identity to authenticate against Azure AD.

    For the authentication flow, our Kubernetes cluster will act as an Open ID Connect (OIDC) issuer and will be able issue identity tokens to ServiceAccounts which will be assigned to our Pods.

    The Azure Managed Identity will be granted permission to access secrets in our Azure Key Vault and with the ServiceAccount being assigned to our Pods, they will be able to retrieve our secrets.

    For more information on how the authentication mechanism all works, check out this doc.

    To implement all this, start by enabling the new preview feature for AKS.

    az feature register \
    --namespace "Microsoft.ContainerService" \
    --name "EnableWorkloadIdentityPreview"
    caution

    This can take several minutes to complete.

    Check the status and ensure the state shows Regestered before moving forward.

    az feature show \
    --namespace "Microsoft.ContainerService" \
    --name "EnableWorkloadIdentityPreview"

    Update your AKS cluster to enable the workload identity feature and enable the OIDC issuer endpoint.

    az aks update \
    --name $AKS_NAME \
    --resource-group $RG_NAME \
    --enable-workload-identity \
    --enable-oidc-issuer

    Create an Azure Managed Identity and retrieve its client ID.

    MANAGED_IDENTITY_CLIENT_ID=$(az identity create \
    --name aks-workload-identity \
    --resource-group $RG_NAME \
    --subscription $(az account show --query id -o tsv) \
    --query 'clientId' -o tsv)

    Create the Kubernetes ServiceAccount.

    # Set namespace (this must align with the namespace that your app is deployed into)
    SERVICE_ACCOUNT_NAMESPACE=default

    # Set the service account name
    SERVICE_ACCOUNT_NAME=eshop-serviceaccount

    # Create the service account
    kubectl apply -f - <<EOF
    apiVersion: v1
    kind: ServiceAccount
    metadata:
    annotations:
    azure.workload.identity/client-id: ${MANAGED_IDENTITY_CLIENT_ID}
    labels:
    azure.workload.identity/use: "true"
    name: ${SERVICE_ACCOUNT_NAME}
    namespace: ${SERVICE_ACCOUNT_NAMESPACE}
    EOF
    info

    Note to enable this ServiceAccount to work with Azure Workload Identity, you must annotate the resource with azure.workload.identity/client-id, and add a label of azure.workload.identity/use: "true"

    That was a lot... Let's review what we just did.

    We have an Azure Managed Identity (object in Azure AD), an OIDC issuer URL (endpoint in our Kubernetes cluster), and a Kubernetes ServiceAccount.

    The next step is to "tie" these components together and establish a Federated Identity Credential so that Azure AD can trust authentication requests from your Kubernetes cluster.

    info

    This identity federation can be established between Azure AD any Kubernetes cluster; not just AKS 🤗

    To establish the federated credential, we'll need the OIDC issuer URL, and a subject which points to your Kubernetes ServiceAccount.

    # Get the OIDC issuer URL
    OIDC_ISSUER_URL=$(az aks show \
    --name $AKS_NAME \
    --resource-group $RG_NAME \
    --query "oidcIssuerProfile.issuerUrl" -o tsv)

    # Set the subject name using this format: `system:serviceaccount:<YOUR_SERVICE_ACCOUNT_NAMESPACE>:<YOUR_SERVICE_ACCOUNT_NAME>`
    SUBJECT=system:serviceaccount:$SERVICE_ACCOUNT_NAMESPACE:$SERVICE_ACCOUNT_NAME

    az identity federated-credential create \
    --name aks-federated-credential \
    --identity-name aks-workload-identity \
    --resource-group $RG_NAME \
    --issuer $OIDC_ISSUER_URL \
    --subject $SUBJECT

    With the authentication components set, we can now create a SecretProviderClass which includes details about the Azure Key Vault, the secrets to pull out from the vault, and identity used to access the vault.

    # Get the tenant id for the key vault
    TENANT_ID=$(az keyvault show \
    --name $AKV_NAME \
    --resource-group $RG_NAME \
    --query properties.tenantId -o tsv)

    # Create the secret provider for azure key vault
    kubectl apply -f - <<EOF
    apiVersion: secrets-store.csi.x-k8s.io/v1
    kind: SecretProviderClass
    metadata:
    name: eshop-azure-keyvault
    spec:
    provider: azure
    parameters:
    usePodIdentity: "false"
    useVMManagedIdentity: "false"
    clientID: "${MANAGED_IDENTITY_CLIENT_ID}"
    keyvaultName: "${AKV_NAME}"
    cloudName: ""
    objects: |
    array:
    - |
    objectName: mssql-password
    objectType: secret
    objectVersion: ""
    - |
    objectName: mssql-connection-catalog
    objectType: secret
    objectVersion: ""
    - |
    objectName: mssql-connection-identity
    objectType: secret
    objectVersion: ""
    tenantId: "${TENANT_ID}"
    secretObjects:
    - secretName: eshop-secrets
    type: Opaque
    data:
    - objectName: mssql-password
    key: mssql-password
    - objectName: mssql-connection-catalog
    key: mssql-connection-catalog
    - objectName: mssql-connection-identity
    key: mssql-connection-identity
    EOF

    Finally, lets grant the Azure Managed Identity permissions to retrieve secrets from the Azure Key Vault.

    az keyvault set-policy \
    --name $AKV_NAME \
    --secret-permissions get \
    --spn $MANAGED_IDENTITY_CLIENT_ID

    Re-package deployments

    Update your database deployment to load environment variables from our ConfigMap, attach the PVC and SecretProviderClass as volumes, mount the volumes into the Pod, and use the ServiceAccount to retrieve secrets.

    Additionally, you may notice the database Pod is set to use fsGroup:10001 as part of the securityContext. This is required as the MSSQL container runs using a non-root account called mssql and this account has the proper permissions to read/write data at the /var/opt/mssql mount path.

    kubectl apply -f - <<EOF
    apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: db
    labels:
    app: db
    spec:
    replicas: 1
    selector:
    matchLabels:
    app: db
    template:
    metadata:
    labels:
    app: db
    spec:
    securityContext:
    fsGroup: 10001
    serviceAccountName: ${SERVICE_ACCOUNT_NAME}
    containers:
    - name: db
    image: mcr.microsoft.com/mssql/server:2019-latest
    ports:
    - containerPort: 1433
    envFrom:
    - configMapRef:
    name: mssql-settings
    env:
    - name: MSSQL_SA_PASSWORD
    valueFrom:
    secretKeyRef:
    name: eshop-secrets
    key: mssql-password
    resources: {}
    volumeMounts:
    - name: mssqldb
    mountPath: /var/opt/mssql
    - name: eshop-secrets
    mountPath: "/mnt/secrets-store"
    readOnly: true
    volumes:
    - name: mssqldb
    persistentVolumeClaim:
    claimName: mssql-data
    - name: eshop-secrets
    csi:
    driver: secrets-store.csi.k8s.io
    readOnly: true
    volumeAttributes:
    secretProviderClass: eshop-azure-keyvault
    EOF

    We'll update the API and Web deployments in a similar way.

    # Set the image tag
    IMAGE_TAG=<YOUR_IMAGE_TAG>

    # API deployment
    kubectl apply -f - <<EOF
    apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: api
    labels:
    app: api
    spec:
    replicas: 1
    selector:
    matchLabels:
    app: api
    template:
    metadata:
    labels:
    app: api
    spec:
    serviceAccount: ${SERVICE_ACCOUNT_NAME}
    containers:
    - name: api
    image: ${ACR_NAME}.azurecr.io/api:${IMAGE_TAG}
    ports:
    - containerPort: 80
    envFrom:
    - configMapRef:
    name: aspnet-settings
    env:
    - name: ConnectionStrings__CatalogConnection
    valueFrom:
    secretKeyRef:
    name: eshop-secrets
    key: mssql-connection-catalog
    - name: ConnectionStrings__IdentityConnection
    valueFrom:
    secretKeyRef:
    name: eshop-secrets
    key: mssql-connection-identity
    resources: {}
    volumeMounts:
    - name: aspnet
    mountPath: ~/.aspnet/https:/root/.aspnet/https:ro
    - name: eshop-secrets
    mountPath: "/mnt/secrets-store"
    readOnly: true
    volumes:
    - name: aspnet
    persistentVolumeClaim:
    claimName: aspnet-data
    - name: eshop-secrets
    csi:
    driver: secrets-store.csi.k8s.io
    readOnly: true
    volumeAttributes:
    secretProviderClass: eshop-azure-keyvault
    EOF

    ## Web deployment
    kubectl apply -f - <<EOF
    apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: web
    labels:
    app: web
    spec:
    replicas: 1
    selector:
    matchLabels:
    app: web
    template:
    metadata:
    labels:
    app: web
    spec:
    serviceAccount: ${SERVICE_ACCOUNT_NAME}
    containers:
    - name: web
    image: ${ACR_NAME}.azurecr.io/web:${IMAGE_TAG}
    ports:
    - containerPort: 80
    envFrom:
    - configMapRef:
    name: aspnet-settings
    env:
    - name: ConnectionStrings__CatalogConnection
    valueFrom:
    secretKeyRef:
    name: eshop-secrets
    key: mssql-connection-catalog
    - name: ConnectionStrings__IdentityConnection
    valueFrom:
    secretKeyRef:
    name: eshop-secrets
    key: mssql-connection-identity
    resources: {}
    volumeMounts:
    - name: aspnet
    mountPath: ~/.aspnet/https:/root/.aspnet/https:ro
    - name: eshop-secrets
    mountPath: "/mnt/secrets-store"
    readOnly: true
    volumes:
    - name: aspnet
    persistentVolumeClaim:
    claimName: aspnet-data
    - name: eshop-secrets
    csi:
    driver: secrets-store.csi.k8s.io
    readOnly: true
    volumeAttributes:
    secretProviderClass: eshop-azure-keyvault
    EOF

    If all went well with your deployment updates, you should be able to browse to your website and buy some merchandise again 🥳

    echo "http://$(kubectl get service web -o jsonpath='{.status.loadBalancer.ingress[0].ip}')"

    Conclusion

    Although there is no visible changes on with our website, we've made a ton of changes on the Kubernetes backend to make this application much more secure and resilient.

    We used a combination of Kubernetes resources and AKS-specific features to achieve our goal of securing our secrets and ensuring data is not lost on container crashes and restarts.

    To learn more about the components we leveraged here today, checkout the resources and additional tutorials listed below.

    You can also find manifests with all the changes made in today's post in the Azure-Samples/eShopOnAKS repository.

    See you in the next post!

    Resources

    Take the Cloud Skills Challenge!

    Enroll in the Cloud Skills Challenge!

    Don't miss out on this opportunity to level up your skills and stay ahead of the curve in the world of cloud native.

    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/aks/page/4/index.html b/cnny-2023/tags/aks/page/4/index.html index ea9c7d5a83..e07f77aa1d 100644 --- a/cnny-2023/tags/aks/page/4/index.html +++ b/cnny-2023/tags/aks/page/4/index.html @@ -14,13 +14,13 @@ - - + +

    5 posts tagged with "aks"

    View All Tags

    · 10 min read
    Paul Yu

    Welcome to Day 3 of Week 3 of #CloudNativeNewYear!

    The theme for this week is Bringing Your Application to Kubernetes. Yesterday we added configuration, secrets, and storage to our app. Today we'll explore how to expose the eShopOnWeb app so that customers can reach it over the internet using a custom domain name and TLS.

    Ask the Experts Thursday, February 9th at 9 AM PST
    Friday, February 10th at 11 AM PST

    Watch the recorded demo and conversation about this week's topics.

    We were live on YouTube walking through today's (and the rest of this week's) demos. Join us Friday, February 10th and bring your questions!

    What We'll Cover

    • Gather requirements
    • Generate TLS certificate and store in Azure Key Vault
    • Implement custom DNS using Azure DNS
    • Enable Web Application Routing add-on for AKS
    • Implement Ingress for the web application
    • Conclusion
    • Resources

    Gather requirements

    Currently, our eShopOnWeb app has three Kubernetes services deployed:

    1. db exposed internally via ClusterIP
    2. api exposed externally via LoadBalancer
    3. web exposed externally via LoadBalancer

    As mentioned in my post last week, Services allow applications to communicate with each other using DNS names. Kubernetes has service discovery capabilities built-in that allows Pods to resolve Services simply by using their names.

    In the case of our api and web deployments, they can simply reach the database by calling its name. The service type of ClusterIP for the db can remain as-is since it only needs to be accessed by the api and web apps.

    On the other hand, api and web both need to be accessed over the public internet. Currently, these services are using service type LoadBalancer which tells AKS to provision an Azure Load Balancer with a public IP address. No one is going to remember the IP addresses, so we need to make the app more accessible by adding a custom domain name and securing it with a TLS certificate.

    Here's what we're going to need:

    • Custom domain name for our app
    • TLS certificate for the custom domain name
    • Routing rule to ensure requests with /api/ in the URL is routed to the backend REST API
    • Routing rule to ensure requests without /api/ in the URL is routing to the web UI

    Just like last week, we will use the Web Application Routing add-on for AKS. But this time, we'll integrate it with Azure DNS and Azure Key Vault to satisfy all of our requirements above.

    info

    At the time of this writing the add-on is still in Public Preview

    Generate TLS certificate and store in Azure Key Vault

    We deployed an Azure Key Vault yesterday to store secrets. We'll use it again to store a TLS certificate too.

    Let's create and export a self-signed certificate for the custom domain.

    DNS_NAME=eshoponweb$RANDOM.com
    openssl req -new -x509 -nodes -out web-tls.crt -keyout web-tls.key -subj "/CN=${DNS_NAME}" -addext "subjectAltName=DNS:${DNS_NAME}"
    openssl pkcs12 -export -in web-tls.crt -inkey web-tls.key -out web-tls.pfx -password pass:
    info

    For learning purposes we'll use a self-signed certificate and a fake custom domain name.

    To browse to the site using the fake domain, we'll mimic a DNS lookup by adding an entry to your host file which maps the public IP address assigned to the ingress controller to the custom domain.

    In a production scenario, you will need to have a real domain delegated to Azure DNS and a valid TLS certificate for the domain.

    Grab your Azure Key Vault name and set the value in a variable for later use.

    RESOURCE_GROUP=cnny-week3

    AKV_NAME=$(az resource list \
    --resource-group $RESOURCE_GROUP \
    --resource-type Microsoft.KeyVault/vaults \
    --query "[0].name" -o tsv)

    Grant yourself permissions to get, list, and import certificates.

    MY_USER_NAME=$(az account show --query user.name -o tsv)
    MY_USER_OBJECT_ID=$(az ad user show --id $MY_USER_NAME --query id -o tsv)

    az keyvault set-policy \
    --name $AKV_NAME \
    --object-id $MY_USER_OBJECT_ID \
    --certificate-permissions get list import

    Upload the TLS certificate to Azure Key Vault and grab its certificate URI.

    WEB_TLS_CERT_ID=$(az keyvault certificate import \
    --vault-name $AKV_NAME \
    --name web-tls \
    --file web-tls.pfx \
    --query id \
    --output tsv)

    Implement custom DNS with Azure DNS

    Create a custom domain for our application and grab its Azure resource id.

    DNS_ZONE_ID=$(az network dns zone create \
    --name $DNS_NAME \
    --resource-group $RESOURCE_GROUP \
    --query id \
    --output tsv)

    Enable Web Application Routing add-on for AKS

    As we enable the Web Application Routing add-on, we'll also pass in the Azure DNS Zone resource id which triggers the installation of the external-dns controller in your Kubernetes cluster. This controller will be able to write Azure DNS zone entries on your behalf as you deploy Ingress manifests.

    AKS_NAME=$(az resource list \
    --resource-group $RESOURCE_GROUP \
    --resource-type Microsoft.ContainerService/managedClusters \
    --query "[0].name" -o tsv)

    az aks enable-addons \
    --name $AKS_NAME \
    --resource-group $RESOURCE_GROUP \
    --addons web_application_routing \
    --dns-zone-resource-id=$DNS_ZONE_ID \
    --enable-secret-rotation

    The add-on will also deploy a new Azure Managed Identity which is used by the external-dns controller when writing Azure DNS zone entries. Currently, it does not have permission to do that, so let's grant it permission.

    # This is where resources are automatically deployed by AKS
    NODE_RESOURCE_GROUP=$(az aks show \
    --name $AKS_NAME \
    --resource-group $RESOURCE_GROUP \
    --query nodeResourceGroup -o tsv)

    # This is the managed identity created by the Web Application Routing add-on
    MANAGED_IDENTTIY_OBJECT_ID=$(az resource show \
    --name webapprouting-${AKS_NAME} \
    --resource-group $NODE_RESOURCE_GROUP \
    --resource-type Microsoft.ManagedIdentity/userAssignedIdentities \
    --query properties.principalId \
    --output tsv)

    # Grant the managed identity permissions to write DNS entries
    az role assignment create \
    --role "DNS Zone Contributor" \
    --assignee $MANAGED_IDENTTIY_OBJECT_ID \
    --scope $DNS_ZONE_ID

    The Azure Managed Identity will also be used to retrieve and rotate TLS certificates from Azure Key Vault. So we'll need to grant it permission for that too.

    az keyvault set-policy \
    --name $AKV_NAME \
    --object-id $MANAGED_IDENTTIY_OBJECT_ID \
    --secret-permissions get \
    --certificate-permissions get

    Implement Ingress for the web application

    Before we create a new Ingress manifest, let's update the existing services to use ClusterIP instead of LoadBalancer. With an Ingress in place, there is no reason why we need the Service resources to be accessible from outside the cluster. The new Ingress will be the only entrypoint for external users.

    We can use the kubectl patch command to update the services

    kubectl patch service api -p '{"spec": {"type": "ClusterIP"}}'
    kubectl patch service web -p '{"spec": {"type": "ClusterIP"}}'

    Deploy a new Ingress to place in front of the web Service. Notice there is a special annotations entry for kubernetes.azure.com/tls-cert-keyvault-uri which points back to our self-signed certificate that was uploaded to Azure Key Vault.

    kubectl apply -f - <<EOF
    apiVersion: networking.k8s.io/v1
    kind: Ingress
    metadata:
    annotations:
    kubernetes.azure.com/tls-cert-keyvault-uri: ${WEB_TLS_CERT_ID}
    name: web
    spec:
    ingressClassName: webapprouting.kubernetes.azure.com
    rules:
    - host: ${DNS_NAME}
    http:
    paths:
    - backend:
    service:
    name: web
    port:
    number: 80
    path: /
    pathType: Prefix
    - backend:
    service:
    name: api
    port:
    number: 80
    path: /api
    pathType: Prefix
    tls:
    - hosts:
    - ${DNS_NAME}
    secretName: web-tls
    EOF

    In our manifest above, we've also configured the Ingress route the traffic to either the web or api services based on the URL path requested. If the request URL includes /api/ then it will send traffic to the api backend service. Otherwise, it will send traffic to the web service.

    Within a few minutes, the external-dns controller will add an A record to Azure DNS which points to the Ingress resource's public IP. With the custom domain in place, we can simply browse using this domain name.

    info

    As mentioned above, since this is not a real domain name, we need to modify our host file to make it seem like our custom domain is resolving to the Ingress' public IP address.

    To get the ingress public IP, run the following:

    # Get the IP
    kubectl get ingress web -o jsonpath="{.status.loadBalancer.ingress[0].ip}"

    # Get the hostname
    kubectl get ingress web -o jsonpath="{.spec.tls[0].hosts[0]}"

    Next, open your host file and add an entry using the format <YOUR_PUBLIC_IP> <YOUR_CUSTOM_DOMAIN>. Below is an example of what it should look like.

    20.237.116.224 eshoponweb11265.com

    See this doc for more info on how to do this.

    When browsing to the website, you may be presented with a warning about the connection not being private. This is due to the fact that we are using a self-signed certificate. This is expected, so go ahead and proceed anyway to load up the page.

    Why is the Admin page broken?

    If you log in using the admin@microsoft.com account and browse to the Admin page, you'll notice no products are loaded on the page.

    This is because the admin page is built using Blazor and compiled as a WebAssembly application that runs in your browser. When the application was compiled, it packed the appsettings.Development.json file as an embedded resource. This file contains the base URL for the public API and it currently points to https://localhost:5099. Now that we have a domain name, we can update the base URL and point it to our custom domain.

    From the root of the eShopOnWeb repo, update the configuration file using a sed command.

    sed -i -e "s/localhost:5099/${DNS_NAME}/g" ./src/BlazorAdmin/wwwroot/appsettings.Development.json

    Rebuild and push the container to Azure Container Registry.

    # Grab the name of your Azure Container Registry
    ACR_NAME=$(az resource list \
    --resource-group $RESOURCE_GROUP \
    --resource-type Microsoft.ContainerRegistry/registries \
    --query "[0].name" -o tsv)

    # Invoke a build and publish job
    az acr build \
    --registry $ACR_NAME \
    --image $ACR_NAME.azurecr.io/web:v0.1.0 \
    --file ./src/Web/Dockerfile .

    Once the container build has completed, we can issue a kubectl patch command to quickly update the web deployment to test our change.

    kubectl patch deployment web -p "$(cat <<EOF
    {
    "spec": {
    "template": {
    "spec": {
    "containers": [
    {
    "name": "web",
    "image": "${ACR_NAME}.azurecr.io/web:v0.1.0"
    }
    ]
    }
    }
    }
    }
    EOF
    )"

    If all went well, you will be able to browse the admin page again and confirm product data is being loaded 🥳

    Conclusion

    The Web Application Routing add-on for AKS aims to streamline the process of exposing it to the public using the open-source NGINX Ingress Controller. With the add-on being managed by Azure, it natively integrates with other Azure services like Azure DNS and eliminates the need to manually create DNS entries. It can also integrate with Azure Key Vault to automatically pull in TLS certificates and rotate them as needed to further reduce operational overhead.

    We are one step closer to production and in the upcoming posts we'll further operationalize and secure our deployment, so stay tuned!

    In the meantime, check out the resources listed below for further reading.

    You can also find manifests with all the changes made in today's post in the Azure-Samples/eShopOnAKS repository.

    Resources

    Take the Cloud Skills Challenge!

    Enroll in the Cloud Skills Challenge!

    Don't miss out on this opportunity to level up your skills and stay ahead of the curve in the world of cloud native.

    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/aks/page/5/index.html b/cnny-2023/tags/aks/page/5/index.html index c1e832fb0a..0538ba2318 100644 --- a/cnny-2023/tags/aks/page/5/index.html +++ b/cnny-2023/tags/aks/page/5/index.html @@ -14,13 +14,13 @@ - - + +

    5 posts tagged with "aks"

    View All Tags

    · 6 min read
    Josh Duffney

    Welcome to Day 5 of Week 3 of #CloudNativeNewYear!

    The theme for this week is Bringing Your Application to Kubernetes. Yesterday we talked about debugging and instrumenting our application. Today we'll explore the topic of container image signing and secure supply chain.

    Ask the Experts Thursday, February 9th at 9 AM PST
    Friday, February 10th at 11 AM PST

    Watch the recorded demo and conversation about this week's topics.

    We were live on YouTube walking through today's (and the rest of this week's) demos. Join us Friday, February 10th and bring your questions!

    What We'll Cover

    • Introduction
    • Prerequisites
    • Create a digital signing certificate
    • Generate an Azure Container Registry Token
    • Set up Notation
    • Install the Notation Azure Key Vault Plugin
    • Add the signing Certificate to Notation
    • Sign Container Images
    • Conclusion

    Introduction

    The secure supply chain is a crucial aspect of software development, delivery, and deployment, and digital signing plays a critical role in this process.

    By using digital signatures to verify the authenticity and integrity of container images, organizations can improve the security of your software supply chain and reduce the risk of security breaches and data compromise.

    In this article, you'll learn how to use Notary, an open-source project hosted by the Cloud Native Computing Foundation (CNCF) to digitally sign container images stored on Azure Container Registry.

    Prerequisites

    To follow along, you'll need an instance of:

    Create a digital signing certificate

    A digital signing certificate is a certificate that is used to digitally sign and verify the authenticity and integrity of digital artifacts. Such documents, software, and of course container images.

    Before you can implement digital signatures, you must first create a digital signing certificate.

    Run the following command to generate the certificate:

    1. Create the policy file

      cat <<EOF > ./my_policy.json
      {
      "issuerParameters": {
      "certificateTransparency": null,
      "name": "Self"
      },
      "x509CertificateProperties": {
      "ekus": [
      "1.3.6.1.5.5.7.3.3"
      ],
      "key_usage": [
      "digitalSignature"
      ],
      "subject": "CN=${keySubjectName}",
      "validityInMonths": 12
      }
      }
      EOF

      The ekus and key usage of this certificate policy dictate that the certificate can only be used for digital signatures.

    2. Create the certificate in Azure Key Vault

      az keyvault certificate create --name $keyName --vault-name $keyVaultName --policy @my_policy.json

      Replace $keyName and $keyVaultName with your desired certificate name and Azure Key Vault instance name.

    Generate a Azure Container Registry token

    Azure Container Registry tokens are used to grant access to the contents of the registry. Tokens can be used for a variety of things such as pulling images, pushing images, or managing the registry.

    As part of the container image signing workflow, you'll need a token to authenticate the Notation CLI with your Azure Container Registry.

    Run the following command to generate an ACR token:

    az acr token create \
    --name $tokenName \
    --registry $registry \
    --scope-map _repositories_admin \
    --query 'credentials.passwords[0].value' \
    --only-show-errors \
    --output tsv

    Replace $tokenName with your name for the ACR token and $registry with the name of your ACR instance.

    Setup Notation

    Notation is the command-line interface for the CNCF Notary project. You'll use it to digitally sign the api and web container images for the eShopOnWeb application.

    Run the following commands to download and install the NotationCli:

    1. Open a terminal or command prompt window

    2. Download the Notary notation release

      curl -Lo notation.tar.gz https://github.com/notaryproject/notation/releases/download/v1.0.0-rc.1/notation_1.0.0-rc.1_linux_amd64.tar.gz > /dev/null 2>&1

      If you're not using Linux, you can find the releases here.

    3. Extract the contents of the notation.tar.gz

      tar xvzf notation.tar.gz > /dev/null 2>&1
    4. Copy the notation binary to the $HOME/bin directory

      cp ./notation $HOME/bin
    5. Add the $HOME/bin directory to the PATH environment variable

      export PATH="$HOME/bin:$PATH"
    6. Remove the downloaded files

      rm notation.tar.gz LICENSE
    7. Check the notation version

      notation --version

    Install the Notation Azure Key Vault plugin

    By design the NotationCli supports plugins that extend its digital signing capabilities to remote registries. And in order to sign your container images stored in Azure Container Registry, you'll need to install the Azure Key Vault plugin for Notation.

    Run the following commands to install the azure-kv plugin:

    1. Download the plugin

      curl -Lo notation-azure-kv.tar.gz \
      https://github.com/Azure/notation-azure-kv/releases/download/v0.5.0-rc.1/notation-azure-kv_0.5.0-rc.1_linux_amd64.tar.gz > /dev/null 2>&1

      Non-Linux releases can be found here.

    2. Extract to the plugin directory & delete download files

      tar xvzf notation-azure-kv.tar.gz -C ~/.config/notation/plugins/azure-kv notation-azure-kv > /dev/null 2>&

      rm -rf notation-azure-kv.tar.gz
    3. Verify the plugin was installed

      notation plugin ls

    Add the signing certificate to Notation

    Now that you have Notation and the Azure Key Vault plugin installed, add the certificate's keyId created above to Notation.

    1. Get the Certificate Key ID from Azure Key Vault

      az keyvault certificate show \
      --vault-name $keyVaultName \
      --name $keyName \
      --query "kid" --only-show-errors --output tsv

      Replace $keyVaultName and $keyName with the appropriate information.

    2. Add the Key ID to KMS using Notation

      notation key add --plugin azure-kv --id $keyID $keyName
    3. Check the key list

      notation key ls

    Sign Container Images

    At this point, all that's left is to sign the container images.

    Run the notation sign command to sign the api and web container images:

    notation sign $registry.azurecr.io/web:$tag \
    --username $tokenName \
    --password $tokenPassword

    notation sign $registry.azurecr.io/api:$tag \
    --username $tokenName \
    --password $tokenPassword

    Replace $registry, $tag, $tokenName, and $tokenPassword with the appropriate values. To improve security, use a SHA hash for the tag.

    NOTE: If you didn't take note of the token password, you can rerun the az acr token create command to generate a new password.

    Conclusion

    Digital signing plays a critical role in ensuring the security of software supply chains.

    By signing software components, organizations can verify the authenticity and integrity of software, helping to prevent unauthorized modifications, tampering, and malware.

    And if you want to take digital signing to a whole new level by using them to prevent the deployment of unsigned container images, check out the Ratify project on GitHub!

    Take the Cloud Skills Challenge!

    Enroll in the Cloud Skills Challenge!

    Don't miss out on this opportunity to level up your skills and stay ahead of the curve in the world of cloud native.

    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/ask-the-expert/index.html b/cnny-2023/tags/ask-the-expert/index.html index b6787837ae..61dc785123 100644 --- a/cnny-2023/tags/ask-the-expert/index.html +++ b/cnny-2023/tags/ask-the-expert/index.html @@ -14,13 +14,13 @@ - - + +

    16 posts tagged with "ask-the-expert"

    View All Tags

    · 4 min read
    Cory Skimming
    Devanshi Joshi
    Steven Murawski
    Nitya Narasimhan

    Welcome to the Kick-off Post for #30DaysOfCloudNative - one of the core initiatives within #CloudNativeNewYear! Over the next four weeks, join us as we take you from fundamentals to functional usage of Cloud-native technologies, one blog post at a time! Read on to learn a little bit about this initiative and what you can expect to learn from this journey!

    What We'll Cover


    Cloud-native New Year

    Welcome to Week 01 of 🥳 #CloudNativeNewYear ! Today, we kick off a full month of content and activities to skill you up on all things Cloud-native on Azure with content, events, and community interactions! Read on to learn about what we have planned!


    Explore our initiatives

    We have a number of initiatives planned for the month to help you learn and skill up on relevant technologies. Click on the links to visit the relevant pages for each.

    We'll go into more details about #30DaysOfCloudNative in this post - don't forget to subscribe to the blog to get daily posts delivered directly to your preferred feed reader!


    Register for events!

    What are 3 things you can do today, to jumpstart your learning journey?


    #30DaysOfCloudNative

    #30DaysOfCloudNative is a month-long series of daily blog posts grouped into 4 themed weeks - taking you from core concepts to end-to-end solution examples in 30 days. Each article will be short (5-8 mins reading time) and provide exercises and resources to help you reinforce learnings and take next steps.

    This series focuses on the Cloud-native On Azure learning journey in four stages, each building on the previous week to help you skill up in a beginner-friendly way:

    We have a tentative weekly-themed roadmap for the topics we hope to cover and will keep this updated as we go with links to actual articles as they get published.

    Week 1: FOCUS ON CLOUD-NATIVE FUNDAMENTALS

    Here's a sneak peek at the week 1 schedule. We'll start with a broad review of cloud-native fundamentals and walkthrough the core concepts of microservices, containers and Kubernetes.

    • Jan 23: Learn Core Concepts for Cloud-native
    • Jan 24: Container 101
    • Jan 25: Adopting Microservices with Kubernetes
    • Jan 26: Kubernetes 101
    • Jan 27: Exploring your Cloud Native Options

    Let's Get Started!

    Now you know everything! We hope you are as excited as we are to dive into a full month of active learning and doing! Don't forget to subscribe for updates in your favorite feed reader! And look out for our first Cloud-native Fundamentals post on January 23rd!


    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/ask-the-expert/page/10/index.html b/cnny-2023/tags/ask-the-expert/page/10/index.html index a811c38e00..b981d1426a 100644 --- a/cnny-2023/tags/ask-the-expert/page/10/index.html +++ b/cnny-2023/tags/ask-the-expert/page/10/index.html @@ -14,15 +14,15 @@ - - + +

    16 posts tagged with "ask-the-expert"

    View All Tags

    · 14 min read
    Steven Murawski

    Welcome to Day 1 of Week 3 of #CloudNativeNewYear!

    The theme for this week is Bringing Your Application to Kubernetes. Last we talked about Kubernetes Fundamentals. Today we'll explore getting an existing application running in Kubernetes with a full pipeline in GitHub Actions.

    Ask the Experts Thursday, February 9th at 9 AM PST
    Friday, February 10th at 11 AM PST

    Watch the recorded demo and conversation about this week's topics.

    We were live on YouTube walking through today's (and the rest of this week's) demos. Join us Friday, February 10th and bring your questions!

    What We'll Cover

    • Our Application
    • Adding Some Infrastructure as Code
    • Building and Publishing a Container Image
    • Deploying to Kubernetes
    • Summary
    • Resources

    Our Application

    This week we'll be taking an exisiting application - something similar to a typical line of business application - and setting it up to run in Kubernetes. Over the course of the week, we'll address different concerns. Today we'll focus on updating our CI/CD process to handle standing up (or validating that we have) an Azure Kubernetes Service (AKS) environment, building and publishing container images for our web site and API server, and getting those services running in Kubernetes.

    The application we'll be starting with is eShopOnWeb. This application has a web site and API which are backed by a SQL Server instance. It's built in .NET 7, so it's cross-platform.

    info

    For the enterprising among you, you may notice that there are a number of different eShopOn* variants on GitHub, including eShopOnContainers. We aren't using that example as it's more of an end state than a starting place. Afterwards, feel free to check out that example as what this solution could look like as a series of microservices.

    Adding Some Infrastructure as Code

    Just like last week, we need to stand up an AKS environment. This week, however, rather than running commands in our own shell, we'll set up GitHub Actions to do that for us.

    There is a LOT of plumbing in this section, but once it's set up, it'll make our lives a lot easier. This section ensures that we have an environment to deploy our application into configured the way we want. We can easily extend this to accomodate multiple environments or add additional microservices with minimal new effort.

    Federated Identity

    Setting up a federated identity will allow us a more securable and auditable way of accessing Azure from GitHub Actions. For more about setting up a federated identity, Microsoft Learn has the details on connecting GitHub Actions to Azure.

    Here, we'll just walk through the setup of the identity and configure GitHub to use that idenity to deploy our AKS environment and interact with our Azure Container Registry.

    The examples will use PowerShell, but a Bash version of the setup commands is available in the week3/day1 branch.

    Prerequisites

    To follow along, you'll need:

    • a GitHub account
    • an Azure Subscription
    • the Azure CLI
    • and the Git CLI.

    You'll need to fork the source repository under your GitHub user or organization where you can manage secrets and GitHub Actions.

    It would be helpful to have the GitHub CLI, but it's not required.

    Set Up Some Defaults

    You will need to update one or more of the variables (your user or organization, what branch you want to work off of, and possibly the Azure AD application name if there is a conflict).

    # Replace the gitHubOrganizationName value
    # with the user or organization you forked
    # the repository under.

    $githubOrganizationName = 'Azure-Samples'
    $githubRepositoryName = 'eShopOnAKS'
    $branchName = 'week3/day1'
    $applicationName = 'cnny-week3-day1'

    Create an Azure AD Application

    Next, we need to create an Azure AD application.

    # Create an Azure AD application
    $aksDeploymentApplication = New-AzADApplication -DisplayName $applicationName

    Set Up Federation for that Azure AD Application

    And configure that application to allow federated credential requests from our GitHub repository for a particular branch.

    # Create a federated identity credential for the application
    New-AzADAppFederatedCredential `
    -Name $applicationName `
    -ApplicationObjectId $aksDeploymentApplication.Id `
    -Issuer 'https://token.actions.githubusercontent.com' `
    -Audience 'api://AzureADTokenExchange' `
    -Subject "repo:$($githubOrganizationName)/$($githubRepositoryName):ref:refs/heads/$branchName"

    Create a Service Principal for the Azure AD Application

    Once the application has been created, we need a service principal tied to that application. The service principal can be granted rights in Azure.

    # Create a service principal for the application
    New-AzADServicePrincipal -AppId $($aksDeploymentApplication.AppId)

    Give that Service Principal Rights to Azure Resources

    Because our Bicep deployment exists at the subscription level and we are creating role assignments, we need to give it Owner rights. If we changed the scope of the deployment to just a resource group, we could apply more scoped permissions.

    $azureContext = Get-AzContext
    New-AzRoleAssignment `
    -ApplicationId $($aksDeploymentApplication.AppId) `
    -RoleDefinitionName Owner `
    -Scope $azureContext.Subscription.Id

    Add Secrets to GitHub Repository

    If you have the GitHub CLI, you can use that right from your shell to set the secrets needed.

    gh secret set AZURE_CLIENT_ID --body $aksDeploymentApplication.AppId
    gh secret set AZURE_TENANT_ID --body $azureContext.Tenant.Id
    gh secret set AZURE_SUBSCRIPTION_ID --body $azureContext.Subscription.Id

    Otherwise, you can create them through the web interface like I did in the Learn Live event below.

    info

    It may look like the whole video will play, but it'll stop after configuring the secrets in GitHub (after about 9 minutes)

    The video shows creating the Azure AD application, service principals, and configuring the federated identity in Azure AD and GitHub.

    Creating a Bicep Deployment

    Resuable Workflows

    We'll create our Bicep deployment in a reusable workflows. What are they? The previous link has the documentation or the video below has my colleague Brandon Martinez and I talking about them.

    This workflow is basically the same deployment we did in last week's series, just in GitHub Actions.

    Start by creating a file called deploy_aks.yml in the .github/workflows directory with the below contents.

    name: deploy

    on:
    workflow_call:
    inputs:
    resourceGroupName:
    required: true
    type: string
    secrets:
    AZURE_CLIENT_ID:
    required: true
    AZURE_TENANT_ID:
    required: true
    AZURE_SUBSCRIPTION_ID:
    required: true
    outputs:
    containerRegistryName:
    description: Container Registry Name
    value: ${{ jobs.deploy.outputs.containerRegistryName }}
    containerRegistryUrl:
    description: Container Registry Login Url
    value: ${{ jobs.deploy.outputs.containerRegistryUrl }}
    resourceGroupName:
    description: Resource Group Name
    value: ${{ jobs.deploy.outputs.resourceGroupName }}
    aksName:
    description: Azure Kubernetes Service Cluster Name
    value: ${{ jobs.deploy.outputs.aksName }}

    permissions:
    id-token: write
    contents: read

    jobs:
    validate:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    - uses: azure/login@v1
    name: Sign in to Azure
    with:
    client-id: ${{ secrets.AZURE_CLIENT_ID }}
    tenant-id: ${{ secrets.AZURE_TENANT_ID }}
    subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
    - uses: azure/arm-deploy@v1
    name: Run preflight validation
    with:
    deploymentName: ${{ github.run_number }}
    scope: subscription
    region: eastus
    template: ./deploy/main.bicep
    parameters: >
    resourceGroup=${{ inputs.resourceGroupName }}
    deploymentMode: Validate

    deploy:
    needs: validate
    runs-on: ubuntu-latest
    outputs:
    containerRegistryName: ${{ steps.deploy.outputs.acr_name }}
    containerRegistryUrl: ${{ steps.deploy.outputs.acr_login_server_url }}
    resourceGroupName: ${{ steps.deploy.outputs.resource_group_name }}
    aksName: ${{ steps.deploy.outputs.aks_name }}
    steps:
    - uses: actions/checkout@v2
    - uses: azure/login@v1
    name: Sign in to Azure
    with:
    client-id: ${{ secrets.AZURE_CLIENT_ID }}
    tenant-id: ${{ secrets.AZURE_TENANT_ID }}
    subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
    - uses: azure/arm-deploy@v1
    id: deploy
    name: Deploy Bicep file
    with:
    failOnStdErr: false
    deploymentName: ${{ github.run_number }}
    scope: subscription
    region: eastus
    template: ./deploy/main.bicep
    parameters: >
    resourceGroup=${{ inputs.resourceGroupName }}

    Adding the Bicep Deployment

    Once we have the Bicep deployment workflow, we can add it to the primary build definition in .github/workflows/dotnetcore.yml

    Permissions

    First, we need to add a permissions block to let the workflow request our Azure AD token. This can go towards the top of the YAML file (I started it on line 5).

    permissions:
    id-token: write
    contents: read

    Deploy AKS Job

    Next, we'll add a reference to our reusable workflow. This will go after the build job.

      deploy_aks:
    needs: [build]
    uses: ./.github/workflows/deploy_aks.yml
    with:
    resourceGroupName: 'cnny-week3'
    secrets:
    AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
    AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
    AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }}

    Building and Publishing a Container Image

    Now that we have our target environment in place and an Azure Container Registry, we can build and publish our container images.

    Add a Reusable Workflow

    First, we'll create a new file for our reusable workflow at .github/workflows/publish_container_image.yml.

    We'll start the file with a name, the parameters it needs to run, and the permissions requirements for the federated identity request.

    name: Publish Container Images

    on:
    workflow_call:
    inputs:
    containerRegistryName:
    required: true
    type: string
    containerRegistryUrl:
    required: true
    type: string
    githubSha:
    required: true
    type: string
    secrets:
    AZURE_CLIENT_ID:
    required: true
    AZURE_TENANT_ID:
    required: true
    AZURE_SUBSCRIPTION_ID:
    required: true

    permissions:
    id-token: write
    contents: read

    Build the Container Images

    Our next step is to build the two container images we'll need for the application, the website and the API. We'll build the container images on our build worker and tag it with the git SHA, so there'll be a direct tie between the point in time in our codebase and the container images that represent it.

    jobs:
    publish_container_image:
    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v2
    - name: docker build
    run: |
    docker build . -f src/Web/Dockerfile -t ${{ inputs.containerRegistryUrl }}/web:${{ inputs.githubSha }}
    docker build . -f src/PublicApi/Dockerfile -t ${{ inputs.containerRegistryUrl }}/api:${{ inputs.githubSha}}

    Scan the Container Images

    Before we publish those container images, we'll scan them for vulnerabilities and best practice violations. We can add these two steps (one scan for each image).

        - name: scan web container image
    uses: Azure/container-scan@v0
    with:
    image-name: ${{ inputs.containerRegistryUrl }}/web:${{ inputs.githubSha}}
    - name: scan api container image
    uses: Azure/container-scan@v0
    with:
    image-name: ${{ inputs.containerRegistryUrl }}/web:${{ inputs.githubSha}}

    The container images provided have a few items that'll be found. We can create an allowed list at .github/containerscan/allowedlist.yaml to define vulnerabilities or best practice violations that we'll explictly allow to not fail our build.

    general:
    vulnerabilities:
    - CVE-2022-29458
    - CVE-2022-3715
    - CVE-2022-1304
    - CVE-2021-33560
    - CVE-2020-16156
    - CVE-2019-8457
    - CVE-2018-8292
    bestPracticeViolations:
    - CIS-DI-0001
    - CIS-DI-0005
    - CIS-DI-0006
    - CIS-DI-0008

    Publish the Container Images

    Finally, we'll log in to Azure, then log in to our Azure Container Registry, and push our images.

        - uses: azure/login@v1
    name: Sign in to Azure
    with:
    client-id: ${{ secrets.AZURE_CLIENT_ID }}
    tenant-id: ${{ secrets.AZURE_TENANT_ID }}
    subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
    - name: acr login
    run: az acr login --name ${{ inputs.containerRegistryName }}
    - name: docker push
    run: |
    docker push ${{ inputs.containerRegistryUrl }}/web:${{ inputs.githubSha}}
    docker push ${{ inputs.containerRegistryUrl }}/api:${{ inputs.githubSha}}

    Update the Build With the Image Build and Publish

    Now that we have our reusable workflow to create and publish our container images, we can include that in our primary build defnition at .github/workflows/dotnetcore.yml.

      publish_container_image:
    needs: [deploy_aks]
    uses: ./.github/workflows/publish_container_image.yml
    with:
    containerRegistryName: ${{ needs.deploy_aks.outputs.containerRegistryName }}
    containerRegistryUrl: ${{ needs.deploy_aks.outputs.containerRegistryUrl }}
    githubSha: ${{ github.sha }}
    secrets:
    AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
    AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
    AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }}

    Deploying to Kubernetes

    Finally, we've gotten enough set up that a commit to the target branch will:

    • build and test our application code
    • set up (or validate) our AKS and ACR environment
    • and create, scan, and publish our container images to ACR

    Our last step will be to deploy our application to Kubernetes. We'll use the basic building blocks we worked with last week, deployments and services.

    Starting the Reusable Workflow to Deploy to AKS

    We'll start our workflow with our parameters that we need, as well as the permissions to access the token to log in to Azure.

    We'll check out our code, then log in to Azure, and use the az CLI to get credentials for our AKS cluster.

    name: deploy_to_aks

    on:
    workflow_call:
    inputs:
    aksName:
    required: true
    type: string
    resourceGroupName:
    required: true
    type: string
    containerRegistryUrl:
    required: true
    type: string
    githubSha:
    required: true
    type: string
    secrets:
    AZURE_CLIENT_ID:
    required: true
    AZURE_TENANT_ID:
    required: true
    AZURE_SUBSCRIPTION_ID:
    required: true

    permissions:
    id-token: write
    contents: read

    jobs:
    deploy:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    - uses: azure/login@v1
    name: Sign in to Azure
    with:
    client-id: ${{ secrets.AZURE_CLIENT_ID }}
    tenant-id: ${{ secrets.AZURE_TENANT_ID }}
    subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
    - name: Get AKS Credentials
    run: |
    az aks get-credentials --resource-group ${{ inputs.resourceGroupName }} --name ${{ inputs.aksName }}

    Edit the Deployment For Our Current Image Tag

    Let's add the Kubernetes manifests to our repo. This post is long enough, so you can find the content for the manifests folder in the manifests folder in the source repo under the week3/day1 branch.

    tip

    If you only forked the main branch of the source repo, you can easily get the updated manifests by using the following git commands:

    git remote add upstream https://github.com/Azure-Samples/eShopOnAks
    git fetch upstream week3/day1
    git checkout upstream/week3/day1 manifests

    This will make the week3/day1 branch available locally and then we can update the manifests directory to match the state of that branch.

    The deployments and the service defintions should be familiar from last week's content (but not the same). This week, however, there's a new file in the manifests - ./manifests/kustomization.yaml

    This file helps us more dynamically edit our kubernetes manifests and support is baked right in to the kubectl command.

    Kustomize Definition

    Kustomize allows us to specify specific resource manifests and areas of that manifest to replace. We've put some placeholders in our file as well, so we can replace those for each run of our CI/CD system.

    In ./manifests/kustomization.yaml you will see:

    resources:
    - deployment-api.yaml
    - deployment-web.yaml

    # Change the image name and version
    images:
    - name: notavalidregistry.azurecr.io/api:v0.1.0
    newName: <YOUR_ACR_SERVER>/api
    newTag: <YOUR_IMAGE_TAG>
    - name: notavalidregistry.azurecr.io/web:v0.1.0
    newName: <YOUR_ACR_SERVER>/web
    newTag: <YOUR_IMAGE_TAG>

    Replacing Values in our Build

    Now, we encounter a little problem - our deployment files need to know the tag and ACR server. We can do a bit of sed magic to edit the file on the fly.

    In .github/workflows/deploy_to_aks.yml, we'll add:

          - name: replace_placeholders_with_current_run
    run: |
    sed -i "s/<YOUR_ACR_SERVER>/${{ inputs.containerRegistryUrl }}/g" ./manifests/kustomization.yaml
    sed -i "s/<YOUR_IMAGE_TAG>/${{ inputs.githubSha }}/g" ./manifests/kustomization.yaml

    Deploying the Manifests

    We have our manifests in place and our kustomization.yaml file (with commands to update it at runtime) ready to go, we can deploy our manifests.

    First, we'll deploy our database (deployment and service). Next, we'll use the -k parameter on kubectl to tell it to look for a kustomize configuration, transform the requested manifests and apply those. Finally, we apply the service defintions for the web and API deployments.

            run: |
    kubectl apply -f ./manifests/deployment-db.yaml \
    -f ./manifests/service-db.yaml
    kubectl apply -k ./manifests
    kubectl apply -f ./manifests/service-api.yaml \
    -f ./manifests/service-web.yaml

    Summary

    We've covered a lot of ground in today's post. We set up federated credentials with GitHub. Then we added reusable workflows to deploy an AKS environment and build/scan/publish our container images, and then to deploy them into our AKS environment.

    This sets us up to start making changes to our application and Kubernetes configuration and have those changes automatically validated and deployed by our CI/CD system. Tomorrow, we'll look at updating our application environment with runtime configuration, persistent storage, and more.

    Resources

    Take the Cloud Skills Challenge!

    Enroll in the Cloud Skills Challenge!

    Don't miss out on this opportunity to level up your skills and stay ahead of the curve in the world of cloud native.

    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/ask-the-expert/page/11/index.html b/cnny-2023/tags/ask-the-expert/page/11/index.html index 3b84487087..cc899e1dc0 100644 --- a/cnny-2023/tags/ask-the-expert/page/11/index.html +++ b/cnny-2023/tags/ask-the-expert/page/11/index.html @@ -14,13 +14,13 @@ - - + +

    16 posts tagged with "ask-the-expert"

    View All Tags

    · 9 min read
    Steven Murawski

    Welcome to Day 4 of Week 3 of #CloudNativeNewYear!

    The theme for this week is Bringing Your Application to Kubernetes. Yesterday we exposed the eShopOnWeb app so that customers can reach it over the internet using a custom domain name and TLS. Today we'll explore the topic of debugging and instrumentation.

    Ask the Experts Thursday, February 9th at 9 AM PST
    Friday, February 10th at 11 AM PST

    Watch the recorded demo and conversation about this week's topics.

    We were live on YouTube walking through today's (and the rest of this week's) demos. Join us Friday, February 10th and bring your questions!

    What We'll Cover

    • Debugging
    • Bridge To Kubernetes
    • Instrumentation
    • Resources: For self-study!

    Debugging

    Debugging applications in a Kubernetes cluster can be challenging for several reasons:

    • Complexity: Kubernetes is a complex system with many moving parts, including pods, nodes, services, and config maps, all of which can interact in unexpected ways and cause issues.
    • Distributed Environment: Applications running in a Kubernetes cluster are often distributed across multiple nodes, which makes it harder to determine the root cause of an issue.
    • Logging and Monitoring: Debugging an application in a Kubernetes cluster requires access to logs and performance metrics, which can be difficult to obtain in a large and dynamic environment.
    • Resource Management: Kubernetes manages resources such as CPU and memory, which can impact the performance and behavior of applications. Debugging resource-related issues requires a deep understanding of the Kubernetes resource model and the underlying infrastructure.
    • Dynamic Nature: Kubernetes is designed to be dynamic, with the ability to add and remove resources as needed. This dynamic nature can make it difficult to reproduce issues and debug problems.

    However, there are many tools and practices that can help make debugging applications in a Kubernetes cluster easier, such as using centralized logging, monitoring, and tracing solutions, and following best practices for managing resources and deployment configurations.

    There's also another great tool in our toolbox - Bridge to Kubernetes.

    Bridge to Kubernetes

    Bridge to Kubernetes is a great tool for microservice development and debugging applications without having to locally replicate all the required microservices.

    Bridge to Kubernetes works with Visual Studio or Visual Studio Code.

    We'll walk through using it with Visual Studio Code.

    Connecting Bridge to Kubernetes to Our Cluster

    Ensure your AKS cluster is the default for kubectl

    If you've recently spun up a new AKS cluster or you have been working with a different cluster, you may need to change what cluster credentials you have configured.

    If it's a new cluster, we can use:

    RESOURCE_GROUP=<YOUR RESOURCE GROUP NAME>
    CLUSTER_NAME=<YOUR AKS CLUSTER NAME>
    az aks get-credentials az aks get-credentials --resource-group $RESOURCE_GROUP --name $CLUSTER_NAME

    Open the command palette

    Open the command palette and find Bridge to Kubernetes: Configure. You may need to start typing the name to get it to show up.

    The command palette for Visual Studio Code is open and the first item is Bridge to Kubernetes: Configure

    Pick the service you want to debug

    Bridge to Kubernetes will redirect a service for you. Pick the service you want to redirect, in this case we'll pick web.

    Selecting the `web` service to redirect in Visual Studio Code

    Identify the port your application runs on

    Next, we'll be prompted to identify what port our application will run on locally. For this application it'll be 5001, but that's just specific to this application (and the default for ASP.NET 7, I believe).

    Setting port 5001 as the port to redirect to the `web` Kubernetes service in Visual Studio Code

    Pick a debug configuration to extend

    Bridge to Kubernetes has a couple of ways to run - it can inject it's setup and teardown to your existing debug configurations. We'll pick .NET Core Launch (web).

    Telling Bridge to Kubernetes to use the .NET Core Launch (web) debug configuration in Visual Studio Code

    Forward Traffic for All Requests

    The last prompt you'll get in the configuration is about how you want Bridge to Kubernetes to handle re-routing traffic. The default is that all requests into the service will get your local version.

    You can also redirect specific traffic. Bridge to Kubernetes will set up a subdomain and route specific traffic to your local service, while allowing other traffic to the deployed service.

    Allowing the launch of Endpoint Manager on Windows

    Using Bridge to Kubernetes to Debug Our Service

    Now that we've configured Bridge to Kubernetes, we see that tasks and a new launch configuration have been added.

    Added to .vscode/tasks.json:

            {
    "label": "bridge-to-kubernetes.resource",
    "type": "bridge-to-kubernetes.resource",
    "resource": "web",
    "resourceType": "service",
    "ports": [
    5001
    ],
    "targetCluster": "aks1",
    "targetNamespace": "default",
    "useKubernetesServiceEnvironmentVariables": false
    },
    {
    "label": "bridge-to-kubernetes.compound",
    "dependsOn": [
    "bridge-to-kubernetes.resource",
    "build"
    ],
    "dependsOrder": "sequence"
    }

    And added to .vscode/launch.json:

    {
    "name": ".NET Core Launch (web) with Kubernetes",
    "type": "coreclr",
    "request": "launch",
    "preLaunchTask": "bridge-to-kubernetes.compound",
    "program": "${workspaceFolder}/src/Web/bin/Debug/net7.0/Web.dll",
    "args": [],
    "cwd": "${workspaceFolder}/src/Web",
    "stopAtEntry": false,
    "env": {
    "ASPNETCORE_ENVIRONMENT": "Development",
    "ASPNETCORE_URLS": "http://+:5001"
    },
    "sourceFileMap": {
    "/Views": "${workspaceFolder}/Views"
    }
    }

    Launch the debug configuration

    We can start the process with the .NET Core Launch (web) with Kubernetes launch configuration in the Debug pane in Visual Studio Code.

    Launch the `.NET Core Launch (web) with Kubernetes` from the Debug pane in Visual Studio Code

    Enable the Endpoint Manager

    Part of this process includes a local service to help manage the traffic routing and your hosts file. This will require admin or sudo privileges. On Windows, you'll get a prompt like:

    Prompt to launch the endpoint manager.

    Access your Kubernetes cluster "locally"

    Bridge to Kubernetes will set up a tunnel (thanks to port forwarding) to your local workstation and create local endpoints for the other Kubernetes hosted services in your cluster, as well as pretending to be a pod in that cluster (for the application you are debugging).

    Output from Bridge To Kubernetes setup task.

    After making the connection to your Kubernetes cluster, the launch configuration will continue. In this case, we'll make a debug build of the application and attach the debugger. (This process may cause the terminal in VS Code to scroll with build output. You can find the Bridge to Kubernetes output with the local IP addresses and ports in the Output pane for Bridge to Kubernetes.)

    You can set breakpoints, use your debug console, set watches, run tests against your local version of the service.

    Exploring the Running Application Environment

    One of the cool things that Bridge to Kubernetes does for our debugging experience is bring the environment configuration that our deployed pod would inherit. When we launch our app, it'll see configuration and secrets that we'd expect our pod to be running with.

    To test this, we'll set a breakpoint in our application's start up to see what SQL Server is being used. We'll set a breakpoint at src/Infrastructure/Dependencies.cs on line 32.

    Then, we will start debugging the application with Bridge to Kubernetes. When it hits the breakpoint, we'll open the Debug pane and type configuration.GetConnectionString("CatalogConnection").

    When we run locally (not with Bridge to Kubernetes), we'd see:

    configuration.GetConnectionString("CatalogConnection")
    "Server=(localdb)\\mssqllocaldb;Integrated Security=true;Initial Catalog=Microsoft.eShopOnWeb.CatalogDb;"

    But, with Bridge to Kubernetes we see something more like (yours will vary based on the password ):

    configuration.GetConnectionString("CatalogConnection")
    "Server=db;Database=Microsoft.eShopOnWeb.CatalogDb;User Id=sa;Password=*****************;TrustServerCertificate=True;"

    Debugging our local application connected to Kubernetes.

    We can see that the database server configured is based on our db service and the password is pulled from our secret in Azure KeyVault (via AKS).

    This helps us run our local application just like it was actually in our cluster.

    Going Further

    Bridge to Kubernetes also supports more advanced scenarios and, as you need to start routing traffic around inside your cluster, may require you to modify your application to pass along a kubernetes-route-as header to help ensure that traffic for your debugging workloads is properly handled. The docs go into much greater detail about that.

    Instrumentation

    Now that we've figured out our debugging story, we'll need to ensure that we have the right context clues to find where we need to debug or to give us a better idea of how well our microservices are running.

    Logging and Tracing

    Logging and tracing become even more critical in Kubernetes, where your application could be running in a number of pods across different nodes. When you have an issue, in addition to the normal application data, you'll want to know what pod and what node had the issue, what the state of those resources were (were you resource constrained or were shared resources unavailable?), and if autoscaling is enabled, you'll want to know if a scale event has been triggered. There are a multitude of other concerns based on your application and the environment you maintain.

    Given these informational needs, it's crucial to revisit your existing logging and instrumentation. Most frameworks and languages have extensible logging, tracing, and instrumentation libraries that you can iteratively add information to, such as pod and node states, and ensuring that requests can be traced across your microservices. This will pay you back time and time again when you have to troubleshoot issues in your existing environment.

    Centralized Logging

    To enhance the troubleshooting process further, it's important to implement centralized logging to consolidate logs from all your microservices into a single location. This makes it easier to search and analyze logs when you're troubleshooting an issue.

    Automated Alerting

    Additionally, implementing automated alerting, such as sending notifications when specific conditions occur in the logs, can help you detect issues before they escalate.

    End to end Visibility

    End-to-end visibility is also essential in understanding the flow of requests and responses between microservices in a distributed system. With end-to-end visibility, you can quickly identify bottlenecks and slowdowns in the system, helping you to resolve issues more efficiently.

    Resources

    Take the Cloud Skills Challenge!

    Enroll in the Cloud Skills Challenge!

    Don't miss out on this opportunity to level up your skills and stay ahead of the curve in the world of cloud native.

    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/ask-the-expert/page/12/index.html b/cnny-2023/tags/ask-the-expert/page/12/index.html index e3913f9365..440b4ad759 100644 --- a/cnny-2023/tags/ask-the-expert/page/12/index.html +++ b/cnny-2023/tags/ask-the-expert/page/12/index.html @@ -14,13 +14,13 @@ - - + +

    16 posts tagged with "ask-the-expert"

    View All Tags

    · 7 min read
    Nitya Narasimhan

    Welcome to Week 4 of #CloudNativeNewYear!

    This week we'll go further with Cloud-native by exploring advanced topics and best practices for the Cloud-native practitioner. We'll start with an exploration of Serverless Container Options - ranging from managed services to Azure Kubernetes Service (AKS) and Azure Container Apps (ACA), to options that allow more granular control!

    What We'll Cover

    • The Azure Compute Landscape
    • Serverless Compute on Azure
    • Comparing Container Options On Azure
    • Other Considerations
    • Exercise: Try this yourself!
    • Resources: For self-study!


    We started this series with an introduction to core concepts:

    • In Containers 101, we learned why containerization matters. Think portability, isolation, scalability, resource-efficiency and cost-effectiveness. But not all apps can be containerized.
    • In Kubernetes 101, we learned how orchestration works. Think systems to automate container deployment, scaling, and management. But using Kubernetes directly can be complex.
    • In Exploring Cloud Native Options we asked the real questions: can we containerize - and should we?. The first depends on app characteristics, the second on your requirements.

    For example:

    • Can we containerize? The answer might be no if your app has system or OS dependencies, requires access to low-level hardware, or maintains complex state across sessions.
    • Should we containerize? The answer might be yes if your app is microservices-based, is stateless by default, requires portability, or is a legaacy app that can benefit from container isolation.

    As with every technology adoption decision process, there are no clear yes/no answers - just tradeoffs that you need to evaluate based on your architecture and application requirements. In today's post, we try to look at this from two main perspectives:

    1. Should you go serverless? Think: managed services that let you focus on app, not infra.
    2. What Azure Compute should I use? Think: best fit for my architecture & technology choices.

    Azure Compute Landscape

    Let's answer the second question first by exploring all available compute options on Azure. The illustrated decision-flow below is my favorite ways to navigate the choices, with questions like:

    • Are you migrating an existing app or building a new one?
    • Can you app be containerized?
    • Does it use a specific technology (Spring Boot, Red Hat Openshift)?
    • Do you need access to the Kubernetes API?
    • What characterizes the workload? (event-driven, web app, microservices etc.)

    Read the docs to understand how your choices can be influenced by the hosting model (IaaS, PaaS, FaaS), supported features (Networking, DevOps, Scalability, Availability, Security), architectural styles (Microservices, Event-driven, High-Performance Compute, Task Automation,Web-Queue Worker) etc.

    Compute Choices

    Now that we know all available compute options, let's address the second question: why go serverless? and what are my serverless compute options on Azure?

    Azure Serverless Compute

    Serverless gets defined many ways, but from a compute perspective, we can focus on a few key characteristics that are key to influencing this decision:

    • managed services - focus on application, let cloud provider handle infrastructure.
    • pay for what you use - get cost-effective resource utilization, flexible pricing options.
    • autoscaling on demand - take advantage of built-in features like KEDA-compliant triggers.
    • use preferred languages - write code in Java, JS, C#, Python etc. (specifics based on service)
    • cloud-native architectures - can support event-driven solutions, APIs, Microservices, DevOps!

    So what are some of the key options for Serverless Compute on Azure? The article dives into serverless support for fully-managed end-to-end serverless solutions with comprehensive support for DevOps, DevTools, AI/ML, Database, Storage, Monitoring and Analytics integrations. But we'll just focus on the 4 categories of applications when we look at Compute!

    1. Serverless Containerized Microservices using Azure Container Apps. Code in your preferred language, exploit full Dapr support, scale easily with any KEDA-compliant trigger.
    2. Serverless Application Environments using Azure App Service. Suitable for hosting monolithic apps (vs. microservices) in a managed service, with built-in support for on-demand scaling.
    3. Serverless Kubernetes using Azure Kubernetes Service (AKS). Spin up pods inside container instances and deploy Kubernetes-based applications with built-in KEDA-compliant autoscaling.
    4. Serverless Functions using Azure Functions. Execute "code at the granularity of functions" in your preferred language, and scale on demand with event-driven compute.

    We'll talk about these, and other compute comparisons, at the end of the article. But let's start with the core option you might choose if you want a managed serverless compute solution with built-in features for delivering containerized microservices at scale. Hello, Azure Container Apps!.

    Azure Container Apps

    Azure Container Apps (ACA) became generally available in May 2022 - providing customers with the ability to run microservices and containerized applications on a serverless, consumption-based platform. The figure below showcases the different types of applications that can be built with ACA. Note that it comes with built-in KEDA-compliant autoscaling triggers, and other auto-scale criteria that may be better-suited to the type of application you are building.

    About ACA

    So far in the series, we've put the spotlight on Azure Kubernetes Service (AKS) - so you're probably asking yourself: How does ACA compare to AKS?. We're glad you asked. Check out our Go Cloud-native with Azure Container Apps post from the #ServerlessSeptember series last year for a deeper-dive, or review the figure below for the main comparison points.

    The key takeaway is this. Azure Container Apps (ACA) also runs on Kubernetes but abstracts away its complexity in a managed service offering that lets you get productive quickly without requiring detailed knowledge of Kubernetes workings or APIs. However, if you want full access and control over the Kubernetes API then go with Azure Kubernetes Service (AKS) instead.

    Comparison

    Other Container Options

    Azure Container Apps is the preferred Platform As a Service (PaaS) option for a fully-managed serverless solution on Azure that is purpose-built for cloud-native microservices-based application workloads. But - there are other options that may be suitable for your specific needs, from a requirements and tradeoffs perspective. Let's review them quickly:

    1. Azure Functions is the serverless Functions-as-a-Service (FaaS) option, as opposed to ACA which supports a PaaS approach. It's optimized for running event-driven applications built at the granularity of ephemeral functions that can be deployed as code or containers.
    2. Azure App Service provides fully managed hosting for web applications that may be deployed using code or containers. It can be integrated with other services including Azure Container Apps and Azure Functions. It's optimized for deploying traditional web apps.
    3. Azure Kubernetes Service provides a fully managed Kubernetes option capable of running any Kubernetes workload, with direct access to the Kubernetes API.
    4. Azure Container Instances provides a single pod of Hyper-V isolated containers on demand, making them more of a low-level "building block" option compared to ACA.

    Based on the technology choices you made for application development you may also have more specialized options you want to consider. For instance:

    1. Azure Spring Apps is ideal if you're running Spring Boot or Spring Cloud workloads on Azure,
    2. Azure Red Hat OpenShift is ideal for integrated Kubernetes-powered OpenShift on Azure.
    3. Azure Confidential Computing is ideal if you have data/code integrity and confidentiality needs.
    4. Kubernetes At The Edge is ideal for bare-metal options that extend compute to edge devices.

    This is just the tip of the iceberg in your decision-making journey - but hopefully, it gave you a good sense of the options and criteria that influences your final choices. Let's wrap this up with a look at self-study resources for skilling up further.

    Exercise

    Want to get hands on learning related to these technologies?

    TAKE THE CLOUD SKILLS CHALLENGE

    Register today and level up your skills by completing free learning modules, while competing with your peers for a place on the leaderboards!

    Resources

    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/ask-the-expert/page/13/index.html b/cnny-2023/tags/ask-the-expert/page/13/index.html index 664a1ec044..21ccac339c 100644 --- a/cnny-2023/tags/ask-the-expert/page/13/index.html +++ b/cnny-2023/tags/ask-the-expert/page/13/index.html @@ -14,13 +14,13 @@ - - + +

    16 posts tagged with "ask-the-expert"

    View All Tags

    · 3 min read
    Cory Skimming

    It's the final week of #CloudNativeNewYear! This week we'll go further with Cloud-native by exploring advanced topics and best practices for the Cloud-native practitioner. In today's post, we will introduce you to the basics of the open-source project Draft and how it can be used to easily create and deploy applications to Kubernetes.

    It's not too late to sign up for and complete the Cloud Skills Challenge!

    What We'll Cover

    • What is Draft?
    • Draft basics
    • Demo: Developing to AKS with Draft
    • Resources


    What is Draft?

    Draft is an open-source tool that can be used to streamline the development and deployment of applications on Kubernetes clusters. It provides a simple and easy-to-use workflow for creating and deploying applications, making it easier for developers to focus on writing code and building features, rather than worrying about the underlying infrastructure. This is great for users who are just getting started with Kubernetes, or those who are just looking to simplify their experience.

    New to Kubernetes?

    Draft basics

    Draft streamlines Kubernetes development by taking a non-containerized application and generating the Dockerfiles, K8s manifests, Helm charts, and other artifacts associated with a containerized application. Draft can also create a GitHub Action workflow file to quickly build and deploy your application onto any Kubernetes cluster.

    1. 'draft create'': Create a new Draft project by simply running the 'draft create' command - this command will walk you through a series of questions on your application specification (such as the application language) and create a Dockerfile, Helm char, and Kubernetes
    2. 'draft generate-workflow'': Automatically build out a GitHub Action using the 'draft generate-workflow' command
    3. 'draft setup-gh'': If you are using Azure, use this command to automate the GitHub OIDC set up process to ensure that you will be able to deploy your application using your GitHub Action.

    At this point, you will have all the files needed to deploy your app onto a Kubernetes cluster (we told you it was easy!).

    You can also use the 'draft info' command if you are looking for information on supported languages and deployment types. Let's see it in action, shall we?


    Developing to AKS with Draft

    In this Microsoft Reactor session below, we'll briefly introduce Kubernetes and the Azure Kubernetes Service (AKS) and then demo how enable your applications for Kubernetes using the open-source tool Draft. We'll show how Draft can help you create the boilerplate code to containerize your applications and add routing and scaling behaviours.

    ##Conclusion

    Overall, Draft simplifies the process of building, deploying, and managing applications on Kubernetes, and can make the overall journey from code to Kubernetes significantly easier.


    Resources


    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/ask-the-expert/page/14/index.html b/cnny-2023/tags/ask-the-expert/page/14/index.html index 5ce8d08090..bd4c389470 100644 --- a/cnny-2023/tags/ask-the-expert/page/14/index.html +++ b/cnny-2023/tags/ask-the-expert/page/14/index.html @@ -14,14 +14,14 @@ - - + +

    16 posts tagged with "ask-the-expert"

    View All Tags

    · 7 min read
    Vinicius Apolinario

    Welcome to Day 3 of Week 4 of #CloudNativeNewYear!

    The theme for this week is going further with Cloud Native. Yesterday we talked about using Draft to accelerate your Kubernetes adoption. Today we'll explore the topic of Windows containers.

    What We'll Cover

    • Introduction
    • Windows containers overview
    • Windows base container images
    • Isolation
    • Exercise: Try this yourself!
    • Resources: For self-study!

    Introduction

    Windows containers were launched along with Windows Server 2016, and have evolved since. In its latest release, Windows Server 2022, Windows containers have reached a great level of maturity and allow for customers to run production grade workloads.

    While suitable for new developments, Windows containers also provide developers and operations with a different approach than Linux containers. It allows for existing Windows applications to be containerized with little or no code changes. It also allows for professionals that are more comfortable with the Windows platform and OS, to leverage their skill set, while taking advantage of the containers platform.

    Windows container overview

    In essence, Windows containers are very similar to Linux. Since Windows containers use the same foundation of Docker containers, you can expect that the same architecture applies - with the specific notes of the Windows OS. For example, when running a Windows container via Docker, you use the same commands, such as docker run. To pull a container image, you can use docker pull, just like on Linux. However, to run a Windows container, you also need a Windows container host. This requirement is there because, as you might remember, a container shares the OS kernel with its container host.

    On Kubernetes, Windows containers are supported since Windows Server 2019. Just like with Docker, you can manage Windows containers like any other resource on the Kubernetes ecosystem. A Windows node can be part of a Kubernetes cluster, allowing you to run Windows container based applications on services like Azure Kubernetes Service. To deploy an Windows application to a Windows pod in Kubernetes, you can author a YAML specification much like you would for Linux. The main difference is that you would point to an image that runs on Windows, and you need to specify a node selection tag to indicate said pod needs to run on a Windows node.

    Windows base container images

    On Windows containers, you will always use a base container image provided by Microsoft. This base container image contains the OS binaries for the container to run. This image can be as large as 3GB+, or small as ~300MB. The difference in the size is a consequence of the APIs and components available in each Windows container base container image. There are primarily, three images: Nano Server, Server Core, and Server.

    Nano Server is the smallest image, ranging around 300MB. It's a base container image for new developments and cloud-native scenarios. Applications need to target Nano Server as the Windows OS, so not all frameworks will work. For example, .Net works on Nano Server, but .Net Framework doesn't. Other third-party frameworks also work on Nano Server, such as Apache, NodeJS, Phyton, Tomcat, Java runtime, JBoss, Redis, among others.

    Server Core is a much larger base container image, ranging around 1.25GB. It's larger size is compensated by it's application compatibility. Simply put, any application that meets the requirements to be run on a Windows container, can be containerized with this image.

    The Server image builds on the Server Core one. It ranges around 3.1GB and has even greater application compatibility than the Server Core image. In addition to the traditional Windows APIs and components, this image allows for scenarios such as Machine Learning via DirectX with GPU access.

    The best image for your scenario is dependent on the requirements your application has on the Windows OS inside a container. However, there are some scenarios that are not supported at all on Windows containers - such as GUI or RDP dependent applications, some Windows Server infrastructure roles, such as Active Directory, among others.

    Isolation

    When running containers, the kernel of the container host is shared with the containers running on it. While extremely convenient, this poses a potential risk for multi-tenant scenarios. If one container is compromised and has access to the host, it could potentially compromise other containers in the same system.

    For enterprise customers running on-premises (or even in the cloud), this can be mitigated by using a VM as a container host and considering the VM itself a security boundary. However, if multiple workloads from different tenants need to share the same host, Windows containers offer another option: Hyper-V isolation. While the name Hyper-V is associated with VMs, its virtualization capabilities extend to other services, including containers. Hyper-V isolated containers run on a purpose built, extremely small, highly performant VM. However, you manage a container running with Hyper-V isolation the same way you do with a process isolated one. In fact, the only notable difference is that you need to append the --isolation=hyperv tag to the docker run command.

    Exercise

    Here are a few examples of how to use Windows containers:

    Run Windows containers via Docker on your machine

    To pull a Windows base container image:

    docker pull mcr.microsoft.com/windows/servercore:ltsc2022

    To run a basic IIS container:

    #This command will pull and start a IIS container. You can access it from http://<your local IP>:8080
    docker run -d -p 8080:80 mcr.microsoft.com/windows/servercore/iis:windowsservercore-ltsc2022

    Run the same IIS container with Hyper-V isolation

    #This command will pull and start a IIS container. You can access it from http://<your local IP>:8080
    docker run -d -p 8080:80 --isolation=hyperv mcr.microsoft.com/windows/servercore/iis:windowsservercore-ltsc2022

    To run a Windows container interactively:

    docker run -it mcr.microsoft.com/windows/servercore:ltsc2022 powershell

    Run Windows containers on Kubernetes

    To prepare an AKS cluster for Windows containers: Note: Replace the values on the example below with the ones from your environment.

    echo "Please enter the username to use as administrator credentials for Windows Server nodes on your cluster: " && read WINDOWS_USERNAME
    az aks create \
    --resource-group myResourceGroup \
    --name myAKSCluster \
    --node-count 2 \
    --generate-ssh-keys \
    --windows-admin-username $WINDOWS_USERNAME \
    --vm-set-type VirtualMachineScaleSets \
    --network-plugin azure

    To add a Windows node pool for Windows containers:

    az aks nodepool add \
    --resource-group myResourceGroup \
    --cluster-name myAKSCluster \
    --os-type Windows \
    --name npwin \
    --node-count 1

    Deploy a sample ASP.Net application to the AKS cluster above using the YAML file below:

    apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: sample
    labels:
    app: sample
    spec:
    replicas: 1
    template:
    metadata:
    name: sample
    labels:
    app: sample
    spec:
    nodeSelector:
    "kubernetes.io/os": windows
    containers:
    - name: sample
    image: mcr.microsoft.com/dotnet/framework/samples:aspnetapp
    resources:
    limits:
    cpu: 1
    memory: 800M
    ports:
    - containerPort: 80
    selector:
    matchLabels:
    app: sample
    ---
    apiVersion: v1
    kind: Service
    metadata:
    name: sample
    spec:
    type: LoadBalancer
    ports:
    - protocol: TCP
    port: 80
    selector:
    app: sample

    Save the file above and run the command below on your Kubernetes cluster:

    kubectl apply -f <filename> .

    Once deployed, you can access the application by getting the IP address of your service:

    kubectl get service

    Resources

    It's not too late to sign up for and complete the Cloud Skills Challenge!
    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/ask-the-expert/page/15/index.html b/cnny-2023/tags/ask-the-expert/page/15/index.html index 55e3863e0e..d3a370063a 100644 --- a/cnny-2023/tags/ask-the-expert/page/15/index.html +++ b/cnny-2023/tags/ask-the-expert/page/15/index.html @@ -14,13 +14,13 @@ - - + +

    16 posts tagged with "ask-the-expert"

    View All Tags

    · 4 min read
    Jorge Arteiro

    Welcome to Day 4 of Week 4 of #CloudNativeNewYear!

    The theme for this week is going further with Cloud Native. Yesterday we talked about Windows Containers. Today we'll explore addons and extensions available to Azure Kubernetes Services (AKS).

    What We'll Cover

    • Introduction
    • Add-ons
    • Extensions
    • Add-ons vs Extensions
    • Resources

    Introduction

    Azure Kubernetes Service (AKS) is a fully managed container orchestration service that makes it easy to deploy and manage containerized applications on Azure. AKS offers a number of features and capabilities, including the ability to extend its supported functionality through the use of add-ons and extensions.

    There are also integrations available from open-source projects and third parties, but they are not covered by the AKS support policy.

    Add-ons

    Add-ons provide a supported way to extend AKS. Installation, configuration and lifecycle are managed by AKS following pre-determine updates rules.

    As an example, let's enable Container Insights with the monitoring addon. on an existing AKS cluster using az aks enable-addons --addons CLI command

    az aks enable-addons \
    --name MyManagedCluster \
    --resource-group MyResourceGroup \
    --addons monitoring

    or you can use az aks create --enable-addons when creating new clusters

    az aks create \
    --name MyManagedCluster \
    --resource-group MyResourceGroup \
    --enable-addons monitoring

    The current available add-ons are:

    1. http_application_routing - Configure ingress with automatic public DNS name creation. Only recommended for development.
    2. monitoring - Container Insights monitoring.
    3. virtual-node - CNCF virtual nodes open source project.
    4. azure-policy - Azure Policy for AKS.
    5. ingress-appgw - Application Gateway Ingress Controller (AGIC).
    6. open-service-mesh - CNCF Open Service Mesh project.
    7. azure-keyvault-secrets-provider - Azure Key Vault Secrets Provider for Secret Store CSI Driver.
    8. web_application_routing - Managed NGINX ingress Controller.
    9. keda - CNCF Event-driven autoscaling project.

    For more details, get the updated list of AKS Add-ons here

    Extensions

    Cluster Extensions uses Helm charts and integrates with Azure Resource Manager (ARM) to provide installation and lifecycle management of capabilities on top of AKS.

    Extensions can be auto upgraded using minor versions, but it requires extra management and configuration. Using Scope parameter, it can be installed on the whole cluster or per namespace.

    AKS Extensions requires an Azure CLI extension to be installed. To add or update this CLI extension use the following commands:

    az extension add --name k8s-extension

    and to update an existing extension

    az extension update --name k8s-extension

    There are only 3 available extensions:

    1. Dapr - CNCF Dapr project.
    2. Azure ML - Integrate Azure Machine Learning with AKS to train, inference and manage ML models.
    3. Flux (GitOps) - CNCF Flux project integrated with AKS to enable cluster configuration and application deployment using GitOps.

    As an example, you can install Azure ML using the following command:

    az k8s-extension create \
    --name aml-compute --extension-type Microsoft.AzureML.Kubernetes \
    --scope cluster --cluster-name <clusterName> \
    --resource-group <resourceGroupName> \
    --cluster-type managedClusters \
    --configuration-settings enableInference=True allowInsecureConnections=True

    For more details, get the updated list of AKS Extensions here

    Add-ons vs Extensions

    AKS Add-ons brings an advantage of been fully managed by AKS itself, and AKS Extensions are more flexible and configurable but requires extra level of management.

    Add-ons are part of the AKS resource provider in the Azure API, and AKS Extensions are a separate resource provider on the Azure API.

    Resources

    It's not too late to sign up for and complete the Cloud Skills Challenge!
    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/ask-the-expert/page/16/index.html b/cnny-2023/tags/ask-the-expert/page/16/index.html index 8524099107..14bf979af6 100644 --- a/cnny-2023/tags/ask-the-expert/page/16/index.html +++ b/cnny-2023/tags/ask-the-expert/page/16/index.html @@ -14,13 +14,13 @@ - - + +

    16 posts tagged with "ask-the-expert"

    View All Tags

    · 6 min read
    Cory Skimming
    Steven Murawski
    Paul Yu
    Josh Duffney
    Nitya Narasimhan
    Vinicius Apolinario
    Jorge Arteiro
    Devanshi Joshi

    And that's a wrap on the inaugural #CloudNativeNewYear! Thank you for joining us to kick off the new year with this learning journey into cloud-native! In this final post of the 2023 celebration of all things cloud-native, we'll do two things:

    • Look Back - with a quick retrospective of what was covered.
    • Look Ahead - with resources and suggestions for how you can continue your skilling journey!

    We appreciate your time and attention and we hope you found this curated learning valuable. Feedback and suggestions are always welcome. From our entire team, we wish you good luck with the learning journey - now go build some apps and share your knowledge! 🎉


    What We'll Cover

    • Cloud-native fundamentals
    • Kubernetes fundamentals
    • Bringing your applications to Kubernetes
    • Go further with cloud-native
    • Resources to keep the celebration going!

    Week 1: Cloud-native Fundamentals

    In Week 1, we took a tour through the fundamentals of cloud-native technologies, including a walkthrough of the core concepts of containers, microservices, and Kubernetes.

    • Jan 23 - Cloud-native Fundamentals: The answers to life and all the universe - what is cloud-native? What makes an application cloud-native? What are the benefits? (yes, we all know it's 42, but hey, gotta start somewhere!)
    • Jan 24 - Containers 101: Containers are an essential component of cloud-native development. In this intro post, we cover how containers work and why they have become so popular.
    • Jan 25 - Kubernetes 101: Kuber-what-now? Learn the basics of Kubernetes and how it enables us to deploy and manage our applications effectively and consistently.
    A QUICKSTART GUIDE TO KUBERNETES CONCEPTS

    Missed it Live? Tune in to A Quickstart Guide to Kubernetes Concepts on demand, now!

    • Jan 26 - Microservices 101: What is a microservices architecture and how can we go about designing one?
    • Jan 27 - Exploring your Cloud Native Options: Cloud-native, while catchy, can be a very broad term. What technologies should you use? Learn some basic guidelines for when it is optimal to use different technologies for your project.

    Week 2: Kubernetes Fundamentals

    In Week 2, we took a deeper dive into the Fundamentals of Kubernetes. The posts and live demo from this week took us through how to build a simple application on Kubernetes, covering everything from deployment to networking and scaling. Note: for our samples and demo we have used Azure Kubernetes Service, but the principles apply to any Kubernetes!

    • Jan 30 - Pods and Deployments: how to use pods and deployments in Kubernetes.
    • Jan 31 - Services and Ingress: how to use services and ingress and a walk through the steps of making our containers accessible internally and externally!
    • Feb 1 - ConfigMaps and Secrets: how to of passing configuration and secrets to our applications in Kubernetes with ConfigMaps and Secrets.
    • Feb 2 - Volumes, Mounts, and Claims: how to use persistent storage on Kubernetes (and ensure your data can survive container restarts!).
    • Feb 3 - Scaling Pods and Nodes: how to scale pods and nodes in our Kubernetes cluster.
    ASK THE EXPERTS: AZURE KUBERNETES SERVICE

    Missed it Live? Tune in to Ask the Expert with Azure Kubernetes Service on demand, now!


    Week 3: Bringing your applications to Kubernetes

    So, you have learned how to build an application on Kubernetes. What about your existing applications? In Week 3, we explored how to take an existing application and set it up to run in Kubernetes:

    • Feb 6 - CI/CD: learn how to get an existing application running in Kubernetes with a full pipeline in GitHub Actions.
    • Feb 7 - Adapting Storage, Secrets, and Configuration: how to evaluate our sample application's configuration, storage, and networking requirements and implement using Kubernetes.
    • Feb 8 - Opening your Application with Ingress: how to expose the eShopOnWeb app so that customers can reach it over the internet using a custom domain name and TLS.
    • Feb 9 - Debugging and Instrumentation: how to debug and instrument your application now that it is on Kubernetes.
    • Feb 10 - CI/CD Secure Supply Chain: now that we have set up our application on Kubernetes, let's talk about container image signing and how to set up a secure supply change.

    Week 4: Go Further with Cloud-Native

    This week we have gone further with Cloud-native by exploring advanced topics and best practices for the Cloud-native practitioner.

    And today, February 17th, with this one post to rule (er, collect) them all!


    Keep the Learning Going!

    Learning is great, so why stop here? We have a host of great resources and samples for you to continue your cloud-native journey with Azure below:


    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/ask-the-expert/page/2/index.html b/cnny-2023/tags/ask-the-expert/page/2/index.html index 5405d9c08b..67bb1a9d6c 100644 --- a/cnny-2023/tags/ask-the-expert/page/2/index.html +++ b/cnny-2023/tags/ask-the-expert/page/2/index.html @@ -14,14 +14,14 @@ - - + +

    16 posts tagged with "ask-the-expert"

    View All Tags

    · 5 min read
    Cory Skimming

    Welcome to Week 1 of #CloudNativeNewYear!

    Cloud-native New Year

    You will often hear the term "cloud-native" when discussing modern application development, but even a quick online search will return a huge number of articles, tweets, and web pages with a variety of definitions. So, what does cloud-native actually mean? Also, what makes an application a cloud-native application versus a "regular" application?

    Today, we will address these questions and more as we kickstart our learning journey (and our new year!) with an introductory dive into the wonderful world of cloud-native.


    What We'll Cover

    • What is cloud-native?
    • What is a cloud-native application?
    • The benefits of cloud-native
    • The five pillars of cloud-native
    • Exercise: Take the Cloud Skills Challenge!

    1. What is cloud-native?

    The term "cloud-native" can seem pretty self-evident (yes, hello, native to the cloud?), and in a way, it is. While there are lots of definitions of cloud-native floating around, at it's core, cloud-native simply refers to a modern approach to building software that takes advantage of cloud services and environments. This includes using cloud-native technologies, such as containers, microservices, and serverless, and following best practices for deploying, scaling, and managing applications in a cloud environment.

    Official definition from the Cloud Native Computing Foundation:

    Cloud-native technologies empower organizations to build and run scalable applications in modern, dynamic environments such as public, private, and hybrid clouds. Containers, service meshes, microservices, immutable infrastructure, and declarative APIs exemplify this approach.

    These techniques enable loosely coupled systems that are resilient, manageable, and observable. Combined with robust automation, they allow engineers to make high-impact changes frequently and predictably with minimal toil. Source


    2. So, what exactly is a cloud-native application?

    Cloud-native applications are specifically designed to take advantage of the scalability, resiliency, and distributed nature of modern cloud infrastructure. But how does this differ from a "traditional" application?

    Traditional applications are generally been built, tested, and deployed as a single, monolithic unit. The monolithic nature of this type of architecture creates close dependencies between components. This complexity and interweaving only increases as an application grows and can make it difficult to evolve (not to mention troubleshoot) and challenging to operate over time.

    To contrast, in cloud-native architectures the application components are decomposed into loosely coupled services, rather than built and deployed as one block of code. This decomposition into multiple self-contained services enables teams to manage complexity and improve the speed, agility, and scale of software delivery. Many small parts enables teams to make targeted updates, deliver new features, and fix any issues without leading to broader service disruption.


    3. The benefits of cloud-native

    Cloud-native architectures can bring many benefits to an organization, including:

    1. Scalability: easily scale up or down based on demand, allowing organizations to adjust their resource usage and costs as needed.
    2. Flexibility: deploy and run on any cloud platform, and easily move between clouds and on-premises environments.
    3. High-availability: techniques such as redundancy, self-healing, and automatic failover help ensure that cloud-native applications are designed to be highly-available and fault tolerant.
    4. Reduced costs: take advantage of the pay-as-you-go model of cloud computing, reducing the need for expensive infrastructure investments.
    5. Improved security: tap in to cloud security features, such as encryption and identity management, to improve the security of the application.
    6. Increased agility: easily add new features or services to your applications to meet changing business needs and market demand.

    4. The pillars of cloud-native

    There are five areas that are generally cited as the core building blocks of cloud-native architecture:

    1. Microservices: Breaking down monolithic applications into smaller, independent, and loosely-coupled services that can be developed, deployed, and scaled independently.
    2. Containers: Packaging software in lightweight, portable, and self-sufficient containers that can run consistently across different environments.
    3. Automation: Using automation tools and DevOps processes to manage and operate the cloud-native infrastructure and applications, including deployment, scaling, monitoring, and self-healing.
    4. Service discovery: Using service discovery mechanisms, such as APIs & service meshes, to enable services to discover and communicate with each other.
    5. Observability: Collecting and analyzing data from the infrastructure and applications to understand and optimize the performance, behavior, and health of the system.

    These can (and should!) be used in combination to deliver cloud-native solutions that are highly scalable, flexible, and available.

    WHAT'S NEXT

    Stay tuned, as we will be diving deeper into these topics in the coming weeks:

    • Jan 24: Containers 101
    • Jan 25: Adopting Microservices with Kubernetes
    • Jan 26: Kubernetes 101
    • Jan 27: Exploring your Cloud-native Options

    Resources


    Don't forget to subscribe to the blog to get daily posts delivered directly to your favorite feed reader!


    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/ask-the-expert/page/3/index.html b/cnny-2023/tags/ask-the-expert/page/3/index.html index 398fe5901a..d7afa6e493 100644 --- a/cnny-2023/tags/ask-the-expert/page/3/index.html +++ b/cnny-2023/tags/ask-the-expert/page/3/index.html @@ -14,14 +14,14 @@ - - + +

    16 posts tagged with "ask-the-expert"

    View All Tags

    · 4 min read
    Steven Murawski
    Paul Yu
    Josh Duffney

    Welcome to Day 2 of Week 1 of #CloudNativeNewYear!

    Today, we'll focus on building an understanding of containers.

    What We'll Cover

    • Introduction
    • How do Containers Work?
    • Why are Containers Becoming so Popular?
    • Conclusion
    • Resources
    • Learning Path

    REGISTER & LEARN: KUBERNETES 101

    Interested in a dive into Kubernetes and a chance to talk to experts?

    🎙: Join us Jan 26 @1pm PST by registering here

    Here's what you will learn:

    • Key concepts and core principles of Kubernetes.
    • How to deploy, scale and manage containerized workloads.
    • Live Demo of the concepts explained
    • How to get started with Azure Kubernetes Service for free.

    Start your free Azure Kubernetes Trial Today!!: aka.ms/TryAKS

    Introduction

    In the beginning, we deployed our applications onto physical servers. We only had a certain number of those servers, so often they hosted multiple applications. This led to some problems when those applications shared dependencies. Upgrading one application could break another application on the same server.

    Enter virtualization. Virtualization allowed us to run our applications in an isolated operating system instance. This removed much of the risk of updating shared dependencies. However, it increased our overhead since we had to run a full operating system for each application environment.

    To address the challenges created by virtualization, containerization was created to improve isolation without duplicating kernel level resources. Containers provide efficient and consistent deployment and runtime experiences for our applications and have become very popular as a way of packaging and distributing applications.

    How do Containers Work?

    Containers build on two capabilities in the Linux operating system, namespaces and cgroups. These constructs allow the operating system to provide isolation to a process or group of processes, keeping their access to filesystem resources separate and providing controls on resource utilization. This, combined with tooling to help package, deploy, and run container images has led to their popularity in today’s operating environment. This provides us our isolation without the overhead of additional operating system resources.

    When a container host is deployed on an operating system, it works at scheduling the access to the OS (operating systems) components. This is done by providing a logical isolated group that can contain processes for a given application, called a namespace. The container host then manages /schedules access from the namespace to the host OS. The container host then uses cgroups to allocate compute resources. Together, the container host with the help of cgroups and namespaces can schedule multiple applications to access host OS resources.

    Overall, this gives the illusion of virtualizing the host OS, where each application gets its own OS. In actuality, all the applications are running on the same operating system and sharing the same kernel as the container host.

    Containers are popular in the software development industry because they provide several benefits over traditional virtualization methods. Some of these benefits include:

    • Portability: Containers make it easy to move an application from one environment to another without having to worry about compatibility issues or missing dependencies.
    • Isolation: Containers provide a level of isolation between the application and the host system, which means that the application running in the container cannot access the host system's resources.
    • Scalability: Containers make it easy to scale an application up or down as needed, which is useful for applications that experience a lot of traffic or need to handle a lot of data.
    • Resource Efficiency: Containers are more resource-efficient than traditional virtualization methods because they don't require a full operating system to be running on each virtual machine.
    • Cost-Effective: Containers are more cost-effective than traditional virtualization methods because they don't require expensive hardware or licensing fees.

    Conclusion

    Containers are a powerful technology that allows developers to package and deploy applications in a portable and isolated environment. This technology is becoming increasingly popular in the world of software development and is being used by many companies and organizations to improve their application deployment and management processes. With the benefits of portability, isolation, scalability, resource efficiency, and cost-effectiveness, containers are definitely worth considering for your next application development project.

    Containerizing applications is a key step in modernizing them, and there are many other patterns that can be adopted to achieve cloud-native architectures, including using serverless platforms, Kubernetes, and implementing DevOps practices.

    Resources

    Learning Path

    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/ask-the-expert/page/4/index.html b/cnny-2023/tags/ask-the-expert/page/4/index.html index ad7a537939..ae45909205 100644 --- a/cnny-2023/tags/ask-the-expert/page/4/index.html +++ b/cnny-2023/tags/ask-the-expert/page/4/index.html @@ -14,14 +14,14 @@ - - + +

    16 posts tagged with "ask-the-expert"

    View All Tags

    · 3 min read
    Steven Murawski

    Welcome to Day 3 of Week 1 of #CloudNativeNewYear!

    This week we'll focus on what Kubernetes is.

    What We'll Cover

    • Introduction
    • What is Kubernetes? (Video)
    • How does Kubernetes Work? (Video)
    • Conclusion


    REGISTER & LEARN: KUBERNETES 101

    Interested in a dive into Kubernetes and a chance to talk to experts?

    🎙: Join us Jan 26 @1pm PST by registering here

    Here's what you will learn:

    • Key concepts and core principles of Kubernetes.
    • How to deploy, scale and manage containerized workloads.
    • Live Demo of the concepts explained
    • How to get started with Azure Kubernetes Service for free.

    Start your free Azure Kubernetes Trial Today!!: aka.ms/TryAKS

    Introduction

    Kubernetes is an open source container orchestration engine that can help with automated deployment, scaling, and management of our applications.

    Kubernetes takes physical (or virtual) resources and provides a consistent API over them, bringing a consistency to the management and runtime experience for our applications. Kubernetes provides us with a number of capabilities such as:

    • Container scheduling
    • Service discovery and load balancing
    • Storage orchestration
    • Automated rollouts and rollbacks
    • Automatic bin packing
    • Self-healing
    • Secret and configuration management

    We'll learn more about most of these topics as we progress through Cloud Native New Year.

    What is Kubernetes?

    Let's hear from Brendan Burns, one of the founders of Kubernetes as to what Kubernetes actually is.

    How does Kubernetes Work?

    And Brendan shares a bit more with us about how Kubernetes works.

    Conclusion

    Kubernetes allows us to deploy and manage our applications effectively and consistently.

    By providing a consistent API across many of the concerns our applications have, like load balancing, networking, storage, and compute, Kubernetes improves both our ability to build and ship new software.

    There are standards for the applications to depend on for resources needed. Deployments, metrics, and logs are provided in a standardized fashion allowing more effecient operations across our application environments.

    And since Kubernetes is an open source platform, it can be found in just about every type of operating environment - cloud, virtual machines, physical hardware, shared data centers, even small devices like Rasberry Pi's!

    Want to learn more? Join us for a webinar on Kubernetes Concepts (or catch the playback) on Thursday, January 26th at 1 PM PST and watch for the rest of this series right here!

    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/ask-the-expert/page/5/index.html b/cnny-2023/tags/ask-the-expert/page/5/index.html index 71cf470e85..af3ef870e5 100644 --- a/cnny-2023/tags/ask-the-expert/page/5/index.html +++ b/cnny-2023/tags/ask-the-expert/page/5/index.html @@ -14,13 +14,13 @@ - - + +

    16 posts tagged with "ask-the-expert"

    View All Tags

    · 6 min read
    Josh Duffney

    Welcome to Day 4 of Week 1 of #CloudNativeNewYear!

    This week we'll focus on advanced topics and best practices for Cloud-Native practitioners, kicking off with this post on Serverless Container Options with Azure. We'll look at technologies, tools and best practices that range from managed services like Azure Kubernetes Service, to options allowing finer granularity of control and oversight.

    What We'll Cover

    • What is Microservice Architecture?
    • How do you design a Microservice?
    • What challenges do Microservices introduce?
    • Conclusion
    • Resources


    Microservices are a modern way of designing and building software that increases deployment velocity by decomposing an application into small autonomous services that can be deployed independently.

    By deploying loosely coupled microservices your applications can be developed, deployed, and scaled independently. Because each service is independent, it can be updated or replaced without having to worry about the impact on the rest of the application. This means that if a bug is found in one service, it can be fixed without having to redeploy the entire application. All of which gives an organization the ability to deliver value to their customers faster.

    In this article, we will explore the basics of microservices architecture, its benefits and challenges, and how it can help improve the development, deployment, and maintenance of software applications.

    What is Microservice Architecture?

    Before explaining what Microservice architecture is, it’s important to understand what problems microservices aim to address.

    Traditional software development is centered around building monolithic applications. Monolithic applications are built as a single, large codebase. Meaning your code is tightly coupled causing the monolithic app to suffer from the following:

    Too much Complexity: Monolithic applications can become complex and difficult to understand and maintain as they grow. This can make it hard to identify and fix bugs and add new features.

    Difficult to Scale: Monolithic applications can be difficult to scale as they often have a single point of failure, which can cause the whole application to crash if a service fails.

    Slow Deployment: Deploying a monolithic application can be risky and time-consuming, as a small change in one part of the codebase can affect the entire application.

    Microservice architecture (often called microservices) is an architecture style that addresses the challenges created by Monolithic applications. Microservices architecture is a way of designing and building software applications as a collection of small, independent services that communicate with each other through APIs. This allows for faster development and deployment cycles, as well as easier scaling and maintenance than is possible with a monolithic application.

    How do you design a Microservice?

    Building applications with Microservices architecture requires a different approach. Microservices architecture focuses on business capabilities rather than technical layers, such as data access or messaging. Doing so requires that you shift your focus away from the technical stack and model your applications based upon the various domains that exist within the business.

    Domain-driven design (DDD) is a way to design software by focusing on the business needs. You can use Domain-driven design as a framework that guides the development of well-designed microservices by building services that encapsulate knowledge in each domain and abstract that knowledge from clients.

    In Domain-driven design you start by modeling the business domain and creating a domain model. A domain model is an abstract model of the business model that distills and organizes a domain of knowledge and provides a common language for developers and domain experts. It’s the resulting domain model that microservices a best suited to be built around because it helps establish a well-defined boundary between external systems and other internal applications.

    In short, before you begin designing microservices, start by mapping the functions of the business and their connections to create a domain model for the microservice(s) to be built around.

    What challenges do Microservices introduce?

    Microservices solve a lot of problems and have several advantages, but the grass isn’t always greener on the other side.

    One of the key challenges of microservices is managing communication between services. Because services are independent, they need to communicate with each other through APIs. This can be complex and difficult to manage, especially as the number of services grows. To address this challenge, it is important to have a clear API design, with well-defined inputs and outputs for each service. It is also important to have a system for managing and monitoring communication between services, to ensure that everything is running smoothly.

    Another challenge of microservices is managing the deployment and scaling of services. Because each service is independent, it needs to be deployed and scaled separately from the rest of the application. This can be complex and difficult to manage, especially as the number of services grows. To address this challenge, it is important to have a clear and consistent deployment process, with well-defined steps for deploying and scaling each service. Furthermore, it is advisable to host them on a system with self-healing capabilities to reduce operational burden.

    It is also important to have a system for monitoring and managing the deployment and scaling of services, to ensure optimal performance.

    Each of these challenges has created fertile ground for tooling and process that exists in the cloud-native ecosystem. Kubernetes, CI CD, and other DevOps practices are part of the package of adopting the microservices architecture.

    Conclusion

    In summary, microservices architecture focuses on software applications as a collection of small, independent services that communicate with each other over well-defined APIs.

    The main advantages of microservices include:

    • increased flexibility and scalability per microservice,
    • efficient resource utilization (with help from a container orchestrator like Kubernetes),
    • and faster development cycles.

    Continue following along with this series to see how you can use Kubernetes to help adopt microservices patterns in your own environments!

    Resources

    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/ask-the-expert/page/6/index.html b/cnny-2023/tags/ask-the-expert/page/6/index.html index 3350718135..f9cbe90d7c 100644 --- a/cnny-2023/tags/ask-the-expert/page/6/index.html +++ b/cnny-2023/tags/ask-the-expert/page/6/index.html @@ -14,14 +14,14 @@ - - + +

    16 posts tagged with "ask-the-expert"

    View All Tags

    · 6 min read
    Cory Skimming

    We are excited to be wrapping up our first week of #CloudNativeNewYear! This week, we have tried to set the stage by covering the fundamentals of cloud-native practices and technologies, including primers on containerization, microservices, and Kubernetes.

    Don't forget to sign up for the the Cloud Skills Challenge!

    Today, we will do a brief recap of some of these technologies and provide some basic guidelines for when it is optimal to use each.


    What We'll Cover

    • To Containerize or not to Containerize?
    • The power of Kubernetes
    • Where does Serverless fit?
    • Resources
    • What's coming next!


    Just joining us now? Check out these other Week 1 posts:

    To Containerize or not to Containerize?

    As mentioned in our Containers 101 post earlier this week, containers can provide several benefits over traditional virtualization methods, which has made them popular within the software development community. Containers provide a consistent and predictable runtime environment, which can help reduce the risk of compatibility issues and simplify the deployment process. Additionally, containers can improve resource efficiency by allowing multiple applications to run on the same host while isolating their dependencies.

    Some types of apps that are a particularly good fit for containerization include:

    1. Microservices: Containers are particularly well-suited for microservices-based applications, as they can be used to isolate and deploy individual components of the system. This allows for more flexibility and scalability in the deployment process.
    2. Stateless applications: Applications that do not maintain state across multiple sessions, such as web applications, are well-suited for containers. Containers can be easily scaled up or down as needed and replaced with new instances, without losing data.
    3. Portable applications: Applications that need to be deployed in different environments, such as on-premises, in the cloud, or on edge devices, can benefit from containerization. The consistent and portable runtime environment of containers can make it easier to move the application between different environments.
    4. Legacy applications: Applications that are built using older technologies or that have compatibility issues can be containerized to run in an isolated environment, without impacting other applications or the host system.
    5. Dev and testing environments: Containerization can be used to create isolated development and testing environments, which can be easily created and destroyed as needed.

    While there are many types of applications that can benefit from a containerized approach, it's worth noting that containerization is not always the best option, and it's important to weigh the benefits and trade-offs before deciding to containerize an application. Additionally, some types of applications may not be a good fit for containers including:

    • Apps that require full access to host resources: Containers are isolated from the host system, so if an application needs direct access to hardware resources such as GPUs or specialized devices, it might not work well in a containerized environment.
    • Apps that require low-level system access: If an application requires deep access to the underlying operating system, it may not be suitable for running in a container.
    • Applications that have specific OS dependencies: Apps that have specific dependencies on a certain version of an operating system or libraries may not be able to run in a container.
    • Stateful applications: Apps that maintain state across multiple sessions, such as databases, may not be well suited for containers. Containers are ephemeral by design, so the data stored inside a container may not persist between restarts.

    The good news is that some of these limitations can be overcome with the use of specialized containerization technologies such as Kubernetes, and by carefully designing the architecture of the application.


    The power of Kubernetes

    Speaking of Kubernetes...

    Kubernetes is a powerful tool for managing and deploying containerized applications in production environments, particularly for applications that need to scale, handle large numbers of requests, or run in multi-cloud or hybrid environments.

    Kubernetes is well-suited for a wide variety of applications, but it is particularly well-suited for the following types of applications:

    1. Microservices-based applications: Kubernetes provides a powerful set of tools for managing and deploying microservices-based applications, making it easy to scale, update, and manage the individual components of the application.
    2. Stateful applications: Kubernetes provides support for stateful applications through the use of Persistent Volumes and StatefulSets, allowing for applications that need to maintain state across multiple instances.
    3. Large-scale, highly-available systems: Kubernetes provides built-in support for scaling, self-healing, and rolling updates, making it an ideal choice for large-scale, highly-available systems that need to handle large numbers of users and requests.
    4. Multi-cloud and hybrid environments: Kubernetes can be used to deploy and manage applications across multiple cloud providers and on-premises environments, making it a good choice for organizations that want to take advantage of the benefits of multiple cloud providers or that need to deploy applications in a hybrid environment.
    New to Kubernetes?

    Where does Serverless fit in?

    Serverless is a cloud computing model where the cloud provider (like Azure) is responsible for executing a piece of code by dynamically allocating the resources. With serverless, you only pay for the exact amount of compute time that you use, rather than paying for a fixed amount of resources. This can lead to significant cost savings, particularly for applications with variable or unpredictable workloads.

    Serverless is commonly used for building applications like web or mobile apps, IoT, data processing, and real-time streaming - apps where the workloads are variable and high scalability is required. It's important to note that serverless is not a replacement for all types of workloads - it's best suited for stateless, short-lived and small-scale workloads.

    For a detailed look into the world of Serverless and lots of great learning content, revisit #30DaysofServerless.


    Resources


    What's up next in #CloudNativeNewYear?

    Week 1 has been all about the fundamentals of cloud-native. Next week, the team will be diving in to application deployment with Azure Kubernetes Service. Don't forget to subscribe to the blog to get daily posts delivered directly to your favorite feed reader!


    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/ask-the-expert/page/7/index.html b/cnny-2023/tags/ask-the-expert/page/7/index.html index b0de10603a..c3671e9ed8 100644 --- a/cnny-2023/tags/ask-the-expert/page/7/index.html +++ b/cnny-2023/tags/ask-the-expert/page/7/index.html @@ -14,13 +14,13 @@ - - + +

    16 posts tagged with "ask-the-expert"

    View All Tags

    · 14 min read
    Steven Murawski

    Welcome to Day #1 of Week 2 of #CloudNativeNewYear!

    The theme for this week is Kubernetes fundamentals. Last week we talked about Cloud Native architectures and the Cloud Native landscape. Today we'll explore the topic of Pods and Deployments in Kubernetes.

    Ask the Experts Thursday, February 9th at 9 AM PST
    Catch the Replay of the Live Demo

    Watch the recorded demo and conversation about this week's topics.

    We were live on YouTube walking through today's (and the rest of this week's) demos.

    What We'll Cover

    • Setting Up A Kubernetes Environment in Azure
    • Running Containers in Kubernetes Pods
    • Making the Pods Resilient with Deployments
    • Exercise
    • Resources

    Setting Up A Kubernetes Environment in Azure

    For this week, we'll be working with a simple app - the Azure Voting App. My teammate Paul Yu ported the app to Rust and we tweaked it a bit to let us highlight some of the basic features of Kubernetes.

    You should be able to replicate this in just about any Kubernetes environment, but we'll use Azure Kubernetes Service (AKS) as our working environment for this week.

    To make it easier to get started, there's a Bicep template to deploy an AKS cluster, an Azure Container Registry (ACR) (to host our container image), and connect the two so that we can easily deploy our application.

    Step 0 - Prerequisites

    There are a few things you'll need if you want to work through this and the following examples this week.

    Required:

    • Git (and probably a GitHub account if you want to persist your work outside of your computer)
    • Azure CLI
    • An Azure subscription (if you want to follow along with the Azure steps)
    • Kubectl (the command line tool for managing Kubernetes)

    Helpful:

    • Visual Studio Code (or equivalent editor)

    Step 1 - Clone the application repository

    First, I forked the source repository to my account.

    $GitHubOrg = 'smurawski' # Replace this with your GitHub account name or org name
    git clone "https://github.com/$GitHubOrg/azure-voting-app-rust"
    cd azure-voting-app-rust

    Leave your shell opened with your current location inside the application repository.

    Step 2 - Set up AKS

    Running the template deployment from the demo script (I'm using the PowerShell example in cnny23-week2-day1.ps1, but there's a Bash variant at cnny23-week2-day1.sh) stands up the environment. The second, third, and fourth commands take some of the output from the Bicep deployment to set up for later commands, so don't close out your shell after you run these commands.

    az deployment sub create --template-file ./deploy/main.bicep --location eastus --parameters 'resourceGroup=cnny-week2'
    $AcrName = az deployment sub show --name main --query 'properties.outputs.acr_name.value' -o tsv
    $AksName = az deployment sub show --name main --query 'properties.outputs.aks_name.value' -o tsv
    $ResourceGroup = az deployment sub show --name main --query 'properties.outputs.resource_group_name.value' -o tsv

    az aks get-credentials --resource-group $ResourceGroup --name $AksName

    Step 3 - Build our application container

    Since we have an Azure Container Registry set up, I'll use ACR Build Tasks to build and store my container image.

    az acr build --registry $AcrName --% --image cnny2023/azure-voting-app-rust:{{.Run.ID}} .
    $BuildTag = az acr repository show-tags `
    --name $AcrName `
    --repository cnny2023/azure-voting-app-rust `
    --orderby time_desc `
    --query '[0]' -o tsv
    tip

    Wondering what the --% is in the first command line? That tells the PowerShell interpreter to pass the input after it "as is" to the command without parsing/evaluating it. Otherwise, PowerShell messes a bit with the templated {{.Run.ID}} bit.

    Running Containers in Kubernetes Pods

    Now that we have our AKS cluster and application image ready to go, let's look into how Kubernetes runs containers.

    If you've been in tech for any length of time, you've seen that every framework, runtime, orchestrator, etc.. can have their own naming scheme for their concepts. So let's get into some of what Kubernetes calls things.

    The Pod

    A container running in Kubernetes is called a Pod. A Pod is basically a running container on a Node or VM. It can be more. For example you can run multiple containers and specify some funky configuration, but we'll keep it simple for now - add the complexity when you need it.

    Our Pod definition can be created via the kubectl command imperatively from arguments or declaratively from a configuration file. We'll do a little of both. We'll use the kubectl command to help us write our configuration files. Kubernetes configuration files are YAML, so having an editor that supports and can help you syntax check YAML is really helpful.

    Creating a Pod Definition

    Let's create a few Pod definitions. Our application requires two containers to get working - the application and a database.

    Let's create the database Pod first. And before you comment, the configuration isn't secure nor best practice. We'll fix that later this week. For now, let's focus on getting up and running.

    This is a trick I learned from one of my teammates - Paul. By using the --output yaml and --dry-run=client options, we can have the command help us write our YAML. And with a bit of output redirection, we can stash it safely in a file for later use.

    kubectl run azure-voting-db `
    --image "postgres:15.0-alpine" `
    --env "POSTGRES_PASSWORD=mypassword" `
    --output yaml `
    --dry-run=client > manifests/pod-db.yaml

    This creates a file that looks like:

    apiVersion: v1
    kind: Pod
    metadata:
    creationTimestamp: null
    labels:
    run: azure-voting-db
    name: azure-voting-db
    spec:
    containers:
    - env:
    - name: POSTGRES_PASSWORD
    value: mypassword
    image: postgres:15.0-alpine
    name: azure-voting-db
    resources: {}
    dnsPolicy: ClusterFirst
    restartPolicy: Always
    status: {}

    The file, when supplied to the Kubernetes API, will identify what kind of resource to create, the API version to use, and the details of the container (as well as an environment variable to be supplied).

    We'll get that container image started with the kubectl command. Because the details of what to create are in the file, we don't need to specify much else to the kubectl command but the path to the file.

    kubectl apply -f ./manifests/pod-db.yaml

    I'm going to need the IP address of the Pod, so that my application can connect to it, so we can use kubectl to get some information about our pod. By default, kubectl get pod only displays certain information but it retrieves a lot more. We can use the JSONPath syntax to index into the response and get the information you want.

    tip

    To see what you can get, I usually run the kubectl command with the output type (-o JSON) of JSON and then I can find where the data I want is and create my JSONPath query to get it.

    $DB_IP = kubectl get pod azure-voting-db -o jsonpath='{.status.podIP}'

    Now, let's create our Pod definition for our application. We'll use the same technique as before.

    kubectl run azure-voting-app `
    --image "$AcrName.azurecr.io/cnny2023/azure-voting-app-rust:$BuildTag" `
    --env "DATABASE_SERVER=$DB_IP" `
    --env "DATABASE_PASSWORD=mypassword`
    --output yaml `
    --dry-run=client > manifests/pod-app.yaml

    That command gets us a similar YAML file to the database container - you can see the full file here

    Let's get our application container running.

    kubectl apply -f ./manifests/pod-app.yaml

    Now that the Application is Running

    We can check the status of our Pods with:

    kubectl get pods

    And we should see something like:

    azure-voting-app-rust ❯  kubectl get pods
    NAME READY STATUS RESTARTS AGE
    azure-voting-app 1/1 Running 0 36s
    azure-voting-db 1/1 Running 0 84s

    Once our pod is running, we can check to make sure everything is working by letting kubectl proxy network connections to our Pod running the application. If we get the voting web page, we'll know the application found the database and we can start voting!

    kubectl port-forward pod/azure-voting-app 8080:8080

    Azure voting website in a browser with three buttons, one for Dogs, one for Cats, and one for Reset.  The counter is Dogs - 0 and Cats - 0.

    When you are done voting, you can stop the port forwarding by using Control-C to break the command.

    Clean Up

    Let's clean up after ourselves and see if we can't get Kubernetes to help us keep our application running. We can use the same configuration files to ensure that Kubernetes only removes what we want removed.

    kubectl delete -f ./manifests/pod-app.yaml
    kubectl delete -f ./manifests/pod-db.yaml

    Summary - Pods

    A Pod is the most basic unit of work inside Kubernetes. Once the Pod is deleted, it's gone. That leads us to our next topic (and final topic for today.)

    Making the Pods Resilient with Deployments

    We've seen how easy it is to deploy a Pod and get our containers running on Nodes in our Kubernetes cluster. But there's a problem with that. Let's illustrate it.

    Breaking Stuff

    Setting Back Up

    First, let's redeploy our application environment. We'll start with our application container.

    kubectl apply -f ./manifests/pod-db.yaml
    kubectl get pod azure-voting-db -o jsonpath='{.status.podIP}'

    The second command will report out the new IP Address for our database container. Let's open ./manifests/pod-app.yaml and update the container IP to our new one.

    - name: DATABASE_SERVER
    value: YOUR_NEW_IP_HERE

    Then we can deploy the application with the information it needs to find its database. We'll also list out our pods to see what is running.

    kubectl apply -f ./manifests/pod-app.yaml
    kubectl get pods

    Feel free to look back and use the port forwarding trick to make sure your app is running if you'd like.

    Knocking It Down

    The first thing we'll try to break is our application pod. Let's delete it.

    kubectl delete pod azure-voting-app

    Then, we'll check our pod's status:

    kubectl get pods

    Which should show something like:

    azure-voting-app-rust ❯  kubectl get pods
    NAME READY STATUS RESTARTS AGE
    azure-voting-db 1/1 Running 0 50s

    We should be able to recreate our application pod deployment with no problem, since it has the current database IP address and nothing else depends on it.

    kubectl apply -f ./manifests/pod-app.yaml

    Again, feel free to do some fun port forwarding and check your site is running.

    Uncomfortable Truths

    Here's where it gets a bit stickier, what if we delete the database container?

    If we delete our database container and recreate it, it'll likely have a new IP address, which would force us to update our application configuration. We'll look at some solutions for these problems in the next three posts this week.

    Because our database problem is a bit tricky, we'll primarily focus on making our application layer more resilient and prepare our database layer for those other techniques over the next few days.

    Let's clean back up and look into making things more resilient.

    kubectl delete -f ./manifests/pod-app.yaml
    kubectl delete -f ./manifests/pod-db.yaml

    The Deployment

    One of the reasons you may want to use Kubernetes is it's ability to orchestrate workloads. Part of that orchestration includes being able to ensure that certain workloads are running (regardless of what Node they might be on).

    We saw that we could delete our application pod and then restart it from the manifest with little problem. It just meant that we had to run a command to restart it. We can use the Deployment in Kubernetes to tell the orchestrator to ensure we have our application pod running.

    The Deployment also can encompass a lot of extra configuration - controlling how many containers of a particular type should be running, how upgrades of container images should proceed, and more.

    Creating the Deployment

    First, we'll create a Deployment for our database. We'll use a technique similar to what we did for the Pod, with just a bit of difference.

    kubectl create deployment azure-voting-db `
    --image "postgres:15.0-alpine" `
    --port 5432 `
    --output yaml `
    --dry-run=client > manifests/deployment-db.yaml

    Unlike our Pod definition creation, we can't pass in environment variable configuration from the command line. We'll have to edit the YAML file to add that.

    So, let's open ./manifests/deployment-db.yaml in our editor and add the following in the spec/containers configuration.

            env:
    - name: POSTGRES_PASSWORD
    value: "mypassword"

    Your file should look like this deployment-db.yaml.

    Once we have our configuration file updated, we can deploy our database container image.

    kubectl apply -f ./manifests/deployment-db.yaml

    For our application, we'll use the same technique.

    kubectl create deployment azure-voting-app `
    --image "$AcrName.azurecr.io/cnny2023/azure-voting-app-rust:$BuildTag" `
    --port 8080 `
    --output yaml `
    --dry-run=client > manifests/deployment-app.yaml

    Next, we'll need to add an environment variable to the generated configuration. We'll also need the new IP address for the database deployment.

    Previously, we named the pod and were able to ask for the IP address with kubectl and a bit of JSONPath. Now, the deployment created the pod for us, so there's a bit of random in the naming. Check out:

    kubectl get pods

    Should return something like:

    azure-voting-app-rust ❯  kubectl get pods
    NAME READY STATUS RESTARTS AGE
    azure-voting-db-686d758fbf-8jnq8 1/1 Running 0 7s

    We can either ask for the IP with the new pod name, or we can use a selector to find our desired pod.

    kubectl get pod --selector app=azure-voting-db -o jsonpath='{.items[0].status.podIP}'

    Now, we can update our application deployment configuration file with:

            env:
    - name: DATABASE_SERVER
    value: YOUR_NEW_IP_HERE
    - name: DATABASE_PASSWORD
    value: mypassword

    Your file should look like this deployment-app.yaml (but with IPs and image names matching your environment).

    After we save those changes, we can deploy our application.

    kubectl apply -f ./manifests/deployment-app.yaml

    Let's test the resilience of our app now. First, we'll delete the pod running our application, then we'll check to make sure Kubernetes restarted our application pod.

    kubectl get pods
    azure-voting-app-rust ❯  kubectl get pods
    NAME READY STATUS RESTARTS AGE
    azure-voting-app-56c9ccc89d-skv7x 1/1 Running 0 71s
    azure-voting-db-686d758fbf-8jnq8 1/1 Running 0 12m
    kubectl delete pod azure-voting-app-56c9ccc89d-skv7x
    kubectl get pods
    azure-voting-app-rust ❯  kubectl delete pod azure-voting-app-56c9ccc89d-skv7x
    >> kubectl get pods
    pod "azure-voting-app-56c9ccc89d-skv7x" deleted
    NAME READY STATUS RESTARTS AGE
    azure-voting-app-56c9ccc89d-2b5mx 1/1 Running 0 2s
    azure-voting-db-686d758fbf-8jnq8 1/1 Running 0 14m
    info

    Your Pods will likely have different identifiers at the end, so adjust your commands to match the names in your environment.

    As you can see, by the time the kubectl get pods command was run, Kubernetes had already spun up a new pod for the application container image. Thanks Kubernetes!

    Clean up

    Since we can't just delete the pods, we have to delete the deployments.

    kubectl delete -f ./manifests/deployment-app.yaml
    kubectl delete -f ./manifests/deployment-db.yaml

    Summary - Deployments

    Deployments allow us to create more durable configuration for the workloads we deploy into Kubernetes. As we dig deeper, we'll discover more capabilities the deployments offer. Check out the Resources below for more.

    Exercise

    If you want to try these steps, head over to the source repository, fork it, clone it locally, and give it a spin!

    You can check your manifests against the manifests in the week2/day1 branch of the source repository.

    Resources

    Take the Cloud Skills Challenge!

    Enroll in the Cloud Skills Challenge!

    Don't miss out on this opportunity to level up your skills and stay ahead of the curve in the world of cloud native.

    Documentation

    Training

    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/ask-the-expert/page/8/index.html b/cnny-2023/tags/ask-the-expert/page/8/index.html index 6354f297db..63ecc32093 100644 --- a/cnny-2023/tags/ask-the-expert/page/8/index.html +++ b/cnny-2023/tags/ask-the-expert/page/8/index.html @@ -14,14 +14,14 @@ - - + +

    16 posts tagged with "ask-the-expert"

    View All Tags

    · 6 min read
    Josh Duffney

    Welcome to Day 3 of Week 2 of #CloudNativeNewYear!

    The theme for this week is Kubernetes fundamentals. Yesterday we talked about Services and Ingress. Today we'll explore the topic of passing configuration and secrets to our applications in Kubernetes with ConfigMaps and Secrets.

    Ask the Experts Thursday, February 9th at 9 AM PST
    Catch the Replay of the Live Demo

    Watch the recorded demo and conversation about this week's topics.

    We were live on YouTube walking through today's (and the rest of this week's) demos.

    What We'll Cover

    • Decouple configurations with ConfigMaps and Secerts
    • Passing Environment Data with ConfigMaps and Secrets
    • Conclusion

    Decouple configurations with ConfigMaps and Secerts

    A ConfigMap is a Kubernetes object that decouples configuration data from pod definitions. Kubernetes secerts are similar, but were designed to decouple senstive information.

    Separating the configuration and secerts from your application promotes better organization and security of your Kubernetes environment. It also enables you to share the same configuration and different secerts across multiple pods and deployments which can simplify scaling and management. Using ConfigMaps and Secerts in Kubernetes is a best practice that can help to improve the scalability, security, and maintainability of your cluster.

    By the end of this tutorial, you'll have added a Kubernetes ConfigMap and Secret to the Azure Voting deployment.

    Passing Environment Data with ConfigMaps and Secrets

    📝 NOTE: If you don't have an AKS cluster deployed, please head over to Azure-Samples/azure-voting-app-rust, clone the repo, and follow the instructions in the README.md to execute the Azure deployment and setup your kubectl context. Check out the first post this week for more on the environment setup.

    Create the ConfigMap

    ConfigMaps can be used in one of two ways; as environment variables or volumes.

    For this tutorial you'll use a ConfigMap to create three environment variables inside the pod; DATABASE_SERVER, FISRT_VALUE, and SECOND_VALUE. The DATABASE_SERVER provides part of connection string to a Postgres. FIRST_VALUE and SECOND_VALUE are configuration options that change what voting options the application presents to the users.

    Follow the below steps to create a new ConfigMap:

    1. Create a YAML file named 'config-map.yaml'. In this file, specify the environment variables for the application.

      apiVersion: v1
      kind: ConfigMap
      metadata:
      name: azure-voting-config
      data:
      DATABASE_SERVER: azure-voting-db
      FIRST_VALUE: "Go"
      SECOND_VALUE: "Rust"
    2. Create the config map in your Kubernetes cluster by running the following command:

      kubectl create -f config-map.yaml

    Create the Secret

    The deployment-db.yaml and deployment-app.yaml are Kubernetes manifests that deploy the Azure Voting App. Currently, those deployment manifests contain the environment variables POSTGRES_PASSWORD and DATABASE_PASSWORD with the value stored as plain text. Your task is to replace that environment variable with a Kubernetes Secret.

    Create a Secret running the following commands:

    1. Encode mypassword.

      echo -n "mypassword" | base64
    2. Create a YAML file named secret.yaml. In this file, add POSTGRES_PASSWORD as the key and the encoded value returned above under as the value in the data section.

      apiVersion: v1
      kind: Secret
      metadata:
      name: azure-voting-secret
      type: Opaque
      data:
      POSTGRES_PASSWORD: bXlwYXNzd29yZA==
    3. Create the Secret in your Kubernetes cluster by running the following command:

      kubectl create -f secret.yaml

    [!WARNING] base64 encoding is a simple and widely supported way to obscure plaintext data, it is not secure, as it can easily be decoded. If you want to store sensitive data like password, you should use a more secure method like encrypting with a Key Management Service (KMS) before storing it in the Secret.

    Modify the app deployment manifest

    With the ConfigMap and Secert both created the next step is to replace the environment variables provided in the application deployment manuscript with the values stored in the ConfigMap and the Secert.

    Complete the following steps to add the ConfigMap and Secert to the deployment mainifest:

    1. Open the Kubernetes manifest file deployment-app.yaml.

    2. In the containers section, add an envFrom section and upate the env section.

      envFrom:
      - configMapRef:
      name: azure-voting-config
      env:
      - name: DATABASE_PASSWORD
      valueFrom:
      secretKeyRef:
      name: azure-voting-secret
      key: POSTGRES_PASSWORD

      Using envFrom exposes all the values witin the ConfigMap as environment variables. Making it so you don't have to list them individually.

    3. Save the changes to the deployment manifest file.

    4. Apply the changes to the deployment by running the following command:

      kubectl apply -f deployment-app.yaml

    Modify the database deployment manifest

    Next, update the database deployment manifest and replace the plain text environment variable with the Kubernetes Secert.

    1. Open the deployment-db.yaml.

    2. To add the secret to the deployment, replace the env section with the following code:

      env:
      - name: POSTGRES_PASSWORD
      valueFrom:
      secretKeyRef:
      name: azure-voting-secret
      key: POSTGRES_PASSWORD
    3. Apply the updated manifest.

      kubectl apply -f deployment-db.yaml

    Verify the ConfigMap and output environment variables

    Verify that the ConfigMap was added to your deploy by running the following command:

    ```bash
    kubectl describe deployment azure-voting-app
    ```

    Browse the output until you find the envFrom section with the config map reference.

    You can also verify that the environment variables from the config map are being passed to the container by running the command kubectl exec -it <pod-name> -- printenv. This command will show you all the environment variables passed to the pod including the one from configmap.

    By following these steps, you will have successfully added a config map to the Azure Voting App Kubernetes deployment, and the environment variables defined in the config map will be passed to the container running in the pod.

    Verify the Secret and describe the deployment

    Once the secret has been created you can verify it exists by running the following command:

    kubectl get secrets

    You can view additional information, such as labels, annotations, type, and the Data by running kubectl describe:

    kubectl describe secret azure-voting-secret

    By default, the describe command doesn't output the encoded value, but if you output the results as JSON or YAML you'll be able to see the secret's encoded value.

     kubectl get secret azure-voting-secret -o json

    Conclusion

    In conclusion, using ConfigMaps and Secrets in Kubernetes can help to improve the scalability, security, and maintainability of your cluster. By decoupling configuration data and sensitive information from pod definitions, you can promote better organization and security in your Kubernetes environment. Additionally, separating these elements allows for sharing the same configuration and different secrets across multiple pods and deployments, simplifying scaling and management.

    Take the Cloud Skills Challenge!

    Enroll in the Cloud Skills Challenge!

    Don't miss out on this opportunity to level up your skills and stay ahead of the curve in the world of cloud native.

    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/ask-the-expert/page/9/index.html b/cnny-2023/tags/ask-the-expert/page/9/index.html index dd542ebbdb..c94ae3bc2e 100644 --- a/cnny-2023/tags/ask-the-expert/page/9/index.html +++ b/cnny-2023/tags/ask-the-expert/page/9/index.html @@ -14,13 +14,13 @@ - - + +

    16 posts tagged with "ask-the-expert"

    View All Tags

    · 10 min read
    Steven Murawski

    Welcome to Day 5 of Week 2 of #CloudNativeNewYear!

    The theme for this week is Kubernetes fundamentals. Yesterday we talked about adding persistent storage to our deployment. Today we'll explore the topic of scaling pods and nodes in our Kubernetes cluster.

    Ask the Experts Thursday, February 9th at 9 AM PST
    Catch the Replay of the Live Demo

    Watch the recorded demo and conversation about this week's topics.

    We were live on YouTube walking through today's (and the rest of this week's) demos.

    What We'll Cover

    • Scaling Our Application
    • Scaling Pods
    • Scaling Nodes
    • Exercise
    • Resources

    Scaling Our Application

    One of our primary reasons to use a service like Kubernetes to orchestrate our workloads is the ability to scale. We've approached scaling in a multitude of ways over the years, taking advantage of the ever-evolving levels of hardware and software. Kubernetes allows us to scale our units of work, Pods, and the Nodes they run on. This allows us to take advantage of both hardware and software scaling abilities. Kubernetes can help improve the utilization of existing hardware (by scheduling Pods on Nodes that have resource capacity). And, with the capabilities of virtualization and/or cloud hosting (or a bit more work, if you have a pool of physical machines), Kubernetes can expand (or contract) the number of Nodes capable of hosting Pods. Scaling is primarily driven by resource utilization, but can be triggered by a variety of other sources thanks to projects like Kubernetes Event-driven Autoscaling (KEDA).

    Scaling Pods

    Our first level of scaling is with our Pods. Earlier, when we worked on our deployment, we talked about how the Kubernetes would use the deployment configuration to ensure that we had the desired workloads running. One thing we didn't explore was running more than one instance of a pod. We can define a number of replicas of a pod in our Deployment.

    Manually Scale Pods

    So, if we wanted to define more pods right at the start (or at any point really), we could update our deployment configuration file with the number of replicas and apply that configuration file.

    spec:
    replicas: 5

    Or we could use the kubectl scale command to update the deployment with a number of pods to create.

    kubectl scale --replicas=5 deployment/azure-voting-app

    Both of these approaches modify the running configuration of our Kubernetes cluster and request that it ensure that we have that set number of replicas running. Because this was a manual change, the Kubernetes cluster won't automatically increase or decrease the number of pods. It'll just ensure that there are always the specified number of pods running.

    Autoscale Pods with the Horizontal Pod Autoscaler

    Another approach to scaling our pods is to allow the Horizontal Pod Autoscaler to help us scale in response to resources being used by the pod. This requires a bit more configuration up front. When we define our pod in our deployment, we need to include resource requests and limits. The requests help Kubernetes determine what nodes may have capacity for a new instance of a pod. The limit tells us where the node should cap utilization for a particular instance of a pod. For example, we'll update our deployment to request 0.25 CPU and set a limit of 0.5 CPU.

        spec:
    containers:
    - image: acrudavoz.azurecr.io/cnny2023/azure-voting-app-rust:ca4
    name: azure-voting-app-rust
    ports:
    - containerPort: 8080
    env:
    - name: DATABASE_URL
    value: postgres://postgres:mypassword@10.244.0.29
    resources:
    requests:
    cpu: 250m
    limits:
    cpu: 500m

    Now that we've given Kubernetes an allowed range and an idea of what free resources a node should have to place new pods, we can set up autoscaling. Because autoscaling is a persistent configuration, I like to define it in a configuration file that I'll be able to keep with the rest of my cluster configuration. We'll use the kubectl command to help us write the configuration file. We'll request that Kubernetes watch our pods and when the average CPU utilization if 50% of the requested usage (in our case if it's using more than 0.375 CPU across the current number of pods), it can grow the number of pods serving requests up to 10. If the utilization drops, Kubernetes will have the permission to deprovision pods down to the minimum (three in our example).

    kubectl autoscale deployment azure-voting-app --cpu-percent=50 --min=3 --max=10 -o YAML --dry-run=client

    Which would give us:

    apiVersion: autoscaling/v1
    kind: HorizontalPodAutoscaler
    metadata:
    creationTimestamp: null
    name: azure-voting-app
    spec:
    maxReplicas: 10
    minReplicas: 3
    scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: azure-voting-app
    targetCPUUtilizationPercentage: 50
    status:
    currentReplicas: 0
    desiredReplicas: 0

    So, how often does the autoscaler check the metrics being monitored? The autoscaler checks the Metrics API every 15 seconds, however the pods stats are only updated every 60 seconds. This means that an autoscale event may be evaluated about once a minute. Once an autoscale down event happens however, Kubernetes has a cooldown period to give the new pods a chance to distribute the workload and let the new metrics accumulate. There is no delay on scale up events.

    Application Architecture Considerations

    We've focused in this example on our front end, which is an easier scaling story. When we start talking about scaling our database layers or anything that deals with persistent storage or has primary/replica configuration requirements things get a bit more complicated. Some of these applications may have built-in leader election or could use sidecars to help use existing features in Kubernetes to perform that function. For shared storage scenarios, persistent volumes (or persistent volumes with Azure) can be of help, if the application knows how to play well with shared file access.

    Ultimately, you know your application architecture and, while Kubernetes may not have an exact match to how you are doing things today, the underlying capability is probably there under a different name. This abstraction allows you to more effectively use Kubernetes to operate a variety of workloads with the levels of controls you need.

    Scaling Nodes

    We've looked at how to scale our pods, but that assumes we have enough resources in our existing pool of nodes to accomodate those scaling requests. Kubernetes can also help scale our available nodes to ensure that our applications have the necessary resources to meet their performance requirements.

    Manually Scale Nodes

    Manually scaling nodes isn't a direct function of Kubernetes, so your operating environment instructions may vary. On Azure, it's pretty straight forward. Using the Azure CLI (or other tools), we can tell our AKS cluster to scale up or scale down the number of nodes in our node pool.

    First, we'll check out how many nodes we currently have in our working environment.

    kubectl get nodes

    This will show us

    azure-voting-app-rust ❯  kubectl get nodes
    NAME STATUS ROLES AGE VERSION
    aks-pool0-37917684-vmss000000 Ready agent 5d21h v1.24.6

    Then, we'll scale it up to three nodes.

    az aks scale --resource-group $ResourceGroup --name $AksName --node-count 3

    Then, we'll check out how many nodes we now have in our working environment.

    kubectl get nodes

    Which returns:

    azure-voting-app-rust ❯  kubectl get nodes
    NAME STATUS ROLES AGE VERSION
    aks-pool0-37917684-vmss000000 Ready agent 5d21h v1.24.6
    aks-pool0-37917684-vmss000001 Ready agent 5m27s v1.24.6
    aks-pool0-37917684-vmss000002 Ready agent 5m10s v1.24.6

    Autoscale Nodes with the Cluster Autoscaler

    Things get more interesting when we start working with the Cluster Autoscaler. The Cluster Autoscaler watches for the inability of Kubernetes to schedule the required number of pods due to resource constraints (and a few other criteria like affinity/anti-affinity). If there are insufficient resources available on the existing nodes, the autoscaler can provision new nodes into the nodepool. Likewise, the autoscaler watches to see if the existing pods could be consolidated to a smaller set of nodes and can remove excess nodes.

    Enabling the autoscaler is likewise an update that can be dependent on where and how your Kubernetes cluster is hosted. Azure makes it easy with a simple Azure CLI command.

    az aks update `
    --resource-group $ResourceGroup `
    --name $AksName `
    --update-cluster-autoscaler `
    --min-count 1 `
    --max-count 5

    There are a variety of settings that can be configured to tune how the autoscaler works.

    Scaling on Different Events

    CPU and memory utilization are the primary drivers for the Horizontal Pod Autoscaler, but those might not be the best measures as to when you might want to scale workloads. There are other options for scaling triggers and one of the more common plugins to help with that is the Kubernetes Event-driven Autoscaling (KEDA) project. The KEDA project makes it easy to plug in different event sources to help drive scaling. Find more information about using KEDA on AKS here.

    Exercise

    Let's try out the scaling configurations that we just walked through using our sample application. If you still have your environment from Day 1, you can use that.

    📝 NOTE: If you don't have an AKS cluster deployed, please head over to Azure-Samples/azure-voting-app-rust, clone the repo, and follow the instructions in the README.md to execute the Azure deployment and setup your kubectl context. Check out the first post this week for more on the environment setup.

    Configure Horizontal Pod Autoscaler

    • Edit ./manifests/deployment-app.yaml to include resource requests and limits.
            resources:
    requests:
    cpu: 250m
    limits:
    cpu: 500m
    • Apply the updated deployment configuration.
    kubectl apply -f ./manifests/deployment-app.yaml
    • Create the horizontal pod autoscaler configuration and apply it
    kubectl autoscale deployment azure-voting-app --cpu-percent=50 --min=3 --max=10 -o YAML --dry-run=client > ./manifests/scaler-app.yaml
    kubectl apply -f ./manifests/scaler-app.yaml
    • Check to see your pods scale out to the minimum.
    kubectl get pods

    Configure Cluster Autoscaler

    Configuring the basic behavior of the Cluster Autoscaler is a bit simpler. We just need to run the Azure CLI command to enable the autoscaler and define our lower and upper limits.

    • Check the current nodes available (should be 1).
    kubectl get nodes
    • Update the cluster to enable the autoscaler
    az aks update `
    --resource-group $ResourceGroup `
    --name $AksName `
    --update-cluster-autoscaler `
    --min-count 2 `
    --max-count 5
    • Check to see the current number of nodes (should be 2 now).
    kubectl get nodes

    Resources

    Take the Cloud Skills Challenge!

    Enroll in the Cloud Skills Challenge!

    Don't miss out on this opportunity to level up your skills and stay ahead of the curve in the world of cloud native.

    Documentation

    Training

    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/azure-dns/index.html b/cnny-2023/tags/azure-dns/index.html index 8bc138b57e..4ddfa323f4 100644 --- a/cnny-2023/tags/azure-dns/index.html +++ b/cnny-2023/tags/azure-dns/index.html @@ -14,13 +14,13 @@ - - + +

    One post tagged with "azure-dns"

    View All Tags

    · 10 min read
    Paul Yu

    Welcome to Day 3 of Week 3 of #CloudNativeNewYear!

    The theme for this week is Bringing Your Application to Kubernetes. Yesterday we added configuration, secrets, and storage to our app. Today we'll explore how to expose the eShopOnWeb app so that customers can reach it over the internet using a custom domain name and TLS.

    Ask the Experts Thursday, February 9th at 9 AM PST
    Friday, February 10th at 11 AM PST

    Watch the recorded demo and conversation about this week's topics.

    We were live on YouTube walking through today's (and the rest of this week's) demos. Join us Friday, February 10th and bring your questions!

    What We'll Cover

    • Gather requirements
    • Generate TLS certificate and store in Azure Key Vault
    • Implement custom DNS using Azure DNS
    • Enable Web Application Routing add-on for AKS
    • Implement Ingress for the web application
    • Conclusion
    • Resources

    Gather requirements

    Currently, our eShopOnWeb app has three Kubernetes services deployed:

    1. db exposed internally via ClusterIP
    2. api exposed externally via LoadBalancer
    3. web exposed externally via LoadBalancer

    As mentioned in my post last week, Services allow applications to communicate with each other using DNS names. Kubernetes has service discovery capabilities built-in that allows Pods to resolve Services simply by using their names.

    In the case of our api and web deployments, they can simply reach the database by calling its name. The service type of ClusterIP for the db can remain as-is since it only needs to be accessed by the api and web apps.

    On the other hand, api and web both need to be accessed over the public internet. Currently, these services are using service type LoadBalancer which tells AKS to provision an Azure Load Balancer with a public IP address. No one is going to remember the IP addresses, so we need to make the app more accessible by adding a custom domain name and securing it with a TLS certificate.

    Here's what we're going to need:

    • Custom domain name for our app
    • TLS certificate for the custom domain name
    • Routing rule to ensure requests with /api/ in the URL is routed to the backend REST API
    • Routing rule to ensure requests without /api/ in the URL is routing to the web UI

    Just like last week, we will use the Web Application Routing add-on for AKS. But this time, we'll integrate it with Azure DNS and Azure Key Vault to satisfy all of our requirements above.

    info

    At the time of this writing the add-on is still in Public Preview

    Generate TLS certificate and store in Azure Key Vault

    We deployed an Azure Key Vault yesterday to store secrets. We'll use it again to store a TLS certificate too.

    Let's create and export a self-signed certificate for the custom domain.

    DNS_NAME=eshoponweb$RANDOM.com
    openssl req -new -x509 -nodes -out web-tls.crt -keyout web-tls.key -subj "/CN=${DNS_NAME}" -addext "subjectAltName=DNS:${DNS_NAME}"
    openssl pkcs12 -export -in web-tls.crt -inkey web-tls.key -out web-tls.pfx -password pass:
    info

    For learning purposes we'll use a self-signed certificate and a fake custom domain name.

    To browse to the site using the fake domain, we'll mimic a DNS lookup by adding an entry to your host file which maps the public IP address assigned to the ingress controller to the custom domain.

    In a production scenario, you will need to have a real domain delegated to Azure DNS and a valid TLS certificate for the domain.

    Grab your Azure Key Vault name and set the value in a variable for later use.

    RESOURCE_GROUP=cnny-week3

    AKV_NAME=$(az resource list \
    --resource-group $RESOURCE_GROUP \
    --resource-type Microsoft.KeyVault/vaults \
    --query "[0].name" -o tsv)

    Grant yourself permissions to get, list, and import certificates.

    MY_USER_NAME=$(az account show --query user.name -o tsv)
    MY_USER_OBJECT_ID=$(az ad user show --id $MY_USER_NAME --query id -o tsv)

    az keyvault set-policy \
    --name $AKV_NAME \
    --object-id $MY_USER_OBJECT_ID \
    --certificate-permissions get list import

    Upload the TLS certificate to Azure Key Vault and grab its certificate URI.

    WEB_TLS_CERT_ID=$(az keyvault certificate import \
    --vault-name $AKV_NAME \
    --name web-tls \
    --file web-tls.pfx \
    --query id \
    --output tsv)

    Implement custom DNS with Azure DNS

    Create a custom domain for our application and grab its Azure resource id.

    DNS_ZONE_ID=$(az network dns zone create \
    --name $DNS_NAME \
    --resource-group $RESOURCE_GROUP \
    --query id \
    --output tsv)

    Enable Web Application Routing add-on for AKS

    As we enable the Web Application Routing add-on, we'll also pass in the Azure DNS Zone resource id which triggers the installation of the external-dns controller in your Kubernetes cluster. This controller will be able to write Azure DNS zone entries on your behalf as you deploy Ingress manifests.

    AKS_NAME=$(az resource list \
    --resource-group $RESOURCE_GROUP \
    --resource-type Microsoft.ContainerService/managedClusters \
    --query "[0].name" -o tsv)

    az aks enable-addons \
    --name $AKS_NAME \
    --resource-group $RESOURCE_GROUP \
    --addons web_application_routing \
    --dns-zone-resource-id=$DNS_ZONE_ID \
    --enable-secret-rotation

    The add-on will also deploy a new Azure Managed Identity which is used by the external-dns controller when writing Azure DNS zone entries. Currently, it does not have permission to do that, so let's grant it permission.

    # This is where resources are automatically deployed by AKS
    NODE_RESOURCE_GROUP=$(az aks show \
    --name $AKS_NAME \
    --resource-group $RESOURCE_GROUP \
    --query nodeResourceGroup -o tsv)

    # This is the managed identity created by the Web Application Routing add-on
    MANAGED_IDENTTIY_OBJECT_ID=$(az resource show \
    --name webapprouting-${AKS_NAME} \
    --resource-group $NODE_RESOURCE_GROUP \
    --resource-type Microsoft.ManagedIdentity/userAssignedIdentities \
    --query properties.principalId \
    --output tsv)

    # Grant the managed identity permissions to write DNS entries
    az role assignment create \
    --role "DNS Zone Contributor" \
    --assignee $MANAGED_IDENTTIY_OBJECT_ID \
    --scope $DNS_ZONE_ID

    The Azure Managed Identity will also be used to retrieve and rotate TLS certificates from Azure Key Vault. So we'll need to grant it permission for that too.

    az keyvault set-policy \
    --name $AKV_NAME \
    --object-id $MANAGED_IDENTTIY_OBJECT_ID \
    --secret-permissions get \
    --certificate-permissions get

    Implement Ingress for the web application

    Before we create a new Ingress manifest, let's update the existing services to use ClusterIP instead of LoadBalancer. With an Ingress in place, there is no reason why we need the Service resources to be accessible from outside the cluster. The new Ingress will be the only entrypoint for external users.

    We can use the kubectl patch command to update the services

    kubectl patch service api -p '{"spec": {"type": "ClusterIP"}}'
    kubectl patch service web -p '{"spec": {"type": "ClusterIP"}}'

    Deploy a new Ingress to place in front of the web Service. Notice there is a special annotations entry for kubernetes.azure.com/tls-cert-keyvault-uri which points back to our self-signed certificate that was uploaded to Azure Key Vault.

    kubectl apply -f - <<EOF
    apiVersion: networking.k8s.io/v1
    kind: Ingress
    metadata:
    annotations:
    kubernetes.azure.com/tls-cert-keyvault-uri: ${WEB_TLS_CERT_ID}
    name: web
    spec:
    ingressClassName: webapprouting.kubernetes.azure.com
    rules:
    - host: ${DNS_NAME}
    http:
    paths:
    - backend:
    service:
    name: web
    port:
    number: 80
    path: /
    pathType: Prefix
    - backend:
    service:
    name: api
    port:
    number: 80
    path: /api
    pathType: Prefix
    tls:
    - hosts:
    - ${DNS_NAME}
    secretName: web-tls
    EOF

    In our manifest above, we've also configured the Ingress route the traffic to either the web or api services based on the URL path requested. If the request URL includes /api/ then it will send traffic to the api backend service. Otherwise, it will send traffic to the web service.

    Within a few minutes, the external-dns controller will add an A record to Azure DNS which points to the Ingress resource's public IP. With the custom domain in place, we can simply browse using this domain name.

    info

    As mentioned above, since this is not a real domain name, we need to modify our host file to make it seem like our custom domain is resolving to the Ingress' public IP address.

    To get the ingress public IP, run the following:

    # Get the IP
    kubectl get ingress web -o jsonpath="{.status.loadBalancer.ingress[0].ip}"

    # Get the hostname
    kubectl get ingress web -o jsonpath="{.spec.tls[0].hosts[0]}"

    Next, open your host file and add an entry using the format <YOUR_PUBLIC_IP> <YOUR_CUSTOM_DOMAIN>. Below is an example of what it should look like.

    20.237.116.224 eshoponweb11265.com

    See this doc for more info on how to do this.

    When browsing to the website, you may be presented with a warning about the connection not being private. This is due to the fact that we are using a self-signed certificate. This is expected, so go ahead and proceed anyway to load up the page.

    Why is the Admin page broken?

    If you log in using the admin@microsoft.com account and browse to the Admin page, you'll notice no products are loaded on the page.

    This is because the admin page is built using Blazor and compiled as a WebAssembly application that runs in your browser. When the application was compiled, it packed the appsettings.Development.json file as an embedded resource. This file contains the base URL for the public API and it currently points to https://localhost:5099. Now that we have a domain name, we can update the base URL and point it to our custom domain.

    From the root of the eShopOnWeb repo, update the configuration file using a sed command.

    sed -i -e "s/localhost:5099/${DNS_NAME}/g" ./src/BlazorAdmin/wwwroot/appsettings.Development.json

    Rebuild and push the container to Azure Container Registry.

    # Grab the name of your Azure Container Registry
    ACR_NAME=$(az resource list \
    --resource-group $RESOURCE_GROUP \
    --resource-type Microsoft.ContainerRegistry/registries \
    --query "[0].name" -o tsv)

    # Invoke a build and publish job
    az acr build \
    --registry $ACR_NAME \
    --image $ACR_NAME.azurecr.io/web:v0.1.0 \
    --file ./src/Web/Dockerfile .

    Once the container build has completed, we can issue a kubectl patch command to quickly update the web deployment to test our change.

    kubectl patch deployment web -p "$(cat <<EOF
    {
    "spec": {
    "template": {
    "spec": {
    "containers": [
    {
    "name": "web",
    "image": "${ACR_NAME}.azurecr.io/web:v0.1.0"
    }
    ]
    }
    }
    }
    }
    EOF
    )"

    If all went well, you will be able to browse the admin page again and confirm product data is being loaded 🥳

    Conclusion

    The Web Application Routing add-on for AKS aims to streamline the process of exposing it to the public using the open-source NGINX Ingress Controller. With the add-on being managed by Azure, it natively integrates with other Azure services like Azure DNS and eliminates the need to manually create DNS entries. It can also integrate with Azure Key Vault to automatically pull in TLS certificates and rotate them as needed to further reduce operational overhead.

    We are one step closer to production and in the upcoming posts we'll further operationalize and secure our deployment, so stay tuned!

    In the meantime, check out the resources listed below for further reading.

    You can also find manifests with all the changes made in today's post in the Azure-Samples/eShopOnAKS repository.

    Resources

    Take the Cloud Skills Challenge!

    Enroll in the Cloud Skills Challenge!

    Don't miss out on this opportunity to level up your skills and stay ahead of the curve in the world of cloud native.

    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/azure-key-vault/index.html b/cnny-2023/tags/azure-key-vault/index.html index e665616216..4ec70c62b5 100644 --- a/cnny-2023/tags/azure-key-vault/index.html +++ b/cnny-2023/tags/azure-key-vault/index.html @@ -14,13 +14,13 @@ - - + +

    2 posts tagged with "azure-key-vault"

    View All Tags

    · 10 min read
    Paul Yu

    Welcome to Day 3 of Week 3 of #CloudNativeNewYear!

    The theme for this week is Bringing Your Application to Kubernetes. Yesterday we added configuration, secrets, and storage to our app. Today we'll explore how to expose the eShopOnWeb app so that customers can reach it over the internet using a custom domain name and TLS.

    Ask the Experts Thursday, February 9th at 9 AM PST
    Friday, February 10th at 11 AM PST

    Watch the recorded demo and conversation about this week's topics.

    We were live on YouTube walking through today's (and the rest of this week's) demos. Join us Friday, February 10th and bring your questions!

    What We'll Cover

    • Gather requirements
    • Generate TLS certificate and store in Azure Key Vault
    • Implement custom DNS using Azure DNS
    • Enable Web Application Routing add-on for AKS
    • Implement Ingress for the web application
    • Conclusion
    • Resources

    Gather requirements

    Currently, our eShopOnWeb app has three Kubernetes services deployed:

    1. db exposed internally via ClusterIP
    2. api exposed externally via LoadBalancer
    3. web exposed externally via LoadBalancer

    As mentioned in my post last week, Services allow applications to communicate with each other using DNS names. Kubernetes has service discovery capabilities built-in that allows Pods to resolve Services simply by using their names.

    In the case of our api and web deployments, they can simply reach the database by calling its name. The service type of ClusterIP for the db can remain as-is since it only needs to be accessed by the api and web apps.

    On the other hand, api and web both need to be accessed over the public internet. Currently, these services are using service type LoadBalancer which tells AKS to provision an Azure Load Balancer with a public IP address. No one is going to remember the IP addresses, so we need to make the app more accessible by adding a custom domain name and securing it with a TLS certificate.

    Here's what we're going to need:

    • Custom domain name for our app
    • TLS certificate for the custom domain name
    • Routing rule to ensure requests with /api/ in the URL is routed to the backend REST API
    • Routing rule to ensure requests without /api/ in the URL is routing to the web UI

    Just like last week, we will use the Web Application Routing add-on for AKS. But this time, we'll integrate it with Azure DNS and Azure Key Vault to satisfy all of our requirements above.

    info

    At the time of this writing the add-on is still in Public Preview

    Generate TLS certificate and store in Azure Key Vault

    We deployed an Azure Key Vault yesterday to store secrets. We'll use it again to store a TLS certificate too.

    Let's create and export a self-signed certificate for the custom domain.

    DNS_NAME=eshoponweb$RANDOM.com
    openssl req -new -x509 -nodes -out web-tls.crt -keyout web-tls.key -subj "/CN=${DNS_NAME}" -addext "subjectAltName=DNS:${DNS_NAME}"
    openssl pkcs12 -export -in web-tls.crt -inkey web-tls.key -out web-tls.pfx -password pass:
    info

    For learning purposes we'll use a self-signed certificate and a fake custom domain name.

    To browse to the site using the fake domain, we'll mimic a DNS lookup by adding an entry to your host file which maps the public IP address assigned to the ingress controller to the custom domain.

    In a production scenario, you will need to have a real domain delegated to Azure DNS and a valid TLS certificate for the domain.

    Grab your Azure Key Vault name and set the value in a variable for later use.

    RESOURCE_GROUP=cnny-week3

    AKV_NAME=$(az resource list \
    --resource-group $RESOURCE_GROUP \
    --resource-type Microsoft.KeyVault/vaults \
    --query "[0].name" -o tsv)

    Grant yourself permissions to get, list, and import certificates.

    MY_USER_NAME=$(az account show --query user.name -o tsv)
    MY_USER_OBJECT_ID=$(az ad user show --id $MY_USER_NAME --query id -o tsv)

    az keyvault set-policy \
    --name $AKV_NAME \
    --object-id $MY_USER_OBJECT_ID \
    --certificate-permissions get list import

    Upload the TLS certificate to Azure Key Vault and grab its certificate URI.

    WEB_TLS_CERT_ID=$(az keyvault certificate import \
    --vault-name $AKV_NAME \
    --name web-tls \
    --file web-tls.pfx \
    --query id \
    --output tsv)

    Implement custom DNS with Azure DNS

    Create a custom domain for our application and grab its Azure resource id.

    DNS_ZONE_ID=$(az network dns zone create \
    --name $DNS_NAME \
    --resource-group $RESOURCE_GROUP \
    --query id \
    --output tsv)

    Enable Web Application Routing add-on for AKS

    As we enable the Web Application Routing add-on, we'll also pass in the Azure DNS Zone resource id which triggers the installation of the external-dns controller in your Kubernetes cluster. This controller will be able to write Azure DNS zone entries on your behalf as you deploy Ingress manifests.

    AKS_NAME=$(az resource list \
    --resource-group $RESOURCE_GROUP \
    --resource-type Microsoft.ContainerService/managedClusters \
    --query "[0].name" -o tsv)

    az aks enable-addons \
    --name $AKS_NAME \
    --resource-group $RESOURCE_GROUP \
    --addons web_application_routing \
    --dns-zone-resource-id=$DNS_ZONE_ID \
    --enable-secret-rotation

    The add-on will also deploy a new Azure Managed Identity which is used by the external-dns controller when writing Azure DNS zone entries. Currently, it does not have permission to do that, so let's grant it permission.

    # This is where resources are automatically deployed by AKS
    NODE_RESOURCE_GROUP=$(az aks show \
    --name $AKS_NAME \
    --resource-group $RESOURCE_GROUP \
    --query nodeResourceGroup -o tsv)

    # This is the managed identity created by the Web Application Routing add-on
    MANAGED_IDENTTIY_OBJECT_ID=$(az resource show \
    --name webapprouting-${AKS_NAME} \
    --resource-group $NODE_RESOURCE_GROUP \
    --resource-type Microsoft.ManagedIdentity/userAssignedIdentities \
    --query properties.principalId \
    --output tsv)

    # Grant the managed identity permissions to write DNS entries
    az role assignment create \
    --role "DNS Zone Contributor" \
    --assignee $MANAGED_IDENTTIY_OBJECT_ID \
    --scope $DNS_ZONE_ID

    The Azure Managed Identity will also be used to retrieve and rotate TLS certificates from Azure Key Vault. So we'll need to grant it permission for that too.

    az keyvault set-policy \
    --name $AKV_NAME \
    --object-id $MANAGED_IDENTTIY_OBJECT_ID \
    --secret-permissions get \
    --certificate-permissions get

    Implement Ingress for the web application

    Before we create a new Ingress manifest, let's update the existing services to use ClusterIP instead of LoadBalancer. With an Ingress in place, there is no reason why we need the Service resources to be accessible from outside the cluster. The new Ingress will be the only entrypoint for external users.

    We can use the kubectl patch command to update the services

    kubectl patch service api -p '{"spec": {"type": "ClusterIP"}}'
    kubectl patch service web -p '{"spec": {"type": "ClusterIP"}}'

    Deploy a new Ingress to place in front of the web Service. Notice there is a special annotations entry for kubernetes.azure.com/tls-cert-keyvault-uri which points back to our self-signed certificate that was uploaded to Azure Key Vault.

    kubectl apply -f - <<EOF
    apiVersion: networking.k8s.io/v1
    kind: Ingress
    metadata:
    annotations:
    kubernetes.azure.com/tls-cert-keyvault-uri: ${WEB_TLS_CERT_ID}
    name: web
    spec:
    ingressClassName: webapprouting.kubernetes.azure.com
    rules:
    - host: ${DNS_NAME}
    http:
    paths:
    - backend:
    service:
    name: web
    port:
    number: 80
    path: /
    pathType: Prefix
    - backend:
    service:
    name: api
    port:
    number: 80
    path: /api
    pathType: Prefix
    tls:
    - hosts:
    - ${DNS_NAME}
    secretName: web-tls
    EOF

    In our manifest above, we've also configured the Ingress route the traffic to either the web or api services based on the URL path requested. If the request URL includes /api/ then it will send traffic to the api backend service. Otherwise, it will send traffic to the web service.

    Within a few minutes, the external-dns controller will add an A record to Azure DNS which points to the Ingress resource's public IP. With the custom domain in place, we can simply browse using this domain name.

    info

    As mentioned above, since this is not a real domain name, we need to modify our host file to make it seem like our custom domain is resolving to the Ingress' public IP address.

    To get the ingress public IP, run the following:

    # Get the IP
    kubectl get ingress web -o jsonpath="{.status.loadBalancer.ingress[0].ip}"

    # Get the hostname
    kubectl get ingress web -o jsonpath="{.spec.tls[0].hosts[0]}"

    Next, open your host file and add an entry using the format <YOUR_PUBLIC_IP> <YOUR_CUSTOM_DOMAIN>. Below is an example of what it should look like.

    20.237.116.224 eshoponweb11265.com

    See this doc for more info on how to do this.

    When browsing to the website, you may be presented with a warning about the connection not being private. This is due to the fact that we are using a self-signed certificate. This is expected, so go ahead and proceed anyway to load up the page.

    Why is the Admin page broken?

    If you log in using the admin@microsoft.com account and browse to the Admin page, you'll notice no products are loaded on the page.

    This is because the admin page is built using Blazor and compiled as a WebAssembly application that runs in your browser. When the application was compiled, it packed the appsettings.Development.json file as an embedded resource. This file contains the base URL for the public API and it currently points to https://localhost:5099. Now that we have a domain name, we can update the base URL and point it to our custom domain.

    From the root of the eShopOnWeb repo, update the configuration file using a sed command.

    sed -i -e "s/localhost:5099/${DNS_NAME}/g" ./src/BlazorAdmin/wwwroot/appsettings.Development.json

    Rebuild and push the container to Azure Container Registry.

    # Grab the name of your Azure Container Registry
    ACR_NAME=$(az resource list \
    --resource-group $RESOURCE_GROUP \
    --resource-type Microsoft.ContainerRegistry/registries \
    --query "[0].name" -o tsv)

    # Invoke a build and publish job
    az acr build \
    --registry $ACR_NAME \
    --image $ACR_NAME.azurecr.io/web:v0.1.0 \
    --file ./src/Web/Dockerfile .

    Once the container build has completed, we can issue a kubectl patch command to quickly update the web deployment to test our change.

    kubectl patch deployment web -p "$(cat <<EOF
    {
    "spec": {
    "template": {
    "spec": {
    "containers": [
    {
    "name": "web",
    "image": "${ACR_NAME}.azurecr.io/web:v0.1.0"
    }
    ]
    }
    }
    }
    }
    EOF
    )"

    If all went well, you will be able to browse the admin page again and confirm product data is being loaded 🥳

    Conclusion

    The Web Application Routing add-on for AKS aims to streamline the process of exposing it to the public using the open-source NGINX Ingress Controller. With the add-on being managed by Azure, it natively integrates with other Azure services like Azure DNS and eliminates the need to manually create DNS entries. It can also integrate with Azure Key Vault to automatically pull in TLS certificates and rotate them as needed to further reduce operational overhead.

    We are one step closer to production and in the upcoming posts we'll further operationalize and secure our deployment, so stay tuned!

    In the meantime, check out the resources listed below for further reading.

    You can also find manifests with all the changes made in today's post in the Azure-Samples/eShopOnAKS repository.

    Resources

    Take the Cloud Skills Challenge!

    Enroll in the Cloud Skills Challenge!

    Don't miss out on this opportunity to level up your skills and stay ahead of the curve in the world of cloud native.

    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/azure-key-vault/page/2/index.html b/cnny-2023/tags/azure-key-vault/page/2/index.html index a8ffccc5c9..75837a0b18 100644 --- a/cnny-2023/tags/azure-key-vault/page/2/index.html +++ b/cnny-2023/tags/azure-key-vault/page/2/index.html @@ -14,13 +14,13 @@ - - + +

    2 posts tagged with "azure-key-vault"

    View All Tags

    · 6 min read
    Josh Duffney

    Welcome to Day 5 of Week 3 of #CloudNativeNewYear!

    The theme for this week is Bringing Your Application to Kubernetes. Yesterday we talked about debugging and instrumenting our application. Today we'll explore the topic of container image signing and secure supply chain.

    Ask the Experts Thursday, February 9th at 9 AM PST
    Friday, February 10th at 11 AM PST

    Watch the recorded demo and conversation about this week's topics.

    We were live on YouTube walking through today's (and the rest of this week's) demos. Join us Friday, February 10th and bring your questions!

    What We'll Cover

    • Introduction
    • Prerequisites
    • Create a digital signing certificate
    • Generate an Azure Container Registry Token
    • Set up Notation
    • Install the Notation Azure Key Vault Plugin
    • Add the signing Certificate to Notation
    • Sign Container Images
    • Conclusion

    Introduction

    The secure supply chain is a crucial aspect of software development, delivery, and deployment, and digital signing plays a critical role in this process.

    By using digital signatures to verify the authenticity and integrity of container images, organizations can improve the security of your software supply chain and reduce the risk of security breaches and data compromise.

    In this article, you'll learn how to use Notary, an open-source project hosted by the Cloud Native Computing Foundation (CNCF) to digitally sign container images stored on Azure Container Registry.

    Prerequisites

    To follow along, you'll need an instance of:

    Create a digital signing certificate

    A digital signing certificate is a certificate that is used to digitally sign and verify the authenticity and integrity of digital artifacts. Such documents, software, and of course container images.

    Before you can implement digital signatures, you must first create a digital signing certificate.

    Run the following command to generate the certificate:

    1. Create the policy file

      cat <<EOF > ./my_policy.json
      {
      "issuerParameters": {
      "certificateTransparency": null,
      "name": "Self"
      },
      "x509CertificateProperties": {
      "ekus": [
      "1.3.6.1.5.5.7.3.3"
      ],
      "key_usage": [
      "digitalSignature"
      ],
      "subject": "CN=${keySubjectName}",
      "validityInMonths": 12
      }
      }
      EOF

      The ekus and key usage of this certificate policy dictate that the certificate can only be used for digital signatures.

    2. Create the certificate in Azure Key Vault

      az keyvault certificate create --name $keyName --vault-name $keyVaultName --policy @my_policy.json

      Replace $keyName and $keyVaultName with your desired certificate name and Azure Key Vault instance name.

    Generate a Azure Container Registry token

    Azure Container Registry tokens are used to grant access to the contents of the registry. Tokens can be used for a variety of things such as pulling images, pushing images, or managing the registry.

    As part of the container image signing workflow, you'll need a token to authenticate the Notation CLI with your Azure Container Registry.

    Run the following command to generate an ACR token:

    az acr token create \
    --name $tokenName \
    --registry $registry \
    --scope-map _repositories_admin \
    --query 'credentials.passwords[0].value' \
    --only-show-errors \
    --output tsv

    Replace $tokenName with your name for the ACR token and $registry with the name of your ACR instance.

    Setup Notation

    Notation is the command-line interface for the CNCF Notary project. You'll use it to digitally sign the api and web container images for the eShopOnWeb application.

    Run the following commands to download and install the NotationCli:

    1. Open a terminal or command prompt window

    2. Download the Notary notation release

      curl -Lo notation.tar.gz https://github.com/notaryproject/notation/releases/download/v1.0.0-rc.1/notation_1.0.0-rc.1_linux_amd64.tar.gz > /dev/null 2>&1

      If you're not using Linux, you can find the releases here.

    3. Extract the contents of the notation.tar.gz

      tar xvzf notation.tar.gz > /dev/null 2>&1
    4. Copy the notation binary to the $HOME/bin directory

      cp ./notation $HOME/bin
    5. Add the $HOME/bin directory to the PATH environment variable

      export PATH="$HOME/bin:$PATH"
    6. Remove the downloaded files

      rm notation.tar.gz LICENSE
    7. Check the notation version

      notation --version

    Install the Notation Azure Key Vault plugin

    By design the NotationCli supports plugins that extend its digital signing capabilities to remote registries. And in order to sign your container images stored in Azure Container Registry, you'll need to install the Azure Key Vault plugin for Notation.

    Run the following commands to install the azure-kv plugin:

    1. Download the plugin

      curl -Lo notation-azure-kv.tar.gz \
      https://github.com/Azure/notation-azure-kv/releases/download/v0.5.0-rc.1/notation-azure-kv_0.5.0-rc.1_linux_amd64.tar.gz > /dev/null 2>&1

      Non-Linux releases can be found here.

    2. Extract to the plugin directory & delete download files

      tar xvzf notation-azure-kv.tar.gz -C ~/.config/notation/plugins/azure-kv notation-azure-kv > /dev/null 2>&

      rm -rf notation-azure-kv.tar.gz
    3. Verify the plugin was installed

      notation plugin ls

    Add the signing certificate to Notation

    Now that you have Notation and the Azure Key Vault plugin installed, add the certificate's keyId created above to Notation.

    1. Get the Certificate Key ID from Azure Key Vault

      az keyvault certificate show \
      --vault-name $keyVaultName \
      --name $keyName \
      --query "kid" --only-show-errors --output tsv

      Replace $keyVaultName and $keyName with the appropriate information.

    2. Add the Key ID to KMS using Notation

      notation key add --plugin azure-kv --id $keyID $keyName
    3. Check the key list

      notation key ls

    Sign Container Images

    At this point, all that's left is to sign the container images.

    Run the notation sign command to sign the api and web container images:

    notation sign $registry.azurecr.io/web:$tag \
    --username $tokenName \
    --password $tokenPassword

    notation sign $registry.azurecr.io/api:$tag \
    --username $tokenName \
    --password $tokenPassword

    Replace $registry, $tag, $tokenName, and $tokenPassword with the appropriate values. To improve security, use a SHA hash for the tag.

    NOTE: If you didn't take note of the token password, you can rerun the az acr token create command to generate a new password.

    Conclusion

    Digital signing plays a critical role in ensuring the security of software supply chains.

    By signing software components, organizations can verify the authenticity and integrity of software, helping to prevent unauthorized modifications, tampering, and malware.

    And if you want to take digital signing to a whole new level by using them to prevent the deployment of unsigned container images, check out the Ratify project on GitHub!

    Take the Cloud Skills Challenge!

    Enroll in the Cloud Skills Challenge!

    Don't miss out on this opportunity to level up your skills and stay ahead of the curve in the world of cloud native.

    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/azure-kubernetes-service/index.html b/cnny-2023/tags/azure-kubernetes-service/index.html index 75f8c61ff2..4d35ca13bf 100644 --- a/cnny-2023/tags/azure-kubernetes-service/index.html +++ b/cnny-2023/tags/azure-kubernetes-service/index.html @@ -14,13 +14,13 @@ - - + +

    21 posts tagged with "azure-kubernetes-service"

    View All Tags

    · 4 min read
    Cory Skimming
    Devanshi Joshi
    Steven Murawski
    Nitya Narasimhan

    Welcome to the Kick-off Post for #30DaysOfCloudNative - one of the core initiatives within #CloudNativeNewYear! Over the next four weeks, join us as we take you from fundamentals to functional usage of Cloud-native technologies, one blog post at a time! Read on to learn a little bit about this initiative and what you can expect to learn from this journey!

    What We'll Cover


    Cloud-native New Year

    Welcome to Week 01 of 🥳 #CloudNativeNewYear ! Today, we kick off a full month of content and activities to skill you up on all things Cloud-native on Azure with content, events, and community interactions! Read on to learn about what we have planned!


    Explore our initiatives

    We have a number of initiatives planned for the month to help you learn and skill up on relevant technologies. Click on the links to visit the relevant pages for each.

    We'll go into more details about #30DaysOfCloudNative in this post - don't forget to subscribe to the blog to get daily posts delivered directly to your preferred feed reader!


    Register for events!

    What are 3 things you can do today, to jumpstart your learning journey?


    #30DaysOfCloudNative

    #30DaysOfCloudNative is a month-long series of daily blog posts grouped into 4 themed weeks - taking you from core concepts to end-to-end solution examples in 30 days. Each article will be short (5-8 mins reading time) and provide exercises and resources to help you reinforce learnings and take next steps.

    This series focuses on the Cloud-native On Azure learning journey in four stages, each building on the previous week to help you skill up in a beginner-friendly way:

    We have a tentative weekly-themed roadmap for the topics we hope to cover and will keep this updated as we go with links to actual articles as they get published.

    Week 1: FOCUS ON CLOUD-NATIVE FUNDAMENTALS

    Here's a sneak peek at the week 1 schedule. We'll start with a broad review of cloud-native fundamentals and walkthrough the core concepts of microservices, containers and Kubernetes.

    • Jan 23: Learn Core Concepts for Cloud-native
    • Jan 24: Container 101
    • Jan 25: Adopting Microservices with Kubernetes
    • Jan 26: Kubernetes 101
    • Jan 27: Exploring your Cloud Native Options

    Let's Get Started!

    Now you know everything! We hope you are as excited as we are to dive into a full month of active learning and doing! Don't forget to subscribe for updates in your favorite feed reader! And look out for our first Cloud-native Fundamentals post on January 23rd!


    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/azure-kubernetes-service/page/10/index.html b/cnny-2023/tags/azure-kubernetes-service/page/10/index.html index 8faa128f16..48965c3745 100644 --- a/cnny-2023/tags/azure-kubernetes-service/page/10/index.html +++ b/cnny-2023/tags/azure-kubernetes-service/page/10/index.html @@ -14,13 +14,13 @@ - - + +

    21 posts tagged with "azure-kubernetes-service"

    View All Tags

    · 8 min read
    Paul Yu

    Welcome to Day 4 of Week 2 of #CloudNativeNewYear!

    The theme for this week is Kubernetes fundamentals. Yesterday we talked about how to set app configurations and secrets at runtime using Kubernetes ConfigMaps and Secrets. Today we'll explore the topic of persistent storage on Kubernetes and show you can leverage Persistent Volumes and Persistent Volume Claims to ensure your PostgreSQL data can survive container restarts.

    Ask the Experts Thursday, February 9th at 9 AM PST
    Catch the Replay of the Live Demo

    Watch the recorded demo and conversation about this week's topics.

    We were live on YouTube walking through today's (and the rest of this week's) demos.

    What We'll Cover

    • Containers are ephemeral
    • Persistent storage on Kubernetes
    • Persistent storage on AKS
    • Takeaways
    • Resources

    Containers are ephemeral

    In our sample application, the frontend UI writes vote values to a backend PostgreSQL database. By default the database container stores its data on the container's local file system, so there will be data loss when the pod is re-deployed or crashes as containers are meant to start with a clean slate each time.

    Let's re-deploy our sample app and experience the problem first hand.

    📝 NOTE: If you don't have an AKS cluster deployed, please head over to Azure-Samples/azure-voting-app-rust, clone the repo, and follow the instructions in the README.md to execute the Azure deployment and setup your kubectl context. Check out the first post this week for more on the environment setup.

    kubectl apply -f ./manifests

    Wait for the azure-voting-app service to be assigned a public IP then browse to the website and submit some votes. Use the command below to print the URL to the terminal.

    echo "http://$(kubectl get ingress azure-voting-app -o jsonpath='{.status.loadBalancer.ingress[0].ip}')"

    Now, let's delete the pods and watch Kubernetes do what it does best... that is, re-schedule pods.

    # wait for the pod to come up then ctrl+c to stop watching
    kubectl delete --all pod --wait=false && kubectl get po -w

    Once the pods have been recovered, reload the website and confirm the vote tally has been reset to zero.

    We need to fix this so that the data outlives the container.

    Persistent storage on Kubernetes

    In order for application data to survive crashes and restarts, you must implement Persistent Volumes and Persistent Volume Claims.

    A persistent volume represents storage that is available to the cluster. Storage volumes can be provisioned manually by an administrator or dynamically using Container Storage Interface (CSI) and storage classes, which includes information on how to provision CSI volumes.

    When a user needs to add persistent storage to their application, a persistent volume claim is made to allocate chunks of storage from the volume. This "claim" includes things like volume mode (e.g., file system or block storage), the amount of storage to allocate, the access mode, and optionally a storage class. Once a persistent volume claim has been deployed, users can add the volume to the pod and mount it in a container.

    In the next section, we'll demonstrate how to enable persistent storage on AKS.

    Persistent storage on AKS

    With AKS, CSI drivers and storage classes are pre-deployed into your cluster. This allows you to natively use Azure Disks, Azure Files, and Azure Blob Storage as persistent volumes. You can either bring your own Azure storage account and use it with AKS or have AKS provision an Azure storage account for you.

    To view the Storage CSI drivers that have been enabled in your AKS cluster, run the following command.

    az aks show \
    --name <YOUR_AKS_NAME> \
    --resource-group <YOUR_AKS_RESOURCE_GROUP> \
    --query storageProfile

    You should see output that looks like this.

    {
    "blobCsiDriver": null,
    "diskCsiDriver": {
    "enabled": true,
    "version": "v1"
    },
    "fileCsiDriver": {
    "enabled": true
    },
    "snapshotController": {
    "enabled": true
    }
    }

    To view the storage classes that have been installed in your cluster, run the following command.

    kubectl get storageclass

    Workload requirements will dictate which CSI driver and storage class you will need to use.

    If you need block storage, then you should use the blobCsiDriver. The driver may not be enabled by default but you can enable it by following instructions which can be found in the Resources section below.

    If you need file storage you should leverage either diskCsiDriver or fileCsiDriver. The decision between these two boils down to whether or not you need to have the underlying storage accessible by one pod or multiple pods. It is important to note that diskCsiDriver currently supports access from a single pod only. Therefore, if you need data to be accessible by multiple pods at the same time, then you should opt for fileCsiDriver.

    For our PostgreSQL deployment, we'll use the diskCsiDriver and have AKS create an Azure Disk resource for us. There is no need to create a PV resource, all we need to do to is create a PVC using the managed-csi-premium storage class.

    Run the following command to create the PVC.

    kubectl apply -f - <<EOF            
    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
    name: pvc-azuredisk
    spec:
    accessModes:
    - ReadWriteOnce
    resources:
    requests:
    storage: 10Gi
    storageClassName: managed-csi-premium
    EOF

    When you check the PVC resource, you'll notice the STATUS is set to Pending. It will be set to Bound once the volume is mounted in the PostgreSQL container.

    kubectl get persistentvolumeclaim

    Let's delete the azure-voting-db deployment.

    kubectl delete deploy azure-voting-db

    Next, we need to apply an updated deployment manifest which includes our PVC.

    kubectl apply -f - <<EOF
    apiVersion: apps/v1
    kind: Deployment
    metadata:
    creationTimestamp: null
    labels:
    app: azure-voting-db
    name: azure-voting-db
    spec:
    replicas: 1
    selector:
    matchLabels:
    app: azure-voting-db
    strategy: {}
    template:
    metadata:
    creationTimestamp: null
    labels:
    app: azure-voting-db
    spec:
    containers:
    - image: postgres:15.0-alpine
    name: postgres
    ports:
    - containerPort: 5432
    env:
    - name: POSTGRES_PASSWORD
    valueFrom:
    secretKeyRef:
    name: azure-voting-secret
    key: POSTGRES_PASSWORD
    resources: {}
    volumeMounts:
    - name: mypvc
    mountPath: "/var/lib/postgresql/data"
    subPath: "data"
    volumes:
    - name: mypvc
    persistentVolumeClaim:
    claimName: pvc-azuredisk
    EOF

    In the manifest above, you'll see that we are mounting a new volume called mypvc (the name can be whatever you want) in the pod which points to a PVC named pvc-azuredisk. With the volume in place, we can mount it in the container by referencing the name of the volume mypvc and setting the mount path to /var/lib/postgresql/data (which is the default path).

    💡 IMPORTANT: When mounting a volume into a non-empty subdirectory, you must add subPath to the volume mount and point it to a subdirectory in the volume rather than mounting at root. In our case, when Azure Disk is formatted, it leaves a lost+found directory as documented here.

    Watch the pods and wait for the STATUS to show Running and the pod's READY status shows 1/1.

    # wait for the pod to come up then ctrl+c to stop watching
    kubectl get po -w

    Verify that the STATUS of the PVC is now set to Bound

    kubectl get persistentvolumeclaim

    With the new database container running, let's restart the application pod, wait for the pod's READY status to show 1/1, then head back over to our web browser and submit a few votes.

    kubectl delete pod -lapp=azure-voting-app --wait=false && kubectl get po -lapp=azure-voting-app -w

    Now the moment of truth... let's rip out the pods again, wait for the pods to be re-scheduled, and confirm our vote counts remain in tact.

    kubectl delete --all pod --wait=false && kubectl get po -w

    If you navigate back to the website, you'll find the vote are still there 🎉

    Takeaways

    By design, containers are meant to be ephemeral and stateless workloads are ideal on Kubernetes. However, there will come a time when your data needs to outlive the container. To persist data in your Kubernetes workloads, you need to leverage PV, PVC, and optionally storage classes. In our demo scenario, we leveraged CSI drivers built into AKS and created a PVC using pre-installed storage classes. From there, we updated the database deployment to mount the PVC in the container and AKS did the rest of the work in provisioning the underlying Azure Disk. If the built-in storage classes does not fit your needs; for example, you need to change the ReclaimPolicy or change the SKU for the Azure resource, then you can create your own custom storage class and configure it just the way you need it 😊

    We'll revisit this topic again next week but in the meantime, check out some of the resources listed below to learn more.

    See you in the next post!

    Resources

    Take the Cloud Skills Challenge!

    Enroll in the Cloud Skills Challenge!

    Don't miss out on this opportunity to level up your skills and stay ahead of the curve in the world of cloud native.

    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/azure-kubernetes-service/page/11/index.html b/cnny-2023/tags/azure-kubernetes-service/page/11/index.html index aa4b5f9fc4..6a338e89b1 100644 --- a/cnny-2023/tags/azure-kubernetes-service/page/11/index.html +++ b/cnny-2023/tags/azure-kubernetes-service/page/11/index.html @@ -14,13 +14,13 @@ - - + +

    21 posts tagged with "azure-kubernetes-service"

    View All Tags

    · 10 min read
    Steven Murawski

    Welcome to Day 5 of Week 2 of #CloudNativeNewYear!

    The theme for this week is Kubernetes fundamentals. Yesterday we talked about adding persistent storage to our deployment. Today we'll explore the topic of scaling pods and nodes in our Kubernetes cluster.

    Ask the Experts Thursday, February 9th at 9 AM PST
    Catch the Replay of the Live Demo

    Watch the recorded demo and conversation about this week's topics.

    We were live on YouTube walking through today's (and the rest of this week's) demos.

    What We'll Cover

    • Scaling Our Application
    • Scaling Pods
    • Scaling Nodes
    • Exercise
    • Resources

    Scaling Our Application

    One of our primary reasons to use a service like Kubernetes to orchestrate our workloads is the ability to scale. We've approached scaling in a multitude of ways over the years, taking advantage of the ever-evolving levels of hardware and software. Kubernetes allows us to scale our units of work, Pods, and the Nodes they run on. This allows us to take advantage of both hardware and software scaling abilities. Kubernetes can help improve the utilization of existing hardware (by scheduling Pods on Nodes that have resource capacity). And, with the capabilities of virtualization and/or cloud hosting (or a bit more work, if you have a pool of physical machines), Kubernetes can expand (or contract) the number of Nodes capable of hosting Pods. Scaling is primarily driven by resource utilization, but can be triggered by a variety of other sources thanks to projects like Kubernetes Event-driven Autoscaling (KEDA).

    Scaling Pods

    Our first level of scaling is with our Pods. Earlier, when we worked on our deployment, we talked about how the Kubernetes would use the deployment configuration to ensure that we had the desired workloads running. One thing we didn't explore was running more than one instance of a pod. We can define a number of replicas of a pod in our Deployment.

    Manually Scale Pods

    So, if we wanted to define more pods right at the start (or at any point really), we could update our deployment configuration file with the number of replicas and apply that configuration file.

    spec:
    replicas: 5

    Or we could use the kubectl scale command to update the deployment with a number of pods to create.

    kubectl scale --replicas=5 deployment/azure-voting-app

    Both of these approaches modify the running configuration of our Kubernetes cluster and request that it ensure that we have that set number of replicas running. Because this was a manual change, the Kubernetes cluster won't automatically increase or decrease the number of pods. It'll just ensure that there are always the specified number of pods running.

    Autoscale Pods with the Horizontal Pod Autoscaler

    Another approach to scaling our pods is to allow the Horizontal Pod Autoscaler to help us scale in response to resources being used by the pod. This requires a bit more configuration up front. When we define our pod in our deployment, we need to include resource requests and limits. The requests help Kubernetes determine what nodes may have capacity for a new instance of a pod. The limit tells us where the node should cap utilization for a particular instance of a pod. For example, we'll update our deployment to request 0.25 CPU and set a limit of 0.5 CPU.

        spec:
    containers:
    - image: acrudavoz.azurecr.io/cnny2023/azure-voting-app-rust:ca4
    name: azure-voting-app-rust
    ports:
    - containerPort: 8080
    env:
    - name: DATABASE_URL
    value: postgres://postgres:mypassword@10.244.0.29
    resources:
    requests:
    cpu: 250m
    limits:
    cpu: 500m

    Now that we've given Kubernetes an allowed range and an idea of what free resources a node should have to place new pods, we can set up autoscaling. Because autoscaling is a persistent configuration, I like to define it in a configuration file that I'll be able to keep with the rest of my cluster configuration. We'll use the kubectl command to help us write the configuration file. We'll request that Kubernetes watch our pods and when the average CPU utilization if 50% of the requested usage (in our case if it's using more than 0.375 CPU across the current number of pods), it can grow the number of pods serving requests up to 10. If the utilization drops, Kubernetes will have the permission to deprovision pods down to the minimum (three in our example).

    kubectl autoscale deployment azure-voting-app --cpu-percent=50 --min=3 --max=10 -o YAML --dry-run=client

    Which would give us:

    apiVersion: autoscaling/v1
    kind: HorizontalPodAutoscaler
    metadata:
    creationTimestamp: null
    name: azure-voting-app
    spec:
    maxReplicas: 10
    minReplicas: 3
    scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: azure-voting-app
    targetCPUUtilizationPercentage: 50
    status:
    currentReplicas: 0
    desiredReplicas: 0

    So, how often does the autoscaler check the metrics being monitored? The autoscaler checks the Metrics API every 15 seconds, however the pods stats are only updated every 60 seconds. This means that an autoscale event may be evaluated about once a minute. Once an autoscale down event happens however, Kubernetes has a cooldown period to give the new pods a chance to distribute the workload and let the new metrics accumulate. There is no delay on scale up events.

    Application Architecture Considerations

    We've focused in this example on our front end, which is an easier scaling story. When we start talking about scaling our database layers or anything that deals with persistent storage or has primary/replica configuration requirements things get a bit more complicated. Some of these applications may have built-in leader election or could use sidecars to help use existing features in Kubernetes to perform that function. For shared storage scenarios, persistent volumes (or persistent volumes with Azure) can be of help, if the application knows how to play well with shared file access.

    Ultimately, you know your application architecture and, while Kubernetes may not have an exact match to how you are doing things today, the underlying capability is probably there under a different name. This abstraction allows you to more effectively use Kubernetes to operate a variety of workloads with the levels of controls you need.

    Scaling Nodes

    We've looked at how to scale our pods, but that assumes we have enough resources in our existing pool of nodes to accomodate those scaling requests. Kubernetes can also help scale our available nodes to ensure that our applications have the necessary resources to meet their performance requirements.

    Manually Scale Nodes

    Manually scaling nodes isn't a direct function of Kubernetes, so your operating environment instructions may vary. On Azure, it's pretty straight forward. Using the Azure CLI (or other tools), we can tell our AKS cluster to scale up or scale down the number of nodes in our node pool.

    First, we'll check out how many nodes we currently have in our working environment.

    kubectl get nodes

    This will show us

    azure-voting-app-rust ❯  kubectl get nodes
    NAME STATUS ROLES AGE VERSION
    aks-pool0-37917684-vmss000000 Ready agent 5d21h v1.24.6

    Then, we'll scale it up to three nodes.

    az aks scale --resource-group $ResourceGroup --name $AksName --node-count 3

    Then, we'll check out how many nodes we now have in our working environment.

    kubectl get nodes

    Which returns:

    azure-voting-app-rust ❯  kubectl get nodes
    NAME STATUS ROLES AGE VERSION
    aks-pool0-37917684-vmss000000 Ready agent 5d21h v1.24.6
    aks-pool0-37917684-vmss000001 Ready agent 5m27s v1.24.6
    aks-pool0-37917684-vmss000002 Ready agent 5m10s v1.24.6

    Autoscale Nodes with the Cluster Autoscaler

    Things get more interesting when we start working with the Cluster Autoscaler. The Cluster Autoscaler watches for the inability of Kubernetes to schedule the required number of pods due to resource constraints (and a few other criteria like affinity/anti-affinity). If there are insufficient resources available on the existing nodes, the autoscaler can provision new nodes into the nodepool. Likewise, the autoscaler watches to see if the existing pods could be consolidated to a smaller set of nodes and can remove excess nodes.

    Enabling the autoscaler is likewise an update that can be dependent on where and how your Kubernetes cluster is hosted. Azure makes it easy with a simple Azure CLI command.

    az aks update `
    --resource-group $ResourceGroup `
    --name $AksName `
    --update-cluster-autoscaler `
    --min-count 1 `
    --max-count 5

    There are a variety of settings that can be configured to tune how the autoscaler works.

    Scaling on Different Events

    CPU and memory utilization are the primary drivers for the Horizontal Pod Autoscaler, but those might not be the best measures as to when you might want to scale workloads. There are other options for scaling triggers and one of the more common plugins to help with that is the Kubernetes Event-driven Autoscaling (KEDA) project. The KEDA project makes it easy to plug in different event sources to help drive scaling. Find more information about using KEDA on AKS here.

    Exercise

    Let's try out the scaling configurations that we just walked through using our sample application. If you still have your environment from Day 1, you can use that.

    📝 NOTE: If you don't have an AKS cluster deployed, please head over to Azure-Samples/azure-voting-app-rust, clone the repo, and follow the instructions in the README.md to execute the Azure deployment and setup your kubectl context. Check out the first post this week for more on the environment setup.

    Configure Horizontal Pod Autoscaler

    • Edit ./manifests/deployment-app.yaml to include resource requests and limits.
            resources:
    requests:
    cpu: 250m
    limits:
    cpu: 500m
    • Apply the updated deployment configuration.
    kubectl apply -f ./manifests/deployment-app.yaml
    • Create the horizontal pod autoscaler configuration and apply it
    kubectl autoscale deployment azure-voting-app --cpu-percent=50 --min=3 --max=10 -o YAML --dry-run=client > ./manifests/scaler-app.yaml
    kubectl apply -f ./manifests/scaler-app.yaml
    • Check to see your pods scale out to the minimum.
    kubectl get pods

    Configure Cluster Autoscaler

    Configuring the basic behavior of the Cluster Autoscaler is a bit simpler. We just need to run the Azure CLI command to enable the autoscaler and define our lower and upper limits.

    • Check the current nodes available (should be 1).
    kubectl get nodes
    • Update the cluster to enable the autoscaler
    az aks update `
    --resource-group $ResourceGroup `
    --name $AksName `
    --update-cluster-autoscaler `
    --min-count 2 `
    --max-count 5
    • Check to see the current number of nodes (should be 2 now).
    kubectl get nodes

    Resources

    Take the Cloud Skills Challenge!

    Enroll in the Cloud Skills Challenge!

    Don't miss out on this opportunity to level up your skills and stay ahead of the curve in the world of cloud native.

    Documentation

    Training

    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/azure-kubernetes-service/page/12/index.html b/cnny-2023/tags/azure-kubernetes-service/page/12/index.html index 3cd9655c62..8f1d074da8 100644 --- a/cnny-2023/tags/azure-kubernetes-service/page/12/index.html +++ b/cnny-2023/tags/azure-kubernetes-service/page/12/index.html @@ -14,15 +14,15 @@ - - + +

    21 posts tagged with "azure-kubernetes-service"

    View All Tags

    · 14 min read
    Steven Murawski

    Welcome to Day 1 of Week 3 of #CloudNativeNewYear!

    The theme for this week is Bringing Your Application to Kubernetes. Last we talked about Kubernetes Fundamentals. Today we'll explore getting an existing application running in Kubernetes with a full pipeline in GitHub Actions.

    Ask the Experts Thursday, February 9th at 9 AM PST
    Friday, February 10th at 11 AM PST

    Watch the recorded demo and conversation about this week's topics.

    We were live on YouTube walking through today's (and the rest of this week's) demos. Join us Friday, February 10th and bring your questions!

    What We'll Cover

    • Our Application
    • Adding Some Infrastructure as Code
    • Building and Publishing a Container Image
    • Deploying to Kubernetes
    • Summary
    • Resources

    Our Application

    This week we'll be taking an exisiting application - something similar to a typical line of business application - and setting it up to run in Kubernetes. Over the course of the week, we'll address different concerns. Today we'll focus on updating our CI/CD process to handle standing up (or validating that we have) an Azure Kubernetes Service (AKS) environment, building and publishing container images for our web site and API server, and getting those services running in Kubernetes.

    The application we'll be starting with is eShopOnWeb. This application has a web site and API which are backed by a SQL Server instance. It's built in .NET 7, so it's cross-platform.

    info

    For the enterprising among you, you may notice that there are a number of different eShopOn* variants on GitHub, including eShopOnContainers. We aren't using that example as it's more of an end state than a starting place. Afterwards, feel free to check out that example as what this solution could look like as a series of microservices.

    Adding Some Infrastructure as Code

    Just like last week, we need to stand up an AKS environment. This week, however, rather than running commands in our own shell, we'll set up GitHub Actions to do that for us.

    There is a LOT of plumbing in this section, but once it's set up, it'll make our lives a lot easier. This section ensures that we have an environment to deploy our application into configured the way we want. We can easily extend this to accomodate multiple environments or add additional microservices with minimal new effort.

    Federated Identity

    Setting up a federated identity will allow us a more securable and auditable way of accessing Azure from GitHub Actions. For more about setting up a federated identity, Microsoft Learn has the details on connecting GitHub Actions to Azure.

    Here, we'll just walk through the setup of the identity and configure GitHub to use that idenity to deploy our AKS environment and interact with our Azure Container Registry.

    The examples will use PowerShell, but a Bash version of the setup commands is available in the week3/day1 branch.

    Prerequisites

    To follow along, you'll need:

    • a GitHub account
    • an Azure Subscription
    • the Azure CLI
    • and the Git CLI.

    You'll need to fork the source repository under your GitHub user or organization where you can manage secrets and GitHub Actions.

    It would be helpful to have the GitHub CLI, but it's not required.

    Set Up Some Defaults

    You will need to update one or more of the variables (your user or organization, what branch you want to work off of, and possibly the Azure AD application name if there is a conflict).

    # Replace the gitHubOrganizationName value
    # with the user or organization you forked
    # the repository under.

    $githubOrganizationName = 'Azure-Samples'
    $githubRepositoryName = 'eShopOnAKS'
    $branchName = 'week3/day1'
    $applicationName = 'cnny-week3-day1'

    Create an Azure AD Application

    Next, we need to create an Azure AD application.

    # Create an Azure AD application
    $aksDeploymentApplication = New-AzADApplication -DisplayName $applicationName

    Set Up Federation for that Azure AD Application

    And configure that application to allow federated credential requests from our GitHub repository for a particular branch.

    # Create a federated identity credential for the application
    New-AzADAppFederatedCredential `
    -Name $applicationName `
    -ApplicationObjectId $aksDeploymentApplication.Id `
    -Issuer 'https://token.actions.githubusercontent.com' `
    -Audience 'api://AzureADTokenExchange' `
    -Subject "repo:$($githubOrganizationName)/$($githubRepositoryName):ref:refs/heads/$branchName"

    Create a Service Principal for the Azure AD Application

    Once the application has been created, we need a service principal tied to that application. The service principal can be granted rights in Azure.

    # Create a service principal for the application
    New-AzADServicePrincipal -AppId $($aksDeploymentApplication.AppId)

    Give that Service Principal Rights to Azure Resources

    Because our Bicep deployment exists at the subscription level and we are creating role assignments, we need to give it Owner rights. If we changed the scope of the deployment to just a resource group, we could apply more scoped permissions.

    $azureContext = Get-AzContext
    New-AzRoleAssignment `
    -ApplicationId $($aksDeploymentApplication.AppId) `
    -RoleDefinitionName Owner `
    -Scope $azureContext.Subscription.Id

    Add Secrets to GitHub Repository

    If you have the GitHub CLI, you can use that right from your shell to set the secrets needed.

    gh secret set AZURE_CLIENT_ID --body $aksDeploymentApplication.AppId
    gh secret set AZURE_TENANT_ID --body $azureContext.Tenant.Id
    gh secret set AZURE_SUBSCRIPTION_ID --body $azureContext.Subscription.Id

    Otherwise, you can create them through the web interface like I did in the Learn Live event below.

    info

    It may look like the whole video will play, but it'll stop after configuring the secrets in GitHub (after about 9 minutes)

    The video shows creating the Azure AD application, service principals, and configuring the federated identity in Azure AD and GitHub.

    Creating a Bicep Deployment

    Resuable Workflows

    We'll create our Bicep deployment in a reusable workflows. What are they? The previous link has the documentation or the video below has my colleague Brandon Martinez and I talking about them.

    This workflow is basically the same deployment we did in last week's series, just in GitHub Actions.

    Start by creating a file called deploy_aks.yml in the .github/workflows directory with the below contents.

    name: deploy

    on:
    workflow_call:
    inputs:
    resourceGroupName:
    required: true
    type: string
    secrets:
    AZURE_CLIENT_ID:
    required: true
    AZURE_TENANT_ID:
    required: true
    AZURE_SUBSCRIPTION_ID:
    required: true
    outputs:
    containerRegistryName:
    description: Container Registry Name
    value: ${{ jobs.deploy.outputs.containerRegistryName }}
    containerRegistryUrl:
    description: Container Registry Login Url
    value: ${{ jobs.deploy.outputs.containerRegistryUrl }}
    resourceGroupName:
    description: Resource Group Name
    value: ${{ jobs.deploy.outputs.resourceGroupName }}
    aksName:
    description: Azure Kubernetes Service Cluster Name
    value: ${{ jobs.deploy.outputs.aksName }}

    permissions:
    id-token: write
    contents: read

    jobs:
    validate:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    - uses: azure/login@v1
    name: Sign in to Azure
    with:
    client-id: ${{ secrets.AZURE_CLIENT_ID }}
    tenant-id: ${{ secrets.AZURE_TENANT_ID }}
    subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
    - uses: azure/arm-deploy@v1
    name: Run preflight validation
    with:
    deploymentName: ${{ github.run_number }}
    scope: subscription
    region: eastus
    template: ./deploy/main.bicep
    parameters: >
    resourceGroup=${{ inputs.resourceGroupName }}
    deploymentMode: Validate

    deploy:
    needs: validate
    runs-on: ubuntu-latest
    outputs:
    containerRegistryName: ${{ steps.deploy.outputs.acr_name }}
    containerRegistryUrl: ${{ steps.deploy.outputs.acr_login_server_url }}
    resourceGroupName: ${{ steps.deploy.outputs.resource_group_name }}
    aksName: ${{ steps.deploy.outputs.aks_name }}
    steps:
    - uses: actions/checkout@v2
    - uses: azure/login@v1
    name: Sign in to Azure
    with:
    client-id: ${{ secrets.AZURE_CLIENT_ID }}
    tenant-id: ${{ secrets.AZURE_TENANT_ID }}
    subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
    - uses: azure/arm-deploy@v1
    id: deploy
    name: Deploy Bicep file
    with:
    failOnStdErr: false
    deploymentName: ${{ github.run_number }}
    scope: subscription
    region: eastus
    template: ./deploy/main.bicep
    parameters: >
    resourceGroup=${{ inputs.resourceGroupName }}

    Adding the Bicep Deployment

    Once we have the Bicep deployment workflow, we can add it to the primary build definition in .github/workflows/dotnetcore.yml

    Permissions

    First, we need to add a permissions block to let the workflow request our Azure AD token. This can go towards the top of the YAML file (I started it on line 5).

    permissions:
    id-token: write
    contents: read

    Deploy AKS Job

    Next, we'll add a reference to our reusable workflow. This will go after the build job.

      deploy_aks:
    needs: [build]
    uses: ./.github/workflows/deploy_aks.yml
    with:
    resourceGroupName: 'cnny-week3'
    secrets:
    AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
    AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
    AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }}

    Building and Publishing a Container Image

    Now that we have our target environment in place and an Azure Container Registry, we can build and publish our container images.

    Add a Reusable Workflow

    First, we'll create a new file for our reusable workflow at .github/workflows/publish_container_image.yml.

    We'll start the file with a name, the parameters it needs to run, and the permissions requirements for the federated identity request.

    name: Publish Container Images

    on:
    workflow_call:
    inputs:
    containerRegistryName:
    required: true
    type: string
    containerRegistryUrl:
    required: true
    type: string
    githubSha:
    required: true
    type: string
    secrets:
    AZURE_CLIENT_ID:
    required: true
    AZURE_TENANT_ID:
    required: true
    AZURE_SUBSCRIPTION_ID:
    required: true

    permissions:
    id-token: write
    contents: read

    Build the Container Images

    Our next step is to build the two container images we'll need for the application, the website and the API. We'll build the container images on our build worker and tag it with the git SHA, so there'll be a direct tie between the point in time in our codebase and the container images that represent it.

    jobs:
    publish_container_image:
    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v2
    - name: docker build
    run: |
    docker build . -f src/Web/Dockerfile -t ${{ inputs.containerRegistryUrl }}/web:${{ inputs.githubSha }}
    docker build . -f src/PublicApi/Dockerfile -t ${{ inputs.containerRegistryUrl }}/api:${{ inputs.githubSha}}

    Scan the Container Images

    Before we publish those container images, we'll scan them for vulnerabilities and best practice violations. We can add these two steps (one scan for each image).

        - name: scan web container image
    uses: Azure/container-scan@v0
    with:
    image-name: ${{ inputs.containerRegistryUrl }}/web:${{ inputs.githubSha}}
    - name: scan api container image
    uses: Azure/container-scan@v0
    with:
    image-name: ${{ inputs.containerRegistryUrl }}/web:${{ inputs.githubSha}}

    The container images provided have a few items that'll be found. We can create an allowed list at .github/containerscan/allowedlist.yaml to define vulnerabilities or best practice violations that we'll explictly allow to not fail our build.

    general:
    vulnerabilities:
    - CVE-2022-29458
    - CVE-2022-3715
    - CVE-2022-1304
    - CVE-2021-33560
    - CVE-2020-16156
    - CVE-2019-8457
    - CVE-2018-8292
    bestPracticeViolations:
    - CIS-DI-0001
    - CIS-DI-0005
    - CIS-DI-0006
    - CIS-DI-0008

    Publish the Container Images

    Finally, we'll log in to Azure, then log in to our Azure Container Registry, and push our images.

        - uses: azure/login@v1
    name: Sign in to Azure
    with:
    client-id: ${{ secrets.AZURE_CLIENT_ID }}
    tenant-id: ${{ secrets.AZURE_TENANT_ID }}
    subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
    - name: acr login
    run: az acr login --name ${{ inputs.containerRegistryName }}
    - name: docker push
    run: |
    docker push ${{ inputs.containerRegistryUrl }}/web:${{ inputs.githubSha}}
    docker push ${{ inputs.containerRegistryUrl }}/api:${{ inputs.githubSha}}

    Update the Build With the Image Build and Publish

    Now that we have our reusable workflow to create and publish our container images, we can include that in our primary build defnition at .github/workflows/dotnetcore.yml.

      publish_container_image:
    needs: [deploy_aks]
    uses: ./.github/workflows/publish_container_image.yml
    with:
    containerRegistryName: ${{ needs.deploy_aks.outputs.containerRegistryName }}
    containerRegistryUrl: ${{ needs.deploy_aks.outputs.containerRegistryUrl }}
    githubSha: ${{ github.sha }}
    secrets:
    AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
    AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
    AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }}

    Deploying to Kubernetes

    Finally, we've gotten enough set up that a commit to the target branch will:

    • build and test our application code
    • set up (or validate) our AKS and ACR environment
    • and create, scan, and publish our container images to ACR

    Our last step will be to deploy our application to Kubernetes. We'll use the basic building blocks we worked with last week, deployments and services.

    Starting the Reusable Workflow to Deploy to AKS

    We'll start our workflow with our parameters that we need, as well as the permissions to access the token to log in to Azure.

    We'll check out our code, then log in to Azure, and use the az CLI to get credentials for our AKS cluster.

    name: deploy_to_aks

    on:
    workflow_call:
    inputs:
    aksName:
    required: true
    type: string
    resourceGroupName:
    required: true
    type: string
    containerRegistryUrl:
    required: true
    type: string
    githubSha:
    required: true
    type: string
    secrets:
    AZURE_CLIENT_ID:
    required: true
    AZURE_TENANT_ID:
    required: true
    AZURE_SUBSCRIPTION_ID:
    required: true

    permissions:
    id-token: write
    contents: read

    jobs:
    deploy:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    - uses: azure/login@v1
    name: Sign in to Azure
    with:
    client-id: ${{ secrets.AZURE_CLIENT_ID }}
    tenant-id: ${{ secrets.AZURE_TENANT_ID }}
    subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
    - name: Get AKS Credentials
    run: |
    az aks get-credentials --resource-group ${{ inputs.resourceGroupName }} --name ${{ inputs.aksName }}

    Edit the Deployment For Our Current Image Tag

    Let's add the Kubernetes manifests to our repo. This post is long enough, so you can find the content for the manifests folder in the manifests folder in the source repo under the week3/day1 branch.

    tip

    If you only forked the main branch of the source repo, you can easily get the updated manifests by using the following git commands:

    git remote add upstream https://github.com/Azure-Samples/eShopOnAks
    git fetch upstream week3/day1
    git checkout upstream/week3/day1 manifests

    This will make the week3/day1 branch available locally and then we can update the manifests directory to match the state of that branch.

    The deployments and the service defintions should be familiar from last week's content (but not the same). This week, however, there's a new file in the manifests - ./manifests/kustomization.yaml

    This file helps us more dynamically edit our kubernetes manifests and support is baked right in to the kubectl command.

    Kustomize Definition

    Kustomize allows us to specify specific resource manifests and areas of that manifest to replace. We've put some placeholders in our file as well, so we can replace those for each run of our CI/CD system.

    In ./manifests/kustomization.yaml you will see:

    resources:
    - deployment-api.yaml
    - deployment-web.yaml

    # Change the image name and version
    images:
    - name: notavalidregistry.azurecr.io/api:v0.1.0
    newName: <YOUR_ACR_SERVER>/api
    newTag: <YOUR_IMAGE_TAG>
    - name: notavalidregistry.azurecr.io/web:v0.1.0
    newName: <YOUR_ACR_SERVER>/web
    newTag: <YOUR_IMAGE_TAG>

    Replacing Values in our Build

    Now, we encounter a little problem - our deployment files need to know the tag and ACR server. We can do a bit of sed magic to edit the file on the fly.

    In .github/workflows/deploy_to_aks.yml, we'll add:

          - name: replace_placeholders_with_current_run
    run: |
    sed -i "s/<YOUR_ACR_SERVER>/${{ inputs.containerRegistryUrl }}/g" ./manifests/kustomization.yaml
    sed -i "s/<YOUR_IMAGE_TAG>/${{ inputs.githubSha }}/g" ./manifests/kustomization.yaml

    Deploying the Manifests

    We have our manifests in place and our kustomization.yaml file (with commands to update it at runtime) ready to go, we can deploy our manifests.

    First, we'll deploy our database (deployment and service). Next, we'll use the -k parameter on kubectl to tell it to look for a kustomize configuration, transform the requested manifests and apply those. Finally, we apply the service defintions for the web and API deployments.

            run: |
    kubectl apply -f ./manifests/deployment-db.yaml \
    -f ./manifests/service-db.yaml
    kubectl apply -k ./manifests
    kubectl apply -f ./manifests/service-api.yaml \
    -f ./manifests/service-web.yaml

    Summary

    We've covered a lot of ground in today's post. We set up federated credentials with GitHub. Then we added reusable workflows to deploy an AKS environment and build/scan/publish our container images, and then to deploy them into our AKS environment.

    This sets us up to start making changes to our application and Kubernetes configuration and have those changes automatically validated and deployed by our CI/CD system. Tomorrow, we'll look at updating our application environment with runtime configuration, persistent storage, and more.

    Resources

    Take the Cloud Skills Challenge!

    Enroll in the Cloud Skills Challenge!

    Don't miss out on this opportunity to level up your skills and stay ahead of the curve in the world of cloud native.

    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/azure-kubernetes-service/page/13/index.html b/cnny-2023/tags/azure-kubernetes-service/page/13/index.html index 752030b295..933157f954 100644 --- a/cnny-2023/tags/azure-kubernetes-service/page/13/index.html +++ b/cnny-2023/tags/azure-kubernetes-service/page/13/index.html @@ -14,13 +14,13 @@ - - + +

    21 posts tagged with "azure-kubernetes-service"

    View All Tags

    · 12 min read
    Paul Yu

    Welcome to Day 2 of Week 3 of #CloudNativeNewYear!

    The theme for this week is Bringing Your Application to Kubernetes. Yesterday we talked about getting an existing application running in Kubernetes with a full pipeline in GitHub Actions. Today we'll evaluate our sample application's configuration, storage, and networking requirements and implement using Kubernetes and Azure resources.

    Ask the Experts Thursday, February 9th at 9 AM PST
    Friday, February 10th at 11 AM PST

    Watch the recorded demo and conversation about this week's topics.

    We were live on YouTube walking through today's (and the rest of this week's) demos. Join us Friday, February 10th and bring your questions!

    What We'll Cover

    • Gather requirements
    • Implement environment variables using ConfigMaps
    • Implement persistent volumes using Azure Files
    • Implement secrets using Azure Key Vault
    • Re-package deployments
    • Conclusion
    • Resources
    caution

    Before you begin, make sure you've gone through yesterday's post to set up your AKS cluster.

    Gather requirements

    The eShopOnWeb application is written in .NET 7 and has two major pieces of functionality. The web UI is where customers can browse and shop. The web UI also includes an admin portal for managing the product catalog. This admin portal, is packaged as a WebAssembly application and relies on a separate REST API service. Both the web UI and the REST API connect to the same SQL Server container.

    Looking through the source code which can be found here we can identify requirements for configs, persistent storage, and secrets.

    Database server

    • Need to store the password for the sa account as a secure secret
    • Need persistent storage volume for data directory
    • Need to inject environment variables for SQL Server license type and EULA acceptance

    Web UI and REST API service

    • Need to store database connection string as a secure secret
    • Need to inject ASP.NET environment variables to override app settings
    • Need persistent storage volume for ASP.NET key storage

    Implement environment variables using ConfigMaps

    ConfigMaps are relatively straight-forward to create. If you were following along with the examples last week, this should be review 😉

    Create a ConfigMap to store database environment variables.

    kubectl apply -f - <<EOF
    apiVersion: v1
    kind: ConfigMap
    metadata:
    name: mssql-settings
    data:
    MSSQL_PID: Developer
    ACCEPT_EULA: "Y"
    EOF

    Create another ConfigMap to store ASP.NET environment variables.

    kubectl apply -f - <<EOF
    apiVersion: v1
    kind: ConfigMap
    metadata:
    name: aspnet-settings
    data:
    ASPNETCORE_ENVIRONMENT: Development
    EOF

    Implement persistent volumes using Azure Files

    Similar to last week, we'll take advantage of storage classes built into AKS. For our SQL Server data, we'll use the azurefile-csi-premium storage class and leverage an Azure Files resource as our PersistentVolume.

    Create a PersistentVolumeClaim (PVC) for persisting SQL Server data.

    kubectl apply -f - <<EOF
    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
    name: mssql-data
    spec:
    accessModes:
    - ReadWriteMany
    storageClassName: azurefile-csi-premium
    resources:
    requests:
    storage: 5Gi
    EOF

    Create another PVC for persisting ASP.NET data.

    kubectl apply -f - <<EOF
    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
    name: aspnet-data
    spec:
    accessModes:
    - ReadWriteMany
    storageClassName: azurefile-csi-premium
    resources:
    requests:
    storage: 5Gi
    EOF

    Implement secrets using Azure Key Vault

    It's a well known fact that Kubernetes secretes are not really secrets. They're just base64-encoded values and not secure, especially if malicious users have access to your Kubernetes cluster.

    In a production scenario, you will want to leverage an external vault like Azure Key Vault or HashiCorp Vault to encrypt and store secrets.

    With AKS, we can enable the Secrets Store CSI driver add-on which will allow us to leverage Azure Key Vault.

    # Set some variables
    RG_NAME=<YOUR_RESOURCE_GROUP_NAME>
    AKS_NAME=<YOUR_AKS_CLUSTER_NAME>
    ACR_NAME=<YOUR_ACR_NAME>

    az aks enable-addons \
    --addons azure-keyvault-secrets-provider \
    --name $AKS_NAME \
    --resource-group $RG_NAME

    With the add-on enabled, you should see aks-secrets-store-csi-driver and aks-secrets-store-provider-azure resources installed on each node in your Kubernetes cluster.

    Run the command below to verify.

    kubectl get pods \
    --namespace kube-system \
    --selector 'app in (secrets-store-csi-driver, secrets-store-provider-azure)'

    The Secrets Store CSI driver allows us to use secret stores via Container Storage Interface (CSI) volumes. This provider offers capabilities such as mounting and syncing between the secure vault and Kubernetes Secrets. On AKS, the Azure Key Vault Provider for Secrets Store CSI Driver enables integration with Azure Key Vault.

    You may not have an Azure Key Vault created yet, so let's create one and add some secrets to it.

    AKV_NAME=$(az keyvault create \
    --name akv-eshop$RANDOM \
    --resource-group $RG_NAME \
    --query name -o tsv)

    # Database server password
    az keyvault secret set \
    --vault-name $AKV_NAME \
    --name mssql-password \
    --value "@someThingComplicated1234"

    # Catalog database connection string
    az keyvault secret set \
    --vault-name $AKV_NAME \
    --name mssql-connection-catalog \
    --value "Server=db;Database=Microsoft.eShopOnWeb.CatalogDb;User Id=sa;Password=@someThingComplicated1234;TrustServerCertificate=True;"

    # Identity database connection string
    az keyvault secret set \
    --vault-name $AKV_NAME \
    --name mssql-connection-identity \
    --value "Server=db;Database=Microsoft.eShopOnWeb.Identity;User Id=sa;Password=@someThingComplicated1234;TrustServerCertificate=True;"

    Pods authentication using Azure Workload Identity

    In order for our Pods to retrieve secrets from Azure Key Vault, we'll need to set up a way for the Pod to authenticate against Azure AD. This can be achieved by implementing the new Azure Workload Identity feature of AKS.

    info

    At the time of this writing, the workload identity feature of AKS is in Preview.

    The workload identity feature within AKS allows us to leverage native Kubernetes resources and link a Kubernetes ServiceAccount to an Azure Managed Identity to authenticate against Azure AD.

    For the authentication flow, our Kubernetes cluster will act as an Open ID Connect (OIDC) issuer and will be able issue identity tokens to ServiceAccounts which will be assigned to our Pods.

    The Azure Managed Identity will be granted permission to access secrets in our Azure Key Vault and with the ServiceAccount being assigned to our Pods, they will be able to retrieve our secrets.

    For more information on how the authentication mechanism all works, check out this doc.

    To implement all this, start by enabling the new preview feature for AKS.

    az feature register \
    --namespace "Microsoft.ContainerService" \
    --name "EnableWorkloadIdentityPreview"
    caution

    This can take several minutes to complete.

    Check the status and ensure the state shows Regestered before moving forward.

    az feature show \
    --namespace "Microsoft.ContainerService" \
    --name "EnableWorkloadIdentityPreview"

    Update your AKS cluster to enable the workload identity feature and enable the OIDC issuer endpoint.

    az aks update \
    --name $AKS_NAME \
    --resource-group $RG_NAME \
    --enable-workload-identity \
    --enable-oidc-issuer

    Create an Azure Managed Identity and retrieve its client ID.

    MANAGED_IDENTITY_CLIENT_ID=$(az identity create \
    --name aks-workload-identity \
    --resource-group $RG_NAME \
    --subscription $(az account show --query id -o tsv) \
    --query 'clientId' -o tsv)

    Create the Kubernetes ServiceAccount.

    # Set namespace (this must align with the namespace that your app is deployed into)
    SERVICE_ACCOUNT_NAMESPACE=default

    # Set the service account name
    SERVICE_ACCOUNT_NAME=eshop-serviceaccount

    # Create the service account
    kubectl apply -f - <<EOF
    apiVersion: v1
    kind: ServiceAccount
    metadata:
    annotations:
    azure.workload.identity/client-id: ${MANAGED_IDENTITY_CLIENT_ID}
    labels:
    azure.workload.identity/use: "true"
    name: ${SERVICE_ACCOUNT_NAME}
    namespace: ${SERVICE_ACCOUNT_NAMESPACE}
    EOF
    info

    Note to enable this ServiceAccount to work with Azure Workload Identity, you must annotate the resource with azure.workload.identity/client-id, and add a label of azure.workload.identity/use: "true"

    That was a lot... Let's review what we just did.

    We have an Azure Managed Identity (object in Azure AD), an OIDC issuer URL (endpoint in our Kubernetes cluster), and a Kubernetes ServiceAccount.

    The next step is to "tie" these components together and establish a Federated Identity Credential so that Azure AD can trust authentication requests from your Kubernetes cluster.

    info

    This identity federation can be established between Azure AD any Kubernetes cluster; not just AKS 🤗

    To establish the federated credential, we'll need the OIDC issuer URL, and a subject which points to your Kubernetes ServiceAccount.

    # Get the OIDC issuer URL
    OIDC_ISSUER_URL=$(az aks show \
    --name $AKS_NAME \
    --resource-group $RG_NAME \
    --query "oidcIssuerProfile.issuerUrl" -o tsv)

    # Set the subject name using this format: `system:serviceaccount:<YOUR_SERVICE_ACCOUNT_NAMESPACE>:<YOUR_SERVICE_ACCOUNT_NAME>`
    SUBJECT=system:serviceaccount:$SERVICE_ACCOUNT_NAMESPACE:$SERVICE_ACCOUNT_NAME

    az identity federated-credential create \
    --name aks-federated-credential \
    --identity-name aks-workload-identity \
    --resource-group $RG_NAME \
    --issuer $OIDC_ISSUER_URL \
    --subject $SUBJECT

    With the authentication components set, we can now create a SecretProviderClass which includes details about the Azure Key Vault, the secrets to pull out from the vault, and identity used to access the vault.

    # Get the tenant id for the key vault
    TENANT_ID=$(az keyvault show \
    --name $AKV_NAME \
    --resource-group $RG_NAME \
    --query properties.tenantId -o tsv)

    # Create the secret provider for azure key vault
    kubectl apply -f - <<EOF
    apiVersion: secrets-store.csi.x-k8s.io/v1
    kind: SecretProviderClass
    metadata:
    name: eshop-azure-keyvault
    spec:
    provider: azure
    parameters:
    usePodIdentity: "false"
    useVMManagedIdentity: "false"
    clientID: "${MANAGED_IDENTITY_CLIENT_ID}"
    keyvaultName: "${AKV_NAME}"
    cloudName: ""
    objects: |
    array:
    - |
    objectName: mssql-password
    objectType: secret
    objectVersion: ""
    - |
    objectName: mssql-connection-catalog
    objectType: secret
    objectVersion: ""
    - |
    objectName: mssql-connection-identity
    objectType: secret
    objectVersion: ""
    tenantId: "${TENANT_ID}"
    secretObjects:
    - secretName: eshop-secrets
    type: Opaque
    data:
    - objectName: mssql-password
    key: mssql-password
    - objectName: mssql-connection-catalog
    key: mssql-connection-catalog
    - objectName: mssql-connection-identity
    key: mssql-connection-identity
    EOF

    Finally, lets grant the Azure Managed Identity permissions to retrieve secrets from the Azure Key Vault.

    az keyvault set-policy \
    --name $AKV_NAME \
    --secret-permissions get \
    --spn $MANAGED_IDENTITY_CLIENT_ID

    Re-package deployments

    Update your database deployment to load environment variables from our ConfigMap, attach the PVC and SecretProviderClass as volumes, mount the volumes into the Pod, and use the ServiceAccount to retrieve secrets.

    Additionally, you may notice the database Pod is set to use fsGroup:10001 as part of the securityContext. This is required as the MSSQL container runs using a non-root account called mssql and this account has the proper permissions to read/write data at the /var/opt/mssql mount path.

    kubectl apply -f - <<EOF
    apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: db
    labels:
    app: db
    spec:
    replicas: 1
    selector:
    matchLabels:
    app: db
    template:
    metadata:
    labels:
    app: db
    spec:
    securityContext:
    fsGroup: 10001
    serviceAccountName: ${SERVICE_ACCOUNT_NAME}
    containers:
    - name: db
    image: mcr.microsoft.com/mssql/server:2019-latest
    ports:
    - containerPort: 1433
    envFrom:
    - configMapRef:
    name: mssql-settings
    env:
    - name: MSSQL_SA_PASSWORD
    valueFrom:
    secretKeyRef:
    name: eshop-secrets
    key: mssql-password
    resources: {}
    volumeMounts:
    - name: mssqldb
    mountPath: /var/opt/mssql
    - name: eshop-secrets
    mountPath: "/mnt/secrets-store"
    readOnly: true
    volumes:
    - name: mssqldb
    persistentVolumeClaim:
    claimName: mssql-data
    - name: eshop-secrets
    csi:
    driver: secrets-store.csi.k8s.io
    readOnly: true
    volumeAttributes:
    secretProviderClass: eshop-azure-keyvault
    EOF

    We'll update the API and Web deployments in a similar way.

    # Set the image tag
    IMAGE_TAG=<YOUR_IMAGE_TAG>

    # API deployment
    kubectl apply -f - <<EOF
    apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: api
    labels:
    app: api
    spec:
    replicas: 1
    selector:
    matchLabels:
    app: api
    template:
    metadata:
    labels:
    app: api
    spec:
    serviceAccount: ${SERVICE_ACCOUNT_NAME}
    containers:
    - name: api
    image: ${ACR_NAME}.azurecr.io/api:${IMAGE_TAG}
    ports:
    - containerPort: 80
    envFrom:
    - configMapRef:
    name: aspnet-settings
    env:
    - name: ConnectionStrings__CatalogConnection
    valueFrom:
    secretKeyRef:
    name: eshop-secrets
    key: mssql-connection-catalog
    - name: ConnectionStrings__IdentityConnection
    valueFrom:
    secretKeyRef:
    name: eshop-secrets
    key: mssql-connection-identity
    resources: {}
    volumeMounts:
    - name: aspnet
    mountPath: ~/.aspnet/https:/root/.aspnet/https:ro
    - name: eshop-secrets
    mountPath: "/mnt/secrets-store"
    readOnly: true
    volumes:
    - name: aspnet
    persistentVolumeClaim:
    claimName: aspnet-data
    - name: eshop-secrets
    csi:
    driver: secrets-store.csi.k8s.io
    readOnly: true
    volumeAttributes:
    secretProviderClass: eshop-azure-keyvault
    EOF

    ## Web deployment
    kubectl apply -f - <<EOF
    apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: web
    labels:
    app: web
    spec:
    replicas: 1
    selector:
    matchLabels:
    app: web
    template:
    metadata:
    labels:
    app: web
    spec:
    serviceAccount: ${SERVICE_ACCOUNT_NAME}
    containers:
    - name: web
    image: ${ACR_NAME}.azurecr.io/web:${IMAGE_TAG}
    ports:
    - containerPort: 80
    envFrom:
    - configMapRef:
    name: aspnet-settings
    env:
    - name: ConnectionStrings__CatalogConnection
    valueFrom:
    secretKeyRef:
    name: eshop-secrets
    key: mssql-connection-catalog
    - name: ConnectionStrings__IdentityConnection
    valueFrom:
    secretKeyRef:
    name: eshop-secrets
    key: mssql-connection-identity
    resources: {}
    volumeMounts:
    - name: aspnet
    mountPath: ~/.aspnet/https:/root/.aspnet/https:ro
    - name: eshop-secrets
    mountPath: "/mnt/secrets-store"
    readOnly: true
    volumes:
    - name: aspnet
    persistentVolumeClaim:
    claimName: aspnet-data
    - name: eshop-secrets
    csi:
    driver: secrets-store.csi.k8s.io
    readOnly: true
    volumeAttributes:
    secretProviderClass: eshop-azure-keyvault
    EOF

    If all went well with your deployment updates, you should be able to browse to your website and buy some merchandise again 🥳

    echo "http://$(kubectl get service web -o jsonpath='{.status.loadBalancer.ingress[0].ip}')"

    Conclusion

    Although there is no visible changes on with our website, we've made a ton of changes on the Kubernetes backend to make this application much more secure and resilient.

    We used a combination of Kubernetes resources and AKS-specific features to achieve our goal of securing our secrets and ensuring data is not lost on container crashes and restarts.

    To learn more about the components we leveraged here today, checkout the resources and additional tutorials listed below.

    You can also find manifests with all the changes made in today's post in the Azure-Samples/eShopOnAKS repository.

    See you in the next post!

    Resources

    Take the Cloud Skills Challenge!

    Enroll in the Cloud Skills Challenge!

    Don't miss out on this opportunity to level up your skills and stay ahead of the curve in the world of cloud native.

    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/azure-kubernetes-service/page/14/index.html b/cnny-2023/tags/azure-kubernetes-service/page/14/index.html index 5bb5a2f9bb..ea8b76377b 100644 --- a/cnny-2023/tags/azure-kubernetes-service/page/14/index.html +++ b/cnny-2023/tags/azure-kubernetes-service/page/14/index.html @@ -14,13 +14,13 @@ - - + +

    21 posts tagged with "azure-kubernetes-service"

    View All Tags

    · 10 min read
    Paul Yu

    Welcome to Day 3 of Week 3 of #CloudNativeNewYear!

    The theme for this week is Bringing Your Application to Kubernetes. Yesterday we added configuration, secrets, and storage to our app. Today we'll explore how to expose the eShopOnWeb app so that customers can reach it over the internet using a custom domain name and TLS.

    Ask the Experts Thursday, February 9th at 9 AM PST
    Friday, February 10th at 11 AM PST

    Watch the recorded demo and conversation about this week's topics.

    We were live on YouTube walking through today's (and the rest of this week's) demos. Join us Friday, February 10th and bring your questions!

    What We'll Cover

    • Gather requirements
    • Generate TLS certificate and store in Azure Key Vault
    • Implement custom DNS using Azure DNS
    • Enable Web Application Routing add-on for AKS
    • Implement Ingress for the web application
    • Conclusion
    • Resources

    Gather requirements

    Currently, our eShopOnWeb app has three Kubernetes services deployed:

    1. db exposed internally via ClusterIP
    2. api exposed externally via LoadBalancer
    3. web exposed externally via LoadBalancer

    As mentioned in my post last week, Services allow applications to communicate with each other using DNS names. Kubernetes has service discovery capabilities built-in that allows Pods to resolve Services simply by using their names.

    In the case of our api and web deployments, they can simply reach the database by calling its name. The service type of ClusterIP for the db can remain as-is since it only needs to be accessed by the api and web apps.

    On the other hand, api and web both need to be accessed over the public internet. Currently, these services are using service type LoadBalancer which tells AKS to provision an Azure Load Balancer with a public IP address. No one is going to remember the IP addresses, so we need to make the app more accessible by adding a custom domain name and securing it with a TLS certificate.

    Here's what we're going to need:

    • Custom domain name for our app
    • TLS certificate for the custom domain name
    • Routing rule to ensure requests with /api/ in the URL is routed to the backend REST API
    • Routing rule to ensure requests without /api/ in the URL is routing to the web UI

    Just like last week, we will use the Web Application Routing add-on for AKS. But this time, we'll integrate it with Azure DNS and Azure Key Vault to satisfy all of our requirements above.

    info

    At the time of this writing the add-on is still in Public Preview

    Generate TLS certificate and store in Azure Key Vault

    We deployed an Azure Key Vault yesterday to store secrets. We'll use it again to store a TLS certificate too.

    Let's create and export a self-signed certificate for the custom domain.

    DNS_NAME=eshoponweb$RANDOM.com
    openssl req -new -x509 -nodes -out web-tls.crt -keyout web-tls.key -subj "/CN=${DNS_NAME}" -addext "subjectAltName=DNS:${DNS_NAME}"
    openssl pkcs12 -export -in web-tls.crt -inkey web-tls.key -out web-tls.pfx -password pass:
    info

    For learning purposes we'll use a self-signed certificate and a fake custom domain name.

    To browse to the site using the fake domain, we'll mimic a DNS lookup by adding an entry to your host file which maps the public IP address assigned to the ingress controller to the custom domain.

    In a production scenario, you will need to have a real domain delegated to Azure DNS and a valid TLS certificate for the domain.

    Grab your Azure Key Vault name and set the value in a variable for later use.

    RESOURCE_GROUP=cnny-week3

    AKV_NAME=$(az resource list \
    --resource-group $RESOURCE_GROUP \
    --resource-type Microsoft.KeyVault/vaults \
    --query "[0].name" -o tsv)

    Grant yourself permissions to get, list, and import certificates.

    MY_USER_NAME=$(az account show --query user.name -o tsv)
    MY_USER_OBJECT_ID=$(az ad user show --id $MY_USER_NAME --query id -o tsv)

    az keyvault set-policy \
    --name $AKV_NAME \
    --object-id $MY_USER_OBJECT_ID \
    --certificate-permissions get list import

    Upload the TLS certificate to Azure Key Vault and grab its certificate URI.

    WEB_TLS_CERT_ID=$(az keyvault certificate import \
    --vault-name $AKV_NAME \
    --name web-tls \
    --file web-tls.pfx \
    --query id \
    --output tsv)

    Implement custom DNS with Azure DNS

    Create a custom domain for our application and grab its Azure resource id.

    DNS_ZONE_ID=$(az network dns zone create \
    --name $DNS_NAME \
    --resource-group $RESOURCE_GROUP \
    --query id \
    --output tsv)

    Enable Web Application Routing add-on for AKS

    As we enable the Web Application Routing add-on, we'll also pass in the Azure DNS Zone resource id which triggers the installation of the external-dns controller in your Kubernetes cluster. This controller will be able to write Azure DNS zone entries on your behalf as you deploy Ingress manifests.

    AKS_NAME=$(az resource list \
    --resource-group $RESOURCE_GROUP \
    --resource-type Microsoft.ContainerService/managedClusters \
    --query "[0].name" -o tsv)

    az aks enable-addons \
    --name $AKS_NAME \
    --resource-group $RESOURCE_GROUP \
    --addons web_application_routing \
    --dns-zone-resource-id=$DNS_ZONE_ID \
    --enable-secret-rotation

    The add-on will also deploy a new Azure Managed Identity which is used by the external-dns controller when writing Azure DNS zone entries. Currently, it does not have permission to do that, so let's grant it permission.

    # This is where resources are automatically deployed by AKS
    NODE_RESOURCE_GROUP=$(az aks show \
    --name $AKS_NAME \
    --resource-group $RESOURCE_GROUP \
    --query nodeResourceGroup -o tsv)

    # This is the managed identity created by the Web Application Routing add-on
    MANAGED_IDENTTIY_OBJECT_ID=$(az resource show \
    --name webapprouting-${AKS_NAME} \
    --resource-group $NODE_RESOURCE_GROUP \
    --resource-type Microsoft.ManagedIdentity/userAssignedIdentities \
    --query properties.principalId \
    --output tsv)

    # Grant the managed identity permissions to write DNS entries
    az role assignment create \
    --role "DNS Zone Contributor" \
    --assignee $MANAGED_IDENTTIY_OBJECT_ID \
    --scope $DNS_ZONE_ID

    The Azure Managed Identity will also be used to retrieve and rotate TLS certificates from Azure Key Vault. So we'll need to grant it permission for that too.

    az keyvault set-policy \
    --name $AKV_NAME \
    --object-id $MANAGED_IDENTTIY_OBJECT_ID \
    --secret-permissions get \
    --certificate-permissions get

    Implement Ingress for the web application

    Before we create a new Ingress manifest, let's update the existing services to use ClusterIP instead of LoadBalancer. With an Ingress in place, there is no reason why we need the Service resources to be accessible from outside the cluster. The new Ingress will be the only entrypoint for external users.

    We can use the kubectl patch command to update the services

    kubectl patch service api -p '{"spec": {"type": "ClusterIP"}}'
    kubectl patch service web -p '{"spec": {"type": "ClusterIP"}}'

    Deploy a new Ingress to place in front of the web Service. Notice there is a special annotations entry for kubernetes.azure.com/tls-cert-keyvault-uri which points back to our self-signed certificate that was uploaded to Azure Key Vault.

    kubectl apply -f - <<EOF
    apiVersion: networking.k8s.io/v1
    kind: Ingress
    metadata:
    annotations:
    kubernetes.azure.com/tls-cert-keyvault-uri: ${WEB_TLS_CERT_ID}
    name: web
    spec:
    ingressClassName: webapprouting.kubernetes.azure.com
    rules:
    - host: ${DNS_NAME}
    http:
    paths:
    - backend:
    service:
    name: web
    port:
    number: 80
    path: /
    pathType: Prefix
    - backend:
    service:
    name: api
    port:
    number: 80
    path: /api
    pathType: Prefix
    tls:
    - hosts:
    - ${DNS_NAME}
    secretName: web-tls
    EOF

    In our manifest above, we've also configured the Ingress route the traffic to either the web or api services based on the URL path requested. If the request URL includes /api/ then it will send traffic to the api backend service. Otherwise, it will send traffic to the web service.

    Within a few minutes, the external-dns controller will add an A record to Azure DNS which points to the Ingress resource's public IP. With the custom domain in place, we can simply browse using this domain name.

    info

    As mentioned above, since this is not a real domain name, we need to modify our host file to make it seem like our custom domain is resolving to the Ingress' public IP address.

    To get the ingress public IP, run the following:

    # Get the IP
    kubectl get ingress web -o jsonpath="{.status.loadBalancer.ingress[0].ip}"

    # Get the hostname
    kubectl get ingress web -o jsonpath="{.spec.tls[0].hosts[0]}"

    Next, open your host file and add an entry using the format <YOUR_PUBLIC_IP> <YOUR_CUSTOM_DOMAIN>. Below is an example of what it should look like.

    20.237.116.224 eshoponweb11265.com

    See this doc for more info on how to do this.

    When browsing to the website, you may be presented with a warning about the connection not being private. This is due to the fact that we are using a self-signed certificate. This is expected, so go ahead and proceed anyway to load up the page.

    Why is the Admin page broken?

    If you log in using the admin@microsoft.com account and browse to the Admin page, you'll notice no products are loaded on the page.

    This is because the admin page is built using Blazor and compiled as a WebAssembly application that runs in your browser. When the application was compiled, it packed the appsettings.Development.json file as an embedded resource. This file contains the base URL for the public API and it currently points to https://localhost:5099. Now that we have a domain name, we can update the base URL and point it to our custom domain.

    From the root of the eShopOnWeb repo, update the configuration file using a sed command.

    sed -i -e "s/localhost:5099/${DNS_NAME}/g" ./src/BlazorAdmin/wwwroot/appsettings.Development.json

    Rebuild and push the container to Azure Container Registry.

    # Grab the name of your Azure Container Registry
    ACR_NAME=$(az resource list \
    --resource-group $RESOURCE_GROUP \
    --resource-type Microsoft.ContainerRegistry/registries \
    --query "[0].name" -o tsv)

    # Invoke a build and publish job
    az acr build \
    --registry $ACR_NAME \
    --image $ACR_NAME.azurecr.io/web:v0.1.0 \
    --file ./src/Web/Dockerfile .

    Once the container build has completed, we can issue a kubectl patch command to quickly update the web deployment to test our change.

    kubectl patch deployment web -p "$(cat <<EOF
    {
    "spec": {
    "template": {
    "spec": {
    "containers": [
    {
    "name": "web",
    "image": "${ACR_NAME}.azurecr.io/web:v0.1.0"
    }
    ]
    }
    }
    }
    }
    EOF
    )"

    If all went well, you will be able to browse the admin page again and confirm product data is being loaded 🥳

    Conclusion

    The Web Application Routing add-on for AKS aims to streamline the process of exposing it to the public using the open-source NGINX Ingress Controller. With the add-on being managed by Azure, it natively integrates with other Azure services like Azure DNS and eliminates the need to manually create DNS entries. It can also integrate with Azure Key Vault to automatically pull in TLS certificates and rotate them as needed to further reduce operational overhead.

    We are one step closer to production and in the upcoming posts we'll further operationalize and secure our deployment, so stay tuned!

    In the meantime, check out the resources listed below for further reading.

    You can also find manifests with all the changes made in today's post in the Azure-Samples/eShopOnAKS repository.

    Resources

    Take the Cloud Skills Challenge!

    Enroll in the Cloud Skills Challenge!

    Don't miss out on this opportunity to level up your skills and stay ahead of the curve in the world of cloud native.

    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/azure-kubernetes-service/page/15/index.html b/cnny-2023/tags/azure-kubernetes-service/page/15/index.html index 7287da937c..1dfbac4e02 100644 --- a/cnny-2023/tags/azure-kubernetes-service/page/15/index.html +++ b/cnny-2023/tags/azure-kubernetes-service/page/15/index.html @@ -14,13 +14,13 @@ - - + +

    21 posts tagged with "azure-kubernetes-service"

    View All Tags

    · 9 min read
    Steven Murawski

    Welcome to Day 4 of Week 3 of #CloudNativeNewYear!

    The theme for this week is Bringing Your Application to Kubernetes. Yesterday we exposed the eShopOnWeb app so that customers can reach it over the internet using a custom domain name and TLS. Today we'll explore the topic of debugging and instrumentation.

    Ask the Experts Thursday, February 9th at 9 AM PST
    Friday, February 10th at 11 AM PST

    Watch the recorded demo and conversation about this week's topics.

    We were live on YouTube walking through today's (and the rest of this week's) demos. Join us Friday, February 10th and bring your questions!

    What We'll Cover

    • Debugging
    • Bridge To Kubernetes
    • Instrumentation
    • Resources: For self-study!

    Debugging

    Debugging applications in a Kubernetes cluster can be challenging for several reasons:

    • Complexity: Kubernetes is a complex system with many moving parts, including pods, nodes, services, and config maps, all of which can interact in unexpected ways and cause issues.
    • Distributed Environment: Applications running in a Kubernetes cluster are often distributed across multiple nodes, which makes it harder to determine the root cause of an issue.
    • Logging and Monitoring: Debugging an application in a Kubernetes cluster requires access to logs and performance metrics, which can be difficult to obtain in a large and dynamic environment.
    • Resource Management: Kubernetes manages resources such as CPU and memory, which can impact the performance and behavior of applications. Debugging resource-related issues requires a deep understanding of the Kubernetes resource model and the underlying infrastructure.
    • Dynamic Nature: Kubernetes is designed to be dynamic, with the ability to add and remove resources as needed. This dynamic nature can make it difficult to reproduce issues and debug problems.

    However, there are many tools and practices that can help make debugging applications in a Kubernetes cluster easier, such as using centralized logging, monitoring, and tracing solutions, and following best practices for managing resources and deployment configurations.

    There's also another great tool in our toolbox - Bridge to Kubernetes.

    Bridge to Kubernetes

    Bridge to Kubernetes is a great tool for microservice development and debugging applications without having to locally replicate all the required microservices.

    Bridge to Kubernetes works with Visual Studio or Visual Studio Code.

    We'll walk through using it with Visual Studio Code.

    Connecting Bridge to Kubernetes to Our Cluster

    Ensure your AKS cluster is the default for kubectl

    If you've recently spun up a new AKS cluster or you have been working with a different cluster, you may need to change what cluster credentials you have configured.

    If it's a new cluster, we can use:

    RESOURCE_GROUP=<YOUR RESOURCE GROUP NAME>
    CLUSTER_NAME=<YOUR AKS CLUSTER NAME>
    az aks get-credentials az aks get-credentials --resource-group $RESOURCE_GROUP --name $CLUSTER_NAME

    Open the command palette

    Open the command palette and find Bridge to Kubernetes: Configure. You may need to start typing the name to get it to show up.

    The command palette for Visual Studio Code is open and the first item is Bridge to Kubernetes: Configure

    Pick the service you want to debug

    Bridge to Kubernetes will redirect a service for you. Pick the service you want to redirect, in this case we'll pick web.

    Selecting the `web` service to redirect in Visual Studio Code

    Identify the port your application runs on

    Next, we'll be prompted to identify what port our application will run on locally. For this application it'll be 5001, but that's just specific to this application (and the default for ASP.NET 7, I believe).

    Setting port 5001 as the port to redirect to the `web` Kubernetes service in Visual Studio Code

    Pick a debug configuration to extend

    Bridge to Kubernetes has a couple of ways to run - it can inject it's setup and teardown to your existing debug configurations. We'll pick .NET Core Launch (web).

    Telling Bridge to Kubernetes to use the .NET Core Launch (web) debug configuration in Visual Studio Code

    Forward Traffic for All Requests

    The last prompt you'll get in the configuration is about how you want Bridge to Kubernetes to handle re-routing traffic. The default is that all requests into the service will get your local version.

    You can also redirect specific traffic. Bridge to Kubernetes will set up a subdomain and route specific traffic to your local service, while allowing other traffic to the deployed service.

    Allowing the launch of Endpoint Manager on Windows

    Using Bridge to Kubernetes to Debug Our Service

    Now that we've configured Bridge to Kubernetes, we see that tasks and a new launch configuration have been added.

    Added to .vscode/tasks.json:

            {
    "label": "bridge-to-kubernetes.resource",
    "type": "bridge-to-kubernetes.resource",
    "resource": "web",
    "resourceType": "service",
    "ports": [
    5001
    ],
    "targetCluster": "aks1",
    "targetNamespace": "default",
    "useKubernetesServiceEnvironmentVariables": false
    },
    {
    "label": "bridge-to-kubernetes.compound",
    "dependsOn": [
    "bridge-to-kubernetes.resource",
    "build"
    ],
    "dependsOrder": "sequence"
    }

    And added to .vscode/launch.json:

    {
    "name": ".NET Core Launch (web) with Kubernetes",
    "type": "coreclr",
    "request": "launch",
    "preLaunchTask": "bridge-to-kubernetes.compound",
    "program": "${workspaceFolder}/src/Web/bin/Debug/net7.0/Web.dll",
    "args": [],
    "cwd": "${workspaceFolder}/src/Web",
    "stopAtEntry": false,
    "env": {
    "ASPNETCORE_ENVIRONMENT": "Development",
    "ASPNETCORE_URLS": "http://+:5001"
    },
    "sourceFileMap": {
    "/Views": "${workspaceFolder}/Views"
    }
    }

    Launch the debug configuration

    We can start the process with the .NET Core Launch (web) with Kubernetes launch configuration in the Debug pane in Visual Studio Code.

    Launch the `.NET Core Launch (web) with Kubernetes` from the Debug pane in Visual Studio Code

    Enable the Endpoint Manager

    Part of this process includes a local service to help manage the traffic routing and your hosts file. This will require admin or sudo privileges. On Windows, you'll get a prompt like:

    Prompt to launch the endpoint manager.

    Access your Kubernetes cluster "locally"

    Bridge to Kubernetes will set up a tunnel (thanks to port forwarding) to your local workstation and create local endpoints for the other Kubernetes hosted services in your cluster, as well as pretending to be a pod in that cluster (for the application you are debugging).

    Output from Bridge To Kubernetes setup task.

    After making the connection to your Kubernetes cluster, the launch configuration will continue. In this case, we'll make a debug build of the application and attach the debugger. (This process may cause the terminal in VS Code to scroll with build output. You can find the Bridge to Kubernetes output with the local IP addresses and ports in the Output pane for Bridge to Kubernetes.)

    You can set breakpoints, use your debug console, set watches, run tests against your local version of the service.

    Exploring the Running Application Environment

    One of the cool things that Bridge to Kubernetes does for our debugging experience is bring the environment configuration that our deployed pod would inherit. When we launch our app, it'll see configuration and secrets that we'd expect our pod to be running with.

    To test this, we'll set a breakpoint in our application's start up to see what SQL Server is being used. We'll set a breakpoint at src/Infrastructure/Dependencies.cs on line 32.

    Then, we will start debugging the application with Bridge to Kubernetes. When it hits the breakpoint, we'll open the Debug pane and type configuration.GetConnectionString("CatalogConnection").

    When we run locally (not with Bridge to Kubernetes), we'd see:

    configuration.GetConnectionString("CatalogConnection")
    "Server=(localdb)\\mssqllocaldb;Integrated Security=true;Initial Catalog=Microsoft.eShopOnWeb.CatalogDb;"

    But, with Bridge to Kubernetes we see something more like (yours will vary based on the password ):

    configuration.GetConnectionString("CatalogConnection")
    "Server=db;Database=Microsoft.eShopOnWeb.CatalogDb;User Id=sa;Password=*****************;TrustServerCertificate=True;"

    Debugging our local application connected to Kubernetes.

    We can see that the database server configured is based on our db service and the password is pulled from our secret in Azure KeyVault (via AKS).

    This helps us run our local application just like it was actually in our cluster.

    Going Further

    Bridge to Kubernetes also supports more advanced scenarios and, as you need to start routing traffic around inside your cluster, may require you to modify your application to pass along a kubernetes-route-as header to help ensure that traffic for your debugging workloads is properly handled. The docs go into much greater detail about that.

    Instrumentation

    Now that we've figured out our debugging story, we'll need to ensure that we have the right context clues to find where we need to debug or to give us a better idea of how well our microservices are running.

    Logging and Tracing

    Logging and tracing become even more critical in Kubernetes, where your application could be running in a number of pods across different nodes. When you have an issue, in addition to the normal application data, you'll want to know what pod and what node had the issue, what the state of those resources were (were you resource constrained or were shared resources unavailable?), and if autoscaling is enabled, you'll want to know if a scale event has been triggered. There are a multitude of other concerns based on your application and the environment you maintain.

    Given these informational needs, it's crucial to revisit your existing logging and instrumentation. Most frameworks and languages have extensible logging, tracing, and instrumentation libraries that you can iteratively add information to, such as pod and node states, and ensuring that requests can be traced across your microservices. This will pay you back time and time again when you have to troubleshoot issues in your existing environment.

    Centralized Logging

    To enhance the troubleshooting process further, it's important to implement centralized logging to consolidate logs from all your microservices into a single location. This makes it easier to search and analyze logs when you're troubleshooting an issue.

    Automated Alerting

    Additionally, implementing automated alerting, such as sending notifications when specific conditions occur in the logs, can help you detect issues before they escalate.

    End to end Visibility

    End-to-end visibility is also essential in understanding the flow of requests and responses between microservices in a distributed system. With end-to-end visibility, you can quickly identify bottlenecks and slowdowns in the system, helping you to resolve issues more efficiently.

    Resources

    Take the Cloud Skills Challenge!

    Enroll in the Cloud Skills Challenge!

    Don't miss out on this opportunity to level up your skills and stay ahead of the curve in the world of cloud native.

    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/azure-kubernetes-service/page/16/index.html b/cnny-2023/tags/azure-kubernetes-service/page/16/index.html index 6e48c53cb1..a340be860f 100644 --- a/cnny-2023/tags/azure-kubernetes-service/page/16/index.html +++ b/cnny-2023/tags/azure-kubernetes-service/page/16/index.html @@ -14,13 +14,13 @@ - - + +

    21 posts tagged with "azure-kubernetes-service"

    View All Tags

    · 6 min read
    Josh Duffney

    Welcome to Day 5 of Week 3 of #CloudNativeNewYear!

    The theme for this week is Bringing Your Application to Kubernetes. Yesterday we talked about debugging and instrumenting our application. Today we'll explore the topic of container image signing and secure supply chain.

    Ask the Experts Thursday, February 9th at 9 AM PST
    Friday, February 10th at 11 AM PST

    Watch the recorded demo and conversation about this week's topics.

    We were live on YouTube walking through today's (and the rest of this week's) demos. Join us Friday, February 10th and bring your questions!

    What We'll Cover

    • Introduction
    • Prerequisites
    • Create a digital signing certificate
    • Generate an Azure Container Registry Token
    • Set up Notation
    • Install the Notation Azure Key Vault Plugin
    • Add the signing Certificate to Notation
    • Sign Container Images
    • Conclusion

    Introduction

    The secure supply chain is a crucial aspect of software development, delivery, and deployment, and digital signing plays a critical role in this process.

    By using digital signatures to verify the authenticity and integrity of container images, organizations can improve the security of your software supply chain and reduce the risk of security breaches and data compromise.

    In this article, you'll learn how to use Notary, an open-source project hosted by the Cloud Native Computing Foundation (CNCF) to digitally sign container images stored on Azure Container Registry.

    Prerequisites

    To follow along, you'll need an instance of:

    Create a digital signing certificate

    A digital signing certificate is a certificate that is used to digitally sign and verify the authenticity and integrity of digital artifacts. Such documents, software, and of course container images.

    Before you can implement digital signatures, you must first create a digital signing certificate.

    Run the following command to generate the certificate:

    1. Create the policy file

      cat <<EOF > ./my_policy.json
      {
      "issuerParameters": {
      "certificateTransparency": null,
      "name": "Self"
      },
      "x509CertificateProperties": {
      "ekus": [
      "1.3.6.1.5.5.7.3.3"
      ],
      "key_usage": [
      "digitalSignature"
      ],
      "subject": "CN=${keySubjectName}",
      "validityInMonths": 12
      }
      }
      EOF

      The ekus and key usage of this certificate policy dictate that the certificate can only be used for digital signatures.

    2. Create the certificate in Azure Key Vault

      az keyvault certificate create --name $keyName --vault-name $keyVaultName --policy @my_policy.json

      Replace $keyName and $keyVaultName with your desired certificate name and Azure Key Vault instance name.

    Generate a Azure Container Registry token

    Azure Container Registry tokens are used to grant access to the contents of the registry. Tokens can be used for a variety of things such as pulling images, pushing images, or managing the registry.

    As part of the container image signing workflow, you'll need a token to authenticate the Notation CLI with your Azure Container Registry.

    Run the following command to generate an ACR token:

    az acr token create \
    --name $tokenName \
    --registry $registry \
    --scope-map _repositories_admin \
    --query 'credentials.passwords[0].value' \
    --only-show-errors \
    --output tsv

    Replace $tokenName with your name for the ACR token and $registry with the name of your ACR instance.

    Setup Notation

    Notation is the command-line interface for the CNCF Notary project. You'll use it to digitally sign the api and web container images for the eShopOnWeb application.

    Run the following commands to download and install the NotationCli:

    1. Open a terminal or command prompt window

    2. Download the Notary notation release

      curl -Lo notation.tar.gz https://github.com/notaryproject/notation/releases/download/v1.0.0-rc.1/notation_1.0.0-rc.1_linux_amd64.tar.gz > /dev/null 2>&1

      If you're not using Linux, you can find the releases here.

    3. Extract the contents of the notation.tar.gz

      tar xvzf notation.tar.gz > /dev/null 2>&1
    4. Copy the notation binary to the $HOME/bin directory

      cp ./notation $HOME/bin
    5. Add the $HOME/bin directory to the PATH environment variable

      export PATH="$HOME/bin:$PATH"
    6. Remove the downloaded files

      rm notation.tar.gz LICENSE
    7. Check the notation version

      notation --version

    Install the Notation Azure Key Vault plugin

    By design the NotationCli supports plugins that extend its digital signing capabilities to remote registries. And in order to sign your container images stored in Azure Container Registry, you'll need to install the Azure Key Vault plugin for Notation.

    Run the following commands to install the azure-kv plugin:

    1. Download the plugin

      curl -Lo notation-azure-kv.tar.gz \
      https://github.com/Azure/notation-azure-kv/releases/download/v0.5.0-rc.1/notation-azure-kv_0.5.0-rc.1_linux_amd64.tar.gz > /dev/null 2>&1

      Non-Linux releases can be found here.

    2. Extract to the plugin directory & delete download files

      tar xvzf notation-azure-kv.tar.gz -C ~/.config/notation/plugins/azure-kv notation-azure-kv > /dev/null 2>&

      rm -rf notation-azure-kv.tar.gz
    3. Verify the plugin was installed

      notation plugin ls

    Add the signing certificate to Notation

    Now that you have Notation and the Azure Key Vault plugin installed, add the certificate's keyId created above to Notation.

    1. Get the Certificate Key ID from Azure Key Vault

      az keyvault certificate show \
      --vault-name $keyVaultName \
      --name $keyName \
      --query "kid" --only-show-errors --output tsv

      Replace $keyVaultName and $keyName with the appropriate information.

    2. Add the Key ID to KMS using Notation

      notation key add --plugin azure-kv --id $keyID $keyName
    3. Check the key list

      notation key ls

    Sign Container Images

    At this point, all that's left is to sign the container images.

    Run the notation sign command to sign the api and web container images:

    notation sign $registry.azurecr.io/web:$tag \
    --username $tokenName \
    --password $tokenPassword

    notation sign $registry.azurecr.io/api:$tag \
    --username $tokenName \
    --password $tokenPassword

    Replace $registry, $tag, $tokenName, and $tokenPassword with the appropriate values. To improve security, use a SHA hash for the tag.

    NOTE: If you didn't take note of the token password, you can rerun the az acr token create command to generate a new password.

    Conclusion

    Digital signing plays a critical role in ensuring the security of software supply chains.

    By signing software components, organizations can verify the authenticity and integrity of software, helping to prevent unauthorized modifications, tampering, and malware.

    And if you want to take digital signing to a whole new level by using them to prevent the deployment of unsigned container images, check out the Ratify project on GitHub!

    Take the Cloud Skills Challenge!

    Enroll in the Cloud Skills Challenge!

    Don't miss out on this opportunity to level up your skills and stay ahead of the curve in the world of cloud native.

    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/azure-kubernetes-service/page/17/index.html b/cnny-2023/tags/azure-kubernetes-service/page/17/index.html index a51a808663..de33c9a3bb 100644 --- a/cnny-2023/tags/azure-kubernetes-service/page/17/index.html +++ b/cnny-2023/tags/azure-kubernetes-service/page/17/index.html @@ -14,13 +14,13 @@ - - + +

    21 posts tagged with "azure-kubernetes-service"

    View All Tags

    · 7 min read
    Nitya Narasimhan

    Welcome to Week 4 of #CloudNativeNewYear!

    This week we'll go further with Cloud-native by exploring advanced topics and best practices for the Cloud-native practitioner. We'll start with an exploration of Serverless Container Options - ranging from managed services to Azure Kubernetes Service (AKS) and Azure Container Apps (ACA), to options that allow more granular control!

    What We'll Cover

    • The Azure Compute Landscape
    • Serverless Compute on Azure
    • Comparing Container Options On Azure
    • Other Considerations
    • Exercise: Try this yourself!
    • Resources: For self-study!


    We started this series with an introduction to core concepts:

    • In Containers 101, we learned why containerization matters. Think portability, isolation, scalability, resource-efficiency and cost-effectiveness. But not all apps can be containerized.
    • In Kubernetes 101, we learned how orchestration works. Think systems to automate container deployment, scaling, and management. But using Kubernetes directly can be complex.
    • In Exploring Cloud Native Options we asked the real questions: can we containerize - and should we?. The first depends on app characteristics, the second on your requirements.

    For example:

    • Can we containerize? The answer might be no if your app has system or OS dependencies, requires access to low-level hardware, or maintains complex state across sessions.
    • Should we containerize? The answer might be yes if your app is microservices-based, is stateless by default, requires portability, or is a legaacy app that can benefit from container isolation.

    As with every technology adoption decision process, there are no clear yes/no answers - just tradeoffs that you need to evaluate based on your architecture and application requirements. In today's post, we try to look at this from two main perspectives:

    1. Should you go serverless? Think: managed services that let you focus on app, not infra.
    2. What Azure Compute should I use? Think: best fit for my architecture & technology choices.

    Azure Compute Landscape

    Let's answer the second question first by exploring all available compute options on Azure. The illustrated decision-flow below is my favorite ways to navigate the choices, with questions like:

    • Are you migrating an existing app or building a new one?
    • Can you app be containerized?
    • Does it use a specific technology (Spring Boot, Red Hat Openshift)?
    • Do you need access to the Kubernetes API?
    • What characterizes the workload? (event-driven, web app, microservices etc.)

    Read the docs to understand how your choices can be influenced by the hosting model (IaaS, PaaS, FaaS), supported features (Networking, DevOps, Scalability, Availability, Security), architectural styles (Microservices, Event-driven, High-Performance Compute, Task Automation,Web-Queue Worker) etc.

    Compute Choices

    Now that we know all available compute options, let's address the second question: why go serverless? and what are my serverless compute options on Azure?

    Azure Serverless Compute

    Serverless gets defined many ways, but from a compute perspective, we can focus on a few key characteristics that are key to influencing this decision:

    • managed services - focus on application, let cloud provider handle infrastructure.
    • pay for what you use - get cost-effective resource utilization, flexible pricing options.
    • autoscaling on demand - take advantage of built-in features like KEDA-compliant triggers.
    • use preferred languages - write code in Java, JS, C#, Python etc. (specifics based on service)
    • cloud-native architectures - can support event-driven solutions, APIs, Microservices, DevOps!

    So what are some of the key options for Serverless Compute on Azure? The article dives into serverless support for fully-managed end-to-end serverless solutions with comprehensive support for DevOps, DevTools, AI/ML, Database, Storage, Monitoring and Analytics integrations. But we'll just focus on the 4 categories of applications when we look at Compute!

    1. Serverless Containerized Microservices using Azure Container Apps. Code in your preferred language, exploit full Dapr support, scale easily with any KEDA-compliant trigger.
    2. Serverless Application Environments using Azure App Service. Suitable for hosting monolithic apps (vs. microservices) in a managed service, with built-in support for on-demand scaling.
    3. Serverless Kubernetes using Azure Kubernetes Service (AKS). Spin up pods inside container instances and deploy Kubernetes-based applications with built-in KEDA-compliant autoscaling.
    4. Serverless Functions using Azure Functions. Execute "code at the granularity of functions" in your preferred language, and scale on demand with event-driven compute.

    We'll talk about these, and other compute comparisons, at the end of the article. But let's start with the core option you might choose if you want a managed serverless compute solution with built-in features for delivering containerized microservices at scale. Hello, Azure Container Apps!.

    Azure Container Apps

    Azure Container Apps (ACA) became generally available in May 2022 - providing customers with the ability to run microservices and containerized applications on a serverless, consumption-based platform. The figure below showcases the different types of applications that can be built with ACA. Note that it comes with built-in KEDA-compliant autoscaling triggers, and other auto-scale criteria that may be better-suited to the type of application you are building.

    About ACA

    So far in the series, we've put the spotlight on Azure Kubernetes Service (AKS) - so you're probably asking yourself: How does ACA compare to AKS?. We're glad you asked. Check out our Go Cloud-native with Azure Container Apps post from the #ServerlessSeptember series last year for a deeper-dive, or review the figure below for the main comparison points.

    The key takeaway is this. Azure Container Apps (ACA) also runs on Kubernetes but abstracts away its complexity in a managed service offering that lets you get productive quickly without requiring detailed knowledge of Kubernetes workings or APIs. However, if you want full access and control over the Kubernetes API then go with Azure Kubernetes Service (AKS) instead.

    Comparison

    Other Container Options

    Azure Container Apps is the preferred Platform As a Service (PaaS) option for a fully-managed serverless solution on Azure that is purpose-built for cloud-native microservices-based application workloads. But - there are other options that may be suitable for your specific needs, from a requirements and tradeoffs perspective. Let's review them quickly:

    1. Azure Functions is the serverless Functions-as-a-Service (FaaS) option, as opposed to ACA which supports a PaaS approach. It's optimized for running event-driven applications built at the granularity of ephemeral functions that can be deployed as code or containers.
    2. Azure App Service provides fully managed hosting for web applications that may be deployed using code or containers. It can be integrated with other services including Azure Container Apps and Azure Functions. It's optimized for deploying traditional web apps.
    3. Azure Kubernetes Service provides a fully managed Kubernetes option capable of running any Kubernetes workload, with direct access to the Kubernetes API.
    4. Azure Container Instances provides a single pod of Hyper-V isolated containers on demand, making them more of a low-level "building block" option compared to ACA.

    Based on the technology choices you made for application development you may also have more specialized options you want to consider. For instance:

    1. Azure Spring Apps is ideal if you're running Spring Boot or Spring Cloud workloads on Azure,
    2. Azure Red Hat OpenShift is ideal for integrated Kubernetes-powered OpenShift on Azure.
    3. Azure Confidential Computing is ideal if you have data/code integrity and confidentiality needs.
    4. Kubernetes At The Edge is ideal for bare-metal options that extend compute to edge devices.

    This is just the tip of the iceberg in your decision-making journey - but hopefully, it gave you a good sense of the options and criteria that influences your final choices. Let's wrap this up with a look at self-study resources for skilling up further.

    Exercise

    Want to get hands on learning related to these technologies?

    TAKE THE CLOUD SKILLS CHALLENGE

    Register today and level up your skills by completing free learning modules, while competing with your peers for a place on the leaderboards!

    Resources

    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/azure-kubernetes-service/page/18/index.html b/cnny-2023/tags/azure-kubernetes-service/page/18/index.html index 138a801223..f33998bfd4 100644 --- a/cnny-2023/tags/azure-kubernetes-service/page/18/index.html +++ b/cnny-2023/tags/azure-kubernetes-service/page/18/index.html @@ -14,13 +14,13 @@ - - + +

    21 posts tagged with "azure-kubernetes-service"

    View All Tags

    · 3 min read
    Cory Skimming

    It's the final week of #CloudNativeNewYear! This week we'll go further with Cloud-native by exploring advanced topics and best practices for the Cloud-native practitioner. In today's post, we will introduce you to the basics of the open-source project Draft and how it can be used to easily create and deploy applications to Kubernetes.

    It's not too late to sign up for and complete the Cloud Skills Challenge!

    What We'll Cover

    • What is Draft?
    • Draft basics
    • Demo: Developing to AKS with Draft
    • Resources


    What is Draft?

    Draft is an open-source tool that can be used to streamline the development and deployment of applications on Kubernetes clusters. It provides a simple and easy-to-use workflow for creating and deploying applications, making it easier for developers to focus on writing code and building features, rather than worrying about the underlying infrastructure. This is great for users who are just getting started with Kubernetes, or those who are just looking to simplify their experience.

    New to Kubernetes?

    Draft basics

    Draft streamlines Kubernetes development by taking a non-containerized application and generating the Dockerfiles, K8s manifests, Helm charts, and other artifacts associated with a containerized application. Draft can also create a GitHub Action workflow file to quickly build and deploy your application onto any Kubernetes cluster.

    1. 'draft create'': Create a new Draft project by simply running the 'draft create' command - this command will walk you through a series of questions on your application specification (such as the application language) and create a Dockerfile, Helm char, and Kubernetes
    2. 'draft generate-workflow'': Automatically build out a GitHub Action using the 'draft generate-workflow' command
    3. 'draft setup-gh'': If you are using Azure, use this command to automate the GitHub OIDC set up process to ensure that you will be able to deploy your application using your GitHub Action.

    At this point, you will have all the files needed to deploy your app onto a Kubernetes cluster (we told you it was easy!).

    You can also use the 'draft info' command if you are looking for information on supported languages and deployment types. Let's see it in action, shall we?


    Developing to AKS with Draft

    In this Microsoft Reactor session below, we'll briefly introduce Kubernetes and the Azure Kubernetes Service (AKS) and then demo how enable your applications for Kubernetes using the open-source tool Draft. We'll show how Draft can help you create the boilerplate code to containerize your applications and add routing and scaling behaviours.

    ##Conclusion

    Overall, Draft simplifies the process of building, deploying, and managing applications on Kubernetes, and can make the overall journey from code to Kubernetes significantly easier.


    Resources


    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/azure-kubernetes-service/page/19/index.html b/cnny-2023/tags/azure-kubernetes-service/page/19/index.html index 23ece36cf7..99c023ae92 100644 --- a/cnny-2023/tags/azure-kubernetes-service/page/19/index.html +++ b/cnny-2023/tags/azure-kubernetes-service/page/19/index.html @@ -14,14 +14,14 @@ - - + +

    21 posts tagged with "azure-kubernetes-service"

    View All Tags

    · 7 min read
    Vinicius Apolinario

    Welcome to Day 3 of Week 4 of #CloudNativeNewYear!

    The theme for this week is going further with Cloud Native. Yesterday we talked about using Draft to accelerate your Kubernetes adoption. Today we'll explore the topic of Windows containers.

    What We'll Cover

    • Introduction
    • Windows containers overview
    • Windows base container images
    • Isolation
    • Exercise: Try this yourself!
    • Resources: For self-study!

    Introduction

    Windows containers were launched along with Windows Server 2016, and have evolved since. In its latest release, Windows Server 2022, Windows containers have reached a great level of maturity and allow for customers to run production grade workloads.

    While suitable for new developments, Windows containers also provide developers and operations with a different approach than Linux containers. It allows for existing Windows applications to be containerized with little or no code changes. It also allows for professionals that are more comfortable with the Windows platform and OS, to leverage their skill set, while taking advantage of the containers platform.

    Windows container overview

    In essence, Windows containers are very similar to Linux. Since Windows containers use the same foundation of Docker containers, you can expect that the same architecture applies - with the specific notes of the Windows OS. For example, when running a Windows container via Docker, you use the same commands, such as docker run. To pull a container image, you can use docker pull, just like on Linux. However, to run a Windows container, you also need a Windows container host. This requirement is there because, as you might remember, a container shares the OS kernel with its container host.

    On Kubernetes, Windows containers are supported since Windows Server 2019. Just like with Docker, you can manage Windows containers like any other resource on the Kubernetes ecosystem. A Windows node can be part of a Kubernetes cluster, allowing you to run Windows container based applications on services like Azure Kubernetes Service. To deploy an Windows application to a Windows pod in Kubernetes, you can author a YAML specification much like you would for Linux. The main difference is that you would point to an image that runs on Windows, and you need to specify a node selection tag to indicate said pod needs to run on a Windows node.

    Windows base container images

    On Windows containers, you will always use a base container image provided by Microsoft. This base container image contains the OS binaries for the container to run. This image can be as large as 3GB+, or small as ~300MB. The difference in the size is a consequence of the APIs and components available in each Windows container base container image. There are primarily, three images: Nano Server, Server Core, and Server.

    Nano Server is the smallest image, ranging around 300MB. It's a base container image for new developments and cloud-native scenarios. Applications need to target Nano Server as the Windows OS, so not all frameworks will work. For example, .Net works on Nano Server, but .Net Framework doesn't. Other third-party frameworks also work on Nano Server, such as Apache, NodeJS, Phyton, Tomcat, Java runtime, JBoss, Redis, among others.

    Server Core is a much larger base container image, ranging around 1.25GB. It's larger size is compensated by it's application compatibility. Simply put, any application that meets the requirements to be run on a Windows container, can be containerized with this image.

    The Server image builds on the Server Core one. It ranges around 3.1GB and has even greater application compatibility than the Server Core image. In addition to the traditional Windows APIs and components, this image allows for scenarios such as Machine Learning via DirectX with GPU access.

    The best image for your scenario is dependent on the requirements your application has on the Windows OS inside a container. However, there are some scenarios that are not supported at all on Windows containers - such as GUI or RDP dependent applications, some Windows Server infrastructure roles, such as Active Directory, among others.

    Isolation

    When running containers, the kernel of the container host is shared with the containers running on it. While extremely convenient, this poses a potential risk for multi-tenant scenarios. If one container is compromised and has access to the host, it could potentially compromise other containers in the same system.

    For enterprise customers running on-premises (or even in the cloud), this can be mitigated by using a VM as a container host and considering the VM itself a security boundary. However, if multiple workloads from different tenants need to share the same host, Windows containers offer another option: Hyper-V isolation. While the name Hyper-V is associated with VMs, its virtualization capabilities extend to other services, including containers. Hyper-V isolated containers run on a purpose built, extremely small, highly performant VM. However, you manage a container running with Hyper-V isolation the same way you do with a process isolated one. In fact, the only notable difference is that you need to append the --isolation=hyperv tag to the docker run command.

    Exercise

    Here are a few examples of how to use Windows containers:

    Run Windows containers via Docker on your machine

    To pull a Windows base container image:

    docker pull mcr.microsoft.com/windows/servercore:ltsc2022

    To run a basic IIS container:

    #This command will pull and start a IIS container. You can access it from http://<your local IP>:8080
    docker run -d -p 8080:80 mcr.microsoft.com/windows/servercore/iis:windowsservercore-ltsc2022

    Run the same IIS container with Hyper-V isolation

    #This command will pull and start a IIS container. You can access it from http://<your local IP>:8080
    docker run -d -p 8080:80 --isolation=hyperv mcr.microsoft.com/windows/servercore/iis:windowsservercore-ltsc2022

    To run a Windows container interactively:

    docker run -it mcr.microsoft.com/windows/servercore:ltsc2022 powershell

    Run Windows containers on Kubernetes

    To prepare an AKS cluster for Windows containers: Note: Replace the values on the example below with the ones from your environment.

    echo "Please enter the username to use as administrator credentials for Windows Server nodes on your cluster: " && read WINDOWS_USERNAME
    az aks create \
    --resource-group myResourceGroup \
    --name myAKSCluster \
    --node-count 2 \
    --generate-ssh-keys \
    --windows-admin-username $WINDOWS_USERNAME \
    --vm-set-type VirtualMachineScaleSets \
    --network-plugin azure

    To add a Windows node pool for Windows containers:

    az aks nodepool add \
    --resource-group myResourceGroup \
    --cluster-name myAKSCluster \
    --os-type Windows \
    --name npwin \
    --node-count 1

    Deploy a sample ASP.Net application to the AKS cluster above using the YAML file below:

    apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: sample
    labels:
    app: sample
    spec:
    replicas: 1
    template:
    metadata:
    name: sample
    labels:
    app: sample
    spec:
    nodeSelector:
    "kubernetes.io/os": windows
    containers:
    - name: sample
    image: mcr.microsoft.com/dotnet/framework/samples:aspnetapp
    resources:
    limits:
    cpu: 1
    memory: 800M
    ports:
    - containerPort: 80
    selector:
    matchLabels:
    app: sample
    ---
    apiVersion: v1
    kind: Service
    metadata:
    name: sample
    spec:
    type: LoadBalancer
    ports:
    - protocol: TCP
    port: 80
    selector:
    app: sample

    Save the file above and run the command below on your Kubernetes cluster:

    kubectl apply -f <filename> .

    Once deployed, you can access the application by getting the IP address of your service:

    kubectl get service

    Resources

    It's not too late to sign up for and complete the Cloud Skills Challenge!
    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/azure-kubernetes-service/page/2/index.html b/cnny-2023/tags/azure-kubernetes-service/page/2/index.html index 3aba3defda..016e8ac914 100644 --- a/cnny-2023/tags/azure-kubernetes-service/page/2/index.html +++ b/cnny-2023/tags/azure-kubernetes-service/page/2/index.html @@ -14,14 +14,14 @@ - - + +

    21 posts tagged with "azure-kubernetes-service"

    View All Tags

    · 5 min read
    Cory Skimming

    Welcome to Week 1 of #CloudNativeNewYear!

    Cloud-native New Year

    You will often hear the term "cloud-native" when discussing modern application development, but even a quick online search will return a huge number of articles, tweets, and web pages with a variety of definitions. So, what does cloud-native actually mean? Also, what makes an application a cloud-native application versus a "regular" application?

    Today, we will address these questions and more as we kickstart our learning journey (and our new year!) with an introductory dive into the wonderful world of cloud-native.


    What We'll Cover

    • What is cloud-native?
    • What is a cloud-native application?
    • The benefits of cloud-native
    • The five pillars of cloud-native
    • Exercise: Take the Cloud Skills Challenge!

    1. What is cloud-native?

    The term "cloud-native" can seem pretty self-evident (yes, hello, native to the cloud?), and in a way, it is. While there are lots of definitions of cloud-native floating around, at it's core, cloud-native simply refers to a modern approach to building software that takes advantage of cloud services and environments. This includes using cloud-native technologies, such as containers, microservices, and serverless, and following best practices for deploying, scaling, and managing applications in a cloud environment.

    Official definition from the Cloud Native Computing Foundation:

    Cloud-native technologies empower organizations to build and run scalable applications in modern, dynamic environments such as public, private, and hybrid clouds. Containers, service meshes, microservices, immutable infrastructure, and declarative APIs exemplify this approach.

    These techniques enable loosely coupled systems that are resilient, manageable, and observable. Combined with robust automation, they allow engineers to make high-impact changes frequently and predictably with minimal toil. Source


    2. So, what exactly is a cloud-native application?

    Cloud-native applications are specifically designed to take advantage of the scalability, resiliency, and distributed nature of modern cloud infrastructure. But how does this differ from a "traditional" application?

    Traditional applications are generally been built, tested, and deployed as a single, monolithic unit. The monolithic nature of this type of architecture creates close dependencies between components. This complexity and interweaving only increases as an application grows and can make it difficult to evolve (not to mention troubleshoot) and challenging to operate over time.

    To contrast, in cloud-native architectures the application components are decomposed into loosely coupled services, rather than built and deployed as one block of code. This decomposition into multiple self-contained services enables teams to manage complexity and improve the speed, agility, and scale of software delivery. Many small parts enables teams to make targeted updates, deliver new features, and fix any issues without leading to broader service disruption.


    3. The benefits of cloud-native

    Cloud-native architectures can bring many benefits to an organization, including:

    1. Scalability: easily scale up or down based on demand, allowing organizations to adjust their resource usage and costs as needed.
    2. Flexibility: deploy and run on any cloud platform, and easily move between clouds and on-premises environments.
    3. High-availability: techniques such as redundancy, self-healing, and automatic failover help ensure that cloud-native applications are designed to be highly-available and fault tolerant.
    4. Reduced costs: take advantage of the pay-as-you-go model of cloud computing, reducing the need for expensive infrastructure investments.
    5. Improved security: tap in to cloud security features, such as encryption and identity management, to improve the security of the application.
    6. Increased agility: easily add new features or services to your applications to meet changing business needs and market demand.

    4. The pillars of cloud-native

    There are five areas that are generally cited as the core building blocks of cloud-native architecture:

    1. Microservices: Breaking down monolithic applications into smaller, independent, and loosely-coupled services that can be developed, deployed, and scaled independently.
    2. Containers: Packaging software in lightweight, portable, and self-sufficient containers that can run consistently across different environments.
    3. Automation: Using automation tools and DevOps processes to manage and operate the cloud-native infrastructure and applications, including deployment, scaling, monitoring, and self-healing.
    4. Service discovery: Using service discovery mechanisms, such as APIs & service meshes, to enable services to discover and communicate with each other.
    5. Observability: Collecting and analyzing data from the infrastructure and applications to understand and optimize the performance, behavior, and health of the system.

    These can (and should!) be used in combination to deliver cloud-native solutions that are highly scalable, flexible, and available.

    WHAT'S NEXT

    Stay tuned, as we will be diving deeper into these topics in the coming weeks:

    • Jan 24: Containers 101
    • Jan 25: Adopting Microservices with Kubernetes
    • Jan 26: Kubernetes 101
    • Jan 27: Exploring your Cloud-native Options

    Resources


    Don't forget to subscribe to the blog to get daily posts delivered directly to your favorite feed reader!


    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/azure-kubernetes-service/page/20/index.html b/cnny-2023/tags/azure-kubernetes-service/page/20/index.html index 402b8217d9..e5b68d34fd 100644 --- a/cnny-2023/tags/azure-kubernetes-service/page/20/index.html +++ b/cnny-2023/tags/azure-kubernetes-service/page/20/index.html @@ -14,13 +14,13 @@ - - + +

    21 posts tagged with "azure-kubernetes-service"

    View All Tags

    · 4 min read
    Jorge Arteiro

    Welcome to Day 4 of Week 4 of #CloudNativeNewYear!

    The theme for this week is going further with Cloud Native. Yesterday we talked about Windows Containers. Today we'll explore addons and extensions available to Azure Kubernetes Services (AKS).

    What We'll Cover

    • Introduction
    • Add-ons
    • Extensions
    • Add-ons vs Extensions
    • Resources

    Introduction

    Azure Kubernetes Service (AKS) is a fully managed container orchestration service that makes it easy to deploy and manage containerized applications on Azure. AKS offers a number of features and capabilities, including the ability to extend its supported functionality through the use of add-ons and extensions.

    There are also integrations available from open-source projects and third parties, but they are not covered by the AKS support policy.

    Add-ons

    Add-ons provide a supported way to extend AKS. Installation, configuration and lifecycle are managed by AKS following pre-determine updates rules.

    As an example, let's enable Container Insights with the monitoring addon. on an existing AKS cluster using az aks enable-addons --addons CLI command

    az aks enable-addons \
    --name MyManagedCluster \
    --resource-group MyResourceGroup \
    --addons monitoring

    or you can use az aks create --enable-addons when creating new clusters

    az aks create \
    --name MyManagedCluster \
    --resource-group MyResourceGroup \
    --enable-addons monitoring

    The current available add-ons are:

    1. http_application_routing - Configure ingress with automatic public DNS name creation. Only recommended for development.
    2. monitoring - Container Insights monitoring.
    3. virtual-node - CNCF virtual nodes open source project.
    4. azure-policy - Azure Policy for AKS.
    5. ingress-appgw - Application Gateway Ingress Controller (AGIC).
    6. open-service-mesh - CNCF Open Service Mesh project.
    7. azure-keyvault-secrets-provider - Azure Key Vault Secrets Provider for Secret Store CSI Driver.
    8. web_application_routing - Managed NGINX ingress Controller.
    9. keda - CNCF Event-driven autoscaling project.

    For more details, get the updated list of AKS Add-ons here

    Extensions

    Cluster Extensions uses Helm charts and integrates with Azure Resource Manager (ARM) to provide installation and lifecycle management of capabilities on top of AKS.

    Extensions can be auto upgraded using minor versions, but it requires extra management and configuration. Using Scope parameter, it can be installed on the whole cluster or per namespace.

    AKS Extensions requires an Azure CLI extension to be installed. To add or update this CLI extension use the following commands:

    az extension add --name k8s-extension

    and to update an existing extension

    az extension update --name k8s-extension

    There are only 3 available extensions:

    1. Dapr - CNCF Dapr project.
    2. Azure ML - Integrate Azure Machine Learning with AKS to train, inference and manage ML models.
    3. Flux (GitOps) - CNCF Flux project integrated with AKS to enable cluster configuration and application deployment using GitOps.

    As an example, you can install Azure ML using the following command:

    az k8s-extension create \
    --name aml-compute --extension-type Microsoft.AzureML.Kubernetes \
    --scope cluster --cluster-name <clusterName> \
    --resource-group <resourceGroupName> \
    --cluster-type managedClusters \
    --configuration-settings enableInference=True allowInsecureConnections=True

    For more details, get the updated list of AKS Extensions here

    Add-ons vs Extensions

    AKS Add-ons brings an advantage of been fully managed by AKS itself, and AKS Extensions are more flexible and configurable but requires extra level of management.

    Add-ons are part of the AKS resource provider in the Azure API, and AKS Extensions are a separate resource provider on the Azure API.

    Resources

    It's not too late to sign up for and complete the Cloud Skills Challenge!
    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/azure-kubernetes-service/page/21/index.html b/cnny-2023/tags/azure-kubernetes-service/page/21/index.html index ca2f34fc45..ab90b9cda0 100644 --- a/cnny-2023/tags/azure-kubernetes-service/page/21/index.html +++ b/cnny-2023/tags/azure-kubernetes-service/page/21/index.html @@ -14,13 +14,13 @@ - - + +

    21 posts tagged with "azure-kubernetes-service"

    View All Tags

    · 6 min read
    Cory Skimming
    Steven Murawski
    Paul Yu
    Josh Duffney
    Nitya Narasimhan
    Vinicius Apolinario
    Jorge Arteiro
    Devanshi Joshi

    And that's a wrap on the inaugural #CloudNativeNewYear! Thank you for joining us to kick off the new year with this learning journey into cloud-native! In this final post of the 2023 celebration of all things cloud-native, we'll do two things:

    • Look Back - with a quick retrospective of what was covered.
    • Look Ahead - with resources and suggestions for how you can continue your skilling journey!

    We appreciate your time and attention and we hope you found this curated learning valuable. Feedback and suggestions are always welcome. From our entire team, we wish you good luck with the learning journey - now go build some apps and share your knowledge! 🎉


    What We'll Cover

    • Cloud-native fundamentals
    • Kubernetes fundamentals
    • Bringing your applications to Kubernetes
    • Go further with cloud-native
    • Resources to keep the celebration going!

    Week 1: Cloud-native Fundamentals

    In Week 1, we took a tour through the fundamentals of cloud-native technologies, including a walkthrough of the core concepts of containers, microservices, and Kubernetes.

    • Jan 23 - Cloud-native Fundamentals: The answers to life and all the universe - what is cloud-native? What makes an application cloud-native? What are the benefits? (yes, we all know it's 42, but hey, gotta start somewhere!)
    • Jan 24 - Containers 101: Containers are an essential component of cloud-native development. In this intro post, we cover how containers work and why they have become so popular.
    • Jan 25 - Kubernetes 101: Kuber-what-now? Learn the basics of Kubernetes and how it enables us to deploy and manage our applications effectively and consistently.
    A QUICKSTART GUIDE TO KUBERNETES CONCEPTS

    Missed it Live? Tune in to A Quickstart Guide to Kubernetes Concepts on demand, now!

    • Jan 26 - Microservices 101: What is a microservices architecture and how can we go about designing one?
    • Jan 27 - Exploring your Cloud Native Options: Cloud-native, while catchy, can be a very broad term. What technologies should you use? Learn some basic guidelines for when it is optimal to use different technologies for your project.

    Week 2: Kubernetes Fundamentals

    In Week 2, we took a deeper dive into the Fundamentals of Kubernetes. The posts and live demo from this week took us through how to build a simple application on Kubernetes, covering everything from deployment to networking and scaling. Note: for our samples and demo we have used Azure Kubernetes Service, but the principles apply to any Kubernetes!

    • Jan 30 - Pods and Deployments: how to use pods and deployments in Kubernetes.
    • Jan 31 - Services and Ingress: how to use services and ingress and a walk through the steps of making our containers accessible internally and externally!
    • Feb 1 - ConfigMaps and Secrets: how to of passing configuration and secrets to our applications in Kubernetes with ConfigMaps and Secrets.
    • Feb 2 - Volumes, Mounts, and Claims: how to use persistent storage on Kubernetes (and ensure your data can survive container restarts!).
    • Feb 3 - Scaling Pods and Nodes: how to scale pods and nodes in our Kubernetes cluster.
    ASK THE EXPERTS: AZURE KUBERNETES SERVICE

    Missed it Live? Tune in to Ask the Expert with Azure Kubernetes Service on demand, now!


    Week 3: Bringing your applications to Kubernetes

    So, you have learned how to build an application on Kubernetes. What about your existing applications? In Week 3, we explored how to take an existing application and set it up to run in Kubernetes:

    • Feb 6 - CI/CD: learn how to get an existing application running in Kubernetes with a full pipeline in GitHub Actions.
    • Feb 7 - Adapting Storage, Secrets, and Configuration: how to evaluate our sample application's configuration, storage, and networking requirements and implement using Kubernetes.
    • Feb 8 - Opening your Application with Ingress: how to expose the eShopOnWeb app so that customers can reach it over the internet using a custom domain name and TLS.
    • Feb 9 - Debugging and Instrumentation: how to debug and instrument your application now that it is on Kubernetes.
    • Feb 10 - CI/CD Secure Supply Chain: now that we have set up our application on Kubernetes, let's talk about container image signing and how to set up a secure supply change.

    Week 4: Go Further with Cloud-Native

    This week we have gone further with Cloud-native by exploring advanced topics and best practices for the Cloud-native practitioner.

    And today, February 17th, with this one post to rule (er, collect) them all!


    Keep the Learning Going!

    Learning is great, so why stop here? We have a host of great resources and samples for you to continue your cloud-native journey with Azure below:


    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/azure-kubernetes-service/page/3/index.html b/cnny-2023/tags/azure-kubernetes-service/page/3/index.html index 92e5afb970..b4d5dc1768 100644 --- a/cnny-2023/tags/azure-kubernetes-service/page/3/index.html +++ b/cnny-2023/tags/azure-kubernetes-service/page/3/index.html @@ -14,14 +14,14 @@ - - + +

    21 posts tagged with "azure-kubernetes-service"

    View All Tags

    · 4 min read
    Steven Murawski
    Paul Yu
    Josh Duffney

    Welcome to Day 2 of Week 1 of #CloudNativeNewYear!

    Today, we'll focus on building an understanding of containers.

    What We'll Cover

    • Introduction
    • How do Containers Work?
    • Why are Containers Becoming so Popular?
    • Conclusion
    • Resources
    • Learning Path

    REGISTER & LEARN: KUBERNETES 101

    Interested in a dive into Kubernetes and a chance to talk to experts?

    🎙: Join us Jan 26 @1pm PST by registering here

    Here's what you will learn:

    • Key concepts and core principles of Kubernetes.
    • How to deploy, scale and manage containerized workloads.
    • Live Demo of the concepts explained
    • How to get started with Azure Kubernetes Service for free.

    Start your free Azure Kubernetes Trial Today!!: aka.ms/TryAKS

    Introduction

    In the beginning, we deployed our applications onto physical servers. We only had a certain number of those servers, so often they hosted multiple applications. This led to some problems when those applications shared dependencies. Upgrading one application could break another application on the same server.

    Enter virtualization. Virtualization allowed us to run our applications in an isolated operating system instance. This removed much of the risk of updating shared dependencies. However, it increased our overhead since we had to run a full operating system for each application environment.

    To address the challenges created by virtualization, containerization was created to improve isolation without duplicating kernel level resources. Containers provide efficient and consistent deployment and runtime experiences for our applications and have become very popular as a way of packaging and distributing applications.

    How do Containers Work?

    Containers build on two capabilities in the Linux operating system, namespaces and cgroups. These constructs allow the operating system to provide isolation to a process or group of processes, keeping their access to filesystem resources separate and providing controls on resource utilization. This, combined with tooling to help package, deploy, and run container images has led to their popularity in today’s operating environment. This provides us our isolation without the overhead of additional operating system resources.

    When a container host is deployed on an operating system, it works at scheduling the access to the OS (operating systems) components. This is done by providing a logical isolated group that can contain processes for a given application, called a namespace. The container host then manages /schedules access from the namespace to the host OS. The container host then uses cgroups to allocate compute resources. Together, the container host with the help of cgroups and namespaces can schedule multiple applications to access host OS resources.

    Overall, this gives the illusion of virtualizing the host OS, where each application gets its own OS. In actuality, all the applications are running on the same operating system and sharing the same kernel as the container host.

    Containers are popular in the software development industry because they provide several benefits over traditional virtualization methods. Some of these benefits include:

    • Portability: Containers make it easy to move an application from one environment to another without having to worry about compatibility issues or missing dependencies.
    • Isolation: Containers provide a level of isolation between the application and the host system, which means that the application running in the container cannot access the host system's resources.
    • Scalability: Containers make it easy to scale an application up or down as needed, which is useful for applications that experience a lot of traffic or need to handle a lot of data.
    • Resource Efficiency: Containers are more resource-efficient than traditional virtualization methods because they don't require a full operating system to be running on each virtual machine.
    • Cost-Effective: Containers are more cost-effective than traditional virtualization methods because they don't require expensive hardware or licensing fees.

    Conclusion

    Containers are a powerful technology that allows developers to package and deploy applications in a portable and isolated environment. This technology is becoming increasingly popular in the world of software development and is being used by many companies and organizations to improve their application deployment and management processes. With the benefits of portability, isolation, scalability, resource efficiency, and cost-effectiveness, containers are definitely worth considering for your next application development project.

    Containerizing applications is a key step in modernizing them, and there are many other patterns that can be adopted to achieve cloud-native architectures, including using serverless platforms, Kubernetes, and implementing DevOps practices.

    Resources

    Learning Path

    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/azure-kubernetes-service/page/4/index.html b/cnny-2023/tags/azure-kubernetes-service/page/4/index.html index 9eaf45cadc..b6a49e89f1 100644 --- a/cnny-2023/tags/azure-kubernetes-service/page/4/index.html +++ b/cnny-2023/tags/azure-kubernetes-service/page/4/index.html @@ -14,14 +14,14 @@ - - + +

    21 posts tagged with "azure-kubernetes-service"

    View All Tags

    · 3 min read
    Steven Murawski

    Welcome to Day 3 of Week 1 of #CloudNativeNewYear!

    This week we'll focus on what Kubernetes is.

    What We'll Cover

    • Introduction
    • What is Kubernetes? (Video)
    • How does Kubernetes Work? (Video)
    • Conclusion


    REGISTER & LEARN: KUBERNETES 101

    Interested in a dive into Kubernetes and a chance to talk to experts?

    🎙: Join us Jan 26 @1pm PST by registering here

    Here's what you will learn:

    • Key concepts and core principles of Kubernetes.
    • How to deploy, scale and manage containerized workloads.
    • Live Demo of the concepts explained
    • How to get started with Azure Kubernetes Service for free.

    Start your free Azure Kubernetes Trial Today!!: aka.ms/TryAKS

    Introduction

    Kubernetes is an open source container orchestration engine that can help with automated deployment, scaling, and management of our applications.

    Kubernetes takes physical (or virtual) resources and provides a consistent API over them, bringing a consistency to the management and runtime experience for our applications. Kubernetes provides us with a number of capabilities such as:

    • Container scheduling
    • Service discovery and load balancing
    • Storage orchestration
    • Automated rollouts and rollbacks
    • Automatic bin packing
    • Self-healing
    • Secret and configuration management

    We'll learn more about most of these topics as we progress through Cloud Native New Year.

    What is Kubernetes?

    Let's hear from Brendan Burns, one of the founders of Kubernetes as to what Kubernetes actually is.

    How does Kubernetes Work?

    And Brendan shares a bit more with us about how Kubernetes works.

    Conclusion

    Kubernetes allows us to deploy and manage our applications effectively and consistently.

    By providing a consistent API across many of the concerns our applications have, like load balancing, networking, storage, and compute, Kubernetes improves both our ability to build and ship new software.

    There are standards for the applications to depend on for resources needed. Deployments, metrics, and logs are provided in a standardized fashion allowing more effecient operations across our application environments.

    And since Kubernetes is an open source platform, it can be found in just about every type of operating environment - cloud, virtual machines, physical hardware, shared data centers, even small devices like Rasberry Pi's!

    Want to learn more? Join us for a webinar on Kubernetes Concepts (or catch the playback) on Thursday, January 26th at 1 PM PST and watch for the rest of this series right here!

    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/azure-kubernetes-service/page/5/index.html b/cnny-2023/tags/azure-kubernetes-service/page/5/index.html index 9fb3811a1f..4726a0c3c1 100644 --- a/cnny-2023/tags/azure-kubernetes-service/page/5/index.html +++ b/cnny-2023/tags/azure-kubernetes-service/page/5/index.html @@ -14,13 +14,13 @@ - - + +

    21 posts tagged with "azure-kubernetes-service"

    View All Tags

    · 6 min read
    Josh Duffney

    Welcome to Day 4 of Week 1 of #CloudNativeNewYear!

    This week we'll focus on advanced topics and best practices for Cloud-Native practitioners, kicking off with this post on Serverless Container Options with Azure. We'll look at technologies, tools and best practices that range from managed services like Azure Kubernetes Service, to options allowing finer granularity of control and oversight.

    What We'll Cover

    • What is Microservice Architecture?
    • How do you design a Microservice?
    • What challenges do Microservices introduce?
    • Conclusion
    • Resources


    Microservices are a modern way of designing and building software that increases deployment velocity by decomposing an application into small autonomous services that can be deployed independently.

    By deploying loosely coupled microservices your applications can be developed, deployed, and scaled independently. Because each service is independent, it can be updated or replaced without having to worry about the impact on the rest of the application. This means that if a bug is found in one service, it can be fixed without having to redeploy the entire application. All of which gives an organization the ability to deliver value to their customers faster.

    In this article, we will explore the basics of microservices architecture, its benefits and challenges, and how it can help improve the development, deployment, and maintenance of software applications.

    What is Microservice Architecture?

    Before explaining what Microservice architecture is, it’s important to understand what problems microservices aim to address.

    Traditional software development is centered around building monolithic applications. Monolithic applications are built as a single, large codebase. Meaning your code is tightly coupled causing the monolithic app to suffer from the following:

    Too much Complexity: Monolithic applications can become complex and difficult to understand and maintain as they grow. This can make it hard to identify and fix bugs and add new features.

    Difficult to Scale: Monolithic applications can be difficult to scale as they often have a single point of failure, which can cause the whole application to crash if a service fails.

    Slow Deployment: Deploying a monolithic application can be risky and time-consuming, as a small change in one part of the codebase can affect the entire application.

    Microservice architecture (often called microservices) is an architecture style that addresses the challenges created by Monolithic applications. Microservices architecture is a way of designing and building software applications as a collection of small, independent services that communicate with each other through APIs. This allows for faster development and deployment cycles, as well as easier scaling and maintenance than is possible with a monolithic application.

    How do you design a Microservice?

    Building applications with Microservices architecture requires a different approach. Microservices architecture focuses on business capabilities rather than technical layers, such as data access or messaging. Doing so requires that you shift your focus away from the technical stack and model your applications based upon the various domains that exist within the business.

    Domain-driven design (DDD) is a way to design software by focusing on the business needs. You can use Domain-driven design as a framework that guides the development of well-designed microservices by building services that encapsulate knowledge in each domain and abstract that knowledge from clients.

    In Domain-driven design you start by modeling the business domain and creating a domain model. A domain model is an abstract model of the business model that distills and organizes a domain of knowledge and provides a common language for developers and domain experts. It’s the resulting domain model that microservices a best suited to be built around because it helps establish a well-defined boundary between external systems and other internal applications.

    In short, before you begin designing microservices, start by mapping the functions of the business and their connections to create a domain model for the microservice(s) to be built around.

    What challenges do Microservices introduce?

    Microservices solve a lot of problems and have several advantages, but the grass isn’t always greener on the other side.

    One of the key challenges of microservices is managing communication between services. Because services are independent, they need to communicate with each other through APIs. This can be complex and difficult to manage, especially as the number of services grows. To address this challenge, it is important to have a clear API design, with well-defined inputs and outputs for each service. It is also important to have a system for managing and monitoring communication between services, to ensure that everything is running smoothly.

    Another challenge of microservices is managing the deployment and scaling of services. Because each service is independent, it needs to be deployed and scaled separately from the rest of the application. This can be complex and difficult to manage, especially as the number of services grows. To address this challenge, it is important to have a clear and consistent deployment process, with well-defined steps for deploying and scaling each service. Furthermore, it is advisable to host them on a system with self-healing capabilities to reduce operational burden.

    It is also important to have a system for monitoring and managing the deployment and scaling of services, to ensure optimal performance.

    Each of these challenges has created fertile ground for tooling and process that exists in the cloud-native ecosystem. Kubernetes, CI CD, and other DevOps practices are part of the package of adopting the microservices architecture.

    Conclusion

    In summary, microservices architecture focuses on software applications as a collection of small, independent services that communicate with each other over well-defined APIs.

    The main advantages of microservices include:

    • increased flexibility and scalability per microservice,
    • efficient resource utilization (with help from a container orchestrator like Kubernetes),
    • and faster development cycles.

    Continue following along with this series to see how you can use Kubernetes to help adopt microservices patterns in your own environments!

    Resources

    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/azure-kubernetes-service/page/6/index.html b/cnny-2023/tags/azure-kubernetes-service/page/6/index.html index 51841ee23d..64e36cb86e 100644 --- a/cnny-2023/tags/azure-kubernetes-service/page/6/index.html +++ b/cnny-2023/tags/azure-kubernetes-service/page/6/index.html @@ -14,14 +14,14 @@ - - + +

    21 posts tagged with "azure-kubernetes-service"

    View All Tags

    · 6 min read
    Cory Skimming

    We are excited to be wrapping up our first week of #CloudNativeNewYear! This week, we have tried to set the stage by covering the fundamentals of cloud-native practices and technologies, including primers on containerization, microservices, and Kubernetes.

    Don't forget to sign up for the the Cloud Skills Challenge!

    Today, we will do a brief recap of some of these technologies and provide some basic guidelines for when it is optimal to use each.


    What We'll Cover

    • To Containerize or not to Containerize?
    • The power of Kubernetes
    • Where does Serverless fit?
    • Resources
    • What's coming next!


    Just joining us now? Check out these other Week 1 posts:

    To Containerize or not to Containerize?

    As mentioned in our Containers 101 post earlier this week, containers can provide several benefits over traditional virtualization methods, which has made them popular within the software development community. Containers provide a consistent and predictable runtime environment, which can help reduce the risk of compatibility issues and simplify the deployment process. Additionally, containers can improve resource efficiency by allowing multiple applications to run on the same host while isolating their dependencies.

    Some types of apps that are a particularly good fit for containerization include:

    1. Microservices: Containers are particularly well-suited for microservices-based applications, as they can be used to isolate and deploy individual components of the system. This allows for more flexibility and scalability in the deployment process.
    2. Stateless applications: Applications that do not maintain state across multiple sessions, such as web applications, are well-suited for containers. Containers can be easily scaled up or down as needed and replaced with new instances, without losing data.
    3. Portable applications: Applications that need to be deployed in different environments, such as on-premises, in the cloud, or on edge devices, can benefit from containerization. The consistent and portable runtime environment of containers can make it easier to move the application between different environments.
    4. Legacy applications: Applications that are built using older technologies or that have compatibility issues can be containerized to run in an isolated environment, without impacting other applications or the host system.
    5. Dev and testing environments: Containerization can be used to create isolated development and testing environments, which can be easily created and destroyed as needed.

    While there are many types of applications that can benefit from a containerized approach, it's worth noting that containerization is not always the best option, and it's important to weigh the benefits and trade-offs before deciding to containerize an application. Additionally, some types of applications may not be a good fit for containers including:

    • Apps that require full access to host resources: Containers are isolated from the host system, so if an application needs direct access to hardware resources such as GPUs or specialized devices, it might not work well in a containerized environment.
    • Apps that require low-level system access: If an application requires deep access to the underlying operating system, it may not be suitable for running in a container.
    • Applications that have specific OS dependencies: Apps that have specific dependencies on a certain version of an operating system or libraries may not be able to run in a container.
    • Stateful applications: Apps that maintain state across multiple sessions, such as databases, may not be well suited for containers. Containers are ephemeral by design, so the data stored inside a container may not persist between restarts.

    The good news is that some of these limitations can be overcome with the use of specialized containerization technologies such as Kubernetes, and by carefully designing the architecture of the application.


    The power of Kubernetes

    Speaking of Kubernetes...

    Kubernetes is a powerful tool for managing and deploying containerized applications in production environments, particularly for applications that need to scale, handle large numbers of requests, or run in multi-cloud or hybrid environments.

    Kubernetes is well-suited for a wide variety of applications, but it is particularly well-suited for the following types of applications:

    1. Microservices-based applications: Kubernetes provides a powerful set of tools for managing and deploying microservices-based applications, making it easy to scale, update, and manage the individual components of the application.
    2. Stateful applications: Kubernetes provides support for stateful applications through the use of Persistent Volumes and StatefulSets, allowing for applications that need to maintain state across multiple instances.
    3. Large-scale, highly-available systems: Kubernetes provides built-in support for scaling, self-healing, and rolling updates, making it an ideal choice for large-scale, highly-available systems that need to handle large numbers of users and requests.
    4. Multi-cloud and hybrid environments: Kubernetes can be used to deploy and manage applications across multiple cloud providers and on-premises environments, making it a good choice for organizations that want to take advantage of the benefits of multiple cloud providers or that need to deploy applications in a hybrid environment.
    New to Kubernetes?

    Where does Serverless fit in?

    Serverless is a cloud computing model where the cloud provider (like Azure) is responsible for executing a piece of code by dynamically allocating the resources. With serverless, you only pay for the exact amount of compute time that you use, rather than paying for a fixed amount of resources. This can lead to significant cost savings, particularly for applications with variable or unpredictable workloads.

    Serverless is commonly used for building applications like web or mobile apps, IoT, data processing, and real-time streaming - apps where the workloads are variable and high scalability is required. It's important to note that serverless is not a replacement for all types of workloads - it's best suited for stateless, short-lived and small-scale workloads.

    For a detailed look into the world of Serverless and lots of great learning content, revisit #30DaysofServerless.


    Resources


    What's up next in #CloudNativeNewYear?

    Week 1 has been all about the fundamentals of cloud-native. Next week, the team will be diving in to application deployment with Azure Kubernetes Service. Don't forget to subscribe to the blog to get daily posts delivered directly to your favorite feed reader!


    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/azure-kubernetes-service/page/7/index.html b/cnny-2023/tags/azure-kubernetes-service/page/7/index.html index 552854d8cf..8299cf87c1 100644 --- a/cnny-2023/tags/azure-kubernetes-service/page/7/index.html +++ b/cnny-2023/tags/azure-kubernetes-service/page/7/index.html @@ -14,13 +14,13 @@ - - + +

    21 posts tagged with "azure-kubernetes-service"

    View All Tags

    · 14 min read
    Steven Murawski

    Welcome to Day #1 of Week 2 of #CloudNativeNewYear!

    The theme for this week is Kubernetes fundamentals. Last week we talked about Cloud Native architectures and the Cloud Native landscape. Today we'll explore the topic of Pods and Deployments in Kubernetes.

    Ask the Experts Thursday, February 9th at 9 AM PST
    Catch the Replay of the Live Demo

    Watch the recorded demo and conversation about this week's topics.

    We were live on YouTube walking through today's (and the rest of this week's) demos.

    What We'll Cover

    • Setting Up A Kubernetes Environment in Azure
    • Running Containers in Kubernetes Pods
    • Making the Pods Resilient with Deployments
    • Exercise
    • Resources

    Setting Up A Kubernetes Environment in Azure

    For this week, we'll be working with a simple app - the Azure Voting App. My teammate Paul Yu ported the app to Rust and we tweaked it a bit to let us highlight some of the basic features of Kubernetes.

    You should be able to replicate this in just about any Kubernetes environment, but we'll use Azure Kubernetes Service (AKS) as our working environment for this week.

    To make it easier to get started, there's a Bicep template to deploy an AKS cluster, an Azure Container Registry (ACR) (to host our container image), and connect the two so that we can easily deploy our application.

    Step 0 - Prerequisites

    There are a few things you'll need if you want to work through this and the following examples this week.

    Required:

    • Git (and probably a GitHub account if you want to persist your work outside of your computer)
    • Azure CLI
    • An Azure subscription (if you want to follow along with the Azure steps)
    • Kubectl (the command line tool for managing Kubernetes)

    Helpful:

    • Visual Studio Code (or equivalent editor)

    Step 1 - Clone the application repository

    First, I forked the source repository to my account.

    $GitHubOrg = 'smurawski' # Replace this with your GitHub account name or org name
    git clone "https://github.com/$GitHubOrg/azure-voting-app-rust"
    cd azure-voting-app-rust

    Leave your shell opened with your current location inside the application repository.

    Step 2 - Set up AKS

    Running the template deployment from the demo script (I'm using the PowerShell example in cnny23-week2-day1.ps1, but there's a Bash variant at cnny23-week2-day1.sh) stands up the environment. The second, third, and fourth commands take some of the output from the Bicep deployment to set up for later commands, so don't close out your shell after you run these commands.

    az deployment sub create --template-file ./deploy/main.bicep --location eastus --parameters 'resourceGroup=cnny-week2'
    $AcrName = az deployment sub show --name main --query 'properties.outputs.acr_name.value' -o tsv
    $AksName = az deployment sub show --name main --query 'properties.outputs.aks_name.value' -o tsv
    $ResourceGroup = az deployment sub show --name main --query 'properties.outputs.resource_group_name.value' -o tsv

    az aks get-credentials --resource-group $ResourceGroup --name $AksName

    Step 3 - Build our application container

    Since we have an Azure Container Registry set up, I'll use ACR Build Tasks to build and store my container image.

    az acr build --registry $AcrName --% --image cnny2023/azure-voting-app-rust:{{.Run.ID}} .
    $BuildTag = az acr repository show-tags `
    --name $AcrName `
    --repository cnny2023/azure-voting-app-rust `
    --orderby time_desc `
    --query '[0]' -o tsv
    tip

    Wondering what the --% is in the first command line? That tells the PowerShell interpreter to pass the input after it "as is" to the command without parsing/evaluating it. Otherwise, PowerShell messes a bit with the templated {{.Run.ID}} bit.

    Running Containers in Kubernetes Pods

    Now that we have our AKS cluster and application image ready to go, let's look into how Kubernetes runs containers.

    If you've been in tech for any length of time, you've seen that every framework, runtime, orchestrator, etc.. can have their own naming scheme for their concepts. So let's get into some of what Kubernetes calls things.

    The Pod

    A container running in Kubernetes is called a Pod. A Pod is basically a running container on a Node or VM. It can be more. For example you can run multiple containers and specify some funky configuration, but we'll keep it simple for now - add the complexity when you need it.

    Our Pod definition can be created via the kubectl command imperatively from arguments or declaratively from a configuration file. We'll do a little of both. We'll use the kubectl command to help us write our configuration files. Kubernetes configuration files are YAML, so having an editor that supports and can help you syntax check YAML is really helpful.

    Creating a Pod Definition

    Let's create a few Pod definitions. Our application requires two containers to get working - the application and a database.

    Let's create the database Pod first. And before you comment, the configuration isn't secure nor best practice. We'll fix that later this week. For now, let's focus on getting up and running.

    This is a trick I learned from one of my teammates - Paul. By using the --output yaml and --dry-run=client options, we can have the command help us write our YAML. And with a bit of output redirection, we can stash it safely in a file for later use.

    kubectl run azure-voting-db `
    --image "postgres:15.0-alpine" `
    --env "POSTGRES_PASSWORD=mypassword" `
    --output yaml `
    --dry-run=client > manifests/pod-db.yaml

    This creates a file that looks like:

    apiVersion: v1
    kind: Pod
    metadata:
    creationTimestamp: null
    labels:
    run: azure-voting-db
    name: azure-voting-db
    spec:
    containers:
    - env:
    - name: POSTGRES_PASSWORD
    value: mypassword
    image: postgres:15.0-alpine
    name: azure-voting-db
    resources: {}
    dnsPolicy: ClusterFirst
    restartPolicy: Always
    status: {}

    The file, when supplied to the Kubernetes API, will identify what kind of resource to create, the API version to use, and the details of the container (as well as an environment variable to be supplied).

    We'll get that container image started with the kubectl command. Because the details of what to create are in the file, we don't need to specify much else to the kubectl command but the path to the file.

    kubectl apply -f ./manifests/pod-db.yaml

    I'm going to need the IP address of the Pod, so that my application can connect to it, so we can use kubectl to get some information about our pod. By default, kubectl get pod only displays certain information but it retrieves a lot more. We can use the JSONPath syntax to index into the response and get the information you want.

    tip

    To see what you can get, I usually run the kubectl command with the output type (-o JSON) of JSON and then I can find where the data I want is and create my JSONPath query to get it.

    $DB_IP = kubectl get pod azure-voting-db -o jsonpath='{.status.podIP}'

    Now, let's create our Pod definition for our application. We'll use the same technique as before.

    kubectl run azure-voting-app `
    --image "$AcrName.azurecr.io/cnny2023/azure-voting-app-rust:$BuildTag" `
    --env "DATABASE_SERVER=$DB_IP" `
    --env "DATABASE_PASSWORD=mypassword`
    --output yaml `
    --dry-run=client > manifests/pod-app.yaml

    That command gets us a similar YAML file to the database container - you can see the full file here

    Let's get our application container running.

    kubectl apply -f ./manifests/pod-app.yaml

    Now that the Application is Running

    We can check the status of our Pods with:

    kubectl get pods

    And we should see something like:

    azure-voting-app-rust ❯  kubectl get pods
    NAME READY STATUS RESTARTS AGE
    azure-voting-app 1/1 Running 0 36s
    azure-voting-db 1/1 Running 0 84s

    Once our pod is running, we can check to make sure everything is working by letting kubectl proxy network connections to our Pod running the application. If we get the voting web page, we'll know the application found the database and we can start voting!

    kubectl port-forward pod/azure-voting-app 8080:8080

    Azure voting website in a browser with three buttons, one for Dogs, one for Cats, and one for Reset.  The counter is Dogs - 0 and Cats - 0.

    When you are done voting, you can stop the port forwarding by using Control-C to break the command.

    Clean Up

    Let's clean up after ourselves and see if we can't get Kubernetes to help us keep our application running. We can use the same configuration files to ensure that Kubernetes only removes what we want removed.

    kubectl delete -f ./manifests/pod-app.yaml
    kubectl delete -f ./manifests/pod-db.yaml

    Summary - Pods

    A Pod is the most basic unit of work inside Kubernetes. Once the Pod is deleted, it's gone. That leads us to our next topic (and final topic for today.)

    Making the Pods Resilient with Deployments

    We've seen how easy it is to deploy a Pod and get our containers running on Nodes in our Kubernetes cluster. But there's a problem with that. Let's illustrate it.

    Breaking Stuff

    Setting Back Up

    First, let's redeploy our application environment. We'll start with our application container.

    kubectl apply -f ./manifests/pod-db.yaml
    kubectl get pod azure-voting-db -o jsonpath='{.status.podIP}'

    The second command will report out the new IP Address for our database container. Let's open ./manifests/pod-app.yaml and update the container IP to our new one.

    - name: DATABASE_SERVER
    value: YOUR_NEW_IP_HERE

    Then we can deploy the application with the information it needs to find its database. We'll also list out our pods to see what is running.

    kubectl apply -f ./manifests/pod-app.yaml
    kubectl get pods

    Feel free to look back and use the port forwarding trick to make sure your app is running if you'd like.

    Knocking It Down

    The first thing we'll try to break is our application pod. Let's delete it.

    kubectl delete pod azure-voting-app

    Then, we'll check our pod's status:

    kubectl get pods

    Which should show something like:

    azure-voting-app-rust ❯  kubectl get pods
    NAME READY STATUS RESTARTS AGE
    azure-voting-db 1/1 Running 0 50s

    We should be able to recreate our application pod deployment with no problem, since it has the current database IP address and nothing else depends on it.

    kubectl apply -f ./manifests/pod-app.yaml

    Again, feel free to do some fun port forwarding and check your site is running.

    Uncomfortable Truths

    Here's where it gets a bit stickier, what if we delete the database container?

    If we delete our database container and recreate it, it'll likely have a new IP address, which would force us to update our application configuration. We'll look at some solutions for these problems in the next three posts this week.

    Because our database problem is a bit tricky, we'll primarily focus on making our application layer more resilient and prepare our database layer for those other techniques over the next few days.

    Let's clean back up and look into making things more resilient.

    kubectl delete -f ./manifests/pod-app.yaml
    kubectl delete -f ./manifests/pod-db.yaml

    The Deployment

    One of the reasons you may want to use Kubernetes is it's ability to orchestrate workloads. Part of that orchestration includes being able to ensure that certain workloads are running (regardless of what Node they might be on).

    We saw that we could delete our application pod and then restart it from the manifest with little problem. It just meant that we had to run a command to restart it. We can use the Deployment in Kubernetes to tell the orchestrator to ensure we have our application pod running.

    The Deployment also can encompass a lot of extra configuration - controlling how many containers of a particular type should be running, how upgrades of container images should proceed, and more.

    Creating the Deployment

    First, we'll create a Deployment for our database. We'll use a technique similar to what we did for the Pod, with just a bit of difference.

    kubectl create deployment azure-voting-db `
    --image "postgres:15.0-alpine" `
    --port 5432 `
    --output yaml `
    --dry-run=client > manifests/deployment-db.yaml

    Unlike our Pod definition creation, we can't pass in environment variable configuration from the command line. We'll have to edit the YAML file to add that.

    So, let's open ./manifests/deployment-db.yaml in our editor and add the following in the spec/containers configuration.

            env:
    - name: POSTGRES_PASSWORD
    value: "mypassword"

    Your file should look like this deployment-db.yaml.

    Once we have our configuration file updated, we can deploy our database container image.

    kubectl apply -f ./manifests/deployment-db.yaml

    For our application, we'll use the same technique.

    kubectl create deployment azure-voting-app `
    --image "$AcrName.azurecr.io/cnny2023/azure-voting-app-rust:$BuildTag" `
    --port 8080 `
    --output yaml `
    --dry-run=client > manifests/deployment-app.yaml

    Next, we'll need to add an environment variable to the generated configuration. We'll also need the new IP address for the database deployment.

    Previously, we named the pod and were able to ask for the IP address with kubectl and a bit of JSONPath. Now, the deployment created the pod for us, so there's a bit of random in the naming. Check out:

    kubectl get pods

    Should return something like:

    azure-voting-app-rust ❯  kubectl get pods
    NAME READY STATUS RESTARTS AGE
    azure-voting-db-686d758fbf-8jnq8 1/1 Running 0 7s

    We can either ask for the IP with the new pod name, or we can use a selector to find our desired pod.

    kubectl get pod --selector app=azure-voting-db -o jsonpath='{.items[0].status.podIP}'

    Now, we can update our application deployment configuration file with:

            env:
    - name: DATABASE_SERVER
    value: YOUR_NEW_IP_HERE
    - name: DATABASE_PASSWORD
    value: mypassword

    Your file should look like this deployment-app.yaml (but with IPs and image names matching your environment).

    After we save those changes, we can deploy our application.

    kubectl apply -f ./manifests/deployment-app.yaml

    Let's test the resilience of our app now. First, we'll delete the pod running our application, then we'll check to make sure Kubernetes restarted our application pod.

    kubectl get pods
    azure-voting-app-rust ❯  kubectl get pods
    NAME READY STATUS RESTARTS AGE
    azure-voting-app-56c9ccc89d-skv7x 1/1 Running 0 71s
    azure-voting-db-686d758fbf-8jnq8 1/1 Running 0 12m
    kubectl delete pod azure-voting-app-56c9ccc89d-skv7x
    kubectl get pods
    azure-voting-app-rust ❯  kubectl delete pod azure-voting-app-56c9ccc89d-skv7x
    >> kubectl get pods
    pod "azure-voting-app-56c9ccc89d-skv7x" deleted
    NAME READY STATUS RESTARTS AGE
    azure-voting-app-56c9ccc89d-2b5mx 1/1 Running 0 2s
    azure-voting-db-686d758fbf-8jnq8 1/1 Running 0 14m
    info

    Your Pods will likely have different identifiers at the end, so adjust your commands to match the names in your environment.

    As you can see, by the time the kubectl get pods command was run, Kubernetes had already spun up a new pod for the application container image. Thanks Kubernetes!

    Clean up

    Since we can't just delete the pods, we have to delete the deployments.

    kubectl delete -f ./manifests/deployment-app.yaml
    kubectl delete -f ./manifests/deployment-db.yaml

    Summary - Deployments

    Deployments allow us to create more durable configuration for the workloads we deploy into Kubernetes. As we dig deeper, we'll discover more capabilities the deployments offer. Check out the Resources below for more.

    Exercise

    If you want to try these steps, head over to the source repository, fork it, clone it locally, and give it a spin!

    You can check your manifests against the manifests in the week2/day1 branch of the source repository.

    Resources

    Take the Cloud Skills Challenge!

    Enroll in the Cloud Skills Challenge!

    Don't miss out on this opportunity to level up your skills and stay ahead of the curve in the world of cloud native.

    Documentation

    Training

    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/azure-kubernetes-service/page/8/index.html b/cnny-2023/tags/azure-kubernetes-service/page/8/index.html index 98b1eca3f5..ff6750eca9 100644 --- a/cnny-2023/tags/azure-kubernetes-service/page/8/index.html +++ b/cnny-2023/tags/azure-kubernetes-service/page/8/index.html @@ -14,13 +14,13 @@ - - + +

    21 posts tagged with "azure-kubernetes-service"

    View All Tags

    · 11 min read
    Paul Yu

    Welcome to Day 2 of Week 2 of #CloudNativeNewYear!

    The theme for this week is #Kubernetes fundamentals. Yesterday we talked about how to deploy a containerized web app workload to Azure Kubernetes Service (AKS). Today we'll explore the topic of services and ingress and walk through the steps of making our containers accessible both internally as well as over the internet so that you can share it with the world 😊

    Ask the Experts Thursday, February 9th at 9 AM PST
    Catch the Replay of the Live Demo

    Watch the recorded demo and conversation about this week's topics.

    We were live on YouTube walking through today's (and the rest of this week's) demos.

    What We'll Cover

    • Exposing Pods via Service
    • Exposing Services via Ingress
    • Takeaways
    • Resources

    Exposing Pods via Service

    There are a few ways to expose your pod in Kubernetes. One way is to take an imperative approach and use the kubectl expose command. This is probably the quickest way to achieve your goal but it isn't the best way. A better way to expose your pod by taking a declarative approach by creating a services manifest file and deploying it using the kubectl apply command.

    Don't worry if you are unsure of how to make this manifest, we'll use kubectl to help generate it.

    First, let's ensure we have the database deployed on our AKS cluster.

    📝 NOTE: If you don't have an AKS cluster deployed, please head over to Azure-Samples/azure-voting-app-rust, clone the repo, and follow the instructions in the README.md to execute the Azure deployment and setup your kubectl context. Check out the first post this week for more on the environment setup.

    kubectl apply -f ./manifests/deployment-db.yaml

    Next, let's deploy the application. If you are following along from yesterday's content, there isn't anything you need to change; however, if you are deploy the app from scratch, you'll need to modify the deployment-app.yaml manifest and update it with your image tag and database pod's IP address.

    kubectl apply -f ./manifests/deployment-app.yaml

    Now, let's expose the database using a service so that we can leverage Kubernetes' built-in service discovery to be able to reference it by name; not pod IP. Run the following command.

    kubectl expose deployment azure-voting-db \
    --port=5432 \
    --target-port=5432

    With the database exposed using service, we can update the app deployment manifest to use the service name instead of pod IP. This way, if the pod ever gets assigned a new IP, we don't have to worry about updating the IP each time and redeploying our web application. Kubernetes has internal service discovery mechanism in place that allows us to reference a service by its name.

    Let's make an update to the manifest. Replace the environment variable for DATABASE_SERVER with the following:

    - name: DATABASE_SERVER
    value: azure-voting-db

    Re-deploy the app with the updated configuration.

    kubectl apply -f ./manifests/deployment-app.yaml

    One service down, one to go. Run the following command to expose the web application.

    kubectl expose deployment azure-voting-app \
    --type=LoadBalancer \
    --port=80 \
    --target-port=8080

    Notice the --type argument has a value of LoadBalancer. This service type is implemented by the cloud-controller-manager which is part of the Kubernetes control plane. When using a managed Kubernetes cluster such as Azure Kubernetes Service, a public standard load balancer will be able to provisioned when the service type is set to LoadBalancer. The load balancer will also have a public IP assigned which will make your deployment publicly available.

    Kubernetes supports four service types:

    • ClusterIP: this is the default and limits service access to internal traffic within the cluster
    • NodePort: this assigns a port mapping on the node's IP address and allows traffic from the virtual network (outside the cluster)
    • LoadBalancer: as mentioned above, this creates a cloud-based load balancer
    • ExternalName: this is used in special case scenarios where you want to map a service to an external DNS name

    📝 NOTE: When exposing a web application to the internet, allowing external users to connect to your Service directly is not the best approach. Instead, you should use an Ingress, which we'll cover in the next section.

    Now, let's confirm you can reach the web app from the internet. You can use the following command to print the URL to your terminal.

    echo "http://$(kubectl get service azure-voting-app -o jsonpath='{.status.loadBalancer.ingress[0].ip}')"

    Great! The kubectl expose command gets the job done, but as mentioned above, it is not the best method of exposing deployments. It is better to expose deployments declaratively using a service manifest, so let's delete the services and redeploy using manifests.

    kubectl delete service azure-voting-db azure-voting-app

    To use kubectl to generate our manifest file, we can use the same kubectl expose command that we ran earlier but this time, we'll include --output=yaml and --dry-run=client. This will instruct the command to output the manifest that would be sent to the kube-api server in YAML format to the terminal.

    Generate the manifest for the database service.

    kubectl expose deployment azure-voting-db \
    --type=ClusterIP \
    --port=5432 \
    --target-port=5432 \
    --output=yaml \
    --dry-run=client > ./manifests/service-db.yaml

    Generate the manifest for the application service.

    kubectl expose deployment azure-voting-app \
    --type=LoadBalancer \
    --port=80 \
    --target-port=8080 \
    --output=yaml \
    --dry-run=client > ./manifests/service-app.yaml

    The command above redirected the YAML output to your manifests directory. Here is what the web application service looks like.

    apiVersion: v1
    kind: Service
    metadata:
    creationTimestamp: null
    labels:
    app: azure-voting-app
    name: azure-voting-app
    spec:
    ports:
    - port: 80
    protocol: TCP
    targetPort: 8080
    selector:
    app: azure-voting-app
    type: LoadBalancer
    status:
    loadBalancer: {}

    💡 TIP: To view the schema of any api-resource in Kubernetes, you can use the kubectl explain command. In this case the kubectl explain service command will tell us exactly what each of these fields do.

    Re-deploy the services using the new service manifests.

    kubectl apply -f ./manifests/service-db.yaml -f ./manifests/service-app.yaml

    # You should see TYPE is set to LoadBalancer and the EXTERNAL-IP is set
    kubectl get service azure-voting-db azure-voting-app

    Confirm again that our application is accessible again. Run the following command to print the URL to the terminal.

    echo "http://$(kubectl get service azure-voting-app -o jsonpath='{.status.loadBalancer.ingress[0].ip}')"

    That was easy, right? We just exposed both of our pods using Kubernetes services. The database only needs to be accessible from within the cluster so ClusterIP is perfect for that. For the web application, we specified the type to be LoadBalancer so that we can access the application over the public internet.

    But wait... remember that if you want to expose web applications over the public internet, a Service with a public IP is not the best way; the better approach is to use an Ingress resource.

    Exposing Services via Ingress

    If you read through the Kubernetes documentation on Ingress you will see a diagram that depicts the Ingress sitting in front of the Service resource with a routing rule between it. In order to use Ingress, you need to deploy an Ingress Controller and it can be configured with many routing rules to forward traffic to one or many backend services. So effectively, an Ingress is a load balancer for your Services.

    With that said, we no longer need a service type of LoadBalancer since the service does not need to be accessible from the internet. It only needs to be accessible from the Ingress Controller (internal to the cluster) so we can change the service type to ClusterIP.

    Update your service.yaml file to look like this:

    apiVersion: v1
    kind: Service
    metadata:
    creationTimestamp: null
    labels:
    app: azure-voting-app
    name: azure-voting-app
    spec:
    ports:
    - port: 80
    protocol: TCP
    targetPort: 8080
    selector:
    app: azure-voting-app

    📝 NOTE: The default service type is ClusterIP so we can omit the type altogether.

    Re-apply the app service manifest.

    kubectl apply -f ./manifests/service-app.yaml

    # You should see TYPE set to ClusterIP and EXTERNAL-IP set to <none>
    kubectl get service azure-voting-app

    Next, we need to install an Ingress Controller. There are quite a few options, and the Kubernetes-maintained NGINX Ingress Controller is commonly deployed.

    You could install this manually by following these instructions, but if you do that you'll be responsible for maintaining and supporting the resource.

    I like to take advantage of free maintenance and support when I can get it, so I'll opt to use the Web Application Routing add-on for AKS.

    💡 TIP: Whenever you install an AKS add-on, it will be maintained and fully supported by Azure Support.

    Enable the web application routing add-on in our AKS cluster with the following command.

    az aks addon enable \
    --name <YOUR_AKS_NAME> \
    --resource-group <YOUR_AKS_RESOURCE_GROUP>
    --addon web_application_routing

    ⚠️ WARNING: This command can take a few minutes to complete

    Now, let's use the same approach we took in creating our service to create our Ingress resource. Run the following command to generate the Ingress manifest.

    kubectl create ingress azure-voting-app \
    --class=webapprouting.kubernetes.azure.com \
    --rule="/*=azure-voting-app:80" \
    --output yaml \
    --dry-run=client > ./manifests/ingress.yaml

    The --class=webapprouting.kubernetes.azure.com option activates the AKS web application routing add-on. This AKS add-on can also integrate with other Azure services such as Azure DNS and Azure Key Vault for TLS certificate management and this special class makes it all work.

    The --rule="/*=azure-voting-app:80" option looks confusing but we can use kubectl again to help us understand how to format the value for the option.

    kubectl create ingress --help

    In the output you will see the following:

    --rule=[]:
    Rule in format host/path=service:port[,tls=secretname]. Paths containing the leading character '*' are
    considered pathType=Prefix. tls argument is optional.

    It expects a host and path separated by a forward-slash, then expects the backend service name and port separated by a colon. We're not using a hostname for this demo so we can omit it. For the path, an asterisk is used to specify a wildcard path prefix.

    So, the value of /*=azure-voting-app:80 creates a routing rule for all paths following the domain (or in our case since we don't have a hostname specified, the IP) to route traffic to our azure-voting-app backend service on port 80.

    📝 NOTE: Configuring the hostname and TLS is outside the scope of this demo but please visit this URL https://bit.ly/aks-webapp-routing for an in-depth hands-on lab centered around Web Application Routing on AKS.

    Your ingress.yaml file should look like this:

    apiVersion: networking.k8s.io/v1
    kind: Ingress
    metadata:
    creationTimestamp: null
    name: azure-voting-app
    spec:
    ingressClassName: webapprouting.kubernetes.azure.com
    rules:
    - http:
    paths:
    - backend:
    service:
    name: azure-voting-app
    port:
    number: 80
    path: /
    pathType: Prefix
    status:
    loadBalancer: {}

    Apply the app ingress manifest.

    kubectl apply -f ./manifests/ingress.yaml

    Validate the web application is available from the internet again. You can run the following command to print the URL to the terminal.

    echo "http://$(kubectl get ingress azure-voting-app -o jsonpath='{.status.loadBalancer.ingress[0].ip}')"

    Takeaways

    Exposing your applications both internally and externally can be easily achieved using Service and Ingress resources respectively. If your service is HTTP or HTTPS based and needs to be accessible from outsie the cluster, use Ingress with an internal Service (i.e., ClusterIP or NodePort); otherwise, use the Service resource. If your TCP-based Service needs to be publicly accessible, you set the type to LoadBalancer to expose a public IP for it. To learn more about these resources, please visit the links listed below.

    Lastly, if you are unsure how to begin writing your service manifest, you can use kubectl and have it do most of the work for you 🥳

    Resources

    Take the Cloud Skills Challenge!

    Enroll in the Cloud Skills Challenge!

    Don't miss out on this opportunity to level up your skills and stay ahead of the curve in the world of cloud native.

    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/azure-kubernetes-service/page/9/index.html b/cnny-2023/tags/azure-kubernetes-service/page/9/index.html index dd58fba1d6..31c0cc5a76 100644 --- a/cnny-2023/tags/azure-kubernetes-service/page/9/index.html +++ b/cnny-2023/tags/azure-kubernetes-service/page/9/index.html @@ -14,14 +14,14 @@ - - + +

    21 posts tagged with "azure-kubernetes-service"

    View All Tags

    · 6 min read
    Josh Duffney

    Welcome to Day 3 of Week 2 of #CloudNativeNewYear!

    The theme for this week is Kubernetes fundamentals. Yesterday we talked about Services and Ingress. Today we'll explore the topic of passing configuration and secrets to our applications in Kubernetes with ConfigMaps and Secrets.

    Ask the Experts Thursday, February 9th at 9 AM PST
    Catch the Replay of the Live Demo

    Watch the recorded demo and conversation about this week's topics.

    We were live on YouTube walking through today's (and the rest of this week's) demos.

    What We'll Cover

    • Decouple configurations with ConfigMaps and Secerts
    • Passing Environment Data with ConfigMaps and Secrets
    • Conclusion

    Decouple configurations with ConfigMaps and Secerts

    A ConfigMap is a Kubernetes object that decouples configuration data from pod definitions. Kubernetes secerts are similar, but were designed to decouple senstive information.

    Separating the configuration and secerts from your application promotes better organization and security of your Kubernetes environment. It also enables you to share the same configuration and different secerts across multiple pods and deployments which can simplify scaling and management. Using ConfigMaps and Secerts in Kubernetes is a best practice that can help to improve the scalability, security, and maintainability of your cluster.

    By the end of this tutorial, you'll have added a Kubernetes ConfigMap and Secret to the Azure Voting deployment.

    Passing Environment Data with ConfigMaps and Secrets

    📝 NOTE: If you don't have an AKS cluster deployed, please head over to Azure-Samples/azure-voting-app-rust, clone the repo, and follow the instructions in the README.md to execute the Azure deployment and setup your kubectl context. Check out the first post this week for more on the environment setup.

    Create the ConfigMap

    ConfigMaps can be used in one of two ways; as environment variables or volumes.

    For this tutorial you'll use a ConfigMap to create three environment variables inside the pod; DATABASE_SERVER, FISRT_VALUE, and SECOND_VALUE. The DATABASE_SERVER provides part of connection string to a Postgres. FIRST_VALUE and SECOND_VALUE are configuration options that change what voting options the application presents to the users.

    Follow the below steps to create a new ConfigMap:

    1. Create a YAML file named 'config-map.yaml'. In this file, specify the environment variables for the application.

      apiVersion: v1
      kind: ConfigMap
      metadata:
      name: azure-voting-config
      data:
      DATABASE_SERVER: azure-voting-db
      FIRST_VALUE: "Go"
      SECOND_VALUE: "Rust"
    2. Create the config map in your Kubernetes cluster by running the following command:

      kubectl create -f config-map.yaml

    Create the Secret

    The deployment-db.yaml and deployment-app.yaml are Kubernetes manifests that deploy the Azure Voting App. Currently, those deployment manifests contain the environment variables POSTGRES_PASSWORD and DATABASE_PASSWORD with the value stored as plain text. Your task is to replace that environment variable with a Kubernetes Secret.

    Create a Secret running the following commands:

    1. Encode mypassword.

      echo -n "mypassword" | base64
    2. Create a YAML file named secret.yaml. In this file, add POSTGRES_PASSWORD as the key and the encoded value returned above under as the value in the data section.

      apiVersion: v1
      kind: Secret
      metadata:
      name: azure-voting-secret
      type: Opaque
      data:
      POSTGRES_PASSWORD: bXlwYXNzd29yZA==
    3. Create the Secret in your Kubernetes cluster by running the following command:

      kubectl create -f secret.yaml

    [!WARNING] base64 encoding is a simple and widely supported way to obscure plaintext data, it is not secure, as it can easily be decoded. If you want to store sensitive data like password, you should use a more secure method like encrypting with a Key Management Service (KMS) before storing it in the Secret.

    Modify the app deployment manifest

    With the ConfigMap and Secert both created the next step is to replace the environment variables provided in the application deployment manuscript with the values stored in the ConfigMap and the Secert.

    Complete the following steps to add the ConfigMap and Secert to the deployment mainifest:

    1. Open the Kubernetes manifest file deployment-app.yaml.

    2. In the containers section, add an envFrom section and upate the env section.

      envFrom:
      - configMapRef:
      name: azure-voting-config
      env:
      - name: DATABASE_PASSWORD
      valueFrom:
      secretKeyRef:
      name: azure-voting-secret
      key: POSTGRES_PASSWORD

      Using envFrom exposes all the values witin the ConfigMap as environment variables. Making it so you don't have to list them individually.

    3. Save the changes to the deployment manifest file.

    4. Apply the changes to the deployment by running the following command:

      kubectl apply -f deployment-app.yaml

    Modify the database deployment manifest

    Next, update the database deployment manifest and replace the plain text environment variable with the Kubernetes Secert.

    1. Open the deployment-db.yaml.

    2. To add the secret to the deployment, replace the env section with the following code:

      env:
      - name: POSTGRES_PASSWORD
      valueFrom:
      secretKeyRef:
      name: azure-voting-secret
      key: POSTGRES_PASSWORD
    3. Apply the updated manifest.

      kubectl apply -f deployment-db.yaml

    Verify the ConfigMap and output environment variables

    Verify that the ConfigMap was added to your deploy by running the following command:

    ```bash
    kubectl describe deployment azure-voting-app
    ```

    Browse the output until you find the envFrom section with the config map reference.

    You can also verify that the environment variables from the config map are being passed to the container by running the command kubectl exec -it <pod-name> -- printenv. This command will show you all the environment variables passed to the pod including the one from configmap.

    By following these steps, you will have successfully added a config map to the Azure Voting App Kubernetes deployment, and the environment variables defined in the config map will be passed to the container running in the pod.

    Verify the Secret and describe the deployment

    Once the secret has been created you can verify it exists by running the following command:

    kubectl get secrets

    You can view additional information, such as labels, annotations, type, and the Data by running kubectl describe:

    kubectl describe secret azure-voting-secret

    By default, the describe command doesn't output the encoded value, but if you output the results as JSON or YAML you'll be able to see the secret's encoded value.

     kubectl get secret azure-voting-secret -o json

    Conclusion

    In conclusion, using ConfigMaps and Secrets in Kubernetes can help to improve the scalability, security, and maintainability of your cluster. By decoupling configuration data and sensitive information from pod definitions, you can promote better organization and security in your Kubernetes environment. Additionally, separating these elements allows for sharing the same configuration and different secrets across multiple pods and deployments, simplifying scaling and management.

    Take the Cloud Skills Challenge!

    Enroll in the Cloud Skills Challenge!

    Don't miss out on this opportunity to level up your skills and stay ahead of the curve in the world of cloud native.

    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/cloud-native-new-year/index.html b/cnny-2023/tags/cloud-native-new-year/index.html index 8994652452..b8856ec39b 100644 --- a/cnny-2023/tags/cloud-native-new-year/index.html +++ b/cnny-2023/tags/cloud-native-new-year/index.html @@ -14,13 +14,13 @@ - - + +

    5 posts tagged with "cloud-native-new-year"

    View All Tags

    · 11 min read
    Paul Yu

    Welcome to Day 2 of Week 2 of #CloudNativeNewYear!

    The theme for this week is #Kubernetes fundamentals. Yesterday we talked about how to deploy a containerized web app workload to Azure Kubernetes Service (AKS). Today we'll explore the topic of services and ingress and walk through the steps of making our containers accessible both internally as well as over the internet so that you can share it with the world 😊

    Ask the Experts Thursday, February 9th at 9 AM PST
    Catch the Replay of the Live Demo

    Watch the recorded demo and conversation about this week's topics.

    We were live on YouTube walking through today's (and the rest of this week's) demos.

    What We'll Cover

    • Exposing Pods via Service
    • Exposing Services via Ingress
    • Takeaways
    • Resources

    Exposing Pods via Service

    There are a few ways to expose your pod in Kubernetes. One way is to take an imperative approach and use the kubectl expose command. This is probably the quickest way to achieve your goal but it isn't the best way. A better way to expose your pod by taking a declarative approach by creating a services manifest file and deploying it using the kubectl apply command.

    Don't worry if you are unsure of how to make this manifest, we'll use kubectl to help generate it.

    First, let's ensure we have the database deployed on our AKS cluster.

    📝 NOTE: If you don't have an AKS cluster deployed, please head over to Azure-Samples/azure-voting-app-rust, clone the repo, and follow the instructions in the README.md to execute the Azure deployment and setup your kubectl context. Check out the first post this week for more on the environment setup.

    kubectl apply -f ./manifests/deployment-db.yaml

    Next, let's deploy the application. If you are following along from yesterday's content, there isn't anything you need to change; however, if you are deploy the app from scratch, you'll need to modify the deployment-app.yaml manifest and update it with your image tag and database pod's IP address.

    kubectl apply -f ./manifests/deployment-app.yaml

    Now, let's expose the database using a service so that we can leverage Kubernetes' built-in service discovery to be able to reference it by name; not pod IP. Run the following command.

    kubectl expose deployment azure-voting-db \
    --port=5432 \
    --target-port=5432

    With the database exposed using service, we can update the app deployment manifest to use the service name instead of pod IP. This way, if the pod ever gets assigned a new IP, we don't have to worry about updating the IP each time and redeploying our web application. Kubernetes has internal service discovery mechanism in place that allows us to reference a service by its name.

    Let's make an update to the manifest. Replace the environment variable for DATABASE_SERVER with the following:

    - name: DATABASE_SERVER
    value: azure-voting-db

    Re-deploy the app with the updated configuration.

    kubectl apply -f ./manifests/deployment-app.yaml

    One service down, one to go. Run the following command to expose the web application.

    kubectl expose deployment azure-voting-app \
    --type=LoadBalancer \
    --port=80 \
    --target-port=8080

    Notice the --type argument has a value of LoadBalancer. This service type is implemented by the cloud-controller-manager which is part of the Kubernetes control plane. When using a managed Kubernetes cluster such as Azure Kubernetes Service, a public standard load balancer will be able to provisioned when the service type is set to LoadBalancer. The load balancer will also have a public IP assigned which will make your deployment publicly available.

    Kubernetes supports four service types:

    • ClusterIP: this is the default and limits service access to internal traffic within the cluster
    • NodePort: this assigns a port mapping on the node's IP address and allows traffic from the virtual network (outside the cluster)
    • LoadBalancer: as mentioned above, this creates a cloud-based load balancer
    • ExternalName: this is used in special case scenarios where you want to map a service to an external DNS name

    📝 NOTE: When exposing a web application to the internet, allowing external users to connect to your Service directly is not the best approach. Instead, you should use an Ingress, which we'll cover in the next section.

    Now, let's confirm you can reach the web app from the internet. You can use the following command to print the URL to your terminal.

    echo "http://$(kubectl get service azure-voting-app -o jsonpath='{.status.loadBalancer.ingress[0].ip}')"

    Great! The kubectl expose command gets the job done, but as mentioned above, it is not the best method of exposing deployments. It is better to expose deployments declaratively using a service manifest, so let's delete the services and redeploy using manifests.

    kubectl delete service azure-voting-db azure-voting-app

    To use kubectl to generate our manifest file, we can use the same kubectl expose command that we ran earlier but this time, we'll include --output=yaml and --dry-run=client. This will instruct the command to output the manifest that would be sent to the kube-api server in YAML format to the terminal.

    Generate the manifest for the database service.

    kubectl expose deployment azure-voting-db \
    --type=ClusterIP \
    --port=5432 \
    --target-port=5432 \
    --output=yaml \
    --dry-run=client > ./manifests/service-db.yaml

    Generate the manifest for the application service.

    kubectl expose deployment azure-voting-app \
    --type=LoadBalancer \
    --port=80 \
    --target-port=8080 \
    --output=yaml \
    --dry-run=client > ./manifests/service-app.yaml

    The command above redirected the YAML output to your manifests directory. Here is what the web application service looks like.

    apiVersion: v1
    kind: Service
    metadata:
    creationTimestamp: null
    labels:
    app: azure-voting-app
    name: azure-voting-app
    spec:
    ports:
    - port: 80
    protocol: TCP
    targetPort: 8080
    selector:
    app: azure-voting-app
    type: LoadBalancer
    status:
    loadBalancer: {}

    💡 TIP: To view the schema of any api-resource in Kubernetes, you can use the kubectl explain command. In this case the kubectl explain service command will tell us exactly what each of these fields do.

    Re-deploy the services using the new service manifests.

    kubectl apply -f ./manifests/service-db.yaml -f ./manifests/service-app.yaml

    # You should see TYPE is set to LoadBalancer and the EXTERNAL-IP is set
    kubectl get service azure-voting-db azure-voting-app

    Confirm again that our application is accessible again. Run the following command to print the URL to the terminal.

    echo "http://$(kubectl get service azure-voting-app -o jsonpath='{.status.loadBalancer.ingress[0].ip}')"

    That was easy, right? We just exposed both of our pods using Kubernetes services. The database only needs to be accessible from within the cluster so ClusterIP is perfect for that. For the web application, we specified the type to be LoadBalancer so that we can access the application over the public internet.

    But wait... remember that if you want to expose web applications over the public internet, a Service with a public IP is not the best way; the better approach is to use an Ingress resource.

    Exposing Services via Ingress

    If you read through the Kubernetes documentation on Ingress you will see a diagram that depicts the Ingress sitting in front of the Service resource with a routing rule between it. In order to use Ingress, you need to deploy an Ingress Controller and it can be configured with many routing rules to forward traffic to one or many backend services. So effectively, an Ingress is a load balancer for your Services.

    With that said, we no longer need a service type of LoadBalancer since the service does not need to be accessible from the internet. It only needs to be accessible from the Ingress Controller (internal to the cluster) so we can change the service type to ClusterIP.

    Update your service.yaml file to look like this:

    apiVersion: v1
    kind: Service
    metadata:
    creationTimestamp: null
    labels:
    app: azure-voting-app
    name: azure-voting-app
    spec:
    ports:
    - port: 80
    protocol: TCP
    targetPort: 8080
    selector:
    app: azure-voting-app

    📝 NOTE: The default service type is ClusterIP so we can omit the type altogether.

    Re-apply the app service manifest.

    kubectl apply -f ./manifests/service-app.yaml

    # You should see TYPE set to ClusterIP and EXTERNAL-IP set to <none>
    kubectl get service azure-voting-app

    Next, we need to install an Ingress Controller. There are quite a few options, and the Kubernetes-maintained NGINX Ingress Controller is commonly deployed.

    You could install this manually by following these instructions, but if you do that you'll be responsible for maintaining and supporting the resource.

    I like to take advantage of free maintenance and support when I can get it, so I'll opt to use the Web Application Routing add-on for AKS.

    💡 TIP: Whenever you install an AKS add-on, it will be maintained and fully supported by Azure Support.

    Enable the web application routing add-on in our AKS cluster with the following command.

    az aks addon enable \
    --name <YOUR_AKS_NAME> \
    --resource-group <YOUR_AKS_RESOURCE_GROUP>
    --addon web_application_routing

    ⚠️ WARNING: This command can take a few minutes to complete

    Now, let's use the same approach we took in creating our service to create our Ingress resource. Run the following command to generate the Ingress manifest.

    kubectl create ingress azure-voting-app \
    --class=webapprouting.kubernetes.azure.com \
    --rule="/*=azure-voting-app:80" \
    --output yaml \
    --dry-run=client > ./manifests/ingress.yaml

    The --class=webapprouting.kubernetes.azure.com option activates the AKS web application routing add-on. This AKS add-on can also integrate with other Azure services such as Azure DNS and Azure Key Vault for TLS certificate management and this special class makes it all work.

    The --rule="/*=azure-voting-app:80" option looks confusing but we can use kubectl again to help us understand how to format the value for the option.

    kubectl create ingress --help

    In the output you will see the following:

    --rule=[]:
    Rule in format host/path=service:port[,tls=secretname]. Paths containing the leading character '*' are
    considered pathType=Prefix. tls argument is optional.

    It expects a host and path separated by a forward-slash, then expects the backend service name and port separated by a colon. We're not using a hostname for this demo so we can omit it. For the path, an asterisk is used to specify a wildcard path prefix.

    So, the value of /*=azure-voting-app:80 creates a routing rule for all paths following the domain (or in our case since we don't have a hostname specified, the IP) to route traffic to our azure-voting-app backend service on port 80.

    📝 NOTE: Configuring the hostname and TLS is outside the scope of this demo but please visit this URL https://bit.ly/aks-webapp-routing for an in-depth hands-on lab centered around Web Application Routing on AKS.

    Your ingress.yaml file should look like this:

    apiVersion: networking.k8s.io/v1
    kind: Ingress
    metadata:
    creationTimestamp: null
    name: azure-voting-app
    spec:
    ingressClassName: webapprouting.kubernetes.azure.com
    rules:
    - http:
    paths:
    - backend:
    service:
    name: azure-voting-app
    port:
    number: 80
    path: /
    pathType: Prefix
    status:
    loadBalancer: {}

    Apply the app ingress manifest.

    kubectl apply -f ./manifests/ingress.yaml

    Validate the web application is available from the internet again. You can run the following command to print the URL to the terminal.

    echo "http://$(kubectl get ingress azure-voting-app -o jsonpath='{.status.loadBalancer.ingress[0].ip}')"

    Takeaways

    Exposing your applications both internally and externally can be easily achieved using Service and Ingress resources respectively. If your service is HTTP or HTTPS based and needs to be accessible from outsie the cluster, use Ingress with an internal Service (i.e., ClusterIP or NodePort); otherwise, use the Service resource. If your TCP-based Service needs to be publicly accessible, you set the type to LoadBalancer to expose a public IP for it. To learn more about these resources, please visit the links listed below.

    Lastly, if you are unsure how to begin writing your service manifest, you can use kubectl and have it do most of the work for you 🥳

    Resources

    Take the Cloud Skills Challenge!

    Enroll in the Cloud Skills Challenge!

    Don't miss out on this opportunity to level up your skills and stay ahead of the curve in the world of cloud native.

    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/cloud-native-new-year/page/2/index.html b/cnny-2023/tags/cloud-native-new-year/page/2/index.html index 80b1cfab74..240c522f6e 100644 --- a/cnny-2023/tags/cloud-native-new-year/page/2/index.html +++ b/cnny-2023/tags/cloud-native-new-year/page/2/index.html @@ -14,13 +14,13 @@ - - + +

    5 posts tagged with "cloud-native-new-year"

    View All Tags

    · 8 min read
    Paul Yu

    Welcome to Day 4 of Week 2 of #CloudNativeNewYear!

    The theme for this week is Kubernetes fundamentals. Yesterday we talked about how to set app configurations and secrets at runtime using Kubernetes ConfigMaps and Secrets. Today we'll explore the topic of persistent storage on Kubernetes and show you can leverage Persistent Volumes and Persistent Volume Claims to ensure your PostgreSQL data can survive container restarts.

    Ask the Experts Thursday, February 9th at 9 AM PST
    Catch the Replay of the Live Demo

    Watch the recorded demo and conversation about this week's topics.

    We were live on YouTube walking through today's (and the rest of this week's) demos.

    What We'll Cover

    • Containers are ephemeral
    • Persistent storage on Kubernetes
    • Persistent storage on AKS
    • Takeaways
    • Resources

    Containers are ephemeral

    In our sample application, the frontend UI writes vote values to a backend PostgreSQL database. By default the database container stores its data on the container's local file system, so there will be data loss when the pod is re-deployed or crashes as containers are meant to start with a clean slate each time.

    Let's re-deploy our sample app and experience the problem first hand.

    📝 NOTE: If you don't have an AKS cluster deployed, please head over to Azure-Samples/azure-voting-app-rust, clone the repo, and follow the instructions in the README.md to execute the Azure deployment and setup your kubectl context. Check out the first post this week for more on the environment setup.

    kubectl apply -f ./manifests

    Wait for the azure-voting-app service to be assigned a public IP then browse to the website and submit some votes. Use the command below to print the URL to the terminal.

    echo "http://$(kubectl get ingress azure-voting-app -o jsonpath='{.status.loadBalancer.ingress[0].ip}')"

    Now, let's delete the pods and watch Kubernetes do what it does best... that is, re-schedule pods.

    # wait for the pod to come up then ctrl+c to stop watching
    kubectl delete --all pod --wait=false && kubectl get po -w

    Once the pods have been recovered, reload the website and confirm the vote tally has been reset to zero.

    We need to fix this so that the data outlives the container.

    Persistent storage on Kubernetes

    In order for application data to survive crashes and restarts, you must implement Persistent Volumes and Persistent Volume Claims.

    A persistent volume represents storage that is available to the cluster. Storage volumes can be provisioned manually by an administrator or dynamically using Container Storage Interface (CSI) and storage classes, which includes information on how to provision CSI volumes.

    When a user needs to add persistent storage to their application, a persistent volume claim is made to allocate chunks of storage from the volume. This "claim" includes things like volume mode (e.g., file system or block storage), the amount of storage to allocate, the access mode, and optionally a storage class. Once a persistent volume claim has been deployed, users can add the volume to the pod and mount it in a container.

    In the next section, we'll demonstrate how to enable persistent storage on AKS.

    Persistent storage on AKS

    With AKS, CSI drivers and storage classes are pre-deployed into your cluster. This allows you to natively use Azure Disks, Azure Files, and Azure Blob Storage as persistent volumes. You can either bring your own Azure storage account and use it with AKS or have AKS provision an Azure storage account for you.

    To view the Storage CSI drivers that have been enabled in your AKS cluster, run the following command.

    az aks show \
    --name <YOUR_AKS_NAME> \
    --resource-group <YOUR_AKS_RESOURCE_GROUP> \
    --query storageProfile

    You should see output that looks like this.

    {
    "blobCsiDriver": null,
    "diskCsiDriver": {
    "enabled": true,
    "version": "v1"
    },
    "fileCsiDriver": {
    "enabled": true
    },
    "snapshotController": {
    "enabled": true
    }
    }

    To view the storage classes that have been installed in your cluster, run the following command.

    kubectl get storageclass

    Workload requirements will dictate which CSI driver and storage class you will need to use.

    If you need block storage, then you should use the blobCsiDriver. The driver may not be enabled by default but you can enable it by following instructions which can be found in the Resources section below.

    If you need file storage you should leverage either diskCsiDriver or fileCsiDriver. The decision between these two boils down to whether or not you need to have the underlying storage accessible by one pod or multiple pods. It is important to note that diskCsiDriver currently supports access from a single pod only. Therefore, if you need data to be accessible by multiple pods at the same time, then you should opt for fileCsiDriver.

    For our PostgreSQL deployment, we'll use the diskCsiDriver and have AKS create an Azure Disk resource for us. There is no need to create a PV resource, all we need to do to is create a PVC using the managed-csi-premium storage class.

    Run the following command to create the PVC.

    kubectl apply -f - <<EOF            
    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
    name: pvc-azuredisk
    spec:
    accessModes:
    - ReadWriteOnce
    resources:
    requests:
    storage: 10Gi
    storageClassName: managed-csi-premium
    EOF

    When you check the PVC resource, you'll notice the STATUS is set to Pending. It will be set to Bound once the volume is mounted in the PostgreSQL container.

    kubectl get persistentvolumeclaim

    Let's delete the azure-voting-db deployment.

    kubectl delete deploy azure-voting-db

    Next, we need to apply an updated deployment manifest which includes our PVC.

    kubectl apply -f - <<EOF
    apiVersion: apps/v1
    kind: Deployment
    metadata:
    creationTimestamp: null
    labels:
    app: azure-voting-db
    name: azure-voting-db
    spec:
    replicas: 1
    selector:
    matchLabels:
    app: azure-voting-db
    strategy: {}
    template:
    metadata:
    creationTimestamp: null
    labels:
    app: azure-voting-db
    spec:
    containers:
    - image: postgres:15.0-alpine
    name: postgres
    ports:
    - containerPort: 5432
    env:
    - name: POSTGRES_PASSWORD
    valueFrom:
    secretKeyRef:
    name: azure-voting-secret
    key: POSTGRES_PASSWORD
    resources: {}
    volumeMounts:
    - name: mypvc
    mountPath: "/var/lib/postgresql/data"
    subPath: "data"
    volumes:
    - name: mypvc
    persistentVolumeClaim:
    claimName: pvc-azuredisk
    EOF

    In the manifest above, you'll see that we are mounting a new volume called mypvc (the name can be whatever you want) in the pod which points to a PVC named pvc-azuredisk. With the volume in place, we can mount it in the container by referencing the name of the volume mypvc and setting the mount path to /var/lib/postgresql/data (which is the default path).

    💡 IMPORTANT: When mounting a volume into a non-empty subdirectory, you must add subPath to the volume mount and point it to a subdirectory in the volume rather than mounting at root. In our case, when Azure Disk is formatted, it leaves a lost+found directory as documented here.

    Watch the pods and wait for the STATUS to show Running and the pod's READY status shows 1/1.

    # wait for the pod to come up then ctrl+c to stop watching
    kubectl get po -w

    Verify that the STATUS of the PVC is now set to Bound

    kubectl get persistentvolumeclaim

    With the new database container running, let's restart the application pod, wait for the pod's READY status to show 1/1, then head back over to our web browser and submit a few votes.

    kubectl delete pod -lapp=azure-voting-app --wait=false && kubectl get po -lapp=azure-voting-app -w

    Now the moment of truth... let's rip out the pods again, wait for the pods to be re-scheduled, and confirm our vote counts remain in tact.

    kubectl delete --all pod --wait=false && kubectl get po -w

    If you navigate back to the website, you'll find the vote are still there 🎉

    Takeaways

    By design, containers are meant to be ephemeral and stateless workloads are ideal on Kubernetes. However, there will come a time when your data needs to outlive the container. To persist data in your Kubernetes workloads, you need to leverage PV, PVC, and optionally storage classes. In our demo scenario, we leveraged CSI drivers built into AKS and created a PVC using pre-installed storage classes. From there, we updated the database deployment to mount the PVC in the container and AKS did the rest of the work in provisioning the underlying Azure Disk. If the built-in storage classes does not fit your needs; for example, you need to change the ReclaimPolicy or change the SKU for the Azure resource, then you can create your own custom storage class and configure it just the way you need it 😊

    We'll revisit this topic again next week but in the meantime, check out some of the resources listed below to learn more.

    See you in the next post!

    Resources

    Take the Cloud Skills Challenge!

    Enroll in the Cloud Skills Challenge!

    Don't miss out on this opportunity to level up your skills and stay ahead of the curve in the world of cloud native.

    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/cloud-native-new-year/page/3/index.html b/cnny-2023/tags/cloud-native-new-year/page/3/index.html index 71dd77cfd2..578e2d0cbb 100644 --- a/cnny-2023/tags/cloud-native-new-year/page/3/index.html +++ b/cnny-2023/tags/cloud-native-new-year/page/3/index.html @@ -14,13 +14,13 @@ - - + +

    5 posts tagged with "cloud-native-new-year"

    View All Tags

    · 12 min read
    Paul Yu

    Welcome to Day 2 of Week 3 of #CloudNativeNewYear!

    The theme for this week is Bringing Your Application to Kubernetes. Yesterday we talked about getting an existing application running in Kubernetes with a full pipeline in GitHub Actions. Today we'll evaluate our sample application's configuration, storage, and networking requirements and implement using Kubernetes and Azure resources.

    Ask the Experts Thursday, February 9th at 9 AM PST
    Friday, February 10th at 11 AM PST

    Watch the recorded demo and conversation about this week's topics.

    We were live on YouTube walking through today's (and the rest of this week's) demos. Join us Friday, February 10th and bring your questions!

    What We'll Cover

    • Gather requirements
    • Implement environment variables using ConfigMaps
    • Implement persistent volumes using Azure Files
    • Implement secrets using Azure Key Vault
    • Re-package deployments
    • Conclusion
    • Resources
    caution

    Before you begin, make sure you've gone through yesterday's post to set up your AKS cluster.

    Gather requirements

    The eShopOnWeb application is written in .NET 7 and has two major pieces of functionality. The web UI is where customers can browse and shop. The web UI also includes an admin portal for managing the product catalog. This admin portal, is packaged as a WebAssembly application and relies on a separate REST API service. Both the web UI and the REST API connect to the same SQL Server container.

    Looking through the source code which can be found here we can identify requirements for configs, persistent storage, and secrets.

    Database server

    • Need to store the password for the sa account as a secure secret
    • Need persistent storage volume for data directory
    • Need to inject environment variables for SQL Server license type and EULA acceptance

    Web UI and REST API service

    • Need to store database connection string as a secure secret
    • Need to inject ASP.NET environment variables to override app settings
    • Need persistent storage volume for ASP.NET key storage

    Implement environment variables using ConfigMaps

    ConfigMaps are relatively straight-forward to create. If you were following along with the examples last week, this should be review 😉

    Create a ConfigMap to store database environment variables.

    kubectl apply -f - <<EOF
    apiVersion: v1
    kind: ConfigMap
    metadata:
    name: mssql-settings
    data:
    MSSQL_PID: Developer
    ACCEPT_EULA: "Y"
    EOF

    Create another ConfigMap to store ASP.NET environment variables.

    kubectl apply -f - <<EOF
    apiVersion: v1
    kind: ConfigMap
    metadata:
    name: aspnet-settings
    data:
    ASPNETCORE_ENVIRONMENT: Development
    EOF

    Implement persistent volumes using Azure Files

    Similar to last week, we'll take advantage of storage classes built into AKS. For our SQL Server data, we'll use the azurefile-csi-premium storage class and leverage an Azure Files resource as our PersistentVolume.

    Create a PersistentVolumeClaim (PVC) for persisting SQL Server data.

    kubectl apply -f - <<EOF
    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
    name: mssql-data
    spec:
    accessModes:
    - ReadWriteMany
    storageClassName: azurefile-csi-premium
    resources:
    requests:
    storage: 5Gi
    EOF

    Create another PVC for persisting ASP.NET data.

    kubectl apply -f - <<EOF
    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
    name: aspnet-data
    spec:
    accessModes:
    - ReadWriteMany
    storageClassName: azurefile-csi-premium
    resources:
    requests:
    storage: 5Gi
    EOF

    Implement secrets using Azure Key Vault

    It's a well known fact that Kubernetes secretes are not really secrets. They're just base64-encoded values and not secure, especially if malicious users have access to your Kubernetes cluster.

    In a production scenario, you will want to leverage an external vault like Azure Key Vault or HashiCorp Vault to encrypt and store secrets.

    With AKS, we can enable the Secrets Store CSI driver add-on which will allow us to leverage Azure Key Vault.

    # Set some variables
    RG_NAME=<YOUR_RESOURCE_GROUP_NAME>
    AKS_NAME=<YOUR_AKS_CLUSTER_NAME>
    ACR_NAME=<YOUR_ACR_NAME>

    az aks enable-addons \
    --addons azure-keyvault-secrets-provider \
    --name $AKS_NAME \
    --resource-group $RG_NAME

    With the add-on enabled, you should see aks-secrets-store-csi-driver and aks-secrets-store-provider-azure resources installed on each node in your Kubernetes cluster.

    Run the command below to verify.

    kubectl get pods \
    --namespace kube-system \
    --selector 'app in (secrets-store-csi-driver, secrets-store-provider-azure)'

    The Secrets Store CSI driver allows us to use secret stores via Container Storage Interface (CSI) volumes. This provider offers capabilities such as mounting and syncing between the secure vault and Kubernetes Secrets. On AKS, the Azure Key Vault Provider for Secrets Store CSI Driver enables integration with Azure Key Vault.

    You may not have an Azure Key Vault created yet, so let's create one and add some secrets to it.

    AKV_NAME=$(az keyvault create \
    --name akv-eshop$RANDOM \
    --resource-group $RG_NAME \
    --query name -o tsv)

    # Database server password
    az keyvault secret set \
    --vault-name $AKV_NAME \
    --name mssql-password \
    --value "@someThingComplicated1234"

    # Catalog database connection string
    az keyvault secret set \
    --vault-name $AKV_NAME \
    --name mssql-connection-catalog \
    --value "Server=db;Database=Microsoft.eShopOnWeb.CatalogDb;User Id=sa;Password=@someThingComplicated1234;TrustServerCertificate=True;"

    # Identity database connection string
    az keyvault secret set \
    --vault-name $AKV_NAME \
    --name mssql-connection-identity \
    --value "Server=db;Database=Microsoft.eShopOnWeb.Identity;User Id=sa;Password=@someThingComplicated1234;TrustServerCertificate=True;"

    Pods authentication using Azure Workload Identity

    In order for our Pods to retrieve secrets from Azure Key Vault, we'll need to set up a way for the Pod to authenticate against Azure AD. This can be achieved by implementing the new Azure Workload Identity feature of AKS.

    info

    At the time of this writing, the workload identity feature of AKS is in Preview.

    The workload identity feature within AKS allows us to leverage native Kubernetes resources and link a Kubernetes ServiceAccount to an Azure Managed Identity to authenticate against Azure AD.

    For the authentication flow, our Kubernetes cluster will act as an Open ID Connect (OIDC) issuer and will be able issue identity tokens to ServiceAccounts which will be assigned to our Pods.

    The Azure Managed Identity will be granted permission to access secrets in our Azure Key Vault and with the ServiceAccount being assigned to our Pods, they will be able to retrieve our secrets.

    For more information on how the authentication mechanism all works, check out this doc.

    To implement all this, start by enabling the new preview feature for AKS.

    az feature register \
    --namespace "Microsoft.ContainerService" \
    --name "EnableWorkloadIdentityPreview"
    caution

    This can take several minutes to complete.

    Check the status and ensure the state shows Regestered before moving forward.

    az feature show \
    --namespace "Microsoft.ContainerService" \
    --name "EnableWorkloadIdentityPreview"

    Update your AKS cluster to enable the workload identity feature and enable the OIDC issuer endpoint.

    az aks update \
    --name $AKS_NAME \
    --resource-group $RG_NAME \
    --enable-workload-identity \
    --enable-oidc-issuer

    Create an Azure Managed Identity and retrieve its client ID.

    MANAGED_IDENTITY_CLIENT_ID=$(az identity create \
    --name aks-workload-identity \
    --resource-group $RG_NAME \
    --subscription $(az account show --query id -o tsv) \
    --query 'clientId' -o tsv)

    Create the Kubernetes ServiceAccount.

    # Set namespace (this must align with the namespace that your app is deployed into)
    SERVICE_ACCOUNT_NAMESPACE=default

    # Set the service account name
    SERVICE_ACCOUNT_NAME=eshop-serviceaccount

    # Create the service account
    kubectl apply -f - <<EOF
    apiVersion: v1
    kind: ServiceAccount
    metadata:
    annotations:
    azure.workload.identity/client-id: ${MANAGED_IDENTITY_CLIENT_ID}
    labels:
    azure.workload.identity/use: "true"
    name: ${SERVICE_ACCOUNT_NAME}
    namespace: ${SERVICE_ACCOUNT_NAMESPACE}
    EOF
    info

    Note to enable this ServiceAccount to work with Azure Workload Identity, you must annotate the resource with azure.workload.identity/client-id, and add a label of azure.workload.identity/use: "true"

    That was a lot... Let's review what we just did.

    We have an Azure Managed Identity (object in Azure AD), an OIDC issuer URL (endpoint in our Kubernetes cluster), and a Kubernetes ServiceAccount.

    The next step is to "tie" these components together and establish a Federated Identity Credential so that Azure AD can trust authentication requests from your Kubernetes cluster.

    info

    This identity federation can be established between Azure AD any Kubernetes cluster; not just AKS 🤗

    To establish the federated credential, we'll need the OIDC issuer URL, and a subject which points to your Kubernetes ServiceAccount.

    # Get the OIDC issuer URL
    OIDC_ISSUER_URL=$(az aks show \
    --name $AKS_NAME \
    --resource-group $RG_NAME \
    --query "oidcIssuerProfile.issuerUrl" -o tsv)

    # Set the subject name using this format: `system:serviceaccount:<YOUR_SERVICE_ACCOUNT_NAMESPACE>:<YOUR_SERVICE_ACCOUNT_NAME>`
    SUBJECT=system:serviceaccount:$SERVICE_ACCOUNT_NAMESPACE:$SERVICE_ACCOUNT_NAME

    az identity federated-credential create \
    --name aks-federated-credential \
    --identity-name aks-workload-identity \
    --resource-group $RG_NAME \
    --issuer $OIDC_ISSUER_URL \
    --subject $SUBJECT

    With the authentication components set, we can now create a SecretProviderClass which includes details about the Azure Key Vault, the secrets to pull out from the vault, and identity used to access the vault.

    # Get the tenant id for the key vault
    TENANT_ID=$(az keyvault show \
    --name $AKV_NAME \
    --resource-group $RG_NAME \
    --query properties.tenantId -o tsv)

    # Create the secret provider for azure key vault
    kubectl apply -f - <<EOF
    apiVersion: secrets-store.csi.x-k8s.io/v1
    kind: SecretProviderClass
    metadata:
    name: eshop-azure-keyvault
    spec:
    provider: azure
    parameters:
    usePodIdentity: "false"
    useVMManagedIdentity: "false"
    clientID: "${MANAGED_IDENTITY_CLIENT_ID}"
    keyvaultName: "${AKV_NAME}"
    cloudName: ""
    objects: |
    array:
    - |
    objectName: mssql-password
    objectType: secret
    objectVersion: ""
    - |
    objectName: mssql-connection-catalog
    objectType: secret
    objectVersion: ""
    - |
    objectName: mssql-connection-identity
    objectType: secret
    objectVersion: ""
    tenantId: "${TENANT_ID}"
    secretObjects:
    - secretName: eshop-secrets
    type: Opaque
    data:
    - objectName: mssql-password
    key: mssql-password
    - objectName: mssql-connection-catalog
    key: mssql-connection-catalog
    - objectName: mssql-connection-identity
    key: mssql-connection-identity
    EOF

    Finally, lets grant the Azure Managed Identity permissions to retrieve secrets from the Azure Key Vault.

    az keyvault set-policy \
    --name $AKV_NAME \
    --secret-permissions get \
    --spn $MANAGED_IDENTITY_CLIENT_ID

    Re-package deployments

    Update your database deployment to load environment variables from our ConfigMap, attach the PVC and SecretProviderClass as volumes, mount the volumes into the Pod, and use the ServiceAccount to retrieve secrets.

    Additionally, you may notice the database Pod is set to use fsGroup:10001 as part of the securityContext. This is required as the MSSQL container runs using a non-root account called mssql and this account has the proper permissions to read/write data at the /var/opt/mssql mount path.

    kubectl apply -f - <<EOF
    apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: db
    labels:
    app: db
    spec:
    replicas: 1
    selector:
    matchLabels:
    app: db
    template:
    metadata:
    labels:
    app: db
    spec:
    securityContext:
    fsGroup: 10001
    serviceAccountName: ${SERVICE_ACCOUNT_NAME}
    containers:
    - name: db
    image: mcr.microsoft.com/mssql/server:2019-latest
    ports:
    - containerPort: 1433
    envFrom:
    - configMapRef:
    name: mssql-settings
    env:
    - name: MSSQL_SA_PASSWORD
    valueFrom:
    secretKeyRef:
    name: eshop-secrets
    key: mssql-password
    resources: {}
    volumeMounts:
    - name: mssqldb
    mountPath: /var/opt/mssql
    - name: eshop-secrets
    mountPath: "/mnt/secrets-store"
    readOnly: true
    volumes:
    - name: mssqldb
    persistentVolumeClaim:
    claimName: mssql-data
    - name: eshop-secrets
    csi:
    driver: secrets-store.csi.k8s.io
    readOnly: true
    volumeAttributes:
    secretProviderClass: eshop-azure-keyvault
    EOF

    We'll update the API and Web deployments in a similar way.

    # Set the image tag
    IMAGE_TAG=<YOUR_IMAGE_TAG>

    # API deployment
    kubectl apply -f - <<EOF
    apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: api
    labels:
    app: api
    spec:
    replicas: 1
    selector:
    matchLabels:
    app: api
    template:
    metadata:
    labels:
    app: api
    spec:
    serviceAccount: ${SERVICE_ACCOUNT_NAME}
    containers:
    - name: api
    image: ${ACR_NAME}.azurecr.io/api:${IMAGE_TAG}
    ports:
    - containerPort: 80
    envFrom:
    - configMapRef:
    name: aspnet-settings
    env:
    - name: ConnectionStrings__CatalogConnection
    valueFrom:
    secretKeyRef:
    name: eshop-secrets
    key: mssql-connection-catalog
    - name: ConnectionStrings__IdentityConnection
    valueFrom:
    secretKeyRef:
    name: eshop-secrets
    key: mssql-connection-identity
    resources: {}
    volumeMounts:
    - name: aspnet
    mountPath: ~/.aspnet/https:/root/.aspnet/https:ro
    - name: eshop-secrets
    mountPath: "/mnt/secrets-store"
    readOnly: true
    volumes:
    - name: aspnet
    persistentVolumeClaim:
    claimName: aspnet-data
    - name: eshop-secrets
    csi:
    driver: secrets-store.csi.k8s.io
    readOnly: true
    volumeAttributes:
    secretProviderClass: eshop-azure-keyvault
    EOF

    ## Web deployment
    kubectl apply -f - <<EOF
    apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: web
    labels:
    app: web
    spec:
    replicas: 1
    selector:
    matchLabels:
    app: web
    template:
    metadata:
    labels:
    app: web
    spec:
    serviceAccount: ${SERVICE_ACCOUNT_NAME}
    containers:
    - name: web
    image: ${ACR_NAME}.azurecr.io/web:${IMAGE_TAG}
    ports:
    - containerPort: 80
    envFrom:
    - configMapRef:
    name: aspnet-settings
    env:
    - name: ConnectionStrings__CatalogConnection
    valueFrom:
    secretKeyRef:
    name: eshop-secrets
    key: mssql-connection-catalog
    - name: ConnectionStrings__IdentityConnection
    valueFrom:
    secretKeyRef:
    name: eshop-secrets
    key: mssql-connection-identity
    resources: {}
    volumeMounts:
    - name: aspnet
    mountPath: ~/.aspnet/https:/root/.aspnet/https:ro
    - name: eshop-secrets
    mountPath: "/mnt/secrets-store"
    readOnly: true
    volumes:
    - name: aspnet
    persistentVolumeClaim:
    claimName: aspnet-data
    - name: eshop-secrets
    csi:
    driver: secrets-store.csi.k8s.io
    readOnly: true
    volumeAttributes:
    secretProviderClass: eshop-azure-keyvault
    EOF

    If all went well with your deployment updates, you should be able to browse to your website and buy some merchandise again 🥳

    echo "http://$(kubectl get service web -o jsonpath='{.status.loadBalancer.ingress[0].ip}')"

    Conclusion

    Although there is no visible changes on with our website, we've made a ton of changes on the Kubernetes backend to make this application much more secure and resilient.

    We used a combination of Kubernetes resources and AKS-specific features to achieve our goal of securing our secrets and ensuring data is not lost on container crashes and restarts.

    To learn more about the components we leveraged here today, checkout the resources and additional tutorials listed below.

    You can also find manifests with all the changes made in today's post in the Azure-Samples/eShopOnAKS repository.

    See you in the next post!

    Resources

    Take the Cloud Skills Challenge!

    Enroll in the Cloud Skills Challenge!

    Don't miss out on this opportunity to level up your skills and stay ahead of the curve in the world of cloud native.

    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/cloud-native-new-year/page/4/index.html b/cnny-2023/tags/cloud-native-new-year/page/4/index.html index f5fe9f856a..93387be544 100644 --- a/cnny-2023/tags/cloud-native-new-year/page/4/index.html +++ b/cnny-2023/tags/cloud-native-new-year/page/4/index.html @@ -14,13 +14,13 @@ - - + +

    5 posts tagged with "cloud-native-new-year"

    View All Tags

    · 10 min read
    Paul Yu

    Welcome to Day 3 of Week 3 of #CloudNativeNewYear!

    The theme for this week is Bringing Your Application to Kubernetes. Yesterday we added configuration, secrets, and storage to our app. Today we'll explore how to expose the eShopOnWeb app so that customers can reach it over the internet using a custom domain name and TLS.

    Ask the Experts Thursday, February 9th at 9 AM PST
    Friday, February 10th at 11 AM PST

    Watch the recorded demo and conversation about this week's topics.

    We were live on YouTube walking through today's (and the rest of this week's) demos. Join us Friday, February 10th and bring your questions!

    What We'll Cover

    • Gather requirements
    • Generate TLS certificate and store in Azure Key Vault
    • Implement custom DNS using Azure DNS
    • Enable Web Application Routing add-on for AKS
    • Implement Ingress for the web application
    • Conclusion
    • Resources

    Gather requirements

    Currently, our eShopOnWeb app has three Kubernetes services deployed:

    1. db exposed internally via ClusterIP
    2. api exposed externally via LoadBalancer
    3. web exposed externally via LoadBalancer

    As mentioned in my post last week, Services allow applications to communicate with each other using DNS names. Kubernetes has service discovery capabilities built-in that allows Pods to resolve Services simply by using their names.

    In the case of our api and web deployments, they can simply reach the database by calling its name. The service type of ClusterIP for the db can remain as-is since it only needs to be accessed by the api and web apps.

    On the other hand, api and web both need to be accessed over the public internet. Currently, these services are using service type LoadBalancer which tells AKS to provision an Azure Load Balancer with a public IP address. No one is going to remember the IP addresses, so we need to make the app more accessible by adding a custom domain name and securing it with a TLS certificate.

    Here's what we're going to need:

    • Custom domain name for our app
    • TLS certificate for the custom domain name
    • Routing rule to ensure requests with /api/ in the URL is routed to the backend REST API
    • Routing rule to ensure requests without /api/ in the URL is routing to the web UI

    Just like last week, we will use the Web Application Routing add-on for AKS. But this time, we'll integrate it with Azure DNS and Azure Key Vault to satisfy all of our requirements above.

    info

    At the time of this writing the add-on is still in Public Preview

    Generate TLS certificate and store in Azure Key Vault

    We deployed an Azure Key Vault yesterday to store secrets. We'll use it again to store a TLS certificate too.

    Let's create and export a self-signed certificate for the custom domain.

    DNS_NAME=eshoponweb$RANDOM.com
    openssl req -new -x509 -nodes -out web-tls.crt -keyout web-tls.key -subj "/CN=${DNS_NAME}" -addext "subjectAltName=DNS:${DNS_NAME}"
    openssl pkcs12 -export -in web-tls.crt -inkey web-tls.key -out web-tls.pfx -password pass:
    info

    For learning purposes we'll use a self-signed certificate and a fake custom domain name.

    To browse to the site using the fake domain, we'll mimic a DNS lookup by adding an entry to your host file which maps the public IP address assigned to the ingress controller to the custom domain.

    In a production scenario, you will need to have a real domain delegated to Azure DNS and a valid TLS certificate for the domain.

    Grab your Azure Key Vault name and set the value in a variable for later use.

    RESOURCE_GROUP=cnny-week3

    AKV_NAME=$(az resource list \
    --resource-group $RESOURCE_GROUP \
    --resource-type Microsoft.KeyVault/vaults \
    --query "[0].name" -o tsv)

    Grant yourself permissions to get, list, and import certificates.

    MY_USER_NAME=$(az account show --query user.name -o tsv)
    MY_USER_OBJECT_ID=$(az ad user show --id $MY_USER_NAME --query id -o tsv)

    az keyvault set-policy \
    --name $AKV_NAME \
    --object-id $MY_USER_OBJECT_ID \
    --certificate-permissions get list import

    Upload the TLS certificate to Azure Key Vault and grab its certificate URI.

    WEB_TLS_CERT_ID=$(az keyvault certificate import \
    --vault-name $AKV_NAME \
    --name web-tls \
    --file web-tls.pfx \
    --query id \
    --output tsv)

    Implement custom DNS with Azure DNS

    Create a custom domain for our application and grab its Azure resource id.

    DNS_ZONE_ID=$(az network dns zone create \
    --name $DNS_NAME \
    --resource-group $RESOURCE_GROUP \
    --query id \
    --output tsv)

    Enable Web Application Routing add-on for AKS

    As we enable the Web Application Routing add-on, we'll also pass in the Azure DNS Zone resource id which triggers the installation of the external-dns controller in your Kubernetes cluster. This controller will be able to write Azure DNS zone entries on your behalf as you deploy Ingress manifests.

    AKS_NAME=$(az resource list \
    --resource-group $RESOURCE_GROUP \
    --resource-type Microsoft.ContainerService/managedClusters \
    --query "[0].name" -o tsv)

    az aks enable-addons \
    --name $AKS_NAME \
    --resource-group $RESOURCE_GROUP \
    --addons web_application_routing \
    --dns-zone-resource-id=$DNS_ZONE_ID \
    --enable-secret-rotation

    The add-on will also deploy a new Azure Managed Identity which is used by the external-dns controller when writing Azure DNS zone entries. Currently, it does not have permission to do that, so let's grant it permission.

    # This is where resources are automatically deployed by AKS
    NODE_RESOURCE_GROUP=$(az aks show \
    --name $AKS_NAME \
    --resource-group $RESOURCE_GROUP \
    --query nodeResourceGroup -o tsv)

    # This is the managed identity created by the Web Application Routing add-on
    MANAGED_IDENTTIY_OBJECT_ID=$(az resource show \
    --name webapprouting-${AKS_NAME} \
    --resource-group $NODE_RESOURCE_GROUP \
    --resource-type Microsoft.ManagedIdentity/userAssignedIdentities \
    --query properties.principalId \
    --output tsv)

    # Grant the managed identity permissions to write DNS entries
    az role assignment create \
    --role "DNS Zone Contributor" \
    --assignee $MANAGED_IDENTTIY_OBJECT_ID \
    --scope $DNS_ZONE_ID

    The Azure Managed Identity will also be used to retrieve and rotate TLS certificates from Azure Key Vault. So we'll need to grant it permission for that too.

    az keyvault set-policy \
    --name $AKV_NAME \
    --object-id $MANAGED_IDENTTIY_OBJECT_ID \
    --secret-permissions get \
    --certificate-permissions get

    Implement Ingress for the web application

    Before we create a new Ingress manifest, let's update the existing services to use ClusterIP instead of LoadBalancer. With an Ingress in place, there is no reason why we need the Service resources to be accessible from outside the cluster. The new Ingress will be the only entrypoint for external users.

    We can use the kubectl patch command to update the services

    kubectl patch service api -p '{"spec": {"type": "ClusterIP"}}'
    kubectl patch service web -p '{"spec": {"type": "ClusterIP"}}'

    Deploy a new Ingress to place in front of the web Service. Notice there is a special annotations entry for kubernetes.azure.com/tls-cert-keyvault-uri which points back to our self-signed certificate that was uploaded to Azure Key Vault.

    kubectl apply -f - <<EOF
    apiVersion: networking.k8s.io/v1
    kind: Ingress
    metadata:
    annotations:
    kubernetes.azure.com/tls-cert-keyvault-uri: ${WEB_TLS_CERT_ID}
    name: web
    spec:
    ingressClassName: webapprouting.kubernetes.azure.com
    rules:
    - host: ${DNS_NAME}
    http:
    paths:
    - backend:
    service:
    name: web
    port:
    number: 80
    path: /
    pathType: Prefix
    - backend:
    service:
    name: api
    port:
    number: 80
    path: /api
    pathType: Prefix
    tls:
    - hosts:
    - ${DNS_NAME}
    secretName: web-tls
    EOF

    In our manifest above, we've also configured the Ingress route the traffic to either the web or api services based on the URL path requested. If the request URL includes /api/ then it will send traffic to the api backend service. Otherwise, it will send traffic to the web service.

    Within a few minutes, the external-dns controller will add an A record to Azure DNS which points to the Ingress resource's public IP. With the custom domain in place, we can simply browse using this domain name.

    info

    As mentioned above, since this is not a real domain name, we need to modify our host file to make it seem like our custom domain is resolving to the Ingress' public IP address.

    To get the ingress public IP, run the following:

    # Get the IP
    kubectl get ingress web -o jsonpath="{.status.loadBalancer.ingress[0].ip}"

    # Get the hostname
    kubectl get ingress web -o jsonpath="{.spec.tls[0].hosts[0]}"

    Next, open your host file and add an entry using the format <YOUR_PUBLIC_IP> <YOUR_CUSTOM_DOMAIN>. Below is an example of what it should look like.

    20.237.116.224 eshoponweb11265.com

    See this doc for more info on how to do this.

    When browsing to the website, you may be presented with a warning about the connection not being private. This is due to the fact that we are using a self-signed certificate. This is expected, so go ahead and proceed anyway to load up the page.

    Why is the Admin page broken?

    If you log in using the admin@microsoft.com account and browse to the Admin page, you'll notice no products are loaded on the page.

    This is because the admin page is built using Blazor and compiled as a WebAssembly application that runs in your browser. When the application was compiled, it packed the appsettings.Development.json file as an embedded resource. This file contains the base URL for the public API and it currently points to https://localhost:5099. Now that we have a domain name, we can update the base URL and point it to our custom domain.

    From the root of the eShopOnWeb repo, update the configuration file using a sed command.

    sed -i -e "s/localhost:5099/${DNS_NAME}/g" ./src/BlazorAdmin/wwwroot/appsettings.Development.json

    Rebuild and push the container to Azure Container Registry.

    # Grab the name of your Azure Container Registry
    ACR_NAME=$(az resource list \
    --resource-group $RESOURCE_GROUP \
    --resource-type Microsoft.ContainerRegistry/registries \
    --query "[0].name" -o tsv)

    # Invoke a build and publish job
    az acr build \
    --registry $ACR_NAME \
    --image $ACR_NAME.azurecr.io/web:v0.1.0 \
    --file ./src/Web/Dockerfile .

    Once the container build has completed, we can issue a kubectl patch command to quickly update the web deployment to test our change.

    kubectl patch deployment web -p "$(cat <<EOF
    {
    "spec": {
    "template": {
    "spec": {
    "containers": [
    {
    "name": "web",
    "image": "${ACR_NAME}.azurecr.io/web:v0.1.0"
    }
    ]
    }
    }
    }
    }
    EOF
    )"

    If all went well, you will be able to browse the admin page again and confirm product data is being loaded 🥳

    Conclusion

    The Web Application Routing add-on for AKS aims to streamline the process of exposing it to the public using the open-source NGINX Ingress Controller. With the add-on being managed by Azure, it natively integrates with other Azure services like Azure DNS and eliminates the need to manually create DNS entries. It can also integrate with Azure Key Vault to automatically pull in TLS certificates and rotate them as needed to further reduce operational overhead.

    We are one step closer to production and in the upcoming posts we'll further operationalize and secure our deployment, so stay tuned!

    In the meantime, check out the resources listed below for further reading.

    You can also find manifests with all the changes made in today's post in the Azure-Samples/eShopOnAKS repository.

    Resources

    Take the Cloud Skills Challenge!

    Enroll in the Cloud Skills Challenge!

    Don't miss out on this opportunity to level up your skills and stay ahead of the curve in the world of cloud native.

    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/cloud-native-new-year/page/5/index.html b/cnny-2023/tags/cloud-native-new-year/page/5/index.html index 4768949741..3b4d68290a 100644 --- a/cnny-2023/tags/cloud-native-new-year/page/5/index.html +++ b/cnny-2023/tags/cloud-native-new-year/page/5/index.html @@ -14,13 +14,13 @@ - - + +

    5 posts tagged with "cloud-native-new-year"

    View All Tags

    · 6 min read
    Josh Duffney

    Welcome to Day 5 of Week 3 of #CloudNativeNewYear!

    The theme for this week is Bringing Your Application to Kubernetes. Yesterday we talked about debugging and instrumenting our application. Today we'll explore the topic of container image signing and secure supply chain.

    Ask the Experts Thursday, February 9th at 9 AM PST
    Friday, February 10th at 11 AM PST

    Watch the recorded demo and conversation about this week's topics.

    We were live on YouTube walking through today's (and the rest of this week's) demos. Join us Friday, February 10th and bring your questions!

    What We'll Cover

    • Introduction
    • Prerequisites
    • Create a digital signing certificate
    • Generate an Azure Container Registry Token
    • Set up Notation
    • Install the Notation Azure Key Vault Plugin
    • Add the signing Certificate to Notation
    • Sign Container Images
    • Conclusion

    Introduction

    The secure supply chain is a crucial aspect of software development, delivery, and deployment, and digital signing plays a critical role in this process.

    By using digital signatures to verify the authenticity and integrity of container images, organizations can improve the security of your software supply chain and reduce the risk of security breaches and data compromise.

    In this article, you'll learn how to use Notary, an open-source project hosted by the Cloud Native Computing Foundation (CNCF) to digitally sign container images stored on Azure Container Registry.

    Prerequisites

    To follow along, you'll need an instance of:

    Create a digital signing certificate

    A digital signing certificate is a certificate that is used to digitally sign and verify the authenticity and integrity of digital artifacts. Such documents, software, and of course container images.

    Before you can implement digital signatures, you must first create a digital signing certificate.

    Run the following command to generate the certificate:

    1. Create the policy file

      cat <<EOF > ./my_policy.json
      {
      "issuerParameters": {
      "certificateTransparency": null,
      "name": "Self"
      },
      "x509CertificateProperties": {
      "ekus": [
      "1.3.6.1.5.5.7.3.3"
      ],
      "key_usage": [
      "digitalSignature"
      ],
      "subject": "CN=${keySubjectName}",
      "validityInMonths": 12
      }
      }
      EOF

      The ekus and key usage of this certificate policy dictate that the certificate can only be used for digital signatures.

    2. Create the certificate in Azure Key Vault

      az keyvault certificate create --name $keyName --vault-name $keyVaultName --policy @my_policy.json

      Replace $keyName and $keyVaultName with your desired certificate name and Azure Key Vault instance name.

    Generate a Azure Container Registry token

    Azure Container Registry tokens are used to grant access to the contents of the registry. Tokens can be used for a variety of things such as pulling images, pushing images, or managing the registry.

    As part of the container image signing workflow, you'll need a token to authenticate the Notation CLI with your Azure Container Registry.

    Run the following command to generate an ACR token:

    az acr token create \
    --name $tokenName \
    --registry $registry \
    --scope-map _repositories_admin \
    --query 'credentials.passwords[0].value' \
    --only-show-errors \
    --output tsv

    Replace $tokenName with your name for the ACR token and $registry with the name of your ACR instance.

    Setup Notation

    Notation is the command-line interface for the CNCF Notary project. You'll use it to digitally sign the api and web container images for the eShopOnWeb application.

    Run the following commands to download and install the NotationCli:

    1. Open a terminal or command prompt window

    2. Download the Notary notation release

      curl -Lo notation.tar.gz https://github.com/notaryproject/notation/releases/download/v1.0.0-rc.1/notation_1.0.0-rc.1_linux_amd64.tar.gz > /dev/null 2>&1

      If you're not using Linux, you can find the releases here.

    3. Extract the contents of the notation.tar.gz

      tar xvzf notation.tar.gz > /dev/null 2>&1
    4. Copy the notation binary to the $HOME/bin directory

      cp ./notation $HOME/bin
    5. Add the $HOME/bin directory to the PATH environment variable

      export PATH="$HOME/bin:$PATH"
    6. Remove the downloaded files

      rm notation.tar.gz LICENSE
    7. Check the notation version

      notation --version

    Install the Notation Azure Key Vault plugin

    By design the NotationCli supports plugins that extend its digital signing capabilities to remote registries. And in order to sign your container images stored in Azure Container Registry, you'll need to install the Azure Key Vault plugin for Notation.

    Run the following commands to install the azure-kv plugin:

    1. Download the plugin

      curl -Lo notation-azure-kv.tar.gz \
      https://github.com/Azure/notation-azure-kv/releases/download/v0.5.0-rc.1/notation-azure-kv_0.5.0-rc.1_linux_amd64.tar.gz > /dev/null 2>&1

      Non-Linux releases can be found here.

    2. Extract to the plugin directory & delete download files

      tar xvzf notation-azure-kv.tar.gz -C ~/.config/notation/plugins/azure-kv notation-azure-kv > /dev/null 2>&

      rm -rf notation-azure-kv.tar.gz
    3. Verify the plugin was installed

      notation plugin ls

    Add the signing certificate to Notation

    Now that you have Notation and the Azure Key Vault plugin installed, add the certificate's keyId created above to Notation.

    1. Get the Certificate Key ID from Azure Key Vault

      az keyvault certificate show \
      --vault-name $keyVaultName \
      --name $keyName \
      --query "kid" --only-show-errors --output tsv

      Replace $keyVaultName and $keyName with the appropriate information.

    2. Add the Key ID to KMS using Notation

      notation key add --plugin azure-kv --id $keyID $keyName
    3. Check the key list

      notation key ls

    Sign Container Images

    At this point, all that's left is to sign the container images.

    Run the notation sign command to sign the api and web container images:

    notation sign $registry.azurecr.io/web:$tag \
    --username $tokenName \
    --password $tokenPassword

    notation sign $registry.azurecr.io/api:$tag \
    --username $tokenName \
    --password $tokenPassword

    Replace $registry, $tag, $tokenName, and $tokenPassword with the appropriate values. To improve security, use a SHA hash for the tag.

    NOTE: If you didn't take note of the token password, you can rerun the az acr token create command to generate a new password.

    Conclusion

    Digital signing plays a critical role in ensuring the security of software supply chains.

    By signing software components, organizations can verify the authenticity and integrity of software, helping to prevent unauthorized modifications, tampering, and malware.

    And if you want to take digital signing to a whole new level by using them to prevent the deployment of unsigned container images, check out the Ratify project on GitHub!

    Take the Cloud Skills Challenge!

    Enroll in the Cloud Skills Challenge!

    Don't miss out on this opportunity to level up your skills and stay ahead of the curve in the world of cloud native.

    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/cloud-native/index.html b/cnny-2023/tags/cloud-native/index.html index 2a773ab310..00445375cd 100644 --- a/cnny-2023/tags/cloud-native/index.html +++ b/cnny-2023/tags/cloud-native/index.html @@ -14,13 +14,13 @@ - - + +

    16 posts tagged with "cloud-native"

    View All Tags

    · 4 min read
    Cory Skimming
    Devanshi Joshi
    Steven Murawski
    Nitya Narasimhan

    Welcome to the Kick-off Post for #30DaysOfCloudNative - one of the core initiatives within #CloudNativeNewYear! Over the next four weeks, join us as we take you from fundamentals to functional usage of Cloud-native technologies, one blog post at a time! Read on to learn a little bit about this initiative and what you can expect to learn from this journey!

    What We'll Cover


    Cloud-native New Year

    Welcome to Week 01 of 🥳 #CloudNativeNewYear ! Today, we kick off a full month of content and activities to skill you up on all things Cloud-native on Azure with content, events, and community interactions! Read on to learn about what we have planned!


    Explore our initiatives

    We have a number of initiatives planned for the month to help you learn and skill up on relevant technologies. Click on the links to visit the relevant pages for each.

    We'll go into more details about #30DaysOfCloudNative in this post - don't forget to subscribe to the blog to get daily posts delivered directly to your preferred feed reader!


    Register for events!

    What are 3 things you can do today, to jumpstart your learning journey?


    #30DaysOfCloudNative

    #30DaysOfCloudNative is a month-long series of daily blog posts grouped into 4 themed weeks - taking you from core concepts to end-to-end solution examples in 30 days. Each article will be short (5-8 mins reading time) and provide exercises and resources to help you reinforce learnings and take next steps.

    This series focuses on the Cloud-native On Azure learning journey in four stages, each building on the previous week to help you skill up in a beginner-friendly way:

    We have a tentative weekly-themed roadmap for the topics we hope to cover and will keep this updated as we go with links to actual articles as they get published.

    Week 1: FOCUS ON CLOUD-NATIVE FUNDAMENTALS

    Here's a sneak peek at the week 1 schedule. We'll start with a broad review of cloud-native fundamentals and walkthrough the core concepts of microservices, containers and Kubernetes.

    • Jan 23: Learn Core Concepts for Cloud-native
    • Jan 24: Container 101
    • Jan 25: Adopting Microservices with Kubernetes
    • Jan 26: Kubernetes 101
    • Jan 27: Exploring your Cloud Native Options

    Let's Get Started!

    Now you know everything! We hope you are as excited as we are to dive into a full month of active learning and doing! Don't forget to subscribe for updates in your favorite feed reader! And look out for our first Cloud-native Fundamentals post on January 23rd!


    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/cloud-native/page/10/index.html b/cnny-2023/tags/cloud-native/page/10/index.html index 1ece72d414..d51364cd47 100644 --- a/cnny-2023/tags/cloud-native/page/10/index.html +++ b/cnny-2023/tags/cloud-native/page/10/index.html @@ -14,15 +14,15 @@ - - + +

    16 posts tagged with "cloud-native"

    View All Tags

    · 14 min read
    Steven Murawski

    Welcome to Day 1 of Week 3 of #CloudNativeNewYear!

    The theme for this week is Bringing Your Application to Kubernetes. Last we talked about Kubernetes Fundamentals. Today we'll explore getting an existing application running in Kubernetes with a full pipeline in GitHub Actions.

    Ask the Experts Thursday, February 9th at 9 AM PST
    Friday, February 10th at 11 AM PST

    Watch the recorded demo and conversation about this week's topics.

    We were live on YouTube walking through today's (and the rest of this week's) demos. Join us Friday, February 10th and bring your questions!

    What We'll Cover

    • Our Application
    • Adding Some Infrastructure as Code
    • Building and Publishing a Container Image
    • Deploying to Kubernetes
    • Summary
    • Resources

    Our Application

    This week we'll be taking an exisiting application - something similar to a typical line of business application - and setting it up to run in Kubernetes. Over the course of the week, we'll address different concerns. Today we'll focus on updating our CI/CD process to handle standing up (or validating that we have) an Azure Kubernetes Service (AKS) environment, building and publishing container images for our web site and API server, and getting those services running in Kubernetes.

    The application we'll be starting with is eShopOnWeb. This application has a web site and API which are backed by a SQL Server instance. It's built in .NET 7, so it's cross-platform.

    info

    For the enterprising among you, you may notice that there are a number of different eShopOn* variants on GitHub, including eShopOnContainers. We aren't using that example as it's more of an end state than a starting place. Afterwards, feel free to check out that example as what this solution could look like as a series of microservices.

    Adding Some Infrastructure as Code

    Just like last week, we need to stand up an AKS environment. This week, however, rather than running commands in our own shell, we'll set up GitHub Actions to do that for us.

    There is a LOT of plumbing in this section, but once it's set up, it'll make our lives a lot easier. This section ensures that we have an environment to deploy our application into configured the way we want. We can easily extend this to accomodate multiple environments or add additional microservices with minimal new effort.

    Federated Identity

    Setting up a federated identity will allow us a more securable and auditable way of accessing Azure from GitHub Actions. For more about setting up a federated identity, Microsoft Learn has the details on connecting GitHub Actions to Azure.

    Here, we'll just walk through the setup of the identity and configure GitHub to use that idenity to deploy our AKS environment and interact with our Azure Container Registry.

    The examples will use PowerShell, but a Bash version of the setup commands is available in the week3/day1 branch.

    Prerequisites

    To follow along, you'll need:

    • a GitHub account
    • an Azure Subscription
    • the Azure CLI
    • and the Git CLI.

    You'll need to fork the source repository under your GitHub user or organization where you can manage secrets and GitHub Actions.

    It would be helpful to have the GitHub CLI, but it's not required.

    Set Up Some Defaults

    You will need to update one or more of the variables (your user or organization, what branch you want to work off of, and possibly the Azure AD application name if there is a conflict).

    # Replace the gitHubOrganizationName value
    # with the user or organization you forked
    # the repository under.

    $githubOrganizationName = 'Azure-Samples'
    $githubRepositoryName = 'eShopOnAKS'
    $branchName = 'week3/day1'
    $applicationName = 'cnny-week3-day1'

    Create an Azure AD Application

    Next, we need to create an Azure AD application.

    # Create an Azure AD application
    $aksDeploymentApplication = New-AzADApplication -DisplayName $applicationName

    Set Up Federation for that Azure AD Application

    And configure that application to allow federated credential requests from our GitHub repository for a particular branch.

    # Create a federated identity credential for the application
    New-AzADAppFederatedCredential `
    -Name $applicationName `
    -ApplicationObjectId $aksDeploymentApplication.Id `
    -Issuer 'https://token.actions.githubusercontent.com' `
    -Audience 'api://AzureADTokenExchange' `
    -Subject "repo:$($githubOrganizationName)/$($githubRepositoryName):ref:refs/heads/$branchName"

    Create a Service Principal for the Azure AD Application

    Once the application has been created, we need a service principal tied to that application. The service principal can be granted rights in Azure.

    # Create a service principal for the application
    New-AzADServicePrincipal -AppId $($aksDeploymentApplication.AppId)

    Give that Service Principal Rights to Azure Resources

    Because our Bicep deployment exists at the subscription level and we are creating role assignments, we need to give it Owner rights. If we changed the scope of the deployment to just a resource group, we could apply more scoped permissions.

    $azureContext = Get-AzContext
    New-AzRoleAssignment `
    -ApplicationId $($aksDeploymentApplication.AppId) `
    -RoleDefinitionName Owner `
    -Scope $azureContext.Subscription.Id

    Add Secrets to GitHub Repository

    If you have the GitHub CLI, you can use that right from your shell to set the secrets needed.

    gh secret set AZURE_CLIENT_ID --body $aksDeploymentApplication.AppId
    gh secret set AZURE_TENANT_ID --body $azureContext.Tenant.Id
    gh secret set AZURE_SUBSCRIPTION_ID --body $azureContext.Subscription.Id

    Otherwise, you can create them through the web interface like I did in the Learn Live event below.

    info

    It may look like the whole video will play, but it'll stop after configuring the secrets in GitHub (after about 9 minutes)

    The video shows creating the Azure AD application, service principals, and configuring the federated identity in Azure AD and GitHub.

    Creating a Bicep Deployment

    Resuable Workflows

    We'll create our Bicep deployment in a reusable workflows. What are they? The previous link has the documentation or the video below has my colleague Brandon Martinez and I talking about them.

    This workflow is basically the same deployment we did in last week's series, just in GitHub Actions.

    Start by creating a file called deploy_aks.yml in the .github/workflows directory with the below contents.

    name: deploy

    on:
    workflow_call:
    inputs:
    resourceGroupName:
    required: true
    type: string
    secrets:
    AZURE_CLIENT_ID:
    required: true
    AZURE_TENANT_ID:
    required: true
    AZURE_SUBSCRIPTION_ID:
    required: true
    outputs:
    containerRegistryName:
    description: Container Registry Name
    value: ${{ jobs.deploy.outputs.containerRegistryName }}
    containerRegistryUrl:
    description: Container Registry Login Url
    value: ${{ jobs.deploy.outputs.containerRegistryUrl }}
    resourceGroupName:
    description: Resource Group Name
    value: ${{ jobs.deploy.outputs.resourceGroupName }}
    aksName:
    description: Azure Kubernetes Service Cluster Name
    value: ${{ jobs.deploy.outputs.aksName }}

    permissions:
    id-token: write
    contents: read

    jobs:
    validate:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    - uses: azure/login@v1
    name: Sign in to Azure
    with:
    client-id: ${{ secrets.AZURE_CLIENT_ID }}
    tenant-id: ${{ secrets.AZURE_TENANT_ID }}
    subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
    - uses: azure/arm-deploy@v1
    name: Run preflight validation
    with:
    deploymentName: ${{ github.run_number }}
    scope: subscription
    region: eastus
    template: ./deploy/main.bicep
    parameters: >
    resourceGroup=${{ inputs.resourceGroupName }}
    deploymentMode: Validate

    deploy:
    needs: validate
    runs-on: ubuntu-latest
    outputs:
    containerRegistryName: ${{ steps.deploy.outputs.acr_name }}
    containerRegistryUrl: ${{ steps.deploy.outputs.acr_login_server_url }}
    resourceGroupName: ${{ steps.deploy.outputs.resource_group_name }}
    aksName: ${{ steps.deploy.outputs.aks_name }}
    steps:
    - uses: actions/checkout@v2
    - uses: azure/login@v1
    name: Sign in to Azure
    with:
    client-id: ${{ secrets.AZURE_CLIENT_ID }}
    tenant-id: ${{ secrets.AZURE_TENANT_ID }}
    subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
    - uses: azure/arm-deploy@v1
    id: deploy
    name: Deploy Bicep file
    with:
    failOnStdErr: false
    deploymentName: ${{ github.run_number }}
    scope: subscription
    region: eastus
    template: ./deploy/main.bicep
    parameters: >
    resourceGroup=${{ inputs.resourceGroupName }}

    Adding the Bicep Deployment

    Once we have the Bicep deployment workflow, we can add it to the primary build definition in .github/workflows/dotnetcore.yml

    Permissions

    First, we need to add a permissions block to let the workflow request our Azure AD token. This can go towards the top of the YAML file (I started it on line 5).

    permissions:
    id-token: write
    contents: read

    Deploy AKS Job

    Next, we'll add a reference to our reusable workflow. This will go after the build job.

      deploy_aks:
    needs: [build]
    uses: ./.github/workflows/deploy_aks.yml
    with:
    resourceGroupName: 'cnny-week3'
    secrets:
    AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
    AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
    AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }}

    Building and Publishing a Container Image

    Now that we have our target environment in place and an Azure Container Registry, we can build and publish our container images.

    Add a Reusable Workflow

    First, we'll create a new file for our reusable workflow at .github/workflows/publish_container_image.yml.

    We'll start the file with a name, the parameters it needs to run, and the permissions requirements for the federated identity request.

    name: Publish Container Images

    on:
    workflow_call:
    inputs:
    containerRegistryName:
    required: true
    type: string
    containerRegistryUrl:
    required: true
    type: string
    githubSha:
    required: true
    type: string
    secrets:
    AZURE_CLIENT_ID:
    required: true
    AZURE_TENANT_ID:
    required: true
    AZURE_SUBSCRIPTION_ID:
    required: true

    permissions:
    id-token: write
    contents: read

    Build the Container Images

    Our next step is to build the two container images we'll need for the application, the website and the API. We'll build the container images on our build worker and tag it with the git SHA, so there'll be a direct tie between the point in time in our codebase and the container images that represent it.

    jobs:
    publish_container_image:
    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v2
    - name: docker build
    run: |
    docker build . -f src/Web/Dockerfile -t ${{ inputs.containerRegistryUrl }}/web:${{ inputs.githubSha }}
    docker build . -f src/PublicApi/Dockerfile -t ${{ inputs.containerRegistryUrl }}/api:${{ inputs.githubSha}}

    Scan the Container Images

    Before we publish those container images, we'll scan them for vulnerabilities and best practice violations. We can add these two steps (one scan for each image).

        - name: scan web container image
    uses: Azure/container-scan@v0
    with:
    image-name: ${{ inputs.containerRegistryUrl }}/web:${{ inputs.githubSha}}
    - name: scan api container image
    uses: Azure/container-scan@v0
    with:
    image-name: ${{ inputs.containerRegistryUrl }}/web:${{ inputs.githubSha}}

    The container images provided have a few items that'll be found. We can create an allowed list at .github/containerscan/allowedlist.yaml to define vulnerabilities or best practice violations that we'll explictly allow to not fail our build.

    general:
    vulnerabilities:
    - CVE-2022-29458
    - CVE-2022-3715
    - CVE-2022-1304
    - CVE-2021-33560
    - CVE-2020-16156
    - CVE-2019-8457
    - CVE-2018-8292
    bestPracticeViolations:
    - CIS-DI-0001
    - CIS-DI-0005
    - CIS-DI-0006
    - CIS-DI-0008

    Publish the Container Images

    Finally, we'll log in to Azure, then log in to our Azure Container Registry, and push our images.

        - uses: azure/login@v1
    name: Sign in to Azure
    with:
    client-id: ${{ secrets.AZURE_CLIENT_ID }}
    tenant-id: ${{ secrets.AZURE_TENANT_ID }}
    subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
    - name: acr login
    run: az acr login --name ${{ inputs.containerRegistryName }}
    - name: docker push
    run: |
    docker push ${{ inputs.containerRegistryUrl }}/web:${{ inputs.githubSha}}
    docker push ${{ inputs.containerRegistryUrl }}/api:${{ inputs.githubSha}}

    Update the Build With the Image Build and Publish

    Now that we have our reusable workflow to create and publish our container images, we can include that in our primary build defnition at .github/workflows/dotnetcore.yml.

      publish_container_image:
    needs: [deploy_aks]
    uses: ./.github/workflows/publish_container_image.yml
    with:
    containerRegistryName: ${{ needs.deploy_aks.outputs.containerRegistryName }}
    containerRegistryUrl: ${{ needs.deploy_aks.outputs.containerRegistryUrl }}
    githubSha: ${{ github.sha }}
    secrets:
    AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
    AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
    AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }}

    Deploying to Kubernetes

    Finally, we've gotten enough set up that a commit to the target branch will:

    • build and test our application code
    • set up (or validate) our AKS and ACR environment
    • and create, scan, and publish our container images to ACR

    Our last step will be to deploy our application to Kubernetes. We'll use the basic building blocks we worked with last week, deployments and services.

    Starting the Reusable Workflow to Deploy to AKS

    We'll start our workflow with our parameters that we need, as well as the permissions to access the token to log in to Azure.

    We'll check out our code, then log in to Azure, and use the az CLI to get credentials for our AKS cluster.

    name: deploy_to_aks

    on:
    workflow_call:
    inputs:
    aksName:
    required: true
    type: string
    resourceGroupName:
    required: true
    type: string
    containerRegistryUrl:
    required: true
    type: string
    githubSha:
    required: true
    type: string
    secrets:
    AZURE_CLIENT_ID:
    required: true
    AZURE_TENANT_ID:
    required: true
    AZURE_SUBSCRIPTION_ID:
    required: true

    permissions:
    id-token: write
    contents: read

    jobs:
    deploy:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    - uses: azure/login@v1
    name: Sign in to Azure
    with:
    client-id: ${{ secrets.AZURE_CLIENT_ID }}
    tenant-id: ${{ secrets.AZURE_TENANT_ID }}
    subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
    - name: Get AKS Credentials
    run: |
    az aks get-credentials --resource-group ${{ inputs.resourceGroupName }} --name ${{ inputs.aksName }}

    Edit the Deployment For Our Current Image Tag

    Let's add the Kubernetes manifests to our repo. This post is long enough, so you can find the content for the manifests folder in the manifests folder in the source repo under the week3/day1 branch.

    tip

    If you only forked the main branch of the source repo, you can easily get the updated manifests by using the following git commands:

    git remote add upstream https://github.com/Azure-Samples/eShopOnAks
    git fetch upstream week3/day1
    git checkout upstream/week3/day1 manifests

    This will make the week3/day1 branch available locally and then we can update the manifests directory to match the state of that branch.

    The deployments and the service defintions should be familiar from last week's content (but not the same). This week, however, there's a new file in the manifests - ./manifests/kustomization.yaml

    This file helps us more dynamically edit our kubernetes manifests and support is baked right in to the kubectl command.

    Kustomize Definition

    Kustomize allows us to specify specific resource manifests and areas of that manifest to replace. We've put some placeholders in our file as well, so we can replace those for each run of our CI/CD system.

    In ./manifests/kustomization.yaml you will see:

    resources:
    - deployment-api.yaml
    - deployment-web.yaml

    # Change the image name and version
    images:
    - name: notavalidregistry.azurecr.io/api:v0.1.0
    newName: <YOUR_ACR_SERVER>/api
    newTag: <YOUR_IMAGE_TAG>
    - name: notavalidregistry.azurecr.io/web:v0.1.0
    newName: <YOUR_ACR_SERVER>/web
    newTag: <YOUR_IMAGE_TAG>

    Replacing Values in our Build

    Now, we encounter a little problem - our deployment files need to know the tag and ACR server. We can do a bit of sed magic to edit the file on the fly.

    In .github/workflows/deploy_to_aks.yml, we'll add:

          - name: replace_placeholders_with_current_run
    run: |
    sed -i "s/<YOUR_ACR_SERVER>/${{ inputs.containerRegistryUrl }}/g" ./manifests/kustomization.yaml
    sed -i "s/<YOUR_IMAGE_TAG>/${{ inputs.githubSha }}/g" ./manifests/kustomization.yaml

    Deploying the Manifests

    We have our manifests in place and our kustomization.yaml file (with commands to update it at runtime) ready to go, we can deploy our manifests.

    First, we'll deploy our database (deployment and service). Next, we'll use the -k parameter on kubectl to tell it to look for a kustomize configuration, transform the requested manifests and apply those. Finally, we apply the service defintions for the web and API deployments.

            run: |
    kubectl apply -f ./manifests/deployment-db.yaml \
    -f ./manifests/service-db.yaml
    kubectl apply -k ./manifests
    kubectl apply -f ./manifests/service-api.yaml \
    -f ./manifests/service-web.yaml

    Summary

    We've covered a lot of ground in today's post. We set up federated credentials with GitHub. Then we added reusable workflows to deploy an AKS environment and build/scan/publish our container images, and then to deploy them into our AKS environment.

    This sets us up to start making changes to our application and Kubernetes configuration and have those changes automatically validated and deployed by our CI/CD system. Tomorrow, we'll look at updating our application environment with runtime configuration, persistent storage, and more.

    Resources

    Take the Cloud Skills Challenge!

    Enroll in the Cloud Skills Challenge!

    Don't miss out on this opportunity to level up your skills and stay ahead of the curve in the world of cloud native.

    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/cloud-native/page/11/index.html b/cnny-2023/tags/cloud-native/page/11/index.html index 67207b635a..f4e65e17bd 100644 --- a/cnny-2023/tags/cloud-native/page/11/index.html +++ b/cnny-2023/tags/cloud-native/page/11/index.html @@ -14,13 +14,13 @@ - - + +

    16 posts tagged with "cloud-native"

    View All Tags

    · 9 min read
    Steven Murawski

    Welcome to Day 4 of Week 3 of #CloudNativeNewYear!

    The theme for this week is Bringing Your Application to Kubernetes. Yesterday we exposed the eShopOnWeb app so that customers can reach it over the internet using a custom domain name and TLS. Today we'll explore the topic of debugging and instrumentation.

    Ask the Experts Thursday, February 9th at 9 AM PST
    Friday, February 10th at 11 AM PST

    Watch the recorded demo and conversation about this week's topics.

    We were live on YouTube walking through today's (and the rest of this week's) demos. Join us Friday, February 10th and bring your questions!

    What We'll Cover

    • Debugging
    • Bridge To Kubernetes
    • Instrumentation
    • Resources: For self-study!

    Debugging

    Debugging applications in a Kubernetes cluster can be challenging for several reasons:

    • Complexity: Kubernetes is a complex system with many moving parts, including pods, nodes, services, and config maps, all of which can interact in unexpected ways and cause issues.
    • Distributed Environment: Applications running in a Kubernetes cluster are often distributed across multiple nodes, which makes it harder to determine the root cause of an issue.
    • Logging and Monitoring: Debugging an application in a Kubernetes cluster requires access to logs and performance metrics, which can be difficult to obtain in a large and dynamic environment.
    • Resource Management: Kubernetes manages resources such as CPU and memory, which can impact the performance and behavior of applications. Debugging resource-related issues requires a deep understanding of the Kubernetes resource model and the underlying infrastructure.
    • Dynamic Nature: Kubernetes is designed to be dynamic, with the ability to add and remove resources as needed. This dynamic nature can make it difficult to reproduce issues and debug problems.

    However, there are many tools and practices that can help make debugging applications in a Kubernetes cluster easier, such as using centralized logging, monitoring, and tracing solutions, and following best practices for managing resources and deployment configurations.

    There's also another great tool in our toolbox - Bridge to Kubernetes.

    Bridge to Kubernetes

    Bridge to Kubernetes is a great tool for microservice development and debugging applications without having to locally replicate all the required microservices.

    Bridge to Kubernetes works with Visual Studio or Visual Studio Code.

    We'll walk through using it with Visual Studio Code.

    Connecting Bridge to Kubernetes to Our Cluster

    Ensure your AKS cluster is the default for kubectl

    If you've recently spun up a new AKS cluster or you have been working with a different cluster, you may need to change what cluster credentials you have configured.

    If it's a new cluster, we can use:

    RESOURCE_GROUP=<YOUR RESOURCE GROUP NAME>
    CLUSTER_NAME=<YOUR AKS CLUSTER NAME>
    az aks get-credentials az aks get-credentials --resource-group $RESOURCE_GROUP --name $CLUSTER_NAME

    Open the command palette

    Open the command palette and find Bridge to Kubernetes: Configure. You may need to start typing the name to get it to show up.

    The command palette for Visual Studio Code is open and the first item is Bridge to Kubernetes: Configure

    Pick the service you want to debug

    Bridge to Kubernetes will redirect a service for you. Pick the service you want to redirect, in this case we'll pick web.

    Selecting the `web` service to redirect in Visual Studio Code

    Identify the port your application runs on

    Next, we'll be prompted to identify what port our application will run on locally. For this application it'll be 5001, but that's just specific to this application (and the default for ASP.NET 7, I believe).

    Setting port 5001 as the port to redirect to the `web` Kubernetes service in Visual Studio Code

    Pick a debug configuration to extend

    Bridge to Kubernetes has a couple of ways to run - it can inject it's setup and teardown to your existing debug configurations. We'll pick .NET Core Launch (web).

    Telling Bridge to Kubernetes to use the .NET Core Launch (web) debug configuration in Visual Studio Code

    Forward Traffic for All Requests

    The last prompt you'll get in the configuration is about how you want Bridge to Kubernetes to handle re-routing traffic. The default is that all requests into the service will get your local version.

    You can also redirect specific traffic. Bridge to Kubernetes will set up a subdomain and route specific traffic to your local service, while allowing other traffic to the deployed service.

    Allowing the launch of Endpoint Manager on Windows

    Using Bridge to Kubernetes to Debug Our Service

    Now that we've configured Bridge to Kubernetes, we see that tasks and a new launch configuration have been added.

    Added to .vscode/tasks.json:

            {
    "label": "bridge-to-kubernetes.resource",
    "type": "bridge-to-kubernetes.resource",
    "resource": "web",
    "resourceType": "service",
    "ports": [
    5001
    ],
    "targetCluster": "aks1",
    "targetNamespace": "default",
    "useKubernetesServiceEnvironmentVariables": false
    },
    {
    "label": "bridge-to-kubernetes.compound",
    "dependsOn": [
    "bridge-to-kubernetes.resource",
    "build"
    ],
    "dependsOrder": "sequence"
    }

    And added to .vscode/launch.json:

    {
    "name": ".NET Core Launch (web) with Kubernetes",
    "type": "coreclr",
    "request": "launch",
    "preLaunchTask": "bridge-to-kubernetes.compound",
    "program": "${workspaceFolder}/src/Web/bin/Debug/net7.0/Web.dll",
    "args": [],
    "cwd": "${workspaceFolder}/src/Web",
    "stopAtEntry": false,
    "env": {
    "ASPNETCORE_ENVIRONMENT": "Development",
    "ASPNETCORE_URLS": "http://+:5001"
    },
    "sourceFileMap": {
    "/Views": "${workspaceFolder}/Views"
    }
    }

    Launch the debug configuration

    We can start the process with the .NET Core Launch (web) with Kubernetes launch configuration in the Debug pane in Visual Studio Code.

    Launch the `.NET Core Launch (web) with Kubernetes` from the Debug pane in Visual Studio Code

    Enable the Endpoint Manager

    Part of this process includes a local service to help manage the traffic routing and your hosts file. This will require admin or sudo privileges. On Windows, you'll get a prompt like:

    Prompt to launch the endpoint manager.

    Access your Kubernetes cluster "locally"

    Bridge to Kubernetes will set up a tunnel (thanks to port forwarding) to your local workstation and create local endpoints for the other Kubernetes hosted services in your cluster, as well as pretending to be a pod in that cluster (for the application you are debugging).

    Output from Bridge To Kubernetes setup task.

    After making the connection to your Kubernetes cluster, the launch configuration will continue. In this case, we'll make a debug build of the application and attach the debugger. (This process may cause the terminal in VS Code to scroll with build output. You can find the Bridge to Kubernetes output with the local IP addresses and ports in the Output pane for Bridge to Kubernetes.)

    You can set breakpoints, use your debug console, set watches, run tests against your local version of the service.

    Exploring the Running Application Environment

    One of the cool things that Bridge to Kubernetes does for our debugging experience is bring the environment configuration that our deployed pod would inherit. When we launch our app, it'll see configuration and secrets that we'd expect our pod to be running with.

    To test this, we'll set a breakpoint in our application's start up to see what SQL Server is being used. We'll set a breakpoint at src/Infrastructure/Dependencies.cs on line 32.

    Then, we will start debugging the application with Bridge to Kubernetes. When it hits the breakpoint, we'll open the Debug pane and type configuration.GetConnectionString("CatalogConnection").

    When we run locally (not with Bridge to Kubernetes), we'd see:

    configuration.GetConnectionString("CatalogConnection")
    "Server=(localdb)\\mssqllocaldb;Integrated Security=true;Initial Catalog=Microsoft.eShopOnWeb.CatalogDb;"

    But, with Bridge to Kubernetes we see something more like (yours will vary based on the password ):

    configuration.GetConnectionString("CatalogConnection")
    "Server=db;Database=Microsoft.eShopOnWeb.CatalogDb;User Id=sa;Password=*****************;TrustServerCertificate=True;"

    Debugging our local application connected to Kubernetes.

    We can see that the database server configured is based on our db service and the password is pulled from our secret in Azure KeyVault (via AKS).

    This helps us run our local application just like it was actually in our cluster.

    Going Further

    Bridge to Kubernetes also supports more advanced scenarios and, as you need to start routing traffic around inside your cluster, may require you to modify your application to pass along a kubernetes-route-as header to help ensure that traffic for your debugging workloads is properly handled. The docs go into much greater detail about that.

    Instrumentation

    Now that we've figured out our debugging story, we'll need to ensure that we have the right context clues to find where we need to debug or to give us a better idea of how well our microservices are running.

    Logging and Tracing

    Logging and tracing become even more critical in Kubernetes, where your application could be running in a number of pods across different nodes. When you have an issue, in addition to the normal application data, you'll want to know what pod and what node had the issue, what the state of those resources were (were you resource constrained or were shared resources unavailable?), and if autoscaling is enabled, you'll want to know if a scale event has been triggered. There are a multitude of other concerns based on your application and the environment you maintain.

    Given these informational needs, it's crucial to revisit your existing logging and instrumentation. Most frameworks and languages have extensible logging, tracing, and instrumentation libraries that you can iteratively add information to, such as pod and node states, and ensuring that requests can be traced across your microservices. This will pay you back time and time again when you have to troubleshoot issues in your existing environment.

    Centralized Logging

    To enhance the troubleshooting process further, it's important to implement centralized logging to consolidate logs from all your microservices into a single location. This makes it easier to search and analyze logs when you're troubleshooting an issue.

    Automated Alerting

    Additionally, implementing automated alerting, such as sending notifications when specific conditions occur in the logs, can help you detect issues before they escalate.

    End to end Visibility

    End-to-end visibility is also essential in understanding the flow of requests and responses between microservices in a distributed system. With end-to-end visibility, you can quickly identify bottlenecks and slowdowns in the system, helping you to resolve issues more efficiently.

    Resources

    Take the Cloud Skills Challenge!

    Enroll in the Cloud Skills Challenge!

    Don't miss out on this opportunity to level up your skills and stay ahead of the curve in the world of cloud native.

    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/cloud-native/page/12/index.html b/cnny-2023/tags/cloud-native/page/12/index.html index 525c43883c..aeff27ce51 100644 --- a/cnny-2023/tags/cloud-native/page/12/index.html +++ b/cnny-2023/tags/cloud-native/page/12/index.html @@ -14,13 +14,13 @@ - - + +

    16 posts tagged with "cloud-native"

    View All Tags

    · 7 min read
    Nitya Narasimhan

    Welcome to Week 4 of #CloudNativeNewYear!

    This week we'll go further with Cloud-native by exploring advanced topics and best practices for the Cloud-native practitioner. We'll start with an exploration of Serverless Container Options - ranging from managed services to Azure Kubernetes Service (AKS) and Azure Container Apps (ACA), to options that allow more granular control!

    What We'll Cover

    • The Azure Compute Landscape
    • Serverless Compute on Azure
    • Comparing Container Options On Azure
    • Other Considerations
    • Exercise: Try this yourself!
    • Resources: For self-study!


    We started this series with an introduction to core concepts:

    • In Containers 101, we learned why containerization matters. Think portability, isolation, scalability, resource-efficiency and cost-effectiveness. But not all apps can be containerized.
    • In Kubernetes 101, we learned how orchestration works. Think systems to automate container deployment, scaling, and management. But using Kubernetes directly can be complex.
    • In Exploring Cloud Native Options we asked the real questions: can we containerize - and should we?. The first depends on app characteristics, the second on your requirements.

    For example:

    • Can we containerize? The answer might be no if your app has system or OS dependencies, requires access to low-level hardware, or maintains complex state across sessions.
    • Should we containerize? The answer might be yes if your app is microservices-based, is stateless by default, requires portability, or is a legaacy app that can benefit from container isolation.

    As with every technology adoption decision process, there are no clear yes/no answers - just tradeoffs that you need to evaluate based on your architecture and application requirements. In today's post, we try to look at this from two main perspectives:

    1. Should you go serverless? Think: managed services that let you focus on app, not infra.
    2. What Azure Compute should I use? Think: best fit for my architecture & technology choices.

    Azure Compute Landscape

    Let's answer the second question first by exploring all available compute options on Azure. The illustrated decision-flow below is my favorite ways to navigate the choices, with questions like:

    • Are you migrating an existing app or building a new one?
    • Can you app be containerized?
    • Does it use a specific technology (Spring Boot, Red Hat Openshift)?
    • Do you need access to the Kubernetes API?
    • What characterizes the workload? (event-driven, web app, microservices etc.)

    Read the docs to understand how your choices can be influenced by the hosting model (IaaS, PaaS, FaaS), supported features (Networking, DevOps, Scalability, Availability, Security), architectural styles (Microservices, Event-driven, High-Performance Compute, Task Automation,Web-Queue Worker) etc.

    Compute Choices

    Now that we know all available compute options, let's address the second question: why go serverless? and what are my serverless compute options on Azure?

    Azure Serverless Compute

    Serverless gets defined many ways, but from a compute perspective, we can focus on a few key characteristics that are key to influencing this decision:

    • managed services - focus on application, let cloud provider handle infrastructure.
    • pay for what you use - get cost-effective resource utilization, flexible pricing options.
    • autoscaling on demand - take advantage of built-in features like KEDA-compliant triggers.
    • use preferred languages - write code in Java, JS, C#, Python etc. (specifics based on service)
    • cloud-native architectures - can support event-driven solutions, APIs, Microservices, DevOps!

    So what are some of the key options for Serverless Compute on Azure? The article dives into serverless support for fully-managed end-to-end serverless solutions with comprehensive support for DevOps, DevTools, AI/ML, Database, Storage, Monitoring and Analytics integrations. But we'll just focus on the 4 categories of applications when we look at Compute!

    1. Serverless Containerized Microservices using Azure Container Apps. Code in your preferred language, exploit full Dapr support, scale easily with any KEDA-compliant trigger.
    2. Serverless Application Environments using Azure App Service. Suitable for hosting monolithic apps (vs. microservices) in a managed service, with built-in support for on-demand scaling.
    3. Serverless Kubernetes using Azure Kubernetes Service (AKS). Spin up pods inside container instances and deploy Kubernetes-based applications with built-in KEDA-compliant autoscaling.
    4. Serverless Functions using Azure Functions. Execute "code at the granularity of functions" in your preferred language, and scale on demand with event-driven compute.

    We'll talk about these, and other compute comparisons, at the end of the article. But let's start with the core option you might choose if you want a managed serverless compute solution with built-in features for delivering containerized microservices at scale. Hello, Azure Container Apps!.

    Azure Container Apps

    Azure Container Apps (ACA) became generally available in May 2022 - providing customers with the ability to run microservices and containerized applications on a serverless, consumption-based platform. The figure below showcases the different types of applications that can be built with ACA. Note that it comes with built-in KEDA-compliant autoscaling triggers, and other auto-scale criteria that may be better-suited to the type of application you are building.

    About ACA

    So far in the series, we've put the spotlight on Azure Kubernetes Service (AKS) - so you're probably asking yourself: How does ACA compare to AKS?. We're glad you asked. Check out our Go Cloud-native with Azure Container Apps post from the #ServerlessSeptember series last year for a deeper-dive, or review the figure below for the main comparison points.

    The key takeaway is this. Azure Container Apps (ACA) also runs on Kubernetes but abstracts away its complexity in a managed service offering that lets you get productive quickly without requiring detailed knowledge of Kubernetes workings or APIs. However, if you want full access and control over the Kubernetes API then go with Azure Kubernetes Service (AKS) instead.

    Comparison

    Other Container Options

    Azure Container Apps is the preferred Platform As a Service (PaaS) option for a fully-managed serverless solution on Azure that is purpose-built for cloud-native microservices-based application workloads. But - there are other options that may be suitable for your specific needs, from a requirements and tradeoffs perspective. Let's review them quickly:

    1. Azure Functions is the serverless Functions-as-a-Service (FaaS) option, as opposed to ACA which supports a PaaS approach. It's optimized for running event-driven applications built at the granularity of ephemeral functions that can be deployed as code or containers.
    2. Azure App Service provides fully managed hosting for web applications that may be deployed using code or containers. It can be integrated with other services including Azure Container Apps and Azure Functions. It's optimized for deploying traditional web apps.
    3. Azure Kubernetes Service provides a fully managed Kubernetes option capable of running any Kubernetes workload, with direct access to the Kubernetes API.
    4. Azure Container Instances provides a single pod of Hyper-V isolated containers on demand, making them more of a low-level "building block" option compared to ACA.

    Based on the technology choices you made for application development you may also have more specialized options you want to consider. For instance:

    1. Azure Spring Apps is ideal if you're running Spring Boot or Spring Cloud workloads on Azure,
    2. Azure Red Hat OpenShift is ideal for integrated Kubernetes-powered OpenShift on Azure.
    3. Azure Confidential Computing is ideal if you have data/code integrity and confidentiality needs.
    4. Kubernetes At The Edge is ideal for bare-metal options that extend compute to edge devices.

    This is just the tip of the iceberg in your decision-making journey - but hopefully, it gave you a good sense of the options and criteria that influences your final choices. Let's wrap this up with a look at self-study resources for skilling up further.

    Exercise

    Want to get hands on learning related to these technologies?

    TAKE THE CLOUD SKILLS CHALLENGE

    Register today and level up your skills by completing free learning modules, while competing with your peers for a place on the leaderboards!

    Resources

    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/cloud-native/page/13/index.html b/cnny-2023/tags/cloud-native/page/13/index.html index d340dc6592..d545fa5940 100644 --- a/cnny-2023/tags/cloud-native/page/13/index.html +++ b/cnny-2023/tags/cloud-native/page/13/index.html @@ -14,13 +14,13 @@ - - + +

    16 posts tagged with "cloud-native"

    View All Tags

    · 3 min read
    Cory Skimming

    It's the final week of #CloudNativeNewYear! This week we'll go further with Cloud-native by exploring advanced topics and best practices for the Cloud-native practitioner. In today's post, we will introduce you to the basics of the open-source project Draft and how it can be used to easily create and deploy applications to Kubernetes.

    It's not too late to sign up for and complete the Cloud Skills Challenge!

    What We'll Cover

    • What is Draft?
    • Draft basics
    • Demo: Developing to AKS with Draft
    • Resources


    What is Draft?

    Draft is an open-source tool that can be used to streamline the development and deployment of applications on Kubernetes clusters. It provides a simple and easy-to-use workflow for creating and deploying applications, making it easier for developers to focus on writing code and building features, rather than worrying about the underlying infrastructure. This is great for users who are just getting started with Kubernetes, or those who are just looking to simplify their experience.

    New to Kubernetes?

    Draft basics

    Draft streamlines Kubernetes development by taking a non-containerized application and generating the Dockerfiles, K8s manifests, Helm charts, and other artifacts associated with a containerized application. Draft can also create a GitHub Action workflow file to quickly build and deploy your application onto any Kubernetes cluster.

    1. 'draft create'': Create a new Draft project by simply running the 'draft create' command - this command will walk you through a series of questions on your application specification (such as the application language) and create a Dockerfile, Helm char, and Kubernetes
    2. 'draft generate-workflow'': Automatically build out a GitHub Action using the 'draft generate-workflow' command
    3. 'draft setup-gh'': If you are using Azure, use this command to automate the GitHub OIDC set up process to ensure that you will be able to deploy your application using your GitHub Action.

    At this point, you will have all the files needed to deploy your app onto a Kubernetes cluster (we told you it was easy!).

    You can also use the 'draft info' command if you are looking for information on supported languages and deployment types. Let's see it in action, shall we?


    Developing to AKS with Draft

    In this Microsoft Reactor session below, we'll briefly introduce Kubernetes and the Azure Kubernetes Service (AKS) and then demo how enable your applications for Kubernetes using the open-source tool Draft. We'll show how Draft can help you create the boilerplate code to containerize your applications and add routing and scaling behaviours.

    ##Conclusion

    Overall, Draft simplifies the process of building, deploying, and managing applications on Kubernetes, and can make the overall journey from code to Kubernetes significantly easier.


    Resources


    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/cloud-native/page/14/index.html b/cnny-2023/tags/cloud-native/page/14/index.html index 440a520510..6e5714a103 100644 --- a/cnny-2023/tags/cloud-native/page/14/index.html +++ b/cnny-2023/tags/cloud-native/page/14/index.html @@ -14,14 +14,14 @@ - - + +

    16 posts tagged with "cloud-native"

    View All Tags

    · 7 min read
    Vinicius Apolinario

    Welcome to Day 3 of Week 4 of #CloudNativeNewYear!

    The theme for this week is going further with Cloud Native. Yesterday we talked about using Draft to accelerate your Kubernetes adoption. Today we'll explore the topic of Windows containers.

    What We'll Cover

    • Introduction
    • Windows containers overview
    • Windows base container images
    • Isolation
    • Exercise: Try this yourself!
    • Resources: For self-study!

    Introduction

    Windows containers were launched along with Windows Server 2016, and have evolved since. In its latest release, Windows Server 2022, Windows containers have reached a great level of maturity and allow for customers to run production grade workloads.

    While suitable for new developments, Windows containers also provide developers and operations with a different approach than Linux containers. It allows for existing Windows applications to be containerized with little or no code changes. It also allows for professionals that are more comfortable with the Windows platform and OS, to leverage their skill set, while taking advantage of the containers platform.

    Windows container overview

    In essence, Windows containers are very similar to Linux. Since Windows containers use the same foundation of Docker containers, you can expect that the same architecture applies - with the specific notes of the Windows OS. For example, when running a Windows container via Docker, you use the same commands, such as docker run. To pull a container image, you can use docker pull, just like on Linux. However, to run a Windows container, you also need a Windows container host. This requirement is there because, as you might remember, a container shares the OS kernel with its container host.

    On Kubernetes, Windows containers are supported since Windows Server 2019. Just like with Docker, you can manage Windows containers like any other resource on the Kubernetes ecosystem. A Windows node can be part of a Kubernetes cluster, allowing you to run Windows container based applications on services like Azure Kubernetes Service. To deploy an Windows application to a Windows pod in Kubernetes, you can author a YAML specification much like you would for Linux. The main difference is that you would point to an image that runs on Windows, and you need to specify a node selection tag to indicate said pod needs to run on a Windows node.

    Windows base container images

    On Windows containers, you will always use a base container image provided by Microsoft. This base container image contains the OS binaries for the container to run. This image can be as large as 3GB+, or small as ~300MB. The difference in the size is a consequence of the APIs and components available in each Windows container base container image. There are primarily, three images: Nano Server, Server Core, and Server.

    Nano Server is the smallest image, ranging around 300MB. It's a base container image for new developments and cloud-native scenarios. Applications need to target Nano Server as the Windows OS, so not all frameworks will work. For example, .Net works on Nano Server, but .Net Framework doesn't. Other third-party frameworks also work on Nano Server, such as Apache, NodeJS, Phyton, Tomcat, Java runtime, JBoss, Redis, among others.

    Server Core is a much larger base container image, ranging around 1.25GB. It's larger size is compensated by it's application compatibility. Simply put, any application that meets the requirements to be run on a Windows container, can be containerized with this image.

    The Server image builds on the Server Core one. It ranges around 3.1GB and has even greater application compatibility than the Server Core image. In addition to the traditional Windows APIs and components, this image allows for scenarios such as Machine Learning via DirectX with GPU access.

    The best image for your scenario is dependent on the requirements your application has on the Windows OS inside a container. However, there are some scenarios that are not supported at all on Windows containers - such as GUI or RDP dependent applications, some Windows Server infrastructure roles, such as Active Directory, among others.

    Isolation

    When running containers, the kernel of the container host is shared with the containers running on it. While extremely convenient, this poses a potential risk for multi-tenant scenarios. If one container is compromised and has access to the host, it could potentially compromise other containers in the same system.

    For enterprise customers running on-premises (or even in the cloud), this can be mitigated by using a VM as a container host and considering the VM itself a security boundary. However, if multiple workloads from different tenants need to share the same host, Windows containers offer another option: Hyper-V isolation. While the name Hyper-V is associated with VMs, its virtualization capabilities extend to other services, including containers. Hyper-V isolated containers run on a purpose built, extremely small, highly performant VM. However, you manage a container running with Hyper-V isolation the same way you do with a process isolated one. In fact, the only notable difference is that you need to append the --isolation=hyperv tag to the docker run command.

    Exercise

    Here are a few examples of how to use Windows containers:

    Run Windows containers via Docker on your machine

    To pull a Windows base container image:

    docker pull mcr.microsoft.com/windows/servercore:ltsc2022

    To run a basic IIS container:

    #This command will pull and start a IIS container. You can access it from http://<your local IP>:8080
    docker run -d -p 8080:80 mcr.microsoft.com/windows/servercore/iis:windowsservercore-ltsc2022

    Run the same IIS container with Hyper-V isolation

    #This command will pull and start a IIS container. You can access it from http://<your local IP>:8080
    docker run -d -p 8080:80 --isolation=hyperv mcr.microsoft.com/windows/servercore/iis:windowsservercore-ltsc2022

    To run a Windows container interactively:

    docker run -it mcr.microsoft.com/windows/servercore:ltsc2022 powershell

    Run Windows containers on Kubernetes

    To prepare an AKS cluster for Windows containers: Note: Replace the values on the example below with the ones from your environment.

    echo "Please enter the username to use as administrator credentials for Windows Server nodes on your cluster: " && read WINDOWS_USERNAME
    az aks create \
    --resource-group myResourceGroup \
    --name myAKSCluster \
    --node-count 2 \
    --generate-ssh-keys \
    --windows-admin-username $WINDOWS_USERNAME \
    --vm-set-type VirtualMachineScaleSets \
    --network-plugin azure

    To add a Windows node pool for Windows containers:

    az aks nodepool add \
    --resource-group myResourceGroup \
    --cluster-name myAKSCluster \
    --os-type Windows \
    --name npwin \
    --node-count 1

    Deploy a sample ASP.Net application to the AKS cluster above using the YAML file below:

    apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: sample
    labels:
    app: sample
    spec:
    replicas: 1
    template:
    metadata:
    name: sample
    labels:
    app: sample
    spec:
    nodeSelector:
    "kubernetes.io/os": windows
    containers:
    - name: sample
    image: mcr.microsoft.com/dotnet/framework/samples:aspnetapp
    resources:
    limits:
    cpu: 1
    memory: 800M
    ports:
    - containerPort: 80
    selector:
    matchLabels:
    app: sample
    ---
    apiVersion: v1
    kind: Service
    metadata:
    name: sample
    spec:
    type: LoadBalancer
    ports:
    - protocol: TCP
    port: 80
    selector:
    app: sample

    Save the file above and run the command below on your Kubernetes cluster:

    kubectl apply -f <filename> .

    Once deployed, you can access the application by getting the IP address of your service:

    kubectl get service

    Resources

    It's not too late to sign up for and complete the Cloud Skills Challenge!
    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/cloud-native/page/15/index.html b/cnny-2023/tags/cloud-native/page/15/index.html index b1a80ac850..74d5df3f46 100644 --- a/cnny-2023/tags/cloud-native/page/15/index.html +++ b/cnny-2023/tags/cloud-native/page/15/index.html @@ -14,13 +14,13 @@ - - + +

    16 posts tagged with "cloud-native"

    View All Tags

    · 4 min read
    Jorge Arteiro

    Welcome to Day 4 of Week 4 of #CloudNativeNewYear!

    The theme for this week is going further with Cloud Native. Yesterday we talked about Windows Containers. Today we'll explore addons and extensions available to Azure Kubernetes Services (AKS).

    What We'll Cover

    • Introduction
    • Add-ons
    • Extensions
    • Add-ons vs Extensions
    • Resources

    Introduction

    Azure Kubernetes Service (AKS) is a fully managed container orchestration service that makes it easy to deploy and manage containerized applications on Azure. AKS offers a number of features and capabilities, including the ability to extend its supported functionality through the use of add-ons and extensions.

    There are also integrations available from open-source projects and third parties, but they are not covered by the AKS support policy.

    Add-ons

    Add-ons provide a supported way to extend AKS. Installation, configuration and lifecycle are managed by AKS following pre-determine updates rules.

    As an example, let's enable Container Insights with the monitoring addon. on an existing AKS cluster using az aks enable-addons --addons CLI command

    az aks enable-addons \
    --name MyManagedCluster \
    --resource-group MyResourceGroup \
    --addons monitoring

    or you can use az aks create --enable-addons when creating new clusters

    az aks create \
    --name MyManagedCluster \
    --resource-group MyResourceGroup \
    --enable-addons monitoring

    The current available add-ons are:

    1. http_application_routing - Configure ingress with automatic public DNS name creation. Only recommended for development.
    2. monitoring - Container Insights monitoring.
    3. virtual-node - CNCF virtual nodes open source project.
    4. azure-policy - Azure Policy for AKS.
    5. ingress-appgw - Application Gateway Ingress Controller (AGIC).
    6. open-service-mesh - CNCF Open Service Mesh project.
    7. azure-keyvault-secrets-provider - Azure Key Vault Secrets Provider for Secret Store CSI Driver.
    8. web_application_routing - Managed NGINX ingress Controller.
    9. keda - CNCF Event-driven autoscaling project.

    For more details, get the updated list of AKS Add-ons here

    Extensions

    Cluster Extensions uses Helm charts and integrates with Azure Resource Manager (ARM) to provide installation and lifecycle management of capabilities on top of AKS.

    Extensions can be auto upgraded using minor versions, but it requires extra management and configuration. Using Scope parameter, it can be installed on the whole cluster or per namespace.

    AKS Extensions requires an Azure CLI extension to be installed. To add or update this CLI extension use the following commands:

    az extension add --name k8s-extension

    and to update an existing extension

    az extension update --name k8s-extension

    There are only 3 available extensions:

    1. Dapr - CNCF Dapr project.
    2. Azure ML - Integrate Azure Machine Learning with AKS to train, inference and manage ML models.
    3. Flux (GitOps) - CNCF Flux project integrated with AKS to enable cluster configuration and application deployment using GitOps.

    As an example, you can install Azure ML using the following command:

    az k8s-extension create \
    --name aml-compute --extension-type Microsoft.AzureML.Kubernetes \
    --scope cluster --cluster-name <clusterName> \
    --resource-group <resourceGroupName> \
    --cluster-type managedClusters \
    --configuration-settings enableInference=True allowInsecureConnections=True

    For more details, get the updated list of AKS Extensions here

    Add-ons vs Extensions

    AKS Add-ons brings an advantage of been fully managed by AKS itself, and AKS Extensions are more flexible and configurable but requires extra level of management.

    Add-ons are part of the AKS resource provider in the Azure API, and AKS Extensions are a separate resource provider on the Azure API.

    Resources

    It's not too late to sign up for and complete the Cloud Skills Challenge!
    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/cloud-native/page/16/index.html b/cnny-2023/tags/cloud-native/page/16/index.html index efe712ad88..41ff5c5d85 100644 --- a/cnny-2023/tags/cloud-native/page/16/index.html +++ b/cnny-2023/tags/cloud-native/page/16/index.html @@ -14,13 +14,13 @@ - - + +

    16 posts tagged with "cloud-native"

    View All Tags

    · 6 min read
    Cory Skimming
    Steven Murawski
    Paul Yu
    Josh Duffney
    Nitya Narasimhan
    Vinicius Apolinario
    Jorge Arteiro
    Devanshi Joshi

    And that's a wrap on the inaugural #CloudNativeNewYear! Thank you for joining us to kick off the new year with this learning journey into cloud-native! In this final post of the 2023 celebration of all things cloud-native, we'll do two things:

    • Look Back - with a quick retrospective of what was covered.
    • Look Ahead - with resources and suggestions for how you can continue your skilling journey!

    We appreciate your time and attention and we hope you found this curated learning valuable. Feedback and suggestions are always welcome. From our entire team, we wish you good luck with the learning journey - now go build some apps and share your knowledge! 🎉


    What We'll Cover

    • Cloud-native fundamentals
    • Kubernetes fundamentals
    • Bringing your applications to Kubernetes
    • Go further with cloud-native
    • Resources to keep the celebration going!

    Week 1: Cloud-native Fundamentals

    In Week 1, we took a tour through the fundamentals of cloud-native technologies, including a walkthrough of the core concepts of containers, microservices, and Kubernetes.

    • Jan 23 - Cloud-native Fundamentals: The answers to life and all the universe - what is cloud-native? What makes an application cloud-native? What are the benefits? (yes, we all know it's 42, but hey, gotta start somewhere!)
    • Jan 24 - Containers 101: Containers are an essential component of cloud-native development. In this intro post, we cover how containers work and why they have become so popular.
    • Jan 25 - Kubernetes 101: Kuber-what-now? Learn the basics of Kubernetes and how it enables us to deploy and manage our applications effectively and consistently.
    A QUICKSTART GUIDE TO KUBERNETES CONCEPTS

    Missed it Live? Tune in to A Quickstart Guide to Kubernetes Concepts on demand, now!

    • Jan 26 - Microservices 101: What is a microservices architecture and how can we go about designing one?
    • Jan 27 - Exploring your Cloud Native Options: Cloud-native, while catchy, can be a very broad term. What technologies should you use? Learn some basic guidelines for when it is optimal to use different technologies for your project.

    Week 2: Kubernetes Fundamentals

    In Week 2, we took a deeper dive into the Fundamentals of Kubernetes. The posts and live demo from this week took us through how to build a simple application on Kubernetes, covering everything from deployment to networking and scaling. Note: for our samples and demo we have used Azure Kubernetes Service, but the principles apply to any Kubernetes!

    • Jan 30 - Pods and Deployments: how to use pods and deployments in Kubernetes.
    • Jan 31 - Services and Ingress: how to use services and ingress and a walk through the steps of making our containers accessible internally and externally!
    • Feb 1 - ConfigMaps and Secrets: how to of passing configuration and secrets to our applications in Kubernetes with ConfigMaps and Secrets.
    • Feb 2 - Volumes, Mounts, and Claims: how to use persistent storage on Kubernetes (and ensure your data can survive container restarts!).
    • Feb 3 - Scaling Pods and Nodes: how to scale pods and nodes in our Kubernetes cluster.
    ASK THE EXPERTS: AZURE KUBERNETES SERVICE

    Missed it Live? Tune in to Ask the Expert with Azure Kubernetes Service on demand, now!


    Week 3: Bringing your applications to Kubernetes

    So, you have learned how to build an application on Kubernetes. What about your existing applications? In Week 3, we explored how to take an existing application and set it up to run in Kubernetes:

    • Feb 6 - CI/CD: learn how to get an existing application running in Kubernetes with a full pipeline in GitHub Actions.
    • Feb 7 - Adapting Storage, Secrets, and Configuration: how to evaluate our sample application's configuration, storage, and networking requirements and implement using Kubernetes.
    • Feb 8 - Opening your Application with Ingress: how to expose the eShopOnWeb app so that customers can reach it over the internet using a custom domain name and TLS.
    • Feb 9 - Debugging and Instrumentation: how to debug and instrument your application now that it is on Kubernetes.
    • Feb 10 - CI/CD Secure Supply Chain: now that we have set up our application on Kubernetes, let's talk about container image signing and how to set up a secure supply change.

    Week 4: Go Further with Cloud-Native

    This week we have gone further with Cloud-native by exploring advanced topics and best practices for the Cloud-native practitioner.

    And today, February 17th, with this one post to rule (er, collect) them all!


    Keep the Learning Going!

    Learning is great, so why stop here? We have a host of great resources and samples for you to continue your cloud-native journey with Azure below:


    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/cloud-native/page/2/index.html b/cnny-2023/tags/cloud-native/page/2/index.html index 95e6379136..ffe247e6d9 100644 --- a/cnny-2023/tags/cloud-native/page/2/index.html +++ b/cnny-2023/tags/cloud-native/page/2/index.html @@ -14,14 +14,14 @@ - - + +

    16 posts tagged with "cloud-native"

    View All Tags

    · 5 min read
    Cory Skimming

    Welcome to Week 1 of #CloudNativeNewYear!

    Cloud-native New Year

    You will often hear the term "cloud-native" when discussing modern application development, but even a quick online search will return a huge number of articles, tweets, and web pages with a variety of definitions. So, what does cloud-native actually mean? Also, what makes an application a cloud-native application versus a "regular" application?

    Today, we will address these questions and more as we kickstart our learning journey (and our new year!) with an introductory dive into the wonderful world of cloud-native.


    What We'll Cover

    • What is cloud-native?
    • What is a cloud-native application?
    • The benefits of cloud-native
    • The five pillars of cloud-native
    • Exercise: Take the Cloud Skills Challenge!

    1. What is cloud-native?

    The term "cloud-native" can seem pretty self-evident (yes, hello, native to the cloud?), and in a way, it is. While there are lots of definitions of cloud-native floating around, at it's core, cloud-native simply refers to a modern approach to building software that takes advantage of cloud services and environments. This includes using cloud-native technologies, such as containers, microservices, and serverless, and following best practices for deploying, scaling, and managing applications in a cloud environment.

    Official definition from the Cloud Native Computing Foundation:

    Cloud-native technologies empower organizations to build and run scalable applications in modern, dynamic environments such as public, private, and hybrid clouds. Containers, service meshes, microservices, immutable infrastructure, and declarative APIs exemplify this approach.

    These techniques enable loosely coupled systems that are resilient, manageable, and observable. Combined with robust automation, they allow engineers to make high-impact changes frequently and predictably with minimal toil. Source


    2. So, what exactly is a cloud-native application?

    Cloud-native applications are specifically designed to take advantage of the scalability, resiliency, and distributed nature of modern cloud infrastructure. But how does this differ from a "traditional" application?

    Traditional applications are generally been built, tested, and deployed as a single, monolithic unit. The monolithic nature of this type of architecture creates close dependencies between components. This complexity and interweaving only increases as an application grows and can make it difficult to evolve (not to mention troubleshoot) and challenging to operate over time.

    To contrast, in cloud-native architectures the application components are decomposed into loosely coupled services, rather than built and deployed as one block of code. This decomposition into multiple self-contained services enables teams to manage complexity and improve the speed, agility, and scale of software delivery. Many small parts enables teams to make targeted updates, deliver new features, and fix any issues without leading to broader service disruption.


    3. The benefits of cloud-native

    Cloud-native architectures can bring many benefits to an organization, including:

    1. Scalability: easily scale up or down based on demand, allowing organizations to adjust their resource usage and costs as needed.
    2. Flexibility: deploy and run on any cloud platform, and easily move between clouds and on-premises environments.
    3. High-availability: techniques such as redundancy, self-healing, and automatic failover help ensure that cloud-native applications are designed to be highly-available and fault tolerant.
    4. Reduced costs: take advantage of the pay-as-you-go model of cloud computing, reducing the need for expensive infrastructure investments.
    5. Improved security: tap in to cloud security features, such as encryption and identity management, to improve the security of the application.
    6. Increased agility: easily add new features or services to your applications to meet changing business needs and market demand.

    4. The pillars of cloud-native

    There are five areas that are generally cited as the core building blocks of cloud-native architecture:

    1. Microservices: Breaking down monolithic applications into smaller, independent, and loosely-coupled services that can be developed, deployed, and scaled independently.
    2. Containers: Packaging software in lightweight, portable, and self-sufficient containers that can run consistently across different environments.
    3. Automation: Using automation tools and DevOps processes to manage and operate the cloud-native infrastructure and applications, including deployment, scaling, monitoring, and self-healing.
    4. Service discovery: Using service discovery mechanisms, such as APIs & service meshes, to enable services to discover and communicate with each other.
    5. Observability: Collecting and analyzing data from the infrastructure and applications to understand and optimize the performance, behavior, and health of the system.

    These can (and should!) be used in combination to deliver cloud-native solutions that are highly scalable, flexible, and available.

    WHAT'S NEXT

    Stay tuned, as we will be diving deeper into these topics in the coming weeks:

    • Jan 24: Containers 101
    • Jan 25: Adopting Microservices with Kubernetes
    • Jan 26: Kubernetes 101
    • Jan 27: Exploring your Cloud-native Options

    Resources


    Don't forget to subscribe to the blog to get daily posts delivered directly to your favorite feed reader!


    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/cloud-native/page/3/index.html b/cnny-2023/tags/cloud-native/page/3/index.html index 9358497a59..e1f2940e55 100644 --- a/cnny-2023/tags/cloud-native/page/3/index.html +++ b/cnny-2023/tags/cloud-native/page/3/index.html @@ -14,14 +14,14 @@ - - + +

    16 posts tagged with "cloud-native"

    View All Tags

    · 4 min read
    Steven Murawski
    Paul Yu
    Josh Duffney

    Welcome to Day 2 of Week 1 of #CloudNativeNewYear!

    Today, we'll focus on building an understanding of containers.

    What We'll Cover

    • Introduction
    • How do Containers Work?
    • Why are Containers Becoming so Popular?
    • Conclusion
    • Resources
    • Learning Path

    REGISTER & LEARN: KUBERNETES 101

    Interested in a dive into Kubernetes and a chance to talk to experts?

    🎙: Join us Jan 26 @1pm PST by registering here

    Here's what you will learn:

    • Key concepts and core principles of Kubernetes.
    • How to deploy, scale and manage containerized workloads.
    • Live Demo of the concepts explained
    • How to get started with Azure Kubernetes Service for free.

    Start your free Azure Kubernetes Trial Today!!: aka.ms/TryAKS

    Introduction

    In the beginning, we deployed our applications onto physical servers. We only had a certain number of those servers, so often they hosted multiple applications. This led to some problems when those applications shared dependencies. Upgrading one application could break another application on the same server.

    Enter virtualization. Virtualization allowed us to run our applications in an isolated operating system instance. This removed much of the risk of updating shared dependencies. However, it increased our overhead since we had to run a full operating system for each application environment.

    To address the challenges created by virtualization, containerization was created to improve isolation without duplicating kernel level resources. Containers provide efficient and consistent deployment and runtime experiences for our applications and have become very popular as a way of packaging and distributing applications.

    How do Containers Work?

    Containers build on two capabilities in the Linux operating system, namespaces and cgroups. These constructs allow the operating system to provide isolation to a process or group of processes, keeping their access to filesystem resources separate and providing controls on resource utilization. This, combined with tooling to help package, deploy, and run container images has led to their popularity in today’s operating environment. This provides us our isolation without the overhead of additional operating system resources.

    When a container host is deployed on an operating system, it works at scheduling the access to the OS (operating systems) components. This is done by providing a logical isolated group that can contain processes for a given application, called a namespace. The container host then manages /schedules access from the namespace to the host OS. The container host then uses cgroups to allocate compute resources. Together, the container host with the help of cgroups and namespaces can schedule multiple applications to access host OS resources.

    Overall, this gives the illusion of virtualizing the host OS, where each application gets its own OS. In actuality, all the applications are running on the same operating system and sharing the same kernel as the container host.

    Containers are popular in the software development industry because they provide several benefits over traditional virtualization methods. Some of these benefits include:

    • Portability: Containers make it easy to move an application from one environment to another without having to worry about compatibility issues or missing dependencies.
    • Isolation: Containers provide a level of isolation between the application and the host system, which means that the application running in the container cannot access the host system's resources.
    • Scalability: Containers make it easy to scale an application up or down as needed, which is useful for applications that experience a lot of traffic or need to handle a lot of data.
    • Resource Efficiency: Containers are more resource-efficient than traditional virtualization methods because they don't require a full operating system to be running on each virtual machine.
    • Cost-Effective: Containers are more cost-effective than traditional virtualization methods because they don't require expensive hardware or licensing fees.

    Conclusion

    Containers are a powerful technology that allows developers to package and deploy applications in a portable and isolated environment. This technology is becoming increasingly popular in the world of software development and is being used by many companies and organizations to improve their application deployment and management processes. With the benefits of portability, isolation, scalability, resource efficiency, and cost-effectiveness, containers are definitely worth considering for your next application development project.

    Containerizing applications is a key step in modernizing them, and there are many other patterns that can be adopted to achieve cloud-native architectures, including using serverless platforms, Kubernetes, and implementing DevOps practices.

    Resources

    Learning Path

    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/cloud-native/page/4/index.html b/cnny-2023/tags/cloud-native/page/4/index.html index b6d67839c4..3bc6265bac 100644 --- a/cnny-2023/tags/cloud-native/page/4/index.html +++ b/cnny-2023/tags/cloud-native/page/4/index.html @@ -14,14 +14,14 @@ - - + +

    16 posts tagged with "cloud-native"

    View All Tags

    · 3 min read
    Steven Murawski

    Welcome to Day 3 of Week 1 of #CloudNativeNewYear!

    This week we'll focus on what Kubernetes is.

    What We'll Cover

    • Introduction
    • What is Kubernetes? (Video)
    • How does Kubernetes Work? (Video)
    • Conclusion


    REGISTER & LEARN: KUBERNETES 101

    Interested in a dive into Kubernetes and a chance to talk to experts?

    🎙: Join us Jan 26 @1pm PST by registering here

    Here's what you will learn:

    • Key concepts and core principles of Kubernetes.
    • How to deploy, scale and manage containerized workloads.
    • Live Demo of the concepts explained
    • How to get started with Azure Kubernetes Service for free.

    Start your free Azure Kubernetes Trial Today!!: aka.ms/TryAKS

    Introduction

    Kubernetes is an open source container orchestration engine that can help with automated deployment, scaling, and management of our applications.

    Kubernetes takes physical (or virtual) resources and provides a consistent API over them, bringing a consistency to the management and runtime experience for our applications. Kubernetes provides us with a number of capabilities such as:

    • Container scheduling
    • Service discovery and load balancing
    • Storage orchestration
    • Automated rollouts and rollbacks
    • Automatic bin packing
    • Self-healing
    • Secret and configuration management

    We'll learn more about most of these topics as we progress through Cloud Native New Year.

    What is Kubernetes?

    Let's hear from Brendan Burns, one of the founders of Kubernetes as to what Kubernetes actually is.

    How does Kubernetes Work?

    And Brendan shares a bit more with us about how Kubernetes works.

    Conclusion

    Kubernetes allows us to deploy and manage our applications effectively and consistently.

    By providing a consistent API across many of the concerns our applications have, like load balancing, networking, storage, and compute, Kubernetes improves both our ability to build and ship new software.

    There are standards for the applications to depend on for resources needed. Deployments, metrics, and logs are provided in a standardized fashion allowing more effecient operations across our application environments.

    And since Kubernetes is an open source platform, it can be found in just about every type of operating environment - cloud, virtual machines, physical hardware, shared data centers, even small devices like Rasberry Pi's!

    Want to learn more? Join us for a webinar on Kubernetes Concepts (or catch the playback) on Thursday, January 26th at 1 PM PST and watch for the rest of this series right here!

    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/cloud-native/page/5/index.html b/cnny-2023/tags/cloud-native/page/5/index.html index f0dbb8ae8d..e08cd7350e 100644 --- a/cnny-2023/tags/cloud-native/page/5/index.html +++ b/cnny-2023/tags/cloud-native/page/5/index.html @@ -14,13 +14,13 @@ - - + +

    16 posts tagged with "cloud-native"

    View All Tags

    · 6 min read
    Josh Duffney

    Welcome to Day 4 of Week 1 of #CloudNativeNewYear!

    This week we'll focus on advanced topics and best practices for Cloud-Native practitioners, kicking off with this post on Serverless Container Options with Azure. We'll look at technologies, tools and best practices that range from managed services like Azure Kubernetes Service, to options allowing finer granularity of control and oversight.

    What We'll Cover

    • What is Microservice Architecture?
    • How do you design a Microservice?
    • What challenges do Microservices introduce?
    • Conclusion
    • Resources


    Microservices are a modern way of designing and building software that increases deployment velocity by decomposing an application into small autonomous services that can be deployed independently.

    By deploying loosely coupled microservices your applications can be developed, deployed, and scaled independently. Because each service is independent, it can be updated or replaced without having to worry about the impact on the rest of the application. This means that if a bug is found in one service, it can be fixed without having to redeploy the entire application. All of which gives an organization the ability to deliver value to their customers faster.

    In this article, we will explore the basics of microservices architecture, its benefits and challenges, and how it can help improve the development, deployment, and maintenance of software applications.

    What is Microservice Architecture?

    Before explaining what Microservice architecture is, it’s important to understand what problems microservices aim to address.

    Traditional software development is centered around building monolithic applications. Monolithic applications are built as a single, large codebase. Meaning your code is tightly coupled causing the monolithic app to suffer from the following:

    Too much Complexity: Monolithic applications can become complex and difficult to understand and maintain as they grow. This can make it hard to identify and fix bugs and add new features.

    Difficult to Scale: Monolithic applications can be difficult to scale as they often have a single point of failure, which can cause the whole application to crash if a service fails.

    Slow Deployment: Deploying a monolithic application can be risky and time-consuming, as a small change in one part of the codebase can affect the entire application.

    Microservice architecture (often called microservices) is an architecture style that addresses the challenges created by Monolithic applications. Microservices architecture is a way of designing and building software applications as a collection of small, independent services that communicate with each other through APIs. This allows for faster development and deployment cycles, as well as easier scaling and maintenance than is possible with a monolithic application.

    How do you design a Microservice?

    Building applications with Microservices architecture requires a different approach. Microservices architecture focuses on business capabilities rather than technical layers, such as data access or messaging. Doing so requires that you shift your focus away from the technical stack and model your applications based upon the various domains that exist within the business.

    Domain-driven design (DDD) is a way to design software by focusing on the business needs. You can use Domain-driven design as a framework that guides the development of well-designed microservices by building services that encapsulate knowledge in each domain and abstract that knowledge from clients.

    In Domain-driven design you start by modeling the business domain and creating a domain model. A domain model is an abstract model of the business model that distills and organizes a domain of knowledge and provides a common language for developers and domain experts. It’s the resulting domain model that microservices a best suited to be built around because it helps establish a well-defined boundary between external systems and other internal applications.

    In short, before you begin designing microservices, start by mapping the functions of the business and their connections to create a domain model for the microservice(s) to be built around.

    What challenges do Microservices introduce?

    Microservices solve a lot of problems and have several advantages, but the grass isn’t always greener on the other side.

    One of the key challenges of microservices is managing communication between services. Because services are independent, they need to communicate with each other through APIs. This can be complex and difficult to manage, especially as the number of services grows. To address this challenge, it is important to have a clear API design, with well-defined inputs and outputs for each service. It is also important to have a system for managing and monitoring communication between services, to ensure that everything is running smoothly.

    Another challenge of microservices is managing the deployment and scaling of services. Because each service is independent, it needs to be deployed and scaled separately from the rest of the application. This can be complex and difficult to manage, especially as the number of services grows. To address this challenge, it is important to have a clear and consistent deployment process, with well-defined steps for deploying and scaling each service. Furthermore, it is advisable to host them on a system with self-healing capabilities to reduce operational burden.

    It is also important to have a system for monitoring and managing the deployment and scaling of services, to ensure optimal performance.

    Each of these challenges has created fertile ground for tooling and process that exists in the cloud-native ecosystem. Kubernetes, CI CD, and other DevOps practices are part of the package of adopting the microservices architecture.

    Conclusion

    In summary, microservices architecture focuses on software applications as a collection of small, independent services that communicate with each other over well-defined APIs.

    The main advantages of microservices include:

    • increased flexibility and scalability per microservice,
    • efficient resource utilization (with help from a container orchestrator like Kubernetes),
    • and faster development cycles.

    Continue following along with this series to see how you can use Kubernetes to help adopt microservices patterns in your own environments!

    Resources

    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/cloud-native/page/6/index.html b/cnny-2023/tags/cloud-native/page/6/index.html index f90080d987..d5c43907d0 100644 --- a/cnny-2023/tags/cloud-native/page/6/index.html +++ b/cnny-2023/tags/cloud-native/page/6/index.html @@ -14,14 +14,14 @@ - - + +

    16 posts tagged with "cloud-native"

    View All Tags

    · 6 min read
    Cory Skimming

    We are excited to be wrapping up our first week of #CloudNativeNewYear! This week, we have tried to set the stage by covering the fundamentals of cloud-native practices and technologies, including primers on containerization, microservices, and Kubernetes.

    Don't forget to sign up for the the Cloud Skills Challenge!

    Today, we will do a brief recap of some of these technologies and provide some basic guidelines for when it is optimal to use each.


    What We'll Cover

    • To Containerize or not to Containerize?
    • The power of Kubernetes
    • Where does Serverless fit?
    • Resources
    • What's coming next!


    Just joining us now? Check out these other Week 1 posts:

    To Containerize or not to Containerize?

    As mentioned in our Containers 101 post earlier this week, containers can provide several benefits over traditional virtualization methods, which has made them popular within the software development community. Containers provide a consistent and predictable runtime environment, which can help reduce the risk of compatibility issues and simplify the deployment process. Additionally, containers can improve resource efficiency by allowing multiple applications to run on the same host while isolating their dependencies.

    Some types of apps that are a particularly good fit for containerization include:

    1. Microservices: Containers are particularly well-suited for microservices-based applications, as they can be used to isolate and deploy individual components of the system. This allows for more flexibility and scalability in the deployment process.
    2. Stateless applications: Applications that do not maintain state across multiple sessions, such as web applications, are well-suited for containers. Containers can be easily scaled up or down as needed and replaced with new instances, without losing data.
    3. Portable applications: Applications that need to be deployed in different environments, such as on-premises, in the cloud, or on edge devices, can benefit from containerization. The consistent and portable runtime environment of containers can make it easier to move the application between different environments.
    4. Legacy applications: Applications that are built using older technologies or that have compatibility issues can be containerized to run in an isolated environment, without impacting other applications or the host system.
    5. Dev and testing environments: Containerization can be used to create isolated development and testing environments, which can be easily created and destroyed as needed.

    While there are many types of applications that can benefit from a containerized approach, it's worth noting that containerization is not always the best option, and it's important to weigh the benefits and trade-offs before deciding to containerize an application. Additionally, some types of applications may not be a good fit for containers including:

    • Apps that require full access to host resources: Containers are isolated from the host system, so if an application needs direct access to hardware resources such as GPUs or specialized devices, it might not work well in a containerized environment.
    • Apps that require low-level system access: If an application requires deep access to the underlying operating system, it may not be suitable for running in a container.
    • Applications that have specific OS dependencies: Apps that have specific dependencies on a certain version of an operating system or libraries may not be able to run in a container.
    • Stateful applications: Apps that maintain state across multiple sessions, such as databases, may not be well suited for containers. Containers are ephemeral by design, so the data stored inside a container may not persist between restarts.

    The good news is that some of these limitations can be overcome with the use of specialized containerization technologies such as Kubernetes, and by carefully designing the architecture of the application.


    The power of Kubernetes

    Speaking of Kubernetes...

    Kubernetes is a powerful tool for managing and deploying containerized applications in production environments, particularly for applications that need to scale, handle large numbers of requests, or run in multi-cloud or hybrid environments.

    Kubernetes is well-suited for a wide variety of applications, but it is particularly well-suited for the following types of applications:

    1. Microservices-based applications: Kubernetes provides a powerful set of tools for managing and deploying microservices-based applications, making it easy to scale, update, and manage the individual components of the application.
    2. Stateful applications: Kubernetes provides support for stateful applications through the use of Persistent Volumes and StatefulSets, allowing for applications that need to maintain state across multiple instances.
    3. Large-scale, highly-available systems: Kubernetes provides built-in support for scaling, self-healing, and rolling updates, making it an ideal choice for large-scale, highly-available systems that need to handle large numbers of users and requests.
    4. Multi-cloud and hybrid environments: Kubernetes can be used to deploy and manage applications across multiple cloud providers and on-premises environments, making it a good choice for organizations that want to take advantage of the benefits of multiple cloud providers or that need to deploy applications in a hybrid environment.
    New to Kubernetes?

    Where does Serverless fit in?

    Serverless is a cloud computing model where the cloud provider (like Azure) is responsible for executing a piece of code by dynamically allocating the resources. With serverless, you only pay for the exact amount of compute time that you use, rather than paying for a fixed amount of resources. This can lead to significant cost savings, particularly for applications with variable or unpredictable workloads.

    Serverless is commonly used for building applications like web or mobile apps, IoT, data processing, and real-time streaming - apps where the workloads are variable and high scalability is required. It's important to note that serverless is not a replacement for all types of workloads - it's best suited for stateless, short-lived and small-scale workloads.

    For a detailed look into the world of Serverless and lots of great learning content, revisit #30DaysofServerless.


    Resources


    What's up next in #CloudNativeNewYear?

    Week 1 has been all about the fundamentals of cloud-native. Next week, the team will be diving in to application deployment with Azure Kubernetes Service. Don't forget to subscribe to the blog to get daily posts delivered directly to your favorite feed reader!


    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/cloud-native/page/7/index.html b/cnny-2023/tags/cloud-native/page/7/index.html index 82f401ff90..b109464b94 100644 --- a/cnny-2023/tags/cloud-native/page/7/index.html +++ b/cnny-2023/tags/cloud-native/page/7/index.html @@ -14,13 +14,13 @@ - - + +

    16 posts tagged with "cloud-native"

    View All Tags

    · 14 min read
    Steven Murawski

    Welcome to Day #1 of Week 2 of #CloudNativeNewYear!

    The theme for this week is Kubernetes fundamentals. Last week we talked about Cloud Native architectures and the Cloud Native landscape. Today we'll explore the topic of Pods and Deployments in Kubernetes.

    Ask the Experts Thursday, February 9th at 9 AM PST
    Catch the Replay of the Live Demo

    Watch the recorded demo and conversation about this week's topics.

    We were live on YouTube walking through today's (and the rest of this week's) demos.

    What We'll Cover

    • Setting Up A Kubernetes Environment in Azure
    • Running Containers in Kubernetes Pods
    • Making the Pods Resilient with Deployments
    • Exercise
    • Resources

    Setting Up A Kubernetes Environment in Azure

    For this week, we'll be working with a simple app - the Azure Voting App. My teammate Paul Yu ported the app to Rust and we tweaked it a bit to let us highlight some of the basic features of Kubernetes.

    You should be able to replicate this in just about any Kubernetes environment, but we'll use Azure Kubernetes Service (AKS) as our working environment for this week.

    To make it easier to get started, there's a Bicep template to deploy an AKS cluster, an Azure Container Registry (ACR) (to host our container image), and connect the two so that we can easily deploy our application.

    Step 0 - Prerequisites

    There are a few things you'll need if you want to work through this and the following examples this week.

    Required:

    • Git (and probably a GitHub account if you want to persist your work outside of your computer)
    • Azure CLI
    • An Azure subscription (if you want to follow along with the Azure steps)
    • Kubectl (the command line tool for managing Kubernetes)

    Helpful:

    • Visual Studio Code (or equivalent editor)

    Step 1 - Clone the application repository

    First, I forked the source repository to my account.

    $GitHubOrg = 'smurawski' # Replace this with your GitHub account name or org name
    git clone "https://github.com/$GitHubOrg/azure-voting-app-rust"
    cd azure-voting-app-rust

    Leave your shell opened with your current location inside the application repository.

    Step 2 - Set up AKS

    Running the template deployment from the demo script (I'm using the PowerShell example in cnny23-week2-day1.ps1, but there's a Bash variant at cnny23-week2-day1.sh) stands up the environment. The second, third, and fourth commands take some of the output from the Bicep deployment to set up for later commands, so don't close out your shell after you run these commands.

    az deployment sub create --template-file ./deploy/main.bicep --location eastus --parameters 'resourceGroup=cnny-week2'
    $AcrName = az deployment sub show --name main --query 'properties.outputs.acr_name.value' -o tsv
    $AksName = az deployment sub show --name main --query 'properties.outputs.aks_name.value' -o tsv
    $ResourceGroup = az deployment sub show --name main --query 'properties.outputs.resource_group_name.value' -o tsv

    az aks get-credentials --resource-group $ResourceGroup --name $AksName

    Step 3 - Build our application container

    Since we have an Azure Container Registry set up, I'll use ACR Build Tasks to build and store my container image.

    az acr build --registry $AcrName --% --image cnny2023/azure-voting-app-rust:{{.Run.ID}} .
    $BuildTag = az acr repository show-tags `
    --name $AcrName `
    --repository cnny2023/azure-voting-app-rust `
    --orderby time_desc `
    --query '[0]' -o tsv
    tip

    Wondering what the --% is in the first command line? That tells the PowerShell interpreter to pass the input after it "as is" to the command without parsing/evaluating it. Otherwise, PowerShell messes a bit with the templated {{.Run.ID}} bit.

    Running Containers in Kubernetes Pods

    Now that we have our AKS cluster and application image ready to go, let's look into how Kubernetes runs containers.

    If you've been in tech for any length of time, you've seen that every framework, runtime, orchestrator, etc.. can have their own naming scheme for their concepts. So let's get into some of what Kubernetes calls things.

    The Pod

    A container running in Kubernetes is called a Pod. A Pod is basically a running container on a Node or VM. It can be more. For example you can run multiple containers and specify some funky configuration, but we'll keep it simple for now - add the complexity when you need it.

    Our Pod definition can be created via the kubectl command imperatively from arguments or declaratively from a configuration file. We'll do a little of both. We'll use the kubectl command to help us write our configuration files. Kubernetes configuration files are YAML, so having an editor that supports and can help you syntax check YAML is really helpful.

    Creating a Pod Definition

    Let's create a few Pod definitions. Our application requires two containers to get working - the application and a database.

    Let's create the database Pod first. And before you comment, the configuration isn't secure nor best practice. We'll fix that later this week. For now, let's focus on getting up and running.

    This is a trick I learned from one of my teammates - Paul. By using the --output yaml and --dry-run=client options, we can have the command help us write our YAML. And with a bit of output redirection, we can stash it safely in a file for later use.

    kubectl run azure-voting-db `
    --image "postgres:15.0-alpine" `
    --env "POSTGRES_PASSWORD=mypassword" `
    --output yaml `
    --dry-run=client > manifests/pod-db.yaml

    This creates a file that looks like:

    apiVersion: v1
    kind: Pod
    metadata:
    creationTimestamp: null
    labels:
    run: azure-voting-db
    name: azure-voting-db
    spec:
    containers:
    - env:
    - name: POSTGRES_PASSWORD
    value: mypassword
    image: postgres:15.0-alpine
    name: azure-voting-db
    resources: {}
    dnsPolicy: ClusterFirst
    restartPolicy: Always
    status: {}

    The file, when supplied to the Kubernetes API, will identify what kind of resource to create, the API version to use, and the details of the container (as well as an environment variable to be supplied).

    We'll get that container image started with the kubectl command. Because the details of what to create are in the file, we don't need to specify much else to the kubectl command but the path to the file.

    kubectl apply -f ./manifests/pod-db.yaml

    I'm going to need the IP address of the Pod, so that my application can connect to it, so we can use kubectl to get some information about our pod. By default, kubectl get pod only displays certain information but it retrieves a lot more. We can use the JSONPath syntax to index into the response and get the information you want.

    tip

    To see what you can get, I usually run the kubectl command with the output type (-o JSON) of JSON and then I can find where the data I want is and create my JSONPath query to get it.

    $DB_IP = kubectl get pod azure-voting-db -o jsonpath='{.status.podIP}'

    Now, let's create our Pod definition for our application. We'll use the same technique as before.

    kubectl run azure-voting-app `
    --image "$AcrName.azurecr.io/cnny2023/azure-voting-app-rust:$BuildTag" `
    --env "DATABASE_SERVER=$DB_IP" `
    --env "DATABASE_PASSWORD=mypassword`
    --output yaml `
    --dry-run=client > manifests/pod-app.yaml

    That command gets us a similar YAML file to the database container - you can see the full file here

    Let's get our application container running.

    kubectl apply -f ./manifests/pod-app.yaml

    Now that the Application is Running

    We can check the status of our Pods with:

    kubectl get pods

    And we should see something like:

    azure-voting-app-rust ❯  kubectl get pods
    NAME READY STATUS RESTARTS AGE
    azure-voting-app 1/1 Running 0 36s
    azure-voting-db 1/1 Running 0 84s

    Once our pod is running, we can check to make sure everything is working by letting kubectl proxy network connections to our Pod running the application. If we get the voting web page, we'll know the application found the database and we can start voting!

    kubectl port-forward pod/azure-voting-app 8080:8080

    Azure voting website in a browser with three buttons, one for Dogs, one for Cats, and one for Reset.  The counter is Dogs - 0 and Cats - 0.

    When you are done voting, you can stop the port forwarding by using Control-C to break the command.

    Clean Up

    Let's clean up after ourselves and see if we can't get Kubernetes to help us keep our application running. We can use the same configuration files to ensure that Kubernetes only removes what we want removed.

    kubectl delete -f ./manifests/pod-app.yaml
    kubectl delete -f ./manifests/pod-db.yaml

    Summary - Pods

    A Pod is the most basic unit of work inside Kubernetes. Once the Pod is deleted, it's gone. That leads us to our next topic (and final topic for today.)

    Making the Pods Resilient with Deployments

    We've seen how easy it is to deploy a Pod and get our containers running on Nodes in our Kubernetes cluster. But there's a problem with that. Let's illustrate it.

    Breaking Stuff

    Setting Back Up

    First, let's redeploy our application environment. We'll start with our application container.

    kubectl apply -f ./manifests/pod-db.yaml
    kubectl get pod azure-voting-db -o jsonpath='{.status.podIP}'

    The second command will report out the new IP Address for our database container. Let's open ./manifests/pod-app.yaml and update the container IP to our new one.

    - name: DATABASE_SERVER
    value: YOUR_NEW_IP_HERE

    Then we can deploy the application with the information it needs to find its database. We'll also list out our pods to see what is running.

    kubectl apply -f ./manifests/pod-app.yaml
    kubectl get pods

    Feel free to look back and use the port forwarding trick to make sure your app is running if you'd like.

    Knocking It Down

    The first thing we'll try to break is our application pod. Let's delete it.

    kubectl delete pod azure-voting-app

    Then, we'll check our pod's status:

    kubectl get pods

    Which should show something like:

    azure-voting-app-rust ❯  kubectl get pods
    NAME READY STATUS RESTARTS AGE
    azure-voting-db 1/1 Running 0 50s

    We should be able to recreate our application pod deployment with no problem, since it has the current database IP address and nothing else depends on it.

    kubectl apply -f ./manifests/pod-app.yaml

    Again, feel free to do some fun port forwarding and check your site is running.

    Uncomfortable Truths

    Here's where it gets a bit stickier, what if we delete the database container?

    If we delete our database container and recreate it, it'll likely have a new IP address, which would force us to update our application configuration. We'll look at some solutions for these problems in the next three posts this week.

    Because our database problem is a bit tricky, we'll primarily focus on making our application layer more resilient and prepare our database layer for those other techniques over the next few days.

    Let's clean back up and look into making things more resilient.

    kubectl delete -f ./manifests/pod-app.yaml
    kubectl delete -f ./manifests/pod-db.yaml

    The Deployment

    One of the reasons you may want to use Kubernetes is it's ability to orchestrate workloads. Part of that orchestration includes being able to ensure that certain workloads are running (regardless of what Node they might be on).

    We saw that we could delete our application pod and then restart it from the manifest with little problem. It just meant that we had to run a command to restart it. We can use the Deployment in Kubernetes to tell the orchestrator to ensure we have our application pod running.

    The Deployment also can encompass a lot of extra configuration - controlling how many containers of a particular type should be running, how upgrades of container images should proceed, and more.

    Creating the Deployment

    First, we'll create a Deployment for our database. We'll use a technique similar to what we did for the Pod, with just a bit of difference.

    kubectl create deployment azure-voting-db `
    --image "postgres:15.0-alpine" `
    --port 5432 `
    --output yaml `
    --dry-run=client > manifests/deployment-db.yaml

    Unlike our Pod definition creation, we can't pass in environment variable configuration from the command line. We'll have to edit the YAML file to add that.

    So, let's open ./manifests/deployment-db.yaml in our editor and add the following in the spec/containers configuration.

            env:
    - name: POSTGRES_PASSWORD
    value: "mypassword"

    Your file should look like this deployment-db.yaml.

    Once we have our configuration file updated, we can deploy our database container image.

    kubectl apply -f ./manifests/deployment-db.yaml

    For our application, we'll use the same technique.

    kubectl create deployment azure-voting-app `
    --image "$AcrName.azurecr.io/cnny2023/azure-voting-app-rust:$BuildTag" `
    --port 8080 `
    --output yaml `
    --dry-run=client > manifests/deployment-app.yaml

    Next, we'll need to add an environment variable to the generated configuration. We'll also need the new IP address for the database deployment.

    Previously, we named the pod and were able to ask for the IP address with kubectl and a bit of JSONPath. Now, the deployment created the pod for us, so there's a bit of random in the naming. Check out:

    kubectl get pods

    Should return something like:

    azure-voting-app-rust ❯  kubectl get pods
    NAME READY STATUS RESTARTS AGE
    azure-voting-db-686d758fbf-8jnq8 1/1 Running 0 7s

    We can either ask for the IP with the new pod name, or we can use a selector to find our desired pod.

    kubectl get pod --selector app=azure-voting-db -o jsonpath='{.items[0].status.podIP}'

    Now, we can update our application deployment configuration file with:

            env:
    - name: DATABASE_SERVER
    value: YOUR_NEW_IP_HERE
    - name: DATABASE_PASSWORD
    value: mypassword

    Your file should look like this deployment-app.yaml (but with IPs and image names matching your environment).

    After we save those changes, we can deploy our application.

    kubectl apply -f ./manifests/deployment-app.yaml

    Let's test the resilience of our app now. First, we'll delete the pod running our application, then we'll check to make sure Kubernetes restarted our application pod.

    kubectl get pods
    azure-voting-app-rust ❯  kubectl get pods
    NAME READY STATUS RESTARTS AGE
    azure-voting-app-56c9ccc89d-skv7x 1/1 Running 0 71s
    azure-voting-db-686d758fbf-8jnq8 1/1 Running 0 12m
    kubectl delete pod azure-voting-app-56c9ccc89d-skv7x
    kubectl get pods
    azure-voting-app-rust ❯  kubectl delete pod azure-voting-app-56c9ccc89d-skv7x
    >> kubectl get pods
    pod "azure-voting-app-56c9ccc89d-skv7x" deleted
    NAME READY STATUS RESTARTS AGE
    azure-voting-app-56c9ccc89d-2b5mx 1/1 Running 0 2s
    azure-voting-db-686d758fbf-8jnq8 1/1 Running 0 14m
    info

    Your Pods will likely have different identifiers at the end, so adjust your commands to match the names in your environment.

    As you can see, by the time the kubectl get pods command was run, Kubernetes had already spun up a new pod for the application container image. Thanks Kubernetes!

    Clean up

    Since we can't just delete the pods, we have to delete the deployments.

    kubectl delete -f ./manifests/deployment-app.yaml
    kubectl delete -f ./manifests/deployment-db.yaml

    Summary - Deployments

    Deployments allow us to create more durable configuration for the workloads we deploy into Kubernetes. As we dig deeper, we'll discover more capabilities the deployments offer. Check out the Resources below for more.

    Exercise

    If you want to try these steps, head over to the source repository, fork it, clone it locally, and give it a spin!

    You can check your manifests against the manifests in the week2/day1 branch of the source repository.

    Resources

    Take the Cloud Skills Challenge!

    Enroll in the Cloud Skills Challenge!

    Don't miss out on this opportunity to level up your skills and stay ahead of the curve in the world of cloud native.

    Documentation

    Training

    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/cloud-native/page/8/index.html b/cnny-2023/tags/cloud-native/page/8/index.html index 112c1cf2c2..de2b996fff 100644 --- a/cnny-2023/tags/cloud-native/page/8/index.html +++ b/cnny-2023/tags/cloud-native/page/8/index.html @@ -14,14 +14,14 @@ - - + +

    16 posts tagged with "cloud-native"

    View All Tags

    · 6 min read
    Josh Duffney

    Welcome to Day 3 of Week 2 of #CloudNativeNewYear!

    The theme for this week is Kubernetes fundamentals. Yesterday we talked about Services and Ingress. Today we'll explore the topic of passing configuration and secrets to our applications in Kubernetes with ConfigMaps and Secrets.

    Ask the Experts Thursday, February 9th at 9 AM PST
    Catch the Replay of the Live Demo

    Watch the recorded demo and conversation about this week's topics.

    We were live on YouTube walking through today's (and the rest of this week's) demos.

    What We'll Cover

    • Decouple configurations with ConfigMaps and Secerts
    • Passing Environment Data with ConfigMaps and Secrets
    • Conclusion

    Decouple configurations with ConfigMaps and Secerts

    A ConfigMap is a Kubernetes object that decouples configuration data from pod definitions. Kubernetes secerts are similar, but were designed to decouple senstive information.

    Separating the configuration and secerts from your application promotes better organization and security of your Kubernetes environment. It also enables you to share the same configuration and different secerts across multiple pods and deployments which can simplify scaling and management. Using ConfigMaps and Secerts in Kubernetes is a best practice that can help to improve the scalability, security, and maintainability of your cluster.

    By the end of this tutorial, you'll have added a Kubernetes ConfigMap and Secret to the Azure Voting deployment.

    Passing Environment Data with ConfigMaps and Secrets

    📝 NOTE: If you don't have an AKS cluster deployed, please head over to Azure-Samples/azure-voting-app-rust, clone the repo, and follow the instructions in the README.md to execute the Azure deployment and setup your kubectl context. Check out the first post this week for more on the environment setup.

    Create the ConfigMap

    ConfigMaps can be used in one of two ways; as environment variables or volumes.

    For this tutorial you'll use a ConfigMap to create three environment variables inside the pod; DATABASE_SERVER, FISRT_VALUE, and SECOND_VALUE. The DATABASE_SERVER provides part of connection string to a Postgres. FIRST_VALUE and SECOND_VALUE are configuration options that change what voting options the application presents to the users.

    Follow the below steps to create a new ConfigMap:

    1. Create a YAML file named 'config-map.yaml'. In this file, specify the environment variables for the application.

      apiVersion: v1
      kind: ConfigMap
      metadata:
      name: azure-voting-config
      data:
      DATABASE_SERVER: azure-voting-db
      FIRST_VALUE: "Go"
      SECOND_VALUE: "Rust"
    2. Create the config map in your Kubernetes cluster by running the following command:

      kubectl create -f config-map.yaml

    Create the Secret

    The deployment-db.yaml and deployment-app.yaml are Kubernetes manifests that deploy the Azure Voting App. Currently, those deployment manifests contain the environment variables POSTGRES_PASSWORD and DATABASE_PASSWORD with the value stored as plain text. Your task is to replace that environment variable with a Kubernetes Secret.

    Create a Secret running the following commands:

    1. Encode mypassword.

      echo -n "mypassword" | base64
    2. Create a YAML file named secret.yaml. In this file, add POSTGRES_PASSWORD as the key and the encoded value returned above under as the value in the data section.

      apiVersion: v1
      kind: Secret
      metadata:
      name: azure-voting-secret
      type: Opaque
      data:
      POSTGRES_PASSWORD: bXlwYXNzd29yZA==
    3. Create the Secret in your Kubernetes cluster by running the following command:

      kubectl create -f secret.yaml

    [!WARNING] base64 encoding is a simple and widely supported way to obscure plaintext data, it is not secure, as it can easily be decoded. If you want to store sensitive data like password, you should use a more secure method like encrypting with a Key Management Service (KMS) before storing it in the Secret.

    Modify the app deployment manifest

    With the ConfigMap and Secert both created the next step is to replace the environment variables provided in the application deployment manuscript with the values stored in the ConfigMap and the Secert.

    Complete the following steps to add the ConfigMap and Secert to the deployment mainifest:

    1. Open the Kubernetes manifest file deployment-app.yaml.

    2. In the containers section, add an envFrom section and upate the env section.

      envFrom:
      - configMapRef:
      name: azure-voting-config
      env:
      - name: DATABASE_PASSWORD
      valueFrom:
      secretKeyRef:
      name: azure-voting-secret
      key: POSTGRES_PASSWORD

      Using envFrom exposes all the values witin the ConfigMap as environment variables. Making it so you don't have to list them individually.

    3. Save the changes to the deployment manifest file.

    4. Apply the changes to the deployment by running the following command:

      kubectl apply -f deployment-app.yaml

    Modify the database deployment manifest

    Next, update the database deployment manifest and replace the plain text environment variable with the Kubernetes Secert.

    1. Open the deployment-db.yaml.

    2. To add the secret to the deployment, replace the env section with the following code:

      env:
      - name: POSTGRES_PASSWORD
      valueFrom:
      secretKeyRef:
      name: azure-voting-secret
      key: POSTGRES_PASSWORD
    3. Apply the updated manifest.

      kubectl apply -f deployment-db.yaml

    Verify the ConfigMap and output environment variables

    Verify that the ConfigMap was added to your deploy by running the following command:

    ```bash
    kubectl describe deployment azure-voting-app
    ```

    Browse the output until you find the envFrom section with the config map reference.

    You can also verify that the environment variables from the config map are being passed to the container by running the command kubectl exec -it <pod-name> -- printenv. This command will show you all the environment variables passed to the pod including the one from configmap.

    By following these steps, you will have successfully added a config map to the Azure Voting App Kubernetes deployment, and the environment variables defined in the config map will be passed to the container running in the pod.

    Verify the Secret and describe the deployment

    Once the secret has been created you can verify it exists by running the following command:

    kubectl get secrets

    You can view additional information, such as labels, annotations, type, and the Data by running kubectl describe:

    kubectl describe secret azure-voting-secret

    By default, the describe command doesn't output the encoded value, but if you output the results as JSON or YAML you'll be able to see the secret's encoded value.

     kubectl get secret azure-voting-secret -o json

    Conclusion

    In conclusion, using ConfigMaps and Secrets in Kubernetes can help to improve the scalability, security, and maintainability of your cluster. By decoupling configuration data and sensitive information from pod definitions, you can promote better organization and security in your Kubernetes environment. Additionally, separating these elements allows for sharing the same configuration and different secrets across multiple pods and deployments, simplifying scaling and management.

    Take the Cloud Skills Challenge!

    Enroll in the Cloud Skills Challenge!

    Don't miss out on this opportunity to level up your skills and stay ahead of the curve in the world of cloud native.

    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/cloud-native/page/9/index.html b/cnny-2023/tags/cloud-native/page/9/index.html index b4b0b55c7e..a70dd61729 100644 --- a/cnny-2023/tags/cloud-native/page/9/index.html +++ b/cnny-2023/tags/cloud-native/page/9/index.html @@ -14,13 +14,13 @@ - - + +

    16 posts tagged with "cloud-native"

    View All Tags

    · 10 min read
    Steven Murawski

    Welcome to Day 5 of Week 2 of #CloudNativeNewYear!

    The theme for this week is Kubernetes fundamentals. Yesterday we talked about adding persistent storage to our deployment. Today we'll explore the topic of scaling pods and nodes in our Kubernetes cluster.

    Ask the Experts Thursday, February 9th at 9 AM PST
    Catch the Replay of the Live Demo

    Watch the recorded demo and conversation about this week's topics.

    We were live on YouTube walking through today's (and the rest of this week's) demos.

    What We'll Cover

    • Scaling Our Application
    • Scaling Pods
    • Scaling Nodes
    • Exercise
    • Resources

    Scaling Our Application

    One of our primary reasons to use a service like Kubernetes to orchestrate our workloads is the ability to scale. We've approached scaling in a multitude of ways over the years, taking advantage of the ever-evolving levels of hardware and software. Kubernetes allows us to scale our units of work, Pods, and the Nodes they run on. This allows us to take advantage of both hardware and software scaling abilities. Kubernetes can help improve the utilization of existing hardware (by scheduling Pods on Nodes that have resource capacity). And, with the capabilities of virtualization and/or cloud hosting (or a bit more work, if you have a pool of physical machines), Kubernetes can expand (or contract) the number of Nodes capable of hosting Pods. Scaling is primarily driven by resource utilization, but can be triggered by a variety of other sources thanks to projects like Kubernetes Event-driven Autoscaling (KEDA).

    Scaling Pods

    Our first level of scaling is with our Pods. Earlier, when we worked on our deployment, we talked about how the Kubernetes would use the deployment configuration to ensure that we had the desired workloads running. One thing we didn't explore was running more than one instance of a pod. We can define a number of replicas of a pod in our Deployment.

    Manually Scale Pods

    So, if we wanted to define more pods right at the start (or at any point really), we could update our deployment configuration file with the number of replicas and apply that configuration file.

    spec:
    replicas: 5

    Or we could use the kubectl scale command to update the deployment with a number of pods to create.

    kubectl scale --replicas=5 deployment/azure-voting-app

    Both of these approaches modify the running configuration of our Kubernetes cluster and request that it ensure that we have that set number of replicas running. Because this was a manual change, the Kubernetes cluster won't automatically increase or decrease the number of pods. It'll just ensure that there are always the specified number of pods running.

    Autoscale Pods with the Horizontal Pod Autoscaler

    Another approach to scaling our pods is to allow the Horizontal Pod Autoscaler to help us scale in response to resources being used by the pod. This requires a bit more configuration up front. When we define our pod in our deployment, we need to include resource requests and limits. The requests help Kubernetes determine what nodes may have capacity for a new instance of a pod. The limit tells us where the node should cap utilization for a particular instance of a pod. For example, we'll update our deployment to request 0.25 CPU and set a limit of 0.5 CPU.

        spec:
    containers:
    - image: acrudavoz.azurecr.io/cnny2023/azure-voting-app-rust:ca4
    name: azure-voting-app-rust
    ports:
    - containerPort: 8080
    env:
    - name: DATABASE_URL
    value: postgres://postgres:mypassword@10.244.0.29
    resources:
    requests:
    cpu: 250m
    limits:
    cpu: 500m

    Now that we've given Kubernetes an allowed range and an idea of what free resources a node should have to place new pods, we can set up autoscaling. Because autoscaling is a persistent configuration, I like to define it in a configuration file that I'll be able to keep with the rest of my cluster configuration. We'll use the kubectl command to help us write the configuration file. We'll request that Kubernetes watch our pods and when the average CPU utilization if 50% of the requested usage (in our case if it's using more than 0.375 CPU across the current number of pods), it can grow the number of pods serving requests up to 10. If the utilization drops, Kubernetes will have the permission to deprovision pods down to the minimum (three in our example).

    kubectl autoscale deployment azure-voting-app --cpu-percent=50 --min=3 --max=10 -o YAML --dry-run=client

    Which would give us:

    apiVersion: autoscaling/v1
    kind: HorizontalPodAutoscaler
    metadata:
    creationTimestamp: null
    name: azure-voting-app
    spec:
    maxReplicas: 10
    minReplicas: 3
    scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: azure-voting-app
    targetCPUUtilizationPercentage: 50
    status:
    currentReplicas: 0
    desiredReplicas: 0

    So, how often does the autoscaler check the metrics being monitored? The autoscaler checks the Metrics API every 15 seconds, however the pods stats are only updated every 60 seconds. This means that an autoscale event may be evaluated about once a minute. Once an autoscale down event happens however, Kubernetes has a cooldown period to give the new pods a chance to distribute the workload and let the new metrics accumulate. There is no delay on scale up events.

    Application Architecture Considerations

    We've focused in this example on our front end, which is an easier scaling story. When we start talking about scaling our database layers or anything that deals with persistent storage or has primary/replica configuration requirements things get a bit more complicated. Some of these applications may have built-in leader election or could use sidecars to help use existing features in Kubernetes to perform that function. For shared storage scenarios, persistent volumes (or persistent volumes with Azure) can be of help, if the application knows how to play well with shared file access.

    Ultimately, you know your application architecture and, while Kubernetes may not have an exact match to how you are doing things today, the underlying capability is probably there under a different name. This abstraction allows you to more effectively use Kubernetes to operate a variety of workloads with the levels of controls you need.

    Scaling Nodes

    We've looked at how to scale our pods, but that assumes we have enough resources in our existing pool of nodes to accomodate those scaling requests. Kubernetes can also help scale our available nodes to ensure that our applications have the necessary resources to meet their performance requirements.

    Manually Scale Nodes

    Manually scaling nodes isn't a direct function of Kubernetes, so your operating environment instructions may vary. On Azure, it's pretty straight forward. Using the Azure CLI (or other tools), we can tell our AKS cluster to scale up or scale down the number of nodes in our node pool.

    First, we'll check out how many nodes we currently have in our working environment.

    kubectl get nodes

    This will show us

    azure-voting-app-rust ❯  kubectl get nodes
    NAME STATUS ROLES AGE VERSION
    aks-pool0-37917684-vmss000000 Ready agent 5d21h v1.24.6

    Then, we'll scale it up to three nodes.

    az aks scale --resource-group $ResourceGroup --name $AksName --node-count 3

    Then, we'll check out how many nodes we now have in our working environment.

    kubectl get nodes

    Which returns:

    azure-voting-app-rust ❯  kubectl get nodes
    NAME STATUS ROLES AGE VERSION
    aks-pool0-37917684-vmss000000 Ready agent 5d21h v1.24.6
    aks-pool0-37917684-vmss000001 Ready agent 5m27s v1.24.6
    aks-pool0-37917684-vmss000002 Ready agent 5m10s v1.24.6

    Autoscale Nodes with the Cluster Autoscaler

    Things get more interesting when we start working with the Cluster Autoscaler. The Cluster Autoscaler watches for the inability of Kubernetes to schedule the required number of pods due to resource constraints (and a few other criteria like affinity/anti-affinity). If there are insufficient resources available on the existing nodes, the autoscaler can provision new nodes into the nodepool. Likewise, the autoscaler watches to see if the existing pods could be consolidated to a smaller set of nodes and can remove excess nodes.

    Enabling the autoscaler is likewise an update that can be dependent on where and how your Kubernetes cluster is hosted. Azure makes it easy with a simple Azure CLI command.

    az aks update `
    --resource-group $ResourceGroup `
    --name $AksName `
    --update-cluster-autoscaler `
    --min-count 1 `
    --max-count 5

    There are a variety of settings that can be configured to tune how the autoscaler works.

    Scaling on Different Events

    CPU and memory utilization are the primary drivers for the Horizontal Pod Autoscaler, but those might not be the best measures as to when you might want to scale workloads. There are other options for scaling triggers and one of the more common plugins to help with that is the Kubernetes Event-driven Autoscaling (KEDA) project. The KEDA project makes it easy to plug in different event sources to help drive scaling. Find more information about using KEDA on AKS here.

    Exercise

    Let's try out the scaling configurations that we just walked through using our sample application. If you still have your environment from Day 1, you can use that.

    📝 NOTE: If you don't have an AKS cluster deployed, please head over to Azure-Samples/azure-voting-app-rust, clone the repo, and follow the instructions in the README.md to execute the Azure deployment and setup your kubectl context. Check out the first post this week for more on the environment setup.

    Configure Horizontal Pod Autoscaler

    • Edit ./manifests/deployment-app.yaml to include resource requests and limits.
            resources:
    requests:
    cpu: 250m
    limits:
    cpu: 500m
    • Apply the updated deployment configuration.
    kubectl apply -f ./manifests/deployment-app.yaml
    • Create the horizontal pod autoscaler configuration and apply it
    kubectl autoscale deployment azure-voting-app --cpu-percent=50 --min=3 --max=10 -o YAML --dry-run=client > ./manifests/scaler-app.yaml
    kubectl apply -f ./manifests/scaler-app.yaml
    • Check to see your pods scale out to the minimum.
    kubectl get pods

    Configure Cluster Autoscaler

    Configuring the basic behavior of the Cluster Autoscaler is a bit simpler. We just need to run the Azure CLI command to enable the autoscaler and define our lower and upper limits.

    • Check the current nodes available (should be 1).
    kubectl get nodes
    • Update the cluster to enable the autoscaler
    az aks update `
    --resource-group $ResourceGroup `
    --name $AksName `
    --update-cluster-autoscaler `
    --min-count 2 `
    --max-count 5
    • Check to see the current number of nodes (should be 2 now).
    kubectl get nodes

    Resources

    Take the Cloud Skills Challenge!

    Enroll in the Cloud Skills Challenge!

    Don't miss out on this opportunity to level up your skills and stay ahead of the curve in the world of cloud native.

    Documentation

    Training

    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/configmaps/index.html b/cnny-2023/tags/configmaps/index.html index 6dcd9cd7e0..180a96d196 100644 --- a/cnny-2023/tags/configmaps/index.html +++ b/cnny-2023/tags/configmaps/index.html @@ -14,13 +14,13 @@ - - + +

    One post tagged with "configmaps"

    View All Tags

    · 12 min read
    Paul Yu

    Welcome to Day 2 of Week 3 of #CloudNativeNewYear!

    The theme for this week is Bringing Your Application to Kubernetes. Yesterday we talked about getting an existing application running in Kubernetes with a full pipeline in GitHub Actions. Today we'll evaluate our sample application's configuration, storage, and networking requirements and implement using Kubernetes and Azure resources.

    Ask the Experts Thursday, February 9th at 9 AM PST
    Friday, February 10th at 11 AM PST

    Watch the recorded demo and conversation about this week's topics.

    We were live on YouTube walking through today's (and the rest of this week's) demos. Join us Friday, February 10th and bring your questions!

    What We'll Cover

    • Gather requirements
    • Implement environment variables using ConfigMaps
    • Implement persistent volumes using Azure Files
    • Implement secrets using Azure Key Vault
    • Re-package deployments
    • Conclusion
    • Resources
    caution

    Before you begin, make sure you've gone through yesterday's post to set up your AKS cluster.

    Gather requirements

    The eShopOnWeb application is written in .NET 7 and has two major pieces of functionality. The web UI is where customers can browse and shop. The web UI also includes an admin portal for managing the product catalog. This admin portal, is packaged as a WebAssembly application and relies on a separate REST API service. Both the web UI and the REST API connect to the same SQL Server container.

    Looking through the source code which can be found here we can identify requirements for configs, persistent storage, and secrets.

    Database server

    • Need to store the password for the sa account as a secure secret
    • Need persistent storage volume for data directory
    • Need to inject environment variables for SQL Server license type and EULA acceptance

    Web UI and REST API service

    • Need to store database connection string as a secure secret
    • Need to inject ASP.NET environment variables to override app settings
    • Need persistent storage volume for ASP.NET key storage

    Implement environment variables using ConfigMaps

    ConfigMaps are relatively straight-forward to create. If you were following along with the examples last week, this should be review 😉

    Create a ConfigMap to store database environment variables.

    kubectl apply -f - <<EOF
    apiVersion: v1
    kind: ConfigMap
    metadata:
    name: mssql-settings
    data:
    MSSQL_PID: Developer
    ACCEPT_EULA: "Y"
    EOF

    Create another ConfigMap to store ASP.NET environment variables.

    kubectl apply -f - <<EOF
    apiVersion: v1
    kind: ConfigMap
    metadata:
    name: aspnet-settings
    data:
    ASPNETCORE_ENVIRONMENT: Development
    EOF

    Implement persistent volumes using Azure Files

    Similar to last week, we'll take advantage of storage classes built into AKS. For our SQL Server data, we'll use the azurefile-csi-premium storage class and leverage an Azure Files resource as our PersistentVolume.

    Create a PersistentVolumeClaim (PVC) for persisting SQL Server data.

    kubectl apply -f - <<EOF
    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
    name: mssql-data
    spec:
    accessModes:
    - ReadWriteMany
    storageClassName: azurefile-csi-premium
    resources:
    requests:
    storage: 5Gi
    EOF

    Create another PVC for persisting ASP.NET data.

    kubectl apply -f - <<EOF
    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
    name: aspnet-data
    spec:
    accessModes:
    - ReadWriteMany
    storageClassName: azurefile-csi-premium
    resources:
    requests:
    storage: 5Gi
    EOF

    Implement secrets using Azure Key Vault

    It's a well known fact that Kubernetes secretes are not really secrets. They're just base64-encoded values and not secure, especially if malicious users have access to your Kubernetes cluster.

    In a production scenario, you will want to leverage an external vault like Azure Key Vault or HashiCorp Vault to encrypt and store secrets.

    With AKS, we can enable the Secrets Store CSI driver add-on which will allow us to leverage Azure Key Vault.

    # Set some variables
    RG_NAME=<YOUR_RESOURCE_GROUP_NAME>
    AKS_NAME=<YOUR_AKS_CLUSTER_NAME>
    ACR_NAME=<YOUR_ACR_NAME>

    az aks enable-addons \
    --addons azure-keyvault-secrets-provider \
    --name $AKS_NAME \
    --resource-group $RG_NAME

    With the add-on enabled, you should see aks-secrets-store-csi-driver and aks-secrets-store-provider-azure resources installed on each node in your Kubernetes cluster.

    Run the command below to verify.

    kubectl get pods \
    --namespace kube-system \
    --selector 'app in (secrets-store-csi-driver, secrets-store-provider-azure)'

    The Secrets Store CSI driver allows us to use secret stores via Container Storage Interface (CSI) volumes. This provider offers capabilities such as mounting and syncing between the secure vault and Kubernetes Secrets. On AKS, the Azure Key Vault Provider for Secrets Store CSI Driver enables integration with Azure Key Vault.

    You may not have an Azure Key Vault created yet, so let's create one and add some secrets to it.

    AKV_NAME=$(az keyvault create \
    --name akv-eshop$RANDOM \
    --resource-group $RG_NAME \
    --query name -o tsv)

    # Database server password
    az keyvault secret set \
    --vault-name $AKV_NAME \
    --name mssql-password \
    --value "@someThingComplicated1234"

    # Catalog database connection string
    az keyvault secret set \
    --vault-name $AKV_NAME \
    --name mssql-connection-catalog \
    --value "Server=db;Database=Microsoft.eShopOnWeb.CatalogDb;User Id=sa;Password=@someThingComplicated1234;TrustServerCertificate=True;"

    # Identity database connection string
    az keyvault secret set \
    --vault-name $AKV_NAME \
    --name mssql-connection-identity \
    --value "Server=db;Database=Microsoft.eShopOnWeb.Identity;User Id=sa;Password=@someThingComplicated1234;TrustServerCertificate=True;"

    Pods authentication using Azure Workload Identity

    In order for our Pods to retrieve secrets from Azure Key Vault, we'll need to set up a way for the Pod to authenticate against Azure AD. This can be achieved by implementing the new Azure Workload Identity feature of AKS.

    info

    At the time of this writing, the workload identity feature of AKS is in Preview.

    The workload identity feature within AKS allows us to leverage native Kubernetes resources and link a Kubernetes ServiceAccount to an Azure Managed Identity to authenticate against Azure AD.

    For the authentication flow, our Kubernetes cluster will act as an Open ID Connect (OIDC) issuer and will be able issue identity tokens to ServiceAccounts which will be assigned to our Pods.

    The Azure Managed Identity will be granted permission to access secrets in our Azure Key Vault and with the ServiceAccount being assigned to our Pods, they will be able to retrieve our secrets.

    For more information on how the authentication mechanism all works, check out this doc.

    To implement all this, start by enabling the new preview feature for AKS.

    az feature register \
    --namespace "Microsoft.ContainerService" \
    --name "EnableWorkloadIdentityPreview"
    caution

    This can take several minutes to complete.

    Check the status and ensure the state shows Regestered before moving forward.

    az feature show \
    --namespace "Microsoft.ContainerService" \
    --name "EnableWorkloadIdentityPreview"

    Update your AKS cluster to enable the workload identity feature and enable the OIDC issuer endpoint.

    az aks update \
    --name $AKS_NAME \
    --resource-group $RG_NAME \
    --enable-workload-identity \
    --enable-oidc-issuer

    Create an Azure Managed Identity and retrieve its client ID.

    MANAGED_IDENTITY_CLIENT_ID=$(az identity create \
    --name aks-workload-identity \
    --resource-group $RG_NAME \
    --subscription $(az account show --query id -o tsv) \
    --query 'clientId' -o tsv)

    Create the Kubernetes ServiceAccount.

    # Set namespace (this must align with the namespace that your app is deployed into)
    SERVICE_ACCOUNT_NAMESPACE=default

    # Set the service account name
    SERVICE_ACCOUNT_NAME=eshop-serviceaccount

    # Create the service account
    kubectl apply -f - <<EOF
    apiVersion: v1
    kind: ServiceAccount
    metadata:
    annotations:
    azure.workload.identity/client-id: ${MANAGED_IDENTITY_CLIENT_ID}
    labels:
    azure.workload.identity/use: "true"
    name: ${SERVICE_ACCOUNT_NAME}
    namespace: ${SERVICE_ACCOUNT_NAMESPACE}
    EOF
    info

    Note to enable this ServiceAccount to work with Azure Workload Identity, you must annotate the resource with azure.workload.identity/client-id, and add a label of azure.workload.identity/use: "true"

    That was a lot... Let's review what we just did.

    We have an Azure Managed Identity (object in Azure AD), an OIDC issuer URL (endpoint in our Kubernetes cluster), and a Kubernetes ServiceAccount.

    The next step is to "tie" these components together and establish a Federated Identity Credential so that Azure AD can trust authentication requests from your Kubernetes cluster.

    info

    This identity federation can be established between Azure AD any Kubernetes cluster; not just AKS 🤗

    To establish the federated credential, we'll need the OIDC issuer URL, and a subject which points to your Kubernetes ServiceAccount.

    # Get the OIDC issuer URL
    OIDC_ISSUER_URL=$(az aks show \
    --name $AKS_NAME \
    --resource-group $RG_NAME \
    --query "oidcIssuerProfile.issuerUrl" -o tsv)

    # Set the subject name using this format: `system:serviceaccount:<YOUR_SERVICE_ACCOUNT_NAMESPACE>:<YOUR_SERVICE_ACCOUNT_NAME>`
    SUBJECT=system:serviceaccount:$SERVICE_ACCOUNT_NAMESPACE:$SERVICE_ACCOUNT_NAME

    az identity federated-credential create \
    --name aks-federated-credential \
    --identity-name aks-workload-identity \
    --resource-group $RG_NAME \
    --issuer $OIDC_ISSUER_URL \
    --subject $SUBJECT

    With the authentication components set, we can now create a SecretProviderClass which includes details about the Azure Key Vault, the secrets to pull out from the vault, and identity used to access the vault.

    # Get the tenant id for the key vault
    TENANT_ID=$(az keyvault show \
    --name $AKV_NAME \
    --resource-group $RG_NAME \
    --query properties.tenantId -o tsv)

    # Create the secret provider for azure key vault
    kubectl apply -f - <<EOF
    apiVersion: secrets-store.csi.x-k8s.io/v1
    kind: SecretProviderClass
    metadata:
    name: eshop-azure-keyvault
    spec:
    provider: azure
    parameters:
    usePodIdentity: "false"
    useVMManagedIdentity: "false"
    clientID: "${MANAGED_IDENTITY_CLIENT_ID}"
    keyvaultName: "${AKV_NAME}"
    cloudName: ""
    objects: |
    array:
    - |
    objectName: mssql-password
    objectType: secret
    objectVersion: ""
    - |
    objectName: mssql-connection-catalog
    objectType: secret
    objectVersion: ""
    - |
    objectName: mssql-connection-identity
    objectType: secret
    objectVersion: ""
    tenantId: "${TENANT_ID}"
    secretObjects:
    - secretName: eshop-secrets
    type: Opaque
    data:
    - objectName: mssql-password
    key: mssql-password
    - objectName: mssql-connection-catalog
    key: mssql-connection-catalog
    - objectName: mssql-connection-identity
    key: mssql-connection-identity
    EOF

    Finally, lets grant the Azure Managed Identity permissions to retrieve secrets from the Azure Key Vault.

    az keyvault set-policy \
    --name $AKV_NAME \
    --secret-permissions get \
    --spn $MANAGED_IDENTITY_CLIENT_ID

    Re-package deployments

    Update your database deployment to load environment variables from our ConfigMap, attach the PVC and SecretProviderClass as volumes, mount the volumes into the Pod, and use the ServiceAccount to retrieve secrets.

    Additionally, you may notice the database Pod is set to use fsGroup:10001 as part of the securityContext. This is required as the MSSQL container runs using a non-root account called mssql and this account has the proper permissions to read/write data at the /var/opt/mssql mount path.

    kubectl apply -f - <<EOF
    apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: db
    labels:
    app: db
    spec:
    replicas: 1
    selector:
    matchLabels:
    app: db
    template:
    metadata:
    labels:
    app: db
    spec:
    securityContext:
    fsGroup: 10001
    serviceAccountName: ${SERVICE_ACCOUNT_NAME}
    containers:
    - name: db
    image: mcr.microsoft.com/mssql/server:2019-latest
    ports:
    - containerPort: 1433
    envFrom:
    - configMapRef:
    name: mssql-settings
    env:
    - name: MSSQL_SA_PASSWORD
    valueFrom:
    secretKeyRef:
    name: eshop-secrets
    key: mssql-password
    resources: {}
    volumeMounts:
    - name: mssqldb
    mountPath: /var/opt/mssql
    - name: eshop-secrets
    mountPath: "/mnt/secrets-store"
    readOnly: true
    volumes:
    - name: mssqldb
    persistentVolumeClaim:
    claimName: mssql-data
    - name: eshop-secrets
    csi:
    driver: secrets-store.csi.k8s.io
    readOnly: true
    volumeAttributes:
    secretProviderClass: eshop-azure-keyvault
    EOF

    We'll update the API and Web deployments in a similar way.

    # Set the image tag
    IMAGE_TAG=<YOUR_IMAGE_TAG>

    # API deployment
    kubectl apply -f - <<EOF
    apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: api
    labels:
    app: api
    spec:
    replicas: 1
    selector:
    matchLabels:
    app: api
    template:
    metadata:
    labels:
    app: api
    spec:
    serviceAccount: ${SERVICE_ACCOUNT_NAME}
    containers:
    - name: api
    image: ${ACR_NAME}.azurecr.io/api:${IMAGE_TAG}
    ports:
    - containerPort: 80
    envFrom:
    - configMapRef:
    name: aspnet-settings
    env:
    - name: ConnectionStrings__CatalogConnection
    valueFrom:
    secretKeyRef:
    name: eshop-secrets
    key: mssql-connection-catalog
    - name: ConnectionStrings__IdentityConnection
    valueFrom:
    secretKeyRef:
    name: eshop-secrets
    key: mssql-connection-identity
    resources: {}
    volumeMounts:
    - name: aspnet
    mountPath: ~/.aspnet/https:/root/.aspnet/https:ro
    - name: eshop-secrets
    mountPath: "/mnt/secrets-store"
    readOnly: true
    volumes:
    - name: aspnet
    persistentVolumeClaim:
    claimName: aspnet-data
    - name: eshop-secrets
    csi:
    driver: secrets-store.csi.k8s.io
    readOnly: true
    volumeAttributes:
    secretProviderClass: eshop-azure-keyvault
    EOF

    ## Web deployment
    kubectl apply -f - <<EOF
    apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: web
    labels:
    app: web
    spec:
    replicas: 1
    selector:
    matchLabels:
    app: web
    template:
    metadata:
    labels:
    app: web
    spec:
    serviceAccount: ${SERVICE_ACCOUNT_NAME}
    containers:
    - name: web
    image: ${ACR_NAME}.azurecr.io/web:${IMAGE_TAG}
    ports:
    - containerPort: 80
    envFrom:
    - configMapRef:
    name: aspnet-settings
    env:
    - name: ConnectionStrings__CatalogConnection
    valueFrom:
    secretKeyRef:
    name: eshop-secrets
    key: mssql-connection-catalog
    - name: ConnectionStrings__IdentityConnection
    valueFrom:
    secretKeyRef:
    name: eshop-secrets
    key: mssql-connection-identity
    resources: {}
    volumeMounts:
    - name: aspnet
    mountPath: ~/.aspnet/https:/root/.aspnet/https:ro
    - name: eshop-secrets
    mountPath: "/mnt/secrets-store"
    readOnly: true
    volumes:
    - name: aspnet
    persistentVolumeClaim:
    claimName: aspnet-data
    - name: eshop-secrets
    csi:
    driver: secrets-store.csi.k8s.io
    readOnly: true
    volumeAttributes:
    secretProviderClass: eshop-azure-keyvault
    EOF

    If all went well with your deployment updates, you should be able to browse to your website and buy some merchandise again 🥳

    echo "http://$(kubectl get service web -o jsonpath='{.status.loadBalancer.ingress[0].ip}')"

    Conclusion

    Although there is no visible changes on with our website, we've made a ton of changes on the Kubernetes backend to make this application much more secure and resilient.

    We used a combination of Kubernetes resources and AKS-specific features to achieve our goal of securing our secrets and ensuring data is not lost on container crashes and restarts.

    To learn more about the components we leveraged here today, checkout the resources and additional tutorials listed below.

    You can also find manifests with all the changes made in today's post in the Azure-Samples/eShopOnAKS repository.

    See you in the next post!

    Resources

    Take the Cloud Skills Challenge!

    Enroll in the Cloud Skills Challenge!

    Don't miss out on this opportunity to level up your skills and stay ahead of the curve in the world of cloud native.

    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/containers/index.html b/cnny-2023/tags/containers/index.html index 18ca338abe..b87f4d0ab7 100644 --- a/cnny-2023/tags/containers/index.html +++ b/cnny-2023/tags/containers/index.html @@ -14,14 +14,14 @@ - - + +

    3 posts tagged with "containers"

    View All Tags

    · 4 min read
    Steven Murawski
    Paul Yu
    Josh Duffney

    Welcome to Day 2 of Week 1 of #CloudNativeNewYear!

    Today, we'll focus on building an understanding of containers.

    What We'll Cover

    • Introduction
    • How do Containers Work?
    • Why are Containers Becoming so Popular?
    • Conclusion
    • Resources
    • Learning Path

    REGISTER & LEARN: KUBERNETES 101

    Interested in a dive into Kubernetes and a chance to talk to experts?

    🎙: Join us Jan 26 @1pm PST by registering here

    Here's what you will learn:

    • Key concepts and core principles of Kubernetes.
    • How to deploy, scale and manage containerized workloads.
    • Live Demo of the concepts explained
    • How to get started with Azure Kubernetes Service for free.

    Start your free Azure Kubernetes Trial Today!!: aka.ms/TryAKS

    Introduction

    In the beginning, we deployed our applications onto physical servers. We only had a certain number of those servers, so often they hosted multiple applications. This led to some problems when those applications shared dependencies. Upgrading one application could break another application on the same server.

    Enter virtualization. Virtualization allowed us to run our applications in an isolated operating system instance. This removed much of the risk of updating shared dependencies. However, it increased our overhead since we had to run a full operating system for each application environment.

    To address the challenges created by virtualization, containerization was created to improve isolation without duplicating kernel level resources. Containers provide efficient and consistent deployment and runtime experiences for our applications and have become very popular as a way of packaging and distributing applications.

    How do Containers Work?

    Containers build on two capabilities in the Linux operating system, namespaces and cgroups. These constructs allow the operating system to provide isolation to a process or group of processes, keeping their access to filesystem resources separate and providing controls on resource utilization. This, combined with tooling to help package, deploy, and run container images has led to their popularity in today’s operating environment. This provides us our isolation without the overhead of additional operating system resources.

    When a container host is deployed on an operating system, it works at scheduling the access to the OS (operating systems) components. This is done by providing a logical isolated group that can contain processes for a given application, called a namespace. The container host then manages /schedules access from the namespace to the host OS. The container host then uses cgroups to allocate compute resources. Together, the container host with the help of cgroups and namespaces can schedule multiple applications to access host OS resources.

    Overall, this gives the illusion of virtualizing the host OS, where each application gets its own OS. In actuality, all the applications are running on the same operating system and sharing the same kernel as the container host.

    Containers are popular in the software development industry because they provide several benefits over traditional virtualization methods. Some of these benefits include:

    • Portability: Containers make it easy to move an application from one environment to another without having to worry about compatibility issues or missing dependencies.
    • Isolation: Containers provide a level of isolation between the application and the host system, which means that the application running in the container cannot access the host system's resources.
    • Scalability: Containers make it easy to scale an application up or down as needed, which is useful for applications that experience a lot of traffic or need to handle a lot of data.
    • Resource Efficiency: Containers are more resource-efficient than traditional virtualization methods because they don't require a full operating system to be running on each virtual machine.
    • Cost-Effective: Containers are more cost-effective than traditional virtualization methods because they don't require expensive hardware or licensing fees.

    Conclusion

    Containers are a powerful technology that allows developers to package and deploy applications in a portable and isolated environment. This technology is becoming increasingly popular in the world of software development and is being used by many companies and organizations to improve their application deployment and management processes. With the benefits of portability, isolation, scalability, resource efficiency, and cost-effectiveness, containers are definitely worth considering for your next application development project.

    Containerizing applications is a key step in modernizing them, and there are many other patterns that can be adopted to achieve cloud-native architectures, including using serverless platforms, Kubernetes, and implementing DevOps practices.

    Resources

    Learning Path

    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/containers/page/2/index.html b/cnny-2023/tags/containers/page/2/index.html index 3427f1bf2c..d391d28f2d 100644 --- a/cnny-2023/tags/containers/page/2/index.html +++ b/cnny-2023/tags/containers/page/2/index.html @@ -14,14 +14,14 @@ - - + +

    3 posts tagged with "containers"

    View All Tags

    · 7 min read
    Vinicius Apolinario

    Welcome to Day 3 of Week 4 of #CloudNativeNewYear!

    The theme for this week is going further with Cloud Native. Yesterday we talked about using Draft to accelerate your Kubernetes adoption. Today we'll explore the topic of Windows containers.

    What We'll Cover

    • Introduction
    • Windows containers overview
    • Windows base container images
    • Isolation
    • Exercise: Try this yourself!
    • Resources: For self-study!

    Introduction

    Windows containers were launched along with Windows Server 2016, and have evolved since. In its latest release, Windows Server 2022, Windows containers have reached a great level of maturity and allow for customers to run production grade workloads.

    While suitable for new developments, Windows containers also provide developers and operations with a different approach than Linux containers. It allows for existing Windows applications to be containerized with little or no code changes. It also allows for professionals that are more comfortable with the Windows platform and OS, to leverage their skill set, while taking advantage of the containers platform.

    Windows container overview

    In essence, Windows containers are very similar to Linux. Since Windows containers use the same foundation of Docker containers, you can expect that the same architecture applies - with the specific notes of the Windows OS. For example, when running a Windows container via Docker, you use the same commands, such as docker run. To pull a container image, you can use docker pull, just like on Linux. However, to run a Windows container, you also need a Windows container host. This requirement is there because, as you might remember, a container shares the OS kernel with its container host.

    On Kubernetes, Windows containers are supported since Windows Server 2019. Just like with Docker, you can manage Windows containers like any other resource on the Kubernetes ecosystem. A Windows node can be part of a Kubernetes cluster, allowing you to run Windows container based applications on services like Azure Kubernetes Service. To deploy an Windows application to a Windows pod in Kubernetes, you can author a YAML specification much like you would for Linux. The main difference is that you would point to an image that runs on Windows, and you need to specify a node selection tag to indicate said pod needs to run on a Windows node.

    Windows base container images

    On Windows containers, you will always use a base container image provided by Microsoft. This base container image contains the OS binaries for the container to run. This image can be as large as 3GB+, or small as ~300MB. The difference in the size is a consequence of the APIs and components available in each Windows container base container image. There are primarily, three images: Nano Server, Server Core, and Server.

    Nano Server is the smallest image, ranging around 300MB. It's a base container image for new developments and cloud-native scenarios. Applications need to target Nano Server as the Windows OS, so not all frameworks will work. For example, .Net works on Nano Server, but .Net Framework doesn't. Other third-party frameworks also work on Nano Server, such as Apache, NodeJS, Phyton, Tomcat, Java runtime, JBoss, Redis, among others.

    Server Core is a much larger base container image, ranging around 1.25GB. It's larger size is compensated by it's application compatibility. Simply put, any application that meets the requirements to be run on a Windows container, can be containerized with this image.

    The Server image builds on the Server Core one. It ranges around 3.1GB and has even greater application compatibility than the Server Core image. In addition to the traditional Windows APIs and components, this image allows for scenarios such as Machine Learning via DirectX with GPU access.

    The best image for your scenario is dependent on the requirements your application has on the Windows OS inside a container. However, there are some scenarios that are not supported at all on Windows containers - such as GUI or RDP dependent applications, some Windows Server infrastructure roles, such as Active Directory, among others.

    Isolation

    When running containers, the kernel of the container host is shared with the containers running on it. While extremely convenient, this poses a potential risk for multi-tenant scenarios. If one container is compromised and has access to the host, it could potentially compromise other containers in the same system.

    For enterprise customers running on-premises (or even in the cloud), this can be mitigated by using a VM as a container host and considering the VM itself a security boundary. However, if multiple workloads from different tenants need to share the same host, Windows containers offer another option: Hyper-V isolation. While the name Hyper-V is associated with VMs, its virtualization capabilities extend to other services, including containers. Hyper-V isolated containers run on a purpose built, extremely small, highly performant VM. However, you manage a container running with Hyper-V isolation the same way you do with a process isolated one. In fact, the only notable difference is that you need to append the --isolation=hyperv tag to the docker run command.

    Exercise

    Here are a few examples of how to use Windows containers:

    Run Windows containers via Docker on your machine

    To pull a Windows base container image:

    docker pull mcr.microsoft.com/windows/servercore:ltsc2022

    To run a basic IIS container:

    #This command will pull and start a IIS container. You can access it from http://<your local IP>:8080
    docker run -d -p 8080:80 mcr.microsoft.com/windows/servercore/iis:windowsservercore-ltsc2022

    Run the same IIS container with Hyper-V isolation

    #This command will pull and start a IIS container. You can access it from http://<your local IP>:8080
    docker run -d -p 8080:80 --isolation=hyperv mcr.microsoft.com/windows/servercore/iis:windowsservercore-ltsc2022

    To run a Windows container interactively:

    docker run -it mcr.microsoft.com/windows/servercore:ltsc2022 powershell

    Run Windows containers on Kubernetes

    To prepare an AKS cluster for Windows containers: Note: Replace the values on the example below with the ones from your environment.

    echo "Please enter the username to use as administrator credentials for Windows Server nodes on your cluster: " && read WINDOWS_USERNAME
    az aks create \
    --resource-group myResourceGroup \
    --name myAKSCluster \
    --node-count 2 \
    --generate-ssh-keys \
    --windows-admin-username $WINDOWS_USERNAME \
    --vm-set-type VirtualMachineScaleSets \
    --network-plugin azure

    To add a Windows node pool for Windows containers:

    az aks nodepool add \
    --resource-group myResourceGroup \
    --cluster-name myAKSCluster \
    --os-type Windows \
    --name npwin \
    --node-count 1

    Deploy a sample ASP.Net application to the AKS cluster above using the YAML file below:

    apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: sample
    labels:
    app: sample
    spec:
    replicas: 1
    template:
    metadata:
    name: sample
    labels:
    app: sample
    spec:
    nodeSelector:
    "kubernetes.io/os": windows
    containers:
    - name: sample
    image: mcr.microsoft.com/dotnet/framework/samples:aspnetapp
    resources:
    limits:
    cpu: 1
    memory: 800M
    ports:
    - containerPort: 80
    selector:
    matchLabels:
    app: sample
    ---
    apiVersion: v1
    kind: Service
    metadata:
    name: sample
    spec:
    type: LoadBalancer
    ports:
    - protocol: TCP
    port: 80
    selector:
    app: sample

    Save the file above and run the command below on your Kubernetes cluster:

    kubectl apply -f <filename> .

    Once deployed, you can access the application by getting the IP address of your service:

    kubectl get service

    Resources

    It's not too late to sign up for and complete the Cloud Skills Challenge!
    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/containers/page/3/index.html b/cnny-2023/tags/containers/page/3/index.html index f5bc2c31de..6c90610206 100644 --- a/cnny-2023/tags/containers/page/3/index.html +++ b/cnny-2023/tags/containers/page/3/index.html @@ -14,13 +14,13 @@ - - + +

    3 posts tagged with "containers"

    View All Tags

    · 4 min read
    Jorge Arteiro

    Welcome to Day 4 of Week 4 of #CloudNativeNewYear!

    The theme for this week is going further with Cloud Native. Yesterday we talked about Windows Containers. Today we'll explore addons and extensions available to Azure Kubernetes Services (AKS).

    What We'll Cover

    • Introduction
    • Add-ons
    • Extensions
    • Add-ons vs Extensions
    • Resources

    Introduction

    Azure Kubernetes Service (AKS) is a fully managed container orchestration service that makes it easy to deploy and manage containerized applications on Azure. AKS offers a number of features and capabilities, including the ability to extend its supported functionality through the use of add-ons and extensions.

    There are also integrations available from open-source projects and third parties, but they are not covered by the AKS support policy.

    Add-ons

    Add-ons provide a supported way to extend AKS. Installation, configuration and lifecycle are managed by AKS following pre-determine updates rules.

    As an example, let's enable Container Insights with the monitoring addon. on an existing AKS cluster using az aks enable-addons --addons CLI command

    az aks enable-addons \
    --name MyManagedCluster \
    --resource-group MyResourceGroup \
    --addons monitoring

    or you can use az aks create --enable-addons when creating new clusters

    az aks create \
    --name MyManagedCluster \
    --resource-group MyResourceGroup \
    --enable-addons monitoring

    The current available add-ons are:

    1. http_application_routing - Configure ingress with automatic public DNS name creation. Only recommended for development.
    2. monitoring - Container Insights monitoring.
    3. virtual-node - CNCF virtual nodes open source project.
    4. azure-policy - Azure Policy for AKS.
    5. ingress-appgw - Application Gateway Ingress Controller (AGIC).
    6. open-service-mesh - CNCF Open Service Mesh project.
    7. azure-keyvault-secrets-provider - Azure Key Vault Secrets Provider for Secret Store CSI Driver.
    8. web_application_routing - Managed NGINX ingress Controller.
    9. keda - CNCF Event-driven autoscaling project.

    For more details, get the updated list of AKS Add-ons here

    Extensions

    Cluster Extensions uses Helm charts and integrates with Azure Resource Manager (ARM) to provide installation and lifecycle management of capabilities on top of AKS.

    Extensions can be auto upgraded using minor versions, but it requires extra management and configuration. Using Scope parameter, it can be installed on the whole cluster or per namespace.

    AKS Extensions requires an Azure CLI extension to be installed. To add or update this CLI extension use the following commands:

    az extension add --name k8s-extension

    and to update an existing extension

    az extension update --name k8s-extension

    There are only 3 available extensions:

    1. Dapr - CNCF Dapr project.
    2. Azure ML - Integrate Azure Machine Learning with AKS to train, inference and manage ML models.
    3. Flux (GitOps) - CNCF Flux project integrated with AKS to enable cluster configuration and application deployment using GitOps.

    As an example, you can install Azure ML using the following command:

    az k8s-extension create \
    --name aml-compute --extension-type Microsoft.AzureML.Kubernetes \
    --scope cluster --cluster-name <clusterName> \
    --resource-group <resourceGroupName> \
    --cluster-type managedClusters \
    --configuration-settings enableInference=True allowInsecureConnections=True

    For more details, get the updated list of AKS Extensions here

    Add-ons vs Extensions

    AKS Add-ons brings an advantage of been fully managed by AKS itself, and AKS Extensions are more flexible and configurable but requires extra level of management.

    Add-ons are part of the AKS resource provider in the Azure API, and AKS Extensions are a separate resource provider on the Azure API.

    Resources

    It's not too late to sign up for and complete the Cloud Skills Challenge!
    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/extensions/index.html b/cnny-2023/tags/extensions/index.html index 13d9bb690c..279a1379fb 100644 --- a/cnny-2023/tags/extensions/index.html +++ b/cnny-2023/tags/extensions/index.html @@ -14,13 +14,13 @@ - - + +

    One post tagged with "extensions"

    View All Tags

    · 4 min read
    Jorge Arteiro

    Welcome to Day 4 of Week 4 of #CloudNativeNewYear!

    The theme for this week is going further with Cloud Native. Yesterday we talked about Windows Containers. Today we'll explore addons and extensions available to Azure Kubernetes Services (AKS).

    What We'll Cover

    • Introduction
    • Add-ons
    • Extensions
    • Add-ons vs Extensions
    • Resources

    Introduction

    Azure Kubernetes Service (AKS) is a fully managed container orchestration service that makes it easy to deploy and manage containerized applications on Azure. AKS offers a number of features and capabilities, including the ability to extend its supported functionality through the use of add-ons and extensions.

    There are also integrations available from open-source projects and third parties, but they are not covered by the AKS support policy.

    Add-ons

    Add-ons provide a supported way to extend AKS. Installation, configuration and lifecycle are managed by AKS following pre-determine updates rules.

    As an example, let's enable Container Insights with the monitoring addon. on an existing AKS cluster using az aks enable-addons --addons CLI command

    az aks enable-addons \
    --name MyManagedCluster \
    --resource-group MyResourceGroup \
    --addons monitoring

    or you can use az aks create --enable-addons when creating new clusters

    az aks create \
    --name MyManagedCluster \
    --resource-group MyResourceGroup \
    --enable-addons monitoring

    The current available add-ons are:

    1. http_application_routing - Configure ingress with automatic public DNS name creation. Only recommended for development.
    2. monitoring - Container Insights monitoring.
    3. virtual-node - CNCF virtual nodes open source project.
    4. azure-policy - Azure Policy for AKS.
    5. ingress-appgw - Application Gateway Ingress Controller (AGIC).
    6. open-service-mesh - CNCF Open Service Mesh project.
    7. azure-keyvault-secrets-provider - Azure Key Vault Secrets Provider for Secret Store CSI Driver.
    8. web_application_routing - Managed NGINX ingress Controller.
    9. keda - CNCF Event-driven autoscaling project.

    For more details, get the updated list of AKS Add-ons here

    Extensions

    Cluster Extensions uses Helm charts and integrates with Azure Resource Manager (ARM) to provide installation and lifecycle management of capabilities on top of AKS.

    Extensions can be auto upgraded using minor versions, but it requires extra management and configuration. Using Scope parameter, it can be installed on the whole cluster or per namespace.

    AKS Extensions requires an Azure CLI extension to be installed. To add or update this CLI extension use the following commands:

    az extension add --name k8s-extension

    and to update an existing extension

    az extension update --name k8s-extension

    There are only 3 available extensions:

    1. Dapr - CNCF Dapr project.
    2. Azure ML - Integrate Azure Machine Learning with AKS to train, inference and manage ML models.
    3. Flux (GitOps) - CNCF Flux project integrated with AKS to enable cluster configuration and application deployment using GitOps.

    As an example, you can install Azure ML using the following command:

    az k8s-extension create \
    --name aml-compute --extension-type Microsoft.AzureML.Kubernetes \
    --scope cluster --cluster-name <clusterName> \
    --resource-group <resourceGroupName> \
    --cluster-type managedClusters \
    --configuration-settings enableInference=True allowInsecureConnections=True

    For more details, get the updated list of AKS Extensions here

    Add-ons vs Extensions

    AKS Add-ons brings an advantage of been fully managed by AKS itself, and AKS Extensions are more flexible and configurable but requires extra level of management.

    Add-ons are part of the AKS resource provider in the Azure API, and AKS Extensions are a separate resource provider on the Azure API.

    Resources

    It's not too late to sign up for and complete the Cloud Skills Challenge!
    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/index.html b/cnny-2023/tags/index.html index e35f6f8520..99880a8bcd 100644 --- a/cnny-2023/tags/index.html +++ b/cnny-2023/tags/index.html @@ -14,13 +14,13 @@ - - + +
    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/ingress/index.html b/cnny-2023/tags/ingress/index.html index 4c37e9c863..deb0a7c7a3 100644 --- a/cnny-2023/tags/ingress/index.html +++ b/cnny-2023/tags/ingress/index.html @@ -14,13 +14,13 @@ - - + +

    2 posts tagged with "ingress"

    View All Tags

    · 11 min read
    Paul Yu

    Welcome to Day 2 of Week 2 of #CloudNativeNewYear!

    The theme for this week is #Kubernetes fundamentals. Yesterday we talked about how to deploy a containerized web app workload to Azure Kubernetes Service (AKS). Today we'll explore the topic of services and ingress and walk through the steps of making our containers accessible both internally as well as over the internet so that you can share it with the world 😊

    Ask the Experts Thursday, February 9th at 9 AM PST
    Catch the Replay of the Live Demo

    Watch the recorded demo and conversation about this week's topics.

    We were live on YouTube walking through today's (and the rest of this week's) demos.

    What We'll Cover

    • Exposing Pods via Service
    • Exposing Services via Ingress
    • Takeaways
    • Resources

    Exposing Pods via Service

    There are a few ways to expose your pod in Kubernetes. One way is to take an imperative approach and use the kubectl expose command. This is probably the quickest way to achieve your goal but it isn't the best way. A better way to expose your pod by taking a declarative approach by creating a services manifest file and deploying it using the kubectl apply command.

    Don't worry if you are unsure of how to make this manifest, we'll use kubectl to help generate it.

    First, let's ensure we have the database deployed on our AKS cluster.

    📝 NOTE: If you don't have an AKS cluster deployed, please head over to Azure-Samples/azure-voting-app-rust, clone the repo, and follow the instructions in the README.md to execute the Azure deployment and setup your kubectl context. Check out the first post this week for more on the environment setup.

    kubectl apply -f ./manifests/deployment-db.yaml

    Next, let's deploy the application. If you are following along from yesterday's content, there isn't anything you need to change; however, if you are deploy the app from scratch, you'll need to modify the deployment-app.yaml manifest and update it with your image tag and database pod's IP address.

    kubectl apply -f ./manifests/deployment-app.yaml

    Now, let's expose the database using a service so that we can leverage Kubernetes' built-in service discovery to be able to reference it by name; not pod IP. Run the following command.

    kubectl expose deployment azure-voting-db \
    --port=5432 \
    --target-port=5432

    With the database exposed using service, we can update the app deployment manifest to use the service name instead of pod IP. This way, if the pod ever gets assigned a new IP, we don't have to worry about updating the IP each time and redeploying our web application. Kubernetes has internal service discovery mechanism in place that allows us to reference a service by its name.

    Let's make an update to the manifest. Replace the environment variable for DATABASE_SERVER with the following:

    - name: DATABASE_SERVER
    value: azure-voting-db

    Re-deploy the app with the updated configuration.

    kubectl apply -f ./manifests/deployment-app.yaml

    One service down, one to go. Run the following command to expose the web application.

    kubectl expose deployment azure-voting-app \
    --type=LoadBalancer \
    --port=80 \
    --target-port=8080

    Notice the --type argument has a value of LoadBalancer. This service type is implemented by the cloud-controller-manager which is part of the Kubernetes control plane. When using a managed Kubernetes cluster such as Azure Kubernetes Service, a public standard load balancer will be able to provisioned when the service type is set to LoadBalancer. The load balancer will also have a public IP assigned which will make your deployment publicly available.

    Kubernetes supports four service types:

    • ClusterIP: this is the default and limits service access to internal traffic within the cluster
    • NodePort: this assigns a port mapping on the node's IP address and allows traffic from the virtual network (outside the cluster)
    • LoadBalancer: as mentioned above, this creates a cloud-based load balancer
    • ExternalName: this is used in special case scenarios where you want to map a service to an external DNS name

    📝 NOTE: When exposing a web application to the internet, allowing external users to connect to your Service directly is not the best approach. Instead, you should use an Ingress, which we'll cover in the next section.

    Now, let's confirm you can reach the web app from the internet. You can use the following command to print the URL to your terminal.

    echo "http://$(kubectl get service azure-voting-app -o jsonpath='{.status.loadBalancer.ingress[0].ip}')"

    Great! The kubectl expose command gets the job done, but as mentioned above, it is not the best method of exposing deployments. It is better to expose deployments declaratively using a service manifest, so let's delete the services and redeploy using manifests.

    kubectl delete service azure-voting-db azure-voting-app

    To use kubectl to generate our manifest file, we can use the same kubectl expose command that we ran earlier but this time, we'll include --output=yaml and --dry-run=client. This will instruct the command to output the manifest that would be sent to the kube-api server in YAML format to the terminal.

    Generate the manifest for the database service.

    kubectl expose deployment azure-voting-db \
    --type=ClusterIP \
    --port=5432 \
    --target-port=5432 \
    --output=yaml \
    --dry-run=client > ./manifests/service-db.yaml

    Generate the manifest for the application service.

    kubectl expose deployment azure-voting-app \
    --type=LoadBalancer \
    --port=80 \
    --target-port=8080 \
    --output=yaml \
    --dry-run=client > ./manifests/service-app.yaml

    The command above redirected the YAML output to your manifests directory. Here is what the web application service looks like.

    apiVersion: v1
    kind: Service
    metadata:
    creationTimestamp: null
    labels:
    app: azure-voting-app
    name: azure-voting-app
    spec:
    ports:
    - port: 80
    protocol: TCP
    targetPort: 8080
    selector:
    app: azure-voting-app
    type: LoadBalancer
    status:
    loadBalancer: {}

    💡 TIP: To view the schema of any api-resource in Kubernetes, you can use the kubectl explain command. In this case the kubectl explain service command will tell us exactly what each of these fields do.

    Re-deploy the services using the new service manifests.

    kubectl apply -f ./manifests/service-db.yaml -f ./manifests/service-app.yaml

    # You should see TYPE is set to LoadBalancer and the EXTERNAL-IP is set
    kubectl get service azure-voting-db azure-voting-app

    Confirm again that our application is accessible again. Run the following command to print the URL to the terminal.

    echo "http://$(kubectl get service azure-voting-app -o jsonpath='{.status.loadBalancer.ingress[0].ip}')"

    That was easy, right? We just exposed both of our pods using Kubernetes services. The database only needs to be accessible from within the cluster so ClusterIP is perfect for that. For the web application, we specified the type to be LoadBalancer so that we can access the application over the public internet.

    But wait... remember that if you want to expose web applications over the public internet, a Service with a public IP is not the best way; the better approach is to use an Ingress resource.

    Exposing Services via Ingress

    If you read through the Kubernetes documentation on Ingress you will see a diagram that depicts the Ingress sitting in front of the Service resource with a routing rule between it. In order to use Ingress, you need to deploy an Ingress Controller and it can be configured with many routing rules to forward traffic to one or many backend services. So effectively, an Ingress is a load balancer for your Services.

    With that said, we no longer need a service type of LoadBalancer since the service does not need to be accessible from the internet. It only needs to be accessible from the Ingress Controller (internal to the cluster) so we can change the service type to ClusterIP.

    Update your service.yaml file to look like this:

    apiVersion: v1
    kind: Service
    metadata:
    creationTimestamp: null
    labels:
    app: azure-voting-app
    name: azure-voting-app
    spec:
    ports:
    - port: 80
    protocol: TCP
    targetPort: 8080
    selector:
    app: azure-voting-app

    📝 NOTE: The default service type is ClusterIP so we can omit the type altogether.

    Re-apply the app service manifest.

    kubectl apply -f ./manifests/service-app.yaml

    # You should see TYPE set to ClusterIP and EXTERNAL-IP set to <none>
    kubectl get service azure-voting-app

    Next, we need to install an Ingress Controller. There are quite a few options, and the Kubernetes-maintained NGINX Ingress Controller is commonly deployed.

    You could install this manually by following these instructions, but if you do that you'll be responsible for maintaining and supporting the resource.

    I like to take advantage of free maintenance and support when I can get it, so I'll opt to use the Web Application Routing add-on for AKS.

    💡 TIP: Whenever you install an AKS add-on, it will be maintained and fully supported by Azure Support.

    Enable the web application routing add-on in our AKS cluster with the following command.

    az aks addon enable \
    --name <YOUR_AKS_NAME> \
    --resource-group <YOUR_AKS_RESOURCE_GROUP>
    --addon web_application_routing

    ⚠️ WARNING: This command can take a few minutes to complete

    Now, let's use the same approach we took in creating our service to create our Ingress resource. Run the following command to generate the Ingress manifest.

    kubectl create ingress azure-voting-app \
    --class=webapprouting.kubernetes.azure.com \
    --rule="/*=azure-voting-app:80" \
    --output yaml \
    --dry-run=client > ./manifests/ingress.yaml

    The --class=webapprouting.kubernetes.azure.com option activates the AKS web application routing add-on. This AKS add-on can also integrate with other Azure services such as Azure DNS and Azure Key Vault for TLS certificate management and this special class makes it all work.

    The --rule="/*=azure-voting-app:80" option looks confusing but we can use kubectl again to help us understand how to format the value for the option.

    kubectl create ingress --help

    In the output you will see the following:

    --rule=[]:
    Rule in format host/path=service:port[,tls=secretname]. Paths containing the leading character '*' are
    considered pathType=Prefix. tls argument is optional.

    It expects a host and path separated by a forward-slash, then expects the backend service name and port separated by a colon. We're not using a hostname for this demo so we can omit it. For the path, an asterisk is used to specify a wildcard path prefix.

    So, the value of /*=azure-voting-app:80 creates a routing rule for all paths following the domain (or in our case since we don't have a hostname specified, the IP) to route traffic to our azure-voting-app backend service on port 80.

    📝 NOTE: Configuring the hostname and TLS is outside the scope of this demo but please visit this URL https://bit.ly/aks-webapp-routing for an in-depth hands-on lab centered around Web Application Routing on AKS.

    Your ingress.yaml file should look like this:

    apiVersion: networking.k8s.io/v1
    kind: Ingress
    metadata:
    creationTimestamp: null
    name: azure-voting-app
    spec:
    ingressClassName: webapprouting.kubernetes.azure.com
    rules:
    - http:
    paths:
    - backend:
    service:
    name: azure-voting-app
    port:
    number: 80
    path: /
    pathType: Prefix
    status:
    loadBalancer: {}

    Apply the app ingress manifest.

    kubectl apply -f ./manifests/ingress.yaml

    Validate the web application is available from the internet again. You can run the following command to print the URL to the terminal.

    echo "http://$(kubectl get ingress azure-voting-app -o jsonpath='{.status.loadBalancer.ingress[0].ip}')"

    Takeaways

    Exposing your applications both internally and externally can be easily achieved using Service and Ingress resources respectively. If your service is HTTP or HTTPS based and needs to be accessible from outsie the cluster, use Ingress with an internal Service (i.e., ClusterIP or NodePort); otherwise, use the Service resource. If your TCP-based Service needs to be publicly accessible, you set the type to LoadBalancer to expose a public IP for it. To learn more about these resources, please visit the links listed below.

    Lastly, if you are unsure how to begin writing your service manifest, you can use kubectl and have it do most of the work for you 🥳

    Resources

    Take the Cloud Skills Challenge!

    Enroll in the Cloud Skills Challenge!

    Don't miss out on this opportunity to level up your skills and stay ahead of the curve in the world of cloud native.

    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/ingress/page/2/index.html b/cnny-2023/tags/ingress/page/2/index.html index 131cdbf799..b7bf485cbb 100644 --- a/cnny-2023/tags/ingress/page/2/index.html +++ b/cnny-2023/tags/ingress/page/2/index.html @@ -14,13 +14,13 @@ - - + +

    2 posts tagged with "ingress"

    View All Tags

    · 10 min read
    Paul Yu

    Welcome to Day 3 of Week 3 of #CloudNativeNewYear!

    The theme for this week is Bringing Your Application to Kubernetes. Yesterday we added configuration, secrets, and storage to our app. Today we'll explore how to expose the eShopOnWeb app so that customers can reach it over the internet using a custom domain name and TLS.

    Ask the Experts Thursday, February 9th at 9 AM PST
    Friday, February 10th at 11 AM PST

    Watch the recorded demo and conversation about this week's topics.

    We were live on YouTube walking through today's (and the rest of this week's) demos. Join us Friday, February 10th and bring your questions!

    What We'll Cover

    • Gather requirements
    • Generate TLS certificate and store in Azure Key Vault
    • Implement custom DNS using Azure DNS
    • Enable Web Application Routing add-on for AKS
    • Implement Ingress for the web application
    • Conclusion
    • Resources

    Gather requirements

    Currently, our eShopOnWeb app has three Kubernetes services deployed:

    1. db exposed internally via ClusterIP
    2. api exposed externally via LoadBalancer
    3. web exposed externally via LoadBalancer

    As mentioned in my post last week, Services allow applications to communicate with each other using DNS names. Kubernetes has service discovery capabilities built-in that allows Pods to resolve Services simply by using their names.

    In the case of our api and web deployments, they can simply reach the database by calling its name. The service type of ClusterIP for the db can remain as-is since it only needs to be accessed by the api and web apps.

    On the other hand, api and web both need to be accessed over the public internet. Currently, these services are using service type LoadBalancer which tells AKS to provision an Azure Load Balancer with a public IP address. No one is going to remember the IP addresses, so we need to make the app more accessible by adding a custom domain name and securing it with a TLS certificate.

    Here's what we're going to need:

    • Custom domain name for our app
    • TLS certificate for the custom domain name
    • Routing rule to ensure requests with /api/ in the URL is routed to the backend REST API
    • Routing rule to ensure requests without /api/ in the URL is routing to the web UI

    Just like last week, we will use the Web Application Routing add-on for AKS. But this time, we'll integrate it with Azure DNS and Azure Key Vault to satisfy all of our requirements above.

    info

    At the time of this writing the add-on is still in Public Preview

    Generate TLS certificate and store in Azure Key Vault

    We deployed an Azure Key Vault yesterday to store secrets. We'll use it again to store a TLS certificate too.

    Let's create and export a self-signed certificate for the custom domain.

    DNS_NAME=eshoponweb$RANDOM.com
    openssl req -new -x509 -nodes -out web-tls.crt -keyout web-tls.key -subj "/CN=${DNS_NAME}" -addext "subjectAltName=DNS:${DNS_NAME}"
    openssl pkcs12 -export -in web-tls.crt -inkey web-tls.key -out web-tls.pfx -password pass:
    info

    For learning purposes we'll use a self-signed certificate and a fake custom domain name.

    To browse to the site using the fake domain, we'll mimic a DNS lookup by adding an entry to your host file which maps the public IP address assigned to the ingress controller to the custom domain.

    In a production scenario, you will need to have a real domain delegated to Azure DNS and a valid TLS certificate for the domain.

    Grab your Azure Key Vault name and set the value in a variable for later use.

    RESOURCE_GROUP=cnny-week3

    AKV_NAME=$(az resource list \
    --resource-group $RESOURCE_GROUP \
    --resource-type Microsoft.KeyVault/vaults \
    --query "[0].name" -o tsv)

    Grant yourself permissions to get, list, and import certificates.

    MY_USER_NAME=$(az account show --query user.name -o tsv)
    MY_USER_OBJECT_ID=$(az ad user show --id $MY_USER_NAME --query id -o tsv)

    az keyvault set-policy \
    --name $AKV_NAME \
    --object-id $MY_USER_OBJECT_ID \
    --certificate-permissions get list import

    Upload the TLS certificate to Azure Key Vault and grab its certificate URI.

    WEB_TLS_CERT_ID=$(az keyvault certificate import \
    --vault-name $AKV_NAME \
    --name web-tls \
    --file web-tls.pfx \
    --query id \
    --output tsv)

    Implement custom DNS with Azure DNS

    Create a custom domain for our application and grab its Azure resource id.

    DNS_ZONE_ID=$(az network dns zone create \
    --name $DNS_NAME \
    --resource-group $RESOURCE_GROUP \
    --query id \
    --output tsv)

    Enable Web Application Routing add-on for AKS

    As we enable the Web Application Routing add-on, we'll also pass in the Azure DNS Zone resource id which triggers the installation of the external-dns controller in your Kubernetes cluster. This controller will be able to write Azure DNS zone entries on your behalf as you deploy Ingress manifests.

    AKS_NAME=$(az resource list \
    --resource-group $RESOURCE_GROUP \
    --resource-type Microsoft.ContainerService/managedClusters \
    --query "[0].name" -o tsv)

    az aks enable-addons \
    --name $AKS_NAME \
    --resource-group $RESOURCE_GROUP \
    --addons web_application_routing \
    --dns-zone-resource-id=$DNS_ZONE_ID \
    --enable-secret-rotation

    The add-on will also deploy a new Azure Managed Identity which is used by the external-dns controller when writing Azure DNS zone entries. Currently, it does not have permission to do that, so let's grant it permission.

    # This is where resources are automatically deployed by AKS
    NODE_RESOURCE_GROUP=$(az aks show \
    --name $AKS_NAME \
    --resource-group $RESOURCE_GROUP \
    --query nodeResourceGroup -o tsv)

    # This is the managed identity created by the Web Application Routing add-on
    MANAGED_IDENTTIY_OBJECT_ID=$(az resource show \
    --name webapprouting-${AKS_NAME} \
    --resource-group $NODE_RESOURCE_GROUP \
    --resource-type Microsoft.ManagedIdentity/userAssignedIdentities \
    --query properties.principalId \
    --output tsv)

    # Grant the managed identity permissions to write DNS entries
    az role assignment create \
    --role "DNS Zone Contributor" \
    --assignee $MANAGED_IDENTTIY_OBJECT_ID \
    --scope $DNS_ZONE_ID

    The Azure Managed Identity will also be used to retrieve and rotate TLS certificates from Azure Key Vault. So we'll need to grant it permission for that too.

    az keyvault set-policy \
    --name $AKV_NAME \
    --object-id $MANAGED_IDENTTIY_OBJECT_ID \
    --secret-permissions get \
    --certificate-permissions get

    Implement Ingress for the web application

    Before we create a new Ingress manifest, let's update the existing services to use ClusterIP instead of LoadBalancer. With an Ingress in place, there is no reason why we need the Service resources to be accessible from outside the cluster. The new Ingress will be the only entrypoint for external users.

    We can use the kubectl patch command to update the services

    kubectl patch service api -p '{"spec": {"type": "ClusterIP"}}'
    kubectl patch service web -p '{"spec": {"type": "ClusterIP"}}'

    Deploy a new Ingress to place in front of the web Service. Notice there is a special annotations entry for kubernetes.azure.com/tls-cert-keyvault-uri which points back to our self-signed certificate that was uploaded to Azure Key Vault.

    kubectl apply -f - <<EOF
    apiVersion: networking.k8s.io/v1
    kind: Ingress
    metadata:
    annotations:
    kubernetes.azure.com/tls-cert-keyvault-uri: ${WEB_TLS_CERT_ID}
    name: web
    spec:
    ingressClassName: webapprouting.kubernetes.azure.com
    rules:
    - host: ${DNS_NAME}
    http:
    paths:
    - backend:
    service:
    name: web
    port:
    number: 80
    path: /
    pathType: Prefix
    - backend:
    service:
    name: api
    port:
    number: 80
    path: /api
    pathType: Prefix
    tls:
    - hosts:
    - ${DNS_NAME}
    secretName: web-tls
    EOF

    In our manifest above, we've also configured the Ingress route the traffic to either the web or api services based on the URL path requested. If the request URL includes /api/ then it will send traffic to the api backend service. Otherwise, it will send traffic to the web service.

    Within a few minutes, the external-dns controller will add an A record to Azure DNS which points to the Ingress resource's public IP. With the custom domain in place, we can simply browse using this domain name.

    info

    As mentioned above, since this is not a real domain name, we need to modify our host file to make it seem like our custom domain is resolving to the Ingress' public IP address.

    To get the ingress public IP, run the following:

    # Get the IP
    kubectl get ingress web -o jsonpath="{.status.loadBalancer.ingress[0].ip}"

    # Get the hostname
    kubectl get ingress web -o jsonpath="{.spec.tls[0].hosts[0]}"

    Next, open your host file and add an entry using the format <YOUR_PUBLIC_IP> <YOUR_CUSTOM_DOMAIN>. Below is an example of what it should look like.

    20.237.116.224 eshoponweb11265.com

    See this doc for more info on how to do this.

    When browsing to the website, you may be presented with a warning about the connection not being private. This is due to the fact that we are using a self-signed certificate. This is expected, so go ahead and proceed anyway to load up the page.

    Why is the Admin page broken?

    If you log in using the admin@microsoft.com account and browse to the Admin page, you'll notice no products are loaded on the page.

    This is because the admin page is built using Blazor and compiled as a WebAssembly application that runs in your browser. When the application was compiled, it packed the appsettings.Development.json file as an embedded resource. This file contains the base URL for the public API and it currently points to https://localhost:5099. Now that we have a domain name, we can update the base URL and point it to our custom domain.

    From the root of the eShopOnWeb repo, update the configuration file using a sed command.

    sed -i -e "s/localhost:5099/${DNS_NAME}/g" ./src/BlazorAdmin/wwwroot/appsettings.Development.json

    Rebuild and push the container to Azure Container Registry.

    # Grab the name of your Azure Container Registry
    ACR_NAME=$(az resource list \
    --resource-group $RESOURCE_GROUP \
    --resource-type Microsoft.ContainerRegistry/registries \
    --query "[0].name" -o tsv)

    # Invoke a build and publish job
    az acr build \
    --registry $ACR_NAME \
    --image $ACR_NAME.azurecr.io/web:v0.1.0 \
    --file ./src/Web/Dockerfile .

    Once the container build has completed, we can issue a kubectl patch command to quickly update the web deployment to test our change.

    kubectl patch deployment web -p "$(cat <<EOF
    {
    "spec": {
    "template": {
    "spec": {
    "containers": [
    {
    "name": "web",
    "image": "${ACR_NAME}.azurecr.io/web:v0.1.0"
    }
    ]
    }
    }
    }
    }
    EOF
    )"

    If all went well, you will be able to browse the admin page again and confirm product data is being loaded 🥳

    Conclusion

    The Web Application Routing add-on for AKS aims to streamline the process of exposing it to the public using the open-source NGINX Ingress Controller. With the add-on being managed by Azure, it natively integrates with other Azure services like Azure DNS and eliminates the need to manually create DNS entries. It can also integrate with Azure Key Vault to automatically pull in TLS certificates and rotate them as needed to further reduce operational overhead.

    We are one step closer to production and in the upcoming posts we'll further operationalize and secure our deployment, so stay tuned!

    In the meantime, check out the resources listed below for further reading.

    You can also find manifests with all the changes made in today's post in the Azure-Samples/eShopOnAKS repository.

    Resources

    Take the Cloud Skills Challenge!

    Enroll in the Cloud Skills Challenge!

    Don't miss out on this opportunity to level up your skills and stay ahead of the curve in the world of cloud native.

    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/kubernetes/index.html b/cnny-2023/tags/kubernetes/index.html index d61c38b4f1..960ead62ad 100644 --- a/cnny-2023/tags/kubernetes/index.html +++ b/cnny-2023/tags/kubernetes/index.html @@ -14,13 +14,13 @@ - - + +

    5 posts tagged with "kubernetes"

    View All Tags

    · 11 min read
    Paul Yu

    Welcome to Day 2 of Week 2 of #CloudNativeNewYear!

    The theme for this week is #Kubernetes fundamentals. Yesterday we talked about how to deploy a containerized web app workload to Azure Kubernetes Service (AKS). Today we'll explore the topic of services and ingress and walk through the steps of making our containers accessible both internally as well as over the internet so that you can share it with the world 😊

    Ask the Experts Thursday, February 9th at 9 AM PST
    Catch the Replay of the Live Demo

    Watch the recorded demo and conversation about this week's topics.

    We were live on YouTube walking through today's (and the rest of this week's) demos.

    What We'll Cover

    • Exposing Pods via Service
    • Exposing Services via Ingress
    • Takeaways
    • Resources

    Exposing Pods via Service

    There are a few ways to expose your pod in Kubernetes. One way is to take an imperative approach and use the kubectl expose command. This is probably the quickest way to achieve your goal but it isn't the best way. A better way to expose your pod by taking a declarative approach by creating a services manifest file and deploying it using the kubectl apply command.

    Don't worry if you are unsure of how to make this manifest, we'll use kubectl to help generate it.

    First, let's ensure we have the database deployed on our AKS cluster.

    📝 NOTE: If you don't have an AKS cluster deployed, please head over to Azure-Samples/azure-voting-app-rust, clone the repo, and follow the instructions in the README.md to execute the Azure deployment and setup your kubectl context. Check out the first post this week for more on the environment setup.

    kubectl apply -f ./manifests/deployment-db.yaml

    Next, let's deploy the application. If you are following along from yesterday's content, there isn't anything you need to change; however, if you are deploy the app from scratch, you'll need to modify the deployment-app.yaml manifest and update it with your image tag and database pod's IP address.

    kubectl apply -f ./manifests/deployment-app.yaml

    Now, let's expose the database using a service so that we can leverage Kubernetes' built-in service discovery to be able to reference it by name; not pod IP. Run the following command.

    kubectl expose deployment azure-voting-db \
    --port=5432 \
    --target-port=5432

    With the database exposed using service, we can update the app deployment manifest to use the service name instead of pod IP. This way, if the pod ever gets assigned a new IP, we don't have to worry about updating the IP each time and redeploying our web application. Kubernetes has internal service discovery mechanism in place that allows us to reference a service by its name.

    Let's make an update to the manifest. Replace the environment variable for DATABASE_SERVER with the following:

    - name: DATABASE_SERVER
    value: azure-voting-db

    Re-deploy the app with the updated configuration.

    kubectl apply -f ./manifests/deployment-app.yaml

    One service down, one to go. Run the following command to expose the web application.

    kubectl expose deployment azure-voting-app \
    --type=LoadBalancer \
    --port=80 \
    --target-port=8080

    Notice the --type argument has a value of LoadBalancer. This service type is implemented by the cloud-controller-manager which is part of the Kubernetes control plane. When using a managed Kubernetes cluster such as Azure Kubernetes Service, a public standard load balancer will be able to provisioned when the service type is set to LoadBalancer. The load balancer will also have a public IP assigned which will make your deployment publicly available.

    Kubernetes supports four service types:

    • ClusterIP: this is the default and limits service access to internal traffic within the cluster
    • NodePort: this assigns a port mapping on the node's IP address and allows traffic from the virtual network (outside the cluster)
    • LoadBalancer: as mentioned above, this creates a cloud-based load balancer
    • ExternalName: this is used in special case scenarios where you want to map a service to an external DNS name

    📝 NOTE: When exposing a web application to the internet, allowing external users to connect to your Service directly is not the best approach. Instead, you should use an Ingress, which we'll cover in the next section.

    Now, let's confirm you can reach the web app from the internet. You can use the following command to print the URL to your terminal.

    echo "http://$(kubectl get service azure-voting-app -o jsonpath='{.status.loadBalancer.ingress[0].ip}')"

    Great! The kubectl expose command gets the job done, but as mentioned above, it is not the best method of exposing deployments. It is better to expose deployments declaratively using a service manifest, so let's delete the services and redeploy using manifests.

    kubectl delete service azure-voting-db azure-voting-app

    To use kubectl to generate our manifest file, we can use the same kubectl expose command that we ran earlier but this time, we'll include --output=yaml and --dry-run=client. This will instruct the command to output the manifest that would be sent to the kube-api server in YAML format to the terminal.

    Generate the manifest for the database service.

    kubectl expose deployment azure-voting-db \
    --type=ClusterIP \
    --port=5432 \
    --target-port=5432 \
    --output=yaml \
    --dry-run=client > ./manifests/service-db.yaml

    Generate the manifest for the application service.

    kubectl expose deployment azure-voting-app \
    --type=LoadBalancer \
    --port=80 \
    --target-port=8080 \
    --output=yaml \
    --dry-run=client > ./manifests/service-app.yaml

    The command above redirected the YAML output to your manifests directory. Here is what the web application service looks like.

    apiVersion: v1
    kind: Service
    metadata:
    creationTimestamp: null
    labels:
    app: azure-voting-app
    name: azure-voting-app
    spec:
    ports:
    - port: 80
    protocol: TCP
    targetPort: 8080
    selector:
    app: azure-voting-app
    type: LoadBalancer
    status:
    loadBalancer: {}

    💡 TIP: To view the schema of any api-resource in Kubernetes, you can use the kubectl explain command. In this case the kubectl explain service command will tell us exactly what each of these fields do.

    Re-deploy the services using the new service manifests.

    kubectl apply -f ./manifests/service-db.yaml -f ./manifests/service-app.yaml

    # You should see TYPE is set to LoadBalancer and the EXTERNAL-IP is set
    kubectl get service azure-voting-db azure-voting-app

    Confirm again that our application is accessible again. Run the following command to print the URL to the terminal.

    echo "http://$(kubectl get service azure-voting-app -o jsonpath='{.status.loadBalancer.ingress[0].ip}')"

    That was easy, right? We just exposed both of our pods using Kubernetes services. The database only needs to be accessible from within the cluster so ClusterIP is perfect for that. For the web application, we specified the type to be LoadBalancer so that we can access the application over the public internet.

    But wait... remember that if you want to expose web applications over the public internet, a Service with a public IP is not the best way; the better approach is to use an Ingress resource.

    Exposing Services via Ingress

    If you read through the Kubernetes documentation on Ingress you will see a diagram that depicts the Ingress sitting in front of the Service resource with a routing rule between it. In order to use Ingress, you need to deploy an Ingress Controller and it can be configured with many routing rules to forward traffic to one or many backend services. So effectively, an Ingress is a load balancer for your Services.

    With that said, we no longer need a service type of LoadBalancer since the service does not need to be accessible from the internet. It only needs to be accessible from the Ingress Controller (internal to the cluster) so we can change the service type to ClusterIP.

    Update your service.yaml file to look like this:

    apiVersion: v1
    kind: Service
    metadata:
    creationTimestamp: null
    labels:
    app: azure-voting-app
    name: azure-voting-app
    spec:
    ports:
    - port: 80
    protocol: TCP
    targetPort: 8080
    selector:
    app: azure-voting-app

    📝 NOTE: The default service type is ClusterIP so we can omit the type altogether.

    Re-apply the app service manifest.

    kubectl apply -f ./manifests/service-app.yaml

    # You should see TYPE set to ClusterIP and EXTERNAL-IP set to <none>
    kubectl get service azure-voting-app

    Next, we need to install an Ingress Controller. There are quite a few options, and the Kubernetes-maintained NGINX Ingress Controller is commonly deployed.

    You could install this manually by following these instructions, but if you do that you'll be responsible for maintaining and supporting the resource.

    I like to take advantage of free maintenance and support when I can get it, so I'll opt to use the Web Application Routing add-on for AKS.

    💡 TIP: Whenever you install an AKS add-on, it will be maintained and fully supported by Azure Support.

    Enable the web application routing add-on in our AKS cluster with the following command.

    az aks addon enable \
    --name <YOUR_AKS_NAME> \
    --resource-group <YOUR_AKS_RESOURCE_GROUP>
    --addon web_application_routing

    ⚠️ WARNING: This command can take a few minutes to complete

    Now, let's use the same approach we took in creating our service to create our Ingress resource. Run the following command to generate the Ingress manifest.

    kubectl create ingress azure-voting-app \
    --class=webapprouting.kubernetes.azure.com \
    --rule="/*=azure-voting-app:80" \
    --output yaml \
    --dry-run=client > ./manifests/ingress.yaml

    The --class=webapprouting.kubernetes.azure.com option activates the AKS web application routing add-on. This AKS add-on can also integrate with other Azure services such as Azure DNS and Azure Key Vault for TLS certificate management and this special class makes it all work.

    The --rule="/*=azure-voting-app:80" option looks confusing but we can use kubectl again to help us understand how to format the value for the option.

    kubectl create ingress --help

    In the output you will see the following:

    --rule=[]:
    Rule in format host/path=service:port[,tls=secretname]. Paths containing the leading character '*' are
    considered pathType=Prefix. tls argument is optional.

    It expects a host and path separated by a forward-slash, then expects the backend service name and port separated by a colon. We're not using a hostname for this demo so we can omit it. For the path, an asterisk is used to specify a wildcard path prefix.

    So, the value of /*=azure-voting-app:80 creates a routing rule for all paths following the domain (or in our case since we don't have a hostname specified, the IP) to route traffic to our azure-voting-app backend service on port 80.

    📝 NOTE: Configuring the hostname and TLS is outside the scope of this demo but please visit this URL https://bit.ly/aks-webapp-routing for an in-depth hands-on lab centered around Web Application Routing on AKS.

    Your ingress.yaml file should look like this:

    apiVersion: networking.k8s.io/v1
    kind: Ingress
    metadata:
    creationTimestamp: null
    name: azure-voting-app
    spec:
    ingressClassName: webapprouting.kubernetes.azure.com
    rules:
    - http:
    paths:
    - backend:
    service:
    name: azure-voting-app
    port:
    number: 80
    path: /
    pathType: Prefix
    status:
    loadBalancer: {}

    Apply the app ingress manifest.

    kubectl apply -f ./manifests/ingress.yaml

    Validate the web application is available from the internet again. You can run the following command to print the URL to the terminal.

    echo "http://$(kubectl get ingress azure-voting-app -o jsonpath='{.status.loadBalancer.ingress[0].ip}')"

    Takeaways

    Exposing your applications both internally and externally can be easily achieved using Service and Ingress resources respectively. If your service is HTTP or HTTPS based and needs to be accessible from outsie the cluster, use Ingress with an internal Service (i.e., ClusterIP or NodePort); otherwise, use the Service resource. If your TCP-based Service needs to be publicly accessible, you set the type to LoadBalancer to expose a public IP for it. To learn more about these resources, please visit the links listed below.

    Lastly, if you are unsure how to begin writing your service manifest, you can use kubectl and have it do most of the work for you 🥳

    Resources

    Take the Cloud Skills Challenge!

    Enroll in the Cloud Skills Challenge!

    Don't miss out on this opportunity to level up your skills and stay ahead of the curve in the world of cloud native.

    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/kubernetes/page/2/index.html b/cnny-2023/tags/kubernetes/page/2/index.html index 17abe815a2..c15809a078 100644 --- a/cnny-2023/tags/kubernetes/page/2/index.html +++ b/cnny-2023/tags/kubernetes/page/2/index.html @@ -14,13 +14,13 @@ - - + +

    5 posts tagged with "kubernetes"

    View All Tags

    · 8 min read
    Paul Yu

    Welcome to Day 4 of Week 2 of #CloudNativeNewYear!

    The theme for this week is Kubernetes fundamentals. Yesterday we talked about how to set app configurations and secrets at runtime using Kubernetes ConfigMaps and Secrets. Today we'll explore the topic of persistent storage on Kubernetes and show you can leverage Persistent Volumes and Persistent Volume Claims to ensure your PostgreSQL data can survive container restarts.

    Ask the Experts Thursday, February 9th at 9 AM PST
    Catch the Replay of the Live Demo

    Watch the recorded demo and conversation about this week's topics.

    We were live on YouTube walking through today's (and the rest of this week's) demos.

    What We'll Cover

    • Containers are ephemeral
    • Persistent storage on Kubernetes
    • Persistent storage on AKS
    • Takeaways
    • Resources

    Containers are ephemeral

    In our sample application, the frontend UI writes vote values to a backend PostgreSQL database. By default the database container stores its data on the container's local file system, so there will be data loss when the pod is re-deployed or crashes as containers are meant to start with a clean slate each time.

    Let's re-deploy our sample app and experience the problem first hand.

    📝 NOTE: If you don't have an AKS cluster deployed, please head over to Azure-Samples/azure-voting-app-rust, clone the repo, and follow the instructions in the README.md to execute the Azure deployment and setup your kubectl context. Check out the first post this week for more on the environment setup.

    kubectl apply -f ./manifests

    Wait for the azure-voting-app service to be assigned a public IP then browse to the website and submit some votes. Use the command below to print the URL to the terminal.

    echo "http://$(kubectl get ingress azure-voting-app -o jsonpath='{.status.loadBalancer.ingress[0].ip}')"

    Now, let's delete the pods and watch Kubernetes do what it does best... that is, re-schedule pods.

    # wait for the pod to come up then ctrl+c to stop watching
    kubectl delete --all pod --wait=false && kubectl get po -w

    Once the pods have been recovered, reload the website and confirm the vote tally has been reset to zero.

    We need to fix this so that the data outlives the container.

    Persistent storage on Kubernetes

    In order for application data to survive crashes and restarts, you must implement Persistent Volumes and Persistent Volume Claims.

    A persistent volume represents storage that is available to the cluster. Storage volumes can be provisioned manually by an administrator or dynamically using Container Storage Interface (CSI) and storage classes, which includes information on how to provision CSI volumes.

    When a user needs to add persistent storage to their application, a persistent volume claim is made to allocate chunks of storage from the volume. This "claim" includes things like volume mode (e.g., file system or block storage), the amount of storage to allocate, the access mode, and optionally a storage class. Once a persistent volume claim has been deployed, users can add the volume to the pod and mount it in a container.

    In the next section, we'll demonstrate how to enable persistent storage on AKS.

    Persistent storage on AKS

    With AKS, CSI drivers and storage classes are pre-deployed into your cluster. This allows you to natively use Azure Disks, Azure Files, and Azure Blob Storage as persistent volumes. You can either bring your own Azure storage account and use it with AKS or have AKS provision an Azure storage account for you.

    To view the Storage CSI drivers that have been enabled in your AKS cluster, run the following command.

    az aks show \
    --name <YOUR_AKS_NAME> \
    --resource-group <YOUR_AKS_RESOURCE_GROUP> \
    --query storageProfile

    You should see output that looks like this.

    {
    "blobCsiDriver": null,
    "diskCsiDriver": {
    "enabled": true,
    "version": "v1"
    },
    "fileCsiDriver": {
    "enabled": true
    },
    "snapshotController": {
    "enabled": true
    }
    }

    To view the storage classes that have been installed in your cluster, run the following command.

    kubectl get storageclass

    Workload requirements will dictate which CSI driver and storage class you will need to use.

    If you need block storage, then you should use the blobCsiDriver. The driver may not be enabled by default but you can enable it by following instructions which can be found in the Resources section below.

    If you need file storage you should leverage either diskCsiDriver or fileCsiDriver. The decision between these two boils down to whether or not you need to have the underlying storage accessible by one pod or multiple pods. It is important to note that diskCsiDriver currently supports access from a single pod only. Therefore, if you need data to be accessible by multiple pods at the same time, then you should opt for fileCsiDriver.

    For our PostgreSQL deployment, we'll use the diskCsiDriver and have AKS create an Azure Disk resource for us. There is no need to create a PV resource, all we need to do to is create a PVC using the managed-csi-premium storage class.

    Run the following command to create the PVC.

    kubectl apply -f - <<EOF            
    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
    name: pvc-azuredisk
    spec:
    accessModes:
    - ReadWriteOnce
    resources:
    requests:
    storage: 10Gi
    storageClassName: managed-csi-premium
    EOF

    When you check the PVC resource, you'll notice the STATUS is set to Pending. It will be set to Bound once the volume is mounted in the PostgreSQL container.

    kubectl get persistentvolumeclaim

    Let's delete the azure-voting-db deployment.

    kubectl delete deploy azure-voting-db

    Next, we need to apply an updated deployment manifest which includes our PVC.

    kubectl apply -f - <<EOF
    apiVersion: apps/v1
    kind: Deployment
    metadata:
    creationTimestamp: null
    labels:
    app: azure-voting-db
    name: azure-voting-db
    spec:
    replicas: 1
    selector:
    matchLabels:
    app: azure-voting-db
    strategy: {}
    template:
    metadata:
    creationTimestamp: null
    labels:
    app: azure-voting-db
    spec:
    containers:
    - image: postgres:15.0-alpine
    name: postgres
    ports:
    - containerPort: 5432
    env:
    - name: POSTGRES_PASSWORD
    valueFrom:
    secretKeyRef:
    name: azure-voting-secret
    key: POSTGRES_PASSWORD
    resources: {}
    volumeMounts:
    - name: mypvc
    mountPath: "/var/lib/postgresql/data"
    subPath: "data"
    volumes:
    - name: mypvc
    persistentVolumeClaim:
    claimName: pvc-azuredisk
    EOF

    In the manifest above, you'll see that we are mounting a new volume called mypvc (the name can be whatever you want) in the pod which points to a PVC named pvc-azuredisk. With the volume in place, we can mount it in the container by referencing the name of the volume mypvc and setting the mount path to /var/lib/postgresql/data (which is the default path).

    💡 IMPORTANT: When mounting a volume into a non-empty subdirectory, you must add subPath to the volume mount and point it to a subdirectory in the volume rather than mounting at root. In our case, when Azure Disk is formatted, it leaves a lost+found directory as documented here.

    Watch the pods and wait for the STATUS to show Running and the pod's READY status shows 1/1.

    # wait for the pod to come up then ctrl+c to stop watching
    kubectl get po -w

    Verify that the STATUS of the PVC is now set to Bound

    kubectl get persistentvolumeclaim

    With the new database container running, let's restart the application pod, wait for the pod's READY status to show 1/1, then head back over to our web browser and submit a few votes.

    kubectl delete pod -lapp=azure-voting-app --wait=false && kubectl get po -lapp=azure-voting-app -w

    Now the moment of truth... let's rip out the pods again, wait for the pods to be re-scheduled, and confirm our vote counts remain in tact.

    kubectl delete --all pod --wait=false && kubectl get po -w

    If you navigate back to the website, you'll find the vote are still there 🎉

    Takeaways

    By design, containers are meant to be ephemeral and stateless workloads are ideal on Kubernetes. However, there will come a time when your data needs to outlive the container. To persist data in your Kubernetes workloads, you need to leverage PV, PVC, and optionally storage classes. In our demo scenario, we leveraged CSI drivers built into AKS and created a PVC using pre-installed storage classes. From there, we updated the database deployment to mount the PVC in the container and AKS did the rest of the work in provisioning the underlying Azure Disk. If the built-in storage classes does not fit your needs; for example, you need to change the ReclaimPolicy or change the SKU for the Azure resource, then you can create your own custom storage class and configure it just the way you need it 😊

    We'll revisit this topic again next week but in the meantime, check out some of the resources listed below to learn more.

    See you in the next post!

    Resources

    Take the Cloud Skills Challenge!

    Enroll in the Cloud Skills Challenge!

    Don't miss out on this opportunity to level up your skills and stay ahead of the curve in the world of cloud native.

    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/kubernetes/page/3/index.html b/cnny-2023/tags/kubernetes/page/3/index.html index dc4bed651a..29b16988a7 100644 --- a/cnny-2023/tags/kubernetes/page/3/index.html +++ b/cnny-2023/tags/kubernetes/page/3/index.html @@ -14,13 +14,13 @@ - - + +

    5 posts tagged with "kubernetes"

    View All Tags

    · 12 min read
    Paul Yu

    Welcome to Day 2 of Week 3 of #CloudNativeNewYear!

    The theme for this week is Bringing Your Application to Kubernetes. Yesterday we talked about getting an existing application running in Kubernetes with a full pipeline in GitHub Actions. Today we'll evaluate our sample application's configuration, storage, and networking requirements and implement using Kubernetes and Azure resources.

    Ask the Experts Thursday, February 9th at 9 AM PST
    Friday, February 10th at 11 AM PST

    Watch the recorded demo and conversation about this week's topics.

    We were live on YouTube walking through today's (and the rest of this week's) demos. Join us Friday, February 10th and bring your questions!

    What We'll Cover

    • Gather requirements
    • Implement environment variables using ConfigMaps
    • Implement persistent volumes using Azure Files
    • Implement secrets using Azure Key Vault
    • Re-package deployments
    • Conclusion
    • Resources
    caution

    Before you begin, make sure you've gone through yesterday's post to set up your AKS cluster.

    Gather requirements

    The eShopOnWeb application is written in .NET 7 and has two major pieces of functionality. The web UI is where customers can browse and shop. The web UI also includes an admin portal for managing the product catalog. This admin portal, is packaged as a WebAssembly application and relies on a separate REST API service. Both the web UI and the REST API connect to the same SQL Server container.

    Looking through the source code which can be found here we can identify requirements for configs, persistent storage, and secrets.

    Database server

    • Need to store the password for the sa account as a secure secret
    • Need persistent storage volume for data directory
    • Need to inject environment variables for SQL Server license type and EULA acceptance

    Web UI and REST API service

    • Need to store database connection string as a secure secret
    • Need to inject ASP.NET environment variables to override app settings
    • Need persistent storage volume for ASP.NET key storage

    Implement environment variables using ConfigMaps

    ConfigMaps are relatively straight-forward to create. If you were following along with the examples last week, this should be review 😉

    Create a ConfigMap to store database environment variables.

    kubectl apply -f - <<EOF
    apiVersion: v1
    kind: ConfigMap
    metadata:
    name: mssql-settings
    data:
    MSSQL_PID: Developer
    ACCEPT_EULA: "Y"
    EOF

    Create another ConfigMap to store ASP.NET environment variables.

    kubectl apply -f - <<EOF
    apiVersion: v1
    kind: ConfigMap
    metadata:
    name: aspnet-settings
    data:
    ASPNETCORE_ENVIRONMENT: Development
    EOF

    Implement persistent volumes using Azure Files

    Similar to last week, we'll take advantage of storage classes built into AKS. For our SQL Server data, we'll use the azurefile-csi-premium storage class and leverage an Azure Files resource as our PersistentVolume.

    Create a PersistentVolumeClaim (PVC) for persisting SQL Server data.

    kubectl apply -f - <<EOF
    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
    name: mssql-data
    spec:
    accessModes:
    - ReadWriteMany
    storageClassName: azurefile-csi-premium
    resources:
    requests:
    storage: 5Gi
    EOF

    Create another PVC for persisting ASP.NET data.

    kubectl apply -f - <<EOF
    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
    name: aspnet-data
    spec:
    accessModes:
    - ReadWriteMany
    storageClassName: azurefile-csi-premium
    resources:
    requests:
    storage: 5Gi
    EOF

    Implement secrets using Azure Key Vault

    It's a well known fact that Kubernetes secretes are not really secrets. They're just base64-encoded values and not secure, especially if malicious users have access to your Kubernetes cluster.

    In a production scenario, you will want to leverage an external vault like Azure Key Vault or HashiCorp Vault to encrypt and store secrets.

    With AKS, we can enable the Secrets Store CSI driver add-on which will allow us to leverage Azure Key Vault.

    # Set some variables
    RG_NAME=<YOUR_RESOURCE_GROUP_NAME>
    AKS_NAME=<YOUR_AKS_CLUSTER_NAME>
    ACR_NAME=<YOUR_ACR_NAME>

    az aks enable-addons \
    --addons azure-keyvault-secrets-provider \
    --name $AKS_NAME \
    --resource-group $RG_NAME

    With the add-on enabled, you should see aks-secrets-store-csi-driver and aks-secrets-store-provider-azure resources installed on each node in your Kubernetes cluster.

    Run the command below to verify.

    kubectl get pods \
    --namespace kube-system \
    --selector 'app in (secrets-store-csi-driver, secrets-store-provider-azure)'

    The Secrets Store CSI driver allows us to use secret stores via Container Storage Interface (CSI) volumes. This provider offers capabilities such as mounting and syncing between the secure vault and Kubernetes Secrets. On AKS, the Azure Key Vault Provider for Secrets Store CSI Driver enables integration with Azure Key Vault.

    You may not have an Azure Key Vault created yet, so let's create one and add some secrets to it.

    AKV_NAME=$(az keyvault create \
    --name akv-eshop$RANDOM \
    --resource-group $RG_NAME \
    --query name -o tsv)

    # Database server password
    az keyvault secret set \
    --vault-name $AKV_NAME \
    --name mssql-password \
    --value "@someThingComplicated1234"

    # Catalog database connection string
    az keyvault secret set \
    --vault-name $AKV_NAME \
    --name mssql-connection-catalog \
    --value "Server=db;Database=Microsoft.eShopOnWeb.CatalogDb;User Id=sa;Password=@someThingComplicated1234;TrustServerCertificate=True;"

    # Identity database connection string
    az keyvault secret set \
    --vault-name $AKV_NAME \
    --name mssql-connection-identity \
    --value "Server=db;Database=Microsoft.eShopOnWeb.Identity;User Id=sa;Password=@someThingComplicated1234;TrustServerCertificate=True;"

    Pods authentication using Azure Workload Identity

    In order for our Pods to retrieve secrets from Azure Key Vault, we'll need to set up a way for the Pod to authenticate against Azure AD. This can be achieved by implementing the new Azure Workload Identity feature of AKS.

    info

    At the time of this writing, the workload identity feature of AKS is in Preview.

    The workload identity feature within AKS allows us to leverage native Kubernetes resources and link a Kubernetes ServiceAccount to an Azure Managed Identity to authenticate against Azure AD.

    For the authentication flow, our Kubernetes cluster will act as an Open ID Connect (OIDC) issuer and will be able issue identity tokens to ServiceAccounts which will be assigned to our Pods.

    The Azure Managed Identity will be granted permission to access secrets in our Azure Key Vault and with the ServiceAccount being assigned to our Pods, they will be able to retrieve our secrets.

    For more information on how the authentication mechanism all works, check out this doc.

    To implement all this, start by enabling the new preview feature for AKS.

    az feature register \
    --namespace "Microsoft.ContainerService" \
    --name "EnableWorkloadIdentityPreview"
    caution

    This can take several minutes to complete.

    Check the status and ensure the state shows Regestered before moving forward.

    az feature show \
    --namespace "Microsoft.ContainerService" \
    --name "EnableWorkloadIdentityPreview"

    Update your AKS cluster to enable the workload identity feature and enable the OIDC issuer endpoint.

    az aks update \
    --name $AKS_NAME \
    --resource-group $RG_NAME \
    --enable-workload-identity \
    --enable-oidc-issuer

    Create an Azure Managed Identity and retrieve its client ID.

    MANAGED_IDENTITY_CLIENT_ID=$(az identity create \
    --name aks-workload-identity \
    --resource-group $RG_NAME \
    --subscription $(az account show --query id -o tsv) \
    --query 'clientId' -o tsv)

    Create the Kubernetes ServiceAccount.

    # Set namespace (this must align with the namespace that your app is deployed into)
    SERVICE_ACCOUNT_NAMESPACE=default

    # Set the service account name
    SERVICE_ACCOUNT_NAME=eshop-serviceaccount

    # Create the service account
    kubectl apply -f - <<EOF
    apiVersion: v1
    kind: ServiceAccount
    metadata:
    annotations:
    azure.workload.identity/client-id: ${MANAGED_IDENTITY_CLIENT_ID}
    labels:
    azure.workload.identity/use: "true"
    name: ${SERVICE_ACCOUNT_NAME}
    namespace: ${SERVICE_ACCOUNT_NAMESPACE}
    EOF
    info

    Note to enable this ServiceAccount to work with Azure Workload Identity, you must annotate the resource with azure.workload.identity/client-id, and add a label of azure.workload.identity/use: "true"

    That was a lot... Let's review what we just did.

    We have an Azure Managed Identity (object in Azure AD), an OIDC issuer URL (endpoint in our Kubernetes cluster), and a Kubernetes ServiceAccount.

    The next step is to "tie" these components together and establish a Federated Identity Credential so that Azure AD can trust authentication requests from your Kubernetes cluster.

    info

    This identity federation can be established between Azure AD any Kubernetes cluster; not just AKS 🤗

    To establish the federated credential, we'll need the OIDC issuer URL, and a subject which points to your Kubernetes ServiceAccount.

    # Get the OIDC issuer URL
    OIDC_ISSUER_URL=$(az aks show \
    --name $AKS_NAME \
    --resource-group $RG_NAME \
    --query "oidcIssuerProfile.issuerUrl" -o tsv)

    # Set the subject name using this format: `system:serviceaccount:<YOUR_SERVICE_ACCOUNT_NAMESPACE>:<YOUR_SERVICE_ACCOUNT_NAME>`
    SUBJECT=system:serviceaccount:$SERVICE_ACCOUNT_NAMESPACE:$SERVICE_ACCOUNT_NAME

    az identity federated-credential create \
    --name aks-federated-credential \
    --identity-name aks-workload-identity \
    --resource-group $RG_NAME \
    --issuer $OIDC_ISSUER_URL \
    --subject $SUBJECT

    With the authentication components set, we can now create a SecretProviderClass which includes details about the Azure Key Vault, the secrets to pull out from the vault, and identity used to access the vault.

    # Get the tenant id for the key vault
    TENANT_ID=$(az keyvault show \
    --name $AKV_NAME \
    --resource-group $RG_NAME \
    --query properties.tenantId -o tsv)

    # Create the secret provider for azure key vault
    kubectl apply -f - <<EOF
    apiVersion: secrets-store.csi.x-k8s.io/v1
    kind: SecretProviderClass
    metadata:
    name: eshop-azure-keyvault
    spec:
    provider: azure
    parameters:
    usePodIdentity: "false"
    useVMManagedIdentity: "false"
    clientID: "${MANAGED_IDENTITY_CLIENT_ID}"
    keyvaultName: "${AKV_NAME}"
    cloudName: ""
    objects: |
    array:
    - |
    objectName: mssql-password
    objectType: secret
    objectVersion: ""
    - |
    objectName: mssql-connection-catalog
    objectType: secret
    objectVersion: ""
    - |
    objectName: mssql-connection-identity
    objectType: secret
    objectVersion: ""
    tenantId: "${TENANT_ID}"
    secretObjects:
    - secretName: eshop-secrets
    type: Opaque
    data:
    - objectName: mssql-password
    key: mssql-password
    - objectName: mssql-connection-catalog
    key: mssql-connection-catalog
    - objectName: mssql-connection-identity
    key: mssql-connection-identity
    EOF

    Finally, lets grant the Azure Managed Identity permissions to retrieve secrets from the Azure Key Vault.

    az keyvault set-policy \
    --name $AKV_NAME \
    --secret-permissions get \
    --spn $MANAGED_IDENTITY_CLIENT_ID

    Re-package deployments

    Update your database deployment to load environment variables from our ConfigMap, attach the PVC and SecretProviderClass as volumes, mount the volumes into the Pod, and use the ServiceAccount to retrieve secrets.

    Additionally, you may notice the database Pod is set to use fsGroup:10001 as part of the securityContext. This is required as the MSSQL container runs using a non-root account called mssql and this account has the proper permissions to read/write data at the /var/opt/mssql mount path.

    kubectl apply -f - <<EOF
    apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: db
    labels:
    app: db
    spec:
    replicas: 1
    selector:
    matchLabels:
    app: db
    template:
    metadata:
    labels:
    app: db
    spec:
    securityContext:
    fsGroup: 10001
    serviceAccountName: ${SERVICE_ACCOUNT_NAME}
    containers:
    - name: db
    image: mcr.microsoft.com/mssql/server:2019-latest
    ports:
    - containerPort: 1433
    envFrom:
    - configMapRef:
    name: mssql-settings
    env:
    - name: MSSQL_SA_PASSWORD
    valueFrom:
    secretKeyRef:
    name: eshop-secrets
    key: mssql-password
    resources: {}
    volumeMounts:
    - name: mssqldb
    mountPath: /var/opt/mssql
    - name: eshop-secrets
    mountPath: "/mnt/secrets-store"
    readOnly: true
    volumes:
    - name: mssqldb
    persistentVolumeClaim:
    claimName: mssql-data
    - name: eshop-secrets
    csi:
    driver: secrets-store.csi.k8s.io
    readOnly: true
    volumeAttributes:
    secretProviderClass: eshop-azure-keyvault
    EOF

    We'll update the API and Web deployments in a similar way.

    # Set the image tag
    IMAGE_TAG=<YOUR_IMAGE_TAG>

    # API deployment
    kubectl apply -f - <<EOF
    apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: api
    labels:
    app: api
    spec:
    replicas: 1
    selector:
    matchLabels:
    app: api
    template:
    metadata:
    labels:
    app: api
    spec:
    serviceAccount: ${SERVICE_ACCOUNT_NAME}
    containers:
    - name: api
    image: ${ACR_NAME}.azurecr.io/api:${IMAGE_TAG}
    ports:
    - containerPort: 80
    envFrom:
    - configMapRef:
    name: aspnet-settings
    env:
    - name: ConnectionStrings__CatalogConnection
    valueFrom:
    secretKeyRef:
    name: eshop-secrets
    key: mssql-connection-catalog
    - name: ConnectionStrings__IdentityConnection
    valueFrom:
    secretKeyRef:
    name: eshop-secrets
    key: mssql-connection-identity
    resources: {}
    volumeMounts:
    - name: aspnet
    mountPath: ~/.aspnet/https:/root/.aspnet/https:ro
    - name: eshop-secrets
    mountPath: "/mnt/secrets-store"
    readOnly: true
    volumes:
    - name: aspnet
    persistentVolumeClaim:
    claimName: aspnet-data
    - name: eshop-secrets
    csi:
    driver: secrets-store.csi.k8s.io
    readOnly: true
    volumeAttributes:
    secretProviderClass: eshop-azure-keyvault
    EOF

    ## Web deployment
    kubectl apply -f - <<EOF
    apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: web
    labels:
    app: web
    spec:
    replicas: 1
    selector:
    matchLabels:
    app: web
    template:
    metadata:
    labels:
    app: web
    spec:
    serviceAccount: ${SERVICE_ACCOUNT_NAME}
    containers:
    - name: web
    image: ${ACR_NAME}.azurecr.io/web:${IMAGE_TAG}
    ports:
    - containerPort: 80
    envFrom:
    - configMapRef:
    name: aspnet-settings
    env:
    - name: ConnectionStrings__CatalogConnection
    valueFrom:
    secretKeyRef:
    name: eshop-secrets
    key: mssql-connection-catalog
    - name: ConnectionStrings__IdentityConnection
    valueFrom:
    secretKeyRef:
    name: eshop-secrets
    key: mssql-connection-identity
    resources: {}
    volumeMounts:
    - name: aspnet
    mountPath: ~/.aspnet/https:/root/.aspnet/https:ro
    - name: eshop-secrets
    mountPath: "/mnt/secrets-store"
    readOnly: true
    volumes:
    - name: aspnet
    persistentVolumeClaim:
    claimName: aspnet-data
    - name: eshop-secrets
    csi:
    driver: secrets-store.csi.k8s.io
    readOnly: true
    volumeAttributes:
    secretProviderClass: eshop-azure-keyvault
    EOF

    If all went well with your deployment updates, you should be able to browse to your website and buy some merchandise again 🥳

    echo "http://$(kubectl get service web -o jsonpath='{.status.loadBalancer.ingress[0].ip}')"

    Conclusion

    Although there is no visible changes on with our website, we've made a ton of changes on the Kubernetes backend to make this application much more secure and resilient.

    We used a combination of Kubernetes resources and AKS-specific features to achieve our goal of securing our secrets and ensuring data is not lost on container crashes and restarts.

    To learn more about the components we leveraged here today, checkout the resources and additional tutorials listed below.

    You can also find manifests with all the changes made in today's post in the Azure-Samples/eShopOnAKS repository.

    See you in the next post!

    Resources

    Take the Cloud Skills Challenge!

    Enroll in the Cloud Skills Challenge!

    Don't miss out on this opportunity to level up your skills and stay ahead of the curve in the world of cloud native.

    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/kubernetes/page/4/index.html b/cnny-2023/tags/kubernetes/page/4/index.html index d793a8abbc..1193a0c00d 100644 --- a/cnny-2023/tags/kubernetes/page/4/index.html +++ b/cnny-2023/tags/kubernetes/page/4/index.html @@ -14,13 +14,13 @@ - - + +

    5 posts tagged with "kubernetes"

    View All Tags

    · 10 min read
    Paul Yu

    Welcome to Day 3 of Week 3 of #CloudNativeNewYear!

    The theme for this week is Bringing Your Application to Kubernetes. Yesterday we added configuration, secrets, and storage to our app. Today we'll explore how to expose the eShopOnWeb app so that customers can reach it over the internet using a custom domain name and TLS.

    Ask the Experts Thursday, February 9th at 9 AM PST
    Friday, February 10th at 11 AM PST

    Watch the recorded demo and conversation about this week's topics.

    We were live on YouTube walking through today's (and the rest of this week's) demos. Join us Friday, February 10th and bring your questions!

    What We'll Cover

    • Gather requirements
    • Generate TLS certificate and store in Azure Key Vault
    • Implement custom DNS using Azure DNS
    • Enable Web Application Routing add-on for AKS
    • Implement Ingress for the web application
    • Conclusion
    • Resources

    Gather requirements

    Currently, our eShopOnWeb app has three Kubernetes services deployed:

    1. db exposed internally via ClusterIP
    2. api exposed externally via LoadBalancer
    3. web exposed externally via LoadBalancer

    As mentioned in my post last week, Services allow applications to communicate with each other using DNS names. Kubernetes has service discovery capabilities built-in that allows Pods to resolve Services simply by using their names.

    In the case of our api and web deployments, they can simply reach the database by calling its name. The service type of ClusterIP for the db can remain as-is since it only needs to be accessed by the api and web apps.

    On the other hand, api and web both need to be accessed over the public internet. Currently, these services are using service type LoadBalancer which tells AKS to provision an Azure Load Balancer with a public IP address. No one is going to remember the IP addresses, so we need to make the app more accessible by adding a custom domain name and securing it with a TLS certificate.

    Here's what we're going to need:

    • Custom domain name for our app
    • TLS certificate for the custom domain name
    • Routing rule to ensure requests with /api/ in the URL is routed to the backend REST API
    • Routing rule to ensure requests without /api/ in the URL is routing to the web UI

    Just like last week, we will use the Web Application Routing add-on for AKS. But this time, we'll integrate it with Azure DNS and Azure Key Vault to satisfy all of our requirements above.

    info

    At the time of this writing the add-on is still in Public Preview

    Generate TLS certificate and store in Azure Key Vault

    We deployed an Azure Key Vault yesterday to store secrets. We'll use it again to store a TLS certificate too.

    Let's create and export a self-signed certificate for the custom domain.

    DNS_NAME=eshoponweb$RANDOM.com
    openssl req -new -x509 -nodes -out web-tls.crt -keyout web-tls.key -subj "/CN=${DNS_NAME}" -addext "subjectAltName=DNS:${DNS_NAME}"
    openssl pkcs12 -export -in web-tls.crt -inkey web-tls.key -out web-tls.pfx -password pass:
    info

    For learning purposes we'll use a self-signed certificate and a fake custom domain name.

    To browse to the site using the fake domain, we'll mimic a DNS lookup by adding an entry to your host file which maps the public IP address assigned to the ingress controller to the custom domain.

    In a production scenario, you will need to have a real domain delegated to Azure DNS and a valid TLS certificate for the domain.

    Grab your Azure Key Vault name and set the value in a variable for later use.

    RESOURCE_GROUP=cnny-week3

    AKV_NAME=$(az resource list \
    --resource-group $RESOURCE_GROUP \
    --resource-type Microsoft.KeyVault/vaults \
    --query "[0].name" -o tsv)

    Grant yourself permissions to get, list, and import certificates.

    MY_USER_NAME=$(az account show --query user.name -o tsv)
    MY_USER_OBJECT_ID=$(az ad user show --id $MY_USER_NAME --query id -o tsv)

    az keyvault set-policy \
    --name $AKV_NAME \
    --object-id $MY_USER_OBJECT_ID \
    --certificate-permissions get list import

    Upload the TLS certificate to Azure Key Vault and grab its certificate URI.

    WEB_TLS_CERT_ID=$(az keyvault certificate import \
    --vault-name $AKV_NAME \
    --name web-tls \
    --file web-tls.pfx \
    --query id \
    --output tsv)

    Implement custom DNS with Azure DNS

    Create a custom domain for our application and grab its Azure resource id.

    DNS_ZONE_ID=$(az network dns zone create \
    --name $DNS_NAME \
    --resource-group $RESOURCE_GROUP \
    --query id \
    --output tsv)

    Enable Web Application Routing add-on for AKS

    As we enable the Web Application Routing add-on, we'll also pass in the Azure DNS Zone resource id which triggers the installation of the external-dns controller in your Kubernetes cluster. This controller will be able to write Azure DNS zone entries on your behalf as you deploy Ingress manifests.

    AKS_NAME=$(az resource list \
    --resource-group $RESOURCE_GROUP \
    --resource-type Microsoft.ContainerService/managedClusters \
    --query "[0].name" -o tsv)

    az aks enable-addons \
    --name $AKS_NAME \
    --resource-group $RESOURCE_GROUP \
    --addons web_application_routing \
    --dns-zone-resource-id=$DNS_ZONE_ID \
    --enable-secret-rotation

    The add-on will also deploy a new Azure Managed Identity which is used by the external-dns controller when writing Azure DNS zone entries. Currently, it does not have permission to do that, so let's grant it permission.

    # This is where resources are automatically deployed by AKS
    NODE_RESOURCE_GROUP=$(az aks show \
    --name $AKS_NAME \
    --resource-group $RESOURCE_GROUP \
    --query nodeResourceGroup -o tsv)

    # This is the managed identity created by the Web Application Routing add-on
    MANAGED_IDENTTIY_OBJECT_ID=$(az resource show \
    --name webapprouting-${AKS_NAME} \
    --resource-group $NODE_RESOURCE_GROUP \
    --resource-type Microsoft.ManagedIdentity/userAssignedIdentities \
    --query properties.principalId \
    --output tsv)

    # Grant the managed identity permissions to write DNS entries
    az role assignment create \
    --role "DNS Zone Contributor" \
    --assignee $MANAGED_IDENTTIY_OBJECT_ID \
    --scope $DNS_ZONE_ID

    The Azure Managed Identity will also be used to retrieve and rotate TLS certificates from Azure Key Vault. So we'll need to grant it permission for that too.

    az keyvault set-policy \
    --name $AKV_NAME \
    --object-id $MANAGED_IDENTTIY_OBJECT_ID \
    --secret-permissions get \
    --certificate-permissions get

    Implement Ingress for the web application

    Before we create a new Ingress manifest, let's update the existing services to use ClusterIP instead of LoadBalancer. With an Ingress in place, there is no reason why we need the Service resources to be accessible from outside the cluster. The new Ingress will be the only entrypoint for external users.

    We can use the kubectl patch command to update the services

    kubectl patch service api -p '{"spec": {"type": "ClusterIP"}}'
    kubectl patch service web -p '{"spec": {"type": "ClusterIP"}}'

    Deploy a new Ingress to place in front of the web Service. Notice there is a special annotations entry for kubernetes.azure.com/tls-cert-keyvault-uri which points back to our self-signed certificate that was uploaded to Azure Key Vault.

    kubectl apply -f - <<EOF
    apiVersion: networking.k8s.io/v1
    kind: Ingress
    metadata:
    annotations:
    kubernetes.azure.com/tls-cert-keyvault-uri: ${WEB_TLS_CERT_ID}
    name: web
    spec:
    ingressClassName: webapprouting.kubernetes.azure.com
    rules:
    - host: ${DNS_NAME}
    http:
    paths:
    - backend:
    service:
    name: web
    port:
    number: 80
    path: /
    pathType: Prefix
    - backend:
    service:
    name: api
    port:
    number: 80
    path: /api
    pathType: Prefix
    tls:
    - hosts:
    - ${DNS_NAME}
    secretName: web-tls
    EOF

    In our manifest above, we've also configured the Ingress route the traffic to either the web or api services based on the URL path requested. If the request URL includes /api/ then it will send traffic to the api backend service. Otherwise, it will send traffic to the web service.

    Within a few minutes, the external-dns controller will add an A record to Azure DNS which points to the Ingress resource's public IP. With the custom domain in place, we can simply browse using this domain name.

    info

    As mentioned above, since this is not a real domain name, we need to modify our host file to make it seem like our custom domain is resolving to the Ingress' public IP address.

    To get the ingress public IP, run the following:

    # Get the IP
    kubectl get ingress web -o jsonpath="{.status.loadBalancer.ingress[0].ip}"

    # Get the hostname
    kubectl get ingress web -o jsonpath="{.spec.tls[0].hosts[0]}"

    Next, open your host file and add an entry using the format <YOUR_PUBLIC_IP> <YOUR_CUSTOM_DOMAIN>. Below is an example of what it should look like.

    20.237.116.224 eshoponweb11265.com

    See this doc for more info on how to do this.

    When browsing to the website, you may be presented with a warning about the connection not being private. This is due to the fact that we are using a self-signed certificate. This is expected, so go ahead and proceed anyway to load up the page.

    Why is the Admin page broken?

    If you log in using the admin@microsoft.com account and browse to the Admin page, you'll notice no products are loaded on the page.

    This is because the admin page is built using Blazor and compiled as a WebAssembly application that runs in your browser. When the application was compiled, it packed the appsettings.Development.json file as an embedded resource. This file contains the base URL for the public API and it currently points to https://localhost:5099. Now that we have a domain name, we can update the base URL and point it to our custom domain.

    From the root of the eShopOnWeb repo, update the configuration file using a sed command.

    sed -i -e "s/localhost:5099/${DNS_NAME}/g" ./src/BlazorAdmin/wwwroot/appsettings.Development.json

    Rebuild and push the container to Azure Container Registry.

    # Grab the name of your Azure Container Registry
    ACR_NAME=$(az resource list \
    --resource-group $RESOURCE_GROUP \
    --resource-type Microsoft.ContainerRegistry/registries \
    --query "[0].name" -o tsv)

    # Invoke a build and publish job
    az acr build \
    --registry $ACR_NAME \
    --image $ACR_NAME.azurecr.io/web:v0.1.0 \
    --file ./src/Web/Dockerfile .

    Once the container build has completed, we can issue a kubectl patch command to quickly update the web deployment to test our change.

    kubectl patch deployment web -p "$(cat <<EOF
    {
    "spec": {
    "template": {
    "spec": {
    "containers": [
    {
    "name": "web",
    "image": "${ACR_NAME}.azurecr.io/web:v0.1.0"
    }
    ]
    }
    }
    }
    }
    EOF
    )"

    If all went well, you will be able to browse the admin page again and confirm product data is being loaded 🥳

    Conclusion

    The Web Application Routing add-on for AKS aims to streamline the process of exposing it to the public using the open-source NGINX Ingress Controller. With the add-on being managed by Azure, it natively integrates with other Azure services like Azure DNS and eliminates the need to manually create DNS entries. It can also integrate with Azure Key Vault to automatically pull in TLS certificates and rotate them as needed to further reduce operational overhead.

    We are one step closer to production and in the upcoming posts we'll further operationalize and secure our deployment, so stay tuned!

    In the meantime, check out the resources listed below for further reading.

    You can also find manifests with all the changes made in today's post in the Azure-Samples/eShopOnAKS repository.

    Resources

    Take the Cloud Skills Challenge!

    Enroll in the Cloud Skills Challenge!

    Don't miss out on this opportunity to level up your skills and stay ahead of the curve in the world of cloud native.

    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/kubernetes/page/5/index.html b/cnny-2023/tags/kubernetes/page/5/index.html index 78c9c621e1..d6896ddddd 100644 --- a/cnny-2023/tags/kubernetes/page/5/index.html +++ b/cnny-2023/tags/kubernetes/page/5/index.html @@ -14,13 +14,13 @@ - - + +

    5 posts tagged with "kubernetes"

    View All Tags

    · 6 min read
    Josh Duffney

    Welcome to Day 5 of Week 3 of #CloudNativeNewYear!

    The theme for this week is Bringing Your Application to Kubernetes. Yesterday we talked about debugging and instrumenting our application. Today we'll explore the topic of container image signing and secure supply chain.

    Ask the Experts Thursday, February 9th at 9 AM PST
    Friday, February 10th at 11 AM PST

    Watch the recorded demo and conversation about this week's topics.

    We were live on YouTube walking through today's (and the rest of this week's) demos. Join us Friday, February 10th and bring your questions!

    What We'll Cover

    • Introduction
    • Prerequisites
    • Create a digital signing certificate
    • Generate an Azure Container Registry Token
    • Set up Notation
    • Install the Notation Azure Key Vault Plugin
    • Add the signing Certificate to Notation
    • Sign Container Images
    • Conclusion

    Introduction

    The secure supply chain is a crucial aspect of software development, delivery, and deployment, and digital signing plays a critical role in this process.

    By using digital signatures to verify the authenticity and integrity of container images, organizations can improve the security of your software supply chain and reduce the risk of security breaches and data compromise.

    In this article, you'll learn how to use Notary, an open-source project hosted by the Cloud Native Computing Foundation (CNCF) to digitally sign container images stored on Azure Container Registry.

    Prerequisites

    To follow along, you'll need an instance of:

    Create a digital signing certificate

    A digital signing certificate is a certificate that is used to digitally sign and verify the authenticity and integrity of digital artifacts. Such documents, software, and of course container images.

    Before you can implement digital signatures, you must first create a digital signing certificate.

    Run the following command to generate the certificate:

    1. Create the policy file

      cat <<EOF > ./my_policy.json
      {
      "issuerParameters": {
      "certificateTransparency": null,
      "name": "Self"
      },
      "x509CertificateProperties": {
      "ekus": [
      "1.3.6.1.5.5.7.3.3"
      ],
      "key_usage": [
      "digitalSignature"
      ],
      "subject": "CN=${keySubjectName}",
      "validityInMonths": 12
      }
      }
      EOF

      The ekus and key usage of this certificate policy dictate that the certificate can only be used for digital signatures.

    2. Create the certificate in Azure Key Vault

      az keyvault certificate create --name $keyName --vault-name $keyVaultName --policy @my_policy.json

      Replace $keyName and $keyVaultName with your desired certificate name and Azure Key Vault instance name.

    Generate a Azure Container Registry token

    Azure Container Registry tokens are used to grant access to the contents of the registry. Tokens can be used for a variety of things such as pulling images, pushing images, or managing the registry.

    As part of the container image signing workflow, you'll need a token to authenticate the Notation CLI with your Azure Container Registry.

    Run the following command to generate an ACR token:

    az acr token create \
    --name $tokenName \
    --registry $registry \
    --scope-map _repositories_admin \
    --query 'credentials.passwords[0].value' \
    --only-show-errors \
    --output tsv

    Replace $tokenName with your name for the ACR token and $registry with the name of your ACR instance.

    Setup Notation

    Notation is the command-line interface for the CNCF Notary project. You'll use it to digitally sign the api and web container images for the eShopOnWeb application.

    Run the following commands to download and install the NotationCli:

    1. Open a terminal or command prompt window

    2. Download the Notary notation release

      curl -Lo notation.tar.gz https://github.com/notaryproject/notation/releases/download/v1.0.0-rc.1/notation_1.0.0-rc.1_linux_amd64.tar.gz > /dev/null 2>&1

      If you're not using Linux, you can find the releases here.

    3. Extract the contents of the notation.tar.gz

      tar xvzf notation.tar.gz > /dev/null 2>&1
    4. Copy the notation binary to the $HOME/bin directory

      cp ./notation $HOME/bin
    5. Add the $HOME/bin directory to the PATH environment variable

      export PATH="$HOME/bin:$PATH"
    6. Remove the downloaded files

      rm notation.tar.gz LICENSE
    7. Check the notation version

      notation --version

    Install the Notation Azure Key Vault plugin

    By design the NotationCli supports plugins that extend its digital signing capabilities to remote registries. And in order to sign your container images stored in Azure Container Registry, you'll need to install the Azure Key Vault plugin for Notation.

    Run the following commands to install the azure-kv plugin:

    1. Download the plugin

      curl -Lo notation-azure-kv.tar.gz \
      https://github.com/Azure/notation-azure-kv/releases/download/v0.5.0-rc.1/notation-azure-kv_0.5.0-rc.1_linux_amd64.tar.gz > /dev/null 2>&1

      Non-Linux releases can be found here.

    2. Extract to the plugin directory & delete download files

      tar xvzf notation-azure-kv.tar.gz -C ~/.config/notation/plugins/azure-kv notation-azure-kv > /dev/null 2>&

      rm -rf notation-azure-kv.tar.gz
    3. Verify the plugin was installed

      notation plugin ls

    Add the signing certificate to Notation

    Now that you have Notation and the Azure Key Vault plugin installed, add the certificate's keyId created above to Notation.

    1. Get the Certificate Key ID from Azure Key Vault

      az keyvault certificate show \
      --vault-name $keyVaultName \
      --name $keyName \
      --query "kid" --only-show-errors --output tsv

      Replace $keyVaultName and $keyName with the appropriate information.

    2. Add the Key ID to KMS using Notation

      notation key add --plugin azure-kv --id $keyID $keyName
    3. Check the key list

      notation key ls

    Sign Container Images

    At this point, all that's left is to sign the container images.

    Run the notation sign command to sign the api and web container images:

    notation sign $registry.azurecr.io/web:$tag \
    --username $tokenName \
    --password $tokenPassword

    notation sign $registry.azurecr.io/api:$tag \
    --username $tokenName \
    --password $tokenPassword

    Replace $registry, $tag, $tokenName, and $tokenPassword with the appropriate values. To improve security, use a SHA hash for the tag.

    NOTE: If you didn't take note of the token password, you can rerun the az acr token create command to generate a new password.

    Conclusion

    Digital signing plays a critical role in ensuring the security of software supply chains.

    By signing software components, organizations can verify the authenticity and integrity of software, helping to prevent unauthorized modifications, tampering, and malware.

    And if you want to take digital signing to a whole new level by using them to prevent the deployment of unsigned container images, check out the Ratify project on GitHub!

    Take the Cloud Skills Challenge!

    Enroll in the Cloud Skills Challenge!

    Don't miss out on this opportunity to level up your skills and stay ahead of the curve in the world of cloud native.

    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/microservices/index.html b/cnny-2023/tags/microservices/index.html index 5b63f3e2a7..de183b0e4a 100644 --- a/cnny-2023/tags/microservices/index.html +++ b/cnny-2023/tags/microservices/index.html @@ -14,13 +14,13 @@ - - + +

    One post tagged with "microservices"

    View All Tags

    · 6 min read
    Josh Duffney

    Welcome to Day 4 of Week 1 of #CloudNativeNewYear!

    This week we'll focus on advanced topics and best practices for Cloud-Native practitioners, kicking off with this post on Serverless Container Options with Azure. We'll look at technologies, tools and best practices that range from managed services like Azure Kubernetes Service, to options allowing finer granularity of control and oversight.

    What We'll Cover

    • What is Microservice Architecture?
    • How do you design a Microservice?
    • What challenges do Microservices introduce?
    • Conclusion
    • Resources


    Microservices are a modern way of designing and building software that increases deployment velocity by decomposing an application into small autonomous services that can be deployed independently.

    By deploying loosely coupled microservices your applications can be developed, deployed, and scaled independently. Because each service is independent, it can be updated or replaced without having to worry about the impact on the rest of the application. This means that if a bug is found in one service, it can be fixed without having to redeploy the entire application. All of which gives an organization the ability to deliver value to their customers faster.

    In this article, we will explore the basics of microservices architecture, its benefits and challenges, and how it can help improve the development, deployment, and maintenance of software applications.

    What is Microservice Architecture?

    Before explaining what Microservice architecture is, it’s important to understand what problems microservices aim to address.

    Traditional software development is centered around building monolithic applications. Monolithic applications are built as a single, large codebase. Meaning your code is tightly coupled causing the monolithic app to suffer from the following:

    Too much Complexity: Monolithic applications can become complex and difficult to understand and maintain as they grow. This can make it hard to identify and fix bugs and add new features.

    Difficult to Scale: Monolithic applications can be difficult to scale as they often have a single point of failure, which can cause the whole application to crash if a service fails.

    Slow Deployment: Deploying a monolithic application can be risky and time-consuming, as a small change in one part of the codebase can affect the entire application.

    Microservice architecture (often called microservices) is an architecture style that addresses the challenges created by Monolithic applications. Microservices architecture is a way of designing and building software applications as a collection of small, independent services that communicate with each other through APIs. This allows for faster development and deployment cycles, as well as easier scaling and maintenance than is possible with a monolithic application.

    How do you design a Microservice?

    Building applications with Microservices architecture requires a different approach. Microservices architecture focuses on business capabilities rather than technical layers, such as data access or messaging. Doing so requires that you shift your focus away from the technical stack and model your applications based upon the various domains that exist within the business.

    Domain-driven design (DDD) is a way to design software by focusing on the business needs. You can use Domain-driven design as a framework that guides the development of well-designed microservices by building services that encapsulate knowledge in each domain and abstract that knowledge from clients.

    In Domain-driven design you start by modeling the business domain and creating a domain model. A domain model is an abstract model of the business model that distills and organizes a domain of knowledge and provides a common language for developers and domain experts. It’s the resulting domain model that microservices a best suited to be built around because it helps establish a well-defined boundary between external systems and other internal applications.

    In short, before you begin designing microservices, start by mapping the functions of the business and their connections to create a domain model for the microservice(s) to be built around.

    What challenges do Microservices introduce?

    Microservices solve a lot of problems and have several advantages, but the grass isn’t always greener on the other side.

    One of the key challenges of microservices is managing communication between services. Because services are independent, they need to communicate with each other through APIs. This can be complex and difficult to manage, especially as the number of services grows. To address this challenge, it is important to have a clear API design, with well-defined inputs and outputs for each service. It is also important to have a system for managing and monitoring communication between services, to ensure that everything is running smoothly.

    Another challenge of microservices is managing the deployment and scaling of services. Because each service is independent, it needs to be deployed and scaled separately from the rest of the application. This can be complex and difficult to manage, especially as the number of services grows. To address this challenge, it is important to have a clear and consistent deployment process, with well-defined steps for deploying and scaling each service. Furthermore, it is advisable to host them on a system with self-healing capabilities to reduce operational burden.

    It is also important to have a system for monitoring and managing the deployment and scaling of services, to ensure optimal performance.

    Each of these challenges has created fertile ground for tooling and process that exists in the cloud-native ecosystem. Kubernetes, CI CD, and other DevOps practices are part of the package of adopting the microservices architecture.

    Conclusion

    In summary, microservices architecture focuses on software applications as a collection of small, independent services that communicate with each other over well-defined APIs.

    The main advantages of microservices include:

    • increased flexibility and scalability per microservice,
    • efficient resource utilization (with help from a container orchestrator like Kubernetes),
    • and faster development cycles.

    Continue following along with this series to see how you can use Kubernetes to help adopt microservices patterns in your own environments!

    Resources

    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/nginx-ingress-controller/index.html b/cnny-2023/tags/nginx-ingress-controller/index.html index c47023126e..b866eefa5f 100644 --- a/cnny-2023/tags/nginx-ingress-controller/index.html +++ b/cnny-2023/tags/nginx-ingress-controller/index.html @@ -14,13 +14,13 @@ - - + +

    One post tagged with "nginx-ingress-controller"

    View All Tags

    · 10 min read
    Paul Yu

    Welcome to Day 3 of Week 3 of #CloudNativeNewYear!

    The theme for this week is Bringing Your Application to Kubernetes. Yesterday we added configuration, secrets, and storage to our app. Today we'll explore how to expose the eShopOnWeb app so that customers can reach it over the internet using a custom domain name and TLS.

    Ask the Experts Thursday, February 9th at 9 AM PST
    Friday, February 10th at 11 AM PST

    Watch the recorded demo and conversation about this week's topics.

    We were live on YouTube walking through today's (and the rest of this week's) demos. Join us Friday, February 10th and bring your questions!

    What We'll Cover

    • Gather requirements
    • Generate TLS certificate and store in Azure Key Vault
    • Implement custom DNS using Azure DNS
    • Enable Web Application Routing add-on for AKS
    • Implement Ingress for the web application
    • Conclusion
    • Resources

    Gather requirements

    Currently, our eShopOnWeb app has three Kubernetes services deployed:

    1. db exposed internally via ClusterIP
    2. api exposed externally via LoadBalancer
    3. web exposed externally via LoadBalancer

    As mentioned in my post last week, Services allow applications to communicate with each other using DNS names. Kubernetes has service discovery capabilities built-in that allows Pods to resolve Services simply by using their names.

    In the case of our api and web deployments, they can simply reach the database by calling its name. The service type of ClusterIP for the db can remain as-is since it only needs to be accessed by the api and web apps.

    On the other hand, api and web both need to be accessed over the public internet. Currently, these services are using service type LoadBalancer which tells AKS to provision an Azure Load Balancer with a public IP address. No one is going to remember the IP addresses, so we need to make the app more accessible by adding a custom domain name and securing it with a TLS certificate.

    Here's what we're going to need:

    • Custom domain name for our app
    • TLS certificate for the custom domain name
    • Routing rule to ensure requests with /api/ in the URL is routed to the backend REST API
    • Routing rule to ensure requests without /api/ in the URL is routing to the web UI

    Just like last week, we will use the Web Application Routing add-on for AKS. But this time, we'll integrate it with Azure DNS and Azure Key Vault to satisfy all of our requirements above.

    info

    At the time of this writing the add-on is still in Public Preview

    Generate TLS certificate and store in Azure Key Vault

    We deployed an Azure Key Vault yesterday to store secrets. We'll use it again to store a TLS certificate too.

    Let's create and export a self-signed certificate for the custom domain.

    DNS_NAME=eshoponweb$RANDOM.com
    openssl req -new -x509 -nodes -out web-tls.crt -keyout web-tls.key -subj "/CN=${DNS_NAME}" -addext "subjectAltName=DNS:${DNS_NAME}"
    openssl pkcs12 -export -in web-tls.crt -inkey web-tls.key -out web-tls.pfx -password pass:
    info

    For learning purposes we'll use a self-signed certificate and a fake custom domain name.

    To browse to the site using the fake domain, we'll mimic a DNS lookup by adding an entry to your host file which maps the public IP address assigned to the ingress controller to the custom domain.

    In a production scenario, you will need to have a real domain delegated to Azure DNS and a valid TLS certificate for the domain.

    Grab your Azure Key Vault name and set the value in a variable for later use.

    RESOURCE_GROUP=cnny-week3

    AKV_NAME=$(az resource list \
    --resource-group $RESOURCE_GROUP \
    --resource-type Microsoft.KeyVault/vaults \
    --query "[0].name" -o tsv)

    Grant yourself permissions to get, list, and import certificates.

    MY_USER_NAME=$(az account show --query user.name -o tsv)
    MY_USER_OBJECT_ID=$(az ad user show --id $MY_USER_NAME --query id -o tsv)

    az keyvault set-policy \
    --name $AKV_NAME \
    --object-id $MY_USER_OBJECT_ID \
    --certificate-permissions get list import

    Upload the TLS certificate to Azure Key Vault and grab its certificate URI.

    WEB_TLS_CERT_ID=$(az keyvault certificate import \
    --vault-name $AKV_NAME \
    --name web-tls \
    --file web-tls.pfx \
    --query id \
    --output tsv)

    Implement custom DNS with Azure DNS

    Create a custom domain for our application and grab its Azure resource id.

    DNS_ZONE_ID=$(az network dns zone create \
    --name $DNS_NAME \
    --resource-group $RESOURCE_GROUP \
    --query id \
    --output tsv)

    Enable Web Application Routing add-on for AKS

    As we enable the Web Application Routing add-on, we'll also pass in the Azure DNS Zone resource id which triggers the installation of the external-dns controller in your Kubernetes cluster. This controller will be able to write Azure DNS zone entries on your behalf as you deploy Ingress manifests.

    AKS_NAME=$(az resource list \
    --resource-group $RESOURCE_GROUP \
    --resource-type Microsoft.ContainerService/managedClusters \
    --query "[0].name" -o tsv)

    az aks enable-addons \
    --name $AKS_NAME \
    --resource-group $RESOURCE_GROUP \
    --addons web_application_routing \
    --dns-zone-resource-id=$DNS_ZONE_ID \
    --enable-secret-rotation

    The add-on will also deploy a new Azure Managed Identity which is used by the external-dns controller when writing Azure DNS zone entries. Currently, it does not have permission to do that, so let's grant it permission.

    # This is where resources are automatically deployed by AKS
    NODE_RESOURCE_GROUP=$(az aks show \
    --name $AKS_NAME \
    --resource-group $RESOURCE_GROUP \
    --query nodeResourceGroup -o tsv)

    # This is the managed identity created by the Web Application Routing add-on
    MANAGED_IDENTTIY_OBJECT_ID=$(az resource show \
    --name webapprouting-${AKS_NAME} \
    --resource-group $NODE_RESOURCE_GROUP \
    --resource-type Microsoft.ManagedIdentity/userAssignedIdentities \
    --query properties.principalId \
    --output tsv)

    # Grant the managed identity permissions to write DNS entries
    az role assignment create \
    --role "DNS Zone Contributor" \
    --assignee $MANAGED_IDENTTIY_OBJECT_ID \
    --scope $DNS_ZONE_ID

    The Azure Managed Identity will also be used to retrieve and rotate TLS certificates from Azure Key Vault. So we'll need to grant it permission for that too.

    az keyvault set-policy \
    --name $AKV_NAME \
    --object-id $MANAGED_IDENTTIY_OBJECT_ID \
    --secret-permissions get \
    --certificate-permissions get

    Implement Ingress for the web application

    Before we create a new Ingress manifest, let's update the existing services to use ClusterIP instead of LoadBalancer. With an Ingress in place, there is no reason why we need the Service resources to be accessible from outside the cluster. The new Ingress will be the only entrypoint for external users.

    We can use the kubectl patch command to update the services

    kubectl patch service api -p '{"spec": {"type": "ClusterIP"}}'
    kubectl patch service web -p '{"spec": {"type": "ClusterIP"}}'

    Deploy a new Ingress to place in front of the web Service. Notice there is a special annotations entry for kubernetes.azure.com/tls-cert-keyvault-uri which points back to our self-signed certificate that was uploaded to Azure Key Vault.

    kubectl apply -f - <<EOF
    apiVersion: networking.k8s.io/v1
    kind: Ingress
    metadata:
    annotations:
    kubernetes.azure.com/tls-cert-keyvault-uri: ${WEB_TLS_CERT_ID}
    name: web
    spec:
    ingressClassName: webapprouting.kubernetes.azure.com
    rules:
    - host: ${DNS_NAME}
    http:
    paths:
    - backend:
    service:
    name: web
    port:
    number: 80
    path: /
    pathType: Prefix
    - backend:
    service:
    name: api
    port:
    number: 80
    path: /api
    pathType: Prefix
    tls:
    - hosts:
    - ${DNS_NAME}
    secretName: web-tls
    EOF

    In our manifest above, we've also configured the Ingress route the traffic to either the web or api services based on the URL path requested. If the request URL includes /api/ then it will send traffic to the api backend service. Otherwise, it will send traffic to the web service.

    Within a few minutes, the external-dns controller will add an A record to Azure DNS which points to the Ingress resource's public IP. With the custom domain in place, we can simply browse using this domain name.

    info

    As mentioned above, since this is not a real domain name, we need to modify our host file to make it seem like our custom domain is resolving to the Ingress' public IP address.

    To get the ingress public IP, run the following:

    # Get the IP
    kubectl get ingress web -o jsonpath="{.status.loadBalancer.ingress[0].ip}"

    # Get the hostname
    kubectl get ingress web -o jsonpath="{.spec.tls[0].hosts[0]}"

    Next, open your host file and add an entry using the format <YOUR_PUBLIC_IP> <YOUR_CUSTOM_DOMAIN>. Below is an example of what it should look like.

    20.237.116.224 eshoponweb11265.com

    See this doc for more info on how to do this.

    When browsing to the website, you may be presented with a warning about the connection not being private. This is due to the fact that we are using a self-signed certificate. This is expected, so go ahead and proceed anyway to load up the page.

    Why is the Admin page broken?

    If you log in using the admin@microsoft.com account and browse to the Admin page, you'll notice no products are loaded on the page.

    This is because the admin page is built using Blazor and compiled as a WebAssembly application that runs in your browser. When the application was compiled, it packed the appsettings.Development.json file as an embedded resource. This file contains the base URL for the public API and it currently points to https://localhost:5099. Now that we have a domain name, we can update the base URL and point it to our custom domain.

    From the root of the eShopOnWeb repo, update the configuration file using a sed command.

    sed -i -e "s/localhost:5099/${DNS_NAME}/g" ./src/BlazorAdmin/wwwroot/appsettings.Development.json

    Rebuild and push the container to Azure Container Registry.

    # Grab the name of your Azure Container Registry
    ACR_NAME=$(az resource list \
    --resource-group $RESOURCE_GROUP \
    --resource-type Microsoft.ContainerRegistry/registries \
    --query "[0].name" -o tsv)

    # Invoke a build and publish job
    az acr build \
    --registry $ACR_NAME \
    --image $ACR_NAME.azurecr.io/web:v0.1.0 \
    --file ./src/Web/Dockerfile .

    Once the container build has completed, we can issue a kubectl patch command to quickly update the web deployment to test our change.

    kubectl patch deployment web -p "$(cat <<EOF
    {
    "spec": {
    "template": {
    "spec": {
    "containers": [
    {
    "name": "web",
    "image": "${ACR_NAME}.azurecr.io/web:v0.1.0"
    }
    ]
    }
    }
    }
    }
    EOF
    )"

    If all went well, you will be able to browse the admin page again and confirm product data is being loaded 🥳

    Conclusion

    The Web Application Routing add-on for AKS aims to streamline the process of exposing it to the public using the open-source NGINX Ingress Controller. With the add-on being managed by Azure, it natively integrates with other Azure services like Azure DNS and eliminates the need to manually create DNS entries. It can also integrate with Azure Key Vault to automatically pull in TLS certificates and rotate them as needed to further reduce operational overhead.

    We are one step closer to production and in the upcoming posts we'll further operationalize and secure our deployment, so stay tuned!

    In the meantime, check out the resources listed below for further reading.

    You can also find manifests with all the changes made in today's post in the Azure-Samples/eShopOnAKS repository.

    Resources

    Take the Cloud Skills Challenge!

    Enroll in the Cloud Skills Challenge!

    Don't miss out on this opportunity to level up your skills and stay ahead of the curve in the world of cloud native.

    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/notary/index.html b/cnny-2023/tags/notary/index.html index 04727d33ad..68696edb0d 100644 --- a/cnny-2023/tags/notary/index.html +++ b/cnny-2023/tags/notary/index.html @@ -14,13 +14,13 @@ - - + +

    One post tagged with "notary"

    View All Tags

    · 6 min read
    Josh Duffney

    Welcome to Day 5 of Week 3 of #CloudNativeNewYear!

    The theme for this week is Bringing Your Application to Kubernetes. Yesterday we talked about debugging and instrumenting our application. Today we'll explore the topic of container image signing and secure supply chain.

    Ask the Experts Thursday, February 9th at 9 AM PST
    Friday, February 10th at 11 AM PST

    Watch the recorded demo and conversation about this week's topics.

    We were live on YouTube walking through today's (and the rest of this week's) demos. Join us Friday, February 10th and bring your questions!

    What We'll Cover

    • Introduction
    • Prerequisites
    • Create a digital signing certificate
    • Generate an Azure Container Registry Token
    • Set up Notation
    • Install the Notation Azure Key Vault Plugin
    • Add the signing Certificate to Notation
    • Sign Container Images
    • Conclusion

    Introduction

    The secure supply chain is a crucial aspect of software development, delivery, and deployment, and digital signing plays a critical role in this process.

    By using digital signatures to verify the authenticity and integrity of container images, organizations can improve the security of your software supply chain and reduce the risk of security breaches and data compromise.

    In this article, you'll learn how to use Notary, an open-source project hosted by the Cloud Native Computing Foundation (CNCF) to digitally sign container images stored on Azure Container Registry.

    Prerequisites

    To follow along, you'll need an instance of:

    Create a digital signing certificate

    A digital signing certificate is a certificate that is used to digitally sign and verify the authenticity and integrity of digital artifacts. Such documents, software, and of course container images.

    Before you can implement digital signatures, you must first create a digital signing certificate.

    Run the following command to generate the certificate:

    1. Create the policy file

      cat <<EOF > ./my_policy.json
      {
      "issuerParameters": {
      "certificateTransparency": null,
      "name": "Self"
      },
      "x509CertificateProperties": {
      "ekus": [
      "1.3.6.1.5.5.7.3.3"
      ],
      "key_usage": [
      "digitalSignature"
      ],
      "subject": "CN=${keySubjectName}",
      "validityInMonths": 12
      }
      }
      EOF

      The ekus and key usage of this certificate policy dictate that the certificate can only be used for digital signatures.

    2. Create the certificate in Azure Key Vault

      az keyvault certificate create --name $keyName --vault-name $keyVaultName --policy @my_policy.json

      Replace $keyName and $keyVaultName with your desired certificate name and Azure Key Vault instance name.

    Generate a Azure Container Registry token

    Azure Container Registry tokens are used to grant access to the contents of the registry. Tokens can be used for a variety of things such as pulling images, pushing images, or managing the registry.

    As part of the container image signing workflow, you'll need a token to authenticate the Notation CLI with your Azure Container Registry.

    Run the following command to generate an ACR token:

    az acr token create \
    --name $tokenName \
    --registry $registry \
    --scope-map _repositories_admin \
    --query 'credentials.passwords[0].value' \
    --only-show-errors \
    --output tsv

    Replace $tokenName with your name for the ACR token and $registry with the name of your ACR instance.

    Setup Notation

    Notation is the command-line interface for the CNCF Notary project. You'll use it to digitally sign the api and web container images for the eShopOnWeb application.

    Run the following commands to download and install the NotationCli:

    1. Open a terminal or command prompt window

    2. Download the Notary notation release

      curl -Lo notation.tar.gz https://github.com/notaryproject/notation/releases/download/v1.0.0-rc.1/notation_1.0.0-rc.1_linux_amd64.tar.gz > /dev/null 2>&1

      If you're not using Linux, you can find the releases here.

    3. Extract the contents of the notation.tar.gz

      tar xvzf notation.tar.gz > /dev/null 2>&1
    4. Copy the notation binary to the $HOME/bin directory

      cp ./notation $HOME/bin
    5. Add the $HOME/bin directory to the PATH environment variable

      export PATH="$HOME/bin:$PATH"
    6. Remove the downloaded files

      rm notation.tar.gz LICENSE
    7. Check the notation version

      notation --version

    Install the Notation Azure Key Vault plugin

    By design the NotationCli supports plugins that extend its digital signing capabilities to remote registries. And in order to sign your container images stored in Azure Container Registry, you'll need to install the Azure Key Vault plugin for Notation.

    Run the following commands to install the azure-kv plugin:

    1. Download the plugin

      curl -Lo notation-azure-kv.tar.gz \
      https://github.com/Azure/notation-azure-kv/releases/download/v0.5.0-rc.1/notation-azure-kv_0.5.0-rc.1_linux_amd64.tar.gz > /dev/null 2>&1

      Non-Linux releases can be found here.

    2. Extract to the plugin directory & delete download files

      tar xvzf notation-azure-kv.tar.gz -C ~/.config/notation/plugins/azure-kv notation-azure-kv > /dev/null 2>&

      rm -rf notation-azure-kv.tar.gz
    3. Verify the plugin was installed

      notation plugin ls

    Add the signing certificate to Notation

    Now that you have Notation and the Azure Key Vault plugin installed, add the certificate's keyId created above to Notation.

    1. Get the Certificate Key ID from Azure Key Vault

      az keyvault certificate show \
      --vault-name $keyVaultName \
      --name $keyName \
      --query "kid" --only-show-errors --output tsv

      Replace $keyVaultName and $keyName with the appropriate information.

    2. Add the Key ID to KMS using Notation

      notation key add --plugin azure-kv --id $keyID $keyName
    3. Check the key list

      notation key ls

    Sign Container Images

    At this point, all that's left is to sign the container images.

    Run the notation sign command to sign the api and web container images:

    notation sign $registry.azurecr.io/web:$tag \
    --username $tokenName \
    --password $tokenPassword

    notation sign $registry.azurecr.io/api:$tag \
    --username $tokenName \
    --password $tokenPassword

    Replace $registry, $tag, $tokenName, and $tokenPassword with the appropriate values. To improve security, use a SHA hash for the tag.

    NOTE: If you didn't take note of the token password, you can rerun the az acr token create command to generate a new password.

    Conclusion

    Digital signing plays a critical role in ensuring the security of software supply chains.

    By signing software components, organizations can verify the authenticity and integrity of software, helping to prevent unauthorized modifications, tampering, and malware.

    And if you want to take digital signing to a whole new level by using them to prevent the deployment of unsigned container images, check out the Ratify project on GitHub!

    Take the Cloud Skills Challenge!

    Enroll in the Cloud Skills Challenge!

    Don't miss out on this opportunity to level up your skills and stay ahead of the curve in the world of cloud native.

    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/notation/index.html b/cnny-2023/tags/notation/index.html index 5aa7769490..6634f4124e 100644 --- a/cnny-2023/tags/notation/index.html +++ b/cnny-2023/tags/notation/index.html @@ -14,13 +14,13 @@ - - + +

    One post tagged with "notation"

    View All Tags

    · 6 min read
    Josh Duffney

    Welcome to Day 5 of Week 3 of #CloudNativeNewYear!

    The theme for this week is Bringing Your Application to Kubernetes. Yesterday we talked about debugging and instrumenting our application. Today we'll explore the topic of container image signing and secure supply chain.

    Ask the Experts Thursday, February 9th at 9 AM PST
    Friday, February 10th at 11 AM PST

    Watch the recorded demo and conversation about this week's topics.

    We were live on YouTube walking through today's (and the rest of this week's) demos. Join us Friday, February 10th and bring your questions!

    What We'll Cover

    • Introduction
    • Prerequisites
    • Create a digital signing certificate
    • Generate an Azure Container Registry Token
    • Set up Notation
    • Install the Notation Azure Key Vault Plugin
    • Add the signing Certificate to Notation
    • Sign Container Images
    • Conclusion

    Introduction

    The secure supply chain is a crucial aspect of software development, delivery, and deployment, and digital signing plays a critical role in this process.

    By using digital signatures to verify the authenticity and integrity of container images, organizations can improve the security of your software supply chain and reduce the risk of security breaches and data compromise.

    In this article, you'll learn how to use Notary, an open-source project hosted by the Cloud Native Computing Foundation (CNCF) to digitally sign container images stored on Azure Container Registry.

    Prerequisites

    To follow along, you'll need an instance of:

    Create a digital signing certificate

    A digital signing certificate is a certificate that is used to digitally sign and verify the authenticity and integrity of digital artifacts. Such documents, software, and of course container images.

    Before you can implement digital signatures, you must first create a digital signing certificate.

    Run the following command to generate the certificate:

    1. Create the policy file

      cat <<EOF > ./my_policy.json
      {
      "issuerParameters": {
      "certificateTransparency": null,
      "name": "Self"
      },
      "x509CertificateProperties": {
      "ekus": [
      "1.3.6.1.5.5.7.3.3"
      ],
      "key_usage": [
      "digitalSignature"
      ],
      "subject": "CN=${keySubjectName}",
      "validityInMonths": 12
      }
      }
      EOF

      The ekus and key usage of this certificate policy dictate that the certificate can only be used for digital signatures.

    2. Create the certificate in Azure Key Vault

      az keyvault certificate create --name $keyName --vault-name $keyVaultName --policy @my_policy.json

      Replace $keyName and $keyVaultName with your desired certificate name and Azure Key Vault instance name.

    Generate a Azure Container Registry token

    Azure Container Registry tokens are used to grant access to the contents of the registry. Tokens can be used for a variety of things such as pulling images, pushing images, or managing the registry.

    As part of the container image signing workflow, you'll need a token to authenticate the Notation CLI with your Azure Container Registry.

    Run the following command to generate an ACR token:

    az acr token create \
    --name $tokenName \
    --registry $registry \
    --scope-map _repositories_admin \
    --query 'credentials.passwords[0].value' \
    --only-show-errors \
    --output tsv

    Replace $tokenName with your name for the ACR token and $registry with the name of your ACR instance.

    Setup Notation

    Notation is the command-line interface for the CNCF Notary project. You'll use it to digitally sign the api and web container images for the eShopOnWeb application.

    Run the following commands to download and install the NotationCli:

    1. Open a terminal or command prompt window

    2. Download the Notary notation release

      curl -Lo notation.tar.gz https://github.com/notaryproject/notation/releases/download/v1.0.0-rc.1/notation_1.0.0-rc.1_linux_amd64.tar.gz > /dev/null 2>&1

      If you're not using Linux, you can find the releases here.

    3. Extract the contents of the notation.tar.gz

      tar xvzf notation.tar.gz > /dev/null 2>&1
    4. Copy the notation binary to the $HOME/bin directory

      cp ./notation $HOME/bin
    5. Add the $HOME/bin directory to the PATH environment variable

      export PATH="$HOME/bin:$PATH"
    6. Remove the downloaded files

      rm notation.tar.gz LICENSE
    7. Check the notation version

      notation --version

    Install the Notation Azure Key Vault plugin

    By design the NotationCli supports plugins that extend its digital signing capabilities to remote registries. And in order to sign your container images stored in Azure Container Registry, you'll need to install the Azure Key Vault plugin for Notation.

    Run the following commands to install the azure-kv plugin:

    1. Download the plugin

      curl -Lo notation-azure-kv.tar.gz \
      https://github.com/Azure/notation-azure-kv/releases/download/v0.5.0-rc.1/notation-azure-kv_0.5.0-rc.1_linux_amd64.tar.gz > /dev/null 2>&1

      Non-Linux releases can be found here.

    2. Extract to the plugin directory & delete download files

      tar xvzf notation-azure-kv.tar.gz -C ~/.config/notation/plugins/azure-kv notation-azure-kv > /dev/null 2>&

      rm -rf notation-azure-kv.tar.gz
    3. Verify the plugin was installed

      notation plugin ls

    Add the signing certificate to Notation

    Now that you have Notation and the Azure Key Vault plugin installed, add the certificate's keyId created above to Notation.

    1. Get the Certificate Key ID from Azure Key Vault

      az keyvault certificate show \
      --vault-name $keyVaultName \
      --name $keyName \
      --query "kid" --only-show-errors --output tsv

      Replace $keyVaultName and $keyName with the appropriate information.

    2. Add the Key ID to KMS using Notation

      notation key add --plugin azure-kv --id $keyID $keyName
    3. Check the key list

      notation key ls

    Sign Container Images

    At this point, all that's left is to sign the container images.

    Run the notation sign command to sign the api and web container images:

    notation sign $registry.azurecr.io/web:$tag \
    --username $tokenName \
    --password $tokenPassword

    notation sign $registry.azurecr.io/api:$tag \
    --username $tokenName \
    --password $tokenPassword

    Replace $registry, $tag, $tokenName, and $tokenPassword with the appropriate values. To improve security, use a SHA hash for the tag.

    NOTE: If you didn't take note of the token password, you can rerun the az acr token create command to generate a new password.

    Conclusion

    Digital signing plays a critical role in ensuring the security of software supply chains.

    By signing software components, organizations can verify the authenticity and integrity of software, helping to prevent unauthorized modifications, tampering, and malware.

    And if you want to take digital signing to a whole new level by using them to prevent the deployment of unsigned container images, check out the Ratify project on GitHub!

    Take the Cloud Skills Challenge!

    Enroll in the Cloud Skills Challenge!

    Don't miss out on this opportunity to level up your skills and stay ahead of the curve in the world of cloud native.

    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/persistent-storage/index.html b/cnny-2023/tags/persistent-storage/index.html index ac05bd7b82..2fcb8e2f5a 100644 --- a/cnny-2023/tags/persistent-storage/index.html +++ b/cnny-2023/tags/persistent-storage/index.html @@ -14,13 +14,13 @@ - - + +

    One post tagged with "persistent-storage"

    View All Tags

    · 12 min read
    Paul Yu

    Welcome to Day 2 of Week 3 of #CloudNativeNewYear!

    The theme for this week is Bringing Your Application to Kubernetes. Yesterday we talked about getting an existing application running in Kubernetes with a full pipeline in GitHub Actions. Today we'll evaluate our sample application's configuration, storage, and networking requirements and implement using Kubernetes and Azure resources.

    Ask the Experts Thursday, February 9th at 9 AM PST
    Friday, February 10th at 11 AM PST

    Watch the recorded demo and conversation about this week's topics.

    We were live on YouTube walking through today's (and the rest of this week's) demos. Join us Friday, February 10th and bring your questions!

    What We'll Cover

    • Gather requirements
    • Implement environment variables using ConfigMaps
    • Implement persistent volumes using Azure Files
    • Implement secrets using Azure Key Vault
    • Re-package deployments
    • Conclusion
    • Resources
    caution

    Before you begin, make sure you've gone through yesterday's post to set up your AKS cluster.

    Gather requirements

    The eShopOnWeb application is written in .NET 7 and has two major pieces of functionality. The web UI is where customers can browse and shop. The web UI also includes an admin portal for managing the product catalog. This admin portal, is packaged as a WebAssembly application and relies on a separate REST API service. Both the web UI and the REST API connect to the same SQL Server container.

    Looking through the source code which can be found here we can identify requirements for configs, persistent storage, and secrets.

    Database server

    • Need to store the password for the sa account as a secure secret
    • Need persistent storage volume for data directory
    • Need to inject environment variables for SQL Server license type and EULA acceptance

    Web UI and REST API service

    • Need to store database connection string as a secure secret
    • Need to inject ASP.NET environment variables to override app settings
    • Need persistent storage volume for ASP.NET key storage

    Implement environment variables using ConfigMaps

    ConfigMaps are relatively straight-forward to create. If you were following along with the examples last week, this should be review 😉

    Create a ConfigMap to store database environment variables.

    kubectl apply -f - <<EOF
    apiVersion: v1
    kind: ConfigMap
    metadata:
    name: mssql-settings
    data:
    MSSQL_PID: Developer
    ACCEPT_EULA: "Y"
    EOF

    Create another ConfigMap to store ASP.NET environment variables.

    kubectl apply -f - <<EOF
    apiVersion: v1
    kind: ConfigMap
    metadata:
    name: aspnet-settings
    data:
    ASPNETCORE_ENVIRONMENT: Development
    EOF

    Implement persistent volumes using Azure Files

    Similar to last week, we'll take advantage of storage classes built into AKS. For our SQL Server data, we'll use the azurefile-csi-premium storage class and leverage an Azure Files resource as our PersistentVolume.

    Create a PersistentVolumeClaim (PVC) for persisting SQL Server data.

    kubectl apply -f - <<EOF
    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
    name: mssql-data
    spec:
    accessModes:
    - ReadWriteMany
    storageClassName: azurefile-csi-premium
    resources:
    requests:
    storage: 5Gi
    EOF

    Create another PVC for persisting ASP.NET data.

    kubectl apply -f - <<EOF
    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
    name: aspnet-data
    spec:
    accessModes:
    - ReadWriteMany
    storageClassName: azurefile-csi-premium
    resources:
    requests:
    storage: 5Gi
    EOF

    Implement secrets using Azure Key Vault

    It's a well known fact that Kubernetes secretes are not really secrets. They're just base64-encoded values and not secure, especially if malicious users have access to your Kubernetes cluster.

    In a production scenario, you will want to leverage an external vault like Azure Key Vault or HashiCorp Vault to encrypt and store secrets.

    With AKS, we can enable the Secrets Store CSI driver add-on which will allow us to leverage Azure Key Vault.

    # Set some variables
    RG_NAME=<YOUR_RESOURCE_GROUP_NAME>
    AKS_NAME=<YOUR_AKS_CLUSTER_NAME>
    ACR_NAME=<YOUR_ACR_NAME>

    az aks enable-addons \
    --addons azure-keyvault-secrets-provider \
    --name $AKS_NAME \
    --resource-group $RG_NAME

    With the add-on enabled, you should see aks-secrets-store-csi-driver and aks-secrets-store-provider-azure resources installed on each node in your Kubernetes cluster.

    Run the command below to verify.

    kubectl get pods \
    --namespace kube-system \
    --selector 'app in (secrets-store-csi-driver, secrets-store-provider-azure)'

    The Secrets Store CSI driver allows us to use secret stores via Container Storage Interface (CSI) volumes. This provider offers capabilities such as mounting and syncing between the secure vault and Kubernetes Secrets. On AKS, the Azure Key Vault Provider for Secrets Store CSI Driver enables integration with Azure Key Vault.

    You may not have an Azure Key Vault created yet, so let's create one and add some secrets to it.

    AKV_NAME=$(az keyvault create \
    --name akv-eshop$RANDOM \
    --resource-group $RG_NAME \
    --query name -o tsv)

    # Database server password
    az keyvault secret set \
    --vault-name $AKV_NAME \
    --name mssql-password \
    --value "@someThingComplicated1234"

    # Catalog database connection string
    az keyvault secret set \
    --vault-name $AKV_NAME \
    --name mssql-connection-catalog \
    --value "Server=db;Database=Microsoft.eShopOnWeb.CatalogDb;User Id=sa;Password=@someThingComplicated1234;TrustServerCertificate=True;"

    # Identity database connection string
    az keyvault secret set \
    --vault-name $AKV_NAME \
    --name mssql-connection-identity \
    --value "Server=db;Database=Microsoft.eShopOnWeb.Identity;User Id=sa;Password=@someThingComplicated1234;TrustServerCertificate=True;"

    Pods authentication using Azure Workload Identity

    In order for our Pods to retrieve secrets from Azure Key Vault, we'll need to set up a way for the Pod to authenticate against Azure AD. This can be achieved by implementing the new Azure Workload Identity feature of AKS.

    info

    At the time of this writing, the workload identity feature of AKS is in Preview.

    The workload identity feature within AKS allows us to leverage native Kubernetes resources and link a Kubernetes ServiceAccount to an Azure Managed Identity to authenticate against Azure AD.

    For the authentication flow, our Kubernetes cluster will act as an Open ID Connect (OIDC) issuer and will be able issue identity tokens to ServiceAccounts which will be assigned to our Pods.

    The Azure Managed Identity will be granted permission to access secrets in our Azure Key Vault and with the ServiceAccount being assigned to our Pods, they will be able to retrieve our secrets.

    For more information on how the authentication mechanism all works, check out this doc.

    To implement all this, start by enabling the new preview feature for AKS.

    az feature register \
    --namespace "Microsoft.ContainerService" \
    --name "EnableWorkloadIdentityPreview"
    caution

    This can take several minutes to complete.

    Check the status and ensure the state shows Regestered before moving forward.

    az feature show \
    --namespace "Microsoft.ContainerService" \
    --name "EnableWorkloadIdentityPreview"

    Update your AKS cluster to enable the workload identity feature and enable the OIDC issuer endpoint.

    az aks update \
    --name $AKS_NAME \
    --resource-group $RG_NAME \
    --enable-workload-identity \
    --enable-oidc-issuer

    Create an Azure Managed Identity and retrieve its client ID.

    MANAGED_IDENTITY_CLIENT_ID=$(az identity create \
    --name aks-workload-identity \
    --resource-group $RG_NAME \
    --subscription $(az account show --query id -o tsv) \
    --query 'clientId' -o tsv)

    Create the Kubernetes ServiceAccount.

    # Set namespace (this must align with the namespace that your app is deployed into)
    SERVICE_ACCOUNT_NAMESPACE=default

    # Set the service account name
    SERVICE_ACCOUNT_NAME=eshop-serviceaccount

    # Create the service account
    kubectl apply -f - <<EOF
    apiVersion: v1
    kind: ServiceAccount
    metadata:
    annotations:
    azure.workload.identity/client-id: ${MANAGED_IDENTITY_CLIENT_ID}
    labels:
    azure.workload.identity/use: "true"
    name: ${SERVICE_ACCOUNT_NAME}
    namespace: ${SERVICE_ACCOUNT_NAMESPACE}
    EOF
    info

    Note to enable this ServiceAccount to work with Azure Workload Identity, you must annotate the resource with azure.workload.identity/client-id, and add a label of azure.workload.identity/use: "true"

    That was a lot... Let's review what we just did.

    We have an Azure Managed Identity (object in Azure AD), an OIDC issuer URL (endpoint in our Kubernetes cluster), and a Kubernetes ServiceAccount.

    The next step is to "tie" these components together and establish a Federated Identity Credential so that Azure AD can trust authentication requests from your Kubernetes cluster.

    info

    This identity federation can be established between Azure AD any Kubernetes cluster; not just AKS 🤗

    To establish the federated credential, we'll need the OIDC issuer URL, and a subject which points to your Kubernetes ServiceAccount.

    # Get the OIDC issuer URL
    OIDC_ISSUER_URL=$(az aks show \
    --name $AKS_NAME \
    --resource-group $RG_NAME \
    --query "oidcIssuerProfile.issuerUrl" -o tsv)

    # Set the subject name using this format: `system:serviceaccount:<YOUR_SERVICE_ACCOUNT_NAMESPACE>:<YOUR_SERVICE_ACCOUNT_NAME>`
    SUBJECT=system:serviceaccount:$SERVICE_ACCOUNT_NAMESPACE:$SERVICE_ACCOUNT_NAME

    az identity federated-credential create \
    --name aks-federated-credential \
    --identity-name aks-workload-identity \
    --resource-group $RG_NAME \
    --issuer $OIDC_ISSUER_URL \
    --subject $SUBJECT

    With the authentication components set, we can now create a SecretProviderClass which includes details about the Azure Key Vault, the secrets to pull out from the vault, and identity used to access the vault.

    # Get the tenant id for the key vault
    TENANT_ID=$(az keyvault show \
    --name $AKV_NAME \
    --resource-group $RG_NAME \
    --query properties.tenantId -o tsv)

    # Create the secret provider for azure key vault
    kubectl apply -f - <<EOF
    apiVersion: secrets-store.csi.x-k8s.io/v1
    kind: SecretProviderClass
    metadata:
    name: eshop-azure-keyvault
    spec:
    provider: azure
    parameters:
    usePodIdentity: "false"
    useVMManagedIdentity: "false"
    clientID: "${MANAGED_IDENTITY_CLIENT_ID}"
    keyvaultName: "${AKV_NAME}"
    cloudName: ""
    objects: |
    array:
    - |
    objectName: mssql-password
    objectType: secret
    objectVersion: ""
    - |
    objectName: mssql-connection-catalog
    objectType: secret
    objectVersion: ""
    - |
    objectName: mssql-connection-identity
    objectType: secret
    objectVersion: ""
    tenantId: "${TENANT_ID}"
    secretObjects:
    - secretName: eshop-secrets
    type: Opaque
    data:
    - objectName: mssql-password
    key: mssql-password
    - objectName: mssql-connection-catalog
    key: mssql-connection-catalog
    - objectName: mssql-connection-identity
    key: mssql-connection-identity
    EOF

    Finally, lets grant the Azure Managed Identity permissions to retrieve secrets from the Azure Key Vault.

    az keyvault set-policy \
    --name $AKV_NAME \
    --secret-permissions get \
    --spn $MANAGED_IDENTITY_CLIENT_ID

    Re-package deployments

    Update your database deployment to load environment variables from our ConfigMap, attach the PVC and SecretProviderClass as volumes, mount the volumes into the Pod, and use the ServiceAccount to retrieve secrets.

    Additionally, you may notice the database Pod is set to use fsGroup:10001 as part of the securityContext. This is required as the MSSQL container runs using a non-root account called mssql and this account has the proper permissions to read/write data at the /var/opt/mssql mount path.

    kubectl apply -f - <<EOF
    apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: db
    labels:
    app: db
    spec:
    replicas: 1
    selector:
    matchLabels:
    app: db
    template:
    metadata:
    labels:
    app: db
    spec:
    securityContext:
    fsGroup: 10001
    serviceAccountName: ${SERVICE_ACCOUNT_NAME}
    containers:
    - name: db
    image: mcr.microsoft.com/mssql/server:2019-latest
    ports:
    - containerPort: 1433
    envFrom:
    - configMapRef:
    name: mssql-settings
    env:
    - name: MSSQL_SA_PASSWORD
    valueFrom:
    secretKeyRef:
    name: eshop-secrets
    key: mssql-password
    resources: {}
    volumeMounts:
    - name: mssqldb
    mountPath: /var/opt/mssql
    - name: eshop-secrets
    mountPath: "/mnt/secrets-store"
    readOnly: true
    volumes:
    - name: mssqldb
    persistentVolumeClaim:
    claimName: mssql-data
    - name: eshop-secrets
    csi:
    driver: secrets-store.csi.k8s.io
    readOnly: true
    volumeAttributes:
    secretProviderClass: eshop-azure-keyvault
    EOF

    We'll update the API and Web deployments in a similar way.

    # Set the image tag
    IMAGE_TAG=<YOUR_IMAGE_TAG>

    # API deployment
    kubectl apply -f - <<EOF
    apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: api
    labels:
    app: api
    spec:
    replicas: 1
    selector:
    matchLabels:
    app: api
    template:
    metadata:
    labels:
    app: api
    spec:
    serviceAccount: ${SERVICE_ACCOUNT_NAME}
    containers:
    - name: api
    image: ${ACR_NAME}.azurecr.io/api:${IMAGE_TAG}
    ports:
    - containerPort: 80
    envFrom:
    - configMapRef:
    name: aspnet-settings
    env:
    - name: ConnectionStrings__CatalogConnection
    valueFrom:
    secretKeyRef:
    name: eshop-secrets
    key: mssql-connection-catalog
    - name: ConnectionStrings__IdentityConnection
    valueFrom:
    secretKeyRef:
    name: eshop-secrets
    key: mssql-connection-identity
    resources: {}
    volumeMounts:
    - name: aspnet
    mountPath: ~/.aspnet/https:/root/.aspnet/https:ro
    - name: eshop-secrets
    mountPath: "/mnt/secrets-store"
    readOnly: true
    volumes:
    - name: aspnet
    persistentVolumeClaim:
    claimName: aspnet-data
    - name: eshop-secrets
    csi:
    driver: secrets-store.csi.k8s.io
    readOnly: true
    volumeAttributes:
    secretProviderClass: eshop-azure-keyvault
    EOF

    ## Web deployment
    kubectl apply -f - <<EOF
    apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: web
    labels:
    app: web
    spec:
    replicas: 1
    selector:
    matchLabels:
    app: web
    template:
    metadata:
    labels:
    app: web
    spec:
    serviceAccount: ${SERVICE_ACCOUNT_NAME}
    containers:
    - name: web
    image: ${ACR_NAME}.azurecr.io/web:${IMAGE_TAG}
    ports:
    - containerPort: 80
    envFrom:
    - configMapRef:
    name: aspnet-settings
    env:
    - name: ConnectionStrings__CatalogConnection
    valueFrom:
    secretKeyRef:
    name: eshop-secrets
    key: mssql-connection-catalog
    - name: ConnectionStrings__IdentityConnection
    valueFrom:
    secretKeyRef:
    name: eshop-secrets
    key: mssql-connection-identity
    resources: {}
    volumeMounts:
    - name: aspnet
    mountPath: ~/.aspnet/https:/root/.aspnet/https:ro
    - name: eshop-secrets
    mountPath: "/mnt/secrets-store"
    readOnly: true
    volumes:
    - name: aspnet
    persistentVolumeClaim:
    claimName: aspnet-data
    - name: eshop-secrets
    csi:
    driver: secrets-store.csi.k8s.io
    readOnly: true
    volumeAttributes:
    secretProviderClass: eshop-azure-keyvault
    EOF

    If all went well with your deployment updates, you should be able to browse to your website and buy some merchandise again 🥳

    echo "http://$(kubectl get service web -o jsonpath='{.status.loadBalancer.ingress[0].ip}')"

    Conclusion

    Although there is no visible changes on with our website, we've made a ton of changes on the Kubernetes backend to make this application much more secure and resilient.

    We used a combination of Kubernetes resources and AKS-specific features to achieve our goal of securing our secrets and ensuring data is not lost on container crashes and restarts.

    To learn more about the components we leveraged here today, checkout the resources and additional tutorials listed below.

    You can also find manifests with all the changes made in today's post in the Azure-Samples/eShopOnAKS repository.

    See you in the next post!

    Resources

    Take the Cloud Skills Challenge!

    Enroll in the Cloud Skills Challenge!

    Don't miss out on this opportunity to level up your skills and stay ahead of the curve in the world of cloud native.

    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/persistent-volume-claims/index.html b/cnny-2023/tags/persistent-volume-claims/index.html index 72d8b70e30..a309175f37 100644 --- a/cnny-2023/tags/persistent-volume-claims/index.html +++ b/cnny-2023/tags/persistent-volume-claims/index.html @@ -14,13 +14,13 @@ - - + +

    One post tagged with "persistent-volume-claims"

    View All Tags

    · 8 min read
    Paul Yu

    Welcome to Day 4 of Week 2 of #CloudNativeNewYear!

    The theme for this week is Kubernetes fundamentals. Yesterday we talked about how to set app configurations and secrets at runtime using Kubernetes ConfigMaps and Secrets. Today we'll explore the topic of persistent storage on Kubernetes and show you can leverage Persistent Volumes and Persistent Volume Claims to ensure your PostgreSQL data can survive container restarts.

    Ask the Experts Thursday, February 9th at 9 AM PST
    Catch the Replay of the Live Demo

    Watch the recorded demo and conversation about this week's topics.

    We were live on YouTube walking through today's (and the rest of this week's) demos.

    What We'll Cover

    • Containers are ephemeral
    • Persistent storage on Kubernetes
    • Persistent storage on AKS
    • Takeaways
    • Resources

    Containers are ephemeral

    In our sample application, the frontend UI writes vote values to a backend PostgreSQL database. By default the database container stores its data on the container's local file system, so there will be data loss when the pod is re-deployed or crashes as containers are meant to start with a clean slate each time.

    Let's re-deploy our sample app and experience the problem first hand.

    📝 NOTE: If you don't have an AKS cluster deployed, please head over to Azure-Samples/azure-voting-app-rust, clone the repo, and follow the instructions in the README.md to execute the Azure deployment and setup your kubectl context. Check out the first post this week for more on the environment setup.

    kubectl apply -f ./manifests

    Wait for the azure-voting-app service to be assigned a public IP then browse to the website and submit some votes. Use the command below to print the URL to the terminal.

    echo "http://$(kubectl get ingress azure-voting-app -o jsonpath='{.status.loadBalancer.ingress[0].ip}')"

    Now, let's delete the pods and watch Kubernetes do what it does best... that is, re-schedule pods.

    # wait for the pod to come up then ctrl+c to stop watching
    kubectl delete --all pod --wait=false && kubectl get po -w

    Once the pods have been recovered, reload the website and confirm the vote tally has been reset to zero.

    We need to fix this so that the data outlives the container.

    Persistent storage on Kubernetes

    In order for application data to survive crashes and restarts, you must implement Persistent Volumes and Persistent Volume Claims.

    A persistent volume represents storage that is available to the cluster. Storage volumes can be provisioned manually by an administrator or dynamically using Container Storage Interface (CSI) and storage classes, which includes information on how to provision CSI volumes.

    When a user needs to add persistent storage to their application, a persistent volume claim is made to allocate chunks of storage from the volume. This "claim" includes things like volume mode (e.g., file system or block storage), the amount of storage to allocate, the access mode, and optionally a storage class. Once a persistent volume claim has been deployed, users can add the volume to the pod and mount it in a container.

    In the next section, we'll demonstrate how to enable persistent storage on AKS.

    Persistent storage on AKS

    With AKS, CSI drivers and storage classes are pre-deployed into your cluster. This allows you to natively use Azure Disks, Azure Files, and Azure Blob Storage as persistent volumes. You can either bring your own Azure storage account and use it with AKS or have AKS provision an Azure storage account for you.

    To view the Storage CSI drivers that have been enabled in your AKS cluster, run the following command.

    az aks show \
    --name <YOUR_AKS_NAME> \
    --resource-group <YOUR_AKS_RESOURCE_GROUP> \
    --query storageProfile

    You should see output that looks like this.

    {
    "blobCsiDriver": null,
    "diskCsiDriver": {
    "enabled": true,
    "version": "v1"
    },
    "fileCsiDriver": {
    "enabled": true
    },
    "snapshotController": {
    "enabled": true
    }
    }

    To view the storage classes that have been installed in your cluster, run the following command.

    kubectl get storageclass

    Workload requirements will dictate which CSI driver and storage class you will need to use.

    If you need block storage, then you should use the blobCsiDriver. The driver may not be enabled by default but you can enable it by following instructions which can be found in the Resources section below.

    If you need file storage you should leverage either diskCsiDriver or fileCsiDriver. The decision between these two boils down to whether or not you need to have the underlying storage accessible by one pod or multiple pods. It is important to note that diskCsiDriver currently supports access from a single pod only. Therefore, if you need data to be accessible by multiple pods at the same time, then you should opt for fileCsiDriver.

    For our PostgreSQL deployment, we'll use the diskCsiDriver and have AKS create an Azure Disk resource for us. There is no need to create a PV resource, all we need to do to is create a PVC using the managed-csi-premium storage class.

    Run the following command to create the PVC.

    kubectl apply -f - <<EOF            
    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
    name: pvc-azuredisk
    spec:
    accessModes:
    - ReadWriteOnce
    resources:
    requests:
    storage: 10Gi
    storageClassName: managed-csi-premium
    EOF

    When you check the PVC resource, you'll notice the STATUS is set to Pending. It will be set to Bound once the volume is mounted in the PostgreSQL container.

    kubectl get persistentvolumeclaim

    Let's delete the azure-voting-db deployment.

    kubectl delete deploy azure-voting-db

    Next, we need to apply an updated deployment manifest which includes our PVC.

    kubectl apply -f - <<EOF
    apiVersion: apps/v1
    kind: Deployment
    metadata:
    creationTimestamp: null
    labels:
    app: azure-voting-db
    name: azure-voting-db
    spec:
    replicas: 1
    selector:
    matchLabels:
    app: azure-voting-db
    strategy: {}
    template:
    metadata:
    creationTimestamp: null
    labels:
    app: azure-voting-db
    spec:
    containers:
    - image: postgres:15.0-alpine
    name: postgres
    ports:
    - containerPort: 5432
    env:
    - name: POSTGRES_PASSWORD
    valueFrom:
    secretKeyRef:
    name: azure-voting-secret
    key: POSTGRES_PASSWORD
    resources: {}
    volumeMounts:
    - name: mypvc
    mountPath: "/var/lib/postgresql/data"
    subPath: "data"
    volumes:
    - name: mypvc
    persistentVolumeClaim:
    claimName: pvc-azuredisk
    EOF

    In the manifest above, you'll see that we are mounting a new volume called mypvc (the name can be whatever you want) in the pod which points to a PVC named pvc-azuredisk. With the volume in place, we can mount it in the container by referencing the name of the volume mypvc and setting the mount path to /var/lib/postgresql/data (which is the default path).

    💡 IMPORTANT: When mounting a volume into a non-empty subdirectory, you must add subPath to the volume mount and point it to a subdirectory in the volume rather than mounting at root. In our case, when Azure Disk is formatted, it leaves a lost+found directory as documented here.

    Watch the pods and wait for the STATUS to show Running and the pod's READY status shows 1/1.

    # wait for the pod to come up then ctrl+c to stop watching
    kubectl get po -w

    Verify that the STATUS of the PVC is now set to Bound

    kubectl get persistentvolumeclaim

    With the new database container running, let's restart the application pod, wait for the pod's READY status to show 1/1, then head back over to our web browser and submit a few votes.

    kubectl delete pod -lapp=azure-voting-app --wait=false && kubectl get po -lapp=azure-voting-app -w

    Now the moment of truth... let's rip out the pods again, wait for the pods to be re-scheduled, and confirm our vote counts remain in tact.

    kubectl delete --all pod --wait=false && kubectl get po -w

    If you navigate back to the website, you'll find the vote are still there 🎉

    Takeaways

    By design, containers are meant to be ephemeral and stateless workloads are ideal on Kubernetes. However, there will come a time when your data needs to outlive the container. To persist data in your Kubernetes workloads, you need to leverage PV, PVC, and optionally storage classes. In our demo scenario, we leveraged CSI drivers built into AKS and created a PVC using pre-installed storage classes. From there, we updated the database deployment to mount the PVC in the container and AKS did the rest of the work in provisioning the underlying Azure Disk. If the built-in storage classes does not fit your needs; for example, you need to change the ReclaimPolicy or change the SKU for the Azure resource, then you can create your own custom storage class and configure it just the way you need it 😊

    We'll revisit this topic again next week but in the meantime, check out some of the resources listed below to learn more.

    See you in the next post!

    Resources

    Take the Cloud Skills Challenge!

    Enroll in the Cloud Skills Challenge!

    Don't miss out on this opportunity to level up your skills and stay ahead of the curve in the world of cloud native.

    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/persistent-volumes/index.html b/cnny-2023/tags/persistent-volumes/index.html index 889787a76a..ede4e6e06d 100644 --- a/cnny-2023/tags/persistent-volumes/index.html +++ b/cnny-2023/tags/persistent-volumes/index.html @@ -14,13 +14,13 @@ - - + +

    One post tagged with "persistent-volumes"

    View All Tags

    · 8 min read
    Paul Yu

    Welcome to Day 4 of Week 2 of #CloudNativeNewYear!

    The theme for this week is Kubernetes fundamentals. Yesterday we talked about how to set app configurations and secrets at runtime using Kubernetes ConfigMaps and Secrets. Today we'll explore the topic of persistent storage on Kubernetes and show you can leverage Persistent Volumes and Persistent Volume Claims to ensure your PostgreSQL data can survive container restarts.

    Ask the Experts Thursday, February 9th at 9 AM PST
    Catch the Replay of the Live Demo

    Watch the recorded demo and conversation about this week's topics.

    We were live on YouTube walking through today's (and the rest of this week's) demos.

    What We'll Cover

    • Containers are ephemeral
    • Persistent storage on Kubernetes
    • Persistent storage on AKS
    • Takeaways
    • Resources

    Containers are ephemeral

    In our sample application, the frontend UI writes vote values to a backend PostgreSQL database. By default the database container stores its data on the container's local file system, so there will be data loss when the pod is re-deployed or crashes as containers are meant to start with a clean slate each time.

    Let's re-deploy our sample app and experience the problem first hand.

    📝 NOTE: If you don't have an AKS cluster deployed, please head over to Azure-Samples/azure-voting-app-rust, clone the repo, and follow the instructions in the README.md to execute the Azure deployment and setup your kubectl context. Check out the first post this week for more on the environment setup.

    kubectl apply -f ./manifests

    Wait for the azure-voting-app service to be assigned a public IP then browse to the website and submit some votes. Use the command below to print the URL to the terminal.

    echo "http://$(kubectl get ingress azure-voting-app -o jsonpath='{.status.loadBalancer.ingress[0].ip}')"

    Now, let's delete the pods and watch Kubernetes do what it does best... that is, re-schedule pods.

    # wait for the pod to come up then ctrl+c to stop watching
    kubectl delete --all pod --wait=false && kubectl get po -w

    Once the pods have been recovered, reload the website and confirm the vote tally has been reset to zero.

    We need to fix this so that the data outlives the container.

    Persistent storage on Kubernetes

    In order for application data to survive crashes and restarts, you must implement Persistent Volumes and Persistent Volume Claims.

    A persistent volume represents storage that is available to the cluster. Storage volumes can be provisioned manually by an administrator or dynamically using Container Storage Interface (CSI) and storage classes, which includes information on how to provision CSI volumes.

    When a user needs to add persistent storage to their application, a persistent volume claim is made to allocate chunks of storage from the volume. This "claim" includes things like volume mode (e.g., file system or block storage), the amount of storage to allocate, the access mode, and optionally a storage class. Once a persistent volume claim has been deployed, users can add the volume to the pod and mount it in a container.

    In the next section, we'll demonstrate how to enable persistent storage on AKS.

    Persistent storage on AKS

    With AKS, CSI drivers and storage classes are pre-deployed into your cluster. This allows you to natively use Azure Disks, Azure Files, and Azure Blob Storage as persistent volumes. You can either bring your own Azure storage account and use it with AKS or have AKS provision an Azure storage account for you.

    To view the Storage CSI drivers that have been enabled in your AKS cluster, run the following command.

    az aks show \
    --name <YOUR_AKS_NAME> \
    --resource-group <YOUR_AKS_RESOURCE_GROUP> \
    --query storageProfile

    You should see output that looks like this.

    {
    "blobCsiDriver": null,
    "diskCsiDriver": {
    "enabled": true,
    "version": "v1"
    },
    "fileCsiDriver": {
    "enabled": true
    },
    "snapshotController": {
    "enabled": true
    }
    }

    To view the storage classes that have been installed in your cluster, run the following command.

    kubectl get storageclass

    Workload requirements will dictate which CSI driver and storage class you will need to use.

    If you need block storage, then you should use the blobCsiDriver. The driver may not be enabled by default but you can enable it by following instructions which can be found in the Resources section below.

    If you need file storage you should leverage either diskCsiDriver or fileCsiDriver. The decision between these two boils down to whether or not you need to have the underlying storage accessible by one pod or multiple pods. It is important to note that diskCsiDriver currently supports access from a single pod only. Therefore, if you need data to be accessible by multiple pods at the same time, then you should opt for fileCsiDriver.

    For our PostgreSQL deployment, we'll use the diskCsiDriver and have AKS create an Azure Disk resource for us. There is no need to create a PV resource, all we need to do to is create a PVC using the managed-csi-premium storage class.

    Run the following command to create the PVC.

    kubectl apply -f - <<EOF            
    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
    name: pvc-azuredisk
    spec:
    accessModes:
    - ReadWriteOnce
    resources:
    requests:
    storage: 10Gi
    storageClassName: managed-csi-premium
    EOF

    When you check the PVC resource, you'll notice the STATUS is set to Pending. It will be set to Bound once the volume is mounted in the PostgreSQL container.

    kubectl get persistentvolumeclaim

    Let's delete the azure-voting-db deployment.

    kubectl delete deploy azure-voting-db

    Next, we need to apply an updated deployment manifest which includes our PVC.

    kubectl apply -f - <<EOF
    apiVersion: apps/v1
    kind: Deployment
    metadata:
    creationTimestamp: null
    labels:
    app: azure-voting-db
    name: azure-voting-db
    spec:
    replicas: 1
    selector:
    matchLabels:
    app: azure-voting-db
    strategy: {}
    template:
    metadata:
    creationTimestamp: null
    labels:
    app: azure-voting-db
    spec:
    containers:
    - image: postgres:15.0-alpine
    name: postgres
    ports:
    - containerPort: 5432
    env:
    - name: POSTGRES_PASSWORD
    valueFrom:
    secretKeyRef:
    name: azure-voting-secret
    key: POSTGRES_PASSWORD
    resources: {}
    volumeMounts:
    - name: mypvc
    mountPath: "/var/lib/postgresql/data"
    subPath: "data"
    volumes:
    - name: mypvc
    persistentVolumeClaim:
    claimName: pvc-azuredisk
    EOF

    In the manifest above, you'll see that we are mounting a new volume called mypvc (the name can be whatever you want) in the pod which points to a PVC named pvc-azuredisk. With the volume in place, we can mount it in the container by referencing the name of the volume mypvc and setting the mount path to /var/lib/postgresql/data (which is the default path).

    💡 IMPORTANT: When mounting a volume into a non-empty subdirectory, you must add subPath to the volume mount and point it to a subdirectory in the volume rather than mounting at root. In our case, when Azure Disk is formatted, it leaves a lost+found directory as documented here.

    Watch the pods and wait for the STATUS to show Running and the pod's READY status shows 1/1.

    # wait for the pod to come up then ctrl+c to stop watching
    kubectl get po -w

    Verify that the STATUS of the PVC is now set to Bound

    kubectl get persistentvolumeclaim

    With the new database container running, let's restart the application pod, wait for the pod's READY status to show 1/1, then head back over to our web browser and submit a few votes.

    kubectl delete pod -lapp=azure-voting-app --wait=false && kubectl get po -lapp=azure-voting-app -w

    Now the moment of truth... let's rip out the pods again, wait for the pods to be re-scheduled, and confirm our vote counts remain in tact.

    kubectl delete --all pod --wait=false && kubectl get po -w

    If you navigate back to the website, you'll find the vote are still there 🎉

    Takeaways

    By design, containers are meant to be ephemeral and stateless workloads are ideal on Kubernetes. However, there will come a time when your data needs to outlive the container. To persist data in your Kubernetes workloads, you need to leverage PV, PVC, and optionally storage classes. In our demo scenario, we leveraged CSI drivers built into AKS and created a PVC using pre-installed storage classes. From there, we updated the database deployment to mount the PVC in the container and AKS did the rest of the work in provisioning the underlying Azure Disk. If the built-in storage classes does not fit your needs; for example, you need to change the ReclaimPolicy or change the SKU for the Azure resource, then you can create your own custom storage class and configure it just the way you need it 😊

    We'll revisit this topic again next week but in the meantime, check out some of the resources listed below to learn more.

    See you in the next post!

    Resources

    Take the Cloud Skills Challenge!

    Enroll in the Cloud Skills Challenge!

    Don't miss out on this opportunity to level up your skills and stay ahead of the curve in the world of cloud native.

    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/secrets-management/index.html b/cnny-2023/tags/secrets-management/index.html index 68318cd092..40f7664c3f 100644 --- a/cnny-2023/tags/secrets-management/index.html +++ b/cnny-2023/tags/secrets-management/index.html @@ -14,13 +14,13 @@ - - + +

    One post tagged with "secrets-management"

    View All Tags

    · 12 min read
    Paul Yu

    Welcome to Day 2 of Week 3 of #CloudNativeNewYear!

    The theme for this week is Bringing Your Application to Kubernetes. Yesterday we talked about getting an existing application running in Kubernetes with a full pipeline in GitHub Actions. Today we'll evaluate our sample application's configuration, storage, and networking requirements and implement using Kubernetes and Azure resources.

    Ask the Experts Thursday, February 9th at 9 AM PST
    Friday, February 10th at 11 AM PST

    Watch the recorded demo and conversation about this week's topics.

    We were live on YouTube walking through today's (and the rest of this week's) demos. Join us Friday, February 10th and bring your questions!

    What We'll Cover

    • Gather requirements
    • Implement environment variables using ConfigMaps
    • Implement persistent volumes using Azure Files
    • Implement secrets using Azure Key Vault
    • Re-package deployments
    • Conclusion
    • Resources
    caution

    Before you begin, make sure you've gone through yesterday's post to set up your AKS cluster.

    Gather requirements

    The eShopOnWeb application is written in .NET 7 and has two major pieces of functionality. The web UI is where customers can browse and shop. The web UI also includes an admin portal for managing the product catalog. This admin portal, is packaged as a WebAssembly application and relies on a separate REST API service. Both the web UI and the REST API connect to the same SQL Server container.

    Looking through the source code which can be found here we can identify requirements for configs, persistent storage, and secrets.

    Database server

    • Need to store the password for the sa account as a secure secret
    • Need persistent storage volume for data directory
    • Need to inject environment variables for SQL Server license type and EULA acceptance

    Web UI and REST API service

    • Need to store database connection string as a secure secret
    • Need to inject ASP.NET environment variables to override app settings
    • Need persistent storage volume for ASP.NET key storage

    Implement environment variables using ConfigMaps

    ConfigMaps are relatively straight-forward to create. If you were following along with the examples last week, this should be review 😉

    Create a ConfigMap to store database environment variables.

    kubectl apply -f - <<EOF
    apiVersion: v1
    kind: ConfigMap
    metadata:
    name: mssql-settings
    data:
    MSSQL_PID: Developer
    ACCEPT_EULA: "Y"
    EOF

    Create another ConfigMap to store ASP.NET environment variables.

    kubectl apply -f - <<EOF
    apiVersion: v1
    kind: ConfigMap
    metadata:
    name: aspnet-settings
    data:
    ASPNETCORE_ENVIRONMENT: Development
    EOF

    Implement persistent volumes using Azure Files

    Similar to last week, we'll take advantage of storage classes built into AKS. For our SQL Server data, we'll use the azurefile-csi-premium storage class and leverage an Azure Files resource as our PersistentVolume.

    Create a PersistentVolumeClaim (PVC) for persisting SQL Server data.

    kubectl apply -f - <<EOF
    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
    name: mssql-data
    spec:
    accessModes:
    - ReadWriteMany
    storageClassName: azurefile-csi-premium
    resources:
    requests:
    storage: 5Gi
    EOF

    Create another PVC for persisting ASP.NET data.

    kubectl apply -f - <<EOF
    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
    name: aspnet-data
    spec:
    accessModes:
    - ReadWriteMany
    storageClassName: azurefile-csi-premium
    resources:
    requests:
    storage: 5Gi
    EOF

    Implement secrets using Azure Key Vault

    It's a well known fact that Kubernetes secretes are not really secrets. They're just base64-encoded values and not secure, especially if malicious users have access to your Kubernetes cluster.

    In a production scenario, you will want to leverage an external vault like Azure Key Vault or HashiCorp Vault to encrypt and store secrets.

    With AKS, we can enable the Secrets Store CSI driver add-on which will allow us to leverage Azure Key Vault.

    # Set some variables
    RG_NAME=<YOUR_RESOURCE_GROUP_NAME>
    AKS_NAME=<YOUR_AKS_CLUSTER_NAME>
    ACR_NAME=<YOUR_ACR_NAME>

    az aks enable-addons \
    --addons azure-keyvault-secrets-provider \
    --name $AKS_NAME \
    --resource-group $RG_NAME

    With the add-on enabled, you should see aks-secrets-store-csi-driver and aks-secrets-store-provider-azure resources installed on each node in your Kubernetes cluster.

    Run the command below to verify.

    kubectl get pods \
    --namespace kube-system \
    --selector 'app in (secrets-store-csi-driver, secrets-store-provider-azure)'

    The Secrets Store CSI driver allows us to use secret stores via Container Storage Interface (CSI) volumes. This provider offers capabilities such as mounting and syncing between the secure vault and Kubernetes Secrets. On AKS, the Azure Key Vault Provider for Secrets Store CSI Driver enables integration with Azure Key Vault.

    You may not have an Azure Key Vault created yet, so let's create one and add some secrets to it.

    AKV_NAME=$(az keyvault create \
    --name akv-eshop$RANDOM \
    --resource-group $RG_NAME \
    --query name -o tsv)

    # Database server password
    az keyvault secret set \
    --vault-name $AKV_NAME \
    --name mssql-password \
    --value "@someThingComplicated1234"

    # Catalog database connection string
    az keyvault secret set \
    --vault-name $AKV_NAME \
    --name mssql-connection-catalog \
    --value "Server=db;Database=Microsoft.eShopOnWeb.CatalogDb;User Id=sa;Password=@someThingComplicated1234;TrustServerCertificate=True;"

    # Identity database connection string
    az keyvault secret set \
    --vault-name $AKV_NAME \
    --name mssql-connection-identity \
    --value "Server=db;Database=Microsoft.eShopOnWeb.Identity;User Id=sa;Password=@someThingComplicated1234;TrustServerCertificate=True;"

    Pods authentication using Azure Workload Identity

    In order for our Pods to retrieve secrets from Azure Key Vault, we'll need to set up a way for the Pod to authenticate against Azure AD. This can be achieved by implementing the new Azure Workload Identity feature of AKS.

    info

    At the time of this writing, the workload identity feature of AKS is in Preview.

    The workload identity feature within AKS allows us to leverage native Kubernetes resources and link a Kubernetes ServiceAccount to an Azure Managed Identity to authenticate against Azure AD.

    For the authentication flow, our Kubernetes cluster will act as an Open ID Connect (OIDC) issuer and will be able issue identity tokens to ServiceAccounts which will be assigned to our Pods.

    The Azure Managed Identity will be granted permission to access secrets in our Azure Key Vault and with the ServiceAccount being assigned to our Pods, they will be able to retrieve our secrets.

    For more information on how the authentication mechanism all works, check out this doc.

    To implement all this, start by enabling the new preview feature for AKS.

    az feature register \
    --namespace "Microsoft.ContainerService" \
    --name "EnableWorkloadIdentityPreview"
    caution

    This can take several minutes to complete.

    Check the status and ensure the state shows Regestered before moving forward.

    az feature show \
    --namespace "Microsoft.ContainerService" \
    --name "EnableWorkloadIdentityPreview"

    Update your AKS cluster to enable the workload identity feature and enable the OIDC issuer endpoint.

    az aks update \
    --name $AKS_NAME \
    --resource-group $RG_NAME \
    --enable-workload-identity \
    --enable-oidc-issuer

    Create an Azure Managed Identity and retrieve its client ID.

    MANAGED_IDENTITY_CLIENT_ID=$(az identity create \
    --name aks-workload-identity \
    --resource-group $RG_NAME \
    --subscription $(az account show --query id -o tsv) \
    --query 'clientId' -o tsv)

    Create the Kubernetes ServiceAccount.

    # Set namespace (this must align with the namespace that your app is deployed into)
    SERVICE_ACCOUNT_NAMESPACE=default

    # Set the service account name
    SERVICE_ACCOUNT_NAME=eshop-serviceaccount

    # Create the service account
    kubectl apply -f - <<EOF
    apiVersion: v1
    kind: ServiceAccount
    metadata:
    annotations:
    azure.workload.identity/client-id: ${MANAGED_IDENTITY_CLIENT_ID}
    labels:
    azure.workload.identity/use: "true"
    name: ${SERVICE_ACCOUNT_NAME}
    namespace: ${SERVICE_ACCOUNT_NAMESPACE}
    EOF
    info

    Note to enable this ServiceAccount to work with Azure Workload Identity, you must annotate the resource with azure.workload.identity/client-id, and add a label of azure.workload.identity/use: "true"

    That was a lot... Let's review what we just did.

    We have an Azure Managed Identity (object in Azure AD), an OIDC issuer URL (endpoint in our Kubernetes cluster), and a Kubernetes ServiceAccount.

    The next step is to "tie" these components together and establish a Federated Identity Credential so that Azure AD can trust authentication requests from your Kubernetes cluster.

    info

    This identity federation can be established between Azure AD any Kubernetes cluster; not just AKS 🤗

    To establish the federated credential, we'll need the OIDC issuer URL, and a subject which points to your Kubernetes ServiceAccount.

    # Get the OIDC issuer URL
    OIDC_ISSUER_URL=$(az aks show \
    --name $AKS_NAME \
    --resource-group $RG_NAME \
    --query "oidcIssuerProfile.issuerUrl" -o tsv)

    # Set the subject name using this format: `system:serviceaccount:<YOUR_SERVICE_ACCOUNT_NAMESPACE>:<YOUR_SERVICE_ACCOUNT_NAME>`
    SUBJECT=system:serviceaccount:$SERVICE_ACCOUNT_NAMESPACE:$SERVICE_ACCOUNT_NAME

    az identity federated-credential create \
    --name aks-federated-credential \
    --identity-name aks-workload-identity \
    --resource-group $RG_NAME \
    --issuer $OIDC_ISSUER_URL \
    --subject $SUBJECT

    With the authentication components set, we can now create a SecretProviderClass which includes details about the Azure Key Vault, the secrets to pull out from the vault, and identity used to access the vault.

    # Get the tenant id for the key vault
    TENANT_ID=$(az keyvault show \
    --name $AKV_NAME \
    --resource-group $RG_NAME \
    --query properties.tenantId -o tsv)

    # Create the secret provider for azure key vault
    kubectl apply -f - <<EOF
    apiVersion: secrets-store.csi.x-k8s.io/v1
    kind: SecretProviderClass
    metadata:
    name: eshop-azure-keyvault
    spec:
    provider: azure
    parameters:
    usePodIdentity: "false"
    useVMManagedIdentity: "false"
    clientID: "${MANAGED_IDENTITY_CLIENT_ID}"
    keyvaultName: "${AKV_NAME}"
    cloudName: ""
    objects: |
    array:
    - |
    objectName: mssql-password
    objectType: secret
    objectVersion: ""
    - |
    objectName: mssql-connection-catalog
    objectType: secret
    objectVersion: ""
    - |
    objectName: mssql-connection-identity
    objectType: secret
    objectVersion: ""
    tenantId: "${TENANT_ID}"
    secretObjects:
    - secretName: eshop-secrets
    type: Opaque
    data:
    - objectName: mssql-password
    key: mssql-password
    - objectName: mssql-connection-catalog
    key: mssql-connection-catalog
    - objectName: mssql-connection-identity
    key: mssql-connection-identity
    EOF

    Finally, lets grant the Azure Managed Identity permissions to retrieve secrets from the Azure Key Vault.

    az keyvault set-policy \
    --name $AKV_NAME \
    --secret-permissions get \
    --spn $MANAGED_IDENTITY_CLIENT_ID

    Re-package deployments

    Update your database deployment to load environment variables from our ConfigMap, attach the PVC and SecretProviderClass as volumes, mount the volumes into the Pod, and use the ServiceAccount to retrieve secrets.

    Additionally, you may notice the database Pod is set to use fsGroup:10001 as part of the securityContext. This is required as the MSSQL container runs using a non-root account called mssql and this account has the proper permissions to read/write data at the /var/opt/mssql mount path.

    kubectl apply -f - <<EOF
    apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: db
    labels:
    app: db
    spec:
    replicas: 1
    selector:
    matchLabels:
    app: db
    template:
    metadata:
    labels:
    app: db
    spec:
    securityContext:
    fsGroup: 10001
    serviceAccountName: ${SERVICE_ACCOUNT_NAME}
    containers:
    - name: db
    image: mcr.microsoft.com/mssql/server:2019-latest
    ports:
    - containerPort: 1433
    envFrom:
    - configMapRef:
    name: mssql-settings
    env:
    - name: MSSQL_SA_PASSWORD
    valueFrom:
    secretKeyRef:
    name: eshop-secrets
    key: mssql-password
    resources: {}
    volumeMounts:
    - name: mssqldb
    mountPath: /var/opt/mssql
    - name: eshop-secrets
    mountPath: "/mnt/secrets-store"
    readOnly: true
    volumes:
    - name: mssqldb
    persistentVolumeClaim:
    claimName: mssql-data
    - name: eshop-secrets
    csi:
    driver: secrets-store.csi.k8s.io
    readOnly: true
    volumeAttributes:
    secretProviderClass: eshop-azure-keyvault
    EOF

    We'll update the API and Web deployments in a similar way.

    # Set the image tag
    IMAGE_TAG=<YOUR_IMAGE_TAG>

    # API deployment
    kubectl apply -f - <<EOF
    apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: api
    labels:
    app: api
    spec:
    replicas: 1
    selector:
    matchLabels:
    app: api
    template:
    metadata:
    labels:
    app: api
    spec:
    serviceAccount: ${SERVICE_ACCOUNT_NAME}
    containers:
    - name: api
    image: ${ACR_NAME}.azurecr.io/api:${IMAGE_TAG}
    ports:
    - containerPort: 80
    envFrom:
    - configMapRef:
    name: aspnet-settings
    env:
    - name: ConnectionStrings__CatalogConnection
    valueFrom:
    secretKeyRef:
    name: eshop-secrets
    key: mssql-connection-catalog
    - name: ConnectionStrings__IdentityConnection
    valueFrom:
    secretKeyRef:
    name: eshop-secrets
    key: mssql-connection-identity
    resources: {}
    volumeMounts:
    - name: aspnet
    mountPath: ~/.aspnet/https:/root/.aspnet/https:ro
    - name: eshop-secrets
    mountPath: "/mnt/secrets-store"
    readOnly: true
    volumes:
    - name: aspnet
    persistentVolumeClaim:
    claimName: aspnet-data
    - name: eshop-secrets
    csi:
    driver: secrets-store.csi.k8s.io
    readOnly: true
    volumeAttributes:
    secretProviderClass: eshop-azure-keyvault
    EOF

    ## Web deployment
    kubectl apply -f - <<EOF
    apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: web
    labels:
    app: web
    spec:
    replicas: 1
    selector:
    matchLabels:
    app: web
    template:
    metadata:
    labels:
    app: web
    spec:
    serviceAccount: ${SERVICE_ACCOUNT_NAME}
    containers:
    - name: web
    image: ${ACR_NAME}.azurecr.io/web:${IMAGE_TAG}
    ports:
    - containerPort: 80
    envFrom:
    - configMapRef:
    name: aspnet-settings
    env:
    - name: ConnectionStrings__CatalogConnection
    valueFrom:
    secretKeyRef:
    name: eshop-secrets
    key: mssql-connection-catalog
    - name: ConnectionStrings__IdentityConnection
    valueFrom:
    secretKeyRef:
    name: eshop-secrets
    key: mssql-connection-identity
    resources: {}
    volumeMounts:
    - name: aspnet
    mountPath: ~/.aspnet/https:/root/.aspnet/https:ro
    - name: eshop-secrets
    mountPath: "/mnt/secrets-store"
    readOnly: true
    volumes:
    - name: aspnet
    persistentVolumeClaim:
    claimName: aspnet-data
    - name: eshop-secrets
    csi:
    driver: secrets-store.csi.k8s.io
    readOnly: true
    volumeAttributes:
    secretProviderClass: eshop-azure-keyvault
    EOF

    If all went well with your deployment updates, you should be able to browse to your website and buy some merchandise again 🥳

    echo "http://$(kubectl get service web -o jsonpath='{.status.loadBalancer.ingress[0].ip}')"

    Conclusion

    Although there is no visible changes on with our website, we've made a ton of changes on the Kubernetes backend to make this application much more secure and resilient.

    We used a combination of Kubernetes resources and AKS-specific features to achieve our goal of securing our secrets and ensuring data is not lost on container crashes and restarts.

    To learn more about the components we leveraged here today, checkout the resources and additional tutorials listed below.

    You can also find manifests with all the changes made in today's post in the Azure-Samples/eShopOnAKS repository.

    See you in the next post!

    Resources

    Take the Cloud Skills Challenge!

    Enroll in the Cloud Skills Challenge!

    Don't miss out on this opportunity to level up your skills and stay ahead of the curve in the world of cloud native.

    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/secure-supply-chain/index.html b/cnny-2023/tags/secure-supply-chain/index.html index e06b6c8b19..19c8fcf045 100644 --- a/cnny-2023/tags/secure-supply-chain/index.html +++ b/cnny-2023/tags/secure-supply-chain/index.html @@ -14,13 +14,13 @@ - - + +

    One post tagged with "secure-supply-chain"

    View All Tags

    · 6 min read
    Josh Duffney

    Welcome to Day 5 of Week 3 of #CloudNativeNewYear!

    The theme for this week is Bringing Your Application to Kubernetes. Yesterday we talked about debugging and instrumenting our application. Today we'll explore the topic of container image signing and secure supply chain.

    Ask the Experts Thursday, February 9th at 9 AM PST
    Friday, February 10th at 11 AM PST

    Watch the recorded demo and conversation about this week's topics.

    We were live on YouTube walking through today's (and the rest of this week's) demos. Join us Friday, February 10th and bring your questions!

    What We'll Cover

    • Introduction
    • Prerequisites
    • Create a digital signing certificate
    • Generate an Azure Container Registry Token
    • Set up Notation
    • Install the Notation Azure Key Vault Plugin
    • Add the signing Certificate to Notation
    • Sign Container Images
    • Conclusion

    Introduction

    The secure supply chain is a crucial aspect of software development, delivery, and deployment, and digital signing plays a critical role in this process.

    By using digital signatures to verify the authenticity and integrity of container images, organizations can improve the security of your software supply chain and reduce the risk of security breaches and data compromise.

    In this article, you'll learn how to use Notary, an open-source project hosted by the Cloud Native Computing Foundation (CNCF) to digitally sign container images stored on Azure Container Registry.

    Prerequisites

    To follow along, you'll need an instance of:

    Create a digital signing certificate

    A digital signing certificate is a certificate that is used to digitally sign and verify the authenticity and integrity of digital artifacts. Such documents, software, and of course container images.

    Before you can implement digital signatures, you must first create a digital signing certificate.

    Run the following command to generate the certificate:

    1. Create the policy file

      cat <<EOF > ./my_policy.json
      {
      "issuerParameters": {
      "certificateTransparency": null,
      "name": "Self"
      },
      "x509CertificateProperties": {
      "ekus": [
      "1.3.6.1.5.5.7.3.3"
      ],
      "key_usage": [
      "digitalSignature"
      ],
      "subject": "CN=${keySubjectName}",
      "validityInMonths": 12
      }
      }
      EOF

      The ekus and key usage of this certificate policy dictate that the certificate can only be used for digital signatures.

    2. Create the certificate in Azure Key Vault

      az keyvault certificate create --name $keyName --vault-name $keyVaultName --policy @my_policy.json

      Replace $keyName and $keyVaultName with your desired certificate name and Azure Key Vault instance name.

    Generate a Azure Container Registry token

    Azure Container Registry tokens are used to grant access to the contents of the registry. Tokens can be used for a variety of things such as pulling images, pushing images, or managing the registry.

    As part of the container image signing workflow, you'll need a token to authenticate the Notation CLI with your Azure Container Registry.

    Run the following command to generate an ACR token:

    az acr token create \
    --name $tokenName \
    --registry $registry \
    --scope-map _repositories_admin \
    --query 'credentials.passwords[0].value' \
    --only-show-errors \
    --output tsv

    Replace $tokenName with your name for the ACR token and $registry with the name of your ACR instance.

    Setup Notation

    Notation is the command-line interface for the CNCF Notary project. You'll use it to digitally sign the api and web container images for the eShopOnWeb application.

    Run the following commands to download and install the NotationCli:

    1. Open a terminal or command prompt window

    2. Download the Notary notation release

      curl -Lo notation.tar.gz https://github.com/notaryproject/notation/releases/download/v1.0.0-rc.1/notation_1.0.0-rc.1_linux_amd64.tar.gz > /dev/null 2>&1

      If you're not using Linux, you can find the releases here.

    3. Extract the contents of the notation.tar.gz

      tar xvzf notation.tar.gz > /dev/null 2>&1
    4. Copy the notation binary to the $HOME/bin directory

      cp ./notation $HOME/bin
    5. Add the $HOME/bin directory to the PATH environment variable

      export PATH="$HOME/bin:$PATH"
    6. Remove the downloaded files

      rm notation.tar.gz LICENSE
    7. Check the notation version

      notation --version

    Install the Notation Azure Key Vault plugin

    By design the NotationCli supports plugins that extend its digital signing capabilities to remote registries. And in order to sign your container images stored in Azure Container Registry, you'll need to install the Azure Key Vault plugin for Notation.

    Run the following commands to install the azure-kv plugin:

    1. Download the plugin

      curl -Lo notation-azure-kv.tar.gz \
      https://github.com/Azure/notation-azure-kv/releases/download/v0.5.0-rc.1/notation-azure-kv_0.5.0-rc.1_linux_amd64.tar.gz > /dev/null 2>&1

      Non-Linux releases can be found here.

    2. Extract to the plugin directory & delete download files

      tar xvzf notation-azure-kv.tar.gz -C ~/.config/notation/plugins/azure-kv notation-azure-kv > /dev/null 2>&

      rm -rf notation-azure-kv.tar.gz
    3. Verify the plugin was installed

      notation plugin ls

    Add the signing certificate to Notation

    Now that you have Notation and the Azure Key Vault plugin installed, add the certificate's keyId created above to Notation.

    1. Get the Certificate Key ID from Azure Key Vault

      az keyvault certificate show \
      --vault-name $keyVaultName \
      --name $keyName \
      --query "kid" --only-show-errors --output tsv

      Replace $keyVaultName and $keyName with the appropriate information.

    2. Add the Key ID to KMS using Notation

      notation key add --plugin azure-kv --id $keyID $keyName
    3. Check the key list

      notation key ls

    Sign Container Images

    At this point, all that's left is to sign the container images.

    Run the notation sign command to sign the api and web container images:

    notation sign $registry.azurecr.io/web:$tag \
    --username $tokenName \
    --password $tokenPassword

    notation sign $registry.azurecr.io/api:$tag \
    --username $tokenName \
    --password $tokenPassword

    Replace $registry, $tag, $tokenName, and $tokenPassword with the appropriate values. To improve security, use a SHA hash for the tag.

    NOTE: If you didn't take note of the token password, you can rerun the az acr token create command to generate a new password.

    Conclusion

    Digital signing plays a critical role in ensuring the security of software supply chains.

    By signing software components, organizations can verify the authenticity and integrity of software, helping to prevent unauthorized modifications, tampering, and malware.

    And if you want to take digital signing to a whole new level by using them to prevent the deployment of unsigned container images, check out the Ratify project on GitHub!

    Take the Cloud Skills Challenge!

    Enroll in the Cloud Skills Challenge!

    Don't miss out on this opportunity to level up your skills and stay ahead of the curve in the world of cloud native.

    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/service/index.html b/cnny-2023/tags/service/index.html index c832ce0a8b..f195e49e10 100644 --- a/cnny-2023/tags/service/index.html +++ b/cnny-2023/tags/service/index.html @@ -14,13 +14,13 @@ - - + +

    One post tagged with "service"

    View All Tags

    · 11 min read
    Paul Yu

    Welcome to Day 2 of Week 2 of #CloudNativeNewYear!

    The theme for this week is #Kubernetes fundamentals. Yesterday we talked about how to deploy a containerized web app workload to Azure Kubernetes Service (AKS). Today we'll explore the topic of services and ingress and walk through the steps of making our containers accessible both internally as well as over the internet so that you can share it with the world 😊

    Ask the Experts Thursday, February 9th at 9 AM PST
    Catch the Replay of the Live Demo

    Watch the recorded demo and conversation about this week's topics.

    We were live on YouTube walking through today's (and the rest of this week's) demos.

    What We'll Cover

    • Exposing Pods via Service
    • Exposing Services via Ingress
    • Takeaways
    • Resources

    Exposing Pods via Service

    There are a few ways to expose your pod in Kubernetes. One way is to take an imperative approach and use the kubectl expose command. This is probably the quickest way to achieve your goal but it isn't the best way. A better way to expose your pod by taking a declarative approach by creating a services manifest file and deploying it using the kubectl apply command.

    Don't worry if you are unsure of how to make this manifest, we'll use kubectl to help generate it.

    First, let's ensure we have the database deployed on our AKS cluster.

    📝 NOTE: If you don't have an AKS cluster deployed, please head over to Azure-Samples/azure-voting-app-rust, clone the repo, and follow the instructions in the README.md to execute the Azure deployment and setup your kubectl context. Check out the first post this week for more on the environment setup.

    kubectl apply -f ./manifests/deployment-db.yaml

    Next, let's deploy the application. If you are following along from yesterday's content, there isn't anything you need to change; however, if you are deploy the app from scratch, you'll need to modify the deployment-app.yaml manifest and update it with your image tag and database pod's IP address.

    kubectl apply -f ./manifests/deployment-app.yaml

    Now, let's expose the database using a service so that we can leverage Kubernetes' built-in service discovery to be able to reference it by name; not pod IP. Run the following command.

    kubectl expose deployment azure-voting-db \
    --port=5432 \
    --target-port=5432

    With the database exposed using service, we can update the app deployment manifest to use the service name instead of pod IP. This way, if the pod ever gets assigned a new IP, we don't have to worry about updating the IP each time and redeploying our web application. Kubernetes has internal service discovery mechanism in place that allows us to reference a service by its name.

    Let's make an update to the manifest. Replace the environment variable for DATABASE_SERVER with the following:

    - name: DATABASE_SERVER
    value: azure-voting-db

    Re-deploy the app with the updated configuration.

    kubectl apply -f ./manifests/deployment-app.yaml

    One service down, one to go. Run the following command to expose the web application.

    kubectl expose deployment azure-voting-app \
    --type=LoadBalancer \
    --port=80 \
    --target-port=8080

    Notice the --type argument has a value of LoadBalancer. This service type is implemented by the cloud-controller-manager which is part of the Kubernetes control plane. When using a managed Kubernetes cluster such as Azure Kubernetes Service, a public standard load balancer will be able to provisioned when the service type is set to LoadBalancer. The load balancer will also have a public IP assigned which will make your deployment publicly available.

    Kubernetes supports four service types:

    • ClusterIP: this is the default and limits service access to internal traffic within the cluster
    • NodePort: this assigns a port mapping on the node's IP address and allows traffic from the virtual network (outside the cluster)
    • LoadBalancer: as mentioned above, this creates a cloud-based load balancer
    • ExternalName: this is used in special case scenarios where you want to map a service to an external DNS name

    📝 NOTE: When exposing a web application to the internet, allowing external users to connect to your Service directly is not the best approach. Instead, you should use an Ingress, which we'll cover in the next section.

    Now, let's confirm you can reach the web app from the internet. You can use the following command to print the URL to your terminal.

    echo "http://$(kubectl get service azure-voting-app -o jsonpath='{.status.loadBalancer.ingress[0].ip}')"

    Great! The kubectl expose command gets the job done, but as mentioned above, it is not the best method of exposing deployments. It is better to expose deployments declaratively using a service manifest, so let's delete the services and redeploy using manifests.

    kubectl delete service azure-voting-db azure-voting-app

    To use kubectl to generate our manifest file, we can use the same kubectl expose command that we ran earlier but this time, we'll include --output=yaml and --dry-run=client. This will instruct the command to output the manifest that would be sent to the kube-api server in YAML format to the terminal.

    Generate the manifest for the database service.

    kubectl expose deployment azure-voting-db \
    --type=ClusterIP \
    --port=5432 \
    --target-port=5432 \
    --output=yaml \
    --dry-run=client > ./manifests/service-db.yaml

    Generate the manifest for the application service.

    kubectl expose deployment azure-voting-app \
    --type=LoadBalancer \
    --port=80 \
    --target-port=8080 \
    --output=yaml \
    --dry-run=client > ./manifests/service-app.yaml

    The command above redirected the YAML output to your manifests directory. Here is what the web application service looks like.

    apiVersion: v1
    kind: Service
    metadata:
    creationTimestamp: null
    labels:
    app: azure-voting-app
    name: azure-voting-app
    spec:
    ports:
    - port: 80
    protocol: TCP
    targetPort: 8080
    selector:
    app: azure-voting-app
    type: LoadBalancer
    status:
    loadBalancer: {}

    💡 TIP: To view the schema of any api-resource in Kubernetes, you can use the kubectl explain command. In this case the kubectl explain service command will tell us exactly what each of these fields do.

    Re-deploy the services using the new service manifests.

    kubectl apply -f ./manifests/service-db.yaml -f ./manifests/service-app.yaml

    # You should see TYPE is set to LoadBalancer and the EXTERNAL-IP is set
    kubectl get service azure-voting-db azure-voting-app

    Confirm again that our application is accessible again. Run the following command to print the URL to the terminal.

    echo "http://$(kubectl get service azure-voting-app -o jsonpath='{.status.loadBalancer.ingress[0].ip}')"

    That was easy, right? We just exposed both of our pods using Kubernetes services. The database only needs to be accessible from within the cluster so ClusterIP is perfect for that. For the web application, we specified the type to be LoadBalancer so that we can access the application over the public internet.

    But wait... remember that if you want to expose web applications over the public internet, a Service with a public IP is not the best way; the better approach is to use an Ingress resource.

    Exposing Services via Ingress

    If you read through the Kubernetes documentation on Ingress you will see a diagram that depicts the Ingress sitting in front of the Service resource with a routing rule between it. In order to use Ingress, you need to deploy an Ingress Controller and it can be configured with many routing rules to forward traffic to one or many backend services. So effectively, an Ingress is a load balancer for your Services.

    With that said, we no longer need a service type of LoadBalancer since the service does not need to be accessible from the internet. It only needs to be accessible from the Ingress Controller (internal to the cluster) so we can change the service type to ClusterIP.

    Update your service.yaml file to look like this:

    apiVersion: v1
    kind: Service
    metadata:
    creationTimestamp: null
    labels:
    app: azure-voting-app
    name: azure-voting-app
    spec:
    ports:
    - port: 80
    protocol: TCP
    targetPort: 8080
    selector:
    app: azure-voting-app

    📝 NOTE: The default service type is ClusterIP so we can omit the type altogether.

    Re-apply the app service manifest.

    kubectl apply -f ./manifests/service-app.yaml

    # You should see TYPE set to ClusterIP and EXTERNAL-IP set to <none>
    kubectl get service azure-voting-app

    Next, we need to install an Ingress Controller. There are quite a few options, and the Kubernetes-maintained NGINX Ingress Controller is commonly deployed.

    You could install this manually by following these instructions, but if you do that you'll be responsible for maintaining and supporting the resource.

    I like to take advantage of free maintenance and support when I can get it, so I'll opt to use the Web Application Routing add-on for AKS.

    💡 TIP: Whenever you install an AKS add-on, it will be maintained and fully supported by Azure Support.

    Enable the web application routing add-on in our AKS cluster with the following command.

    az aks addon enable \
    --name <YOUR_AKS_NAME> \
    --resource-group <YOUR_AKS_RESOURCE_GROUP>
    --addon web_application_routing

    ⚠️ WARNING: This command can take a few minutes to complete

    Now, let's use the same approach we took in creating our service to create our Ingress resource. Run the following command to generate the Ingress manifest.

    kubectl create ingress azure-voting-app \
    --class=webapprouting.kubernetes.azure.com \
    --rule="/*=azure-voting-app:80" \
    --output yaml \
    --dry-run=client > ./manifests/ingress.yaml

    The --class=webapprouting.kubernetes.azure.com option activates the AKS web application routing add-on. This AKS add-on can also integrate with other Azure services such as Azure DNS and Azure Key Vault for TLS certificate management and this special class makes it all work.

    The --rule="/*=azure-voting-app:80" option looks confusing but we can use kubectl again to help us understand how to format the value for the option.

    kubectl create ingress --help

    In the output you will see the following:

    --rule=[]:
    Rule in format host/path=service:port[,tls=secretname]. Paths containing the leading character '*' are
    considered pathType=Prefix. tls argument is optional.

    It expects a host and path separated by a forward-slash, then expects the backend service name and port separated by a colon. We're not using a hostname for this demo so we can omit it. For the path, an asterisk is used to specify a wildcard path prefix.

    So, the value of /*=azure-voting-app:80 creates a routing rule for all paths following the domain (or in our case since we don't have a hostname specified, the IP) to route traffic to our azure-voting-app backend service on port 80.

    📝 NOTE: Configuring the hostname and TLS is outside the scope of this demo but please visit this URL https://bit.ly/aks-webapp-routing for an in-depth hands-on lab centered around Web Application Routing on AKS.

    Your ingress.yaml file should look like this:

    apiVersion: networking.k8s.io/v1
    kind: Ingress
    metadata:
    creationTimestamp: null
    name: azure-voting-app
    spec:
    ingressClassName: webapprouting.kubernetes.azure.com
    rules:
    - http:
    paths:
    - backend:
    service:
    name: azure-voting-app
    port:
    number: 80
    path: /
    pathType: Prefix
    status:
    loadBalancer: {}

    Apply the app ingress manifest.

    kubectl apply -f ./manifests/ingress.yaml

    Validate the web application is available from the internet again. You can run the following command to print the URL to the terminal.

    echo "http://$(kubectl get ingress azure-voting-app -o jsonpath='{.status.loadBalancer.ingress[0].ip}')"

    Takeaways

    Exposing your applications both internally and externally can be easily achieved using Service and Ingress resources respectively. If your service is HTTP or HTTPS based and needs to be accessible from outsie the cluster, use Ingress with an internal Service (i.e., ClusterIP or NodePort); otherwise, use the Service resource. If your TCP-based Service needs to be publicly accessible, you set the type to LoadBalancer to expose a public IP for it. To learn more about these resources, please visit the links listed below.

    Lastly, if you are unsure how to begin writing your service manifest, you can use kubectl and have it do most of the work for you 🥳

    Resources

    Take the Cloud Skills Challenge!

    Enroll in the Cloud Skills Challenge!

    Don't miss out on this opportunity to level up your skills and stay ahead of the curve in the world of cloud native.

    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/windows/index.html b/cnny-2023/tags/windows/index.html index 92b92865a4..a69ac1173b 100644 --- a/cnny-2023/tags/windows/index.html +++ b/cnny-2023/tags/windows/index.html @@ -14,14 +14,14 @@ - - + +

    One post tagged with "windows"

    View All Tags

    · 7 min read
    Vinicius Apolinario

    Welcome to Day 3 of Week 4 of #CloudNativeNewYear!

    The theme for this week is going further with Cloud Native. Yesterday we talked about using Draft to accelerate your Kubernetes adoption. Today we'll explore the topic of Windows containers.

    What We'll Cover

    • Introduction
    • Windows containers overview
    • Windows base container images
    • Isolation
    • Exercise: Try this yourself!
    • Resources: For self-study!

    Introduction

    Windows containers were launched along with Windows Server 2016, and have evolved since. In its latest release, Windows Server 2022, Windows containers have reached a great level of maturity and allow for customers to run production grade workloads.

    While suitable for new developments, Windows containers also provide developers and operations with a different approach than Linux containers. It allows for existing Windows applications to be containerized with little or no code changes. It also allows for professionals that are more comfortable with the Windows platform and OS, to leverage their skill set, while taking advantage of the containers platform.

    Windows container overview

    In essence, Windows containers are very similar to Linux. Since Windows containers use the same foundation of Docker containers, you can expect that the same architecture applies - with the specific notes of the Windows OS. For example, when running a Windows container via Docker, you use the same commands, such as docker run. To pull a container image, you can use docker pull, just like on Linux. However, to run a Windows container, you also need a Windows container host. This requirement is there because, as you might remember, a container shares the OS kernel with its container host.

    On Kubernetes, Windows containers are supported since Windows Server 2019. Just like with Docker, you can manage Windows containers like any other resource on the Kubernetes ecosystem. A Windows node can be part of a Kubernetes cluster, allowing you to run Windows container based applications on services like Azure Kubernetes Service. To deploy an Windows application to a Windows pod in Kubernetes, you can author a YAML specification much like you would for Linux. The main difference is that you would point to an image that runs on Windows, and you need to specify a node selection tag to indicate said pod needs to run on a Windows node.

    Windows base container images

    On Windows containers, you will always use a base container image provided by Microsoft. This base container image contains the OS binaries for the container to run. This image can be as large as 3GB+, or small as ~300MB. The difference in the size is a consequence of the APIs and components available in each Windows container base container image. There are primarily, three images: Nano Server, Server Core, and Server.

    Nano Server is the smallest image, ranging around 300MB. It's a base container image for new developments and cloud-native scenarios. Applications need to target Nano Server as the Windows OS, so not all frameworks will work. For example, .Net works on Nano Server, but .Net Framework doesn't. Other third-party frameworks also work on Nano Server, such as Apache, NodeJS, Phyton, Tomcat, Java runtime, JBoss, Redis, among others.

    Server Core is a much larger base container image, ranging around 1.25GB. It's larger size is compensated by it's application compatibility. Simply put, any application that meets the requirements to be run on a Windows container, can be containerized with this image.

    The Server image builds on the Server Core one. It ranges around 3.1GB and has even greater application compatibility than the Server Core image. In addition to the traditional Windows APIs and components, this image allows for scenarios such as Machine Learning via DirectX with GPU access.

    The best image for your scenario is dependent on the requirements your application has on the Windows OS inside a container. However, there are some scenarios that are not supported at all on Windows containers - such as GUI or RDP dependent applications, some Windows Server infrastructure roles, such as Active Directory, among others.

    Isolation

    When running containers, the kernel of the container host is shared with the containers running on it. While extremely convenient, this poses a potential risk for multi-tenant scenarios. If one container is compromised and has access to the host, it could potentially compromise other containers in the same system.

    For enterprise customers running on-premises (or even in the cloud), this can be mitigated by using a VM as a container host and considering the VM itself a security boundary. However, if multiple workloads from different tenants need to share the same host, Windows containers offer another option: Hyper-V isolation. While the name Hyper-V is associated with VMs, its virtualization capabilities extend to other services, including containers. Hyper-V isolated containers run on a purpose built, extremely small, highly performant VM. However, you manage a container running with Hyper-V isolation the same way you do with a process isolated one. In fact, the only notable difference is that you need to append the --isolation=hyperv tag to the docker run command.

    Exercise

    Here are a few examples of how to use Windows containers:

    Run Windows containers via Docker on your machine

    To pull a Windows base container image:

    docker pull mcr.microsoft.com/windows/servercore:ltsc2022

    To run a basic IIS container:

    #This command will pull and start a IIS container. You can access it from http://<your local IP>:8080
    docker run -d -p 8080:80 mcr.microsoft.com/windows/servercore/iis:windowsservercore-ltsc2022

    Run the same IIS container with Hyper-V isolation

    #This command will pull and start a IIS container. You can access it from http://<your local IP>:8080
    docker run -d -p 8080:80 --isolation=hyperv mcr.microsoft.com/windows/servercore/iis:windowsservercore-ltsc2022

    To run a Windows container interactively:

    docker run -it mcr.microsoft.com/windows/servercore:ltsc2022 powershell

    Run Windows containers on Kubernetes

    To prepare an AKS cluster for Windows containers: Note: Replace the values on the example below with the ones from your environment.

    echo "Please enter the username to use as administrator credentials for Windows Server nodes on your cluster: " && read WINDOWS_USERNAME
    az aks create \
    --resource-group myResourceGroup \
    --name myAKSCluster \
    --node-count 2 \
    --generate-ssh-keys \
    --windows-admin-username $WINDOWS_USERNAME \
    --vm-set-type VirtualMachineScaleSets \
    --network-plugin azure

    To add a Windows node pool for Windows containers:

    az aks nodepool add \
    --resource-group myResourceGroup \
    --cluster-name myAKSCluster \
    --os-type Windows \
    --name npwin \
    --node-count 1

    Deploy a sample ASP.Net application to the AKS cluster above using the YAML file below:

    apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: sample
    labels:
    app: sample
    spec:
    replicas: 1
    template:
    metadata:
    name: sample
    labels:
    app: sample
    spec:
    nodeSelector:
    "kubernetes.io/os": windows
    containers:
    - name: sample
    image: mcr.microsoft.com/dotnet/framework/samples:aspnetapp
    resources:
    limits:
    cpu: 1
    memory: 800M
    ports:
    - containerPort: 80
    selector:
    matchLabels:
    app: sample
    ---
    apiVersion: v1
    kind: Service
    metadata:
    name: sample
    spec:
    type: LoadBalancer
    ports:
    - protocol: TCP
    port: 80
    selector:
    app: sample

    Save the file above and run the command below on your Kubernetes cluster:

    kubectl apply -f <filename> .

    Once deployed, you can access the application by getting the IP address of your service:

    kubectl get service

    Resources

    It's not too late to sign up for and complete the Cloud Skills Challenge!
    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/workload-identity/index.html b/cnny-2023/tags/workload-identity/index.html index f47d374727..a28eca32f4 100644 --- a/cnny-2023/tags/workload-identity/index.html +++ b/cnny-2023/tags/workload-identity/index.html @@ -14,13 +14,13 @@ - - + +

    One post tagged with "workload-identity"

    View All Tags

    · 12 min read
    Paul Yu

    Welcome to Day 2 of Week 3 of #CloudNativeNewYear!

    The theme for this week is Bringing Your Application to Kubernetes. Yesterday we talked about getting an existing application running in Kubernetes with a full pipeline in GitHub Actions. Today we'll evaluate our sample application's configuration, storage, and networking requirements and implement using Kubernetes and Azure resources.

    Ask the Experts Thursday, February 9th at 9 AM PST
    Friday, February 10th at 11 AM PST

    Watch the recorded demo and conversation about this week's topics.

    We were live on YouTube walking through today's (and the rest of this week's) demos. Join us Friday, February 10th and bring your questions!

    What We'll Cover

    • Gather requirements
    • Implement environment variables using ConfigMaps
    • Implement persistent volumes using Azure Files
    • Implement secrets using Azure Key Vault
    • Re-package deployments
    • Conclusion
    • Resources
    caution

    Before you begin, make sure you've gone through yesterday's post to set up your AKS cluster.

    Gather requirements

    The eShopOnWeb application is written in .NET 7 and has two major pieces of functionality. The web UI is where customers can browse and shop. The web UI also includes an admin portal for managing the product catalog. This admin portal, is packaged as a WebAssembly application and relies on a separate REST API service. Both the web UI and the REST API connect to the same SQL Server container.

    Looking through the source code which can be found here we can identify requirements for configs, persistent storage, and secrets.

    Database server

    • Need to store the password for the sa account as a secure secret
    • Need persistent storage volume for data directory
    • Need to inject environment variables for SQL Server license type and EULA acceptance

    Web UI and REST API service

    • Need to store database connection string as a secure secret
    • Need to inject ASP.NET environment variables to override app settings
    • Need persistent storage volume for ASP.NET key storage

    Implement environment variables using ConfigMaps

    ConfigMaps are relatively straight-forward to create. If you were following along with the examples last week, this should be review 😉

    Create a ConfigMap to store database environment variables.

    kubectl apply -f - <<EOF
    apiVersion: v1
    kind: ConfigMap
    metadata:
    name: mssql-settings
    data:
    MSSQL_PID: Developer
    ACCEPT_EULA: "Y"
    EOF

    Create another ConfigMap to store ASP.NET environment variables.

    kubectl apply -f - <<EOF
    apiVersion: v1
    kind: ConfigMap
    metadata:
    name: aspnet-settings
    data:
    ASPNETCORE_ENVIRONMENT: Development
    EOF

    Implement persistent volumes using Azure Files

    Similar to last week, we'll take advantage of storage classes built into AKS. For our SQL Server data, we'll use the azurefile-csi-premium storage class and leverage an Azure Files resource as our PersistentVolume.

    Create a PersistentVolumeClaim (PVC) for persisting SQL Server data.

    kubectl apply -f - <<EOF
    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
    name: mssql-data
    spec:
    accessModes:
    - ReadWriteMany
    storageClassName: azurefile-csi-premium
    resources:
    requests:
    storage: 5Gi
    EOF

    Create another PVC for persisting ASP.NET data.

    kubectl apply -f - <<EOF
    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
    name: aspnet-data
    spec:
    accessModes:
    - ReadWriteMany
    storageClassName: azurefile-csi-premium
    resources:
    requests:
    storage: 5Gi
    EOF

    Implement secrets using Azure Key Vault

    It's a well known fact that Kubernetes secretes are not really secrets. They're just base64-encoded values and not secure, especially if malicious users have access to your Kubernetes cluster.

    In a production scenario, you will want to leverage an external vault like Azure Key Vault or HashiCorp Vault to encrypt and store secrets.

    With AKS, we can enable the Secrets Store CSI driver add-on which will allow us to leverage Azure Key Vault.

    # Set some variables
    RG_NAME=<YOUR_RESOURCE_GROUP_NAME>
    AKS_NAME=<YOUR_AKS_CLUSTER_NAME>
    ACR_NAME=<YOUR_ACR_NAME>

    az aks enable-addons \
    --addons azure-keyvault-secrets-provider \
    --name $AKS_NAME \
    --resource-group $RG_NAME

    With the add-on enabled, you should see aks-secrets-store-csi-driver and aks-secrets-store-provider-azure resources installed on each node in your Kubernetes cluster.

    Run the command below to verify.

    kubectl get pods \
    --namespace kube-system \
    --selector 'app in (secrets-store-csi-driver, secrets-store-provider-azure)'

    The Secrets Store CSI driver allows us to use secret stores via Container Storage Interface (CSI) volumes. This provider offers capabilities such as mounting and syncing between the secure vault and Kubernetes Secrets. On AKS, the Azure Key Vault Provider for Secrets Store CSI Driver enables integration with Azure Key Vault.

    You may not have an Azure Key Vault created yet, so let's create one and add some secrets to it.

    AKV_NAME=$(az keyvault create \
    --name akv-eshop$RANDOM \
    --resource-group $RG_NAME \
    --query name -o tsv)

    # Database server password
    az keyvault secret set \
    --vault-name $AKV_NAME \
    --name mssql-password \
    --value "@someThingComplicated1234"

    # Catalog database connection string
    az keyvault secret set \
    --vault-name $AKV_NAME \
    --name mssql-connection-catalog \
    --value "Server=db;Database=Microsoft.eShopOnWeb.CatalogDb;User Id=sa;Password=@someThingComplicated1234;TrustServerCertificate=True;"

    # Identity database connection string
    az keyvault secret set \
    --vault-name $AKV_NAME \
    --name mssql-connection-identity \
    --value "Server=db;Database=Microsoft.eShopOnWeb.Identity;User Id=sa;Password=@someThingComplicated1234;TrustServerCertificate=True;"

    Pods authentication using Azure Workload Identity

    In order for our Pods to retrieve secrets from Azure Key Vault, we'll need to set up a way for the Pod to authenticate against Azure AD. This can be achieved by implementing the new Azure Workload Identity feature of AKS.

    info

    At the time of this writing, the workload identity feature of AKS is in Preview.

    The workload identity feature within AKS allows us to leverage native Kubernetes resources and link a Kubernetes ServiceAccount to an Azure Managed Identity to authenticate against Azure AD.

    For the authentication flow, our Kubernetes cluster will act as an Open ID Connect (OIDC) issuer and will be able issue identity tokens to ServiceAccounts which will be assigned to our Pods.

    The Azure Managed Identity will be granted permission to access secrets in our Azure Key Vault and with the ServiceAccount being assigned to our Pods, they will be able to retrieve our secrets.

    For more information on how the authentication mechanism all works, check out this doc.

    To implement all this, start by enabling the new preview feature for AKS.

    az feature register \
    --namespace "Microsoft.ContainerService" \
    --name "EnableWorkloadIdentityPreview"
    caution

    This can take several minutes to complete.

    Check the status and ensure the state shows Regestered before moving forward.

    az feature show \
    --namespace "Microsoft.ContainerService" \
    --name "EnableWorkloadIdentityPreview"

    Update your AKS cluster to enable the workload identity feature and enable the OIDC issuer endpoint.

    az aks update \
    --name $AKS_NAME \
    --resource-group $RG_NAME \
    --enable-workload-identity \
    --enable-oidc-issuer

    Create an Azure Managed Identity and retrieve its client ID.

    MANAGED_IDENTITY_CLIENT_ID=$(az identity create \
    --name aks-workload-identity \
    --resource-group $RG_NAME \
    --subscription $(az account show --query id -o tsv) \
    --query 'clientId' -o tsv)

    Create the Kubernetes ServiceAccount.

    # Set namespace (this must align with the namespace that your app is deployed into)
    SERVICE_ACCOUNT_NAMESPACE=default

    # Set the service account name
    SERVICE_ACCOUNT_NAME=eshop-serviceaccount

    # Create the service account
    kubectl apply -f - <<EOF
    apiVersion: v1
    kind: ServiceAccount
    metadata:
    annotations:
    azure.workload.identity/client-id: ${MANAGED_IDENTITY_CLIENT_ID}
    labels:
    azure.workload.identity/use: "true"
    name: ${SERVICE_ACCOUNT_NAME}
    namespace: ${SERVICE_ACCOUNT_NAMESPACE}
    EOF
    info

    Note to enable this ServiceAccount to work with Azure Workload Identity, you must annotate the resource with azure.workload.identity/client-id, and add a label of azure.workload.identity/use: "true"

    That was a lot... Let's review what we just did.

    We have an Azure Managed Identity (object in Azure AD), an OIDC issuer URL (endpoint in our Kubernetes cluster), and a Kubernetes ServiceAccount.

    The next step is to "tie" these components together and establish a Federated Identity Credential so that Azure AD can trust authentication requests from your Kubernetes cluster.

    info

    This identity federation can be established between Azure AD any Kubernetes cluster; not just AKS 🤗

    To establish the federated credential, we'll need the OIDC issuer URL, and a subject which points to your Kubernetes ServiceAccount.

    # Get the OIDC issuer URL
    OIDC_ISSUER_URL=$(az aks show \
    --name $AKS_NAME \
    --resource-group $RG_NAME \
    --query "oidcIssuerProfile.issuerUrl" -o tsv)

    # Set the subject name using this format: `system:serviceaccount:<YOUR_SERVICE_ACCOUNT_NAMESPACE>:<YOUR_SERVICE_ACCOUNT_NAME>`
    SUBJECT=system:serviceaccount:$SERVICE_ACCOUNT_NAMESPACE:$SERVICE_ACCOUNT_NAME

    az identity federated-credential create \
    --name aks-federated-credential \
    --identity-name aks-workload-identity \
    --resource-group $RG_NAME \
    --issuer $OIDC_ISSUER_URL \
    --subject $SUBJECT

    With the authentication components set, we can now create a SecretProviderClass which includes details about the Azure Key Vault, the secrets to pull out from the vault, and identity used to access the vault.

    # Get the tenant id for the key vault
    TENANT_ID=$(az keyvault show \
    --name $AKV_NAME \
    --resource-group $RG_NAME \
    --query properties.tenantId -o tsv)

    # Create the secret provider for azure key vault
    kubectl apply -f - <<EOF
    apiVersion: secrets-store.csi.x-k8s.io/v1
    kind: SecretProviderClass
    metadata:
    name: eshop-azure-keyvault
    spec:
    provider: azure
    parameters:
    usePodIdentity: "false"
    useVMManagedIdentity: "false"
    clientID: "${MANAGED_IDENTITY_CLIENT_ID}"
    keyvaultName: "${AKV_NAME}"
    cloudName: ""
    objects: |
    array:
    - |
    objectName: mssql-password
    objectType: secret
    objectVersion: ""
    - |
    objectName: mssql-connection-catalog
    objectType: secret
    objectVersion: ""
    - |
    objectName: mssql-connection-identity
    objectType: secret
    objectVersion: ""
    tenantId: "${TENANT_ID}"
    secretObjects:
    - secretName: eshop-secrets
    type: Opaque
    data:
    - objectName: mssql-password
    key: mssql-password
    - objectName: mssql-connection-catalog
    key: mssql-connection-catalog
    - objectName: mssql-connection-identity
    key: mssql-connection-identity
    EOF

    Finally, lets grant the Azure Managed Identity permissions to retrieve secrets from the Azure Key Vault.

    az keyvault set-policy \
    --name $AKV_NAME \
    --secret-permissions get \
    --spn $MANAGED_IDENTITY_CLIENT_ID

    Re-package deployments

    Update your database deployment to load environment variables from our ConfigMap, attach the PVC and SecretProviderClass as volumes, mount the volumes into the Pod, and use the ServiceAccount to retrieve secrets.

    Additionally, you may notice the database Pod is set to use fsGroup:10001 as part of the securityContext. This is required as the MSSQL container runs using a non-root account called mssql and this account has the proper permissions to read/write data at the /var/opt/mssql mount path.

    kubectl apply -f - <<EOF
    apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: db
    labels:
    app: db
    spec:
    replicas: 1
    selector:
    matchLabels:
    app: db
    template:
    metadata:
    labels:
    app: db
    spec:
    securityContext:
    fsGroup: 10001
    serviceAccountName: ${SERVICE_ACCOUNT_NAME}
    containers:
    - name: db
    image: mcr.microsoft.com/mssql/server:2019-latest
    ports:
    - containerPort: 1433
    envFrom:
    - configMapRef:
    name: mssql-settings
    env:
    - name: MSSQL_SA_PASSWORD
    valueFrom:
    secretKeyRef:
    name: eshop-secrets
    key: mssql-password
    resources: {}
    volumeMounts:
    - name: mssqldb
    mountPath: /var/opt/mssql
    - name: eshop-secrets
    mountPath: "/mnt/secrets-store"
    readOnly: true
    volumes:
    - name: mssqldb
    persistentVolumeClaim:
    claimName: mssql-data
    - name: eshop-secrets
    csi:
    driver: secrets-store.csi.k8s.io
    readOnly: true
    volumeAttributes:
    secretProviderClass: eshop-azure-keyvault
    EOF

    We'll update the API and Web deployments in a similar way.

    # Set the image tag
    IMAGE_TAG=<YOUR_IMAGE_TAG>

    # API deployment
    kubectl apply -f - <<EOF
    apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: api
    labels:
    app: api
    spec:
    replicas: 1
    selector:
    matchLabels:
    app: api
    template:
    metadata:
    labels:
    app: api
    spec:
    serviceAccount: ${SERVICE_ACCOUNT_NAME}
    containers:
    - name: api
    image: ${ACR_NAME}.azurecr.io/api:${IMAGE_TAG}
    ports:
    - containerPort: 80
    envFrom:
    - configMapRef:
    name: aspnet-settings
    env:
    - name: ConnectionStrings__CatalogConnection
    valueFrom:
    secretKeyRef:
    name: eshop-secrets
    key: mssql-connection-catalog
    - name: ConnectionStrings__IdentityConnection
    valueFrom:
    secretKeyRef:
    name: eshop-secrets
    key: mssql-connection-identity
    resources: {}
    volumeMounts:
    - name: aspnet
    mountPath: ~/.aspnet/https:/root/.aspnet/https:ro
    - name: eshop-secrets
    mountPath: "/mnt/secrets-store"
    readOnly: true
    volumes:
    - name: aspnet
    persistentVolumeClaim:
    claimName: aspnet-data
    - name: eshop-secrets
    csi:
    driver: secrets-store.csi.k8s.io
    readOnly: true
    volumeAttributes:
    secretProviderClass: eshop-azure-keyvault
    EOF

    ## Web deployment
    kubectl apply -f - <<EOF
    apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: web
    labels:
    app: web
    spec:
    replicas: 1
    selector:
    matchLabels:
    app: web
    template:
    metadata:
    labels:
    app: web
    spec:
    serviceAccount: ${SERVICE_ACCOUNT_NAME}
    containers:
    - name: web
    image: ${ACR_NAME}.azurecr.io/web:${IMAGE_TAG}
    ports:
    - containerPort: 80
    envFrom:
    - configMapRef:
    name: aspnet-settings
    env:
    - name: ConnectionStrings__CatalogConnection
    valueFrom:
    secretKeyRef:
    name: eshop-secrets
    key: mssql-connection-catalog
    - name: ConnectionStrings__IdentityConnection
    valueFrom:
    secretKeyRef:
    name: eshop-secrets
    key: mssql-connection-identity
    resources: {}
    volumeMounts:
    - name: aspnet
    mountPath: ~/.aspnet/https:/root/.aspnet/https:ro
    - name: eshop-secrets
    mountPath: "/mnt/secrets-store"
    readOnly: true
    volumes:
    - name: aspnet
    persistentVolumeClaim:
    claimName: aspnet-data
    - name: eshop-secrets
    csi:
    driver: secrets-store.csi.k8s.io
    readOnly: true
    volumeAttributes:
    secretProviderClass: eshop-azure-keyvault
    EOF

    If all went well with your deployment updates, you should be able to browse to your website and buy some merchandise again 🥳

    echo "http://$(kubectl get service web -o jsonpath='{.status.loadBalancer.ingress[0].ip}')"

    Conclusion

    Although there is no visible changes on with our website, we've made a ton of changes on the Kubernetes backend to make this application much more secure and resilient.

    We used a combination of Kubernetes resources and AKS-specific features to achieve our goal of securing our secrets and ensuring data is not lost on container crashes and restarts.

    To learn more about the components we leveraged here today, checkout the resources and additional tutorials listed below.

    You can also find manifests with all the changes made in today's post in the Azure-Samples/eShopOnAKS repository.

    See you in the next post!

    Resources

    Take the Cloud Skills Challenge!

    Enroll in the Cloud Skills Challenge!

    Don't miss out on this opportunity to level up your skills and stay ahead of the curve in the world of cloud native.

    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/zero-to-hero/index.html b/cnny-2023/tags/zero-to-hero/index.html index 18c0b8cd71..a5ef8a64df 100644 --- a/cnny-2023/tags/zero-to-hero/index.html +++ b/cnny-2023/tags/zero-to-hero/index.html @@ -14,13 +14,13 @@ - - + +

    16 posts tagged with "zero-to-hero"

    View All Tags

    · 4 min read
    Cory Skimming
    Devanshi Joshi
    Steven Murawski
    Nitya Narasimhan

    Welcome to the Kick-off Post for #30DaysOfCloudNative - one of the core initiatives within #CloudNativeNewYear! Over the next four weeks, join us as we take you from fundamentals to functional usage of Cloud-native technologies, one blog post at a time! Read on to learn a little bit about this initiative and what you can expect to learn from this journey!

    What We'll Cover


    Cloud-native New Year

    Welcome to Week 01 of 🥳 #CloudNativeNewYear ! Today, we kick off a full month of content and activities to skill you up on all things Cloud-native on Azure with content, events, and community interactions! Read on to learn about what we have planned!


    Explore our initiatives

    We have a number of initiatives planned for the month to help you learn and skill up on relevant technologies. Click on the links to visit the relevant pages for each.

    We'll go into more details about #30DaysOfCloudNative in this post - don't forget to subscribe to the blog to get daily posts delivered directly to your preferred feed reader!


    Register for events!

    What are 3 things you can do today, to jumpstart your learning journey?


    #30DaysOfCloudNative

    #30DaysOfCloudNative is a month-long series of daily blog posts grouped into 4 themed weeks - taking you from core concepts to end-to-end solution examples in 30 days. Each article will be short (5-8 mins reading time) and provide exercises and resources to help you reinforce learnings and take next steps.

    This series focuses on the Cloud-native On Azure learning journey in four stages, each building on the previous week to help you skill up in a beginner-friendly way:

    We have a tentative weekly-themed roadmap for the topics we hope to cover and will keep this updated as we go with links to actual articles as they get published.

    Week 1: FOCUS ON CLOUD-NATIVE FUNDAMENTALS

    Here's a sneak peek at the week 1 schedule. We'll start with a broad review of cloud-native fundamentals and walkthrough the core concepts of microservices, containers and Kubernetes.

    • Jan 23: Learn Core Concepts for Cloud-native
    • Jan 24: Container 101
    • Jan 25: Adopting Microservices with Kubernetes
    • Jan 26: Kubernetes 101
    • Jan 27: Exploring your Cloud Native Options

    Let's Get Started!

    Now you know everything! We hope you are as excited as we are to dive into a full month of active learning and doing! Don't forget to subscribe for updates in your favorite feed reader! And look out for our first Cloud-native Fundamentals post on January 23rd!


    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/zero-to-hero/page/10/index.html b/cnny-2023/tags/zero-to-hero/page/10/index.html index 1f1648365c..3577d95f7a 100644 --- a/cnny-2023/tags/zero-to-hero/page/10/index.html +++ b/cnny-2023/tags/zero-to-hero/page/10/index.html @@ -14,15 +14,15 @@ - - + +

    16 posts tagged with "zero-to-hero"

    View All Tags

    · 14 min read
    Steven Murawski

    Welcome to Day 1 of Week 3 of #CloudNativeNewYear!

    The theme for this week is Bringing Your Application to Kubernetes. Last we talked about Kubernetes Fundamentals. Today we'll explore getting an existing application running in Kubernetes with a full pipeline in GitHub Actions.

    Ask the Experts Thursday, February 9th at 9 AM PST
    Friday, February 10th at 11 AM PST

    Watch the recorded demo and conversation about this week's topics.

    We were live on YouTube walking through today's (and the rest of this week's) demos. Join us Friday, February 10th and bring your questions!

    What We'll Cover

    • Our Application
    • Adding Some Infrastructure as Code
    • Building and Publishing a Container Image
    • Deploying to Kubernetes
    • Summary
    • Resources

    Our Application

    This week we'll be taking an exisiting application - something similar to a typical line of business application - and setting it up to run in Kubernetes. Over the course of the week, we'll address different concerns. Today we'll focus on updating our CI/CD process to handle standing up (or validating that we have) an Azure Kubernetes Service (AKS) environment, building and publishing container images for our web site and API server, and getting those services running in Kubernetes.

    The application we'll be starting with is eShopOnWeb. This application has a web site and API which are backed by a SQL Server instance. It's built in .NET 7, so it's cross-platform.

    info

    For the enterprising among you, you may notice that there are a number of different eShopOn* variants on GitHub, including eShopOnContainers. We aren't using that example as it's more of an end state than a starting place. Afterwards, feel free to check out that example as what this solution could look like as a series of microservices.

    Adding Some Infrastructure as Code

    Just like last week, we need to stand up an AKS environment. This week, however, rather than running commands in our own shell, we'll set up GitHub Actions to do that for us.

    There is a LOT of plumbing in this section, but once it's set up, it'll make our lives a lot easier. This section ensures that we have an environment to deploy our application into configured the way we want. We can easily extend this to accomodate multiple environments or add additional microservices with minimal new effort.

    Federated Identity

    Setting up a federated identity will allow us a more securable and auditable way of accessing Azure from GitHub Actions. For more about setting up a federated identity, Microsoft Learn has the details on connecting GitHub Actions to Azure.

    Here, we'll just walk through the setup of the identity and configure GitHub to use that idenity to deploy our AKS environment and interact with our Azure Container Registry.

    The examples will use PowerShell, but a Bash version of the setup commands is available in the week3/day1 branch.

    Prerequisites

    To follow along, you'll need:

    • a GitHub account
    • an Azure Subscription
    • the Azure CLI
    • and the Git CLI.

    You'll need to fork the source repository under your GitHub user or organization where you can manage secrets and GitHub Actions.

    It would be helpful to have the GitHub CLI, but it's not required.

    Set Up Some Defaults

    You will need to update one or more of the variables (your user or organization, what branch you want to work off of, and possibly the Azure AD application name if there is a conflict).

    # Replace the gitHubOrganizationName value
    # with the user or organization you forked
    # the repository under.

    $githubOrganizationName = 'Azure-Samples'
    $githubRepositoryName = 'eShopOnAKS'
    $branchName = 'week3/day1'
    $applicationName = 'cnny-week3-day1'

    Create an Azure AD Application

    Next, we need to create an Azure AD application.

    # Create an Azure AD application
    $aksDeploymentApplication = New-AzADApplication -DisplayName $applicationName

    Set Up Federation for that Azure AD Application

    And configure that application to allow federated credential requests from our GitHub repository for a particular branch.

    # Create a federated identity credential for the application
    New-AzADAppFederatedCredential `
    -Name $applicationName `
    -ApplicationObjectId $aksDeploymentApplication.Id `
    -Issuer 'https://token.actions.githubusercontent.com' `
    -Audience 'api://AzureADTokenExchange' `
    -Subject "repo:$($githubOrganizationName)/$($githubRepositoryName):ref:refs/heads/$branchName"

    Create a Service Principal for the Azure AD Application

    Once the application has been created, we need a service principal tied to that application. The service principal can be granted rights in Azure.

    # Create a service principal for the application
    New-AzADServicePrincipal -AppId $($aksDeploymentApplication.AppId)

    Give that Service Principal Rights to Azure Resources

    Because our Bicep deployment exists at the subscription level and we are creating role assignments, we need to give it Owner rights. If we changed the scope of the deployment to just a resource group, we could apply more scoped permissions.

    $azureContext = Get-AzContext
    New-AzRoleAssignment `
    -ApplicationId $($aksDeploymentApplication.AppId) `
    -RoleDefinitionName Owner `
    -Scope $azureContext.Subscription.Id

    Add Secrets to GitHub Repository

    If you have the GitHub CLI, you can use that right from your shell to set the secrets needed.

    gh secret set AZURE_CLIENT_ID --body $aksDeploymentApplication.AppId
    gh secret set AZURE_TENANT_ID --body $azureContext.Tenant.Id
    gh secret set AZURE_SUBSCRIPTION_ID --body $azureContext.Subscription.Id

    Otherwise, you can create them through the web interface like I did in the Learn Live event below.

    info

    It may look like the whole video will play, but it'll stop after configuring the secrets in GitHub (after about 9 minutes)

    The video shows creating the Azure AD application, service principals, and configuring the federated identity in Azure AD and GitHub.

    Creating a Bicep Deployment

    Resuable Workflows

    We'll create our Bicep deployment in a reusable workflows. What are they? The previous link has the documentation or the video below has my colleague Brandon Martinez and I talking about them.

    This workflow is basically the same deployment we did in last week's series, just in GitHub Actions.

    Start by creating a file called deploy_aks.yml in the .github/workflows directory with the below contents.

    name: deploy

    on:
    workflow_call:
    inputs:
    resourceGroupName:
    required: true
    type: string
    secrets:
    AZURE_CLIENT_ID:
    required: true
    AZURE_TENANT_ID:
    required: true
    AZURE_SUBSCRIPTION_ID:
    required: true
    outputs:
    containerRegistryName:
    description: Container Registry Name
    value: ${{ jobs.deploy.outputs.containerRegistryName }}
    containerRegistryUrl:
    description: Container Registry Login Url
    value: ${{ jobs.deploy.outputs.containerRegistryUrl }}
    resourceGroupName:
    description: Resource Group Name
    value: ${{ jobs.deploy.outputs.resourceGroupName }}
    aksName:
    description: Azure Kubernetes Service Cluster Name
    value: ${{ jobs.deploy.outputs.aksName }}

    permissions:
    id-token: write
    contents: read

    jobs:
    validate:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    - uses: azure/login@v1
    name: Sign in to Azure
    with:
    client-id: ${{ secrets.AZURE_CLIENT_ID }}
    tenant-id: ${{ secrets.AZURE_TENANT_ID }}
    subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
    - uses: azure/arm-deploy@v1
    name: Run preflight validation
    with:
    deploymentName: ${{ github.run_number }}
    scope: subscription
    region: eastus
    template: ./deploy/main.bicep
    parameters: >
    resourceGroup=${{ inputs.resourceGroupName }}
    deploymentMode: Validate

    deploy:
    needs: validate
    runs-on: ubuntu-latest
    outputs:
    containerRegistryName: ${{ steps.deploy.outputs.acr_name }}
    containerRegistryUrl: ${{ steps.deploy.outputs.acr_login_server_url }}
    resourceGroupName: ${{ steps.deploy.outputs.resource_group_name }}
    aksName: ${{ steps.deploy.outputs.aks_name }}
    steps:
    - uses: actions/checkout@v2
    - uses: azure/login@v1
    name: Sign in to Azure
    with:
    client-id: ${{ secrets.AZURE_CLIENT_ID }}
    tenant-id: ${{ secrets.AZURE_TENANT_ID }}
    subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
    - uses: azure/arm-deploy@v1
    id: deploy
    name: Deploy Bicep file
    with:
    failOnStdErr: false
    deploymentName: ${{ github.run_number }}
    scope: subscription
    region: eastus
    template: ./deploy/main.bicep
    parameters: >
    resourceGroup=${{ inputs.resourceGroupName }}

    Adding the Bicep Deployment

    Once we have the Bicep deployment workflow, we can add it to the primary build definition in .github/workflows/dotnetcore.yml

    Permissions

    First, we need to add a permissions block to let the workflow request our Azure AD token. This can go towards the top of the YAML file (I started it on line 5).

    permissions:
    id-token: write
    contents: read

    Deploy AKS Job

    Next, we'll add a reference to our reusable workflow. This will go after the build job.

      deploy_aks:
    needs: [build]
    uses: ./.github/workflows/deploy_aks.yml
    with:
    resourceGroupName: 'cnny-week3'
    secrets:
    AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
    AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
    AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }}

    Building and Publishing a Container Image

    Now that we have our target environment in place and an Azure Container Registry, we can build and publish our container images.

    Add a Reusable Workflow

    First, we'll create a new file for our reusable workflow at .github/workflows/publish_container_image.yml.

    We'll start the file with a name, the parameters it needs to run, and the permissions requirements for the federated identity request.

    name: Publish Container Images

    on:
    workflow_call:
    inputs:
    containerRegistryName:
    required: true
    type: string
    containerRegistryUrl:
    required: true
    type: string
    githubSha:
    required: true
    type: string
    secrets:
    AZURE_CLIENT_ID:
    required: true
    AZURE_TENANT_ID:
    required: true
    AZURE_SUBSCRIPTION_ID:
    required: true

    permissions:
    id-token: write
    contents: read

    Build the Container Images

    Our next step is to build the two container images we'll need for the application, the website and the API. We'll build the container images on our build worker and tag it with the git SHA, so there'll be a direct tie between the point in time in our codebase and the container images that represent it.

    jobs:
    publish_container_image:
    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v2
    - name: docker build
    run: |
    docker build . -f src/Web/Dockerfile -t ${{ inputs.containerRegistryUrl }}/web:${{ inputs.githubSha }}
    docker build . -f src/PublicApi/Dockerfile -t ${{ inputs.containerRegistryUrl }}/api:${{ inputs.githubSha}}

    Scan the Container Images

    Before we publish those container images, we'll scan them for vulnerabilities and best practice violations. We can add these two steps (one scan for each image).

        - name: scan web container image
    uses: Azure/container-scan@v0
    with:
    image-name: ${{ inputs.containerRegistryUrl }}/web:${{ inputs.githubSha}}
    - name: scan api container image
    uses: Azure/container-scan@v0
    with:
    image-name: ${{ inputs.containerRegistryUrl }}/web:${{ inputs.githubSha}}

    The container images provided have a few items that'll be found. We can create an allowed list at .github/containerscan/allowedlist.yaml to define vulnerabilities or best practice violations that we'll explictly allow to not fail our build.

    general:
    vulnerabilities:
    - CVE-2022-29458
    - CVE-2022-3715
    - CVE-2022-1304
    - CVE-2021-33560
    - CVE-2020-16156
    - CVE-2019-8457
    - CVE-2018-8292
    bestPracticeViolations:
    - CIS-DI-0001
    - CIS-DI-0005
    - CIS-DI-0006
    - CIS-DI-0008

    Publish the Container Images

    Finally, we'll log in to Azure, then log in to our Azure Container Registry, and push our images.

        - uses: azure/login@v1
    name: Sign in to Azure
    with:
    client-id: ${{ secrets.AZURE_CLIENT_ID }}
    tenant-id: ${{ secrets.AZURE_TENANT_ID }}
    subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
    - name: acr login
    run: az acr login --name ${{ inputs.containerRegistryName }}
    - name: docker push
    run: |
    docker push ${{ inputs.containerRegistryUrl }}/web:${{ inputs.githubSha}}
    docker push ${{ inputs.containerRegistryUrl }}/api:${{ inputs.githubSha}}

    Update the Build With the Image Build and Publish

    Now that we have our reusable workflow to create and publish our container images, we can include that in our primary build defnition at .github/workflows/dotnetcore.yml.

      publish_container_image:
    needs: [deploy_aks]
    uses: ./.github/workflows/publish_container_image.yml
    with:
    containerRegistryName: ${{ needs.deploy_aks.outputs.containerRegistryName }}
    containerRegistryUrl: ${{ needs.deploy_aks.outputs.containerRegistryUrl }}
    githubSha: ${{ github.sha }}
    secrets:
    AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
    AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
    AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }}

    Deploying to Kubernetes

    Finally, we've gotten enough set up that a commit to the target branch will:

    • build and test our application code
    • set up (or validate) our AKS and ACR environment
    • and create, scan, and publish our container images to ACR

    Our last step will be to deploy our application to Kubernetes. We'll use the basic building blocks we worked with last week, deployments and services.

    Starting the Reusable Workflow to Deploy to AKS

    We'll start our workflow with our parameters that we need, as well as the permissions to access the token to log in to Azure.

    We'll check out our code, then log in to Azure, and use the az CLI to get credentials for our AKS cluster.

    name: deploy_to_aks

    on:
    workflow_call:
    inputs:
    aksName:
    required: true
    type: string
    resourceGroupName:
    required: true
    type: string
    containerRegistryUrl:
    required: true
    type: string
    githubSha:
    required: true
    type: string
    secrets:
    AZURE_CLIENT_ID:
    required: true
    AZURE_TENANT_ID:
    required: true
    AZURE_SUBSCRIPTION_ID:
    required: true

    permissions:
    id-token: write
    contents: read

    jobs:
    deploy:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    - uses: azure/login@v1
    name: Sign in to Azure
    with:
    client-id: ${{ secrets.AZURE_CLIENT_ID }}
    tenant-id: ${{ secrets.AZURE_TENANT_ID }}
    subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
    - name: Get AKS Credentials
    run: |
    az aks get-credentials --resource-group ${{ inputs.resourceGroupName }} --name ${{ inputs.aksName }}

    Edit the Deployment For Our Current Image Tag

    Let's add the Kubernetes manifests to our repo. This post is long enough, so you can find the content for the manifests folder in the manifests folder in the source repo under the week3/day1 branch.

    tip

    If you only forked the main branch of the source repo, you can easily get the updated manifests by using the following git commands:

    git remote add upstream https://github.com/Azure-Samples/eShopOnAks
    git fetch upstream week3/day1
    git checkout upstream/week3/day1 manifests

    This will make the week3/day1 branch available locally and then we can update the manifests directory to match the state of that branch.

    The deployments and the service defintions should be familiar from last week's content (but not the same). This week, however, there's a new file in the manifests - ./manifests/kustomization.yaml

    This file helps us more dynamically edit our kubernetes manifests and support is baked right in to the kubectl command.

    Kustomize Definition

    Kustomize allows us to specify specific resource manifests and areas of that manifest to replace. We've put some placeholders in our file as well, so we can replace those for each run of our CI/CD system.

    In ./manifests/kustomization.yaml you will see:

    resources:
    - deployment-api.yaml
    - deployment-web.yaml

    # Change the image name and version
    images:
    - name: notavalidregistry.azurecr.io/api:v0.1.0
    newName: <YOUR_ACR_SERVER>/api
    newTag: <YOUR_IMAGE_TAG>
    - name: notavalidregistry.azurecr.io/web:v0.1.0
    newName: <YOUR_ACR_SERVER>/web
    newTag: <YOUR_IMAGE_TAG>

    Replacing Values in our Build

    Now, we encounter a little problem - our deployment files need to know the tag and ACR server. We can do a bit of sed magic to edit the file on the fly.

    In .github/workflows/deploy_to_aks.yml, we'll add:

          - name: replace_placeholders_with_current_run
    run: |
    sed -i "s/<YOUR_ACR_SERVER>/${{ inputs.containerRegistryUrl }}/g" ./manifests/kustomization.yaml
    sed -i "s/<YOUR_IMAGE_TAG>/${{ inputs.githubSha }}/g" ./manifests/kustomization.yaml

    Deploying the Manifests

    We have our manifests in place and our kustomization.yaml file (with commands to update it at runtime) ready to go, we can deploy our manifests.

    First, we'll deploy our database (deployment and service). Next, we'll use the -k parameter on kubectl to tell it to look for a kustomize configuration, transform the requested manifests and apply those. Finally, we apply the service defintions for the web and API deployments.

            run: |
    kubectl apply -f ./manifests/deployment-db.yaml \
    -f ./manifests/service-db.yaml
    kubectl apply -k ./manifests
    kubectl apply -f ./manifests/service-api.yaml \
    -f ./manifests/service-web.yaml

    Summary

    We've covered a lot of ground in today's post. We set up federated credentials with GitHub. Then we added reusable workflows to deploy an AKS environment and build/scan/publish our container images, and then to deploy them into our AKS environment.

    This sets us up to start making changes to our application and Kubernetes configuration and have those changes automatically validated and deployed by our CI/CD system. Tomorrow, we'll look at updating our application environment with runtime configuration, persistent storage, and more.

    Resources

    Take the Cloud Skills Challenge!

    Enroll in the Cloud Skills Challenge!

    Don't miss out on this opportunity to level up your skills and stay ahead of the curve in the world of cloud native.

    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/zero-to-hero/page/11/index.html b/cnny-2023/tags/zero-to-hero/page/11/index.html index f325ae9bff..20718dc5b7 100644 --- a/cnny-2023/tags/zero-to-hero/page/11/index.html +++ b/cnny-2023/tags/zero-to-hero/page/11/index.html @@ -14,13 +14,13 @@ - - + +

    16 posts tagged with "zero-to-hero"

    View All Tags

    · 9 min read
    Steven Murawski

    Welcome to Day 4 of Week 3 of #CloudNativeNewYear!

    The theme for this week is Bringing Your Application to Kubernetes. Yesterday we exposed the eShopOnWeb app so that customers can reach it over the internet using a custom domain name and TLS. Today we'll explore the topic of debugging and instrumentation.

    Ask the Experts Thursday, February 9th at 9 AM PST
    Friday, February 10th at 11 AM PST

    Watch the recorded demo and conversation about this week's topics.

    We were live on YouTube walking through today's (and the rest of this week's) demos. Join us Friday, February 10th and bring your questions!

    What We'll Cover

    • Debugging
    • Bridge To Kubernetes
    • Instrumentation
    • Resources: For self-study!

    Debugging

    Debugging applications in a Kubernetes cluster can be challenging for several reasons:

    • Complexity: Kubernetes is a complex system with many moving parts, including pods, nodes, services, and config maps, all of which can interact in unexpected ways and cause issues.
    • Distributed Environment: Applications running in a Kubernetes cluster are often distributed across multiple nodes, which makes it harder to determine the root cause of an issue.
    • Logging and Monitoring: Debugging an application in a Kubernetes cluster requires access to logs and performance metrics, which can be difficult to obtain in a large and dynamic environment.
    • Resource Management: Kubernetes manages resources such as CPU and memory, which can impact the performance and behavior of applications. Debugging resource-related issues requires a deep understanding of the Kubernetes resource model and the underlying infrastructure.
    • Dynamic Nature: Kubernetes is designed to be dynamic, with the ability to add and remove resources as needed. This dynamic nature can make it difficult to reproduce issues and debug problems.

    However, there are many tools and practices that can help make debugging applications in a Kubernetes cluster easier, such as using centralized logging, monitoring, and tracing solutions, and following best practices for managing resources and deployment configurations.

    There's also another great tool in our toolbox - Bridge to Kubernetes.

    Bridge to Kubernetes

    Bridge to Kubernetes is a great tool for microservice development and debugging applications without having to locally replicate all the required microservices.

    Bridge to Kubernetes works with Visual Studio or Visual Studio Code.

    We'll walk through using it with Visual Studio Code.

    Connecting Bridge to Kubernetes to Our Cluster

    Ensure your AKS cluster is the default for kubectl

    If you've recently spun up a new AKS cluster or you have been working with a different cluster, you may need to change what cluster credentials you have configured.

    If it's a new cluster, we can use:

    RESOURCE_GROUP=<YOUR RESOURCE GROUP NAME>
    CLUSTER_NAME=<YOUR AKS CLUSTER NAME>
    az aks get-credentials az aks get-credentials --resource-group $RESOURCE_GROUP --name $CLUSTER_NAME

    Open the command palette

    Open the command palette and find Bridge to Kubernetes: Configure. You may need to start typing the name to get it to show up.

    The command palette for Visual Studio Code is open and the first item is Bridge to Kubernetes: Configure

    Pick the service you want to debug

    Bridge to Kubernetes will redirect a service for you. Pick the service you want to redirect, in this case we'll pick web.

    Selecting the `web` service to redirect in Visual Studio Code

    Identify the port your application runs on

    Next, we'll be prompted to identify what port our application will run on locally. For this application it'll be 5001, but that's just specific to this application (and the default for ASP.NET 7, I believe).

    Setting port 5001 as the port to redirect to the `web` Kubernetes service in Visual Studio Code

    Pick a debug configuration to extend

    Bridge to Kubernetes has a couple of ways to run - it can inject it's setup and teardown to your existing debug configurations. We'll pick .NET Core Launch (web).

    Telling Bridge to Kubernetes to use the .NET Core Launch (web) debug configuration in Visual Studio Code

    Forward Traffic for All Requests

    The last prompt you'll get in the configuration is about how you want Bridge to Kubernetes to handle re-routing traffic. The default is that all requests into the service will get your local version.

    You can also redirect specific traffic. Bridge to Kubernetes will set up a subdomain and route specific traffic to your local service, while allowing other traffic to the deployed service.

    Allowing the launch of Endpoint Manager on Windows

    Using Bridge to Kubernetes to Debug Our Service

    Now that we've configured Bridge to Kubernetes, we see that tasks and a new launch configuration have been added.

    Added to .vscode/tasks.json:

            {
    "label": "bridge-to-kubernetes.resource",
    "type": "bridge-to-kubernetes.resource",
    "resource": "web",
    "resourceType": "service",
    "ports": [
    5001
    ],
    "targetCluster": "aks1",
    "targetNamespace": "default",
    "useKubernetesServiceEnvironmentVariables": false
    },
    {
    "label": "bridge-to-kubernetes.compound",
    "dependsOn": [
    "bridge-to-kubernetes.resource",
    "build"
    ],
    "dependsOrder": "sequence"
    }

    And added to .vscode/launch.json:

    {
    "name": ".NET Core Launch (web) with Kubernetes",
    "type": "coreclr",
    "request": "launch",
    "preLaunchTask": "bridge-to-kubernetes.compound",
    "program": "${workspaceFolder}/src/Web/bin/Debug/net7.0/Web.dll",
    "args": [],
    "cwd": "${workspaceFolder}/src/Web",
    "stopAtEntry": false,
    "env": {
    "ASPNETCORE_ENVIRONMENT": "Development",
    "ASPNETCORE_URLS": "http://+:5001"
    },
    "sourceFileMap": {
    "/Views": "${workspaceFolder}/Views"
    }
    }

    Launch the debug configuration

    We can start the process with the .NET Core Launch (web) with Kubernetes launch configuration in the Debug pane in Visual Studio Code.

    Launch the `.NET Core Launch (web) with Kubernetes` from the Debug pane in Visual Studio Code

    Enable the Endpoint Manager

    Part of this process includes a local service to help manage the traffic routing and your hosts file. This will require admin or sudo privileges. On Windows, you'll get a prompt like:

    Prompt to launch the endpoint manager.

    Access your Kubernetes cluster "locally"

    Bridge to Kubernetes will set up a tunnel (thanks to port forwarding) to your local workstation and create local endpoints for the other Kubernetes hosted services in your cluster, as well as pretending to be a pod in that cluster (for the application you are debugging).

    Output from Bridge To Kubernetes setup task.

    After making the connection to your Kubernetes cluster, the launch configuration will continue. In this case, we'll make a debug build of the application and attach the debugger. (This process may cause the terminal in VS Code to scroll with build output. You can find the Bridge to Kubernetes output with the local IP addresses and ports in the Output pane for Bridge to Kubernetes.)

    You can set breakpoints, use your debug console, set watches, run tests against your local version of the service.

    Exploring the Running Application Environment

    One of the cool things that Bridge to Kubernetes does for our debugging experience is bring the environment configuration that our deployed pod would inherit. When we launch our app, it'll see configuration and secrets that we'd expect our pod to be running with.

    To test this, we'll set a breakpoint in our application's start up to see what SQL Server is being used. We'll set a breakpoint at src/Infrastructure/Dependencies.cs on line 32.

    Then, we will start debugging the application with Bridge to Kubernetes. When it hits the breakpoint, we'll open the Debug pane and type configuration.GetConnectionString("CatalogConnection").

    When we run locally (not with Bridge to Kubernetes), we'd see:

    configuration.GetConnectionString("CatalogConnection")
    "Server=(localdb)\\mssqllocaldb;Integrated Security=true;Initial Catalog=Microsoft.eShopOnWeb.CatalogDb;"

    But, with Bridge to Kubernetes we see something more like (yours will vary based on the password ):

    configuration.GetConnectionString("CatalogConnection")
    "Server=db;Database=Microsoft.eShopOnWeb.CatalogDb;User Id=sa;Password=*****************;TrustServerCertificate=True;"

    Debugging our local application connected to Kubernetes.

    We can see that the database server configured is based on our db service and the password is pulled from our secret in Azure KeyVault (via AKS).

    This helps us run our local application just like it was actually in our cluster.

    Going Further

    Bridge to Kubernetes also supports more advanced scenarios and, as you need to start routing traffic around inside your cluster, may require you to modify your application to pass along a kubernetes-route-as header to help ensure that traffic for your debugging workloads is properly handled. The docs go into much greater detail about that.

    Instrumentation

    Now that we've figured out our debugging story, we'll need to ensure that we have the right context clues to find where we need to debug or to give us a better idea of how well our microservices are running.

    Logging and Tracing

    Logging and tracing become even more critical in Kubernetes, where your application could be running in a number of pods across different nodes. When you have an issue, in addition to the normal application data, you'll want to know what pod and what node had the issue, what the state of those resources were (were you resource constrained or were shared resources unavailable?), and if autoscaling is enabled, you'll want to know if a scale event has been triggered. There are a multitude of other concerns based on your application and the environment you maintain.

    Given these informational needs, it's crucial to revisit your existing logging and instrumentation. Most frameworks and languages have extensible logging, tracing, and instrumentation libraries that you can iteratively add information to, such as pod and node states, and ensuring that requests can be traced across your microservices. This will pay you back time and time again when you have to troubleshoot issues in your existing environment.

    Centralized Logging

    To enhance the troubleshooting process further, it's important to implement centralized logging to consolidate logs from all your microservices into a single location. This makes it easier to search and analyze logs when you're troubleshooting an issue.

    Automated Alerting

    Additionally, implementing automated alerting, such as sending notifications when specific conditions occur in the logs, can help you detect issues before they escalate.

    End to end Visibility

    End-to-end visibility is also essential in understanding the flow of requests and responses between microservices in a distributed system. With end-to-end visibility, you can quickly identify bottlenecks and slowdowns in the system, helping you to resolve issues more efficiently.

    Resources

    Take the Cloud Skills Challenge!

    Enroll in the Cloud Skills Challenge!

    Don't miss out on this opportunity to level up your skills and stay ahead of the curve in the world of cloud native.

    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/zero-to-hero/page/12/index.html b/cnny-2023/tags/zero-to-hero/page/12/index.html index 5bba3feebb..54d68923ff 100644 --- a/cnny-2023/tags/zero-to-hero/page/12/index.html +++ b/cnny-2023/tags/zero-to-hero/page/12/index.html @@ -14,13 +14,13 @@ - - + +

    16 posts tagged with "zero-to-hero"

    View All Tags

    · 7 min read
    Nitya Narasimhan

    Welcome to Week 4 of #CloudNativeNewYear!

    This week we'll go further with Cloud-native by exploring advanced topics and best practices for the Cloud-native practitioner. We'll start with an exploration of Serverless Container Options - ranging from managed services to Azure Kubernetes Service (AKS) and Azure Container Apps (ACA), to options that allow more granular control!

    What We'll Cover

    • The Azure Compute Landscape
    • Serverless Compute on Azure
    • Comparing Container Options On Azure
    • Other Considerations
    • Exercise: Try this yourself!
    • Resources: For self-study!


    We started this series with an introduction to core concepts:

    • In Containers 101, we learned why containerization matters. Think portability, isolation, scalability, resource-efficiency and cost-effectiveness. But not all apps can be containerized.
    • In Kubernetes 101, we learned how orchestration works. Think systems to automate container deployment, scaling, and management. But using Kubernetes directly can be complex.
    • In Exploring Cloud Native Options we asked the real questions: can we containerize - and should we?. The first depends on app characteristics, the second on your requirements.

    For example:

    • Can we containerize? The answer might be no if your app has system or OS dependencies, requires access to low-level hardware, or maintains complex state across sessions.
    • Should we containerize? The answer might be yes if your app is microservices-based, is stateless by default, requires portability, or is a legaacy app that can benefit from container isolation.

    As with every technology adoption decision process, there are no clear yes/no answers - just tradeoffs that you need to evaluate based on your architecture and application requirements. In today's post, we try to look at this from two main perspectives:

    1. Should you go serverless? Think: managed services that let you focus on app, not infra.
    2. What Azure Compute should I use? Think: best fit for my architecture & technology choices.

    Azure Compute Landscape

    Let's answer the second question first by exploring all available compute options on Azure. The illustrated decision-flow below is my favorite ways to navigate the choices, with questions like:

    • Are you migrating an existing app or building a new one?
    • Can you app be containerized?
    • Does it use a specific technology (Spring Boot, Red Hat Openshift)?
    • Do you need access to the Kubernetes API?
    • What characterizes the workload? (event-driven, web app, microservices etc.)

    Read the docs to understand how your choices can be influenced by the hosting model (IaaS, PaaS, FaaS), supported features (Networking, DevOps, Scalability, Availability, Security), architectural styles (Microservices, Event-driven, High-Performance Compute, Task Automation,Web-Queue Worker) etc.

    Compute Choices

    Now that we know all available compute options, let's address the second question: why go serverless? and what are my serverless compute options on Azure?

    Azure Serverless Compute

    Serverless gets defined many ways, but from a compute perspective, we can focus on a few key characteristics that are key to influencing this decision:

    • managed services - focus on application, let cloud provider handle infrastructure.
    • pay for what you use - get cost-effective resource utilization, flexible pricing options.
    • autoscaling on demand - take advantage of built-in features like KEDA-compliant triggers.
    • use preferred languages - write code in Java, JS, C#, Python etc. (specifics based on service)
    • cloud-native architectures - can support event-driven solutions, APIs, Microservices, DevOps!

    So what are some of the key options for Serverless Compute on Azure? The article dives into serverless support for fully-managed end-to-end serverless solutions with comprehensive support for DevOps, DevTools, AI/ML, Database, Storage, Monitoring and Analytics integrations. But we'll just focus on the 4 categories of applications when we look at Compute!

    1. Serverless Containerized Microservices using Azure Container Apps. Code in your preferred language, exploit full Dapr support, scale easily with any KEDA-compliant trigger.
    2. Serverless Application Environments using Azure App Service. Suitable for hosting monolithic apps (vs. microservices) in a managed service, with built-in support for on-demand scaling.
    3. Serverless Kubernetes using Azure Kubernetes Service (AKS). Spin up pods inside container instances and deploy Kubernetes-based applications with built-in KEDA-compliant autoscaling.
    4. Serverless Functions using Azure Functions. Execute "code at the granularity of functions" in your preferred language, and scale on demand with event-driven compute.

    We'll talk about these, and other compute comparisons, at the end of the article. But let's start with the core option you might choose if you want a managed serverless compute solution with built-in features for delivering containerized microservices at scale. Hello, Azure Container Apps!.

    Azure Container Apps

    Azure Container Apps (ACA) became generally available in May 2022 - providing customers with the ability to run microservices and containerized applications on a serverless, consumption-based platform. The figure below showcases the different types of applications that can be built with ACA. Note that it comes with built-in KEDA-compliant autoscaling triggers, and other auto-scale criteria that may be better-suited to the type of application you are building.

    About ACA

    So far in the series, we've put the spotlight on Azure Kubernetes Service (AKS) - so you're probably asking yourself: How does ACA compare to AKS?. We're glad you asked. Check out our Go Cloud-native with Azure Container Apps post from the #ServerlessSeptember series last year for a deeper-dive, or review the figure below for the main comparison points.

    The key takeaway is this. Azure Container Apps (ACA) also runs on Kubernetes but abstracts away its complexity in a managed service offering that lets you get productive quickly without requiring detailed knowledge of Kubernetes workings or APIs. However, if you want full access and control over the Kubernetes API then go with Azure Kubernetes Service (AKS) instead.

    Comparison

    Other Container Options

    Azure Container Apps is the preferred Platform As a Service (PaaS) option for a fully-managed serverless solution on Azure that is purpose-built for cloud-native microservices-based application workloads. But - there are other options that may be suitable for your specific needs, from a requirements and tradeoffs perspective. Let's review them quickly:

    1. Azure Functions is the serverless Functions-as-a-Service (FaaS) option, as opposed to ACA which supports a PaaS approach. It's optimized for running event-driven applications built at the granularity of ephemeral functions that can be deployed as code or containers.
    2. Azure App Service provides fully managed hosting for web applications that may be deployed using code or containers. It can be integrated with other services including Azure Container Apps and Azure Functions. It's optimized for deploying traditional web apps.
    3. Azure Kubernetes Service provides a fully managed Kubernetes option capable of running any Kubernetes workload, with direct access to the Kubernetes API.
    4. Azure Container Instances provides a single pod of Hyper-V isolated containers on demand, making them more of a low-level "building block" option compared to ACA.

    Based on the technology choices you made for application development you may also have more specialized options you want to consider. For instance:

    1. Azure Spring Apps is ideal if you're running Spring Boot or Spring Cloud workloads on Azure,
    2. Azure Red Hat OpenShift is ideal for integrated Kubernetes-powered OpenShift on Azure.
    3. Azure Confidential Computing is ideal if you have data/code integrity and confidentiality needs.
    4. Kubernetes At The Edge is ideal for bare-metal options that extend compute to edge devices.

    This is just the tip of the iceberg in your decision-making journey - but hopefully, it gave you a good sense of the options and criteria that influences your final choices. Let's wrap this up with a look at self-study resources for skilling up further.

    Exercise

    Want to get hands on learning related to these technologies?

    TAKE THE CLOUD SKILLS CHALLENGE

    Register today and level up your skills by completing free learning modules, while competing with your peers for a place on the leaderboards!

    Resources

    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/zero-to-hero/page/13/index.html b/cnny-2023/tags/zero-to-hero/page/13/index.html index 7f6a240d33..36a7ecbeed 100644 --- a/cnny-2023/tags/zero-to-hero/page/13/index.html +++ b/cnny-2023/tags/zero-to-hero/page/13/index.html @@ -14,13 +14,13 @@ - - + +

    16 posts tagged with "zero-to-hero"

    View All Tags

    · 3 min read
    Cory Skimming

    It's the final week of #CloudNativeNewYear! This week we'll go further with Cloud-native by exploring advanced topics and best practices for the Cloud-native practitioner. In today's post, we will introduce you to the basics of the open-source project Draft and how it can be used to easily create and deploy applications to Kubernetes.

    It's not too late to sign up for and complete the Cloud Skills Challenge!

    What We'll Cover

    • What is Draft?
    • Draft basics
    • Demo: Developing to AKS with Draft
    • Resources


    What is Draft?

    Draft is an open-source tool that can be used to streamline the development and deployment of applications on Kubernetes clusters. It provides a simple and easy-to-use workflow for creating and deploying applications, making it easier for developers to focus on writing code and building features, rather than worrying about the underlying infrastructure. This is great for users who are just getting started with Kubernetes, or those who are just looking to simplify their experience.

    New to Kubernetes?

    Draft basics

    Draft streamlines Kubernetes development by taking a non-containerized application and generating the Dockerfiles, K8s manifests, Helm charts, and other artifacts associated with a containerized application. Draft can also create a GitHub Action workflow file to quickly build and deploy your application onto any Kubernetes cluster.

    1. 'draft create'': Create a new Draft project by simply running the 'draft create' command - this command will walk you through a series of questions on your application specification (such as the application language) and create a Dockerfile, Helm char, and Kubernetes
    2. 'draft generate-workflow'': Automatically build out a GitHub Action using the 'draft generate-workflow' command
    3. 'draft setup-gh'': If you are using Azure, use this command to automate the GitHub OIDC set up process to ensure that you will be able to deploy your application using your GitHub Action.

    At this point, you will have all the files needed to deploy your app onto a Kubernetes cluster (we told you it was easy!).

    You can also use the 'draft info' command if you are looking for information on supported languages and deployment types. Let's see it in action, shall we?


    Developing to AKS with Draft

    In this Microsoft Reactor session below, we'll briefly introduce Kubernetes and the Azure Kubernetes Service (AKS) and then demo how enable your applications for Kubernetes using the open-source tool Draft. We'll show how Draft can help you create the boilerplate code to containerize your applications and add routing and scaling behaviours.

    ##Conclusion

    Overall, Draft simplifies the process of building, deploying, and managing applications on Kubernetes, and can make the overall journey from code to Kubernetes significantly easier.


    Resources


    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/zero-to-hero/page/14/index.html b/cnny-2023/tags/zero-to-hero/page/14/index.html index ed1ae034a9..3215a056ee 100644 --- a/cnny-2023/tags/zero-to-hero/page/14/index.html +++ b/cnny-2023/tags/zero-to-hero/page/14/index.html @@ -14,14 +14,14 @@ - - + +

    16 posts tagged with "zero-to-hero"

    View All Tags

    · 7 min read
    Vinicius Apolinario

    Welcome to Day 3 of Week 4 of #CloudNativeNewYear!

    The theme for this week is going further with Cloud Native. Yesterday we talked about using Draft to accelerate your Kubernetes adoption. Today we'll explore the topic of Windows containers.

    What We'll Cover

    • Introduction
    • Windows containers overview
    • Windows base container images
    • Isolation
    • Exercise: Try this yourself!
    • Resources: For self-study!

    Introduction

    Windows containers were launched along with Windows Server 2016, and have evolved since. In its latest release, Windows Server 2022, Windows containers have reached a great level of maturity and allow for customers to run production grade workloads.

    While suitable for new developments, Windows containers also provide developers and operations with a different approach than Linux containers. It allows for existing Windows applications to be containerized with little or no code changes. It also allows for professionals that are more comfortable with the Windows platform and OS, to leverage their skill set, while taking advantage of the containers platform.

    Windows container overview

    In essence, Windows containers are very similar to Linux. Since Windows containers use the same foundation of Docker containers, you can expect that the same architecture applies - with the specific notes of the Windows OS. For example, when running a Windows container via Docker, you use the same commands, such as docker run. To pull a container image, you can use docker pull, just like on Linux. However, to run a Windows container, you also need a Windows container host. This requirement is there because, as you might remember, a container shares the OS kernel with its container host.

    On Kubernetes, Windows containers are supported since Windows Server 2019. Just like with Docker, you can manage Windows containers like any other resource on the Kubernetes ecosystem. A Windows node can be part of a Kubernetes cluster, allowing you to run Windows container based applications on services like Azure Kubernetes Service. To deploy an Windows application to a Windows pod in Kubernetes, you can author a YAML specification much like you would for Linux. The main difference is that you would point to an image that runs on Windows, and you need to specify a node selection tag to indicate said pod needs to run on a Windows node.

    Windows base container images

    On Windows containers, you will always use a base container image provided by Microsoft. This base container image contains the OS binaries for the container to run. This image can be as large as 3GB+, or small as ~300MB. The difference in the size is a consequence of the APIs and components available in each Windows container base container image. There are primarily, three images: Nano Server, Server Core, and Server.

    Nano Server is the smallest image, ranging around 300MB. It's a base container image for new developments and cloud-native scenarios. Applications need to target Nano Server as the Windows OS, so not all frameworks will work. For example, .Net works on Nano Server, but .Net Framework doesn't. Other third-party frameworks also work on Nano Server, such as Apache, NodeJS, Phyton, Tomcat, Java runtime, JBoss, Redis, among others.

    Server Core is a much larger base container image, ranging around 1.25GB. It's larger size is compensated by it's application compatibility. Simply put, any application that meets the requirements to be run on a Windows container, can be containerized with this image.

    The Server image builds on the Server Core one. It ranges around 3.1GB and has even greater application compatibility than the Server Core image. In addition to the traditional Windows APIs and components, this image allows for scenarios such as Machine Learning via DirectX with GPU access.

    The best image for your scenario is dependent on the requirements your application has on the Windows OS inside a container. However, there are some scenarios that are not supported at all on Windows containers - such as GUI or RDP dependent applications, some Windows Server infrastructure roles, such as Active Directory, among others.

    Isolation

    When running containers, the kernel of the container host is shared with the containers running on it. While extremely convenient, this poses a potential risk for multi-tenant scenarios. If one container is compromised and has access to the host, it could potentially compromise other containers in the same system.

    For enterprise customers running on-premises (or even in the cloud), this can be mitigated by using a VM as a container host and considering the VM itself a security boundary. However, if multiple workloads from different tenants need to share the same host, Windows containers offer another option: Hyper-V isolation. While the name Hyper-V is associated with VMs, its virtualization capabilities extend to other services, including containers. Hyper-V isolated containers run on a purpose built, extremely small, highly performant VM. However, you manage a container running with Hyper-V isolation the same way you do with a process isolated one. In fact, the only notable difference is that you need to append the --isolation=hyperv tag to the docker run command.

    Exercise

    Here are a few examples of how to use Windows containers:

    Run Windows containers via Docker on your machine

    To pull a Windows base container image:

    docker pull mcr.microsoft.com/windows/servercore:ltsc2022

    To run a basic IIS container:

    #This command will pull and start a IIS container. You can access it from http://<your local IP>:8080
    docker run -d -p 8080:80 mcr.microsoft.com/windows/servercore/iis:windowsservercore-ltsc2022

    Run the same IIS container with Hyper-V isolation

    #This command will pull and start a IIS container. You can access it from http://<your local IP>:8080
    docker run -d -p 8080:80 --isolation=hyperv mcr.microsoft.com/windows/servercore/iis:windowsservercore-ltsc2022

    To run a Windows container interactively:

    docker run -it mcr.microsoft.com/windows/servercore:ltsc2022 powershell

    Run Windows containers on Kubernetes

    To prepare an AKS cluster for Windows containers: Note: Replace the values on the example below with the ones from your environment.

    echo "Please enter the username to use as administrator credentials for Windows Server nodes on your cluster: " && read WINDOWS_USERNAME
    az aks create \
    --resource-group myResourceGroup \
    --name myAKSCluster \
    --node-count 2 \
    --generate-ssh-keys \
    --windows-admin-username $WINDOWS_USERNAME \
    --vm-set-type VirtualMachineScaleSets \
    --network-plugin azure

    To add a Windows node pool for Windows containers:

    az aks nodepool add \
    --resource-group myResourceGroup \
    --cluster-name myAKSCluster \
    --os-type Windows \
    --name npwin \
    --node-count 1

    Deploy a sample ASP.Net application to the AKS cluster above using the YAML file below:

    apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: sample
    labels:
    app: sample
    spec:
    replicas: 1
    template:
    metadata:
    name: sample
    labels:
    app: sample
    spec:
    nodeSelector:
    "kubernetes.io/os": windows
    containers:
    - name: sample
    image: mcr.microsoft.com/dotnet/framework/samples:aspnetapp
    resources:
    limits:
    cpu: 1
    memory: 800M
    ports:
    - containerPort: 80
    selector:
    matchLabels:
    app: sample
    ---
    apiVersion: v1
    kind: Service
    metadata:
    name: sample
    spec:
    type: LoadBalancer
    ports:
    - protocol: TCP
    port: 80
    selector:
    app: sample

    Save the file above and run the command below on your Kubernetes cluster:

    kubectl apply -f <filename> .

    Once deployed, you can access the application by getting the IP address of your service:

    kubectl get service

    Resources

    It's not too late to sign up for and complete the Cloud Skills Challenge!
    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/zero-to-hero/page/15/index.html b/cnny-2023/tags/zero-to-hero/page/15/index.html index bc56e1cb32..5edc1412a4 100644 --- a/cnny-2023/tags/zero-to-hero/page/15/index.html +++ b/cnny-2023/tags/zero-to-hero/page/15/index.html @@ -14,13 +14,13 @@ - - + +

    16 posts tagged with "zero-to-hero"

    View All Tags

    · 4 min read
    Jorge Arteiro

    Welcome to Day 4 of Week 4 of #CloudNativeNewYear!

    The theme for this week is going further with Cloud Native. Yesterday we talked about Windows Containers. Today we'll explore addons and extensions available to Azure Kubernetes Services (AKS).

    What We'll Cover

    • Introduction
    • Add-ons
    • Extensions
    • Add-ons vs Extensions
    • Resources

    Introduction

    Azure Kubernetes Service (AKS) is a fully managed container orchestration service that makes it easy to deploy and manage containerized applications on Azure. AKS offers a number of features and capabilities, including the ability to extend its supported functionality through the use of add-ons and extensions.

    There are also integrations available from open-source projects and third parties, but they are not covered by the AKS support policy.

    Add-ons

    Add-ons provide a supported way to extend AKS. Installation, configuration and lifecycle are managed by AKS following pre-determine updates rules.

    As an example, let's enable Container Insights with the monitoring addon. on an existing AKS cluster using az aks enable-addons --addons CLI command

    az aks enable-addons \
    --name MyManagedCluster \
    --resource-group MyResourceGroup \
    --addons monitoring

    or you can use az aks create --enable-addons when creating new clusters

    az aks create \
    --name MyManagedCluster \
    --resource-group MyResourceGroup \
    --enable-addons monitoring

    The current available add-ons are:

    1. http_application_routing - Configure ingress with automatic public DNS name creation. Only recommended for development.
    2. monitoring - Container Insights monitoring.
    3. virtual-node - CNCF virtual nodes open source project.
    4. azure-policy - Azure Policy for AKS.
    5. ingress-appgw - Application Gateway Ingress Controller (AGIC).
    6. open-service-mesh - CNCF Open Service Mesh project.
    7. azure-keyvault-secrets-provider - Azure Key Vault Secrets Provider for Secret Store CSI Driver.
    8. web_application_routing - Managed NGINX ingress Controller.
    9. keda - CNCF Event-driven autoscaling project.

    For more details, get the updated list of AKS Add-ons here

    Extensions

    Cluster Extensions uses Helm charts and integrates with Azure Resource Manager (ARM) to provide installation and lifecycle management of capabilities on top of AKS.

    Extensions can be auto upgraded using minor versions, but it requires extra management and configuration. Using Scope parameter, it can be installed on the whole cluster or per namespace.

    AKS Extensions requires an Azure CLI extension to be installed. To add or update this CLI extension use the following commands:

    az extension add --name k8s-extension

    and to update an existing extension

    az extension update --name k8s-extension

    There are only 3 available extensions:

    1. Dapr - CNCF Dapr project.
    2. Azure ML - Integrate Azure Machine Learning with AKS to train, inference and manage ML models.
    3. Flux (GitOps) - CNCF Flux project integrated with AKS to enable cluster configuration and application deployment using GitOps.

    As an example, you can install Azure ML using the following command:

    az k8s-extension create \
    --name aml-compute --extension-type Microsoft.AzureML.Kubernetes \
    --scope cluster --cluster-name <clusterName> \
    --resource-group <resourceGroupName> \
    --cluster-type managedClusters \
    --configuration-settings enableInference=True allowInsecureConnections=True

    For more details, get the updated list of AKS Extensions here

    Add-ons vs Extensions

    AKS Add-ons brings an advantage of been fully managed by AKS itself, and AKS Extensions are more flexible and configurable but requires extra level of management.

    Add-ons are part of the AKS resource provider in the Azure API, and AKS Extensions are a separate resource provider on the Azure API.

    Resources

    It's not too late to sign up for and complete the Cloud Skills Challenge!
    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/zero-to-hero/page/16/index.html b/cnny-2023/tags/zero-to-hero/page/16/index.html index f4e00eb596..cdd58978f7 100644 --- a/cnny-2023/tags/zero-to-hero/page/16/index.html +++ b/cnny-2023/tags/zero-to-hero/page/16/index.html @@ -14,13 +14,13 @@ - - + +

    16 posts tagged with "zero-to-hero"

    View All Tags

    · 6 min read
    Cory Skimming
    Steven Murawski
    Paul Yu
    Josh Duffney
    Nitya Narasimhan
    Vinicius Apolinario
    Jorge Arteiro
    Devanshi Joshi

    And that's a wrap on the inaugural #CloudNativeNewYear! Thank you for joining us to kick off the new year with this learning journey into cloud-native! In this final post of the 2023 celebration of all things cloud-native, we'll do two things:

    • Look Back - with a quick retrospective of what was covered.
    • Look Ahead - with resources and suggestions for how you can continue your skilling journey!

    We appreciate your time and attention and we hope you found this curated learning valuable. Feedback and suggestions are always welcome. From our entire team, we wish you good luck with the learning journey - now go build some apps and share your knowledge! 🎉


    What We'll Cover

    • Cloud-native fundamentals
    • Kubernetes fundamentals
    • Bringing your applications to Kubernetes
    • Go further with cloud-native
    • Resources to keep the celebration going!

    Week 1: Cloud-native Fundamentals

    In Week 1, we took a tour through the fundamentals of cloud-native technologies, including a walkthrough of the core concepts of containers, microservices, and Kubernetes.

    • Jan 23 - Cloud-native Fundamentals: The answers to life and all the universe - what is cloud-native? What makes an application cloud-native? What are the benefits? (yes, we all know it's 42, but hey, gotta start somewhere!)
    • Jan 24 - Containers 101: Containers are an essential component of cloud-native development. In this intro post, we cover how containers work and why they have become so popular.
    • Jan 25 - Kubernetes 101: Kuber-what-now? Learn the basics of Kubernetes and how it enables us to deploy and manage our applications effectively and consistently.
    A QUICKSTART GUIDE TO KUBERNETES CONCEPTS

    Missed it Live? Tune in to A Quickstart Guide to Kubernetes Concepts on demand, now!

    • Jan 26 - Microservices 101: What is a microservices architecture and how can we go about designing one?
    • Jan 27 - Exploring your Cloud Native Options: Cloud-native, while catchy, can be a very broad term. What technologies should you use? Learn some basic guidelines for when it is optimal to use different technologies for your project.

    Week 2: Kubernetes Fundamentals

    In Week 2, we took a deeper dive into the Fundamentals of Kubernetes. The posts and live demo from this week took us through how to build a simple application on Kubernetes, covering everything from deployment to networking and scaling. Note: for our samples and demo we have used Azure Kubernetes Service, but the principles apply to any Kubernetes!

    • Jan 30 - Pods and Deployments: how to use pods and deployments in Kubernetes.
    • Jan 31 - Services and Ingress: how to use services and ingress and a walk through the steps of making our containers accessible internally and externally!
    • Feb 1 - ConfigMaps and Secrets: how to of passing configuration and secrets to our applications in Kubernetes with ConfigMaps and Secrets.
    • Feb 2 - Volumes, Mounts, and Claims: how to use persistent storage on Kubernetes (and ensure your data can survive container restarts!).
    • Feb 3 - Scaling Pods and Nodes: how to scale pods and nodes in our Kubernetes cluster.
    ASK THE EXPERTS: AZURE KUBERNETES SERVICE

    Missed it Live? Tune in to Ask the Expert with Azure Kubernetes Service on demand, now!


    Week 3: Bringing your applications to Kubernetes

    So, you have learned how to build an application on Kubernetes. What about your existing applications? In Week 3, we explored how to take an existing application and set it up to run in Kubernetes:

    • Feb 6 - CI/CD: learn how to get an existing application running in Kubernetes with a full pipeline in GitHub Actions.
    • Feb 7 - Adapting Storage, Secrets, and Configuration: how to evaluate our sample application's configuration, storage, and networking requirements and implement using Kubernetes.
    • Feb 8 - Opening your Application with Ingress: how to expose the eShopOnWeb app so that customers can reach it over the internet using a custom domain name and TLS.
    • Feb 9 - Debugging and Instrumentation: how to debug and instrument your application now that it is on Kubernetes.
    • Feb 10 - CI/CD Secure Supply Chain: now that we have set up our application on Kubernetes, let's talk about container image signing and how to set up a secure supply change.

    Week 4: Go Further with Cloud-Native

    This week we have gone further with Cloud-native by exploring advanced topics and best practices for the Cloud-native practitioner.

    And today, February 17th, with this one post to rule (er, collect) them all!


    Keep the Learning Going!

    Learning is great, so why stop here? We have a host of great resources and samples for you to continue your cloud-native journey with Azure below:


    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/zero-to-hero/page/2/index.html b/cnny-2023/tags/zero-to-hero/page/2/index.html index 664e524622..efad439fd1 100644 --- a/cnny-2023/tags/zero-to-hero/page/2/index.html +++ b/cnny-2023/tags/zero-to-hero/page/2/index.html @@ -14,14 +14,14 @@ - - + +

    16 posts tagged with "zero-to-hero"

    View All Tags

    · 5 min read
    Cory Skimming

    Welcome to Week 1 of #CloudNativeNewYear!

    Cloud-native New Year

    You will often hear the term "cloud-native" when discussing modern application development, but even a quick online search will return a huge number of articles, tweets, and web pages with a variety of definitions. So, what does cloud-native actually mean? Also, what makes an application a cloud-native application versus a "regular" application?

    Today, we will address these questions and more as we kickstart our learning journey (and our new year!) with an introductory dive into the wonderful world of cloud-native.


    What We'll Cover

    • What is cloud-native?
    • What is a cloud-native application?
    • The benefits of cloud-native
    • The five pillars of cloud-native
    • Exercise: Take the Cloud Skills Challenge!

    1. What is cloud-native?

    The term "cloud-native" can seem pretty self-evident (yes, hello, native to the cloud?), and in a way, it is. While there are lots of definitions of cloud-native floating around, at it's core, cloud-native simply refers to a modern approach to building software that takes advantage of cloud services and environments. This includes using cloud-native technologies, such as containers, microservices, and serverless, and following best practices for deploying, scaling, and managing applications in a cloud environment.

    Official definition from the Cloud Native Computing Foundation:

    Cloud-native technologies empower organizations to build and run scalable applications in modern, dynamic environments such as public, private, and hybrid clouds. Containers, service meshes, microservices, immutable infrastructure, and declarative APIs exemplify this approach.

    These techniques enable loosely coupled systems that are resilient, manageable, and observable. Combined with robust automation, they allow engineers to make high-impact changes frequently and predictably with minimal toil. Source


    2. So, what exactly is a cloud-native application?

    Cloud-native applications are specifically designed to take advantage of the scalability, resiliency, and distributed nature of modern cloud infrastructure. But how does this differ from a "traditional" application?

    Traditional applications are generally been built, tested, and deployed as a single, monolithic unit. The monolithic nature of this type of architecture creates close dependencies between components. This complexity and interweaving only increases as an application grows and can make it difficult to evolve (not to mention troubleshoot) and challenging to operate over time.

    To contrast, in cloud-native architectures the application components are decomposed into loosely coupled services, rather than built and deployed as one block of code. This decomposition into multiple self-contained services enables teams to manage complexity and improve the speed, agility, and scale of software delivery. Many small parts enables teams to make targeted updates, deliver new features, and fix any issues without leading to broader service disruption.


    3. The benefits of cloud-native

    Cloud-native architectures can bring many benefits to an organization, including:

    1. Scalability: easily scale up or down based on demand, allowing organizations to adjust their resource usage and costs as needed.
    2. Flexibility: deploy and run on any cloud platform, and easily move between clouds and on-premises environments.
    3. High-availability: techniques such as redundancy, self-healing, and automatic failover help ensure that cloud-native applications are designed to be highly-available and fault tolerant.
    4. Reduced costs: take advantage of the pay-as-you-go model of cloud computing, reducing the need for expensive infrastructure investments.
    5. Improved security: tap in to cloud security features, such as encryption and identity management, to improve the security of the application.
    6. Increased agility: easily add new features or services to your applications to meet changing business needs and market demand.

    4. The pillars of cloud-native

    There are five areas that are generally cited as the core building blocks of cloud-native architecture:

    1. Microservices: Breaking down monolithic applications into smaller, independent, and loosely-coupled services that can be developed, deployed, and scaled independently.
    2. Containers: Packaging software in lightweight, portable, and self-sufficient containers that can run consistently across different environments.
    3. Automation: Using automation tools and DevOps processes to manage and operate the cloud-native infrastructure and applications, including deployment, scaling, monitoring, and self-healing.
    4. Service discovery: Using service discovery mechanisms, such as APIs & service meshes, to enable services to discover and communicate with each other.
    5. Observability: Collecting and analyzing data from the infrastructure and applications to understand and optimize the performance, behavior, and health of the system.

    These can (and should!) be used in combination to deliver cloud-native solutions that are highly scalable, flexible, and available.

    WHAT'S NEXT

    Stay tuned, as we will be diving deeper into these topics in the coming weeks:

    • Jan 24: Containers 101
    • Jan 25: Adopting Microservices with Kubernetes
    • Jan 26: Kubernetes 101
    • Jan 27: Exploring your Cloud-native Options

    Resources


    Don't forget to subscribe to the blog to get daily posts delivered directly to your favorite feed reader!


    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/zero-to-hero/page/3/index.html b/cnny-2023/tags/zero-to-hero/page/3/index.html index 03ac784f75..dee51bc226 100644 --- a/cnny-2023/tags/zero-to-hero/page/3/index.html +++ b/cnny-2023/tags/zero-to-hero/page/3/index.html @@ -14,14 +14,14 @@ - - + +

    16 posts tagged with "zero-to-hero"

    View All Tags

    · 4 min read
    Steven Murawski
    Paul Yu
    Josh Duffney

    Welcome to Day 2 of Week 1 of #CloudNativeNewYear!

    Today, we'll focus on building an understanding of containers.

    What We'll Cover

    • Introduction
    • How do Containers Work?
    • Why are Containers Becoming so Popular?
    • Conclusion
    • Resources
    • Learning Path

    REGISTER & LEARN: KUBERNETES 101

    Interested in a dive into Kubernetes and a chance to talk to experts?

    🎙: Join us Jan 26 @1pm PST by registering here

    Here's what you will learn:

    • Key concepts and core principles of Kubernetes.
    • How to deploy, scale and manage containerized workloads.
    • Live Demo of the concepts explained
    • How to get started with Azure Kubernetes Service for free.

    Start your free Azure Kubernetes Trial Today!!: aka.ms/TryAKS

    Introduction

    In the beginning, we deployed our applications onto physical servers. We only had a certain number of those servers, so often they hosted multiple applications. This led to some problems when those applications shared dependencies. Upgrading one application could break another application on the same server.

    Enter virtualization. Virtualization allowed us to run our applications in an isolated operating system instance. This removed much of the risk of updating shared dependencies. However, it increased our overhead since we had to run a full operating system for each application environment.

    To address the challenges created by virtualization, containerization was created to improve isolation without duplicating kernel level resources. Containers provide efficient and consistent deployment and runtime experiences for our applications and have become very popular as a way of packaging and distributing applications.

    How do Containers Work?

    Containers build on two capabilities in the Linux operating system, namespaces and cgroups. These constructs allow the operating system to provide isolation to a process or group of processes, keeping their access to filesystem resources separate and providing controls on resource utilization. This, combined with tooling to help package, deploy, and run container images has led to their popularity in today’s operating environment. This provides us our isolation without the overhead of additional operating system resources.

    When a container host is deployed on an operating system, it works at scheduling the access to the OS (operating systems) components. This is done by providing a logical isolated group that can contain processes for a given application, called a namespace. The container host then manages /schedules access from the namespace to the host OS. The container host then uses cgroups to allocate compute resources. Together, the container host with the help of cgroups and namespaces can schedule multiple applications to access host OS resources.

    Overall, this gives the illusion of virtualizing the host OS, where each application gets its own OS. In actuality, all the applications are running on the same operating system and sharing the same kernel as the container host.

    Containers are popular in the software development industry because they provide several benefits over traditional virtualization methods. Some of these benefits include:

    • Portability: Containers make it easy to move an application from one environment to another without having to worry about compatibility issues or missing dependencies.
    • Isolation: Containers provide a level of isolation between the application and the host system, which means that the application running in the container cannot access the host system's resources.
    • Scalability: Containers make it easy to scale an application up or down as needed, which is useful for applications that experience a lot of traffic or need to handle a lot of data.
    • Resource Efficiency: Containers are more resource-efficient than traditional virtualization methods because they don't require a full operating system to be running on each virtual machine.
    • Cost-Effective: Containers are more cost-effective than traditional virtualization methods because they don't require expensive hardware or licensing fees.

    Conclusion

    Containers are a powerful technology that allows developers to package and deploy applications in a portable and isolated environment. This technology is becoming increasingly popular in the world of software development and is being used by many companies and organizations to improve their application deployment and management processes. With the benefits of portability, isolation, scalability, resource efficiency, and cost-effectiveness, containers are definitely worth considering for your next application development project.

    Containerizing applications is a key step in modernizing them, and there are many other patterns that can be adopted to achieve cloud-native architectures, including using serverless platforms, Kubernetes, and implementing DevOps practices.

    Resources

    Learning Path

    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/zero-to-hero/page/4/index.html b/cnny-2023/tags/zero-to-hero/page/4/index.html index de7d20bef8..457054a074 100644 --- a/cnny-2023/tags/zero-to-hero/page/4/index.html +++ b/cnny-2023/tags/zero-to-hero/page/4/index.html @@ -14,14 +14,14 @@ - - + +

    16 posts tagged with "zero-to-hero"

    View All Tags

    · 3 min read
    Steven Murawski

    Welcome to Day 3 of Week 1 of #CloudNativeNewYear!

    This week we'll focus on what Kubernetes is.

    What We'll Cover

    • Introduction
    • What is Kubernetes? (Video)
    • How does Kubernetes Work? (Video)
    • Conclusion


    REGISTER & LEARN: KUBERNETES 101

    Interested in a dive into Kubernetes and a chance to talk to experts?

    🎙: Join us Jan 26 @1pm PST by registering here

    Here's what you will learn:

    • Key concepts and core principles of Kubernetes.
    • How to deploy, scale and manage containerized workloads.
    • Live Demo of the concepts explained
    • How to get started with Azure Kubernetes Service for free.

    Start your free Azure Kubernetes Trial Today!!: aka.ms/TryAKS

    Introduction

    Kubernetes is an open source container orchestration engine that can help with automated deployment, scaling, and management of our applications.

    Kubernetes takes physical (or virtual) resources and provides a consistent API over them, bringing a consistency to the management and runtime experience for our applications. Kubernetes provides us with a number of capabilities such as:

    • Container scheduling
    • Service discovery and load balancing
    • Storage orchestration
    • Automated rollouts and rollbacks
    • Automatic bin packing
    • Self-healing
    • Secret and configuration management

    We'll learn more about most of these topics as we progress through Cloud Native New Year.

    What is Kubernetes?

    Let's hear from Brendan Burns, one of the founders of Kubernetes as to what Kubernetes actually is.

    How does Kubernetes Work?

    And Brendan shares a bit more with us about how Kubernetes works.

    Conclusion

    Kubernetes allows us to deploy and manage our applications effectively and consistently.

    By providing a consistent API across many of the concerns our applications have, like load balancing, networking, storage, and compute, Kubernetes improves both our ability to build and ship new software.

    There are standards for the applications to depend on for resources needed. Deployments, metrics, and logs are provided in a standardized fashion allowing more effecient operations across our application environments.

    And since Kubernetes is an open source platform, it can be found in just about every type of operating environment - cloud, virtual machines, physical hardware, shared data centers, even small devices like Rasberry Pi's!

    Want to learn more? Join us for a webinar on Kubernetes Concepts (or catch the playback) on Thursday, January 26th at 1 PM PST and watch for the rest of this series right here!

    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/zero-to-hero/page/5/index.html b/cnny-2023/tags/zero-to-hero/page/5/index.html index d31cd690ee..778319a5d2 100644 --- a/cnny-2023/tags/zero-to-hero/page/5/index.html +++ b/cnny-2023/tags/zero-to-hero/page/5/index.html @@ -14,13 +14,13 @@ - - + +

    16 posts tagged with "zero-to-hero"

    View All Tags

    · 6 min read
    Josh Duffney

    Welcome to Day 4 of Week 1 of #CloudNativeNewYear!

    This week we'll focus on advanced topics and best practices for Cloud-Native practitioners, kicking off with this post on Serverless Container Options with Azure. We'll look at technologies, tools and best practices that range from managed services like Azure Kubernetes Service, to options allowing finer granularity of control and oversight.

    What We'll Cover

    • What is Microservice Architecture?
    • How do you design a Microservice?
    • What challenges do Microservices introduce?
    • Conclusion
    • Resources


    Microservices are a modern way of designing and building software that increases deployment velocity by decomposing an application into small autonomous services that can be deployed independently.

    By deploying loosely coupled microservices your applications can be developed, deployed, and scaled independently. Because each service is independent, it can be updated or replaced without having to worry about the impact on the rest of the application. This means that if a bug is found in one service, it can be fixed without having to redeploy the entire application. All of which gives an organization the ability to deliver value to their customers faster.

    In this article, we will explore the basics of microservices architecture, its benefits and challenges, and how it can help improve the development, deployment, and maintenance of software applications.

    What is Microservice Architecture?

    Before explaining what Microservice architecture is, it’s important to understand what problems microservices aim to address.

    Traditional software development is centered around building monolithic applications. Monolithic applications are built as a single, large codebase. Meaning your code is tightly coupled causing the monolithic app to suffer from the following:

    Too much Complexity: Monolithic applications can become complex and difficult to understand and maintain as they grow. This can make it hard to identify and fix bugs and add new features.

    Difficult to Scale: Monolithic applications can be difficult to scale as they often have a single point of failure, which can cause the whole application to crash if a service fails.

    Slow Deployment: Deploying a monolithic application can be risky and time-consuming, as a small change in one part of the codebase can affect the entire application.

    Microservice architecture (often called microservices) is an architecture style that addresses the challenges created by Monolithic applications. Microservices architecture is a way of designing and building software applications as a collection of small, independent services that communicate with each other through APIs. This allows for faster development and deployment cycles, as well as easier scaling and maintenance than is possible with a monolithic application.

    How do you design a Microservice?

    Building applications with Microservices architecture requires a different approach. Microservices architecture focuses on business capabilities rather than technical layers, such as data access or messaging. Doing so requires that you shift your focus away from the technical stack and model your applications based upon the various domains that exist within the business.

    Domain-driven design (DDD) is a way to design software by focusing on the business needs. You can use Domain-driven design as a framework that guides the development of well-designed microservices by building services that encapsulate knowledge in each domain and abstract that knowledge from clients.

    In Domain-driven design you start by modeling the business domain and creating a domain model. A domain model is an abstract model of the business model that distills and organizes a domain of knowledge and provides a common language for developers and domain experts. It’s the resulting domain model that microservices a best suited to be built around because it helps establish a well-defined boundary between external systems and other internal applications.

    In short, before you begin designing microservices, start by mapping the functions of the business and their connections to create a domain model for the microservice(s) to be built around.

    What challenges do Microservices introduce?

    Microservices solve a lot of problems and have several advantages, but the grass isn’t always greener on the other side.

    One of the key challenges of microservices is managing communication between services. Because services are independent, they need to communicate with each other through APIs. This can be complex and difficult to manage, especially as the number of services grows. To address this challenge, it is important to have a clear API design, with well-defined inputs and outputs for each service. It is also important to have a system for managing and monitoring communication between services, to ensure that everything is running smoothly.

    Another challenge of microservices is managing the deployment and scaling of services. Because each service is independent, it needs to be deployed and scaled separately from the rest of the application. This can be complex and difficult to manage, especially as the number of services grows. To address this challenge, it is important to have a clear and consistent deployment process, with well-defined steps for deploying and scaling each service. Furthermore, it is advisable to host them on a system with self-healing capabilities to reduce operational burden.

    It is also important to have a system for monitoring and managing the deployment and scaling of services, to ensure optimal performance.

    Each of these challenges has created fertile ground for tooling and process that exists in the cloud-native ecosystem. Kubernetes, CI CD, and other DevOps practices are part of the package of adopting the microservices architecture.

    Conclusion

    In summary, microservices architecture focuses on software applications as a collection of small, independent services that communicate with each other over well-defined APIs.

    The main advantages of microservices include:

    • increased flexibility and scalability per microservice,
    • efficient resource utilization (with help from a container orchestrator like Kubernetes),
    • and faster development cycles.

    Continue following along with this series to see how you can use Kubernetes to help adopt microservices patterns in your own environments!

    Resources

    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/zero-to-hero/page/6/index.html b/cnny-2023/tags/zero-to-hero/page/6/index.html index dc2b7acc6e..f45de4b2df 100644 --- a/cnny-2023/tags/zero-to-hero/page/6/index.html +++ b/cnny-2023/tags/zero-to-hero/page/6/index.html @@ -14,14 +14,14 @@ - - + +

    16 posts tagged with "zero-to-hero"

    View All Tags

    · 6 min read
    Cory Skimming

    We are excited to be wrapping up our first week of #CloudNativeNewYear! This week, we have tried to set the stage by covering the fundamentals of cloud-native practices and technologies, including primers on containerization, microservices, and Kubernetes.

    Don't forget to sign up for the the Cloud Skills Challenge!

    Today, we will do a brief recap of some of these technologies and provide some basic guidelines for when it is optimal to use each.


    What We'll Cover

    • To Containerize or not to Containerize?
    • The power of Kubernetes
    • Where does Serverless fit?
    • Resources
    • What's coming next!


    Just joining us now? Check out these other Week 1 posts:

    To Containerize or not to Containerize?

    As mentioned in our Containers 101 post earlier this week, containers can provide several benefits over traditional virtualization methods, which has made them popular within the software development community. Containers provide a consistent and predictable runtime environment, which can help reduce the risk of compatibility issues and simplify the deployment process. Additionally, containers can improve resource efficiency by allowing multiple applications to run on the same host while isolating their dependencies.

    Some types of apps that are a particularly good fit for containerization include:

    1. Microservices: Containers are particularly well-suited for microservices-based applications, as they can be used to isolate and deploy individual components of the system. This allows for more flexibility and scalability in the deployment process.
    2. Stateless applications: Applications that do not maintain state across multiple sessions, such as web applications, are well-suited for containers. Containers can be easily scaled up or down as needed and replaced with new instances, without losing data.
    3. Portable applications: Applications that need to be deployed in different environments, such as on-premises, in the cloud, or on edge devices, can benefit from containerization. The consistent and portable runtime environment of containers can make it easier to move the application between different environments.
    4. Legacy applications: Applications that are built using older technologies or that have compatibility issues can be containerized to run in an isolated environment, without impacting other applications or the host system.
    5. Dev and testing environments: Containerization can be used to create isolated development and testing environments, which can be easily created and destroyed as needed.

    While there are many types of applications that can benefit from a containerized approach, it's worth noting that containerization is not always the best option, and it's important to weigh the benefits and trade-offs before deciding to containerize an application. Additionally, some types of applications may not be a good fit for containers including:

    • Apps that require full access to host resources: Containers are isolated from the host system, so if an application needs direct access to hardware resources such as GPUs or specialized devices, it might not work well in a containerized environment.
    • Apps that require low-level system access: If an application requires deep access to the underlying operating system, it may not be suitable for running in a container.
    • Applications that have specific OS dependencies: Apps that have specific dependencies on a certain version of an operating system or libraries may not be able to run in a container.
    • Stateful applications: Apps that maintain state across multiple sessions, such as databases, may not be well suited for containers. Containers are ephemeral by design, so the data stored inside a container may not persist between restarts.

    The good news is that some of these limitations can be overcome with the use of specialized containerization technologies such as Kubernetes, and by carefully designing the architecture of the application.


    The power of Kubernetes

    Speaking of Kubernetes...

    Kubernetes is a powerful tool for managing and deploying containerized applications in production environments, particularly for applications that need to scale, handle large numbers of requests, or run in multi-cloud or hybrid environments.

    Kubernetes is well-suited for a wide variety of applications, but it is particularly well-suited for the following types of applications:

    1. Microservices-based applications: Kubernetes provides a powerful set of tools for managing and deploying microservices-based applications, making it easy to scale, update, and manage the individual components of the application.
    2. Stateful applications: Kubernetes provides support for stateful applications through the use of Persistent Volumes and StatefulSets, allowing for applications that need to maintain state across multiple instances.
    3. Large-scale, highly-available systems: Kubernetes provides built-in support for scaling, self-healing, and rolling updates, making it an ideal choice for large-scale, highly-available systems that need to handle large numbers of users and requests.
    4. Multi-cloud and hybrid environments: Kubernetes can be used to deploy and manage applications across multiple cloud providers and on-premises environments, making it a good choice for organizations that want to take advantage of the benefits of multiple cloud providers or that need to deploy applications in a hybrid environment.
    New to Kubernetes?

    Where does Serverless fit in?

    Serverless is a cloud computing model where the cloud provider (like Azure) is responsible for executing a piece of code by dynamically allocating the resources. With serverless, you only pay for the exact amount of compute time that you use, rather than paying for a fixed amount of resources. This can lead to significant cost savings, particularly for applications with variable or unpredictable workloads.

    Serverless is commonly used for building applications like web or mobile apps, IoT, data processing, and real-time streaming - apps where the workloads are variable and high scalability is required. It's important to note that serverless is not a replacement for all types of workloads - it's best suited for stateless, short-lived and small-scale workloads.

    For a detailed look into the world of Serverless and lots of great learning content, revisit #30DaysofServerless.


    Resources


    What's up next in #CloudNativeNewYear?

    Week 1 has been all about the fundamentals of cloud-native. Next week, the team will be diving in to application deployment with Azure Kubernetes Service. Don't forget to subscribe to the blog to get daily posts delivered directly to your favorite feed reader!


    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/zero-to-hero/page/7/index.html b/cnny-2023/tags/zero-to-hero/page/7/index.html index c4659d83b9..d29354f222 100644 --- a/cnny-2023/tags/zero-to-hero/page/7/index.html +++ b/cnny-2023/tags/zero-to-hero/page/7/index.html @@ -14,13 +14,13 @@ - - + +

    16 posts tagged with "zero-to-hero"

    View All Tags

    · 14 min read
    Steven Murawski

    Welcome to Day #1 of Week 2 of #CloudNativeNewYear!

    The theme for this week is Kubernetes fundamentals. Last week we talked about Cloud Native architectures and the Cloud Native landscape. Today we'll explore the topic of Pods and Deployments in Kubernetes.

    Ask the Experts Thursday, February 9th at 9 AM PST
    Catch the Replay of the Live Demo

    Watch the recorded demo and conversation about this week's topics.

    We were live on YouTube walking through today's (and the rest of this week's) demos.

    What We'll Cover

    • Setting Up A Kubernetes Environment in Azure
    • Running Containers in Kubernetes Pods
    • Making the Pods Resilient with Deployments
    • Exercise
    • Resources

    Setting Up A Kubernetes Environment in Azure

    For this week, we'll be working with a simple app - the Azure Voting App. My teammate Paul Yu ported the app to Rust and we tweaked it a bit to let us highlight some of the basic features of Kubernetes.

    You should be able to replicate this in just about any Kubernetes environment, but we'll use Azure Kubernetes Service (AKS) as our working environment for this week.

    To make it easier to get started, there's a Bicep template to deploy an AKS cluster, an Azure Container Registry (ACR) (to host our container image), and connect the two so that we can easily deploy our application.

    Step 0 - Prerequisites

    There are a few things you'll need if you want to work through this and the following examples this week.

    Required:

    • Git (and probably a GitHub account if you want to persist your work outside of your computer)
    • Azure CLI
    • An Azure subscription (if you want to follow along with the Azure steps)
    • Kubectl (the command line tool for managing Kubernetes)

    Helpful:

    • Visual Studio Code (or equivalent editor)

    Step 1 - Clone the application repository

    First, I forked the source repository to my account.

    $GitHubOrg = 'smurawski' # Replace this with your GitHub account name or org name
    git clone "https://github.com/$GitHubOrg/azure-voting-app-rust"
    cd azure-voting-app-rust

    Leave your shell opened with your current location inside the application repository.

    Step 2 - Set up AKS

    Running the template deployment from the demo script (I'm using the PowerShell example in cnny23-week2-day1.ps1, but there's a Bash variant at cnny23-week2-day1.sh) stands up the environment. The second, third, and fourth commands take some of the output from the Bicep deployment to set up for later commands, so don't close out your shell after you run these commands.

    az deployment sub create --template-file ./deploy/main.bicep --location eastus --parameters 'resourceGroup=cnny-week2'
    $AcrName = az deployment sub show --name main --query 'properties.outputs.acr_name.value' -o tsv
    $AksName = az deployment sub show --name main --query 'properties.outputs.aks_name.value' -o tsv
    $ResourceGroup = az deployment sub show --name main --query 'properties.outputs.resource_group_name.value' -o tsv

    az aks get-credentials --resource-group $ResourceGroup --name $AksName

    Step 3 - Build our application container

    Since we have an Azure Container Registry set up, I'll use ACR Build Tasks to build and store my container image.

    az acr build --registry $AcrName --% --image cnny2023/azure-voting-app-rust:{{.Run.ID}} .
    $BuildTag = az acr repository show-tags `
    --name $AcrName `
    --repository cnny2023/azure-voting-app-rust `
    --orderby time_desc `
    --query '[0]' -o tsv
    tip

    Wondering what the --% is in the first command line? That tells the PowerShell interpreter to pass the input after it "as is" to the command without parsing/evaluating it. Otherwise, PowerShell messes a bit with the templated {{.Run.ID}} bit.

    Running Containers in Kubernetes Pods

    Now that we have our AKS cluster and application image ready to go, let's look into how Kubernetes runs containers.

    If you've been in tech for any length of time, you've seen that every framework, runtime, orchestrator, etc.. can have their own naming scheme for their concepts. So let's get into some of what Kubernetes calls things.

    The Pod

    A container running in Kubernetes is called a Pod. A Pod is basically a running container on a Node or VM. It can be more. For example you can run multiple containers and specify some funky configuration, but we'll keep it simple for now - add the complexity when you need it.

    Our Pod definition can be created via the kubectl command imperatively from arguments or declaratively from a configuration file. We'll do a little of both. We'll use the kubectl command to help us write our configuration files. Kubernetes configuration files are YAML, so having an editor that supports and can help you syntax check YAML is really helpful.

    Creating a Pod Definition

    Let's create a few Pod definitions. Our application requires two containers to get working - the application and a database.

    Let's create the database Pod first. And before you comment, the configuration isn't secure nor best practice. We'll fix that later this week. For now, let's focus on getting up and running.

    This is a trick I learned from one of my teammates - Paul. By using the --output yaml and --dry-run=client options, we can have the command help us write our YAML. And with a bit of output redirection, we can stash it safely in a file for later use.

    kubectl run azure-voting-db `
    --image "postgres:15.0-alpine" `
    --env "POSTGRES_PASSWORD=mypassword" `
    --output yaml `
    --dry-run=client > manifests/pod-db.yaml

    This creates a file that looks like:

    apiVersion: v1
    kind: Pod
    metadata:
    creationTimestamp: null
    labels:
    run: azure-voting-db
    name: azure-voting-db
    spec:
    containers:
    - env:
    - name: POSTGRES_PASSWORD
    value: mypassword
    image: postgres:15.0-alpine
    name: azure-voting-db
    resources: {}
    dnsPolicy: ClusterFirst
    restartPolicy: Always
    status: {}

    The file, when supplied to the Kubernetes API, will identify what kind of resource to create, the API version to use, and the details of the container (as well as an environment variable to be supplied).

    We'll get that container image started with the kubectl command. Because the details of what to create are in the file, we don't need to specify much else to the kubectl command but the path to the file.

    kubectl apply -f ./manifests/pod-db.yaml

    I'm going to need the IP address of the Pod, so that my application can connect to it, so we can use kubectl to get some information about our pod. By default, kubectl get pod only displays certain information but it retrieves a lot more. We can use the JSONPath syntax to index into the response and get the information you want.

    tip

    To see what you can get, I usually run the kubectl command with the output type (-o JSON) of JSON and then I can find where the data I want is and create my JSONPath query to get it.

    $DB_IP = kubectl get pod azure-voting-db -o jsonpath='{.status.podIP}'

    Now, let's create our Pod definition for our application. We'll use the same technique as before.

    kubectl run azure-voting-app `
    --image "$AcrName.azurecr.io/cnny2023/azure-voting-app-rust:$BuildTag" `
    --env "DATABASE_SERVER=$DB_IP" `
    --env "DATABASE_PASSWORD=mypassword`
    --output yaml `
    --dry-run=client > manifests/pod-app.yaml

    That command gets us a similar YAML file to the database container - you can see the full file here

    Let's get our application container running.

    kubectl apply -f ./manifests/pod-app.yaml

    Now that the Application is Running

    We can check the status of our Pods with:

    kubectl get pods

    And we should see something like:

    azure-voting-app-rust ❯  kubectl get pods
    NAME READY STATUS RESTARTS AGE
    azure-voting-app 1/1 Running 0 36s
    azure-voting-db 1/1 Running 0 84s

    Once our pod is running, we can check to make sure everything is working by letting kubectl proxy network connections to our Pod running the application. If we get the voting web page, we'll know the application found the database and we can start voting!

    kubectl port-forward pod/azure-voting-app 8080:8080

    Azure voting website in a browser with three buttons, one for Dogs, one for Cats, and one for Reset.  The counter is Dogs - 0 and Cats - 0.

    When you are done voting, you can stop the port forwarding by using Control-C to break the command.

    Clean Up

    Let's clean up after ourselves and see if we can't get Kubernetes to help us keep our application running. We can use the same configuration files to ensure that Kubernetes only removes what we want removed.

    kubectl delete -f ./manifests/pod-app.yaml
    kubectl delete -f ./manifests/pod-db.yaml

    Summary - Pods

    A Pod is the most basic unit of work inside Kubernetes. Once the Pod is deleted, it's gone. That leads us to our next topic (and final topic for today.)

    Making the Pods Resilient with Deployments

    We've seen how easy it is to deploy a Pod and get our containers running on Nodes in our Kubernetes cluster. But there's a problem with that. Let's illustrate it.

    Breaking Stuff

    Setting Back Up

    First, let's redeploy our application environment. We'll start with our application container.

    kubectl apply -f ./manifests/pod-db.yaml
    kubectl get pod azure-voting-db -o jsonpath='{.status.podIP}'

    The second command will report out the new IP Address for our database container. Let's open ./manifests/pod-app.yaml and update the container IP to our new one.

    - name: DATABASE_SERVER
    value: YOUR_NEW_IP_HERE

    Then we can deploy the application with the information it needs to find its database. We'll also list out our pods to see what is running.

    kubectl apply -f ./manifests/pod-app.yaml
    kubectl get pods

    Feel free to look back and use the port forwarding trick to make sure your app is running if you'd like.

    Knocking It Down

    The first thing we'll try to break is our application pod. Let's delete it.

    kubectl delete pod azure-voting-app

    Then, we'll check our pod's status:

    kubectl get pods

    Which should show something like:

    azure-voting-app-rust ❯  kubectl get pods
    NAME READY STATUS RESTARTS AGE
    azure-voting-db 1/1 Running 0 50s

    We should be able to recreate our application pod deployment with no problem, since it has the current database IP address and nothing else depends on it.

    kubectl apply -f ./manifests/pod-app.yaml

    Again, feel free to do some fun port forwarding and check your site is running.

    Uncomfortable Truths

    Here's where it gets a bit stickier, what if we delete the database container?

    If we delete our database container and recreate it, it'll likely have a new IP address, which would force us to update our application configuration. We'll look at some solutions for these problems in the next three posts this week.

    Because our database problem is a bit tricky, we'll primarily focus on making our application layer more resilient and prepare our database layer for those other techniques over the next few days.

    Let's clean back up and look into making things more resilient.

    kubectl delete -f ./manifests/pod-app.yaml
    kubectl delete -f ./manifests/pod-db.yaml

    The Deployment

    One of the reasons you may want to use Kubernetes is it's ability to orchestrate workloads. Part of that orchestration includes being able to ensure that certain workloads are running (regardless of what Node they might be on).

    We saw that we could delete our application pod and then restart it from the manifest with little problem. It just meant that we had to run a command to restart it. We can use the Deployment in Kubernetes to tell the orchestrator to ensure we have our application pod running.

    The Deployment also can encompass a lot of extra configuration - controlling how many containers of a particular type should be running, how upgrades of container images should proceed, and more.

    Creating the Deployment

    First, we'll create a Deployment for our database. We'll use a technique similar to what we did for the Pod, with just a bit of difference.

    kubectl create deployment azure-voting-db `
    --image "postgres:15.0-alpine" `
    --port 5432 `
    --output yaml `
    --dry-run=client > manifests/deployment-db.yaml

    Unlike our Pod definition creation, we can't pass in environment variable configuration from the command line. We'll have to edit the YAML file to add that.

    So, let's open ./manifests/deployment-db.yaml in our editor and add the following in the spec/containers configuration.

            env:
    - name: POSTGRES_PASSWORD
    value: "mypassword"

    Your file should look like this deployment-db.yaml.

    Once we have our configuration file updated, we can deploy our database container image.

    kubectl apply -f ./manifests/deployment-db.yaml

    For our application, we'll use the same technique.

    kubectl create deployment azure-voting-app `
    --image "$AcrName.azurecr.io/cnny2023/azure-voting-app-rust:$BuildTag" `
    --port 8080 `
    --output yaml `
    --dry-run=client > manifests/deployment-app.yaml

    Next, we'll need to add an environment variable to the generated configuration. We'll also need the new IP address for the database deployment.

    Previously, we named the pod and were able to ask for the IP address with kubectl and a bit of JSONPath. Now, the deployment created the pod for us, so there's a bit of random in the naming. Check out:

    kubectl get pods

    Should return something like:

    azure-voting-app-rust ❯  kubectl get pods
    NAME READY STATUS RESTARTS AGE
    azure-voting-db-686d758fbf-8jnq8 1/1 Running 0 7s

    We can either ask for the IP with the new pod name, or we can use a selector to find our desired pod.

    kubectl get pod --selector app=azure-voting-db -o jsonpath='{.items[0].status.podIP}'

    Now, we can update our application deployment configuration file with:

            env:
    - name: DATABASE_SERVER
    value: YOUR_NEW_IP_HERE
    - name: DATABASE_PASSWORD
    value: mypassword

    Your file should look like this deployment-app.yaml (but with IPs and image names matching your environment).

    After we save those changes, we can deploy our application.

    kubectl apply -f ./manifests/deployment-app.yaml

    Let's test the resilience of our app now. First, we'll delete the pod running our application, then we'll check to make sure Kubernetes restarted our application pod.

    kubectl get pods
    azure-voting-app-rust ❯  kubectl get pods
    NAME READY STATUS RESTARTS AGE
    azure-voting-app-56c9ccc89d-skv7x 1/1 Running 0 71s
    azure-voting-db-686d758fbf-8jnq8 1/1 Running 0 12m
    kubectl delete pod azure-voting-app-56c9ccc89d-skv7x
    kubectl get pods
    azure-voting-app-rust ❯  kubectl delete pod azure-voting-app-56c9ccc89d-skv7x
    >> kubectl get pods
    pod "azure-voting-app-56c9ccc89d-skv7x" deleted
    NAME READY STATUS RESTARTS AGE
    azure-voting-app-56c9ccc89d-2b5mx 1/1 Running 0 2s
    azure-voting-db-686d758fbf-8jnq8 1/1 Running 0 14m
    info

    Your Pods will likely have different identifiers at the end, so adjust your commands to match the names in your environment.

    As you can see, by the time the kubectl get pods command was run, Kubernetes had already spun up a new pod for the application container image. Thanks Kubernetes!

    Clean up

    Since we can't just delete the pods, we have to delete the deployments.

    kubectl delete -f ./manifests/deployment-app.yaml
    kubectl delete -f ./manifests/deployment-db.yaml

    Summary - Deployments

    Deployments allow us to create more durable configuration for the workloads we deploy into Kubernetes. As we dig deeper, we'll discover more capabilities the deployments offer. Check out the Resources below for more.

    Exercise

    If you want to try these steps, head over to the source repository, fork it, clone it locally, and give it a spin!

    You can check your manifests against the manifests in the week2/day1 branch of the source repository.

    Resources

    Take the Cloud Skills Challenge!

    Enroll in the Cloud Skills Challenge!

    Don't miss out on this opportunity to level up your skills and stay ahead of the curve in the world of cloud native.

    Documentation

    Training

    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/zero-to-hero/page/8/index.html b/cnny-2023/tags/zero-to-hero/page/8/index.html index 7c4f310e78..f570aadc16 100644 --- a/cnny-2023/tags/zero-to-hero/page/8/index.html +++ b/cnny-2023/tags/zero-to-hero/page/8/index.html @@ -14,14 +14,14 @@ - - + +

    16 posts tagged with "zero-to-hero"

    View All Tags

    · 6 min read
    Josh Duffney

    Welcome to Day 3 of Week 2 of #CloudNativeNewYear!

    The theme for this week is Kubernetes fundamentals. Yesterday we talked about Services and Ingress. Today we'll explore the topic of passing configuration and secrets to our applications in Kubernetes with ConfigMaps and Secrets.

    Ask the Experts Thursday, February 9th at 9 AM PST
    Catch the Replay of the Live Demo

    Watch the recorded demo and conversation about this week's topics.

    We were live on YouTube walking through today's (and the rest of this week's) demos.

    What We'll Cover

    • Decouple configurations with ConfigMaps and Secerts
    • Passing Environment Data with ConfigMaps and Secrets
    • Conclusion

    Decouple configurations with ConfigMaps and Secerts

    A ConfigMap is a Kubernetes object that decouples configuration data from pod definitions. Kubernetes secerts are similar, but were designed to decouple senstive information.

    Separating the configuration and secerts from your application promotes better organization and security of your Kubernetes environment. It also enables you to share the same configuration and different secerts across multiple pods and deployments which can simplify scaling and management. Using ConfigMaps and Secerts in Kubernetes is a best practice that can help to improve the scalability, security, and maintainability of your cluster.

    By the end of this tutorial, you'll have added a Kubernetes ConfigMap and Secret to the Azure Voting deployment.

    Passing Environment Data with ConfigMaps and Secrets

    📝 NOTE: If you don't have an AKS cluster deployed, please head over to Azure-Samples/azure-voting-app-rust, clone the repo, and follow the instructions in the README.md to execute the Azure deployment and setup your kubectl context. Check out the first post this week for more on the environment setup.

    Create the ConfigMap

    ConfigMaps can be used in one of two ways; as environment variables or volumes.

    For this tutorial you'll use a ConfigMap to create three environment variables inside the pod; DATABASE_SERVER, FISRT_VALUE, and SECOND_VALUE. The DATABASE_SERVER provides part of connection string to a Postgres. FIRST_VALUE and SECOND_VALUE are configuration options that change what voting options the application presents to the users.

    Follow the below steps to create a new ConfigMap:

    1. Create a YAML file named 'config-map.yaml'. In this file, specify the environment variables for the application.

      apiVersion: v1
      kind: ConfigMap
      metadata:
      name: azure-voting-config
      data:
      DATABASE_SERVER: azure-voting-db
      FIRST_VALUE: "Go"
      SECOND_VALUE: "Rust"
    2. Create the config map in your Kubernetes cluster by running the following command:

      kubectl create -f config-map.yaml

    Create the Secret

    The deployment-db.yaml and deployment-app.yaml are Kubernetes manifests that deploy the Azure Voting App. Currently, those deployment manifests contain the environment variables POSTGRES_PASSWORD and DATABASE_PASSWORD with the value stored as plain text. Your task is to replace that environment variable with a Kubernetes Secret.

    Create a Secret running the following commands:

    1. Encode mypassword.

      echo -n "mypassword" | base64
    2. Create a YAML file named secret.yaml. In this file, add POSTGRES_PASSWORD as the key and the encoded value returned above under as the value in the data section.

      apiVersion: v1
      kind: Secret
      metadata:
      name: azure-voting-secret
      type: Opaque
      data:
      POSTGRES_PASSWORD: bXlwYXNzd29yZA==
    3. Create the Secret in your Kubernetes cluster by running the following command:

      kubectl create -f secret.yaml

    [!WARNING] base64 encoding is a simple and widely supported way to obscure plaintext data, it is not secure, as it can easily be decoded. If you want to store sensitive data like password, you should use a more secure method like encrypting with a Key Management Service (KMS) before storing it in the Secret.

    Modify the app deployment manifest

    With the ConfigMap and Secert both created the next step is to replace the environment variables provided in the application deployment manuscript with the values stored in the ConfigMap and the Secert.

    Complete the following steps to add the ConfigMap and Secert to the deployment mainifest:

    1. Open the Kubernetes manifest file deployment-app.yaml.

    2. In the containers section, add an envFrom section and upate the env section.

      envFrom:
      - configMapRef:
      name: azure-voting-config
      env:
      - name: DATABASE_PASSWORD
      valueFrom:
      secretKeyRef:
      name: azure-voting-secret
      key: POSTGRES_PASSWORD

      Using envFrom exposes all the values witin the ConfigMap as environment variables. Making it so you don't have to list them individually.

    3. Save the changes to the deployment manifest file.

    4. Apply the changes to the deployment by running the following command:

      kubectl apply -f deployment-app.yaml

    Modify the database deployment manifest

    Next, update the database deployment manifest and replace the plain text environment variable with the Kubernetes Secert.

    1. Open the deployment-db.yaml.

    2. To add the secret to the deployment, replace the env section with the following code:

      env:
      - name: POSTGRES_PASSWORD
      valueFrom:
      secretKeyRef:
      name: azure-voting-secret
      key: POSTGRES_PASSWORD
    3. Apply the updated manifest.

      kubectl apply -f deployment-db.yaml

    Verify the ConfigMap and output environment variables

    Verify that the ConfigMap was added to your deploy by running the following command:

    ```bash
    kubectl describe deployment azure-voting-app
    ```

    Browse the output until you find the envFrom section with the config map reference.

    You can also verify that the environment variables from the config map are being passed to the container by running the command kubectl exec -it <pod-name> -- printenv. This command will show you all the environment variables passed to the pod including the one from configmap.

    By following these steps, you will have successfully added a config map to the Azure Voting App Kubernetes deployment, and the environment variables defined in the config map will be passed to the container running in the pod.

    Verify the Secret and describe the deployment

    Once the secret has been created you can verify it exists by running the following command:

    kubectl get secrets

    You can view additional information, such as labels, annotations, type, and the Data by running kubectl describe:

    kubectl describe secret azure-voting-secret

    By default, the describe command doesn't output the encoded value, but if you output the results as JSON or YAML you'll be able to see the secret's encoded value.

     kubectl get secret azure-voting-secret -o json

    Conclusion

    In conclusion, using ConfigMaps and Secrets in Kubernetes can help to improve the scalability, security, and maintainability of your cluster. By decoupling configuration data and sensitive information from pod definitions, you can promote better organization and security in your Kubernetes environment. Additionally, separating these elements allows for sharing the same configuration and different secrets across multiple pods and deployments, simplifying scaling and management.

    Take the Cloud Skills Challenge!

    Enroll in the Cloud Skills Challenge!

    Don't miss out on this opportunity to level up your skills and stay ahead of the curve in the world of cloud native.

    - - + + \ No newline at end of file diff --git a/cnny-2023/tags/zero-to-hero/page/9/index.html b/cnny-2023/tags/zero-to-hero/page/9/index.html index 35e781dd87..3ecb9fc67e 100644 --- a/cnny-2023/tags/zero-to-hero/page/9/index.html +++ b/cnny-2023/tags/zero-to-hero/page/9/index.html @@ -14,13 +14,13 @@ - - + +

    16 posts tagged with "zero-to-hero"

    View All Tags

    · 10 min read
    Steven Murawski

    Welcome to Day 5 of Week 2 of #CloudNativeNewYear!

    The theme for this week is Kubernetes fundamentals. Yesterday we talked about adding persistent storage to our deployment. Today we'll explore the topic of scaling pods and nodes in our Kubernetes cluster.

    Ask the Experts Thursday, February 9th at 9 AM PST
    Catch the Replay of the Live Demo

    Watch the recorded demo and conversation about this week's topics.

    We were live on YouTube walking through today's (and the rest of this week's) demos.

    What We'll Cover

    • Scaling Our Application
    • Scaling Pods
    • Scaling Nodes
    • Exercise
    • Resources

    Scaling Our Application

    One of our primary reasons to use a service like Kubernetes to orchestrate our workloads is the ability to scale. We've approached scaling in a multitude of ways over the years, taking advantage of the ever-evolving levels of hardware and software. Kubernetes allows us to scale our units of work, Pods, and the Nodes they run on. This allows us to take advantage of both hardware and software scaling abilities. Kubernetes can help improve the utilization of existing hardware (by scheduling Pods on Nodes that have resource capacity). And, with the capabilities of virtualization and/or cloud hosting (or a bit more work, if you have a pool of physical machines), Kubernetes can expand (or contract) the number of Nodes capable of hosting Pods. Scaling is primarily driven by resource utilization, but can be triggered by a variety of other sources thanks to projects like Kubernetes Event-driven Autoscaling (KEDA).

    Scaling Pods

    Our first level of scaling is with our Pods. Earlier, when we worked on our deployment, we talked about how the Kubernetes would use the deployment configuration to ensure that we had the desired workloads running. One thing we didn't explore was running more than one instance of a pod. We can define a number of replicas of a pod in our Deployment.

    Manually Scale Pods

    So, if we wanted to define more pods right at the start (or at any point really), we could update our deployment configuration file with the number of replicas and apply that configuration file.

    spec:
    replicas: 5

    Or we could use the kubectl scale command to update the deployment with a number of pods to create.

    kubectl scale --replicas=5 deployment/azure-voting-app

    Both of these approaches modify the running configuration of our Kubernetes cluster and request that it ensure that we have that set number of replicas running. Because this was a manual change, the Kubernetes cluster won't automatically increase or decrease the number of pods. It'll just ensure that there are always the specified number of pods running.

    Autoscale Pods with the Horizontal Pod Autoscaler

    Another approach to scaling our pods is to allow the Horizontal Pod Autoscaler to help us scale in response to resources being used by the pod. This requires a bit more configuration up front. When we define our pod in our deployment, we need to include resource requests and limits. The requests help Kubernetes determine what nodes may have capacity for a new instance of a pod. The limit tells us where the node should cap utilization for a particular instance of a pod. For example, we'll update our deployment to request 0.25 CPU and set a limit of 0.5 CPU.

        spec:
    containers:
    - image: acrudavoz.azurecr.io/cnny2023/azure-voting-app-rust:ca4
    name: azure-voting-app-rust
    ports:
    - containerPort: 8080
    env:
    - name: DATABASE_URL
    value: postgres://postgres:mypassword@10.244.0.29
    resources:
    requests:
    cpu: 250m
    limits:
    cpu: 500m

    Now that we've given Kubernetes an allowed range and an idea of what free resources a node should have to place new pods, we can set up autoscaling. Because autoscaling is a persistent configuration, I like to define it in a configuration file that I'll be able to keep with the rest of my cluster configuration. We'll use the kubectl command to help us write the configuration file. We'll request that Kubernetes watch our pods and when the average CPU utilization if 50% of the requested usage (in our case if it's using more than 0.375 CPU across the current number of pods), it can grow the number of pods serving requests up to 10. If the utilization drops, Kubernetes will have the permission to deprovision pods down to the minimum (three in our example).

    kubectl autoscale deployment azure-voting-app --cpu-percent=50 --min=3 --max=10 -o YAML --dry-run=client

    Which would give us:

    apiVersion: autoscaling/v1
    kind: HorizontalPodAutoscaler
    metadata:
    creationTimestamp: null
    name: azure-voting-app
    spec:
    maxReplicas: 10
    minReplicas: 3
    scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: azure-voting-app
    targetCPUUtilizationPercentage: 50
    status:
    currentReplicas: 0
    desiredReplicas: 0

    So, how often does the autoscaler check the metrics being monitored? The autoscaler checks the Metrics API every 15 seconds, however the pods stats are only updated every 60 seconds. This means that an autoscale event may be evaluated about once a minute. Once an autoscale down event happens however, Kubernetes has a cooldown period to give the new pods a chance to distribute the workload and let the new metrics accumulate. There is no delay on scale up events.

    Application Architecture Considerations

    We've focused in this example on our front end, which is an easier scaling story. When we start talking about scaling our database layers or anything that deals with persistent storage or has primary/replica configuration requirements things get a bit more complicated. Some of these applications may have built-in leader election or could use sidecars to help use existing features in Kubernetes to perform that function. For shared storage scenarios, persistent volumes (or persistent volumes with Azure) can be of help, if the application knows how to play well with shared file access.

    Ultimately, you know your application architecture and, while Kubernetes may not have an exact match to how you are doing things today, the underlying capability is probably there under a different name. This abstraction allows you to more effectively use Kubernetes to operate a variety of workloads with the levels of controls you need.

    Scaling Nodes

    We've looked at how to scale our pods, but that assumes we have enough resources in our existing pool of nodes to accomodate those scaling requests. Kubernetes can also help scale our available nodes to ensure that our applications have the necessary resources to meet their performance requirements.

    Manually Scale Nodes

    Manually scaling nodes isn't a direct function of Kubernetes, so your operating environment instructions may vary. On Azure, it's pretty straight forward. Using the Azure CLI (or other tools), we can tell our AKS cluster to scale up or scale down the number of nodes in our node pool.

    First, we'll check out how many nodes we currently have in our working environment.

    kubectl get nodes

    This will show us

    azure-voting-app-rust ❯  kubectl get nodes
    NAME STATUS ROLES AGE VERSION
    aks-pool0-37917684-vmss000000 Ready agent 5d21h v1.24.6

    Then, we'll scale it up to three nodes.

    az aks scale --resource-group $ResourceGroup --name $AksName --node-count 3

    Then, we'll check out how many nodes we now have in our working environment.

    kubectl get nodes

    Which returns:

    azure-voting-app-rust ❯  kubectl get nodes
    NAME STATUS ROLES AGE VERSION
    aks-pool0-37917684-vmss000000 Ready agent 5d21h v1.24.6
    aks-pool0-37917684-vmss000001 Ready agent 5m27s v1.24.6
    aks-pool0-37917684-vmss000002 Ready agent 5m10s v1.24.6

    Autoscale Nodes with the Cluster Autoscaler

    Things get more interesting when we start working with the Cluster Autoscaler. The Cluster Autoscaler watches for the inability of Kubernetes to schedule the required number of pods due to resource constraints (and a few other criteria like affinity/anti-affinity). If there are insufficient resources available on the existing nodes, the autoscaler can provision new nodes into the nodepool. Likewise, the autoscaler watches to see if the existing pods could be consolidated to a smaller set of nodes and can remove excess nodes.

    Enabling the autoscaler is likewise an update that can be dependent on where and how your Kubernetes cluster is hosted. Azure makes it easy with a simple Azure CLI command.

    az aks update `
    --resource-group $ResourceGroup `
    --name $AksName `
    --update-cluster-autoscaler `
    --min-count 1 `
    --max-count 5

    There are a variety of settings that can be configured to tune how the autoscaler works.

    Scaling on Different Events

    CPU and memory utilization are the primary drivers for the Horizontal Pod Autoscaler, but those might not be the best measures as to when you might want to scale workloads. There are other options for scaling triggers and one of the more common plugins to help with that is the Kubernetes Event-driven Autoscaling (KEDA) project. The KEDA project makes it easy to plug in different event sources to help drive scaling. Find more information about using KEDA on AKS here.

    Exercise

    Let's try out the scaling configurations that we just walked through using our sample application. If you still have your environment from Day 1, you can use that.

    📝 NOTE: If you don't have an AKS cluster deployed, please head over to Azure-Samples/azure-voting-app-rust, clone the repo, and follow the instructions in the README.md to execute the Azure deployment and setup your kubectl context. Check out the first post this week for more on the environment setup.

    Configure Horizontal Pod Autoscaler

    • Edit ./manifests/deployment-app.yaml to include resource requests and limits.
            resources:
    requests:
    cpu: 250m
    limits:
    cpu: 500m
    • Apply the updated deployment configuration.
    kubectl apply -f ./manifests/deployment-app.yaml
    • Create the horizontal pod autoscaler configuration and apply it
    kubectl autoscale deployment azure-voting-app --cpu-percent=50 --min=3 --max=10 -o YAML --dry-run=client > ./manifests/scaler-app.yaml
    kubectl apply -f ./manifests/scaler-app.yaml
    • Check to see your pods scale out to the minimum.
    kubectl get pods

    Configure Cluster Autoscaler

    Configuring the basic behavior of the Cluster Autoscaler is a bit simpler. We just need to run the Azure CLI command to enable the autoscaler and define our lower and upper limits.

    • Check the current nodes available (should be 1).
    kubectl get nodes
    • Update the cluster to enable the autoscaler
    az aks update `
    --resource-group $ResourceGroup `
    --name $AksName `
    --update-cluster-autoscaler `
    --min-count 2 `
    --max-count 5
    • Check to see the current number of nodes (should be 2 now).
    kubectl get nodes

    Resources

    Take the Cloud Skills Challenge!

    Enroll in the Cloud Skills Challenge!

    Don't miss out on this opportunity to level up your skills and stay ahead of the curve in the world of cloud native.

    Documentation

    Training

    - - + + \ No newline at end of file diff --git a/cnny-2023/windows-containers/index.html b/cnny-2023/windows-containers/index.html index 412d02ffc1..40878548a7 100644 --- a/cnny-2023/windows-containers/index.html +++ b/cnny-2023/windows-containers/index.html @@ -14,14 +14,14 @@ - - + +

    4-3. Windows Containers

    · 7 min read
    Vinicius Apolinario

    Welcome to Day 3 of Week 4 of #CloudNativeNewYear!

    The theme for this week is going further with Cloud Native. Yesterday we talked about using Draft to accelerate your Kubernetes adoption. Today we'll explore the topic of Windows containers.

    What We'll Cover

    • Introduction
    • Windows containers overview
    • Windows base container images
    • Isolation
    • Exercise: Try this yourself!
    • Resources: For self-study!

    Introduction

    Windows containers were launched along with Windows Server 2016, and have evolved since. In its latest release, Windows Server 2022, Windows containers have reached a great level of maturity and allow for customers to run production grade workloads.

    While suitable for new developments, Windows containers also provide developers and operations with a different approach than Linux containers. It allows for existing Windows applications to be containerized with little or no code changes. It also allows for professionals that are more comfortable with the Windows platform and OS, to leverage their skill set, while taking advantage of the containers platform.

    Windows container overview

    In essence, Windows containers are very similar to Linux. Since Windows containers use the same foundation of Docker containers, you can expect that the same architecture applies - with the specific notes of the Windows OS. For example, when running a Windows container via Docker, you use the same commands, such as docker run. To pull a container image, you can use docker pull, just like on Linux. However, to run a Windows container, you also need a Windows container host. This requirement is there because, as you might remember, a container shares the OS kernel with its container host.

    On Kubernetes, Windows containers are supported since Windows Server 2019. Just like with Docker, you can manage Windows containers like any other resource on the Kubernetes ecosystem. A Windows node can be part of a Kubernetes cluster, allowing you to run Windows container based applications on services like Azure Kubernetes Service. To deploy an Windows application to a Windows pod in Kubernetes, you can author a YAML specification much like you would for Linux. The main difference is that you would point to an image that runs on Windows, and you need to specify a node selection tag to indicate said pod needs to run on a Windows node.

    Windows base container images

    On Windows containers, you will always use a base container image provided by Microsoft. This base container image contains the OS binaries for the container to run. This image can be as large as 3GB+, or small as ~300MB. The difference in the size is a consequence of the APIs and components available in each Windows container base container image. There are primarily, three images: Nano Server, Server Core, and Server.

    Nano Server is the smallest image, ranging around 300MB. It's a base container image for new developments and cloud-native scenarios. Applications need to target Nano Server as the Windows OS, so not all frameworks will work. For example, .Net works on Nano Server, but .Net Framework doesn't. Other third-party frameworks also work on Nano Server, such as Apache, NodeJS, Phyton, Tomcat, Java runtime, JBoss, Redis, among others.

    Server Core is a much larger base container image, ranging around 1.25GB. It's larger size is compensated by it's application compatibility. Simply put, any application that meets the requirements to be run on a Windows container, can be containerized with this image.

    The Server image builds on the Server Core one. It ranges around 3.1GB and has even greater application compatibility than the Server Core image. In addition to the traditional Windows APIs and components, this image allows for scenarios such as Machine Learning via DirectX with GPU access.

    The best image for your scenario is dependent on the requirements your application has on the Windows OS inside a container. However, there are some scenarios that are not supported at all on Windows containers - such as GUI or RDP dependent applications, some Windows Server infrastructure roles, such as Active Directory, among others.

    Isolation

    When running containers, the kernel of the container host is shared with the containers running on it. While extremely convenient, this poses a potential risk for multi-tenant scenarios. If one container is compromised and has access to the host, it could potentially compromise other containers in the same system.

    For enterprise customers running on-premises (or even in the cloud), this can be mitigated by using a VM as a container host and considering the VM itself a security boundary. However, if multiple workloads from different tenants need to share the same host, Windows containers offer another option: Hyper-V isolation. While the name Hyper-V is associated with VMs, its virtualization capabilities extend to other services, including containers. Hyper-V isolated containers run on a purpose built, extremely small, highly performant VM. However, you manage a container running with Hyper-V isolation the same way you do with a process isolated one. In fact, the only notable difference is that you need to append the --isolation=hyperv tag to the docker run command.

    Exercise

    Here are a few examples of how to use Windows containers:

    Run Windows containers via Docker on your machine

    To pull a Windows base container image:

    docker pull mcr.microsoft.com/windows/servercore:ltsc2022

    To run a basic IIS container:

    #This command will pull and start a IIS container. You can access it from http://<your local IP>:8080
    docker run -d -p 8080:80 mcr.microsoft.com/windows/servercore/iis:windowsservercore-ltsc2022

    Run the same IIS container with Hyper-V isolation

    #This command will pull and start a IIS container. You can access it from http://<your local IP>:8080
    docker run -d -p 8080:80 --isolation=hyperv mcr.microsoft.com/windows/servercore/iis:windowsservercore-ltsc2022

    To run a Windows container interactively:

    docker run -it mcr.microsoft.com/windows/servercore:ltsc2022 powershell

    Run Windows containers on Kubernetes

    To prepare an AKS cluster for Windows containers: Note: Replace the values on the example below with the ones from your environment.

    echo "Please enter the username to use as administrator credentials for Windows Server nodes on your cluster: " && read WINDOWS_USERNAME
    az aks create \
    --resource-group myResourceGroup \
    --name myAKSCluster \
    --node-count 2 \
    --generate-ssh-keys \
    --windows-admin-username $WINDOWS_USERNAME \
    --vm-set-type VirtualMachineScaleSets \
    --network-plugin azure

    To add a Windows node pool for Windows containers:

    az aks nodepool add \
    --resource-group myResourceGroup \
    --cluster-name myAKSCluster \
    --os-type Windows \
    --name npwin \
    --node-count 1

    Deploy a sample ASP.Net application to the AKS cluster above using the YAML file below:

    apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: sample
    labels:
    app: sample
    spec:
    replicas: 1
    template:
    metadata:
    name: sample
    labels:
    app: sample
    spec:
    nodeSelector:
    "kubernetes.io/os": windows
    containers:
    - name: sample
    image: mcr.microsoft.com/dotnet/framework/samples:aspnetapp
    resources:
    limits:
    cpu: 1
    memory: 800M
    ports:
    - containerPort: 80
    selector:
    matchLabels:
    app: sample
    ---
    apiVersion: v1
    kind: Service
    metadata:
    name: sample
    spec:
    type: LoadBalancer
    ports:
    - protocol: TCP
    port: 80
    selector:
    app: sample

    Save the file above and run the command below on your Kubernetes cluster:

    kubectl apply -f <filename> .

    Once deployed, you can access the application by getting the IP address of your service:

    kubectl get service

    Resources

    It's not too late to sign up for and complete the Cloud Skills Challenge!
    - - + + \ No newline at end of file diff --git a/docs/category/resources/index.html b/docs/category/resources/index.html index 5e70f1fcc2..5514296876 100644 --- a/docs/category/resources/index.html +++ b/docs/category/resources/index.html @@ -14,13 +14,13 @@ - - + + - - + + \ No newline at end of file diff --git a/docs/category/videos/index.html b/docs/category/videos/index.html index 63f32dd1b5..21656768d6 100644 --- a/docs/category/videos/index.html +++ b/docs/category/videos/index.html @@ -14,13 +14,13 @@ - - + + - - + + \ No newline at end of file diff --git a/docs/resources/devtools/index.html b/docs/resources/devtools/index.html index 21d4bc00cc..3d9b59dfd6 100644 --- a/docs/resources/devtools/index.html +++ b/docs/resources/devtools/index.html @@ -14,13 +14,13 @@ - - + + - - + + \ No newline at end of file diff --git a/docs/resources/intro/index.html b/docs/resources/intro/index.html index e9c3e5e66a..ff8a6eeeee 100644 --- a/docs/resources/intro/index.html +++ b/docs/resources/intro/index.html @@ -14,13 +14,13 @@ - - + +

    Cloud-Native Apps

    Cloud-Native Apps are built from the ground up and optimized for cloud scale and performance.

    They’re based on microservices architectures, use managed services, and take advantage of continuous delivery to achieve reliability and faster time to market. Learn how to build cloud-native apps on Azure!

    Azure Microservices
    Microservices
    Azure Serverless Icon
    Serverless
    Azure Containers Icon
    Containers
    Simplify development of distributed cloud apps and take advantage of built-in, enterprise-grade security and autoscaling.Build cloud-native apps without provisioning and managing infrastructure using a fully managed platform.Containerize apps and let Azure managed services handle orchestration, provisioning, upgrading, and scaling on demand

    Resources

    Visit the Azure Architecture Center and

    - - + + \ No newline at end of file diff --git a/docs/resources/languages/index.html b/docs/resources/languages/index.html index 631d2e81e4..bc45915fb9 100644 --- a/docs/resources/languages/index.html +++ b/docs/resources/languages/index.html @@ -14,13 +14,13 @@ - - + + - - + + \ No newline at end of file diff --git a/docs/resources/serverless/index.html b/docs/resources/serverless/index.html index d625f0b22c..7189b80f2f 100644 --- a/docs/resources/serverless/index.html +++ b/docs/resources/serverless/index.html @@ -14,13 +14,13 @@ - - + + - - + + \ No newline at end of file diff --git a/docs/videos/intro/index.html b/docs/videos/intro/index.html index 2bcdeb7f99..d53815b466 100644 --- a/docs/videos/intro/index.html +++ b/docs/videos/intro/index.html @@ -14,13 +14,13 @@ - - + +

    Serverless Hacks

    VIDEO PLAYLIST

    Watch The Serverless Hacks Walkthrough playlist on YouTube!

    In this series of 12 videos, Microsoft Cloud Advocate Gwyneth Peña-Siguenza walks you through building a .NET version of a Tollbooth app using Azure technologies as one approach to the Serverless Hacks Challenge.

    Then:

    Don't forget to bring your questions and insights to the weekly office hours to keep going. Explore the walkthrough videos below for inspiration.


    1. Overview

    2. Setup: Local Env

    3. Build: Hello World

    4. Provision: Resources

    5. Configure: Settings

    6. Deploy: Use VSCode

    7. Use: Azure Functions

    8. Use: App Insights

    9. Use: Logic Apps

    10. Debug: View Errors

    11. Query: Cosmos DB

    12. It's a Wrap!

    - - + + \ No newline at end of file diff --git a/img/fallforia/blogs/2023-09-27/blog-image-1-1.png b/img/fallforia/blogs/2023-09-27/blog-image-1-1.png new file mode 100644 index 0000000000000000000000000000000000000000..e01bea0f6a22d1a182bc2ca259378f99d2c22b8f GIT binary patch literal 268714 zcmYhicRZW#8$E6mMNze9(AKD0Rhx)fZH;JY?OjFfy<)H0loqvux6v9AdvCETX6zLs zsJ$ZAPe0$^`r~;$uP1+A=f2K;&bh8cX=$obkTH|t;o(udd9Cyw509`I5060lKJncz z&(f?W?q2wA?^R#nmH%Pgy8F0it)QWRhX+p}zcMGf`zCdMt?!11NB{KSga79JlLI`w z+k!Vr3Oe2EMY|ac0Qc(Q-R_bkSM2f4{+qc72&((fBpZQkW|MU3lrnRcg z?Oe`liIdB*nPETma0>Tv-t7<254f4lCc(9MyYg%4^4s2nt|jBB*K0kDKYh9x(s(ERO5jfIG{sK?6&utcCjB%i;A z5ayY`Ez7CH7TNFJY?7+h*N7M=Zmc5E)wI>J3FV+9l=Ic}6;LCH2F$z5BMh0~Yq-?+7 z_fcQ|wdCdH)wO%>B_Z1zykpI9kWIGm80tqK>c3KZ*m`5slq!E-aI;-;Qxvcyr(Awn zwhix*dvru07GFJr{dmjmrit$f{;^Md2=_*uy_u^Zf0)l0*!}hTb0DV8@o$_~X%*ZO z@3I~eefo5U6ZK=tm^qnI(J!mUHu}eT8gwq#8u@1au)6_dOxUJSXqig-lyo$f!sh4B zPiOu2Pe6IV@r`Bf@P?7*NQplzj#Vr_9Sc1zdnhQ;Rd1F$sdSo|!f#xwzR*K!VipaB z?vLXiVN^M*+Zb4s#P^+3>S-?v6u$rv3MF~Jae29Obh8InAP(dA20_v(j6>T^Zo$$i z#@FAD&aOE8nS@%?m#HpP)Qo365vD-uw(Wega! zTe8Ra`iOJiI8cT%oKdoj1ABKq-f~nOgqiWun(dWxuQIjL2&N(&Ye3;toJ-M<5TPh?U?#x^ zr~H;$6}5G2Mj1!saQ#PFsw5Zcpqn8WP}i2mgo3B+@~tf01?QYu^5zv30B@wUZ?P9n zz*ryipCebtr{$YS9$3&>gaoWV9jO+DcQ&5LrMaBmB z*oecBRbRXp$Ao5PE?QmY){IHG-@|sOV8&`iZ;qm{%7B{cnfgt4PY}cJ8(>jQQIGTd zb=O&Stj0Tkakj1pCFH?;^;y*@NJXDD_ecNu-Cu(rf?TOQJ4xVKYtcu~PE#Q;k#*23 znLes>H}=qM>vgIY3!|PvsDJsq?$3In^zTAzs>~d47b&4Kz?HH1QcBI`VvQk%=MviE z`7HNEY1r0BV0xGYq)fQ_($Y=q9`()KdvPqIJYsl+ ze$tb+C(#c0IhX8q?Ceo;%90h=8vAZkgItEC?!CSkGqAC0=nchGJCl`0-w6EcgX`mS z*h893rwT_Tz|sm<1^=1HhQVv?jX!SywE%-zzD!Y>4JDW2W!y$9DY_mR52scx54m}#z7<`zpPc;FS-rF6)>qv z6TmbobIXUrMtmJVIfKDb*h|R84yG8G8E+o(+j?V@4zv5*WzdE=?eO|r0*t&bVAdA+ zwITbiM^On*5 z=7RlLfExxtKAkOiD!xB zUd;Lj%hp^o_Uu#CQN@gZwX6FMwh}4N9^q>n3_A;?pY>!P({|eOkCuiJ*>~ac-DS9I|qc=AL4k$KgFw)w!xVLZloDzj^ zmaE}kQC!38my&TBFa8H~2zByE{3)53f;FFCQvN?aIC1T?LawQehrQjC=q%A)3aJpso(+C{) z3S?v36YZ8~%K=S_9v_pH{!9>w8^H$j_o9d@1kL4lUjn@6zcl6QniMo_x59iJ%w%5} z^ut~DG#VZ6SnKTBO_|9%>jMHdZ$5+o8=^-dDV?Es5p^_CZ!cT{YM~&~;mz|lZs+lz z=)i&x4C6AswGOt6oPy?;O=V;b#&wT&?hWWhKL1zr5jEn!3g)wc-ySq1ul@Z%u)4Mr z1`0^PQB_l4(oGee5gP9k4z&#==Il1&UhZuk-QGJXG-OF0Fx<=$whqD~d`d!b4DQqP zMS9SmZsRwiwS*8^kUk#B^XspQupXJ;S6J${)Yaoy9)^+mc|K?SS%_cUwbl3=?65#Z z%L+Nto`r6KNxd#}WDlB!plK~Z$^rJ(~59%@Ch zQvvNeAB7GoYobMV-PQ^iRU6qpeY~_H!M(^JH}P|TC?5k z_`T<`Tl`p>_I>?jJ4H`9Y(1=vwB7H;QF$Ed7v8!38s|~FJsRlqyRjVK;+~)E?T&x+ zbt^aJ(7Ah-+)T&yAcRbwsNmjuGwj=n<7bjxpVr9 zp}0JhGW>`L!g&4Y^r^t916Ha#MiyD#+PBNnPrT1}lb9fzx<4%lSHBqKS!M8_82gN_ zw2EHTq8io|l~$6#^yb30gl^T5PL$^}1$4!BRIgfdKDEN6s(zq8gx%Fr1eI)*6sR3o zW2r{;yG*E@>0A_+QO}=n@$a;*T3K4P@m1%uesf@CFhwdeE7sONky+<6p>|}6(V^82 z_(p)P#8cxoyKxt}AKe>i*N!4F-!Y#L z)O-Gg5^p)`>|=!o@C!LSP@tm7!uZlIW_AhrnHL=%?>~_0O#ui_y;QXht9wM-*D6WJXw<`++ zWnD%UQWeQ7jDzC3s8rOZe+z$&EP9l%hMX9)fbidf1noPC|I-_CP`Nv-apK#_$T4;y zvP%mpZY4+jM&VS`v_Y(u}WNL zT&Sv^uDCxCt#(;pa^!Ks%y^Su07s9_jLQjz#Am@W0urTkc3O{U!FC;^}}+a zFP*waSOd$s0~(-4`%dF=QRNei!wf&$6^zXfsKZ0?k zpqf|D*1c4yx`o!PPYa^8_t8#9#TZq1v;?7JQ_jnLR!L$GG}l@tS4Cmk0C z(oLF`4&@i@i+I6!Tgezk-qMewC^7yz+bxKT05=3kGFxDZ%1*|JwZ+4@EWbP!5IJ_S z2TlFKc!pf2XFRkFj&nrR2Db*t6~NplBBb)2P6+4JS|9#D)tudyskO%bzlT-rgm?k8 zjSt$5V!3smKS>RoH~qis$%?yoY6RW8Uv6HN<7wc?>v%+Zq#=w0!oJCV+t2dcB>1o+ zfPBMlYG=<9Acl%I`Hyf(Jo1_G=o=Ivv*LHAHFdgTt6Q%H))P9+6UB9)!@Gaj!a zhmoNXyX9jqK`qgx$AZYfJ+}1{N>)KLc}V=c);ux-jEvMh2M?DFnI5$u`UY_?X43%T zCgmCRigoQ(CUsH|OvF=G`OsBI%xizt!Mq1@=6^DStPt2by6xiVeBS~Ma@FOpy< zKh-tsFWI5vVu0FXB04!%k9W~%7_m@aH6Xvy7)4C(M~SI0YEv39TLceu)!|2Hx%ex> z*>l%k`RYm0ufBr!OvdjZgd3)j@)E67#mQ6QNUTkMMvC}PU%bxT=edO28=+F{gRzc0 zHi{wp*S_L6f*C+p|3ZlAE@#<@8V2t|K_YOydx8Q!aCWF4SPc4JjwGp^aNH`0!A1oP z2#rRDo>c=1pN^i(uG7C!!`m5(Z0W5qWj<4`*oI}E+|)W2IRn&|g_OFk>vi@DR6P_?P4HC0Fl z%}FJGW!$D1?FZaD5dM;C%W3OTH5EXWsBO6fe~E|xH-J#fezlHW&-=$x<#MlY31E2h ztIX!C7?~~}+_;I#KB_yyUcgwNW3z@He=snv=SC5-1mjUO$~UN6G@V*}iZ4}1Ubs=Z z*2DS*cV}F@us2U~f){(gl;#DBF3YBjMqFRKJeniZk^U-yCowItCR$Ed8ut_}6qfN< znbR>bQLYI@W7FaKZdX!5(Wss72;A{e;;qBHQDnWzJE-e%d(D>jfqBLbARtnse9-8s z;IRd_Klf_Sp+EVASaX*oTC4|fsN)#QjsyMJ5zmVhlzliOdGrfs(|K3qp)J;p#Q}eH&dtu@HZ}(||F=9-d)76@0@(rNoaa=z256(r^8t zC7oqw!1+_$=G8P}($+2(S;VSGcpxh0okIdu4a2?EOuHfjz)s-%g8W*XPXu4d2T`SG zx;%x8$457yJ1JYJU%Rh!h?m5ECez~pdL$kS1_3S`R9N&#?FtfYe);bM+aoYZc z)eF^XP=OFIC4qa3LhOg8_HMf5eI4I;bOiMOqmSm9q3N1C>+Z7xSKGSR!*wiYU(Xw8 zOi%1*e)vBOVD&c{aDQ^0bys6kyibqCldNNRgZ>rYlTXV^RYe!2DWoc0xI=O*;@HDO zu6=FDbbDI_87Nv!A^Kfcgq73pzJzMIhl;VeYzRiy6{;!-SCjBad zE8Fg|z#h|Ln_X#%eJS}8A&FKFLbTfLnaL{A7djmhGUy2tN}krKw_xED`qhlb@umvq zBBsHO+=Fx;T0?(yTJb%hdaqc%`~&S%B(d_GGliV}c)yLVVnFD~mG9Jf9srYZ`4flZ ztU>;4CH=?H9E$RTQxQMVnlMGLI5kDpVvvV1QrAMd!&E#PaH8rrAe7fDd6INT)Dg^T zMYg-P@3!(=?Ei%%dxUGo(!k&^C?(cZu7MmzBF! zWh>6rkxfTRT=iHfhSg(2`qg87z3IJ_WcWu8Qm0t-&pG(!t&bai>cgErJw>ZHMo1>`Yn6*2q9B% z*hvwDkZSxXLy6{0a|zyb;i+};`PKBuX24y%fALxv$v7E?%(tpyuvrHiUf)rLy2ojr(@4XW|mkrHXC3#dU7hf{RG8l*l| z2VqKD58TQXC+dpW#_>m~?7K$n!H1r{bvY;6S0A7%xI(!gds=x+!kzI*@nS|@@%bvA z#@*R%A?#SM->v#x$&Wfx5_pgZTkHC)3<|M7M>3#vR^y6M2okM7?SH` zs&Y!iY!L+?nTKw5Cp^7WZHO`6i23yAL(r65aDAS9wx9TOP52MmwnokTdC||*ceFCr z`^DVhaJI_>M!MowV!U%EDvFtTw$Z?QV1C7)4VzuR@W^V1tedHZ+T`8n{HLC9P`0iCP?(4i?#`JO#U{d{u$8R)EQbFZpZ z?(9uQBw^nv5M_&<)z`1CFH6Sw;}n!lz) zYCD^~TJMzPglOM+=S3$JF8VrMFY;Y{a`nRBAV5;%w)>w4*aN`nUMC6w%sy_{s>a@(Wmc9cdyDirP2l;-@o9jguP~&tZoIznUG@ z!QK;)kmPDcXCLs@DR9ZzU!<0%-RXWnQ4I&5mN_1=aWEO958{-BeIinFb^}iT+8vYI zQ_bJGy2F5^yu)a?CT?{v zIh;iY@@dCtV;?6q@XrT8MySEoe336Gye)Qa5fQcQ&+PCMJ34hd^MwhAZg976zLSvO zWWIKwC6~6(HY@+3m;o7&Jf?d^8%8EGUNGuC& zQ{xEUwdQ-E;jC;blE-gi*}8cg92XyIqMUS2Qa2PyNlZ2r!CZ@+93DqP9%22ulUdH$8FTdL5ZUT6Yy!Ue{L5s z(QkXyaLJ#nMt$j)zm|{hLz=6dnz=vBV zri@p>v_qGOFS;9>V$L%9(9dXr9}uAcMyKea9fC>#Fmi-eqP7?K&cy2GeSD9|nN@4t z1t(kSefurp)W#sylwmmr82V~M3|lMpBs~p)vRB)DQB#K})8azF+=u7XVO9RK?cTJx z?7K9Rrkbm!?*^H9yq+h3Kn?qOvd=VRNQ1OjwPX_p#XkTq^4Q%1pvHwldkk(kiwAfB zj(mKj)HpVli4l+H=WLY0Tcu(>&8qVgFpsaz5plco-x+C(f>Wm;Np{!6%^ILfqcdf5 z&1{71tty%cWccn(7MFBuK&Ohq048Q+sd8*&wXdN7UD=#|1nV_cL;fY#wE2l4QI#oVqN9aS)!N0e(%*<9u3W z3u97}i(`>zcz0bQ_J0sT0=F8eAhToAzx_2IUl#p!xPEtJ;& zQyWVYKp7@6|FtZCFl(8^3wsgB*SpGi`C{oUz)dScjX z#m0wr2>%elZ8Wf*^~lL~(^-404&Mfl{bcnqY@~Diedx{gTLG;>F`Vt->}du0vK^#U zxAO4a=PSD`17S4=y1~Bikcot^RQ8sv=MxN&9vPDyI3Z5*;a3KnBE)-NmZK;Fta%AR zO0)DWFppB=*)?~ba9H?#DGf&`)65aCqaXdiqFi4wEOI5;rZS@a1 zd9$pSIck8=_7I$W-r;2^fbi9Fo%5E zH1FhLbXcYLL`t@vdiO_`>uE zU;IPJux)cLOGS-(rcqgkL9M&kUxeaivO<{;ijN;Qdf_!LJ-DP+%y;l9QyMY{mbsZ2 z>?rRgty0OPSSoljk;B|vjF_Di+PJUns5ez+8XZBmnlsZ}Sj(%miOcVVcGPIYO=i<> z2HCpBm}gV8#X(X^WJknLK@FmsG}$kk|5$uLOUCbP-$T5TQn7gDgb8E0YoU;7>->B` zbe7d9_e+ZCgf$k$6E+`vOE0;Kj*rG*OfO4x^fL$Ma-O!q{DjCAVN2M0)2r}^E1K*{ z)Yk^s@eG#4|CfxnlNK}@wraSk>3|sPLTh0Sn+n_}4u1e+EKkdh|HWqby9vxrbEJ7r zZ|8Q?E9y>3PObg8KeFVpzuK@!gU)`x@S&Lhb4v7kM+)utN`$LyrWzJdM7;hxxM8f8 z1qa-J6xe@$^psMqUiDM!choOJTLT#kB{omcD0Zb5SO8bFf%j)5@n$*=9@c5NLN%lQ`3qAiDBL zl(PAm!|+-V5tLV4LuxRFiO&Z?8XWbt`>@_B=EI>{dz{2zF#T(Fq+=FTPpcw#vWuYy z>Lk!dbH-$PpoeV0@~$;rZRqXh6CDs+zUWIh`xh1o$3w&SfK-vBH?#jy3VBC1-K?%% zuNylfH5Q(zJF?XcCtSsOy(Q-*jsYC*N+=H`E~&Uv&Y*oi8uEy*ZA&Crt~l*RXcnWB zfI|QfaI`*nm#?jUY1{jBs~S+^uc=M7_dnHkTIVCM`;G%J)Fa$cnuBWJT|6QE{cu&i z>1^^a1uDsu#&2`!FEc1mmOo+2-Xw|e{#?x|@0*P_8f82-ad)8Hfrg((GagDuffMyD zXU0YY>K^y~@~y5EdNc;`t3I>Eyj?Kq45?U0a7XVm&bYqs$?*!y?_NDm8}i z)o3)pZaZfKr>%qJni_k&{fKH9w7UOO$eNX+afCLIpIuMlax-pEf6Ymx{Jikt2pTBm zamisQ#c&B@z)G{0me5x>b=Sy-FC%XjW{<{_et@nKdZzU;b zrXN?Qi)vPnl+#E<>G=-oM$l9S%l)>`wG@*EOasG)>)(BH8T%O_zYI9YIi^_mHTmE# zY8pnNjq7xJEFC$gC|73ekabiwK3MpO0rL&XOx*Zn7rztO>q0oE9pG{&12E%1rrzzk z{ahfMST@W^9lZu0@~a z{6izHH`e+G>Ki#Anx~wl@#p3;XX8hh0J%=+lxB+CBd}IMAQ5Dzc6x`x`^zr^B$-lx z>i0HpJLR2ohhmwNpahae#`*nkrk95i#nD#uLs;N%gg@nKj6`1#KqSZ^>dz2qtQxy{ zGqD4q$6FlHmagp;v-BI-TlB6m2b*f+pN@5^lSSooik);*M+HSn~cPFt@+?n1Wsj}zZy4k#BIWAo7!w&$56T<>ARF-L$2jvXk zw4(Z^d+TK?zACj5Re;*-^mWGM&UWt>JvOuZV79aIK#|)DC(P?96vESs&MejBtXfI=0mos`zGgyy{#^Sf` zABoA$!#wtM9Hm+iYDT61yR0@}%q21eNL9bOvqUr(dMg1XXa^aEnh{&~uAE9Q3>A7G z=kz@?=D?$BM`m>>iSWc|D3m^$Zll^IivR*6q_SxBT;$`LrOL_Ot+f)-f-G-(+Ur2S zrJh>(2uK~|c&|3*miSPzvm2IO;AagA!r^uYU(j5JB}18-Qnc$6&GtSfPoVPdp@Knk z>cz`$b046q6sp&Q>BCdNQ{EfK#~50ds;yvAV)x2Ruql-I2|SI+<{W=d-un~+^C!TG z(c3=T_*5qyf%;L-b;j~OUHdEKk%>&lhMH5^V+3NAAnRo<&2!!MLCY)JdHF2eq1+Db zW&v~A9TN&E?&Rx2#f42IYM>nG(hVoqvtI>5wQ#>)mq#TK-4$+X9%jfA#p9 z9u$(i?`;0Y@$Vc-6~v5({&mfWgG0sSN>HUbr4vVEw%%0|eFo)?Dj?u>Xd9l7p?9?O zGcAoxK7UT#)-%QtN1KVBGJ?_HcVbf1C^-4;Jk9n^Y8#f;Xu z$4j$saVFHhe6dEeC6kmuXTV}WQX{w9`z2=_Qz&oc1a?9rdrz$epPXcb0);O>q2EsM zeDK?DNFxylablEDIOn207CRQEPZ~9%R|m`}t64;^+>Adh)oCbA;d+5?GUYsl@Ps}oS)m@mp>z)ZpjR{#muRD9lRT2 zU6-I}#y+^FlQewIfK3%<{|dXJ;9z&NJ74A+!n+B(tgK}l5+j)nl?vH7Gs`}f1}xDY z7=Pzc?_(Pkz-#uGD>!drgPs~%lAGoMUJzejT!ord68G@gE4IyurYuZHdPcl{yMfk|72zcEPJu6ymb^##I z`vTSU)=z}R>wkWIdgI|@kN;eFV4LCoiZtMvT5S>Js!(e;9GnTvbBrf>MQ$pQ+`y%F zvqAB;6~#i$`|*&v!T|cif{uiJJ0?^MA&Q%Y^~>iI@zza)7drfYgOtmW>@2WqOX(0u zu4?Y6ijnOE!~Cb_&P9bB4Qa*^P(b85wA# z>`a&J(55}vtj*cbbjk{npoad9wof9y$fT^Fl}x@}N4>2{o+!4up7$0OzNK`%?p8hg zW7ZKY(obX~4X}6W1AJI%=6HP*r*+`jEG(;J8Lq(mAQ<(8*Sa zxdOkZh_2|0Hnugdqh*dv=^DufmB_TRMmU+a2Yz%uLiv?FEO+w!sd>!BccyoCH@s?8nR}&>icTs>0UXqFkhLgzBO{-+kzY9 zb$+ua?!F-OjyI7Tuge6={{vg@hW-D{u-6SU{>h|KbNOkq#pdzvMQ^%x zHIVGVZ}^WTwu<+ z{kp}y$^yb52`Nh#*5FW#jzS2w{XBL=Qt;{LuW`NB_i|*2hS}m~=5fq0e@N8+pZm?K zX)JRzM_bH6Q%PmFHM~1hcbRv0Nx4k>nsImX_|Ho?pmNUd-VufGID?lL$~$0ptDnR( zsJwqTnRxe154lBa+gL zZS1ze(HfM1%XDv5kBl&Pv)o`OJ>Gln!v0j>pEhk`Y1`B{k3XKd5(eE?ymJ*8##WBa z)|=Ass#_{VYv{r9MzHv!r~pZl{SVe`f^UAG^B;3VDIuza0P3}f{n>D?hWBuZ>s_6n*h>$o)Wx{n{LDoIY z1E`nQzS6mI2hWe2e9r>VDc5B5lq*c&Y@E2WTq4mhm|zm(P61ZW;bAiZ&=%LJ)AdKe zM2F}E()I&BJxEHphl{u=M>e+vHP{KO0xpz?Mn7BnS(pWiPMs;6?h~PlF4GmydCRQM zTo$a3slsNu{KH+B@UPQ#KzuZuCT2F^LH8Z`P z;|BSTJ#imeA*!x+xp@QvTyr*wY+TE&e^+QCxbS^;EX4T0Z>&UISohy2Y(YhMvu zk~zdQzGJ_Sn_No-h%*_1x1Jir$_RAq3+94qrkB~yae}$#wr@4cgEZJ6pjSRdE ztYUH<@8on=4t^$YMicL__<-5ql`qyiiu}{lYESg@hTYbx)>^s$&aaYNzhn$i3qY^I z|K!aKQloYJU?cP_tmV7OnfP%6C9eHbXK(rlH?r3SET@FP8^&M zX-gmJ+2?ymj;Rj~6emIJoaBzoc`CwWJMCnCv`A?|lDgjqAbuXEp-yRp`waX4+>vc8 zAuM^iU%c;KvoqTkC={k}ko-h2vXOa2tRdE=)EYzPY(h1Q&pdYFfv_g~$QlCn|H4GB z)50|0_o>wLdldnM&>;L*v^FNl?`>h%$AqT3Pf-hw_Z&5gX$ZuB}VSW8$n) zc7i7o*t%wTaN5-jQ58X@UwlHVjKufe*$jwaUnCuSwu0_ea^<=zmgukt=u`4yP{?)ufcw1L#*V~ zTqWSI=LL(CELr(Z9zHLxMA=Lur1Rq5uyQg)s<5Ddh=f4#f3285pouZ^1cY|ODm^%= za;xla2F04;%xgL~0wcr|4Aqht1Nc`S5y-j_idmT0Ozzbg@WOWaR!Z3%C)9Bqx{Nr@)deg-@EZgPjk5v0l*K_qT2e+K_=uja|VLVgR|c?_$un+ za4$zp?PPMf*i$#E=HXLSK-VZ#Y7-A33)SIbn}FVUv$?odtW zB;-V!=R^(V51*vb=|~$`Ud65=Ma%JMg}A?S#t60FtCi$GeDPBepXn`b8m8DjpQn#M zBimafq$QWS&NIBw=ZPEpq{Uz=ukD*OC7%CQjm3Uwb|M)-QgvwTi1|6_t%;Qj!O<@r zs)!}?&ti(nJ#q>KAR4uoH8^D!7xJwY^ix5VrMq-e&=~o-{QO^Jyj4=$&pJwp$O;Gt zlsILoNL=5gV7>$H8O7G0dw<*ktqPpMA8YBho8?Ika;Wq`s8&t)n!3aX-~J&bVV74* z4W-O>{qFz2M%lxaP-6hdtU-3(y7Tw3S-!eK7?+wnHOoI0VrdRLs%;lGi z8t4Ult+QBkP%CAh4RcEP#EDx#)XX37n_A*;tc}&Ad(tCn)1e`E|PmEYI%Kkr%4K zJfrFG*6F|pNEMV1m6WC2uqHZvRMWiAvaaFYS`>*zlM3)EUJA=jLpADIq2Q925Oeru zoX|fvBvVP<{4sXl2Hhd-b42X{y~$lb+GQ2&==nj97{Nk`g!(3mf@|$C?#ScMWI6-K zVu^>^rTye6{pL^nh)=32JW?G2Gje%E6}PjQcJyumbvfbM+Opwy4ID}!-B}pBWMD>x z62r1~?mPq~6c37+)pD9@vCbc4cDK=a)3lS_$I>4*J;${iqPSqpJCc$9+ zWo?&-5|Gh)eDB}#NYt5T2p5xKJ>9Y z5;FQ5;8)SlI6TL6=dti5C)u=?74!+HKpIP&dV^qhwG+FoT9uT^ZQ-Cx=C8+SBR?xH zPI%hdqUq*-_Cbc?1r0q_u<|aB3g;DeCDOauNv?CyB_5zx9KX3uXgxZdmtvaOk#H)kX`{}vNl3GZsE|FC2(~Z0IhX{>b~a|7OtPL;gu*+L z+3EQMoE@WeSV7&%Qq7SL8F*(OZfWI+?{w16bK~Blu5=bnA3$!;oGsV{R z8eF`Zp|PtaI_Vj|%@F84h`QI@z{`O`l1$gMT57Z=hF=pL@m4jI2gUnRApN=KSq7*Q znCwT|fiHf#-7ogCxTUpV?P@K#t~kgzgk6n~skE7A)gGU9!pU^nB`!qF=E>T%I9M_| z=QE8jC~S&O7CYFXlbqwf9Exn#2kKt2+97s0r&dG%%WD>PoZhsAWDxi&q@FFWlAC$r zmjzt`dpF;c=`6`|ORyrpgc9$DP?S6rFCC~l^$TJe>x7G-AYcyIoUR^7CgYhOpL;P88Nevb2+fM!Fxbb<<|!+>3h7?|DqGtjtid zaE@`zsFJ@SG#~z)Qz_eNJ*gOp)JN_d}VQ?@yV6#@K8fr&ReFC{}aB= z-f<8+$g??vbOB>7RnG(nLAIbw&-uav!#nuvQwd&1uLFAz$@~zzdp(5h36Cyo>h!fH zxH`%7n(@uTpD#_k#b&{&(=O==*F;b{{pMvevu(YtZC^hxOnS_m!Sr?0^8G2ylA6Q80-V}ZQb0hgVnCxC;DG4jmB8r=35Hqn9{|m-ucZr_nJT|C)Dc~EUD_G_aw6xYS&e@bUy+ac- zhLhvM3{oUWKXE5^_&h;<49Y;_i!sDm+g{H93JsXdj};ZPsdJ}{kF@x2%=)GZxJ`hY z&OVCS5@x&`QQXb+@-uq>YawuVS$LC>VDL-+FV$Mcj3>u71sO>8T(b1R$+`D_*c6Fq zK(^AFf=r>mSj7zG%dY()d`~PWINED9?uR(O>cK6SS?blQ^n+One`Bw7s&zn|erdWT z&CNy!S?f!w%*dS|h+am~3$cHOlL3~@*#EBwQ~Qz@%D~vs#=k&vHlw2Vuk}k0Pry<^ zMUW|QKsy}R?>w2CW?ohdDh8hE%ED_yWhpOZ5AyZG10C#X=R|AqSM#nSay(4e8;=6Y z%RcSpLhTdew9YfkZ)c}I^9@mx2Js8xIv!q8AZa9g@U;li-$NN5s^pZYgTUaUiqpZcXNQ2Xz<27-heB zV<qAvorrIM8mh;X{zw% z+vFHGy6U8YqOlzXms?NuoJaSGV|iZu_6HWR&!>{<;2m|wWdK7Tu$Gft)8N?2&@zcx zqt8KS`(;Jz^L->t3#^HC*(A1(zVpP51d?Ic+y>cq0vP#?sCq#Exf+PFK3h4VZ~@tM z&N_0%vlNDPUQq9jISc~yh#uPB?KC8s(gb+;hh(D=CB~|i!~P~#weH^@Uy2AFBvj`U z(78wR{e5MqOqWw=@Yga}dL=`Ybwl;$y>RxEmHuB>&rLJ-Pn{I7S#oQ%WFL@SZ4UM$ zl8o!X#a@MTXeE8~zMc=;6c?w|{L;YxF*IcLDyl8C7C-M8NZ@ASMw(A_}6XJdn7{&O@B@^*6){->_!THCC7*wD6)AO*9g8}=%8xkP$k|aZJThZ6G9-$ zMVTgnNT|RPcxH~^#X{jT7FSvBZHC*jQ5e;<6b*{0k7R--;H}MP1wcLJ8hp%)dNkq4 zoDp5J%D22l6-1Z-c)gtwaG<>AG8!aTUgYY8bi~-PRxu6;aYkZFqB1R>FDo8)-;reN zCzEG4r1Cvs&E8qq548&M7GeJz6kdN1+@U#e_kr{nVMwI=KsjVOgfRlG`qTk7Pmg3Q z?;mqcy}e)nv;au4^zAHidZycvZ8L%{nitt{h5_G~IZd$jC56CR3H#v2DWlpc&AFxW z&6)}yUxKCEG2ftm%z5fqykECw;-9_`&AApw*o}g=;2sIR#4g6jd4Gplx>!rNSA{37 z>2{@=DC9}t8>eahC4u!w2-=qx$`<4@)_tZ-SDZ@Ptfom~YW5!V=i74(9V`lnm9l^E z=0`lfI8Y`Ke{56hb+5fWg}q?19?OrQu8^mDxs6{$h7MO&V`BO@ANCd0Pg4c0Id>sX zQ%vS{mQ(Zm{0HW6whTJih%T5lmA-=NV)t@yl-D2cddnnhbeig{2-^yzX2wNu?Ky36 zw4xAyu2@PvUCWG$y5e0P_X$NX>9N20zZBI}Xf{K^m8dUL4&&vgcB?K+6>Pyh!!%6r z>3q?5>|+j+}~Qc zCU7OV8$d5t?ohXhMpV>vo4)ADkdZ7!KTd^+nXz>g9f)4-iu^v1y!)&2G?PIL(Sd`$ z1jFIl$uE!*_*>WY?|neox^eKuauI8??7ANIzj+fUXX3;hEMcPcIng42%3Pw`lbqVG zIHmMc3A#!-F#@{}@CsD+N0Cb4LDovHD3{N)FZb@3`HLsKT11SQr#Wze3#nyN3nd@_ed4!*Uah-JYxls_%>|Cfs5Dk*Mg0;pSdG?%(B zNI&I3T5R@dIp!R8te6uD1GiKvh2B!u+t0K;CdP&KmiyKMB~wTs(|_vHri!evl1|#t z%GVv7Yp(-LD5h6@MN(~2XVF(>hsUz4%l%r=+DZ|I=D*JU0E~jsZemmsCxq+~<!K{MKMcm@}~Eu z9AZn(XwLiq?z6m820LM!^v7z_#X{*#1@#>+x%!cXmxT{q z^A27%RM<_()1VZg)4R_eV_R{dvW3^;QCLSIVf5u2%$r{uB-i)(r?GGWNGfLEAeX>k zbmz@OR2RF*JlTLU;W=&fHL}-JacR?hRcMCZ;;kQ#g}T&1#^wu{A@jPFH?Px+JAH2J zzJU8sTl+$RC+Lqu?AcCS3pE(f{F@BJpxjL-( zH*$BL@ZF7@l2Fs$)6gOhma@kO6F0;ePsovrg!-5BZldjh(=|pTiqRmBnYK#IxfCpjOAHjI0{+ejJ~h7)l%U^QhX4M* zn0oI(w&U&#*j7@b)!Kw=t(sM0#;Vb(qA0azQLFY$P_wpHi`ulPy|>tV&)8BkcI=4Y zO`qraz3=;H{yw?)d+#~to{zMML7Vl7sF6+e;wK?W*{i(7@q5Rh)ACxZY(s+BS~W1$ zPHihbpg?VcRSu`DIxyibUkrQ~3#Kw6Lj5^1S_HZFj0xO7r4~9M|N4ji(PxSLNF}t9 zPkh&VFV|)**kU*KCR&=l^#d6D#Rac{oB;`FOzK?9aFKp6COfa;mm|yQYpPQHo8=qu zfd;eYhnXO(LVy`-sUah9jg@2Wi^=+i`-bF~P=`?FqYhCdLD$oP6hY0Bz=_t#wp$_p zWwonEb+&C@?9#|D-qk^efo1EH1CprJC@z>tB3|W{QfK$-iBUMem>8ecmIf5X;%!sSziNc; z->RgNp24X4`{RVwk`)eV*sp0(cMV&Q;4A^uR+{5+-ASQazL@reA9Dc+nSVJ5ntEbK zg$Er|!c;n*a!VV`OD<0g=2Mj9&TqkH@XZei9>!-ZuOr&X2x0p{jMzn z&WHoB;zI<886AmzEerSho+-pNcH-eg^Eo!Fx5!lEQh|x{ z(qb*Oiee41n`)3QRg?X7*_M65si0Eox9F71#mfa;Tl-ZT1I;9;`R$=|wFqw!)a!Df zjNh+4lcXQb3pi4);=(X3hB)iV!Pw8IMRtTYCMB+OS`nQ!wcgBw((9X0@GR4qp=YO8 zwIY=Msun;RR^wxMF{-mS`mqDeq+IAXq?Ym^?Opk-1Ia> zU-;NFUfC}So6Ey_VrP~g%Tmhm zDEM6?4bMAUmoxYA4e?+J(a5OU+cCe)V3=1%HQA*^ymgt=trmVmMrZXfd_v1LsDT!{ zD+Y)5DM_V19diy-jnYF?=ub%PJ=7&V_lpfWd?O4FAU%t=#ET+eQv4DBqx#8v2w}TN z(|cR38UO*{j&u_3JVABPF#iu%Hy4)QTy5&Cp8R|>-|6WJJX;27Y#hi-IY7it_vN>q zn6k5GbsM#EW=7Kn{tvhu41;7_W`wZ*O^dZkgLzM8V*WL8`9r7~ zpO65sj9>HVby|xO%(eMqBc!DX4^!Wwr954b?m8P#(@01HkOHg& zq0yyE?5gemA)Ru|GtrtcV%vtu(e=cZAR!Wiih_y`ow8zO^pQVOYT-Xu?!B zb6Z(6@LbZU?w_4L{PnShm0n=n(R! z*YrRnr4UFuBWzAuN-Sgi5Pzy0-rnuM^}xR5A_}XSQ4n?$vnutqd$Hn%+dj%~4;B~) zI(J*rwywhLGfJdnbTa9*2ED`Zcp^J}9X9U<8s)@M1pR2t&h+;>yTbLlHGM?S*yCR{ zYWI`69|-HqWhn+U{c*du{|%{V%RYQGj-S^-_r8H_Zt>Hs1kqPTsL_yT`q=Oz2hBz1 zoXmX>PsL)`xzE_|Ge08hhgUViZ!bdLrLtb%mC9CAQ0ZHNHJm|HBJTA@vDywU|4FXJ zt*8g#h0cx|Xr;AdCNcZk{Qd9F&-4ES6oF(z%=*)%0{e+p;T=p6mHG$es*oKHvq!xj z#xV#@)~4CjqhE_M`zwGk(7f8U&0bQ*d)B@KA!XCo>q7#>ydEhD{F(oir?qfHfuZEa zBP-v`W49Nn_|@Eumusp@ByKd7Syzm#YJqpL8paPpj>1#!>~_)^&*G;r#3|wjbcj8D zeRj)K#HvMiqe_1oxI$HJ{Oq#K2K(o^f75qULF($x(=C;bBQ5JWOYs?SQ%L1$teR zf;9Xuv@yD$+8wLLzsLMq6yN#l2)wF)`2fFO=~e&}xG6XhF_X;1R!w>Bm575g`=;XDt*@=s$AGyB9I z8HPxSJ%HVSxR9D$5wF#CcV&FYd!M+DP@*m1Pw?tgX7ai3ow2o>o0nU(zHN0S0pHTZ zebBH-&@*JFX4bRtzfU(~#(4u`?`+g(oyq#UMxIT~R^TgZs6qn%(oD}gLztEhb4seo zt_k0l2Qa#GX9FDn``g}}_K4mf)S9gmDrd^`uknn7&gPaO` zGkl^s9=7qcEX6^=4Hv0xxH~bko0f6BKW4D99Xv`SsQ?}0+}eM+B(dC^-SrzOnxo!h zv%NvO!@59&sar!`Gq+^-EbW)T-2KD|-Q5iPSJo>tj}>lbTv?{ypdX!_k#om=ft)%( z%AZmpDFVLqzB07BT2+UtwawVtEI4hO)Abm#Y*#%KJRm)lx7CYah27KmpZedBfcjSE z*UAM>ax5A8@#fO<`A$)Von2fE50mB)wP#FzjW@X-po3lWg;OaAi$90Hg?YdAtHQ@1 za=T|#j7c=!UWex-gP3*+76c>(4DpX%z}Rg#s4YiFVSNfndOO7V6OOYzD*J#C}IZ*a>j%T2;B5di`hLVT3$dNp_Q5Yn0UW$NkrBwwCPyHZVC{tDiJ z!erVs!1>8)*xPj$Z`=E_=Z)kAjM-vt#6P z##BoJ|E;bJQ}sX2Y`BL~K+{l`qz_bChjINdvTVEZGVgAcp>K}!KrO{s*^_S~Nl_vN zH;>|E{6pxKfO|Qlu3g!eLlmdhr?I&`?&R_+m_Lqnr=^z0Ua|C5$-Sq4Lu9Im3$a@+ z#Xum-=FvTx>NDiBK{7#Bmq0&mIf-ZTwhV$4s83zTvyNdB!Lra8t>y z>m6vrH-cE!7-*4s+Ee7LP{s6I?8yXOKs@YAH=eu-M8|f3P8Yn_E{&GX=J-|T?c?{> zYiGPSd*I_Xb996I%Xz|j;6VMnvh43xq$tkrqH4-=ZqJ&PzU|JP^=;v`#I*Cl1%q*2 zo0$wd{Ql1;I6GOL?wuQEj2V@z$W}V3zZlKaIq9a1+A!C+^Y2EC)rP*KP0u-xb;mMq zQp@w1#iHZ9CuAwq={dT9(ATRkOq$>L^DnT4!vv&N+?hoh%>rW7)%+Y@_>8UihxwUb z{(Ul;z+GDfym%?yU7b?_#nM5_MUG{N`s~(vZ)F*yv@iT{UcPQI%Jc!1K*b>6acJ|$ zzyJi-o&-^;;p;OSq4|3nXJlfV$GeRu^-ipUN$*{IHf%M7-&bT-?_*^&53>Uu7@HaF z%i2jv39-45q-bx`ZHPHkzx=)53LBM0^pAs4;SqV#zG-O1gSA3JWc$jR^u6mv^x9Z0UR)j3-yjz+s9EwktJZWi1qaS6E9GF>3 zRiWX#<`J5{m@Ib)?GCb=sVyu!I&IKC#bF{ZAgBEA|9^CR{_Ei1@MK-%PPwU#&|5_| zidIi^G3zbhG8X7sdKNqhwxjfFH}tBCc7ExZ3540Rx3Kkk6p<$3kWnzw{@O}I5>9oY zs|}u5bl|`UEpuASoIMP#6G^>g{3W2X$O%TyQibH zqg6X`##=1kLgDM&!E?bmdW^jTr-c`L;0-DTysT#W;P#l#{YX&?W6sG`v_jP#_u%~s zryoI}zh?kdAo$`s3#j3h-P<6y#DdZ$o@aQZ-o^Nn$cPE@T0(Zx(KI=Fz;q01QTfqn zc<;NE;>9R8Ai}448bkfax!7=z9ku^N-q&SR-@eM$Jjx;n-Cv6wyxV-yP^=1QSU1<7 zYCp}VBpG|N*>x#Z&>Q}l9X(bZEEKn$^1;1>F%k^xAKVs;+mI-Z#o(rMm+Y zyVGolhnN>my{pHdx^8Ae?r6b)1dTl>i|&r-T829#28m0O=>?HNp-ZS@z!z>;vJqo_ zBPla)v!|D?nkK5w%@glX8GrR4#_J!QUIBCs>~)m&5bSPih2>ez;X4S z`dB3jMdZG-8@82qsg7@oyh4WAjZYaY za-gWRD6RUqDSE=6aV_>*q$nXtX`%$mJ$K__%>RaRM=K~a2O%6Zu1DzG59+mS z-%EsiYaGdpdV3CG4 z;vW!hQ{ZxPBxjY{;8|NFx~1Du(X{cftTSxJaj0uLEL@=4@wEz3%lLsu=9rSN4u-Ah zcXxfS@fyw&JNv;hv^Az`-^JOE<6l4f+(;FOD+Rz9mDxg>JissCzmlMnV0!*QrB*%e z$BjOHvEaIiLRHAJETSS@198!2>6!kT9aF|oWbX%DIuCW#aJ7^gWZKT`mJ=q&Oq$Z1 zf4U2$U)NF8-e8Ce3HVJY$iyLNTzAQ{p!|$UvR|0-O#Z{b$>(;N2j}nEy@MZy;TXrb zk>%0baK=Lo!cW&uMN+E)`|qw@%jH{V0LC4?(M8w-s^u3=R(64NS9KQ} zvm&S+;X;?x&*Quq!ab+>7(HZQy_Jp4e&?#lzs{zjX*Cykjg_7U2h+f z`H7RrBEf;XxR4D~swpaem!+N;wOV;6IYVK%1GwvfmV5iegnrx^Iy)IUx8Dc%(KX8{ zk3^5=mg~3Q+&JqFKuS>)H}md)zfHfUQv9i>q0CJxVzs-=OK3j|-)Mu)wpzy?ZyTGyqPK~y#=L8vbHX2sGZ&beRy5ijb`8Vh)`yejsA=R;H;OJM%X5fSQ z0>#nJi}>;DTkO4+HG&|Yyd{6{DmyW6PUUA?yd7U-qdx3t4nsA@9H0PHXO2)tF!Zte z>*Ja%DEfN`%I)&+2O~=wZ#uuvH~JCyhD(XNI5_;-53PW<9QDYkLn}g1ii3ce+lw-L zR)>GPpwyU5_Qqz6%Z_z5%1}Y2zVwW`;g=L-dhmnMDX|=TbL3&51JdiEu(x zl^pgF6|(wA+_>&iYDFWMy6nTAt){@+(mA<>=JdJ8w>FZzdTqT|$o#R`EM~NM50}SX z>B2qX)<&EHqiqu?@02N zrBdI3VFzg<(2{({H)39BvE5ECY?g!ThU$F87V^}pI-g`xdpFvpgOzpJ2w^a^X{^*m z5H5k(O#^dZr_L8a9t}L3^5ewRj(mHU=PG=ff3;1C5#S~ zE_UuGJ*I=qPQF{}0*(B^E#|$Ug6sCUC`ocLvGV>bBl)Qq3B{M|mGtlCqm@D$ek|fp zsu7EtZ6)Hn?;kSYRDE_k@b^P8Q)SKN66-fIg8?|fR|@@jd_%`KXVd`bBDH5Zm3#T{ zxHEINS7lJ@D>2}w%Xw~9Dia2N>QfeE#@M^d2^y#x@=mlO)qh4dn9r-)y^LdHDX<#O zkJlo1hD!%bY{eICP0Oo2QcFkEQ4VOa+~02mcLy@hrfNi8si&udwYbM%wN3=$}w%Kwdo$6+P9pgF_i!8j6_%8-8klH>FI=4&Nw&Er)%o_-Gy76 zy(wp~*Gr;UNrA{oOrB1~OK?_Pb}y^*`cMbDLQ^?tXtkP0^DMw%jS)W?M2f+OTBXU% z#O|#9rcWn$-MjiN2ZYsGK?slia9GwQw79odctx^j6Ki0qZ$nB5bFi*hv1Sk(rCMM8 zJoHBfWyRKN&Z2bNM{=ryp%u*}jH9>QQ&Mx8k+@@To<-ZT)n+{10ASKDB```S8ZpSG4Y?s}%(6arAwZN8uy0 zg!lszMl%oCGB0|zLRTNuPXlz2^yOdnQG)39x_Xs0y~v|4oXzHo2Odo5OIx8OTS8ZI zXwb-j)S2~3>3y?Y3u4<*O;oY(LlTP7)|@o%zZ@}w z_~XvHTwQ(FJy+vtrCGXZN=$GbZ$5O(PM}0iNri~cO6=D8KN^gg;Bg7p-g7<%ugg2} z_ZLi5K$g)ZulDfx#r&rXlz0JCn!gB=jI$I<7htwPO5r@hTRm3jtO7B`vPZAB#qF*8 z)I;p16C;Ys7Q#{3h|9k@?Y6HUvE2>3mlVcX^>n%pvgtK!!7e#klO_-WnP)05rOdYPI*{f zcx3_(pFmMQ32GRgTSEDjpaU{~Z&&7X#29(&6Za78K2rf!w%gEGr^WiI)sxc_!4J4E zl^Fe+v6&koI60*bB=;GPBd zNIemLx4wI1K-(zM+hj19Ssxs4uKbI8H5N9GIC( zhIt2lE+m=T^R+%y_pYmQ!sXI}g?H9{+v&#W+@Z0|V?de+rE>Ni-qs`V^L!fbHWCzG z2}Y(&8~9w+QsP*UH2q)Y4CK6!YH z_NfJ^?JmR^*wiQcU_!-p+(bL;1N5B}(Y3jag9>imP9FS&{1(!x2^X<#11-?y9`+Az z)VY6&sq}jR2z2fP24=n+PgQm)Q%d-_OQXT9>|gp!v!GVP<1a3xar3G=fE*=g5vlR~ zFNJva@@NNyqV0MpQf3+8nc&6{2<;QutLod;hI^4ke=h^d*++O9$gW19>rT#0$DeS{wrVywyhn`0!-a{&hBI2X>4WRN zMhwKF9N770s|o4VPXHW$)$Mdp|`dW#dv(zBJM!t49sSpqq~iOpU*^}}}a z%3?xNNlngjb72aWmQv6pCstt>_~;T z;7}Z0g$F_o`ih?k&wXi_yp9gI(Gb`@xE$4fva<|^oJKU0V+S(^W z0sD?;arDB^b!}p#@Eh;gJ2)pc+&6yi!qD{Tq^+5(Yu%)-%ck}y<$u7GNGklP7=%0p5#1P!h!V13#u!;b8Fn3`Ywr+7kw7?xQ zsV~V0`>bEpaCd2fYPBeS!mmqoV9-ObusY?f{6#&Y-i(otf3_xqn7|zFcD`+=dJT4j zd?iOyu$(JF-C*54c$ZmU;0*i-%-33;`-Y#s$+t&&9moOx)9^~@z5OOeWA>h`)T~AC zK4`(Vxzv~shTP;N9ef;}Ra=SZfpDY@X)}AL9ttOfJ$r3~RKGP~f`5s=t4q0=?i%zF z3D1&;E!|V}bNh+^Kx)zU-c>ZGf+Q#Q_w75sZeE>h_0oQug^TFdaXV{6vd1iFZ{Rh< z{Jlg%@Ynv`u#uCPMxKlN(8M*#q}>iLIWC+o-KcCR8OvZ?Zi=%@D=bCr;Tw^fEX=QK z(>&0-Ii-TrGxfuE0yCdwmMTe6g}*~nUzpPVJ}2)|lsIs_>F0z3X~8|IrN?D>(1;aW zqKq&F`gv)vw=>{6*XK&X{`tVitA!fIpqxH8-GT=8YPUSA`DTd}WG5HzM;Au(t+M0b zQ~}_H=VE?0RinIMx0I zu5z4ARvqD0=Pw1D)xzU={o2*h=3fp~gOj``TWO>#B93s1D6XOVU zs|7f6OFX~OW%>@G8xr^p^U-y%^s|~XExMR03d7f#Kg1@U-cD!afKDrTz<(T>N1W<{ z8DP$l!Az$wq!C_YtIBs-#{l3?`ckGF?PSR3M#4-$@Nv#g+Srh}5+BLzcQB5E_~vY` zDCPNFHK1R~FHf!n^VExDDDTr(y1oE>+-w7EGFrt#V^HXoe=jUh)qLhA= z)M&N#r+wS70c_rLRd)7|0B+VEG4!7vjhPh1SE&W*p)zS^r7 z`VlChMkY&YD7W~$JZO9S!QN-e;Z5P$=VY^{w9Fn^EkPF<5rDMv-S<~!gvc6(wTHE{ z!!R(56ZurmE|G=TPns@TWm-I(PHRAJGyzs0N6VPcis?e<~j^ z_PctVN;p4{Tn9D9sCqhqVWY76;HQ%KQp&jQhd_e%WnE=E>E$L{8Q!_&D6wqYx!Daj z&CxWz!6+N26wh-nJ|%%hjW6VaX%?T}JC= z`}z8Z8&cahnsS*IQE+eVPzeW&60}~7g)<>GH6s^>HzF)CkqJxdSBYkKR%+@YO#oig zuYaLEvdoyB&7mZjg#04(;E3aRSRi!2hvw6A^jDiSF^i7->^#H*$5saqx@Z7TnKB8! z`LJ&%QSZw;y+K(VM8?W31a(&z9niFOuntrV2egBo`o!tY3Ew5~&MFUkoj@!(TN}=7 zD!J*z{?5Z4Uje#iFGWp6=6QkVeBH5YJ$lXao&XkifB*lIdfJUMxamSRu&!q=<0$HX z!clXjqwlmtISWYab|rXf?#%XbFS1BK>;6DEqDnWSV(hOCWe%vg(OLIKxs0h;5teQM zRB4yH{R%3|$og;uPrfvPWN`?tNoAElg}^vl@5xVRL<*|Roz=M~{aL%cGG`eirq&%p z$$Q+>%X^DoGM;UUF}o}KlT-#jQ&A$s*fQOM>YR%+nBe;flX1qLP(Y&h&%+^8|5uT# ztYe3qFT`0^Lb{8(**E+W##-`;&jX8?59yiM_Npp?yXHd98dH93X4pw+NjHy~dX9K^ zYNNOBAA%Yv*VKqAf-t=z6E3><>|xnh=6v{B9Cos?w`R-vnxFbXzBdQ)mf(Z(zWu(k zv%?A@x6&s$tVbpT%>vg|!X)XNYocR6W*KsvMdV1)%qJZDSKVuOR^<0VM-jxDZngLH;G}n-O17mffHmw{NcHatEodR6>g#eY?eiI$H+IAfy)hStK|uBb~Z%6q#?g zLOIs+feeS+@zvdcOV5uGW@4=X^qqYii&xT)updj%+f7A=_qmnIsiXBIOKiq8IlW81 zOZ+0deUuN>UJ1kzlN|d=%G%K4VrH>Hl{aeaub}s^p^j@w4-2$`0<#56}akxCFxiM-AkOwPVC>?9M~5>JS^LFSG?K* z;|Z05H+M6#ic`E5L8*hl`L~pRRL>5B39#;j=$&QKJ}}#?o)rlL0hUmb4b$J&g5SCt zWs$7$8gkb?$aE=uj0*CobvC1mk?|YoLfIj@s$~Yc=d+XV%!yrI?`H$jYcmm=c5#kG z1wzMaCM2nLZKB&&W?(fS0nO_%eNeLPotQV94)~m-pS3fh17*+8$ozLmQ`xDt<917h ztqClbyEw~<))R+ixkt(e&oUPr8fNXB%b70s+6V?W_aiIPzMs!2Y|0XC_3Arlhky~G zUfJ!W@ME}9P+@W#XV2p1P7O(xu*a1=+Y ze`6lIn(v9Lq(J7pHo0PS^eA%mWvW@`yr4r>1@EKU440{!@ky8Ue}(M=V$Le?WtH1* zy@zeRf-Z4S*>ohKiZZ+}dmmjY<5`5x`ms{iXGFq@b9O@XI?Beq9HdSbRN_ICz-d%H17vTCxnl`CCv|YLg35ru9O5kemS5h z=Rtx9>fhwUC$YM-mbj)7qwIdCqd<^tV4fVY3s&vLJ+LrP=kvrR0(xzkvxTS7=x3Ed~wv{?~DS4|Pbu_2|N_%L$7tB9vgfQGlU49Z$wis4@hvorY zsve|sma`gfJEq5)76)ZP@ZOQSP%g*n^1XTO=3VuU{{lH!ms7SM!V1MZ3eB>z;HoH83eDgN71F;|KT} zTvB)&Yqf=M;%;ijH3)hPGWYS;hJoP7Zl1teY?QulbfGhQQ_Matov+^`bTK4j2O!~T zO?}OG-H?HSVqocYRLYgo8K_(gd>|)QY;@%2jK-Q*l|#QC6X#?eForDvSeFj*_fId@ z)7^;=79}v~mtV2oaGsLDEV-}GkzA{R=`RN{W^opEJ>a7VNmUS@#+xF_US3W1v`E8+ z^h!954liY=NWFZ0*ib5kYGR_mE?w6a9-G`fq_+HIY{}=#n23MpLU$?BpGtL4HA zcsHANXSUjt@Lm?-CgY*IKYy02tDW6FJ~@}vHN{pXSv1bdtp|Lag-L~lLr#2$2~K{2 zOwup*%oR=#sOD{ae^tw^AHua^+~+u#=Dm@l66mL&g;yb&4Hf5Wgzx$Jd(7!@M3JM- zDJnQ-+NCyz6_*?MUv2BBX{FwI@)H04kAln1MqQZ??lL^ww5W#`-^O1QM|9&cO~B7w z){`mOAZ&E@gpP|_xTOBA)v}5#S!CZJn$k8GD4pc91mmIh;iaRwJ@rd!dCTcN-1jRT zhMWDjY4+l4w`s<5A}!^jX2DHl#l8m1?%S3i=(uzE5j3|CopB_`#<#% z?N~l(k~_P(^I^;;CnVd#)E+UuLae_7gnL9i%S-ZE=0R(U(c@dmK-(gxumnm&x&k4B zsG0j4JVK1*!%NItfrx=WHb#%6eTaHo!n=#RC||aKq}y%iI>?2}_yo7mT@=xXG*+pt z6QV-qX2bq&Xdon+D?m}UHKWBYl2y#E3Kt&^h~ToVQ0|w#Al?tk6sDA_5dOzou`xPp z9BtlE_e33zjX3@`ruSN@-N!dejrs0Uv-E@SVu@?o`Dg1n885~*&N%fSa>$dCz7pS| zZfs(fe)5Rjp23uUakaCe@1^In@HqVc>ykC^doe!c-6-R3-komQK!Ry2m)mgGuR#)51M`e|!3QAp zPj!CHY3B5|?C`y=0}NKaGRMP8u3K;QgeKyqB%K_PYbq*ph`=Szd*{Nn5T ztvQ1k=RtHc9jb5o=XgmWD6gKi*$7ExnQAVAyscuYM2)UZn^9QbYh&5#6xQ8t=tpG|I&RQ?>)Cj7Ie4gSVw?ukhlAjSO&j`7az;&%)Qi_o_sXX22EB?Ur$v$ zh{91-!hy&J%5O)sBG|Qgr`}lH3x55gm&jnJ3YoywGU?YioW3>Z0R0`0eJkZ^F3AGq zeF`DLa864(P&}D$ZZ1xP#1N(X-N=iTiqa_+;*!fqw0>1;ib^j8B*x-?6Pd2RvgkDV zlseE_&aDxQPS@-E3|yRXKgbT;M{%s9C*`rSoAx)t{q~~=x9ODll9$D2m*j}(zDM70 zY939SoD~&ap_)FS1*JvK=OF7L*;l_;lh2%g*cb#L$ll{Z@Z=SIs;h>Y(46=yv-QJ) z1iI41B@AmTgD+Nsgb8Jf@sm2G11S>MuG^QRNDgHu;(SA(d*6|!^L46*drW(o7&Pc| z1Qob{>~l)&|9FXDg*{d?tAh+DtN&*6h9fjxy!sfDh#toVk*({$TBm*WYRJ%S%#d)a z!R<{AYS=PXqcuSS5fxY>c3Alyy{Vi+>SQ|-2cjt;!^PFN)7XE9vpN5f_h*zxLs=a zw!Ul`rnQFWKec5DRL=N^+bfeqG86S?!<4N5qIUJtUP=G#%gIMeVwqD*ccNwg-fu6G zl*~qGFT{~{&F_&qmr54(M{8+x$y#7DD9IsRh zeANHhXVRlC|KLH->hbOP41~_5X7guVSUA`CKNhQJZYoRZFJ#{q6kyZ@{5KsZZC=PG zc@2EeYQ@SLfTpQXhV`+}+|Xh8x3 zSy9esM-08nOSeJwrH~jG^)9R|(T_|6n<1J}co3yw|FawKV8Y#Sjq9^tKrB1m?w~cR z18{Eut9wOJU6%sS`783J7BNC?x9V{!HOeDEb!Aed@!~9LoHXG3Y(Ff;gK8m%Z(M;i zbx?RmsMm5gK*a5i8TCW2-VB#7D5AIbho4vg9KlT^hJ>cfq$i?FS!>LhP#RH#b?e{~ zF&Eb8ECBilBX^YsJWEP-Il9tjQWWUwxC`QGToE*x<~mxz>L1T&<@91 z>2Kr*lTV3ab8o{=JsfQ$eE4*(pTOsF!=d5I3)#E=_iY2Y1^)`#%6x;FjP_ZvGNlg! z$6kKG(T6Be0^gdGI103UHxDIT+-mJ7u%zbmr@*Vru7rxEw;JfTlG45F@8-^TlTmaY6)>6(u>0$3M(L5~o1@Lg?)sNs~6iF2Lewpa! zmY_y@?vp^HZpj2=LZ)KVS{Ew2%^DOaq>u25Y4Z)f`}e<6H#QgaJOX z-WK5H=!YwJPZK6T1_TZj5MnmHg+2&^69{y}e?4i_rzcktE+7#Q`VJG0@B%Z{Z#&J})hzBs=4M?w@V&@}(CjGzBf zG~B)C-u}=VOxJcrLY0i-Av9}c)xUl*SovjHdDvcW;d%v<#Bf@BUNnEOoHO zm6#VIMH59^A!jW49BcB@>?OOi9I_>%;mtr)s^-U!n(nO z8~Susf|jix>5;l~q$sPTP~b|&J_WuoPEZ}(;1-znZbl&RW_AnJr3iHE_AepBQzRMc|#on3+}hsm7vq5p+K@)8%c!6_|QP@`MV~ zyyZZxxKV8tROYI^ab#V z-^xtE1zPm@WAy7*L>X9vc1F_CjWtIp)4Oycdm>XVh}>FXOxw(VfX9#vqZ`JhC`nSi z_R%KdWJd1gpLIg2$9_x2dk@K?jv#2l#ENpcnKqA~YubQ2@FwDu15KH`Upb)bkUOT^ z6tYou7GLq7*;%G&uRZ@Aj9$~l?u+JH@A<4%x!?j$l~pvw+_@|5mtPP4Sraf!wfOkd z@wq{ClX0}At;S^0{|8SOwouZNb+zqgNscweiDI2=TZ~&%Z>ZIMtWKoDHpt@fY}1|N zaE-rlR?VTv{(u4vW1yQ)t7zd3Y3Op1F_)ZDS*A8zIXpjq4{5ABzq9fhqr{-JN_V>l{@%ka?dgA3T)4e~uX~1ka zA&ZImk$?N}(H}qz!0j$dT%RhKoXCk2#pDg_(6cAUT#9-O+v4|4ZLUF6rsc6myf-*< zVa5gZIej(_XP>>HRbOyw6R&4=7#+vGra^JvH-335-*bYiCu(guBNv`W-r9Zn(dLdc z*>()HNdP1Hh$Y)|lFBCqiho4X;aPO`hp@sufN$>Azpo4^Vu|r42Lw?j{>UJeLhx|^ z$1>cu(P`)B&Xzg`jN#vWFr)FS=s4P@Ire!NFX{6$UmQIcQ!4Y~@B9Wr_u7L6ubjn_ z|JJhe4E}B4Eqw>c?A3kR9Bv}r=f^y}kve`#e;M~B?u>Op(z1~@7p@Qbk6sFp>mW&P zl``uBGu+l}n8|4Mmer(xn8#dP1X&sH6ZetZM{3*$tjxB)A0h84OJX>Ve-^b*x&SYx zI><%XWjWF-nl`~}BK#}$nF)QXc>2`tdE61^y|j#g4F?JzMO2r+mqz4znh;?nBIc}b zRNvm6c_0MdN)!^El|YmKC<4Jvj?}8!gJz#LFsP(8_tfu z91ZaVFSHYl*R0D3eMc&e+IwvFC1sC9A&-_?_8=oyh|EOJ4mci_uQu^c7e<`? zpZ3WABEZ^L_0}^T|A(4&aVBW{3R#z|Jz$zWAd+z;IYGa(jL+B_CQItB#aWu2YHFXR zx|#(VC12?hq{H79G!qP@D2JPrEjfg;FHfE27oh1;6f>JD_WOvG1LG?>SX3kRMCbZd z>(pN_o%fFky*uZ6az;hCaY^P3y}{?cfx8Qq)R?uO;zVaK0&ZZuUPZvJhgdwr`Qvjx zFp!r{9TzHDkSKgshPg*=b8#OQaA3WXreZY;dN5xD!J)1 zRrsBxKN6KLZ^NTyahJ&(fMWuFG^aa})wiV1;wQGuo&fUjtE^*u|HxhJXlu4P>; zr^|V!m`g%#S?kPob1Hi~!w3)(f0i(vjf()nAilhyeI^$WpS`=ol`lb8K!NyXjD%2F z{ck|{CtNKY)@)0rkGIAWUvoi#*R*bhE#WHvo37QKRRAa7sxu~j(=+;IwF`x>+9BNw788*4i!$Wq92wo z&RpLs7oWLGUXAvf$oCffgDpZOA;QTeM0-?KCJt#iP;d{wdi8kwesMwu+yVA zBE9zI+rDpK+BgsG0Y$re|NwsO)C90v2oidyr(!uZ7hs%CDLowTRHw9 znJ{%J_9ctlVYSRHA_~v3ZT-o@zs9Eww%#YvwMjBwBW__dK*RR0)FygU=3+Wf}$j+!m$_*m#vpDtY`72NgA3ao4S%aMYW{?(Ts0@dS9)$3)A~BV{b+ zSqLE8J7Vl;>`&a(X10-iXqz>8G^tz?`m}emc zkXb`S(+|^qkXb;&9uIO)&KE_$8@a0l?#06*#VC*E8;{hZ8DuS-%c^@+uCz{e}BP}23CQ~~L!m}Z$|Isc~GFevR^JX-{s3EVIO>|fhX{ps_9q%73!>=p zmCo^ft|e`UV`=salVTNhTCgeI_AFqHnrQfOH$Cupul`VXJYUCr)b`2TfN>1`O7>ns zD-YVYa?4SV&h9&&cWXvLuTtQ7{!IAgJxSB`dNi)+@-}oL`$EJa#ymlWE`R1b-W9frm*Ii`| zXD113-hNTCu_M5pPJDAM%L|=b9>*r}!HjA5uV7v!eeysd7drHhxx+~i4?DvS@la0IBAT88>2!xbL zfCfWwk7!l=Um;{1=%Yb<36*lNpTN-EIgNb^CYdVZydx1Y$nn*)4lm@h8V8K-{7-ei zC(78~J6aCEL)vrWXvfRXC2)KLq-hKE1oP=L=7KlouBnhD4qrhdo2xcIYw2hK{EdUe zOKgFQ3>AmqVa4I6vTLMwPrA9-zR{4{$?5C&Vg;egbYSfTd9Nl9l}Lq$uR|`Q&R8sG zmF{@bh%XJu(N`P{1dm#7*9~owYpZ`sgvup<) z&ZDkZE4mfrtO#3UYoo{g5hXZ2tiNlOGV^>TP53Y*XT>3h=~p%ijEmhNNmNTtA5WBR zoh`#u3XTrQY=p`WSaW4L^!)rK*RIk_lr$YXVncs{R&3@%WyZChK-my6p?+id4j)zU z13Xau{iyeQT-LLFxn2^UQ3kVLt6N^faamm?O{z*l>f-sv8>zaQQ!`VX>8snNmI?PY_(?$XD#^K z@H^K(-EA)@{jbGA9y^l;+QaWhur72yB$*L^4gB>M9aH>h+kfX%++NK-M)#TNVVgxu z%3*lz&7&u@5ZMp&1ouShl6xbo6@j-m?f>=<0yu*4PD~k0EH~ThbT`Obc zEVL{QxmoAv({7_=A1zvaL|(XA(U?5m zzDhr9JGve|&Cxg$;f{^1!VbG`RF?Iq^kJr=9%ef+ zfl)VkC_T-7Ogm?P9%qi))T%_j4jHHkY@%Zqz8Vyr0Ac;%e@%RV3~_^qIQnU!j?Os(GHVJwkL8wP|MkT29^r=?Qjy<>Mr9 z8k2!ee<+KFpej%3D_t-Ju>}i{%yT58wxaXNuqDbP+vT-W1V9HQ{sn-zvFj3`4II!vyS*! zu=_$AZ0MpwcJF${xjN;xW*L#I9E8lQiOW%yR_?KIJ%9Yi?SPOF?&OcDXr(e?#aA>JQli(kI+Lj|GcOX<+ZRLfbSbWVkP)1*+8RmhG&7ObJJw` zU8phtA)`D$yk}<;G*y_L5!r+T*}5Z(pF_68edmC~mdCRcb4pqFO)T#*VA@bySHr)cdj{u>qV$$*sS50{lu zsiccy|8v0U(a-kA{0_D=SYzgxYgJ@=JL1R3K6>(@OQm3`}e-@lwD%I!8tD*W{-lAWp|6+UV{KXE$3@=097VahU;2uZu z=BmuKBx_B6&s72OMSOI=T8`|wA@h%WkFjy)8%$T`<+-11yDCXb6G)W^5^$ui-1!3M zzm%3$CzB_EM(S>?Uf{i^3Uegch8dg}>SMkPKCh`A^)g^_)5L&H|R z1-YIl<-V^7ptIyAKA@iG(v0?5S_s}MuMR?jOs-raYGmmEX{DWkh75nbU0ikr$WOl7 z^X$Eu)@wnK&6e+6_LVH77*Cdy+;$cAseI(2HN=OziL70+zr_KUL6*aDa=urijebUA z)d|##;H(Pzvs+IToP(^iYg7EUwY?$nOfo68B5-L$k!4j;f|~z2(?&=!b$jj@)5M~( z>P*VeL+)>TpIwa$C-d$aRcU7C_H!`yM$4SUdCD0E(X`)s&yfG}BD~lkgFk*KBDpai z^90?Q^5^pIBGvUYfa_xp-H+?w=JRf!fQz@n*4=St28W%Rp)uoZ*E;Wx=tE!J^nLkW zLuBGxwUWq%R$;n=+cv|Hu8@V$<01W~Eg8*vnt>A6XUn7~7A|upxVF^x8TG%A=7VU` zWXGmD(b;eF`=+YLyAqa3d zwHWWIfjE477&{pSqFdI>u2NA5RC8}AJ;`8k?%!a$rS0>p`wK=;wA+%xKS08I5`szr z95b#^Ozo)xDf`R5rUU<$*w=SSmkNSRD=gzBimnM$zPy*KF5a$pj%=pciC#CER4%^_ zUbs2rMdQxZMCr?YCJ?{#mXDWpvcUDe<@yF}mcWKqo^$MNJV6FvyeNt})>ICQo7czS zxYvEl85dSc74WtRTShM{XDts9S|=Z`ou*YzcK@E|w_Cam^DYzS(AU`t(Y3t}CB(Gu+gIR$l+Wh4ZNDUf6#-?#fHbd^%Oh>uk9$ z^MWldWu&04SAaddsVwHS=CzhFEAPuHO@uBkRlqqu;F5TY{wUA!&` zC;y)5C%nA2VJd>hW!~-?*M8BAx$Bqfx*_VI0Mhe|xxNx7d0DvC`k{hbkrNBSmdr9- z9-aU^4iI(gG!rj3(s9a%dV$Mc-^gYhxvxUf#C3H1D)$L%6r{NY- z;1__n9l9laSu$OE^$sNCr}r2;f9?HF^!bzGaQT2>yiu>yVI}PQ_9v`%Hl9z!w1cl0 zTKx02hZR2O#68>df)BbPu<9u4K9vZ7Ohm!ixBT^m@^KXJm66?9E^JQ^oyaWhDmto9 zTD9Xx23T?%u#gkQ1%JfbHCGGN-!ut(8nJt?)}jr97%1)<%J<(V2U8**EVw$>oF;?Q zG(4?aJX-!BGUAM*UFHq@Tr9i{99N#Hevs?*GL>|@`45~P z0?jc^r)v9WR_KBiWDCE|p$^+>aQ2GBl;EO;ryMC_mOHl^&mUrfpffxD?3nl%lJA!b zd|h|vTArS@H#RRdbmB3(00+fMWWOEJMl3rK9K?!!bLWP#?x0F+5++k}2oxu47$6<- zIWh6yb;(N%m}1D|c6c25Sto9oNnoq+vS<8f$&%Fn!qfFJ!ril=2`$;y#Jab0i=1$K zcOz_tPyBqz(jZyMJ*EZ}X{c`KQGO%CnIuwxfOMle61;NwNh9!g+P|k-t0Nf@SaWlK zZ{)^Sljt0VB1#szdH|zh{wbRRD+wgIK!nZw)_;?*S(+=&s$Zh2{*uhio<#*y+MOJ>8gzX*=nb> z@`g071->LL>K1{SE!us0WKL%sN4$ZDi9Ea2)23 z?3ET;zb?ZHHcJzP)V3-Tm^E%ioFz)xiyM2N;}Ntt zM>gk#qBKq&ikf@E=S-i#BT;H7IpyKbj=g4*E^2~#)!F8A)0E}Mt$T@L-}&hkFn7@D z*KEgTuN5m)Sd~uVXcWu3B>Xj|0y74VWP_;_NLgLz+mr}b`{MPN?qxIsap}RngBv}m z^}1tvBk=pU4#=6-9FvJ_1bemow@i#6LXh93UM(iBw|J+|(7C4cc^#6+J2&R^;5`t# zDCFN)f|CjJM#8s4r51%DKXsLPedtaj;&+QN@ANI#fsnx!y<;XU%3*}(bXirb*_sc4 z;LZ9Ynq&F!++Iy<1XV2xfvA>s^3;SiV{`N{%_gYL{wCU>Zrp|Lhr;G~OW_ONO9R`B zhAbmY`lc+Vb?fFH89i@S7qI^k?uEr?Er}g}Rkk;u=Shy-uht^vUiyU^b(^P)B0=K{ z7Dc>`EzmNO6+)z8&mbY|_SeQGUNJm9+?+@WsVk{~_LE&|DWSvaM+l z&+Dr@RrmDMR6ndy^ew)_atK7hgI<&0#Tc=gyf?;n899G!n%rD=(}z8`#-(D6tI=P? zALwJkDnJ=xU^%ul{t91yP`b zy_1AJf1KfK%`a9-Tf1B7qn5U6SERC$o}d=sIUzmp69R3|lO1q$=eIr^@P^qXa$t4Q zYX}k~z7Gog#&@0*xAl_tC9{EVWMo|4GT#q^$@Mimq8@$Mj)vH>gUW|y56fT#1>uQG z)P4=awCa;Mr@$+=k}m2*i1L zvSw|zi8t}WHGB6cpw-55hjX9y#AZiin$mwl@ShtCEoS0;!9r4b$IIY~7WaJLFS#rv zjR6h4oR}6kq>kZS!ME@^m48@jkrk}VD8pslU`+GXP^DX*gg*1 zxKr-$r;r4tNhEJb`qwT0A*ywvWA=<#uij;lZp{}Mhp?(Y29zb@6xkeT22<%|YnI#c zFCcLMaFQ?~7)2{%F-WF-nv>}$P={gs*vd%zVu+JaB7%mTBr; zuvVT~-Zj7+Ma8XN98dRK>pui@qsh2bv|*)-5)Y9<<22OzK;%+T{hGYn2|-Ccwynt zzYJazMC^IdU@<16ku3Y~!sjK-dr@uAVCZ4p=?(9vb` zllM>~&-?c}?K#XuYd4g3y>kQEZg?im%T+cgXL8`?INcpD}0qr;^`Mc z^x@JdQdwY&tpXAzgD)mx%uCL@PYjf7-7)^C$XjG`;o38Zwy z59WUdhnqW^+pU@@hf;3EYM(s`J^h4dsa_Zd!otPp+~^t*Kd%_uj+BUWP5U0vL5;4P zd|jT3Ru1-%Wu>pxwDEl6;kThHjAxD$!Juw|95>tH0aMx|r;>sLwvr!LoXYs%gCrNg z`291V1nwh&vzncSM^}wBFPV^FfGdqe1oCD}YG$%LMxuot+QD@2D%awTW5Uw8di6qQ z>%+depSo|hL6{q*uY)!}bb+=WPY4T--;~i90@h7cdk*BWN-xS*y9XD{7>!rXqzseq z3!G{!_+2*CzU7+m2=o!rH+Spyem(lcQvumVKIXw0c$y&I)zWVb=<@cTv5M-?5xxu# z6ggkvBV}Rd4kR?aVOZ`a?Px$i+oQHrspX(P=<1{R?pJ1u)D;PO;2^(m0+4~`6OJA{ z-%)Kjs)QD@nLYdvnRTdq!lB!ShBy(Tng)}-f5RiOsYp@4Z(1y0?GTw?7>T-71f5eH z>v@hE4%D)@6 zqQWHvi*GEi2sQUluh$g#W)-NYSbq!n2z2vbipe)|wiRAt2_Ovwi2Ob4;jwpDosH3^ z8ER?96%n0~&2Fut`{{@qdRS8@Ct?<(WflFCY)|D6%pC2>)E7G<+}s$)4GbnFbuBq(zsUEN(L7xIxiDiXCR(=@CLzO?L*iop$SwJma9vYUvE5jknz-&~ z9{rH@9qM<D|sR<7CR`0%_w_@*y&gH}&B((Uq9Zt>+h7<}vwWFur>y zs|fCSBZmEWOzJ;3`gcN4@y-pDZdpz{V1g~>)#L2R6dDWVMdgh zzYTsWp<*WK{tpOb5A+Ydb~n91?kYzvMCtJvSCFGVohMeFzU1)alx$+zSG2MvUKI>V zBghX$Xc<0*dFO9%(2Gg)tlKkYo}#9A zoXAz573wQolLL1TPPYyXO6Oh5FYJ+JcACtOwx8D;g5uwLP@ftM-WV5!7MBSg-fF}< z>4|eOM}R+@oz1l)5{I zr22FFEgut-kz;)H-LSx4LG56sYLQey1|jxIs`~i6*_SsJwCCC$EQ0W~Yi=tkCT(hv z;u7(_v;6p8ZtU3fa}cau%WQCSQzA+)o)%8RCeuK)yI(ef71aH zpEa(XIjOzFC_NlIoh%_IrLZ^BxfD2nm2Tzz-FTCITE;WVs~ED;E0C57%*yXXDKA2- zJRHPPqIKGI7k03SC(dry&gdXPZA=C4e~6x@4!-{QfjMwn;rm|kK~4>5p$RXjYN@i_3sYMO{-?SIxZ^-qh(bhOhV zTu{Ig!|Hn@>+~H=`+PryGLR`J6Vj*o zYFmC2oZ{Z*x%cja?H8zyNM8WX5ApIax-cm9X#_+@pfNZ5(bvBEfnhiG8(-GVfNM;I zdn7Bumx1qYIizQen}7dQSLu&MIi^S3$Q2g$+GY49?r%l2>Ng=BMwN5Mv^w2~1`d=f zRJjA~9G)&f#ibSgQO^-O3~psx%@$L;@VLe}#NWu~Ld9H#n5g`6@(`Ox5*NYH$rn^a zTaS095}dCS7r8WSwP`0Uy)S8{G(&urZ8_7{CEMkVcVitE1vvM`M@_zQ?kjiC^ju-S z(E~gs<9+yiAo~DNeJRAZKH54a+zU=OnWZ*0nQp}duU>j>_+!CF26zyY-)E1FhHGr} z9@~iBd1j3@ zz!)s;;;oyQ(XpKh?*?&3drO>r=PFx;IM34N-jyTeoa|ShSP?vij-|n@(;wZ^1Lcd#VZlVjl@svlzzuC z84pFf`9)K@A&(+GlQ3oKJRt+C+I1~ptsKPljBv=~x(gM-44O%AdD&FNTkYJd0i^I@ ze?Q(+cA2lVI#npKU@zL>neB{)&Iu*?dFqb#y~$!V8E5#(#rOA~`#xMh5;ttCj#MI) z8T{3tZFCpo;});2|4^g3y8eR62;vU_l;XaN@Ecz3(ra{t&_?N#0;CA#3iaiF1xMJ;+`zV=V%CFHui#W;06S zY=vK6sa^gwevbM=x!m)uRGhuSS|Z!MMb&26{7E1+MYCt&ie&W7(_a8E3fZ1Z?|yeP zCa3%84=ckgio9Zjg}e7J1k1_Vh~3m8ukc4wpVLD6IffoaG|wsNfWWm`0?WHf_l<~o zO=SNP|ChP9O2KI(Cj?VOyo_{WTz_P3r6~pP0KB!LN|EU1k zWE}t3uwDc%bQWtz=V%tT(bg(sHzh}F-eOnrBm5N>Ibm2rA-D36fdEwKa&y!Ow*@rZ z0;+r;pjIcM0%YdZaZYxEBHt`q5V01voj4cT$$Kzf*(yoIse+Yren;-5K~o95)ZIeS zeCsIH&7+oWRj9kIrMgFIQQ=>I_b12opCCBej0{HJ@yD+oa1WmRUP43^}VIBoA)^%wXa4zPnu9jvdG(jdt9_mWUMYfuwHHz_apkC&st6<;0z}XmSvy2fbbc{gCbra1QNX*~0VS zz*p05CFxx^@0C}FmK9xyjM#iAeY(pcLQPa!Up^~oC(zL}Zm@U-BXTbTwm6-`R1QO} zhoHTP+hJV+`%I--tmsN)`KZYhX%uaz@({I`?iXPFQ#D4m`ufn2e8_+tjJH;3_>|-a z$fwls+74GhHyIo{v9huJ*GX(GD0DW&FPj9jl^od-MS08kFduu6YC1VLN;*n6_b{t$ z#uj={ACo`v*Y7V>`D&JJmMF#$kl~}H-KFK?>SDSN)EOS>Xqh?7(K}@*=x>!f<8s$9 zdIs|J$QnyIyiN#7>0e@Ea>^59)(tZ6T`!Fbv;pIkJ<+*M#vbuN)11VE;gQ^H$~jIg z>EdhZ^W=7x!rR}$o)ab63jJ%ySfy3Gn@qJbsqEj&qGx?FN(VDT zyRUwnr8h>1BY68}Si_UxkG0$TKWM?mQYH;G>ju!Y!>6B<~pycO|su} zU)u#uro*5s+Wtk?iZsiL4>-NbWUks7)>@h)LfcjKQo7j!Oej{dWe%|2ZuB)q^rxY&EO-$s zLB6#r*y@u9$`kvOY)*FdbZ5pDSRWqh&JV~m^5jqL^v&Y8ZD`+E-I*Ul=B;OVJVqA3 zS`f@Vx*2a5AygVYW zoot=`qj?!KQ|S92PM7}&c3H7;1}+V?0Do__gv3>z?#fPU(c;1(ctNlG-r}-DVY|zw ztHwEwDyr_r46nY&w9>lVXyi42V|o1cAdkVG!VWk-7&kJd#F|qIspxpJF!gPqu2wIH zr=eeDLlSsK>%nfj6{2)GXygU~+MD_vS%EpCLl10)NXvA&;cn#zWQ(A)G0)27Ti-v4 z`FDK1bRzWb+MZQ~Zs`SFFq+JWPlR%Ct684l8Tdeb% zbMs{ZI<}=VL)@DoH*k;@P8;Y4W+Lzf{03(>5Ff_k%|pwdj%S*_Ka&ohb*dgsXqYSU zkeKnSM-$^3bGz6NZH0!7Z0Gv80SZC$xJdZ5+LnMT$9WTq^gNYO=!V`6--ig9hFS(Z z+F1lNKPxsp4Vsz<@wgAPbkV%L7EN3AH~N|!7Ws^W>JDRuO187}glZRn(05q42(5@KT-PPXtcN07mtz{3u@ zIh2VTqJ(ptX_2(Re}(4Qu<*}^Z53RlbI-r`)9V-flg3op|As7kPnH!^ ziIcaMg9=`e6zVM#+iHruJ=-~~t-C^li1en3}CaCN?yntj$6 z&~wr5yO@lB#9TL7odY@1BBOqbqd(DT=_K!BLLZaMbvgmdxV8=O#0v}_k_^7_n@!7<3$ZH*hy!N)2kQjTLYXZW&F#y$n$*bH|bs`TK~ z5xv4GjLo7hsj&)+kPA2B9Gg;g-v)=T7{Zp$_KGX6($P;f4EGTsH2~i-&AYPDp&7kt zY-CS?12cG0kb^?pOY2T8TmuQqu;jdlig$V0ftI*?kf=!oIRUE4@S1A{HTMOzyfw64 z-3?m$AC>*pl}u0Jov)#JJWuN-sl@o%AaJv87qx!vADDDlNZCA3C)?zkM64l!vYH^1 z=<{P{mpM6c26MD*4j(T#@R4n8eoag4iWnMr{{+-m?j6GhleLKYlh6cka|1DyqkxVn zF_PiYY#mbBF8iV-C{b!F#izkx#IL|IUm27zikh2&gsM*RuI(EsS%-4{b;Ox4lu>H( z_-f2kEQ(ILRQ&l~r8}4hkWuZp<;N`+p(&w)v;#U}mq7EL2|i96x)4H1j>EQ0MF7jM zOA({9;Y+*BcXM|RAR7?hR|~!N(VswTK@1aLJeIXTdQBg>;UX3IEWL!&5@IF9shU35 z-ey3(#m3Tfzw?r8oJO?1bbe5U0z_ZPi7pv=ZOt?L7Pb3YmR|JOCI6jVJnz1<-C)?1 zXOgvc*w4|%c*bin*;|ucOV6ik^XLrMmI}$C)=5ZO;lOIgUzCS_Mou_2zdcNI)V=WtM5Um_0GaTqNM; zQ1=l4H_ev-UST>rQQum($o_dLP7cCK?yrkf4OqPsd81lPrt<7fikI@&05O{&nilt# zSiqa7YOWID^1)$CwtR#vN)?G<2vE!nbHy$QKD2)1PGN-!g$Nms(py<(^Ri&sRH+d= ze(Yj<(URl-YWjcEEL)^3}@46CkuMc+L7bc#=M9 z88s(u#UTbTF9WUb2NRWP>a6_2t7N_+=(NyrK<&=R`&~6_jBGKMn*BZ2RDV0PDA|%AN7`x zk;UunFfz=tB1%%sP=!BDn0wq;kh->11`>PyH1Yif88*@=BiwkKWj{PgAC-&hCni`m ztmDqr)Fu~h|Bh?Fh0iP9Tl^#f*V#BZ40+VBV6*U9g;jFLq0;y}{j^80NJLO?l*IBp zTh2jH)(w#-0dF&?0~|$90#=gOCXcaMK>?ArX1`f^ScbDE5mTx{%tq6@-eK@eMJ*3J z1*Sy0+m|h^EL$SoPm!#lLV%9&+KhREaqH1TmD~~-6|VL&AgCc&r3-f4naloRrdEAS zV2O;oD7*cVD-NcntXphXaM6{`*TruW^g#RBU!XTiLfUH5@pLsnRR02Dy>t#@hu#y9 z4f^VspB{)-^*aPK$&a5LQ%yd`?IdK2FxB`OXAo@l2$y}TSEV&ZR_}pFr;QgKy&dI# z_3GhL2bi4ehrG7g(v0i%^yPu^trbN>ug>$wNQ(MMe#Uc!pg9X35-NgHw}-%lTI$$FRGzjXEcVMesQ2fJm@Xbes@N#n=_mLOO< zNVJ`h?`K?9M56{fP2;o5+&X4m*X$I%A9g{_^2#z#pOlGz^`^t^r259d_A0udeL(JY zX%#lTZTcmDRojRUi_m{DcDJ(wX8hAM>da!t?eu&&kk8u30nA1O9bt>9a3kvZNyL{H zi%#*>U}s_Wo$FMm^>daLAOa=Y>xqC4NHJ!KD*qrKBaJU=-8Ccle^`6b-qYzikeodw#y@9#>}Eyg`^!2KLF zB6ytKI5m0g9}Ct%RyS#;62IzrARPbN(S<8{frs47tsu5=Osx7tF}kRk~#nHlrA-C6HwR zSdsrQKhD;P&7-v7_@P(&`m)5I)$d6|yY2qmi+yw;ZshWy;f9zGl^j#_*(@}qM6}}) z1}kU!tt$NNo_gng85X4h0JhJ_RH+3$$aojDNgI+bdkNuij%evwGxKWmCDXBk6jni$ zAK@u*k+dL~thAKm4{#PB09Y02M-m<+V>M5Uw&k;D?7QSA0qp_Lj4+x_8Me633B%+l zpXtBNUEPjbRJ5?1iHzMpMt|;7pH1qce^`Kc_(|T6TWIQRl6igd08mXeGHQfG2-2n@nrZ`4Y%~_?N z#!%~3#jLTTpiij|Z-EzXM7B=@{STtL@x}w>btV==aZ`kAu3+5T)Bk{s4A}(`OfpUq z9! zRQ*k8)h0aTP`tsL)KTXyU{n#{n$E8bv#n3F58t`8&3&i{q-GJDPhI~2IHX4RT2QWqH@=Hy ze5-|o@84zoaBH{ZRE2Ji_E&SD>*|dircEL(g^jRjKO&m`e939AdP4U&ukU66DrE}q zb_i54z7h%XTfNYz|6-T=tV?k{M-nv8WiyYt#qoh>ftDJYn0AyFHlPf=%0m=ou_wj? zrx|DN@-R%vUjHq=F6&r6xZKeoYr$VgGQ+UEb^acunk;eu=?%&El5d=!GYc?Z5+kmW zTu;z~mz``XBILDf%Si4ZD_f6N5@(;>LPz_?0o`FxA*mV_lddrTIv7;(+*qhw9sm*= ztSPJ~Bg{v2R#I{F3kg+)K*Ch(HD%mLnT>Y){N`tVBd)8abTnZVK%#lJ0Jm4>@J+SY zwck0URUBc^i)jT{4;0(!^cI)!+aU+f1^nk-t%10TRn^vz7fH%rp&1@|$7~jGUd|y6 zft_M}tt>9Cs%-b6KjRXthoA|E$IaO^wBOx|&klzFs% z@x1z}DDEx1@fmhVw9a>q{KDYGQl@EdcUMc1cfoh7QDbo^8X+BG+#_S{WGZA7uJF+b zr=guF_)z%_Rtjkz-ZfBdbdcxHZYW0`OFmS!{D?pD-4)w!xO>L~_wuDa;u{O$?Onxe zIvURmUE5)q*XX4N*0r;SVKX%t8`-3t(?{=VC*k5r(HZwF5@10da{J(gX*fl@2>Ci= ztEy=og{;nhTF#xyK3(e5M>}m6yewZOYNnm@PSacP^lqNhw){{PX1G@>!=0cz3%QGI#1L-WY%mSlqss-T zA5=eiVJ5{WCyS_8*;aAIlOBe4>Cfi9P}DYEd4m0P9_BhA1MW_E*iW7Ub4v`1&#t0 z_SP%??J>3a87{s}OSRv%l?R_@!CG3^$qYOwkGYu?3TpTK6*ZuNHQ!Z$X*sG~-MC9w z5TTZ?W9U?T_j8$Nm`r^d@L5UVBL1fvZTkiQU(TP?z#{YT^0+@}(ygRCP>j{(SH+s% zA#z}grfm}psC&P|ZWlGZiIK);82;HfPd;;tH9^%KT+67AGrXT3^eUF-MbGw)kbt=$ zGRX83uHFSx^vy4)HzG%bw0y&*>Tgw|6@Obgfjjn2vlQzJCb=pk>m&!{FwvwRTOX9_ zMVQ0v*u`%8F1BfR!zW)V<@zkXm+2KV%3I`Lq8wScXGYZfaWn^orWP7??auqsDsjcA zeMIr{E0=~~DhC$sKl0i~8S`(qYPF;)T|7s}27BcO)xHeV*|pV{?Xjbt!SllgJ!NbW z2X`-Oa(F80RpdkamPZa(US5E^K634UrpE&H>WeS__|WiO{cu6!pB=o?xO7{n(r@pa z*)uN=MI-bxPBdpx3s|b7+xfl}bG>-AbU$IOKvvIe^)1`30TT3M{UOE=7%-;k3PJ); zuNnxgI$tQx+F$ipTw5WTO@P@l10N`<_;!HyztE%6ZPl+_g7(}Lk|8P23Tl`qX^r6LW)5Mxw@^<>u3StCElKeW zTgvz+zGU$>8(;ZWqtmEZV|p|#_WvZ zGU0!TU(Ny`Javl(8Cd}#C0r)y*_>@kz2Bo{mrX8&E>$`jbk4NV#)W*HU)igUZbmz6 zLOnlHhbh5bN=gftG9&8B^o5%mu`_7BRYa>;r zxi+qo%*I1m!6mN2=Do7Y4W^Oc704;IsO|H!CB496#jDJ#0mORq(ZcB-(Py?9AGk6y zr`JT_o`VDqkj%l9c4Bcnu-7#=7RoDgldgC^@q1<+k)&_gKb-m$n6%*OJ-@tyzwfB+97 zmJaZU-1Ew~tEz>!y$gJMr^a{(IFY3R;(*`mRli`dW&H{AE!%`NKFmRv$7Ol}L{E(E z!aQ_@=>){?y>Jiv8>kkjV|qr_&TnyyL=RdBUE7aTsb35EC>DN170A7*NHL`M=Xrcr z*ks%h(eMyyxQ4p+M+L7t_C{}1{fUetop>T$cb1xEDP?9`$i?L^^5b(NX9Tn>c}i+u zldJmjsVmifmt!~Bf13R~-S2wt;8~3^V)Xfz1E55M>z$F*2N9AfO)}a2C609T&A7d5 z^S|PxQa;`mYB`jLeWss(q<)jnmF*&5Jw#eh?_dQtbG1%guNSx$1yA3uOKDu?oAbF! z?<71tZicf4A}RekI2AN zJ*VPJ?ziq_l6!J8!@oDH{G>pGJv+)teBuYXQ-{z8moWz4CH3R(jS!A0a}GeEW}?+; zBe$_~fscQ}F;8my8(4z|RoQR>W-_O2(3IdR)w#|H!MB9FYTe9m=LqCpWq0(N@xOrF zv2Smh57cLuZ#Pj0!SJ}PY#o@gj>7sxP7aL+x-g1uh6Qm*u1xQv0oYp5)* zmapDul3BwD#SgAD=?8e^t`<3|eYJ&N2AS~6FwUV;y|Rv#OgEl@F+@%4%&9+;TyJS( z4Zo@H`SWbkYBr3+6>SX_tI$b(la@PfYrDZ}WE=$g$dT0}SK-OrXOG-}!I|?{V;HXn zNTyHb`H@XuntdxTo;0CT`@zH0f`pv7lWLNpG4G{pVkSaw%KXD&o8gYqO-7Zts5j(K zWg0>vAPi`7b?_p&I8S#*)L*lp$n-mK&He1TE0Jbl@42fF3-$F2UL)5pja&ck$e4wr zK}^e667#2LpaT_RvOpHr^&B5}l-E>nD0oIJyQ4U=f0Z>JXLe!*rJ?6SlVxhWhu?m0 zu1_=eq4<}kO~@S0m^z{r?@tyJ$E{IfLcvat{d7A3EgOq5`Qc#*sM|E~2@xoO9lU~A zIa8>}7lj&$Cc;NJlJo#ar-e62t%QEFAZ~8{zG{J5g7Mk8K!MDc)6OWRHMX@NDzv=0 z3f1i^H8n?xUuNl&1rCei1Sn}d{-mCG7x8S<<|2#F2;cqly@-AZ#U^=cKE2p|ZtGhX zRBJRZi8i7tHn=I#z)!N8fwF7(Id9RZ@#xF{y77N4d6LJ{7QY2O(P$oR+>=%5JwFJ5 z9&*$}X8e{PX65FpHnt2%1)98D{|G(>K9XCXJY{YNA~ob0mRP(QT9#&KlxXE2hIsU# z^qo}|h9U+m{8&$FlUXtRTU3f3V*)?(2T>6}-)F5XZ1dM8`YbPu2pSRK&xvUYWn6%{=8BD1WhWrk3;i$0R+>( z-)rfY8_>}lpbT|Q8qWIfg81*2=xp61?y`A7k3akDXAx^f;or}{WCyNx6XQm~zX9!? zf!7f>J;5v`T(s1_iUB_WJ4+6C>qXFBsLC?6vPsmx2ocf>L5z&>Q_5&*B3Cm1eg>u8 zm_R`pZyEf5i24e@Cf_e?r3Dp`5+xK=S_GtHgn){afRr!=B$N=OYXTx&q976@1cr3W zfQ|0%8Y84(Fko!3diVXk@8|RG4|w*R`*SXGppDPDhK6iz7l5<6iHCxI$qn@7@ z>5mG$Ick)3-SsAW@uyHZbsa7KWT6pPPTg>PjN-}?*I3(o^k{e|R<}!^-lZem!CDk&tu)`P`I>7M< zNaP>Pw3f<$Gz6(((e`HJF5WoV`J>2O|7grI7`ARuaI0bF|^%MLwi z7gcpDTX0bE0)^|!czkIS8sI_CBWETx#f^6U?sZoQv9idB-?`^Z)sp!Q<3VN3VuA!} zkY_X8B|`>v{RM;ZQEKzVWv5lgPfCD+kN?k5L(UalCUO+NzXU+C|j-G zEm^Yc1tc5WPI7?9*8Hf6S45f2dHP~jEg^^ei2#ns{Cd2=M9))7e6kXQsR)XIqc(*A zeDGNE!`J3lU35=(c&Ht=`(0GPweT!>L#WrSfK<>cmPN6Jf+po$$+@otJ95p{Nf)E6 zG}3TRmJVr5XiB#bS!D|8^!U+hdNl~Kbw_PosJH@2=cy=BrH)az_3Kf6_A#}BRhfRR z&w`50{zLM(u37`@3l7Ld*gZm$2|@L!2AzWD3-b`Cq+;dyBoNHrnc`4RgLCm_Zy^JHP$FydlXRng7i`Wn@8kJGbsvAA~K zV68hju5YC8HsQZ$zT|=4Orp9cY~=Is+4DDoch`Jo+&|N@ELMSMf67xv9-vU}uPt+z zLI26b|6k`#@5D+3d<1|b@!)1})6oeRqP=s&kA0lB}@#$ZoeMPZa$O}w9H zu11Ki+hx?_W1@|ycwQ-E!#4f#D}eug<%bRCskO&A=YFnnkFQ|>`lh9!4;mLw9H(g2 zoVRS`ox!;SDfl%}&c82;zC9J}`u*48W5KJJa>9pC+#p*M9hZY^Eia-E|9=z?_Lgu$ zMS*1gyv%^`+H8C z=$&Mgnb1*LFGdRm>N)?c42Sk@pMMW<%lnC5q*_6M#r7uk22M$Zhn5v>+x;ayrd9RC zD}7o@ksYqhLdDS^j_r01;u8dfVKIN5924?p<8vck=i8ZWuGs_l-Us@ zkL@F5z>lN*6WRnt?84ok-1}EtIVY-VDgaH z5@;XJlgdt6|J$$;J2EEE@W8d~7`<27Xc63rZ0-Fh?*c<{zfV_-H;4JEu6>ApOtjek zu+VC7_Fnp444O74SpN&l`Q#e3s?BS?_J%4aS zdF1}GV&tTRQTg9atRdK^GW)|mA<}Lbbw2BwuoCs{`Qr60g}KJ4ptD78hOK+>hr;Vf z7SG9S#4I;uO-QOpOg_qwe8A-cA3T}{;Uf`;b>?;BN$ASvokY~$<-SMJLT_P}-N^?( zaG|aeM(ES@tzECg{ZS`>iJJiJdbPO15ubX6SmGIU$CA0_Eq#Di(FtGQW7X?Fu8{j4 zrkbBelDa#*a@qX-0mqu5bV=0S<%aTMAw+)v7rj*(lT@d&4>cYf-#>QQvhnc0u*p-8 zY=1r`#Ac^C{ebFI~#he)=OLs1TIa6ECT$Eu?K)q!vp*vAW#=u4)Auun$N@F_)>tcE_SOt zVeb$Z!{vuO)AI8MZSW(kcf?5Js))u=GH4sNGXp*#O??Wj#-D-zHd3B&9F8(vCVfJH zTMo9M0L&73ir91r3Z?vmuLpLPj1EBn33*$$k(3fVD@;(hvx=hHugNtSYLUE3 zd5RQqoYcTi7)5|(5O55A4w4wj-e;3zeZooNgTKu@Zpe8z3g;lt#4X>t_M4jp8S-O}gm5qv}+NND2s zzl*`m9Fnn{pE8|7$&(=RIRfabc%Twy&v^pCw*Y^`St#B?bNP`Ax|uJ?F^iNJaC<Jb#cU z)(MP1(Vkrl&5;YI4~|s1m>j(f25!R=ln{%=`*CP_q!r=ZZFAjjQk6RQu(B;}>nCdT zA_J8Pb~+>D^u#&I^IoU!J;$SJhX8NnMdyQvYHjf$_kXvxkoEy&f^^Q2B&lOT`WJuG zpc^cXXul97&S%a5a3@)Y!GovY22=rDdF60t`X8}0(hG4qm7~C#Gxn+x(hzg&Z+z>h zDEt=~p=+JBuN$mSE?$_=W#{&7_;}jw_=U??T_x#@qCaur5LSTC2JJ;6%t2dBWeg#C z9NB(VPl=V~p9yd^Y#1tuyfO#{;0q)VVZ>s-ebNOWKp)Em+BHLf&r{OPHzGsL{D zB_z>yl3sgvHn@?a8j0yZmBw@7LbA~ZK94MIozckEIgBj3&+2PMHLYLQ&aC20*)3n) znUS$oowS6X2#3xei8BKJFi?SZZsPL>!=#IIvJBLWhc7Mo(|x6=TL|&hItR02c>1!@ zw8YJe&kYx~Th$N08mIU5UUWx6>rN_U#+JDX)Fm`5ueMs-x z>Az75HE+GhLrG1deUelcT93^trB&usal28?QT(A_YL=aqU}xjH%~3!mn1>v!QYNEP zcDeF^FAzr&8c(V02Vu0B7hB|glK}2s`cYA3#O*y~+u5EQN-Ig_{rU{$CY@j9<8w*S zMuhg3yyhs4hrbqftd#%pd1a&Azm~k9F8`Jp|Dw|?d_m=>Abae#VA9X)m9lvL9lj8R z65r8D1T*YU&_=|j*aaXlL;vW<`MKxGUV$%>!CMyocDZx2cs2vXABrh$Hvj3tA379_ z*1?hUP#l&1h3EKb_so+o%)p4Bxz{YU?tqt%=x~Sas0}$XM`dt*e^5mCsGW!J+ZNhW z^)g!zSn$6*^Tif(sWWD^QwE>G&8u%bgAz9S1&fq@9%TaUr2@{yY};`hM7^xG+ft!( z1cKFx-QRPjfywV9F0rR?h$0RwiQ6SjYRxA zd4pxJ$Nk;d7Tg?7&s00=-LvuQOZqICbvOuIZe7zDH2VOE=Ly-zoKBVkiMKWx3CGZ0 z+Yfu|vUPY&8illO`EkAS#Fx2FBWg}%O7+w#j;O`#QyDO~c4#lb7w3=IlkfdNe=y5S zw>Vl=%p0paELlvoZ_iQb(*?5$3n^^x%)GSc!fgTMy1r;m8Ju&TvySlvozJJM%!RBU z_!?M-099A1IHFJtSyCTKB~>!atoW>v&lCZ&Ue|L;@+K3M+;J3?uq96(lk`KLSMnW% z;%Ge%LP=(ahqlBKn7ksHT1lsFPOBH4n>kW($6nqMuWqM{0(Sq z$f*pXbeS}ca-5yVuYBZtLQ;^jm{&GEIz=-28*5dx(AGtJE)+Elt?|LO4+_TYhAuu-=+1|| zQjv~+Rdf1hPl?~_LTX@j&$`*K#Vxv#o;|a(bn^9M$fz~a>!p9@QJOqZY55jMs^oV`zM!MCAbGdyiK_R?g))i_cRh-vO`s(_~J; zQT;B1LS5Y|&zr(8!0(RyY8otlTZhHbX3mvk(OjsxGg@z> zA-SqUh@lbK4}=9DLYspKQ*-!{B~`_dyq1@^M+SVR*1v2n~QUI=!GVKo&8?ZWGnKc27GoAcr8%D}mh z0*)B+A&3Ix_-%=$h{_N`W3y0Bu8S@ie{7z!t#*`2L1KtO*ufnP^QJ|PjR3)cZK)f(Ot}{w7X_5dlBN1#zR$5DpO?L~p!>sHf$Br#m${t9 zS9wkxXgKf7vl`Z!Jg0kjOU%dv*4X@2(+aJ0ijZ}?pkJ||6@?`oBh~OZs7Pj&xmGN% zM*|h+pg@bTSbq4^dBf&G4K;zgI6*wISN#D#Q2Ujztz3;Jnmw5#Ha(&2X^X1^?V89P zvFCyn#Z(;p=x)ig-4e(cXDRNw2#lVQfFv(dBLJajWvD!H*JC_wkQB7v%6}M&i}W}k zHB<67WE$C#B-xrAxwT>*B6IVa+ZW<;l)rljT>|aAi75+n?43P;(dHCZ6CVlyMkxxu z*naHq6&qfj-AMfv#t_&;q6MC6-*?@0Da0?_ydL}jd52$=e;xGZbp?%PioxQbpAkoL zUh1fV)SGdEleymT182(IbM9f1J-6*HW5zbI(LGle+jb#oQPF*IX9E%a(6NUsuw;C*a)0U!uECOUW}#P{3J) z442RiC}zN67^)O@LW`JPcDPty>d}%P@gQPMavYtMly&nI zrr{6~=4VT!&6awul~F5y4H(7lC_Y;jp4T+pnqurBsYLD~Z$_2pAQ=z(E&+C@U1I=l zG`lj-M7T)QZ6CO9IA?WEld(M-{82R`bldGAgfi~pvcmSWm1r5c?rO%V4{1s8!%gL8 zpg&ry8QY5Ib-5dGb^8*b6n;^P=mn6_s75t(425H5f|FL6{XA-eHS>}=5{kncF15UO zX6iQ4wpOuyToaD2N#=;5I9^?a)}dSEBZ31G{LR^!(Ts5G5OgjNMiSc>Q9GmiGh~$WF{hj%Ib?wV#cD=b& zDX;|E3*B-eiY-JkzAg_SA_eP}68VxQ{pio;}5iahc|>RV79&1tIavs84X>A|f4r zT1YYWHc%>fF4mMs+W2XG7lp|tmBDoUR4QUNysp>^u zTqYz0`41U z*|{2JDJ7+#m`F4sV}+6i)ckV`BhA;1V<%a-=76l^;rO>wF(V2E?NUrr9(7chNVvBY z?VszL3Ri*fdd~juU6DA@{<>#UXB5u+um+J3wfv_MyhqA(AkcJNV57L$^u7vg~*!@@5 zZ&0I9vCg4V_u)T2tEy{^9c2pIf7>H)xc0mph6|@l-_2h(F}in6D=VXgMx)hrX6%q9aaY$V)M0zK^jt z?uqpyN~qc$nWk`1+_+b-UQTxGIg$l!C8GYz(sQyN8lYEM4EGD(HbIb2NBK~&-DXOQ z|5f+3SjiABj_B?y6@C)0>{w$X;EXktg9jlg^+F44rrq-f-7W|=d;s&LXbQI>m z6#rX$;|t2Jb3R?=UN>uLM@s?2cUJoUDeZAUY4TP!!VN(v zJp>@Q-TFMP3x_N(ydgVTB<+%JBQT#P-W9C5F7&N=r-rc2S3$+{Cf4ncY(?sKc3)xIHeWXkt;YG+Vhk~%*bIo) zMfHf-S)c7VqpTUy9Y01(&s#oGL_MW&wQa9ZFDru4B|PsDt&#mSJ7n5Z_sRLy;N+;> zC%KXWd)As8(?e!ttFESB$Oajx$i8Nf_9L^8?#8(!4Kbrb+{Wxfir9u%Y$pO6Jc{*3 z;u4Tg31NUEdeKnwA(@EaCv}4U;&;F#7d8E1|6JiqNwTz}j6+GG`>BXp@+@$dbS4HE zBOOt;~^xb9&#gKnz-pfa>UM$Zbc&Q;%YeZ zz!%$5&CJ0#djwax`V%}8_`Dp|2|pW0O@c`d*gR&^b8P=;a0+prFkN*HyjUuRV&UuA zh|1t?dqg@I_Z++NC@~+`2sp1sMevgnP@jO079RO>7{}sY{wqjO30M;Ya*w-QrQOwH zR_%(Ah^-gL9&IxAfYyXU^Mh8x=%Lt_E!6^nLkeWMm&0hc>d#Ht8wNFBJ+WoC^Yj<( zi|vW)Swwmf*&%RzkunrOh@jZFX~SP&YnTSd^OG^C+a9e^8$C!;*G4R@0}3 z;Uzh?I8yJXn&Ls+pE=+=!^=uL{VroW+ek%OGKO}wM)IqnfehbX?7-XCSvhLLM8lE8 zES;bHD@?f69Vt|vx=nDeJyTci7j;(g2bU)W^Am$^2lk-Z*;?L%!iDYCGl%26B@WnK zgWqi3`qD&g%a&Yeb#;4;vqk?_J$26Jx?nUNxq0s|?QXUh$^ zd^c*5bkK?LgB|E0kfG~a%|QUdc?K0G4#6gOXl_d$K;_W$?W$pZRN#+wmnRUPum}V_ z9l#)3C{rg6Xy#1H!QF_>mkb;v!Bos%-x_8!^!5nohws41VA2+<04&iGx{((I1))bl z4MC%oWJhdbquNZ!ztpgFjx@tL9$Tx+sPr@Q({Uu*gA=`B-Ac8y@e)7_>c;hQ;JpH{>?k zrf@`eSTB18_@87N3)}K7pFUuZN>7r9u|}?KtfG63*>gB4bc9NOi%JV|*keBw9V%&L zzHDxm3p=MSHp$IbAesQqj#2s*lc@)XvWJ9W6!~lke_mjO7I|;=dsm$I^1>me6Tp1# zD|b>2Q_yC0aB5WWFuw_$W}*(w0~J%|EATDD-+Eesd&YH@aRTfnDku4s(>6qtJFU3S zwon?iZQ#U4;OFx{FcGOsgg2i7I!)`eqGGuAwzHM+2%f%^&!-&qVolLqRqa1prH`J0 zvSe~I*3vcVO!_u2nbe^%My}mX@)SX3n5YqwoZ~k`9Yn`|wK8zJ6q@M)Yr;2@&yc=I zG7&i{Wb9{+reE?yh9E|XOQSxZ!$V^0h0PS_2PRV$T3S+L(DDKi?W79Fg9s7LQ1Yl2 zc$?Hy0Nx?ZD+6XjE6XgdufY8$sU;_r&Z5((+o|zfW_8h0MP;4N!~~LRuT%8oB2b}YMaTp zqvrE!U~m^MW|ZiN?HfexlcoXqEkqpf#P(!Z2&xMV0Q$|w9Uj;c`!bKHI`4(u<{wTl!Wg&AC-rZ9z+kM;i6(UkPbUiW1~1Hfq> zGy0jz@xK!=S>Q2i|I2Y{WJ8%B0(VHlS{F+ zPvM-R7ZsugCF-=x1joCQ0!az-tLI7kxgf#@@N-=X z+qE|f&=1Ke166{Mz0KsoPpCuKLhMv={?qONMb6)U6A#9aT*`SrrBAzEK%2{4sYpeN z(lBF{NY$fHR=LMjpL>n5!soa-S`_=JA&`4s8KV|&Vfqz!&TXp*LicX%VWU|v?Wx;*L ziWaxiLwxJray`sXrJ=i~`5j&Vy`X*WllY9cJ!kPwLuo2}N#c6a)@AM*5x+8;4JY3^ z{`c!agA0-kG)x zr$XHR<#*(otNaflTjpbm&y^`?VBE-yWfPZ((79#>U1*p!4E%(lChKbR{YG`0DQM zoZm>E;R4Ww>fp{`%&9~FO`3@tx(cS=Rn2jwBO?-o969E z05K*<3H(AvPkx6kZt;zujM?{eWBS3uze1-yH+~cWi?^=x<^B5}{Qg5pUMZL1cm@;e z)ST(Fyt`45mg+bGNuFFW<7ZQ2M%uGU2gL zvS+tX%xT1Ijf^!Ve{^5x>F(MzS(1uk~V z3HTa%TEIU}fnh-l)%H=sy*}|9M&=wwAkSFjD|?x~61Hsh3Lz!W*M{E5e{;6)c@~WK zuLo95(_##+&yZI=`b}pXyBI1lBDKEB%fv+m=z-HJ2@yAdE)>|i|5J>_A4K( z-Dy82%t;r|fnq#);!i4+Iay2_JIr*l*gviRi|OcRyS=&ogns_CY{TC1OKuStUIM~! zoT3-(*D+l?|V0r}J&7O|S178#;5$>j4*D*H^L{bqSg@lJ@$r)6;$!Ok0 zq{~V}B(z1m&!69u^}c3g9>8d0uQ50vobS)+`NGHL3Onl!onoyKlY_CxKe}J%&h6iN zUoucU_tTWI+51aQ@Xnpc*DAVPYsb!Bo-y(+iML|ecBV>FzuU|^dbhVWJ?$=;-8Ox# zR2T&;vgfLN`7%&i+a{^-;;R=l)KjwizQapr|3nK(iBsdbbC&CzIuK<*{77L#^x8$1 zGxP?xt_!-_TUH?PJ>$#msSTQ2odT`X-V)-m6E8bNM4FR(F08ke^ql0Eysua;X^TEn z(1VlrYQ2x!3@)bGB#no0*Pv1p20J@VQ_6BrELqJVb)UX}eJ{Q9-r=F_tK#wQsz_c10p0cJG>?j^GJ6 zGjp(4(1sAUu~7dZUu^&#`;HPdqtk6HZRL>$H|1|oqK2LlH6H(qnkSxu<5&J!)&8Kx zpn$ru2Yr9|*%w)};zf#nN1@jL;u3)h_ohW4$+x-xQH!N*dYOg)W_ZY@t?70^w6c+> z>8#F*-M#euY&zKluy{nbux>FqowmmD(m>;q*ZU?${!go-`B^|>%JW4fn?*aejUPGb z<(~sXB|ctdZ&6yYV*2ydpYzvQutWTl$`99x(sA!=~ z6xP`56}L}=d2QWSW9?btYqP({meUPhJ`a_%0r;d%oZK#Hg~b~jE^7C77VIpLwAgQe zpNX(?S$;ezQBaN-_*Ux@T$TE8AE`C|15x)SEaCxFgfJxny_Nmqe&IXsgSm$bT$~16 zeyvY>EAE+SePU|#{5Aq)F0Qi2@2s4=zTfnT`l3Cy(swBmtf-zRwPQLtrDH!?Yjx$d z;Ya-!`UbJ?t$n!yVo9z-72F8*XMb|-s<_OIE=w0|kHqb<&3C8bBs0)4jkYK zIC4&xq6O*HOzVFYQJ}H9GSPR_owMo6tX6AL}X?TSk1+@m>p^Fjw&w(TFJfs|$ zExG1E^ueJmNuOKN!vEy32jo|c3!nHH)6Yx0a%v0@4Y4BxJeiRsBEF=y*5o^N{6J<# z#%g+Q>{hO?w=*pVHI2_T{fWwvSK|?eMUz-&j>*o|kkhn|pa*FX(P<0l#2qtG)O8SYroD@^^NO|CE*&<@HsAciU{MKqYVgyW-3~^@&9g0 zaewb}SQJyu`o65BIIP|FPld0ev zpAVH6_2-2?zY2fi^svjMw~I%=F@17pJ!+?;-g%eL^7lBOTBt z@@DTvNlQ@*b`-|}08ANNc^1RY>GeI4Pg#4_Q*mn9%Fy`k){g5_g&(rs<9+I4(^Zd# ztBPE zSbNpmXX^zm#5B5Tqf_r0M0?t80AB=??hE#%*~u`7>Y%&t$lRpGG;QUuVbQB3wP>2Z z-aFs=3^eYgi9uKZ4}XVmzP(sQi(X8WzVj9?{6W|2w3G4g@`deFA)?=!Fwu{|a9_?K z*0r=k`H#Qcj{b@5^E3s5oTiOWM}g&|yw~v|U3;==);9_e&72>vLmKSje@& znNVb5%iA}1vwbdve>QVIgZqQ}J%Yxi2_;oo?N7C}?mpD4rtxb*Yh72+QJk+wp@E`G zF%(ph|95_@pW?yPUd)sKZ2|(b)Y*?K=UhI4&!}`3uu0ieV^R}KD(zaOGDzs z-6QNLO&8=eyUPWql%=l?7L1oDitc6o{#If^{aeR^)EEgzR*Wdmx^vr@y3x(B96&>?QhQaALXqOcUg_@ zkYfJx4-;P#<`!k^)qx4+#qo+k{gaY>83a0VGW7ulo6@ZfqLuR6^uFKdCN-xSRjlHKnN z5Pz^U3){IY@|h`Y1ghpWynR+^Qx@WAFn#e}IbQ!enx4+5$(prsNaPIMj!s#@;gpIv zOC=yP{T)}NW0ps=*`lkDj8M7QIZAREuC>_)o5 z&v-CPvrRm&-QMu_1is$bm75zV3lw!!{(1QK@60Qs0ukx@Hc1WIWK-|0>9C-et^FdK z=Qm70yWXKAU+&vo4frX~Ddm47-TuuTI_Mn1DSG_!APcymJj zq_+=8hW@kpd^qk%2cnsKnk<2Q`ca~S>n4wDfqLbygrc%C*I`JhXtCHc|Ldak>wKZi z%AQw5zcyI|G`bH-K?$3QI~CH_6kgDgf-4emOdU}cSKbA?6R#6g zKjW-9p3d{FJ^6$XNb`PJyv8CYGPGtaU0PQzz$d%N&-mX-g*f@OIKLRxq>`ZC1{p2;{RA@{PP+}KpR9N_@XE**T+TSzG?{*sVIw<2(6 z{CiAXXH!-bG)%xS+}awp@M#8(nK#L}4%$_aO0VV>ll+Ny3t`QuXPdGJT&ZSYOft0B zn5I7!qt(m}UztwNrMt2w8OA93@AMUmsgQxj(qrd;o;Krs>(I62{`x0E$$Mdx7^cd8 zQ}Mn09%Op~PoxfboV2Jf=Nz6JD%+5@>{pn@*YdI_-0H<)b6a?z9P&OS>ZQvf?4Fj9 zpU&-YrDo4?r4cVeH&^X;LzLxL7LZ0?a)MTTIm3MHeP=W2;y*t{Jo6VCD&>OPr3xD) z_ADO3g0)hfmsTA3OT(ZTD$Jb4!BId>;$;u8$adr7-Shg7ywqWppYq?;(@aAj*#Ozs zC7Bd7NLkMI0UO+h~4imW4=$Sj}NX} z6>yDjtUO4#{rMH&ENi(+g`h#kYrWTqj~LA+&YK8#%-Y}OztHX7LK&8Z*j!D~<#P-1 z4f$&^KJHUA8bn5>UF!6~9W6~Gy%!q81%5e0MUDJ}zZ(T20^3-o;7D$5ehQV>{oP`E zbW7qZic?Cs*U7O(oL0TdQQFt~`XA?#1Cb3inPP}o5Tyl!sdM9>=(kyg}xyV2ce&TpkdKa=b*l{~Q$0FdE zF#iaB|KmQ*LRUY>N5xIWb`_f@n!${fe2xP#w^$}iyUZ>1$()84xt3}y9|_*?pvPu{ zpWS+!j6?6dX{zTiwNL)tpmV*;6N$JLuFi3phNd142M9wnUR|xY8;`PnW%XQJo6>?X zuTJg@x*Jl^oHP+r3L}Qkg(=@{`~oDWSDMCR-2OQ*yjMl= zzkC42g&HxuXGM588X+ASch9K%y$?(Ka@~WufG=)}9j&r;pJD;+tq`6F%Nt!=Ha$y& zk?TtHGXv7ajtJegE-}&@yC0WV>3_W=Y7e{rSu98!ygALrQ`@}jH54aZec*W8AG76u z@t$67{lDsWYWsH(8NW*|kN-_AUDPeUnvilR) zRK&J+V7jO9pRe?c?4|WeYRfG%F|XZrElr!7o4bX}l~x&U+%rn!l^T0Jw^lnm2kp0e`hkcCNbErh957mK7oBqM&)$;nQfyb~OcJi5 zum+&}Q)cE9KYr)yOF%4;)S#!iMb~cKe3l*m5av@6WTbUfLz~Xb7ag*<>b>B;9C3k_ z)ESRTa&O7C6LsY)YOFd%B+Th0bn{60s7EV|urB7kPBBS|_Iy~J-xZz@i^ezc{tXl7 za{G{mVe1qbIxzjxD~jE+5SD(|6f@^r`})Tx!#A)%SJ#6+w|{N{*ZN9U-FrO8I(xMg z%?Iu8hsUOdUu*BaYZ$%c9I)21^35dRThmn`?p)Q^SFL~eEQfkhE6s?EPg#o0oPPsc z9XhG^4Zo$&tiq?Cv7v9LE?{#6=ZtoWk%k6jhMk`)w)5zDClB}Tm+^jod#AjqDWEYx z%-$E1kx~6K!$!F%qU@>~U{Yq%I-J{6VtC>zh&jCMA+Gf&a?X$+XA+LHDaEVqI<(Sa z5}4f*_eCC<8S%G-YkZ404vx&td3>m$nt%1%mxnNxscWdUxGPEaLE8>jT1n|VexC6M zCOjcwKS!dnE(C8@h>NJSEj{Qeh+Z#mK%}4CHe9stFX6WU16%Ze&_G?_?;b>U%#v*i)Oy}%RXs@&5E)$il;u7 z9me-D7|Y)G@paE}#5w(vbd11#adt6b5^dNaA%%?IMO9ZMq10 zjvdL&`)rv#JXWOmZS;Dv1$6Ijm)!%X!Z4TSpBE`ccK&Ans7AAX@m0loiDql&x+ayr z_GB;=eksy8KIbb^JU|NSoJl<8+=5+W#0jEq9(p^qki}LyoJmc;WTH z6}?CPt$%*b+Xa-m;lk{j)QuWVhI>jTL}&M|FuOqzfkZD(Z3CO$uh{7a1J->u{}h?o zF}KiP0xMp<$PNFKVscj;w?BHNW>=K!@xyOFY=<;vxX!z`KKX&^S6{1YxP=Zgiq;fu z2gdC4s3HGWdupB17q75qg{Q*V=zKLAyKeKCj!KHhg5Rd5eudmpWw)qtx#BoG!>UiTh3-y({2DT1C8?A;z zL+KY?kWDoa*xUXh(XAY#J>~!Ki;elm7gdNGz%46n9>aA7e+RP-b{YYQg2)_$r{<7! zR*O1`G3;G=s;lisVJ1g|MxHfhwqVsVj4H&k(pck%my)uopiaa!gLW6iRF=qL1~S*b zdg>=Wj)7?pdi0-dfIrAg2BI0)_%khy4$r|)FZNG%9?Lcdg!*04NGL6)uc*qkmCK0` zh2s8%2htSaiY$^c0$6N<<)6`Um>_ZDA=}JXyy?NUpJ~iejNZh=2&adyDku8AyT*$B zu)q>p74C1#s+oBN*`Qz%b93BROA4)BGMy+;{Qlfg_ac*ezn}pY8yJdsloSa#poxcv ziyFrM#l=gnSMm_%582!*Khzh`FxAwroHWf-mX6-{myKhPhnf$-Tcs`@mN(xZ(Z}HRP>F& zsO^nGeoihA2Je$snT3U!REU=8H@=+ICFn!Z!qg!GL4bVyC^%lOHW!A@`xq$o$Dqu< z_JxfJ)0&hFb5)j_KL;XMZ<};NOF_3jqKkj&F-Cv3*YFQ8ijd5wMpCMwu%YQ;bFOqn z^u6TgO&-RpP=Q4GJ!`LlchMW!Zj#iQ1W~W|>2~~!E{i&~6-QjPOGvlVxkP9sEOCCb zeZie~IZ=e7Immrw0TXZ_&IO_|v+Y{@^7Gx=KlImgsKq5;mO&*$>4)l|hwM}BH3|$= zwgxMNvb2mhiX4U~Q(lUOE%J+|d;M6^T`#B?FQu|Ls*kU*amuJj6gLOpKiR&R2~7U| zgF*D)bwSAW>sOt(D(P6>JgKp_xBs4}u%&RwY3?$t+z2xls=ve6&dIUPb2kQk^p!DZ0duvo9q#46wDiZ;v zri8S+l7>EHn+*|ss38y3f8j`tZW!_^L0D-wiEu}gTlyc?B=Im=^L@T){9VkA0dNFx z_tt8VY=~N{8Q{>kv4^R--sLE&b?FlM^w|>))A|ZmE!SpCl*i`xy}8-`cXQ_cal%{X zpoRG-7K@4IDy3(vp8!T*Pbw{Yf;OjUdf(pPLWohgpB&-WbDNQO4I6w?0;va;C7pTh zd{^AtWx?>$JKT>>tx5E^LRz`@iZ<@dp1+&%PW~-qVI-C&1d&&?%gD^m!+oK7uBXRl zIaKc-d^P_-G>xKBXE-rkYAzxo5@Tm?zk@@sule*Q@|qgkK1>XA4-o3hiM^a+@$f%? zCa(|(!J=Yf1sxrl1A~JyTQtSiZXG|WA0C0kG=zjRue_-D!4O*Y^Xg8oJ~uEZ%wM~4 zx0Ef_#MIQ(j+Y5>9M(q0HLHrDTDzu}CZ0=Gdrpmw39Id;p}9y-VOQS$y2o(15!A4Wa5v#usQO|h6ZNhLCQ*sUDM7&E-(EfXBrmjfI&i8bn`ql{W zw_duYu)HLAaKhhKZI;`{?3s;&#*@Z>0Md>mRUQ1-x7c1o*~YrDjY0FB?sVHCbKir! zxb%mUrTFV(Ij#6Eijt1|{IDqGCeMzTq|?OQ>E;b(si!YrXH(Mk4isnmgbOJECBF{4 z54m~I1%LU3lEB+iGP_f%Dq7^zMu))l(at)l3|bnkp3&&0!l%(*S_V(NIx^q%{j}+d zjNW*6TgUbA{bKd&OBPgD*cE+#oCF4U7tHb1UXw>w4G>vG-dWcjM2BD`<(6MWAM`fZ zgFlYAfmp`&le|<^^R3;@&<`LJIo0;ZRB1wnISL-pypNd~)CN=InT#5?u1%R=rx+T@CM0zIj?mELG?tOPC24hKzDrzj1@wK$OXiWny+G^u z!~~PK#?{F)Je**?yXKI;zn?fX8qGGfz*j`!?etf}f0!EyOD9ds)|(nz-tYQb-Wu`Y zqfG_GdYR1&PDe*yQq~e4t2c;S*kN0Y-S_Zu%XWr?-aK{q;?vtE)S^$Y(`OS;-`UA% zA^K>tJ2~2@sy@NP&ce0~4MsjH-<+Rq%pDn7VfKq-_u3FuCGOXHYM!f>prd*Ul zHhHpD{lc#EDZLm~g-2ApZZ@bD_t};%{V>QyxxHpVQXIMgt?RozIQgS=))~8OzQbNA zrXukCyTozJd4QH_!aFMZFhLyq~T^dCU}@{#u5s1XrK zl^^V-!=jP;S2V((ap>Kz@U&XCEEZV_@_>~!n*YQzLuMlF0@(qVZc zURo~MdOz7$?yenwa#zQBM>VFwaQ7TTQ<2R?Qc+?@DdUm}~S4UUm;lo5}g-2{5aBnv9_gRTU zNsH00E*f4{-xuEN-S-atB3T530yl@a7ixp#uhrhuJoY|N$jH=EmlLeXBN?!)_w?ut z3=HHC4ZTQFl@JwOyCa?L<>duwYML+G3EK4#{r#Ol_p5!~mF3y-;~9kJ{dd!khwYTc z5l`pOe?0ELoXUW7UtU>pF}NBgSzJha9kzLHapa5mcixkgIc1e%80n*v-ZP(foij}O zR(P&gE4NClCa=-@<_&$cXI5JKLSS%Tfj;~sIx*na{{hrME5DnW66bE3gS-V6ZwS2HM)4l2Lc6n5G`nCN zgW0JGnw-{jxn7*(w#R9P187V)&ChdYaErEXu+*bFBMuSfPnsDmIdwObQ9*e#Z_6~* zS65P91%EE7634iuB#Uy&TgA1ps9eh84;g%pAF@9zLfmaPtNqJDgLq z$k<2EU|ZJ-LnC99 zoR%(xc$BZlfx%(Q$jqX$ib~35(VOFI2$!gCd5@qzFN91cWVF1pni7*!_!^wz?e3?m zp<#6OdKlF=Hd*4RZO02(W@2)hT01(arml|Cd7H5=tQSHIk*}+76x%TC$39>?@ZB53 zy{PGr@B*}OV>IYd;-+!@443c#J*#cvbBxF#*=&GG?fG$`mojU8Upau29v$1V^wFR2UyhXOEqtGv_0zc4(Sr`zt6cL2Hqf>`ZP)uemE!-TU_>&i@xN%(U56w+bUTQR*J9m;UTu-3^J|}g#Np$JV z3EI2wFy$4NQ!I;o#{?g*nI6i^Or^fQVaiI3ptG0GQ*nI{b<`HqrH~68G)7T*k0QR) zeUu(`iB6t8OJS*HG&0gl31OG%_~~PG@mf3$Pu`&6njAWM?lN7zc!92j$5UcLBpp0@ znzAapjYfE1%L@yrb8v$CTKL!}#8Xm2BE==7Qe)3BwN&MCAe>EQd`t_=8)$N2kp{b( zsVJ|2iuwAkXzrnTmd|!Ro&^QPRFGdpRrU2$U0OuhIYrbnu}C-OC#k8XT+AzPXs0576Y)6h%hI z2tiF@F&0_WWgK(tdpS#VY} z?-T?T1HyR*na0tv3EI7PAOAcr6W1s*h@oK-V*ijW#gDhjs%m<5|3MbiF|mxUEnh6~ ze)aVB)1UwPH`@8q%c6Z`xzU4Unq@kdf0l-Zg;Q8~1Z8Api~DJf%%;9QZ(zHz{DFgq z`Ma7c%8s8rB{IWU2j0uqmtNULJw5z;gtt#Fe{*qR(+MhmZ@=e>c&h8+pW~Zs>Yg?p zGlM~r@ElZ2P}MHJrl88l%zW^0GL39PLCvZir2F`s-}R6~P(}Q1#?Q!{%9vZm;UbwvBXq`%hhwW=0H&K*8+K=sd6gsIW!gxpY6Z}z*^kvd8?zIEA_a0kkqvo)FzIQ{1a4UDf>7;`paMbLNC4alDPjgDl$@Z zbyW!u3lsNgVwvUMmx`%?gB9F&ipklI=%UNp17SXU_N)+8f7T@)Efe72Mi5kFOp&?6 zgl@f~qsDQcoS0y7DeyZZJJ?#rf?7aBlatg?lto2#9W;onDa+3I+&qo6RZ&7(5lzl6 zP-kThCFhojY-D|D0oAs2QgcxzB^OlCz{n&`j15sc2hIx|P>!}&Q&DBBk>#8pr;6fY z8k%0B*4#*X`p^Yx=o_S(tZQ`aTnaVTWzg|6*Qt#Iva-|&3W?66w#sOF@uhuK-9JwK z%|&$j^Z}~u7^nH^A*!jWq}-@8^xXcF)Hyyt$!9{S#1hn5YN{=#lEM_)|Lkr`s~w`b zY2G)M)PpRmFzwxOYo&K*5S;e|~!*;_#eUw)ZFGm9xH{5%~y zpF)$O;RVV`xI!1>vV{zEcD7MY%q5oK4C?9cp`crWtN!cCemui)v|-#kn#sg~B)>4Ugc!J2sWt2B)dBBAb$Ps%UJem-4f- zsIsn+a$M*F0i7vhaLq^wnT^pW2eJq!iog=co$kb=D zI5qJ)(3Kcj#@M)cN@YQXypB&!iS5y~`@t@-ez}<*E==oEz*U)`x5_u9f`<4++Bfdy2uHFi+Q(KcFiy+PQcsg!%^t zXy=PBi*Dwa_8&MXUU7V1*zqEj^Yys2bi-Qq=HfgHYG1&D`hZPPJ62Ut@8RwE)U&BH zHMjXjpYNSspf5X~gX$b4$r98(@$`RLP~U$jiN-h4qjKalI5AK6KNe>^Zbrsq$ZqNc zQpDj#xky&X`Z`e8l2Z?YtG2xi5KD@7WLvs&HU8J3)|LAtw$UtCx7`~`J8s@0+kqoF zm)p|t{y6_YVRJ0(yNX#ZkMp|0f@)oCas$N$W1bLs@_bz^jB#=CvhrlglPueJ)OQ2* z<-t(bC#BguKta|B`GsWgS1ft_aV}iP-&HQzOo9plL^iXjsZnG$F?bOsWH-e;2f8-= z1ygNJ4V^rBl7k@*kXQh9b!w`s#ehjmey&q=@eOc*Bq^x4?-Uc>*YLJsLU-dp1a+XL zlroEr?pZ@EWmH(xO2Y%~R9sRcg3+1YdP>VR+U}_-qVgsqV=~fO#dTX~Zfb-Yt4pb< ztb+O`jblJJr>Ul-fTe0$d>rd(;Q0+A@NLRZqH{^5qMSyn>Fmp4l#~@o=Tpsd*H=d2 zp>b4E7)56)Adt(>G9o1sCB4^QqEnb(q==#W(K<{ zGdh-o+4J<`pPr-CDn3RxMkqTvj%Am(Z?T&N^)xjgCY)`h@QdfDp|PE=UJawFQNHea za_NP~|3W9P#n9D@7bq+$pQc31<87n#$ zOof=Ab}@pgdgf@PC5bLxiJ{3GH>kNJiz?c%@arq{InBr`6UPBleQ^#|*7r~?AK#Xq3A!=bK{*ALgd^Y6P)NCaoU6<7C@v|B z$|@@4BpuWK&n&R)>;K&H&v)HwDbc)ZDGcy#I zkVvBwlcKJ!4PWqK6YWJt#ZVTDFhI7kqsx>%w9Dc#&n)owUe|^v_)t?1i%nK8%OP*; z7=Pd66BC87W-|FK*HL`^BRdOW?dSbm&VFD${X;{>{kp>=hUDU!;MazxCd$su6LRWb z+x)$RbV5*Bs3CsdK-cuq(Q(@IDhq09nYb2l?`mjx1Vyl*V$dnvUyBSX1a+7N70c+_ z@&~ksEPib6c?XBuk79<1QpqGx$F&7P(SFWpl;*esd)SgM%XQFH-0al%W0?oFYaYz zan&soD0lrjlK$&(fjG~bZ8};vKpzgRrU$Q8D1ut#64c|k2Q{7UJCICw?T%wXjijAf z_2$x>YnokHqAxt3PPhHS6ja2?Qce-W$rxIco3S0@Hy^RMC8vnRWg9Fr0Y9e26Q1Hp zYrQD<;TyU(AY6)^`r|CNcC39>j+a_~E2BD=lu{f6aU9HJp`NRC1A>YRLtY%##m5_r zGJKZ8=?$D{QP**jQRb|xL%C3_Hbl93;Lw(@i#Ev0asDu~$~8X_Og2*&U$AICnx&(o zgU++0B2$XVDX0F(qBro|Q;6jL{rjne0}QziYrZLj$eyO8r0_m)V5ThZiRIjbiosoq zIB1d?RD6c0W15n~EqkzgYy;{f)(xSJg}-!|jxtnM(r`9K7{4Q9^Pq4USDw zXLT+mXO~h>Uk?>?fZWj9M}3{`)Y0F|LYYa~h1FD5+epJbtyEH0LsMdtmZ-6;fa=XI zU@bWjwDaH@Dyy%fge!;WN^BYRv=-2@11DHwYbh-@l)|$ssiQKSj-I$eQ1H3o2D*6W z0OeISQ(EXjx)_&FwOQBbk1y?|#({21I(?B+lX59FE|aq2B5B{L2&!%=p+nE^q}beM zN($RguN*&5)%8srba&7>K8NSiN~kz3gr0wKFV#1+(}fF{sjz#BZcKI2nPW#NJg1Cm z8=7g5MWrqyonrDUDK{~M4j&IQ?)|H&qoU+cI&w0MN^8p~>g;h!F7BkJln~l^y@kvnUEM78y=vX>_Qq5m+Va%>T0E?mTqclY@t!}bTt)a@Hyz@cXL!$ zkVoaU&6JbLQrpl;{hck8o1I6UBjZ$&pF{N>qqH#EO4)f8LQvalSW-$_si&=ha#&2; zd;3|yCRm2_0xIDc*o{Et(_Kn7CpgTw2XzXA&hRudw;-~C$V$p7DJ6|Y#wL~}c96Jv z{w_ie8=G6j_X^;CM%=r(c;kj~A1NPK-SWPW?W5~$d{T;#-}uBNs;#f5ME)EW9w|n* zEh;IcqT*7TWzoViy0*N5Wq1==dULI=$nqyjb!|P>)HR6x(k-h4T~u)os(*qyC$fsT z2Ne=3CvgvIjO!j$berC_d+&+`6<}YGL5CDi^Urw*FeDix?Ed}_f21e=_(wW={DkNh z4x!bx;SX4Llz(pTVKK$F@7;Gm{1M>FwNU!{H@`&}E`?BfMi#xiYmewg?Ix&o{t4=x z9KgSoUw_2%cr>+{#%7G)4>jH6^qaFK^x&Tp>Goem@b75-^8SJmeE)K#QnbC9rt7UE z^bg@R^x*YM`Unduy7;;>s0H-yQ@JdtSwc|p9Mu1JXEgm!T)FsJwizdE)GaTaBr`hE4YovjY`**p#gy#F``@;HDEb1yT4DmDTapPV5sROKS|g1ES_tUQ_W zBr6x7S0F|5K&a~z7ot(_&vvXU*e`68L|134B5r~=&xdkN*nfz)$~T*!N-*{G^w7C; z=cu%_RGZ;kDVVA%D{0rRUDUt86VmmM}58k5;g6i)*sFOoo)Z8&70yMnu=o{uhbdH*V5E2gS8V`S0!u`bHXEu=AX z_gV_mXwQ)&bn?WjbohD-O^VIDLCpn;bY%CdbUGrPhB*?R8>ptd!V1wXmS~nL(_<(+ zDvKIxO6cs#6Lj{}MV8278kruarjl}MsH&lYoO0^v?VyCKCura4Q*@o%YGWylxps#3 z>_0#U4<4k9s$LptE2Wc%chkA+5iAYGG(0p)Wn~rAF>0)Bn1%WD;a&9V(X*6W*G}!F zC6rOx&Zl~s^209Do;~|0EU%u=`#j|*UZXvGcT+@asmM4EHu^Cz~=e@~LeI z`P?Oz&^GGl?OmK1rq=de!r>k6YonI#F%GVm#JR!%TF9tQ&KUz-A;1$ssp$9#HbUWhRU|G`&Nx2(VLW{TG-47@YPKP%0$si|499_(9XbuCTu=jH4J zz`389T@d%{4h@g83^wt1rGPrRdZ?Dgu(-6`sAq1AcW#KT#yHlvhZUJuZ%ob1iZ0d{ zL$1&>&%Hp$Po1WVm#@%?Q)lS(+4Ew+qGKmd(hECZqRUsV(+vOY#WK3K{AGzx6h!~f zkz-WBKTCG+-A^U_GhujijQ6=)08!U18z|V`v56@vqR38VT#Uhnxe|4!%M)4F#20U6q3EWn#-O6i%T zPej$zgQ3;*(ar+>RvxamgkbxzNR)zIJnEX*Yn zY8;NZS}pe$i;E1YSr^EdR>t!FwBZBy_|a@jxP~nE5li!SS#{O8Tej_Q=2YDl-M%{8 zzg5BURQFkkKO@S_a(_K;O@N?cBjm-0lO!$}{_N`nS#_1aG{}=IELZS+Zr|K~$ZTFg1(+~E5grAz(u}DmFjZ7k(DCEP zC^R%wNB~C9#l5LvVPRrKVSlrylDSz9$QXaS`J!G2%S(24wiwA5uj>0Qvn%0hPhC|7 zRMzA<=n>P+IcjUDr~U~eLp9RXL^Vy_mVhXSBeLLTx)pMR8?I^scD%)BGWRmC^kNk zl2g*fXO!m`6w}=NlArSB4#Wexu+NEgNHD4E4-~X9F*PHk z4acaWs+!t5x`Z^L3v_;=f5!+NJ0Q`JQ%EY>RAtQ*=n@P$gdk&-+oaSqI)5>QE?vGV zGTi7gjm+c7=(wn>Yr}tLX`Xi^E+L8j=eLj2^E-D^cx1E?M`WPUKA6V+s@mlP2kXNq z&&Z%+~^Vnk+iWj=7|i1=dwMoE^-EU}>GvY>jq2Njvqf7y{v?L+dW0MFAb zpTEa1mecJIM`+vf#eEEx^(MmWJrxhl&=(S#=pzxe^p6oW^bahkA4V77%VqSwi-w${ zi|@Nd7vC(p+av?G1yRz{8SE%rGqGIq+(3q^d1us%L8>VZ%XoTm!~-3-K+%3R(=d>M81 zR|%K6VL$o3I9@)sb&Fz?RNhZ{PStW8@69Zz0GZ3Rcm}EprqR(6%3&G2e*HSd#Kcg2 zeZ9zR`ulhpC+OxINEQ_n>LYWj5@;L#Pd90DxSs|`je9*kPAt!=AUUObw6B9|+xx{c zKwm2pjsT8|=?&&*AMk(PfM|I_h^T_QkOI4eO0+WR3y3Dr2+Bq>zu=k4sX3aMoE5Lr zGYi&dFgMTNVZK0h%lU(pMQ1t6R?_Zzf*&Ju3pa!$4GfK1(u;ZBJ^j=_I3nsemve$P z#wMmk=6HN^nwjwnEQ4;?xPJMccx`HJr{>lUs|?rih>a(COco$Rz-MHU0wWeR3 zD}Sc`@j3d!&UBG=b++dV{C>y1tf|%huWv1<2Hux%rMA+CBkM&5RTb0+uap~Ie9s$$ zB;heLZ#tY#??0JE#l7YLdyeT!_c(nqr=2RzKelf46kgmze+NMo-BHch7jdy~>h zBCaPK6Ctd|u@l!5mpSXI80YQ=jk8z{k5AGl%jsAEf~sR`W?@aG5p83e*K!?Bre_z8 zKAhe{_ry2QH}X2E-Moi)Bd7R^K6=ALk#sNOaKvVO4am3*=g6yIAm-P_+bCCK zS|o#<%leCmAIQz`uYtC+)ltrg0iO$;ZwWs8Iw$J} z1XWy+I7$9(n7p{GlPq^wxd6SQToAVP@q2B#2I~uibz(W|;G%4e4z4&(=Jqi{>`^ZE z-&nufGPeLY7jmD)KAPuPezAjsx^~RX;gK^l9Q!pyonugdGLRpU_bNGT(!_ypM+%$jmKG&UhdL|&I zVGn&Irjb4!*F>L;H3aoz5!FIakwF!LdXWY7d?CH_bRKO#mPJR(I?NXAQ|;tDeKxC| z-kR7%52Q5HTM`=SM3wv}ujj;}aB49>Ou!Nui4L0wyrNyTWv7(59B9|=PVPOq5>XWL&Oq`qK3 z^bd$EUv2_q$${MECJAznfz*D-QK5b86OA z<872%Z74C`a!}&MH_5gTKU?cn5Tl!3fyK61?eMF1xm7@j zuv{;Y+wJEuTz7CxaN&vL#oH*3)$)#yweODo-OlZ*zZfM!L0vuO(cO0j50N(pPZE!j zS-{{bi$OLi3(J9nc>x=q1f$4$1EZS?csUH9VzA&~o<(87Gjk4RP0Y`;CRVuu?$>l; zbiMS({IZx_EvAF`4+a7Xj0pxAzR`(kmeNTfrg#m?>FVN5P?6pI$X}DGYuFfxJdg<^ z7(aBfnEv0NhSKiX2D8+C8l768`~Q$2?mNXgu?-LYC5fuL^!ISC^|Z*_y}NdpJ{I3h zpGjl<|__$NTL;QBc>K{q91Me0^6dtg>|Uy1K|vsWhH8? ziFjUakIfbQ*haO!^b~9hj)BfaZdKHCBEbGT*p$b??Bc7RX(CT3@0bullu0?|9doO% zL)I5L6V+`rYs05Eg3@g8U}+MK~{~ykBqLrkk%jpGQCo!O{TO=FO#XMz?cLW1yu_k z-(fKB2V>}R=|G)_E}4RH{*3zv6-L&HAr>5T57i^*`^hLT^BQ1qWLz+`0%RM3g7u4x zDW5xJPsKf_Q!_zUk%fhM%Jwiw5;AG;emtJC>IcQ|-$16RxkdWIjtns(?>{}2LOWRO zjwCnH!6bu2%mJ3OA0H_c>yo{wAgT|ru$HtLzw_67I?*ypA5UtfPbam|XCbHw&Gc!O zR96>YbC9H~<@6v6>f6s2(6^$isCU|Z&?Ct1AFBI>ghEDDIhDK{8RW!9+P^_vn$v3s z_`Ju^y&@h}L{10dcr^x;@wQsdugi`Nff93CZ7Dqu#d;Ah>z0+MajWVCifkedwJoE= zCa1=+k=|fAgV>f8?C(~<7PA9fD-P7-ys3boYG=4`$*H*$a))`x{JNOcixwA(SuQRX zSsv&C5*M4XT^9as&uSpPcqY2}8x4*RE6LGCsL_uxtx-^Sm;#C7GD;%;Z{O z9Ded$x$*n?8Ka^cI%S_h`ga1(NzM=0fNCXN?mo z`{}la`CLFweMp-vUPfGpa$B6paiNbkyn?t;Z3{7@Gh=ItqIP*8+ED7s@<3fdt&euF z47TIZINJXEM1e?diS6{Gy{!t4CxlcU3-R6r=L)FDdDG!dP;v2!6O9Yk+zC}qo%MCd z`f3|)vI{f*+Jw*QxdYbuTiZdB zKJbTFx{y*%=~eBNP}UY?ttf5X(!~2oT!{yY{iQ`qY;m(6pq^)9xQ>qKB7-PT6l!qH zi}XQ6fr9wlBnTHYyOYo54#M)A74X?0)WrGOpiyw$6S>~Grf?PHIBBrB;)`rOmzP0^ ziL0#MMu;R!c~!U+2$}9&bfeBA7cx9^-bs3`mUCY4x8AulJ!id`+$!UK`srJb! zC&_wFCRh0;NzVxnoJ*^rXRemed!L9C10p$phu*=ztN;B#9!&@M?9RRMjdTzHj+ejN z@hX0IJ8-T~l=F5Vv$vj8Qr`@HA-$76liE(7XZ{(2n#gh*kNZ!XgrNSD7{#|%+=KeQ zkW%`{)iO%%)Mt7xbd1qElA2gPjX{xo@lLM$fz)P->(|TfT1`=)7+Hl69I+Se@?U?(CF=Mv> zf#viMNfz54XG!H4*ROWB8p`oB;NvLHw+Y^F`u+AKsMcRTlnWX=jGgk;l|aJFSqDcw z5bDKzD_$5I3C@d&aUq(=S)4b<{IY9*SYRGY>GsDH%r7$YL?Uf_lttD2qTA}5>sU)c z{f9rr(4&{i>09U8>8qz(>1(Ikg7^iFm2bX!YW?`i8t~9$@a@<<5Iwsb%mg-@Ef3$;hs_Z;#=WYjgJ>%j|2X{Dm+MU~;uGI3z zr`&l1*T4NQ#dIJ4j(*E;-G8rQgk3z(6oVQ0JK^{B{lAYFzq_67`C>cA0(br@f)e<5 z{d!FWBeV3)>~5CR4*Eh`2YoK3O$e&h#W#+B)>x03sig<6v!GrsqYDjsiB4e2yq}NP zJN=YX$=g{{|1qPL@<#%{=Xbf&<=hVXzdydtv6vE<+29yW7jGkW6S88&hAR>@5$7vy zncL!)Idv|lf;QC5sf;yk+wO;RsP%dMq8)A@1;i)Z*M%m)MFDv+4vy{Ox61LQ@Z*A83vkg&bn|Zo za1O5Y&(g`sVpA`pTIO`f89h z<144z*Nrc)0be@h2PeJrMNgb+^U4=aw$bNL*!hB%c8kxOe2&Y#@h>c~{_xNKa*TsF zaMXm~f5zm~;Ao3)j_CQ6gHM`#;&8K9K7Po~CmgRj{_&vyeC&XWj|!UTA9U<@@ez{` zdt;xS2OWIKjW4z_`vb>7+G;>{*+Qr_PSUa)fyc;3a`7Pam?XKI-Ms=VRp8hSm0SMc|!|JPEFf(p<@_uWs#QAA0%_&w}x zs_Y!2ue_W^cd%sY+VY1tJ`_&pv)j!U*LJF%n5Q4)_t9t4JL!uVo%H!M{&~uR3O|TSE^F%(;3t@)VgXL{Nd!KaZ;^#JC{dQLvclBr@B zP(x5ZZt{skO+NX!$v+)*=O1mbbV_Uz+T;OKP+2a4OGF#>>}zE4biF?436@Ni4>@qj zXcJ2)a0%%L_BQZq1B++_OQxOsb?kQMy(ZRs7EP^)3fZi8v$83s*`(PsC9=$72M+kRb*9h z6|kr}x&7H1Ew{0#O5Vuz?)aN`K?RUh{Om!(nO#CqzsREcIoyNFa{AdM{@E6f3@Qdm zY7`mN2d-7o<9Qu4hi7PcnX-pw=ws=v^!8;)Dh5@;V`+Nk7R}byZhG`=DZT!uIzeSg zu?btWQO>cS6&rep1^otbzSQmHTXg&$5hP#d*>!>j`mel{Rs%S90A1ges`w;xcx|I$6@ju~GhvcBR<6 zdcv)Xc3pSQfqD_!PMV*8q1g)H94Z&6*e?~`GF&d|V^n1I3#@#RQSRy7t>q}b5Y)$5 zP``Vzi@tHLgT5JL)zAs+TA`EHm7)_?kFb~m2=g`fdELYFH|UY_LHd*_r&3VAkZyGG#UM$a6@uDi3F-&KYUsx-okO$!iDUZEH2p(b zi|D$m6H#w$XG#5WNe_)KtlcwG7g(~t{Yn+uCJKAWGW0N}TR7W>rz!kh%t$l)EGo*kn`NZhL8ZcWMaK94U}QJ=np0CT(B`h=lNBFGWD~TVuN#j zfcoc(N9bo)JB4`O$GpV^@11OC;Z#w(0LyN`wydf7`QGQ91SX~uuUS;!5L9218i=4u zZa2H{%8_Zg$pP4)^>zlD_m(Ce3P|E4muf^wLfGTkQzTX%~Gtt6QKK z)K>b87$m8YJ{(m~-(b;f7@IX)@o!2Rn4$++QZZnXPDDL{XQtkl&_It?_S5VdJtTEv zc7Z`@om%i8f?iC9bq>yC2)A zZi}@}$F@K|e;02Il4QQV`B4+hll8W;pdyA>_t^qvCh}Sc1l0*~j@3VWz&CWutSS&x zWyRKCEQGZ1h2!r@!-awk=Yu46#w@qKETVipcE|&cFOcYOj_t&D%W_<-60_cx1jjm% zzi!~z$Ypuow?EEbWGSb}sA8lu`~^z=L87qcf_k}<{*46{BBjeR1zA2$QC&-P!n#s) zvTEaui?5vSpzmGmq-2)U%APqY?wYkIu`#nOiX9YbD3o-%DClsf(D6ARuk8l;plyco zOz>WmndC9KT;_=!C#@<|lr3p7$TD$$&UBDrlG&^$qiNDB=}i*~_&iBz783!Xww2as zklL_(QkF}-JITu+X_+L}EtfH7RJYe*` z*x9bIjd}A6dfvo>sp6E>ds#3&agTymL6!I`shA(2pvq^J7Bmlw-^*vS+QogNy1L5& z?Z5wz3Di9rm<;+e!S!Bg7^BZ*Kumk+E7?7kph8Y@52`haF9bEAnf@`Rfj-NUn%h5V zwi4JB%gcQ*HE>CNFZ0%fCVIARkk&#`tGmYN?Ho_t^~(q&-V<@WOYp2XT#bE29PVdK zuS1O$Wm{ssN^FfmRCUXmycy%!$b^-sYkGlh+r5FSpgLoG z5i@U~pR5G+oY;`Zf%nZ{k5f_FwT_K)d{=ZG`3(!IaZxKL$o%a0XuYT}Hrjsi%XXdR z8-j9RMM_`xImZPo%ke6~wr>b4Temo0{+u%hV!h}BA%xUGbboln?Eb)bI*02is8%=0 zAYReQ=t^-4=USqZ(Liv@sY_71MCP=VMN}0)7t<_=pxCnL-xN^G2pgMl)b>6tE=u0f7kW+}Ml|fDK6r=cl9vM{JgPPbvpNwx3k{Z)BVKlPLDSmK@KAhev zo_VU1QBS;w>!XYC;noqerPZ6x@b=vOPz2q>yy@3!yv^~N6{{(6FTbuR4p(DkMo16h zYIVJ$OtY*+v|%hS5*{*E6>XU1{%oUKUnEY@4)#^k4zEMV>7OyKiuwFgSx16nvy8ZT z!*QGw7br9Fbxu5ib1U1!@j*LErglZwlHWj3c_*xsgB`{OVPo7XyYpri5P3k<3@I)S zYsWUpiI)df3^^_2%RKnZhGus+1$-V^+PD8v>C%g=sd;?7oyWBn)W=y+zjvvNzGceS zcX0oohHo?9Jl9U&@B-S%lf*fF6|L{nr_6tGeuJBDdo!yiRnB zmeQ&N_hX_Sq)<0sq7&A&!YQkPVu>nCsmfPbP`}TDif*@_1XYMBf>6d5*K%|BwOHh$PWhv!Xfi8Qh!InK$coVXw8X%(fOlpmu=j`00aM!ag z?hJt2pQ$!@*5Ed7>(0NGEK5-H8Vx}`lexNrdLM&PfS|RSw z)$FP4;`_POcELX*d&+|P2^Q2{mHjk1zbKfP3mhzm?#gFchv@^UEp&fMi&jQG@h%qB z_a-+{2ut#6W>Ww6R0+NAC)YU!b4h~{%eutODo6abk{DOU^Hy8@87M;dCY0-zHL;vP zwjrdF=P9!3m-MLZNKaAsRkUN@SIomRPc1nWi69ef%O-J@fWSV>!#mC_RxQXbkcwF`-eii>E9g7t|s4NeiYh6kH+-Uk1io|*v3+9 z%(JA^eE(m(J-_1Z{T`N+^L(I_%C$l#pnl_!R9}LMZnb$VrdJEQ=y+NqozHHi#M)uA z8?H`5(QS8@io0j2gpnCnosg=y$tjcDIzed-V?sD}SyU;fLQrLyNfrdv4@q^2>7>BD zi>;q>%7W>XQbSB96j(5wu#~3oK1P-HQ&i=s_#B9gdPB&jnX$BIJtrukW`rUuhbi7| z9*bo4u$X6LHuc?9y#W!0WZAtZ$%!K$H2(c?5hNW{Q z7}Dw|)h?n|iAV=H&RRkBAgNB&uD*~|716aQPjmF;PAn z*;7MQZ$sIW%N11P9@NvBt*a`i;P!_i=zIHeX<=z~1__$uc79mUNB^AJLtoA5braMt zW|;S&;yI`+sJI6e-F!cCwVXbBG?hMfG==_Q1$>nGr(-Gfp7VJus7>@f7FC^$>UbaX zmc&N-Qbr3cIz~@k?ld|zPhWgKo!;=XFe5(G#IlyKtyI7e=R4a%EUm5=BIIoyXhT<5 zxPn>kPaN)SN0$5Bj=Al^OMv{WtGk)OaggW46MTMFyl=d`a3(3JGHK=Q7_TZ2RJ;)< zn0H7$>E6Hr5(k2pQU6*Y%UAlPSSzqi*jJo9?CVy%IM)(!;p59|9v2)RAIE4ty7+#d z1@$`@I_bL}z$mFd;5tY0N9a`12%RW&=S;~cJ)Jxt;4+Q$6!NZ^j3W4_gMBz z7@?IvBWr7nT)Q%yvKlBjRw`fPWA=kfMi<{Q7E1`JN^$2Lg_ZQMn6|Oh3{g~BFP+M0 zrj+_oasQ~BoOVu8QfU{ROlhEFneCLy(kJd|?U|>No;gESS#C>u=c$xGLtyheXQ`-r zPKYh;E5-Y=-UZ68>7=7^b(GXTE9&M$P+2|=Sf+W~3VB)FV_MLK<>$n_?6w(hbDrnV z8p6uU7x1~qdW6uL_nG1zQ6tl8yy9NeY$2-<;aL{sX_nL}7Tp>C%=LL+AfuW1jODqm z6jGTTP2**=c%A4L3@J_LwzFDMk42Q{i~CU-%oiC`E=zA3qbq!TLrR7zncGk0cCnnu zu3|omG}fEM(t$S8c^&8;EQL}6sY`2`=Jt45meXW(C2r((bNv)vmk>zQX_=vf;&$2< zQ_nJkWeu@}RA%tH!tCZ1T#j`nuuP`#dzVo1{JK%P z$k)ip!U3K?MyFHiX=g+w?ToCYGevzA$IGEEy1%ND7|+`rUffNGVru9R2icHSqf4#3 z5c-2w4*ftUfS&O$qv=M~+7Mxa`(IyTLB(Ti&~?`)s9q%1Qx;W4EwkN^YKp3gR#1T{ zs7_J6oAFChfs|BJSZ#v3%nYg=B*_reBZ{ryI9*)hVkO<*IOolS2PZzY+1E(Y~!MEB^PL^@PkA2`hU> z4}$4$8v>=ikwoJ%v9D@7E7;eqf^yD1VcZA2O2-1{9LFBl6ufC&kFl9S4OmXa7l2VN zEaQSR%2)KI2ogA}dAuLilzCw-1@*x{#?T`n5Y%q3f(riaVkiAFtcM;B@1ch@{3@b{ zetgY$JIet=^j~4UR62p*KsP8NcYwadvi2V_L)40~B9<2E<)l7(BypIIWDn90Sit`6 zN)P=tWsr8J4bX4Gy6L|o`sgn#&`&4#(@!pU($Dzw3+Y4jToQi{VS#0d{YiKqJ(E62 z&n5QLk9fV`yx2{DNE)OUx$H6Czwhwp1LZR`HGhMWONVKH{wQ@#-=MsjVfxh-mV7C? zES>)qH%zrtylK;m6k9w<-(rFLMPwg6mo`Yxru5TOyd956_t2xUee}>ZUM{4I9*ygx zhp%?hkN9~0F@ftO4bUT@UG$SsmTz9}srWwncNXAp^8Tq=U5QRaR|BV<3Rp^2a3AWw zdl1wj7CK}y&t$f+5RFg;OJ#ZQ99_z5r*M{544x$JO*I7-BAQv%L5HKO>2gsoUCimA z1Qw&B&IyVv?4YZ~{gjP1n@8wcej8mX?4=Brz|6*BiYn=*h_WHdX&2xy)@RAgjpNV0c+Sg%N~;C)|WD_t#T8RBzr zkmsK*7@%0LACJ+JIp}=mQW2ft@6YL^YI^EQ1szIjrGs&GbUderV(K8ggLFKhp01Wb zrXZrc549t7IHsJQzg|X9UM!|_r7X?7Z?U{DAZD4U8B2eOstN=aQwKVEbfeNm#3~VP zf{gS_seP89s?S^XCaJ2Rs*nP=%yA1l3@RR@DeS8Zkgg zUGrju*jyHlr>^$WzUmq37PjiqC0dwUr0c~al*6KSKD~#2 z5Z+HkL%e-y-SlkP6iv?EpcD>1cVvxFGBR>3NHx42*YiA`^L?a*U_OEmNw}cvA*1SVU0L!tux@KFF6PBV(-#LX4fei* zXld0-ie2@y-PnpUOkyf5VfzJzLnfkKf6tZuI1jNn3aaH~NWgP_mxgTFMQ2(DwAoga z{(H)AS_w=PbXA&n_4sGwO7mr4BTF89v{qH3e-?jBR)sR^Wq5|A{FO>+89)0>rj-It z`M^T}(xz)AGEi;a$lOIjDQlN>7HADex^$H=67>i8%68-4xqQK=3s0@k24UhHUB;k` z4lK4^yAaY2?iZ=-xz}tMu$iXTws3PwUtOQyd4_7k=RC%T|1n?iVwjQZVKR8QbTGK> zwPuX14_unAnY+{1hTzGOOqg3o?{1B|+;wm9Hi0HI`Wb=%#oXUF|I$g9_At4OlL{B- zhhK%J{*C?3?sF!M_b8H|Q(-MIPB%(Eex9Ixu{7->I@aYaQJd0k&U|rk$u1hP@rSXA zuQ+ma^BV5ODDy$xdVP2gHXt9x>QG=kMmpi0GTwk-Xwq3414&A2`Z*Y}Ip*zfk^b$x z;KJS-i(ec-mM)#h`_Qaal0Vmj#T{hEz~wC*_COXIT;jaz^52En55HFuoz@nl zK$_f7lOb8m{6M8zI27A9b#;265FG%+t~dW!WN8JgR+AD>Z@UY|K6*IDyz6`tGcYRZ zFV~72{o7vXKCbI}C$CVOWOr8Pu&uw+y$j8*6EJ4j(5jNzTcHKV< zz8t7&e8-V#wc|9t<7qKY3n*9=-mWwA)Hczf3TK)jcWx`-#Du16UV4wgY-Hx&=#TQ` z*)MV*0}+OY5C_BU3jum4;u>qP9(N=h6kCUXm@)F4v>UC+@Rs_*6}X z0YIAFtp3OR!z#mK({(>4$GAMk%^NqSBm_0cvHM{Gev!Pw@qU1o=r_tVc@>S!*n^0o zz}CMik;~^vrabb^)qOB?uaum|8Kw3ZTF5qxIqJ`TLEx4{56=VF=AjF>?$A}10>4*9#I8aXiV5#>cLUhp7(n7<-XM#EDfk3hQ`fPl*2wtoF7D%z z4@N6ULMEgDP%OukB-!$jX*uy?AG|AVPp(CpZC^o>ne{3hl5cnxcp~shiWB*WqnxO{ za+Bw4rP5W?-12Ai^dr34Jve@hLIZAWXjoUfN(6QuP4g@)Rk`q{^qd` z81hB`D;fAh8w1GG)xYB^p8Cs!SB_0}YYyXSKjbP*8;Cn>DvPdSXHY+3 zoLbK*SRdvgCjgZ1XQho}EzC}EcY9kG^W@Q*#l@mM5uH#=GWwU;xMROp^aD9ta*>UO z5ufo052`@oqIkB>aHkD%D}|1-Zdo5&W5c)9U(lK>@1ZSM|Fp#?J_|rw@Y5f-XRPI8 zvR$v++xqaIy&DF4>I@~jK8BMT%@yRxKfr-RSLmZc7tY>b*)(9u^5O=~ge5P_1lz8# zr`TC^gx5bNWKD)g+fAZ9(WKQ#@gAIMqhWz&44@@k4&r{NJI(~fv2k)i5qD>aE^7Xk z%3Yj|#ksCuK9CXB8Gh3`APo=7QTzh2=GvN!-DO$mhqv&xh5Yq0G-)zWY+kqhV(rBo zs-vN)YCq9g0>aOe7$cL>W+Y)E-o3En6ZQ7}~`cd9Vm#ELS8FQ$RDp=8nNj!itF(=w# zXJb`e!(=Eg_fVyHHH)jTl`We@cWhs=BtO;6fHgL55Bc`oieQOy+{Z^bWe?OP!MY~5Z{ zf11^|BPtq18;u11s3QD#&h# zWI5p*zki;$u_0-5chYVs+bDS^_79~aC8ARPk&aKu(e)DUqBBmFPH+@m7v0^m_T5N= z*WEQi#HZBVzI`mQjWfwgw~0XSH3eK$G-;S&0P|Xoid#D;!W0r=kJmCD7JEG^ufqtUUCA_%rHY1w{K~Aof|^k@AZTb$h1^yhCZCV?~CW1a4j72 zBvKd>Kr)Y9a8=ah^X(BYo%3|_^cS{_d4Ce%`3%@{8=ah5S5xMoCAsc=Q<9yRmq&Sb zSmbeMviTyp+Ra4qxcImlk`hM&NAf^x3p{@Y^zLj~9MW7?ul0Rh3y7@W6UMlcG-r4q zt5iw$id5IrjZ)LOHQMdK9FlA~Aes8YZWhn*x>x*LB*2$>*{ukY$c20vSe;Wum&mBA zof*Dkqr1Ml(wNvQVH6$ZZI*0G^XL_VFV%uAzR+cV{w9|6R~Dl{B@|+!{0lnS*qQ`h zRBJ9^XR8ZPwlD_|FMJ7ZZyBcf;pp_mFbvp8V21S$U3Z z4jPEjN|>EK!1W&~Ab;9TI4>QfFPs>y2i!2+^-w~VXJwWWy7Ns-?5k;IZc=S+7TrLX#$k^L`|a^Qz^EsMVbFM!y~YYQIe1>H`ZhQ7eVg z<}te!qo(V(IfB^tPS4Q*mp_)|`Hb0Ui{{`aKDRzR;=^z%W1%m~2vlaRYE^~Q$?DI^ z7c}*kuTTZbzk4vdgA5*D&_=A8(}HEVPk0(l1mr&Z`91j*1zV!e&nh)#naBjT>c}N5 z&4@@y21b_@8Z+EpxY)K25ah;`<);>ud1b2X*&GFV&9EFXyyo5`xj%h1Y~dx~gb_`M zdYRng7{#?;2p!v+NXU{>{*tl%2rD2j06Im*Y119P=78e#GObI@%wexoj)O##rtL8O zj3RCLN)+WM+i)yvHV@y0RQQ&2j~9LTsz;&L)uP$f)KjI^o@REuey`J#Ax!t- zJcSleMz#4#X$GRi?InMR;L!iVf&^WILLoDproh|gh%diTFWVI2<$i4D>bjeWm+>6_ zqRZ1L_MFhAxz0l_G-NHS9XA7IoxB)~EJSL}8lxgq*zk@4EAY|pz4Y;5zNVI zkK`c7YRMnF1yTRxqBcKX!?K(+HvG9NXbO+}n7!OsBpfW|(T1ygA8Wyo2CKWpx zjJ!&V*OQ5-C`nUoijmXZfS=iJzkM=%^ocYq@Ii{iP3!8D(O4+!5@U~BiTv+dKa#72 zvw3<&M(O8YZT`??j<(P{q8HB;k!m$}y_njTMK;DxS*`;auk)hOX%mtYy zrRhcA^4RX^O!_C~5~n1F9JZ*9Vb3&+Oz7Sqv=ug3**A5q_Gn*w0gB)S22_FhgLNDp zPOI_OYlF|^w5laV0Ev{{6~iu&!A!`6w{u`=bCxu_tF7;@J^S!mm7RnJTkJBL{DBs9 z(Ts6LWJ36EhoJ}5r%5#2)shyWRdRq1Q@(afL7FC*2XjGnCf?w!#6qVglUqDQ`#_|t zyfQquBW^6s;c)sA+G>(iQ;qxeOx6O|lrDXf>)vV`tD33~Trh6a-r3o`_k8>Vmnacf zs)=Hl-Ah;4PD?x6+f-XBha$Kn$t0^x-M~lf>oT@h->k{uHQk`(6 zFg@P6X_W$d*+%Buc!<70tuKWEHrP}CjQ>wU!R=J7g0irB?}u32R_yh)sul6!`Zb1L z7XTjIaX$%9XibC8%(}9up}F95EV_Gm%i^F z{^DuRM613J`TaLQqTO3#9>4uEYq7>R)_uY(0WxdH~Z^@4Q zmUoTOpqsSgH~@5mlGaE^S;M5VaL0Q&unEM8Dv-l06lLi$J8ed0TM&J0%0#^jG7x`E zK)aofVE17tkBVDDuKLX((gIV zx!$QHkNTn@D=EXUWJ+W>214`BVAnRMPJu%v& z9#u(Kz?$MDRJt@_$<>-SV&j7{uau(njmdm3`}3;SE|wvSvjEeDnQi3&BtNqvS$yky zw8boJp_&4CcLvZfD&k3N=#L5T}>!d=<2QSB3a=m`wN6hPK|o3Z>Q6h z|e1y>SCvo)CXF2Q9@F^>ccoHt#e|@A3h3w z+Mqvs*(X(p=k@}F#Nm^1{AVXfES@o~M4s8A#o~-F`FivbD<5#-v)Jt9Mk)7V{Q1;1 z`e@yjHK}`!pVQicJT>ApzQ0F*8(%r|`t)|{(Zv7;Sr@%MIqu#&b)A=%URC zAo#2FOgka-Ogy+Y-RZ>WySEe1pF(X+&GFClG$M2SO_RX~C5#Q9J%zkSl4;>V__*3L zm4G5ogEfC~YR*U+goCCefA|b1bXQdL9r}()lEg=a=y$t%TsZu^{-6Eblq_bgU=W9> ztG8EwyZ?odObBzLFGg;6oa*X+*sI@TN*Z(^@f-ze-C~OOFg~|OcAF*r?Z17j=klC< zNjCg9md!_?Ew!!RA@JOmE|h;1XfZ+Wk5O=$vjwUm_1(&zV+IWqG>krB6}El zVnh8&G?$dZH}-qi<4GE(^OBR`29yVj}zdjW#yEW`*$Mj+1~z6#o^;$U!!(=r&eF+^Sji9mQr-^7zlvLu^mYB|**r;n`z-Y2tY^CnT zZGJ&M&XrPyFJoCquT#9V%H6KdJV9@D`FAtpWTWu7TOP7*@O<{Vgua`v9QlHX zuAU1{_UJ#?-hOOjkIK!uiEo8uO&yDI_p;_?Vc&)66del6S{$I@%uq4Yonw7KG!wj3 zv-c3t(d)*ayI4ZKW&T$kLvIeh*fYw9$1gVivS+ttAa=chCYBxAI!H^>-M_8uLHxL9 z#E{L&vk<`JI^Lh(&~L-*8cp*^yjs#Bh|g4`gT1TCrg+g zb-VJ3elJ^5Fzs~Cy#C0~p_VnX<}Sf)H9*$NI5zuxH0|W6W=d#bQ*`M!O_S;aTw8P; z_9Hy;Hu-Ca@`>t{Il!@ur#5zMqJ8V~?@qOmu^&(Aaeu-7?r5sH?3EVBPrC>Lvy`cN z6W+I%FP}sh&@O3})GpFv$f(i{vyWQ2<)29)WSs6__k-iooQ2mrwyY)^ zI5{MA&3!au)0XS?qW-`Z!jeU{TnMl4IiMAH@*j*?7Ph^>b=}U;MVERiWcwp83ATes z>B)ph%l<(re1posx-E6)@ztUpWk0{?rO@zrSaln1kz>5t>!!2a0G?k)KqOrm@c3j) z`nZ#f9HZQ!oNH%lgM)4cn#x`;JYL6hP0-%nZ~OEJM#H%n#v3%qjwfg-r7!8MuFcKc z8Ufr)Zx4yaniu3GEhfHy&Et%?i)+AjD8M^n3q8nx4G0&s{NVCvCQ`w)_$1N(gb|ps zPw4)eCNK(#_w?qHJLJQDUjF7aKP z0~Vr?xkOq}e@%wrTr6+0C~;>SMpntysP86HX+7c=N&O?cE%@VU6hPs|7XxH{uq?>TS_ z?$x5w#M*65c1o5+85f{r%@$5Veco738Ry_E&W?UT2N$(^rh2&I^LTrP0`O}XH$KG>^M%>`YYlqHizH6ETsBMJ5#Qua4O z-Dfcg`V%ap+J0LvLP;(Nnp&i}UJvqbn~yWy8UgdT zxYAGnD~BXqpcq0o?y60?p#!2}MijQ=5nPgM)ne-(-N57?`6NsHD=^ z|NJebjZQ}^U2WG9deF4cXAKZ`IxtrLiEziVd zwMJ3koGn640@UZ0AK)(Q^PYW-yMvppn-$^0=R8F#ih|H z;D|dw?^L8$1>3N>2Zsu?3e$IzxHv*H+cpdWuvFs991iT;q>rlKC@vIVUYk-;P-;T3 z*?NeNPh&ve&P>E!g?$T((mijbnZu-1#xj3Og9ZeV@ z{@;}{EZZ)CXr$jo!3DoH+w%MuAkk?nSZF&|flE&{R^+8B1AW}=+PxYf> ziX+=3wiGJ#z@K!qMKK>0M;M}i+89Zta{sDIaN+%;OYa^R!-ad`MrLSMlbJh*D3IUab7k+{$b(4o^w|o+Ks4(CaRcz}Wm|S9aQo|0jDV z-zOd*89YE+yjrUdR9Z0AS3}MGh7}!9a-`abnu=B%Z#flIM3FlJf0gL>TH0)}I87;K zPHE_~lKU&?+MtdMBD;Zo;o;H`6cEc2Idk0$aKmccfO@T(dGh6bx zo;w~g-9&>Wr!PUBq2reN%q!nWkn^6L4Qyw3hT)~1#^GNmWL+JUWFMtp>qP;kT*wK= zx7JBM<^C=yRh&1*(Q3TJLg?boW1gd{*y=S#0|VDA*Gt3qdXYc{zK~LnQ}^{h#S6Tp zqerb&$VH-2u6DLm_9riu=au4I{oOY$Y%O6#jO%kl5mK2XxYGQf zH3(*Hv2~qKhNm_u8I5bp-MVjah1_h694LerwmsTniN~+VJH=H(*Z(C;niRKL2d_NK ziY;HAf7SJi(e0T6y+I+>3dhvv5nuPSq%~R8S_B>&l5SZwzZ`~{8v@bmqHsPfo+^ZF zy|@OAMt%^}$Re&NGrZ;uXD?|`+(i2p5`1pcaQ(b+|$(7!HMdq>Wukh=PF@$r{VMQav zq%5DV3L>3E|7kaeN;VFxql*LmT|p}P`88_7>E#P}$!@V*u3B(X z5d(GQu9aQLR{Y@R^(!*@AjdlNim`Yr+Fjnv8Gbp~fFCd0kKHR)eH~&C2t>e8KI8l5ba+F= zQ7(X|?d$QKhy}!Ig`~oJtuzrtuKUnq5(xaNAN(yZ$e=hyB}%|jy+M5dyOD&RoRmvg z;sbKimeqiA$DO3fbri=X-PFXP{yR8T9IZ6Tmaim6M>(4nRN9773c2GbRcF(#Qi5fO zCyN=Xc|JRM2WNnmoiJ_LaS+{h93?M^9;PmB3319TY_rn}xQps0Gu5~MKJPf`?mdgS zXFrWiW`+Xq0&`mApLf#V-!;3UUBIST5E(sRBZqSxm)_o8!p!cPbHlKeTzhwDFqo|H zw16XN>OJSmhGuWpMJWUC6GgP^-=&ENTp7Qat$neB>RfNNT+b2(S>O^h>d_J5BFWid zy^d@+F(Oaz0aKS|t;-soZ{8&fI@10&{dSaDf4A?Kb(5jD&($Ukth|i4r*{XD$;PSk z>qe=w&thi>-F=tS%1@j+Hb$&H?iSLS?*9$G_hpuBGuu(njln$#G~OdK4%Pm8{Og0) zLBi=S)BgFE`W-{%bNDvL(P?i%T#RlqUT3=sS1nFJ$KrEHv_~7aP5o&r4$IEs?kdHt z?YN_v-_z{*FZ7*{)c)jIXurr-HE*sciCe7O+>3kH+^Vrw>c@pRGtgFrr_F+||0so2 zqCAzK32;rGCMT*LWySL^-0-G~-2Qd;Y#Z?F9OUtoa4OI|X)JiH%Jeu)xca~+yrh_W z*=daj;~$4=<1O5hoGYDc2al~0=LoN@Z(tynZ{Gc0%??DpKk$pWEeQ#HQJHb@tNJD) zR-8-XPp)K)ZX$+f^G*C9M)m=$==!*#$g@?8iMQkx8IUx){8cstTaUlh{hBl#Or}wP z)Mn(ke>BW!+bXT9&!T-~xtD~=VEJz<&*L&#?`P91DK#spY$WxOi4Dosv77DH4c0rM zjq3#koDa#@7=JQfG@Ow$ZB0^HZA+3_#Ts08^Sd__^p7-+o4QOlY<*y>C{I1|7XEou z_x;CMUOY)`C7}=odkJ17eBHTrNw<7#-+9`-hGFqift{40uC}15LuW86YQx&`Jc9rd zxJ-m8wG}OELeTJ@Q)Wlk`Q^XxVQf6STw&*DMicRxe?P(1Ifji@4w+cXk7+sa*M0`M z&SI#-g4?I3e{&AofQs!3xLh?sxYh6>rGk1VPd`GBngLFtZQbiEw-qR2rRdjH0rxcRId=7@ob9^=V~|I(;Z-3@G1n_< z>qWJb`Awu0K}CJ|`q#tCnw`;=K)_=uq!&#EvSxvUlhv835u|f^M|%2(e==1owjQ(1 zi{>~pv_Z+O3mx$J!@{5ZuvwNnn&>pV>~czeOo_68d_*aHq-^%NMEn_Px%yqW2vVbU zmQA$;ew1*CQdV%E-ybZT`TnbEED3h+X+FA_>YA?fs3Y>fdc!Fq(l=2=^%lfl=J|d~ z?YsxrqT;hIJ!7|vew=^grf$*o_KzEECZ+$emd&l>YYmO|6!vNktLJgj_8N|5*Sx}W z+&{zvR^)SC&(N++M8RGaYW2?dk~dhZYa8inwZmb6LUrd*$V#=5)Gq`y{gzoYtEKK5 zQu;}DHPBxmmBm`uT5HkJpNb;4U`#w!-s!>eJ1I)psdq>4&}luVA)C2Fkx@^g$-=3y zK_z46(*VrC)IlM_;=jt`@=hpNKap< z*Y!LR=rm4k9y81#rEaIj4sN`4WV_EkUlCw}XR(ghp6_O{fVW}i zrhCKF@b5Y?Jg8e%JMZ;OUcQ}SuJn21)ti0I-m+sE;#pnjvti7=;0>i~=|-glGch5g zq$w+nJ!t>J%p$Cldj(%Rp#xHg0t+Y8v0C@jmD?N6zihL8@Dv*vey(NxB-Q$if11?O z9tTCTS`z6?>{ffp>2-n%k7CM6d8EFci7mG%UG#*y{O8B>C6h9D(T5D3y@aL`DFIUD z+_0qF$=p9_er>qT|6W9UpL$=bPA-M%ALFnP-yHc?LxqIlrf@kHfffy_(c2W%vzO#9 z0~p4wUNbMs+M666bB06gCB(3HY_Slz721;;71x-9la@HCBc$Y473pPhOuWt3b zJwr=MDGk^vZPPpJzhUSncSYeI2#wV>ed2jUKUX80bW#Lb4IzZo>rd>W8bs zsJ7M@@lPj8=;_){4VFgC_xtt_8xrKai?+iJck9!lDLV7obu)e8*K>sFI$XL^Guq(; z9q<^RSNV4VDVk0#ah6(J0T{=YN&;Uk4VFJ?Chxx0>EJC*)R)ouaZt)PX-lk22BIBR zg=Moh#W8EiCF?k?-?6*{lzoigh%PPjbtw8WCh&5Wi7yM_BGSgQgVU9LcF(?lvCf_-IDN7fJ-F1!J|$~ix+b|&;td}Z5!w{CzxsEr!TzUt zbxO!v{}X&3H%@5e*#~R3G6x^+!4-P9Mfm5M3d!`KSy}>@M$4ZJX}mVh7-P?F>t6yg zdT4rBr=_A2<%_>Wop@2l_v9v0y;#XSxtEQ`QnsOh-uRF^dkN632JhIf>*soLK~F{H zc{*s8Fvr$oh++&X6Q)(CS>-V00d8acxXDQ>F)yPk(v3J1ano@Z(O-iw@Vm|>jBzO{ zdnwYaJAUBEp}0+;9y2$=&womb?_l4dSwC*Z2%Q~f>3j{iQp)LsS$_Sysbxaf&c1nv z1Vzx)PtXzcyBN{vuHev|$8BHyN@Fs(+xzN~tBZ#wo$^fMwg!9nSllUfK23ek=WA@i zdQAJN{h4S&_W0IcOhuJGm`VeS)SsWHJ2<1E4Y^Wdc6&3II5;&W#B3I_W|6CGps?H8 zt^xV*M12YZID`~_oU=#DWkuWXNVH;!<;0F-VAC$SRc!{kUt|*0WZ397oB?_ zPF~e-2{O%Du|2MIILn9|8#`?oGKaJjuf5UoCb+pAY47W_cxapx`?lrxX^^dr0;GOB zj1*)@bKaUnwDcAkQt*-Xgen?B*(ZT(eQHgEOYilG09a(HDK?#* z59^F0oI%Xz?g>`u>E0oGw`kEY{SYOqE(<@8oIdNcJ#Q*LN&(6Q)zOKQ9>8m!ke|-8 z_b&q^-%H3mnxp%Fn(7?Dz09}>`u9buK8$NHUJCkYC|`!R1Zo`8?}PG^F%<{SvO5YU zc&1oC{%j`4?Y|a>B`xOOu8{rdHzTI4kvDhXHTmvGvyCPk_J-`)+w4{m{`XaIO-H9% znE%%diF&4xm6Kt`iCRzUejvewB^JAxg9?BUoOuRKimp=08iolg>P3o9oJ{ zKYp?1gN$XZR|ku&%#Ce9^pEZEk%Fw#&UpFrj*7Jx`akVHcq6dfl3_%)i8E_hXZW`$ z)+qXRQX{_{$%m1yl{I9EBb4qZfyD|yjYHN>FrY0e_{qN z9*C+H!LHH#d!Nia61ILayUMpw#kfGe5dVRBQ|0hen4zP&zC!&@;a9L)I95m=GXmls zX8sNzM#*NH1VnQ!Fw^KME$AH>(AES6gjl^+^G_uEK}F^Bno3~6`w6&8{B~dlW7egA z99J&+V*DP3bZWI9Y_HG#oTUeOq<&j+&7*g$5la@4>)sh@^F2^K&{_kp^@yB;Byx;Y zHp09d8K?g73SlINRepO6Kweb`k0H*nuH;z!IsmKmT4sT^#7xIho`-O{j=}bnT$(w7 z#UeUVI!H;|Lx`OvMhds|WpH=sui!+epU!NxfeZUgT;0P`uR4B5o1Fwy4Oz!@^q9`GrIMAZ6v>bITBn04$@vjG8>6%?*u$z+;+**?1vG zX0`*c=CoKWky>lXa5WKuo+L}H{0Nwu^2_Ki_&rH`Gnij?-Vd{$FcyN8LnCZ0v`B%_ z@5jYb+W8c16&4%a4LDeWyS?9%phWMD4S z!Rb7qX;FXJhGCU4FtBQ97P}cDX;M(3Sc?>FY^6mCf+m$RmgYx6jX%176Q`I3Rns&D zqt3r@o6W~K@yFpf!ZQLs6KL=NO1cwqYPj3Jbfc2eziCL6+l!3HexhvXk@Pn8B@W1u z^n-t2xdFa)kfGsRx9+K>p>vHPjfc1Z4x)NROsWTAxmAJ)#JbBsF>^yeM4BE{b9E(_ zs0?GBhB|W2YSwvreZK)6iy#xS9{sPsH#k}y6SiN7*`u_+<(=ap66x~lSG!`#m*YpIo*rKd@pSyVZjiVcJkTyGDn5$ z`D6;McRwa|*ZpLG)$c!NA_o&GOBUYV$HSy{VDIH)iKeEh45t z+%GzO9j)iz>G)yA2$nlcOyj^sOH8$BoVW~;w+s%p?N@`M$mPF654l28uVs;FwYx-- zY4<|`UMLZtE7EFLz?FTIWZ9ckBsrLUGYN%}!)~auH0za<8GCh+w0bIA@ev4-^U~ca z%5SiQ%WD?>`6Y0khwSY@Q$aJC_&3e3GDtm}|1iPS01=tillO0-iqG9>r1pVt!&U~I z*YM*=fRfJ#KAfsIB6mNGMJ_~BCWV}7nqTwqu3Hm=Hn3EmGeK>DTwf4GuPm7DG*gxdgq0fSpqXj@hBQoCg-Qm0* zVyJkNQvZl1oeZ8bMH9I^=H_X6W53A$X(tj@#|39T5UgP8>b zID`FlC#I1qM%qSRspYFhFLO#iDb+o<=AcILi8|qQ<@;^k;RUwS&?Iuq`n<9N#(Iql z2BEsiFaUTb`g&Uwb##mx_vqK9N8qo@+izX{J)-qP5C}SSM6k-#C;a)G z1=?XMW8p}SRX|JkPD%p!uvf%Ls$U~M^YV4h#raMaEDN7E~$wZRM&j)aJ2w-3a$uV z3^X04{&RaKK6PWM)_$OAOD^_)LYik)MXo&Ahz)zab=aU#vmhgU=W0*-8{!gU*;6Jj zcE$1p*e-Lfr*p2;*6s1OrG^_z%Bmv2qka}>TfHUth4l&PdD;Dqfvi`WPt1*Y1u-$x zOY0=>KL0kmd7FNEr2v|*mzCH^#Gkd&4_60zDo-vZ^%{}YiMBc69H+8l)JsaHMC-PU zL(0I32>>@jCFyQX;;9Ajwc})~TLMDsH_=n}{km67f4AbFM6Pp9eL%_LLEG}tv2kS` z4p2N$jLuR@>b1I|rF8iQ>C>2`hydy|3-n{A`0H}JN-kG~ph2b8`?r+^7d{l(vnc}Z z5wPwb^h<1kwIt3IP8_9?HA_9m?bR1xk zzn5K+G=5fv4`%ldcGi5K4w}U(-T=-2%`y%N)OidxQ;=zSOPN`&L+k|Qd+R=9=crR4 zhDGB(S~b2YBq4t(t5_e?GuiZFW~UsVLC&94pANKJPDj7q)mvw`jrF^=Er8x$E{TqJ zy}|tX#qquY79z7@mNSkU|o4+(v$gU*$1|Y#;y4a19nN>+iETm z{r@=5ZMDP(peUa`PpUo~ z{ug6P7h$>Ku0|GpMW9Xk$Ou}InKMIRLAZR-@s8NQ_Fu=_&iCb1!_s@}ww?c%+W2PftgfzR%6LBTW*x0-EyG&u)b-i`!nOS!$DQe&5 ztXz>9xZm0T*QeWbZzI6e##UJ6t$HD{RtpN9Jwt^U`w@Ee$F#rkHjS&TRuy8P%51=l4w5$e6$z3OVJ_!EIKcntF>tqxD z2LIPW@wc;-T8-|092gw=CLQea(28*@b6Mb8b^I^yf=LjdmXcGg=1t<7; zN)EGh0CLA&vb*lfTp7}Sop=Ev!9i3CMnbT^C00_bN}DONH>8?(AaQF-$DcL**Fk8{ zG;?AFkL%Y8I?MErW9hs;`vp}Oce%*h)vJxIKX<^*%ru@k;JngOY^$pbGSnnZKe9&a zOd(;oC11YJSEsXKCXoZLAT`W%h`tm_)o$40h0gGtz96z)iQTt*y(Q-4%Wo2+P*WNL z$DRQLQDh%N!#yd8-g3EZsbKng8iwuURa_Pkn+n$koU9uV4)OH%Hay}-);z{3xviQz zFPI9JF_y&1Yr1Jx>q!WsL6g;KBzKYTf$|JhJCUlq z0VxhW-}@i4Ms13Y4+aSrpPF(*CyW6T(xQa@F1XlIy{_s!zZ2Aer*BS$5H9J~QrhdL z@;zp0{X8k!qm?Ebp0%&1wj2nzqU>+%QG^#0E|WcwH|N4tFR>Y2{lHWCyR$f|b^*fv zAz_pCCS9rYcUkV7@1rUo7*EJ?lKoAx_P^hh^)WdEWUw5qDwgH_Zs{}#1S@BZuO$9; z^$Nas9Nwr7{`Cg9UyAfyjjRIQ`Ki~PJBMXCJ`>|0Hcl5zh5yUnnGR;NQ{B(re{y^f z3pg}!$<9MCsISJMi|z5u2NqsvMgJhZD?5hVS-OaJ(*?H3w1xUFuGXluQWthj49T-3 z9_?d}+GP#)P%TnTq^4vF#wJ_`-SYxXr@B%9-5rUf&2A(ye+BlCTQJz6(X3El3wyLY<- z)e9BIlK&PRcrg4By0N}6P>p_YrIW-Tv#6P<)JPt`56J!KbfeF@d=>qk=k9j|)SJ5A z^`P-buOaH;9C?o>qY3{8d(X-fCi|7X6^95HZx2y_a9lDa_3+zRKF8{h_wg?&i8I{~ z(}8N``yH;^Q!)5kb)W8OhaClb1c%Mnu`KCAY=W-W{G#{AS_0hqhg)(EAzbaE`v$VY zXwxAN4g?4}Zf-J$;Ebk^4+KDN;kpyc!#MBw-li;5AAQFe!a%wB{hocYTNiZH3jD^n zcBCwsa!P29BGk`T`7DCN*#8OtK_a2;3`8dG-0b04We}Gm@7eXZ&`wesg^6?eFOt)1YRbT1C5fL;CT}Y50}UbH=whhFGH0 z0*;C^_L7@F$N$-q+VvJ#H9BjQv1M)ZIzk*Q3ja`O*KiLD!1o9>P3sC8UR5qGNDw~X z*;w<@FLk*a)!w+O2A4B{fVUwHCxYn^?BC++!{i+vc}G4+;dAm4NVJmSE8j9Vj%|O< z_J&>M*6hHS5nOF?m1^Xsw`u7Q8F*EGU$NCWvV~X1yPr8#fNCPs*ho;03h>g{_cfdQ zAS^?K)McnM!4>#60;aD1IG9yha8YW8IQUyOk|D}lT=z20Z5{-cKQ$RB@g6Jr6RZ5P z*Ec(;R39j`R}<`J{N5?ZxATFy+L0a^_UNMwv>UGM5qo1DbkggJnakf0d3#1F%g-NK zwBMCpJ1>1+O0`vlybL7UdNAP%rwFw?a3t&cAmY)u@sG7B+mtZXf3uS9;>Ys*#dsu< z&jSeU@e-*@B{xxN-k{q3WJY`#g;ZR1+wP6&mhDzWlU|%}^j_5J!uOC|yopx7rE$Ck znP!`36G!63phKd?Al;{!33r|#!jgY^_ z`+Yq)2_^0SDTS{41e_m=XcuHNHF@g1u~I}3N7{Mdm+p&{>@$?8@c&(v%l4P)si-P~ z-bMzLmcgJ^ly#+R7MWjdYfl12rQBb~T437JBX6XYeKnvlpv;aNXU~NXy3%wZjqLw4 zS*rG{@dzN(XBxW;;!lz?+hq!EGN^PU*oOdQ-I4R9n{>^33LVR3KVfW@eChtCgBSHu z+N1fVqW$+gD(OdlW_Sz_Z;6+saGNxXwgvm?(7BO&!U+Fd9}k5ILzL%Zl;vff;wj+o z>?427)3Juk-ti4-uG6#J_kp(c^JmMGD5=;ZFV&W^ep2iajyL`DB1<%BtY+9Lhh zSV{PV-Smo^x)f;$vM)5$VGOL2a7^9c#6)BYr|=?N#d}inv+CvgQVpcsr+dpt7tM`d z4#4{F%nbjyR8>kW%;QG*^Tb8Idk|rfnt2I@oDE{-q-nqCCy;FDK^r5#*GbjVFh#8P zMYek(0_N?Rk-TynXm#a7!c)^eNp0-SDw++Sy3EL}8)BEObZ>RxzAEE?g`Ff6(sU$_ zj2cVH%8|GwgWR^(Y^!cExYF1+St@}Z`brXA4x8TDSA6yc9Gh1O)__m?vnSYkshWxzmGH98Ir}gzd=BlE&1gL$!dt$P zfOCL$frn6E-GuBF>#=Vw7E8w&T(w1LF0Ag43#CjWg3Vpd-m+Xbso>4fAKcu5F2CX>wII0JS9K9;MVH?Dn**BF^@OX(|Gkxj?wYR#ivXn`)aOO zZOtp50r?Gg~CYdED}FEW+|VBz?kPi&rvoK~zGT z;Lx@lUxeM1NxDew(FbJ5ixyev7&~rH`KFqXfx+t2F)*K8*h2O!xDMI>^!J$JDjEgs zD$8lTLt$r|mI}5vahnAUe4!{z+~fR)gFtp51gn3P-ZVcN>5XhtzTML#gZHmf#Bv0X zD_oyiHy6SIX93Dmr21+tQDcC@2a}j2j-UwMcp@*?U3vvRY`Y#xUx&V z4iS!R`E3(z#z13-IWvukI>P!}_sS9wvIWFAl)u!tWF>nL?X4=7cVr%~7PKf{(n(x{ zSi@l83V8T9KC;UA>3*V!{5qSefSEc!%9q=%{cBweWF@P(Fj|52ZDW%1r1aiJvPDKv z?ug6R?3|CE|NSE&B~(u3GARxo5=4CADo87&p&~K1u`Xd{iv2kx8ku5M!q1WN7Y=8W z;7Ta{AaCPXo_Yq_VB=-{xej)b4O7W>o~~*-8Zkg2p;?9ai5KK~#+cYb}c9{mED2OSI^j z=tHH##DII6FSij{fPMp6L!UEVs?=Cex>HXjii;DTMQrx^SkGja6m z{bvoau4j=g60aoiry@SE!D4$(*ATAG>BOMZSbnYV`JA&Gf$s z!I6O*a*QGv=&4>P4crj%K-wtzgrSj&Gv-)IwYny{oi2}pnjW1o*@s~S(_UoS8zTGh zS`)pxivNeHw+xH&jn;VSmhSGBl$35kU|{G}It1x10qO1-V90?{KtV!MBppg*0O{@+ zdT7u5_dfev=i7Xo>v`AvK5O0Ux9-J18poG{lc3L<8;4TO2)4pLh?0yZHMF+|51ktZ zr3MoX;572?noe=$+dF+F>h;XNFo-iNQd9+OpT|j`-aY9<_hmgm%$GYi2S(NGx7MN_0OIVPD4v zceRY-&a*~GcL*i0&|2OSo__gCviNT#`p$XtOpKOue_XK6oxApd(3B|4sIxU%b`hwj zNm5c(RIXlx_yiUoo&*?ArjSm|4p~4Ogdij0q5JcuX$Ij)ycWuV-=!REa0Oy?5O}2d zeoxDas(6f3y(}UzfJs)@d;bT}R7XaJervaDubc;^-A;5T&KQn({@be2*bj;e&Bit+8 zA?ZLZ2X@D*p+AZJxV*B>MX+WLmw%blIR<8Wx!cAsq*EU|CAhD3Cr=}fKu?5F6TNPH zw>L2yblrg=fWHA1A2nW|FdSO5O74&TjZ}6`WFXS6`SC5^zjO{*NNTN z?OE5atHH1?slEu-e!-8)`Bd9~6~5YNe&+$$j)UwN4AMN*3VTuH%s@}U{R#fpbjz0? z{Mx z>gID0mh87clEJOn3r72*lZGns_%wipapTRBA*m*={zpTIR#UU6swcj_<|Pd7c1Z zgy8W)U@W^G1)qdyX@gaV)-UWU_+WI%=_wy{k5N3|2E6op(~p}!eF$ff@A9WPzg-=z zhj2Mia{@5^R~M?Qpra`?@Vnqoc3U@RX+UpR+0V@^UM{}U25X}7s?P*a0e#4(X1bx8 zA#uxfT0zVERD4YB8wKFtsxFHU1U|4I2=pMx%>(Q3<_0x!831PX$o<~8cu z*wAEJo<(DcN+2Yttb?ly_$QyYh}GyiN!jpK=Vj108furQKq(B`-qmWzp2_^e#|Wd4 z*we4wdsc9h)Pu?%m{5(VM8Bs7M(a3m&o1(8V`awhY5C#xo)fiEnlM(wNsrwchmN87 z5UXy+YajqyaZX%7*$_r8+uTR~8HRY92YYvNeBgNa4w66UrK8 z&x?Bp6N||GT1IL#5Jk$S#R-cblu^;390lvD1A>DGtb=CjMmjnM)0TvKEuxb=#PX>| zM>}@zo|9!?$+NchLmM6RmKO}#9DVH^Lam3@M%{;kTiNxLw<)fRq|NPE=H4itsbVWe zoz4m0=8N4r=0*0AM`BM4c2fPIVK%&z{-+Z5V&Y*G9B(UWsRVdGkq%fI?Eeg%? zN^EGQoLnn41=7St9#41s+y_!YzS}tmpJ~WN|)6$ z0r--Xw)k|{5}b2NH~zVx((r8_Z}6l$n>x4gJ#?zZ{C{%gqBJAX#RL;11?|w&TF^MtEQZTj;p+j;5J8H-FMa@ z66V0*$U4ZoIv!D7i9tAbRZ_u$7#JD`-HQ%dBS-5(rnjG?f`kPXB=}*GX;n%9%)^cQ zQl5>$?oT&1irs}+x!9Kqd6Irt{sd57igaUdJSbm*Sd7~U`xe&4+5G2CQltE(qJn7N zAI@DqOWX(GXA#EO#&~R#C<742<0LcsV=uZUs8ZMt?Qc|!ME+&w5TZ=4JVR*}@9?0s zgEYsMXSDALd$pBYIzwAxVsw9$s5pr#gFbXoV^h%x z^^Bcw3~~TD1u``sNbfyB-jO>LIE#^!jlKQCBsMh6vni%we6uQPbA(nb|590^h7{I> zi|%YC1!&;Ne+qE~sCkq4qH~vjCox99#ng0klE1Z!=*vr(`(3Vy^1He~mTH^7*(1qd zf^`)|P`86UhVqpi4B6`g1p@tF<$sp2LawTJ%>jqzfMLG7C7UFe2c^eq0m>mm9M_DW zqxN$}Qqs9kU01_2Y|M_{^;7v#g&6b`e+A(%@t>A0@&k{?3UOTI)OG<86bh~CN)dFk zzul|Xu6%?v0Peqt*o!WM7^)JoJNIr)uYoEC&Abe*u{m}U(93p4(v+lXl_$Xn_{YX8?$RRM>OXBAf3AWZNiSTTj|yV+S9DvxwcEYq zh3F|855jfzFlia_k)^WD5Vsx5za7_NZgVz;@>feObE<7Ns6g7LoSzQS$d{4mxMcS| zIGDABwI|j&IoLSp`r92G{5*W6EuUC>wlb2?Ay)oG;A|CKX8EI$+;Vo4Wn11#Q7hvL zcdqoh2p)x)$lR)N#6LKLQ=f&S+z_sIzT;j+ruJpTR@wdF^gAB(P^$rNm_rN!>fE`T z(x{J+(nsF&&`c^SbAT#i>LgCwk0L|YS>Pzj7Fs6F|FcgTf{08)%}|SfctTPcI{`Aj z9Sdf6+Gzi?w=rr6yXUY|8L86+d?iN3UbxMyu(EN!>ZJTPo>9`dg*<;vt#8Xso(Kq7PfIg{GMN+(KsmDuuyet&PK z;p?tVtaeOu^V!&k@B8LtmFoUF%gh1%Tx^%tz5P|5V>tq;^`M@t>At=e?tOU&_n^-EIm(tB~wF`sDqCkK%rJm*xceW9!GAePWh+>-hU{ z;e(NVZV4)`Kl3_da9KG4?ICj|`=9o8s!JT5YSt~-e%lXxvi>`a)l^cy$rNI091%FtTJ(&z|?tP?PgQG)IP~-K%u}`h|*i=4AM9{ri_XtU>nOtRmeEwu`hPEmJ zTe6B6TWHYaUo7J>@q-1Jx^)7*LUGgRSMWPb44ynPf9Y#_kB*{)q5@(iPm|JC&p~n zVl+JHZQq^1FO!$v?W||p12jaZG(gKq{Kzx)q3N@~IgW_l^TEH@L0uzNNs`<7*tSVZ z5iak@W-S1OhqC|EgLocjhp?hi&YcHFVi^Y___mC|ng6vHg+dR)Kq~oHJE@6wO+X2*#T&PW02z?6g;JC);0V| z)RussbC%pFLINxI(94=_v_>iEwQwt$C@OzEi5FE zMj{4^<+t{=~ER7b~QVi57c zd$#hLjG`vD{v&b$G0=k>9u!>znVX&_xo7pZ6f~-TgE=9 zloqV@&AF>0En%G%!c8i}R@?M?Xqs2k`mp)C!a%+YZgk>g#j{GQ&%mK|JL?a4(C>^3 znlgyw6dCJL=^wI8XLX;JQWC#2HF~_GhKHZUHg1wRWV4|zI_ZCCf8BThHW@=;wHiiP zvH?%jUn$3b?;;(~cL|S4Zj>D&=(b4kZ|M|`20~5?ZCgg)i$+J&gCq|Jdg|i;5t;#7 zeB212?2uAyH-O+g!lg%-w1uk*JZDL9}FJ>C7pDmd=K)E zqESD(9tA7NGjm^joF+9=RjIywRrf-H0aaZz@N7|e3-7FV%dBQtR4RyUQGl%l`$a+X zJwe-(DsRX%lf~?1IN2S(Qv#=i@-(dnqBCAMKc6DzxHcH5(vbg$mAjfOJx%o=-0Ds@ zP?PkR9|=NDKzo!FQ~KL3d94n!T7IlYweESxlL2_#tqg0)i9QiC&o^5m&&7M`J!E!p zqxm{89+bo*wPVB~P?}5rlN3SJ@qtNF-^BOJ@@;(Y#T4WvC zlAfzPXQO)!?{;sD0C1vOWre_aP)N@l{9eAMw5j?$0tM17h*fc$n83q~n>C?91dL_= zA0o^QOfy*9h`u@s)}D=0!mGHa?$=Rbvt&G2Oj3C}2yP7^11f8eTwk{Pkp zjDWSx9ndMWTxVObjA7-461jyYsWng;i$ZDMl0g`9y_Va}^oR>J%cV4>M0?mkvHl!Ft?9+l-m> zSuGRmmj+G4`ff172C!&YTbGYiK0Tf%un?CbCvTFc2(DQcW3OPsn7s>6^dH3@)=s~L zTW0+Kj!N#={xkh~;QMHpp&LuL)M=>x*%I*B-qGZD3(a!K;fwTa(NbU82_M-V7)Wd) zGi3X*h7?$-(|GrnR{b>{@^hWeYwQ0hi+$XjwArdta@krs_KU>$w}BY9Iqiu&3W4}4 zg#LbaG21ZBquhn&RY_3bZ$2Rd}>*7ldSK=%6WF~{4PW^hRQ?o?~RLUXlS4Lq02-+$5g zBOI1c)aXY2YJr$|t7Z;I3YaQ0rnKWkCF71R6 zgb}C&m};S+GxEF0-56;UQr<>BBn7L;sy|@&iE-*U84PyU8j8{7gU_A2w9o7vURo#* z%E9}uy{&j~mC2)FjX|vft-8XsL(ZZdjkS}b942Z^HZ%)a9!WO_<`WPwY|?DPV(8;v z&+49B`P@UFS*b;e4T05SGmpX{`7jjDcYH zy4{IfOu69OU-DK4{JJ6mcU`G7}l}aZz+r$_gfs( zi2d1ISmK zf~uQPwC;zo9C|x^Hu0*lkp%jSXLJ&~XQELFNseRNM0A7aLmjD}J&Z^tf^o;#J#VJU z&<6GbGu(U+3e<5Yd>X&YbE!C9Jr&bxV&xu?e9Mf2sc8k>wQ3WoLcN(Ht^{kB^3La< zy^~_Ghk)JeoqbEq;`wUX(a3DlimQXaKk?zrI)h=edRYzIc zsoafPAQXGnZZLkaI9MJBy~INySPLl>_chE<`aF;ymEau<9Q7=p1JwEkT-9h01|Q_y1{l^m^BD!iXiM2R zx55kga;*bU#2Z(&ipnvaf%Ek%ngLsMV*GYS&Jk)2o8gZ2B=Xmqa8vng8vwq7WJvzRbf657ond7A!@Cj^}zjYKu~E_X%iv4e$DAlnI1@@8}u%z@hrxCTGoit$j*!fV!l$ z&%CnfxY82AgzqyIMk|;rjn(B3>{qQXcVn>TG4>jkKRtxclbNfc_>wr!Q0eFsJM*)_ zV=EGC7u&=-n$>yD$^5Cq2a@uw>@Ur7A~GXm5Xdr zm0>>=evesQJsZV4OX}FTq8WNv#MbyOntu4m{?p=G5`K@p!f=IHn{4|BS`~Lu734XH zon|nY{46~BWr>MIlg`U3@V?Q0;C?YU@+2rhnMq;71(9kttXkmAdY!tvn&z<)-kX}= znVKKWk#|d%hhHhBebf9wmgJajv^x`te^F9avJ@9S9A*PKD_jI9EdB_vFaFZa*oM(X zT}?IY2)aHYW?O{!;8Td$Wk$}P<|W@F>K@G1p$$QRx>v*?89;qx5iAr{0-$~H^6_U8jyL5vEW+Zyw=OirZ~Jgpp({?vAi$ShKxh6e zh$uB`PMD0&@~p=)xeFd4_?tdm1`teuh zHr>2@vNK`|M{Q^GSiaf;PFDSalsau4PV-&EA$=udX{CUKglGn{QA0l7(os@XQ@f$k&pfxEU`t| z6LZ8bvdlB$sm3X88`Tso+M?xqUxKy$0hb#5DQ8CG(1@ zI_xP9OhwbMw{S}^-P5f^ub=AW&1v8~>-h~`BXc^#KP%McXqn$FY>U(vV*#JVo&=}r zRcE~;0yZR9Z|4(bR_A(L^Qi;@bEicA^k^$gQLsT}0|MMEEH8sg;f!um^j4CD=A-o&3y^6k`YkHfDw$Z<5u!g?>9M1 z?;0g28i*n{`d*cs27QZ9r$M1twKiPF#WLHI6=Q;$KZk*@pOq=wjc`M*ucNa%%)N=T zD33Ne1J;t`gt2;FxBV6+{@>3&eB{~FWHwkx^obDF(NYBrK0=6z7bh1=%7l-VssI?Gj#fU&nP_w)DN!RS0Bh^e!ElK7oXuR6RvZ|LEJ4j+u85P@tCgPhmx=|MG#3+QSvXE(d+NB)mkUrn=Sqo2}aIVawy zDT|`&{`dc#E9j0tpE9)_tCD(vd+YO)%UZ}7M}b0MprURn|Iq|f2O~!l3EtXb%KsR~U&qo| z6M-rLeu1==uU<%_dI#5``aC%eC@|S+hcZ2v&(+ue*k8d=W3FW`3y9M9aMj2QrwJ}Z z#b8nDwBK~qQxdzUE5S{HcRaNVnq%#O2-M8THryXkcXoi-bPSN*CN(*Oa2dD898`=N%cx%)!Cavz9Bvz4C^M+lZI`n|t_t`X zVLQD?#3G;&QJ``2=mTMpx@4CVY0(l7D-~G}yVe)_^ZPPOI)AkAx5?sObi2k@Y=HaS zZms8=6_XR!pAu4Ii>%AxcEp(Qy*AuMb46+W@%{^^#E74gVBLr_nR4dYcn>C!MTNJ` zzr7>!RMCUj&^UM8o!TPg`1Ez%^uMz1e`x8Pzs_Iw%_B~NhVRoljdYm5TV+j4`0AW6 z&t!^^u8(vle2_QJv#iF)l&(+gE(Us_&T%F$eyEGv|ODC@hHRGhyR52Y5>*xwP?-2GV)ptD^A zLEu1gSSUUgf)QO>B)i$8-T@KHIikb;x1s<>K}jL;21Q9vDaO*#^l`Q}mX}N{HGZFZ zeeY5)k~caHHIBuV;j|BbaOfb7tK>_DFSj1N=9!E(yLkO~34}PbdaXO(M%X=2QMcU)dyV=*`l8T`Q>F{LJsiVYQBXq;32}DVFHDy_Fo!Y`m zszGH~PlegAga?r1CD_LXdA^T7Jh=^9*o#|u?;W08%;)w`vKk%ggge!4*{XNxtP{oG z?<%O2%Dlor1Jw+fhh#*s+uFemwZmymO+s&+)k4a+~!HO5h^=M!ksI7Ct-=qh7 z)DQZ6z{@gmW~pRTsCe>eRv7kKkgdx3-dz4)6*rG0%w8IPIm&&&lG zZDdz%{D4p!UjJ^h@o$MqNtq1zv4+oNS3C784TBsZX|OQk`P#c?%s~zt13l-xhwFK? zr1)-jmgOPVCS3WOoP&x<#-D>GJPOdS$)~|at&=nb1|mnw%dsRZ(@#UcqDJl^SH64A zu^E3}rdQ@T>EXurPOX!#W$Vv%!w*PTWyVeG6r?A<^#pgRd$2~12>kH|ZSdufiWQ#w z*97Z@A;9*|MWFU&^=+klx(z9@>af2UaGN0}aw`wGLhhr$rK6J*{e@>8s8ToQ`A6e} z7kS=R+Qr*`y!l(!1j77pZ)g?&?nz=#$uj+j^;MS3XfO7rH<=a;KUdHzv*SioL`5o$ zeW4p|ulHu)Wx1O2r}0ibNJ6mCv>1EFRQs)xEcRFQG)#-eSfv%oFAdf2SI2rj^p`mg zqeF^C2qtAxi5o*oD$l7?HEo0jIfTikcO=5b{`lVo6vCAQaOaNScE*wfcJpS5A#!#>4IhE7W{n{e ze4%<5+orJ7dp()PK=HUvo@U*fT38yA-veHF#U76qr(A9a7yglsei711fsX}f!BPB> zKmnoO!8@YfHmhApC>nF35TtH)(M<*PMx1Hi~2Tl^!y0M#WOk8eD^4OHOcNxtcv31RSCtw^0qN77W z@lHv_%6YjON7R2{1q^lc~!S!kgD%ZCK?aGcm#TyPIP{xZ*f|5;Q zAikeMHbmrXjGl zlSeZehkw8V;#u?Mn>@tPGgoqKq_p`~yGm4sZ^C*cSKZ&W@s<&#y~8)+Ir_DIE~CFa z;&sUoJ)iuXcsMedKh)aeQT@u7PmQh1a1@*e)_ zyci!*+bf)!5Pp3j1O?WdOpJ}e4pH}Wqe&~VEDb*n z((fO-7&bTUFTcxnL|u%YiLv@xI(zM0;MN{VKOK0Nq0(Rjg?6TM|Lj^1pZj&6tyX{% zTIuV#o$Jx75fUnGzA~bHE1TUqq3<5&@&nr=+Yz}Ze4h4JrbjJ1Q#cZvf8>WnMt=tt z{pt7i>fF4rtZRVnEfK-q@4f?k^A#c#a`qI@=UX~@sL*PIT5k4}T@9p%dZbxi z$UhS`Z?&Dq9!~Ve(~vWVHc~Ns=+KD`)x?R^SmNkt?w1oYi(X%+(k$zJM-YJ(&ONbL3+_@g?ZV2GKfdW^l3tAfYdK~;?=H)Hqu-+fAcrRNROEd zHQ1T$_Zt$reu5aCMj1|}=;?me@mqUgCQQ6ga=HR(#7qv$by_R%PFk1f*#F$iA}w9s zH6G@HWyFkbNMtsHrAeemG`NrDbR@sM-U-^#h*pw}PA9vA0$@*PKP_+5Q6MKDa4upYK(L*fTqOIlbAC^P!CsT4|`b3V)$6aL$-uCn}XszZ|hO7-1j@vX+M^b?aEHTAaXe4 zd5R5&uMm%rtxy>f`i>p(5VgB+)j^^u_fvK3M{wc^2Q&G22FxRo#g5WIHV2nyiTbvj zVD}@7oL9v6)T_dny-fL~K$lhQj>(&+&3G~^oQpn3gM4TFAejatD2I@*9*L?b%jWeE z30a6%*!ySX4j28b4ZY+^kl6DdtG5yS;4fA$mK0V6n-g0r3Z34Wx)a?D>+cx!>YqrB zhMyUQZ$5N>d-&}22z4ppvh0m&Ign8(R zayXjlneID~rp94^+k&geQ9pc%w1ZnDl3+P)r4i=pY%EzKyQ9Id zcyP@0z?Xr33Ka4B%0IzIR8&WEDpwBd4D&->Z3W^s{)#iB;)M(A%{-#tZgz6ysc-I6dOc1+e%# z9S{pk=Nr2-U!4-V?#QRTqoi@EDGI`I%!M(?J|A&L0i z7d1H7dXqPR?8iE2$+>RRu4kWoCU(>`m&X$D#T+k;?quat4hm254v}FYO0fEO)VC9l z^MDpL{S$52#t%o>lgA3t)tkkdE8j=-*(%;mJSE*e$i*c!e^t}_%Uo`}h;O%#j340T zuH>kPwy5IvvET>cIj%N+Pnw4eaQ*^W=+!D;8v#PGUm->=RQ6V(uBKh443>cANda2n zo2x)AyVOYXmiM)xdm+=f)Y_hUA!#W#XrM^8(7GZ%fqo7$NZ7G+8 zk?dx&P@VxiG?j{q$2f?B(gY#$-IFn_k7;f-bc?wME=tB%2IdS`)!#{2W_p)11O3Fk z%Pe|U5Z!ujV1PSIa!e>j-M4?4G_;0N=x(~+2R7*Z)~ZI|I?$n|pd-S+b?ADTVUGzx zU7ap2q7&(PE?quyVG|KsYMywDUJTWmrU@k}Zm)Rhki}jtsH_0*rua{8pjFo_-c+!1&8X` zQ^`1Y4aXtje~5E_Ibb*b^lCS0iy z8*a_Ut-|U4#g*Spr(G9#Xr*(EqmpjUQ~o3SrW?DbR?NkY#r?cjd-^25F6xuq3M;AUtNZIOiDdzsUuuH_MC4wBPMcru^;paR!)cPof#VhU*l!S+R(U%3Ss9(!NJvskNn`WQBw_nw7;f^?5OdJa zujH-AZLD`4oI7@Fg?y*%MYbom+Cc zI7G{RX#zj9tDlf}d==F(f3IJOKBKy!o^j$Q>-F_5@uP;KKmX&1i1g`x6gCe&j^%4+ zE81`Cw0&=Oq(d9=ta#*Za#*hnD~-|kCRMF_rL%}2oUY!SaX@4Tvb5o%G^?t-+fB*( zBhb;M5ml)S_J?qILFpGp-xSSA#vHXCV;;oVEt&7Kl^s3K9IerXBHnom()$Ugm@+!) zhsX`Bys~jg*}ZqGnU8t%DGGYo_o_m_108d~>&L)yWbVAmWv;PawZ!VJxCN}G7BP0A zH+X8pMyE$-F&_g{jjU^AGEd;M`4w!#;Y6Yh8DTiI`8p_{DTbR1)~0;0!QdYjpr^8q zAAaAfLN!LkcbI9a!{ud2kDe^RZDQ5Qrbi<6#|#WzViojcMe0<7{gS%8I&Qf)#3D*y z`SHxP@oc=C+jKVS=D;8rGN7xL9_+dN$08iw7kP@FD-Veo(%AAVW95}k{MjeYODmU% zvHZ#?om2V!@3$dt+!)nvT8{c|(p@dYv|o32DiqyBjK6UyRZpzvp-Hqy@juGx%`V`< zAHUrF#Ly!NWH^)TTk($WbBZ3H#+2+*{(=nH6>J*bdE<98<$1amBNjdD*-5+e3AHQJ z$(xf7Fq6ztUl`gk5!w-OQg_!P2K%Xnp=Z1@jRzJ3!AgOHp+z%Ll#o*7T2tt>*z6w# zdz?zWt5|Y2^~Fdo7dQFq!bj8u>{cUE9b>;yM>h_5I%PB}d_RCR+x4t+flT3Sh%JyV zvGDRS!@F^e(Dgn&+eFfWa(GClPA~aCWE&b3OZ-{Ri83NzWpoo|MGRjB>VQb}kc3=W zZARa{)ox8-7S9hg+dps1W}c7fyS2`qN+>ONRJVoT16Ur|dS-($3GRhh8~<@8#Z6Rr z?NPc7y;oJ7!}#P(t~boc&BG%KoujDL>vIrOXZ3e-!Xb`VCCW~olisTN?;IWZykM%! z0StidKJ_u_7`qAA!QXO2JN>-HgysDwnqqw$nKp!3_>A|TZ8#~Unb~MxPc2TlowJ57 zC895stsV|!5>Rw-FWWJ@U7oItY-ovr8?3+2@vY_1XeMJ?YIHar$n!VJwt`T+!Cie-(EaDE3ytMfF7=lX zIQg7`jzT`u%OfV^mkX5XbqSVaC6#G9-QAfv?&>{2IR=-P9Lxw|^-zG!bmub%Mq4(6=*WuJXj#=RFKBBAO8~DCq@iBq34OW=AQY4I5Ftc)6;feIk64t z%wqa(OkZ-ZELmM@q65`K@oRg2V;l_0N!v=ri-u;by-1lJB5nH2<~ND`+v6N^QkkK#5UXe(=5An_*FXSo5>VB|i%5|^|!W-W9ulNMmQ z`ah8wR8K^L;F8YWDb$i|D5yOyt!p%|!C(#zD+Gn@2z(;JWm<$f(GeNTh@Ghp)~WqZ~rH5w5vqM zheL?nrTF*v0yk=8SH^9JGO4&OQWVwch1^X6#G(4R-wcY>I&Ne;@oRIgwLleuMvqst zyGLeW&kUTFNOm8V1fCY8lyWzmT(Y>SC}`_Y@x7v%mFHID`$8#I)qVFyS zj%4vO9UZkyZ`!l}ZY-i|w_`dm4$T{6oOe%O8jOtO^Aq+Sh>a*~2w%?w3u0mzqRf?M z-TvX*CC2N^bo+&^pI>Iu6{tVd_U)*??ZxEzG``0{n@g>AzJ#j!h*bqqy69jgyRSa# z@)}&1ca(NS@L{^SUA*|i?4$Hdw#I8?FaG!V*soAd!l|e1VTB6W?3LbNgkZwa;c%vz zDDV6ClPW#IV=_D2&smQG-+pOi=(o>Kc(ACCecRTU7vmp*5ss;P`f>JOsf20n+k}N1 zX#Ja&?SU>+`URHjN&6ueG9;o~iBNm{`7dhKh}S7Zu8fnK0xD&d2Z%jrI!09LQ>M@kQ~M=D0+40PWYjar4%b*j5n4LYsQY zH1kXfW7B~vX;gVsJo!vo#a@?ey$DKe4I!}KB}UDX{5ctaWi=P=vXH}7K!uZEL2Gr2 z&`0ajNp5zR*R0DLKP+N16rVn3NyAYWDeCch-~whY^rbM3Xq5N2Yh)-%Ci=ImmAaqD zy6NB@U%L8GRQ>5`eKK&MG%5e6;Ox5kO*~=$J6g?=f_a2Pz~uVt)Q?~z^S;2%xlvTt z#T_N7?Ucd#LpQA%`I_7s#gIl4-%QU+PiO)8o1xvPMN?7s)NPHnA68`3e>~7OSa;&Q z6rGJ;Oo=@-6?L;^;`4ehn9?-HFycv@+1BPXsw`C!$+UV`+&grLSSGy}r0BjGr%>#p z(3elw*DKYGB5i5=N+c}zBrkV;>Aq3w4(p9$i9}f5-0DZfmdX7ST1L9>=J0?NKfok@ zw?=&$R#Z_jP+b1v2?|E|Ta7>my6^Zvu3AHu2$t+N&gnVFO^EFywZg+CZCZE;=qWV` zVZHpm_l)0=foHq^=ACN<{U8^WSKRj(bBjsoC;dl;{P)N68$TD24XEt3TLd4_Jd45K zU$?Pj$O#rqQ-DgFD#WjQSNaw_m!9wt$0-@!J)x=`2@kc2dL@0FlluXibQrI^{g+en!j$6bcPL1@kANQBI~hn1!Uic~zK9pV>4n+?gw&@?zw# zx5B02993?1t*W}5UTOSAkC1R|l^i3f<7;w%C2|rBUOA~B9;no<*0N17db(l-H>y+r&Skar- z#k%Los;BKoB9_*826q^6y^r;=qB63vJ$-M-aJi76Wof?Z{x$`s>$8Zk%bmrWo!x(= zjf_2dyYXIkgDjhEJlTUWQook8y1nQX@AvI*PAG)!f;}k}Z+BKV%qB>wG4#6bnuXUZ zEw_Elew|n;B-6V!erk;pD2qsW`SCfgw#-NU2~G+N9qMywFvHPs&XiDVi4lo*HsQUz zS4Wqb4N+nUBRyTbEg#yvRRCdJZ9Q#Giea%CLEquk54WPBwT#k(@GIrSez}jz%cQdB zLhId?&RgACS5!Rfd-(jAx!7`^?M%KRDa7_+Fv>G--{n+d61&)g?Nq51CE{hpbpTpK zI>inhCXQdo-LaW}dQr_Y_zI4wy8$?yu8$Prj@@$`{I~YmK|Ide9G0Nht&VG&ilM~g z9eiht->7u-b*MZn#m7%t>M`N|rNH?QxQrU8;b%LU;N;vxEy{I0On~lh;zuHkzmy#kElIRDaShWa1WLp%8 z*6^kX@%Oqt649}pC}{MwSrqg~9Y*Z|RhT`x+eNHtmy^bOe5GB2a?skhKaA07XfsCy zKeq^eXq7cjpw&1^m*k-5_6F*Ocy;{|jJr*oNeGBvJa}lYS;hrZM zxwA3CptD~r>z1oe%nEYne5JR8l{c`!GE&mh1&Y<7Umz#4r`bjQjgKTkjl}Y~^__y~ ziZ`=!XP(%fDmw6g5;(D*mVByb4Uxebve>Y`Mgf{nU zfgx6q5x0AR8Gi?uzhQS>G{0XPC?VK85I=*9Pi~~n+4MD*Mq-LzxJS}p z=!q+Y+|C8*gX=u8`P*-eNs^j;?HzH@cB(N-qqPlI_1yk@Nq z5~;f%grENJz(3{XEU~4vdEVOY4bZm#5MdiXTrnJ|fHd#AzU~Y<+qYf6nR#(q$=Oj2 zC{o|PA0RXn@!vd*yf>9L9zs46Omsaxe>iHRkuFN8$Azmeq4?z0#}$3=BMsRupKY1c z5Rlxtf{?9razCOg{a}b1B!9=8rNi|D_T3vNA9#_G?MJ1lS2{4`-X;PQdN-d(O=D1& zpCCXpvEJ6$lk^S7{9`s=b#mH&f@kd}v~j(A(^Q>V!$PN)(Pm)3nZ|ohN`EaESz7{q zS0zcP`0tv;d*@u;>U$AZav-{Ke%fY!@69vMm7bJUxeH1k_fXV^mG-c6%&vRCk zNJHhPM@~-88#gy?1^Pvl*~jcF@$rDsSu}p6qq?HWL7Qhhrkbr8dOhv%+-3g!hyJ;> z_YhBdQ6F{V%!b%vXCYTi?aR$cbt*USiC&ct&y4Lx6`xN z(-wUCpmqa3DbRUcUP#%&y-BHFT{Zf_EJ{YCzLNVJ)$y$<>SUjQk{G1Af67If4pLow zC&`FT@nK_u=dn*+ai_!>iywjVB`fG*!TuQ{qZT$)viVFs@)`p`CGE)f2Ig~rD^4^W zhG$x|3;2zQn;J(b<#t1U`qIZ{{>WMP8``(UHj18p^>Q@*azX}FD_QC6#%xbrb{Rs4 z0iQvZHH3GkEQHd#U)jU~(QA+7{~w;tGAzpXdHcI`cQ*pkDV>W**V0HVB^}Z&EZra_ zNG>3ejUXM;-Q6wltl!`Nc;4=@Z|=SJntSFvKQluLr3yG^N8VrB&J)NH$XvG1 zdd(s0um~Nzdf)b1>iijf?5Z6QsTgv-g34hZa9`~y8F_a!dAn@(Xpo~^sW9fqEc%y; zrY$oHwh~Dncvi#acqC^!s1^8lcC>w03q9jDA8?O4)^Re1z>pGenkHqBs zODmD@pYP~5#)0k-gf&6Lsc}Hd~4BKZ6oE+1BcWAci zq>+!Ilj)DU^0-&YUT*OvPb|Fs??UBTT?3>$1os^V`* zjiZTGP46V12m8;T_?ACxkvf#*`aUN#jK5M%h;*tx7KYsIBGpxC(L8O^EMmur-VeTa z{RY>eX+M9$Zv3PrH;%WF&|SiiOPbFQqm%O2{hqcwH5ENsqS>joBJwH-mqPCFc9w#R z4BJyYH}DPO%DZ5}R9wsUi-v>oyQS=z;N3Lj;w*e54)FpKCC9jplhEY-=Eg+rH9 z-caDTsA^92)qRczT!NZ&4;I?&OFl>NLeVXtiTRs+6}doEm_{D}5e@7>2{5j8@+YdjJ+TG$1WF{k52(^E}lDXf{ zFKP+whm!fp80{9~7@e?#U2jsXu{|7g%1);1jTv*!`puVDpw zJt-Rnwqk0%9k^+WjXV=Gy!5-hzoF|TSz2G41O7dwHj|x< z$NrjUDS2OGwmwmMb2^mWzgDZU*8aR_dv^Kz{l?S)@$KfX61M%UF0S76{PcwCECnuJ z9`nLVO#0S4qM*~7lCj0*ke#+?)WC~9IZ>~DlEe>}JXjpY;J|fu$)|fa4$=WMYJt7S2eg7JoRCJy^E+8#&;b;@obnXjSusjSwcR|1~7 z!@G8+U5+x(0*Nf;cvrS@LOnP4rB;o;Qnd7RK|lld{odt;lI7k}vq1a-Wo4xnFJe9C zFhkG8HJoCR>b5)#DBH``KTSy7Ztk-9saAKr%3n1q zM49ULniKWg7r?@DS)1CDu%usjh+0u_t3;Knvu_OKmPc$45%a`Qw*Gm$+imq@{C&!g zIitVVOh`H>e9IF4zzQ(M&azDWx`ut5}q7u;2nWG4%co-qpjD6KI7p&+<~@>!A-<`209YIkYrN5{z>4Q4b(e& zY_^T4#$OlE=hV-1R+0p+om+`oC)Em6W-z8lU7VLR9r_gH+>n9e=#@ajeoBCJsbZ@? zJS10Uug5!3*Oa*_n;6ItYt_u;i=WU`qToz3!FYa4we2??---KwSlU+pnx6)5@E zk*3~8nK@}1en7eA;jg{XXr}c~CAUgmYdyzmv-GXpluEdIniT2|P~M*ou;NZ6`5eFz zpxL{wRMk>~QbKrAx-RRSL-cV_E@z{f6w5XUclRZq^WXMl42^?lysP)mO<%QL zXXGnR!#`7HWx#GK&w~Fx$bh_en1w(x%MVu#rw1!^-K#%+=yvUMqJEX2zVW4gWUrC-aar0X1u1}2o}rZfmg zwqA70bXP*e)wd|R?hJxM`|~8?r^4fI=V#i&x}?8hqIL2?YFbWJc?UUifut^;vK!ydCf)sn{JpPIOg0jO)=vnwTt8u|X=qx#o=Gfak6}P?CzE5%{K4p`U5#Zj!Wk_ zzD#&bP19-@b!B_3KrXud&Pr#2W~GIpCgAUKjqiG~PqEUw173}eNkmRA?j??cw{Pwf zknD5Fr(rhoDKUd(%{27)__%({P@`Z&69zlsFr^2bB!pl+O>B1E2MBe(FW1 ze*}(7JrZ#Q^!1+TNcMRY>gZ^LP4yybYXuQ%%b=p*8c`1h~n%mV`LXYGxnu0XKgFLrcX`U43H&^2UY7=QpOD}xrk;u&8vo9C35cHQ$j^U1z{32WFVGp1E)q_TF3j{Kr0nobw zfTAnuN6YM}l`as*EFBrh+Smxv11~1-eKbg33HW4w6>2D18ZEyPch3WZ?I5KOynR=j zt1x3I{HZG&Uu5A|w1`#Q)1frN{bR6a0(sqbxhC7>$`000?;ITcI&>?r?Tae?3C#=} zjAP-?IS*BGMoWTD5bN2z^PKB-MdadcJdp+H(BDH z=>2!e5k-04P*y|(dlee3js6+7w$-a+RM;Q|W;1B30!GV$UahqnBZ?77C&xu<+cKLV~)k1h1 zz4i3kCu5Cw5;tvQe@1Kza&w3v|R&nn1#3rGcTDKn_sNUT03&7bd9I( ztQB}b0V>)&jIA0}!Yg_P(KpLM$R{*XNo{ZG$!O6`#N_vUclbt@m57~%Kcz4IgbqpW z$%4#XsAhI^8t_}=s{8bQ?VZ?l)u9Z?%^GJU`Rs|))62w#C}WyW*VBPB#th$vZbR%f zz7*6@&7^t|8XCU&5G0_HD;c9YgF(m2y?(2!BTo0>UTDaTrDCW^@_;NcUoxR~5vy@e zTF=RPJgh4%Th!|eOWS1$WU5ZxRlz<*@Wrq3R|Y&V7OS8n`qPf>N7p@^5tF8v#du~C zeHDmF@Ak`kxBDd@x6N688ZmdAVPv#;L4D#`)eDQ^dzZ8{)g8~7&5g|%VkPC#&f!f@ z9OJ)ZLhshB90K0@xU6oL*4WXfDB#?3p*gJV*ss_#+mLks*85>8XYrO6DNYs6&GSZP zX~&5+UEa6{8sc83*G<5TGFnNFU2vo zA%Ru($raW$ulRJwmAwCmGOf3`TJvL6i@N+IR7tPbj2P8?m*(k8*Q-NK6wJ}_Yl}Yj z>(Q5_5yI{OO7Kcqnhat`jVtNQh9_Hsf0Q-M58U8l&kfo1?mIhfLGRJul_~Nd87}(C zcHA2I_xJ)us^sHz5Z2sL31%n{tWbu~--A_l<@$F-vmw8Hus0fk$6PzS<{pO2`i;AB(N6yAM=Eq0J50~N4ySTi z!YL*4MuGQZ?eSv?Vsw=mZ};+~9>}!W|73|w(I_{71Gx+V7XpMzEmT1Je*ozT49@%S zW9**L&F_tMl@~s|?JGn5aM9?t>-P6P_X4B*e4yTm{jkK}_M?gH+11S%dH%DsP0pDt z)aLB%MDHY=&H&lrZ+$h1!m&EiK)9vf=%WR9FJ_~^*xTg^gi-hc+NApUjQh|~TTT-f zU0E8q!bouvNAk%$bLXk$MW?1#p)3DJBX8{rbg;1R=2zU|784)om@ZGAOk^-BX<@nfSkZL-{bx%ebndqB-w`(%ijahK%EjBZeCmZ5bNUICM`od z-`)4mc1Q31-iYknv)}!{XyP0M8H?IL+n{Kd3K;3gq zN7sI#@B;red=LG>+_NIL|EH<(TS&*PtBjG+ym`*T9Wu%ApX^DOq6bB);}#*~zDqdX z*M}dFzjWx^hZ2ql2q%-_GhoVWbKR_e*UCkaa5et;Vxr!i>l2n;GZC_s=%`@?thwB)7^gSPltbq z$%d3(vCh-Ala-%8eC;;ad6Uh0Fm?7|{An&)TC+=M?0-M)+nn>{+IX_Q<-}uHG zj4j2KkJXVF8(~^+-A)e0e3g;6q2H&uttJjaC1TnhBMx)PXJ zSNe9LS%gsk5luwoiYcqQbj!c#bWy?VC&u-A%&^T@&?mSd+(yUvt%9v0lS8EK4;s9# zBX^x#E+wxa(x&Qgb3Ul`Jn^ZHNlTKGMQ2>$y8F<=4qw?piTfwX@6!4B77l;{(8v7@ z3wKONrDl&}GNb4TS>>NoKdC8c_){3;-eZ<(fzaCP5I|kJJW8mnn{w4E&VcY@V)B!8 zd=fPQ-u(~eT-yn9V<85-gSCo~4ckq3>nhd^ssuw*17Gc*`~&t{t7-BNbZfQjkSW76I=&+@ z*nUGp;Wr!ne79$rue+LpFaa(l*CrY0U}gFoC&)B6xCsj`*&;#(c2u%_+CzhKNW!zr zm#Md;(zo+9us~ZDZ3{@c*8(H>1Jv2ZW3Q4D6NNW*@jJw9 z8t9qNI`s~1Qizwu@d+=6jZ{D~xfE)3(+eu<$yls~c4rderh~a(5yROyU)x&~8y%Dt z%p`R?e-5$LPdeU563o~xtc6p&YVyvckw#hcuAQ2(GJi*+V^QNfGhfFGMhH;4@vT5O?nNfl=XJt!M{MnPYpsqm|pTZ2xgY{+q_~LTH_!*{P zLp`j#oLFvSlHxNTay=o=rFT(FBLQtWEqp+^=QjM3NButlEX*7o6@25=wBiyyIRfQq zjNHjr86qG|!m~y&oFVSwOaH~Q}G2^sVzilgg)CdaTcWTERJ>kijj_T*H_2Nwdp zNHrRe;naxW1JsS2R0mGeB7={JV2Fkv9C!;zTMd+(jL0aE5wAEwD)0d&crKs}!eC!? zlfUirzQw=N-|fqsFGaTRXzJ`g!{48D47Df@F%37lVcL%7%B#3wIymn0J78ONS}7W+6UN4=jIF$l zVr&KK`HXbiWxe)RV8{A4Kc4497D4I5=iq4(Suts3ka(61)YTU$fPJAlETNgiEvRZE z>%FVX{4-rpQvj69Rj06LS9ErxyKidZTbvGEYjVOFZ6wTTC`x7c3I9&O;um4}-bpUN zmYj%_b+s0Usi7hS){I_~dH`Np#5`FPRKdmqFBC*g@5%kLf32gAE1o@(oSu)L-5Zmf zr;npKY-!^}G0l`paqOrl%PH%x&gmqz1^@2QVW^30^i8F}u{`wmQ973HKgjP8khv|J zpgBbN_YW}wja&FZocL4rifW)4Pmc`n%g+W)+}wP`o_JBWhi(cTMdK|_gga`CMYUwh zCx-O?cOF|Y1@!`7ROIY`x?f|0-(vCoX5jc>fXop9{>{?y2k_sazU#?F9I7yyE@@8n zH4yCgRLcGdJsp7XT5#Z90aWNP1^rV!?vJ&Eu;ru266$uy%j^ACEZDL(Kq+t3W^5k_m`8I`|sr$8UYtzas@R!(LM=&@mV>*9A;{7_;n%~ zPJ%i}Mii$>>(!>{Y{)YPcQWj6ZVeIdSHM;;fuRG;N}b7xjWpvg~kwM4bruexGKW47lx zYCV0--^hd~jzH8XaO_<6pv!zQ*Vp>BURRwib+F<#D%0E}J~b-BT1_o+MgFfR-CU-c z{~Z==76RP2E$lpy1x(H8K@nYWZ9R_r1fxH!;w}O+dR;zq|8b2Gjk;e%4-w1EZlrsI zP`MlaBF%*HguV!R(NXznZIJjk59gRlK9c^He2VW%o|ITLk}iKw24h;(rFJ)w{i=0g z`duj!crVr9LNAiuBW#U2lG?b|(lqtPBPe{+gK2?3QNG_#rz4kuKpq*Yf)0D!{uv5D1tyjg;n3^Ud4Xiy(<|;0PBeN zVr6Cwpv<>*$OvS5;ZNxoebRJ?(A|g--%A73xfu<~kV1_b=%I*o5`j$5h1pTT`dESR zK@7ERG$cJ>31L5tBXJV^mrO)KivV)8BBM30IMl_-EU#^6}*zuinSw*u;G=r?sD%ZaO$|f5yqJrHV zp$=CTi`!;3u_yXKJzvdf1OdPS;b&@x*t`Pi@qQ$E^ru8c1S}3RC2c^ROST92f9!Gq zT%CHII!g3tN`$=SlR(??epf3_o+h$A^WpiNM*40Pg`U-ERD$C@R@96BGu`DCCD{&m!rO8 z5=ROmoqLgp7kH)?nNkYS>*zt#z>J^x;<3cXuQk3FG=TyvlO2+qmW2OD(PueN(;0%S zD;c`U`>At3l}DQy5A1EUi6r&Cb78Q##R<@yesssG+4BGSdA~+n|NruJ#2tD8Q~6;1X|b~8)=E6%_~l4gmX#>oPZ^%`?*4UJ!ofAM z{Gk_>DonymXDP@2PcEt!%zDc&{Es0>WhRaMNr)A4B}nId^S?x$=jlUg40n#}MY+@P z+S*Rj&cTp8TaEzL-V%RN-jeqX6ED}3F9vaCp=By9!JOLbdP`jG&##?27NtN^jdFo< zA*g~$59T-oXCKd+DWsO=J=JcB8--FQOmM}lq(qhGa;}>eEn^ySw%QE)qPzuJ=>F`;>S__p! zLczR1c+JxgtgRdScb`mcC}M18GHgBTW!fL_q9jmlWhK5}@i1GNvx00Go?I$Q>Jc*B z@t`Lm+-b3t610*=o9;EN#O?6L-%G@(&&kf|sJDF4h@=6pz{ekgri(uR4rGcDO}Swl z|B8|HHP2Td((V9KP=bGJe0Au_u+ebX|3%X4!w_WMIs-nHgNfSDX_p*3Ix7K>O|mNV zQvShCn3bjGN!8O=E{4tNH@_OG>H)N z?^gW1_Ng16^6;bwq1~hT;elL#}3f6Cg-F2u6ZsGO0*_HAQx+btsDVTnOa zQIdStHR_xpLxm3_f?U*1>`ne4H0vAJ$1g$8?8?5HMgR518=O!7|9*`5Ff@3mb6Swp zP5Rq&Hf8VA9&y&MjlmVMwhgn;i=X5O8V#o;(3IuO}LX%Hd$6FnoUH!pq3=@U%~FB7zGJ+{V{z>E_}(xg=m>qfoeAN=a^tc zT8sGEEMa|U@x0dL_wLzv%wgxFT^brW^*Fq*eCBz%AazBV z{euOt27~CqWzlDb`ZgaL7L+RI?o>7|d%1(}laWX5oM@2A;SFU;u@1$51IL9&$=#vgEoyH^j}y! z)(lt$q;IT034Fln&u*+(jN1OdKN9n)M`ho`J0{_{u90g>F8@vZ*xNq%aKa)NG6EbqK}=&vcx#S%BcVrXL( zCU`UOoE2Zp24u^(RePxKpXP2uE{)H*P1>F&@aeZ~nh^3g|G#2kCE7gvB#&>Dn)IcC zwup$yjYw(eSH<8cLRJd&RJcJnbFarnBluPKbFezv-vAD<3qHdMyF5g15QqfZ{c5oX8AKut|6+wOvc*FgPsPI*_{XiUr@G=Ie2(IH}dE5j;gwDG^N zi4M!MDH)p(#!8(`EUOmK^&IZ7X(+6yLY33Oo0=9bto}eymyl~mn=9+6iXEH%lJDrW zu&R#OYiWb6;s|sQO=xZH^~;`Ce&&=y0PEnR*-F&nzPIj;W>F|Ws7@m(@2w^ZxR+oB z7og|gJMyk(2X_mnr)Au-^Lz2p)l?q)c@aO)>00{)?bX zJJ%ilVD=FVnD%1wzY~p~q*+=?9O-{7+ih{iDEab>Mz?rkvnwu;^+=@?zQGz35i&K_ z(+%lO89+%sTno77A#EgOpLuzt0;%V0JG;Giw^~ZAW%IMf$dIZ%Ks1#({%j$|cQL_d zvIOlf^QL%?nZ2q(B9<*pWblma5P0Ld@j|>9-@;`SDU!=Vt&yA%0*PE|g=#Gz*+|8i z2kY?th{oJ`@fxMp<>W==cMFPb17P|_!@1Zs-`BmIa=ZX33^Lcz7>kyTNPFKN2QAef zl>Ph;nLRDRgug9h;&nFo)F@%}>b|wKA&K0!~MwyE3 z@kC&wE<7ZPoaqt3FKiWJAC#Q-EOb2*qI)$OJ2T|DYcmF`fdve5Z|8e+cz~y&2qp2qx*kn;i}ckv zHZv)FJ6`}6i<&aUdCP&wsmlNeC9+lBd=TpY3J;M0=Fd5OY7kr~{j8=?^7I@ns5@V2tBqPqz~Td9T83XS5G|6AtQRy9%jpu<9rSrJyyisd5ZT! z&U4%;IsH;E$aRngFAzn+uROpTT{cDLlLcJfeX0}c&Pj2Q-kUMz$W{)zrX?bgdsBy7 z^tX;w!uG>mYFt7&?ta8Av1`*)4D01PzjPQTp1!YtYem)#fPX}oBB9(pIxd9pIk7ItKG2giTjTw*kF@GAW9GCG@f;^h9( zMY<`!-PeOCILHg>cvYZ6r|mqdzFssNQHIsOT6hJrj{0~%8wBJ~-sAL;F;T0u>QC-6 z|h@fDW+& z&uPpE2L&1I%mMNuB=8(T)3QA+`XpGO*7EXVG=Yhm1vB^2s9(wxHNXWtDz1ZJzF=Q97rZGv{j$Zw! zz&BN-o(<^;ei{J+Ju-aC$l%{esc5dQM#=ciy|3A#5xf798+5gEhPrtI8N{Am8WaeX zZC9$9p@)uI%X+*e3qAaKCB6qqgGg<8ede1GkB@nJIVFhu;>`{UlH}rNp4atiXQKVa zTBds8!(WdYbRHgD&T$|-)#hD4p^iryp42mV*${W_Y?&+J#5~PVG0@TQNoQB6;L7|# zHDgD&+3mw$$)l1ut)N!AhRY6X78LxW9cC^t)ng}g7Gj7|*ie;rY{t>QMX)U?%f6-S z62|?=_j5-}7aVry3SIYN8j{if%T(V8Sa5wM9dyIj6#2D>XIQX#TP*Rz&N*#%rFGkpy%6>J_CMIKL>Og?x(*FV7g^F6MYs9AR*OVtLe$%9 z<4p~5uN?^o&K^mVB$?>FGl*`wf#srIk83bF2PgIT%6Rzwpyh6)zn1AFwBi#-n!YW= zt9G2158Z3P1q`qOylQl0#L~`{z`qqT^tcogvDbEcoJ%?TsA^dF(6en!gx!P*h?^Cl zl5qIwej#q8GQl^yk~9^sd%3TG*~8(sG02@}J6+IFM7AfkwYzuRH%*+8oRXU~j)^?z zK7rU|mH4$xU~{?-)?7o&&K|+W!_|>25)^jrLW^~wqct}c(pzlpyRG25m#(QbJ{B7@ zz?f!JLWBUDo8IFr??G^GygF>`WFhuEyWR_9cn0&QS_$fbJ3i_cVMLHG8qUix-f66K za+VJKD%ks4I(bnkk#zBRSB!VtLhp7AEZ*ryg{G0zdgH^IzECK$8;s8j;h4VX++xC& zySI5~YhwXN2o1e+aA)V9h@vRhqVLGatOcWH<6PxB*Wsd1FwttNXYUp9lviCMyK*olj#med**I;jJ-zwVh7c>KoLx@W9 zCr9qq-P14X#d5UUgK=*VM@wGp>f2b-LQ-9rOBUin>XkkVCoWv_yFtgEG;*0g=}If|)U?468+0&^$&?i_3vsCoefrR}=krT+o(8exsSTo! z9CKJLF621(TAnxF>-*`PjY@;1Cki@Vm^}GXVTYG$YI}&wxe)EP@FK8A%8rIw*36N+ zFDi1&m(FPc2ka+Xy}fPY>Fphzp6X*6m@oOs;tg&mH8s9oFwY9k3YEB#!l)~{3+q>= z@>-zJNL$hkek)Y&eQ@-!CBYE`;D%j)Qumi06LMPPJ%$X2sX0C@`~b!ra(LECnid-%rV zkKVgaQ!hBzaDpDzFMO{PCL3KjiHmfZbM=aSZ+Zv^yDJf$7HB+6zaoQ{Bks>>0^d9Y z-yI(O8jwz0Z_)^-)oHxjV+?c^ql_q)-{muOyX?L>y;f}pv4@BmZ;wwlW}1QSaMote zl1^*L@fk&4QF?g||9bDz|0OI4{a)Mx*G?{3D1h(K0)6V2ppr_Mz>Z+GrUg78v&~uA zm8JNt1rZB=RV|*zTP#=~3JsCYLl$v8V`4Uz9)P(0S{|r-orP7Ihg61AikKhh_4pwG zcjyBzhm}iDa`ncE$udd_*58YuAi$|WNEyX7XNFh+xB(mfr4`a^fIRh~o-YJ!CIZw^ zS_Y!fyu{h-TV-^W@U2Jr-NWpa#e(RGaLwHv4MQ|Mm3)|Vxl|iC0U^-h5?}OveMH~0 zE`XzQbwU1L)`O;N9l~4{dO`;%OJ`s$T}ZG9K=dye` z{lOpBNF{S^$pBBD6_hK@ufpu7!5+e+y1Xv~4)}ycq4C%yn1z*vWk2!NaSw5Mq@cFncl*~V0+SzJXMnK&fmOFWKATW%s|4fXVAO1SgM8#6y1B7xZP_hV1aQ-_^D zT9Kwk7vDq6Ow8P<+_M^Go(m5YD zwt1YNB5wp}2I!>dMmQm2-7t=8Vy-g+Gcg^+i%eG6mMD`0_q7WN@2Rx>c@U3}-)^NK zdV0Q~^GRP|Cb_aZVIWR+7=;hdi^ke;Egb^nKYX|9e2k%Fx$J4`xIkjEz1p(ZpRg9M}U!r_Dqv4+H!MKmS&EX>p#8Xp`k7bM@ekt*NYJ56DjR52P zik$sB-O^)sW|4p~oo$!N(ms8p>;g&lANuQ)@Solu_W4By7MK=E z#?SEtFq-Osp#OB%W<9@fk@lI$i2J zj&u5l>|_-7e4?EF_)*$fqvh|VGHc#iWKGG*@Z&aXCGAge5Jm>a1xn*T2B~CIe-^2Y zKa|CiEsRIHM^mp`5Upz9b@*)R&g*X;M+9b zO%7_)vdgTKx8T;MoMt;|;v9QzLme#GQz3jtt>$3~G2Z}b%I>7J?73~yJ|loft54e~ zqVTsN_jQiKjMf_m(2|uK>M6m>uV$t6o_4m%eT3=ZpIM0pKZ`?1MkeeAjPsxJo4(5^ ztgkJ)`#D0Jyr^@}3LOO4$O0&9Zu$2Od*XAU(`2u2;>|irqZn9M&EE8*0HXs4{gI5q zB;MpptNE2uzy2XCa1a$EesE+(hoeoyBa(1TSPv?hu1$?_->iqL+G?!_G1`(-+{p)CHXr4Z@Rfzo8iy`UxIZdk5( zeEl4zIjH>^0C>Jsej}@Ud6Up(m)rY81OMA_WWmTwPwK-=kpwx#T+VEJ72jT)ezft$ zKe(yZM8oMq6j_-ekQupLt+;4K5n)ve*0EQjXmM^>>Q`5MYLw$MOJT|zIQPa`ymG_`&7EyU}<%-^bk2fyjHNrN#EdiG8)}@VlUaJ_3#roCJ6wMBatupCyM{ z=>;bsCDV3zryQ*MFzBN3vi!C?Y`keqB)K@x;GkubI>c;%z3(;Bk`kwVdMWs6E$iT8hK&r(D__r7s&8blzZ1RAAaWb(fQs1*qsGvFo z>NyV$LW1y!)mX#>rsMfT_b@{xg)KD;48KNBJtFId>Lj6*kfDN0r6`n}ap8#hC4XZP zCftAEfKgE@pe_ZFej?pbKiE4tl7DNDS;p~O=m@lB zT?{WRd(H4;07dmbMB<@?&Ks2AtCoJCv+~xd{Z}AKo%Cp{%El7&ptn%2PsC*Nwv`Mp zT^=(9%M|!BeF~yCapI0<*;`;QSN=I_&{~}co2eDTQ=T}48~IzT>ATO^D!mP8dh~XD z=OdubXClfXRK?bnQr0gmbCg)2yfcc>80`Dz5k*>95u+%PuTKpHu_TRtf68a<=SzCv zzAj*dZ`BoLOq@Ce60Hdm6h4qk`fSen1Bd=-0i(maH&Ea6GKn}%OI(6#eCicWhAmwx z3S9UB=e`Ne+)Ipaekq}9fHHp16D1AX@Y0Y^r9)ImARGY)AO5i?^j?JZ!-BG)?}4dd zN4r9X7L>H0m0!#`7j!7Ev44!>6eM+h=ZZvyWl8?d6xqWB_w(^fh65j&X)63~!%#y0 zV0zx*V>Z^RQSdnn%C1Zzx6YA<=+Hfh?6`KFTuhW;KS9a$Cuu@G!68#&7;s{Qf~bPf zF>t|iuL}rrG+0e|GQQ?U51y_DX#QuJ9=n_&Km+2^_A(_1n9T?&^o@)t6?n^v+8+JG z8m%%=m+pLUK}Gy$txdf(@kXh6Ureu-66TKL(2G@r`XO?i0MM(60%1=j zd!voZZ^|^%;UY{ThL2UtPE>JW`fK46ONni+WyDe}8{RnJ9fdU;DYka&K%Dkjpbp1^ zlHYG!>!@y2IvY^#%Tt>C47({gzIl1*oIHa)Hdp82*P}gvzq5rerYp`iL)M;e13Qig z5Zpnou#iTdTLZkLlyi;v#BYJyss7<_da-qUHj>?E9CJGtU|YwFdmFn2lCGY=kS5Im zB#n3q8(HO^95<4pNntK-REsRZu7fKAC|;qaB6Lp9+@Z$G+nvbcNuCRpCX><7bAgNE z@IZED6%ZN8ib|uALfxKzQ&QM`@0fV;@(S7Y78B`c zGE-oV;IxrXax5-ySz7I_jUs@T6Fo59;FOzT%ZoC3!Ggp!OWid~*_8^Y zdq)fRQE~+8`9JT~aRbV1Zlca!sQy+}ohW&!o+Wrxzk3s5Kw}$+4NiY5&myJRwY=+Uh^1W`2z5}K~zjK zVSjDHN)!@zB)<{%&XnpD-pa#fJnf$;mV!;rG1y6*W+Zq!@;q$Gyp&_!2Sz@r6RQ+c zZ&i#ok4%-(_1X-5V-5E7R(16^^`zIEnp01rD8Z!fb1bpnNCt&c0;NzSq(dpMQmBFA z3e7M2c+Gam$2j}WO6&iGo2}ncr0K5R^y%-?C+#|ZHux3AWmm z+{6Av_S!OpfN*q>C=lhda2#RS(o&|oGWIMNZ)ERj&uQUhQrbEGG}dExubH25j3(9r zD|qwA1b_XsqmEdF9FnMC>UHdm(D~ip0eiC-yE~g~DXW*O2$@L}|0PUA^t^jce##kXr|q{v`YCHG>F@Icu!<#}9ap$fri&+6qTuKN)>JuTqkn0Hl(>jnv8 znksjt(eFIt-hgj(HZ8`kjRkD9Dn=LtGbM--oA8d@o%{{LIE~U(L^Z?vx_4%^XKuH{=~bsoZSdmdcr$MR|*D zH&5%D!jUsEOlRCiCs%cjr+*i)Da1+RGD`I_y(r~VhKl~m<2~ArnKE`dvRu=ti?+D8 z)s-b;)@cGgMl>p%BAgvpn-VcmM%^Akn0{68{H3W(PCnhX*~-5?mRTsng{txPh8m$e z;{AfotGdq1xz5WY9@F80&KE2MYJEubqCq#VybBtHCzkMAqYt(GcO3V$xJh6ONUgW1f!uIJ8VKbZ0BVLzGYj zc=WO-MwH!Sk>ZxBs_PJxqmhiC7j{=Bxu^$X>|_WaCRMT~e^mwaG%ozaoI^GfJNxM1$?30o36P3uOK8 zi8bQ0#P!LL;REVDB_+H%3+}4u{~2R=(av*WWlD1m$>>5 zv4B_rx{=KsAhzy;Q=W4%cZkn7#_#sN)j=nu*RZk*-Y;LOX!8AjqO_~-MU$@7_lUG0 zHeUF!Aslf8_&ZLS#ByH*+`mSU)u#x$`E3yTz6)?)T69{7DVxHsGx=tvGy8pOLUt*t z<96%+{n-$)2{j5VlT|Ws1_xO?(6Bd+(qRUS0riG4JRiLdWmj9Z=uTqZ+zssM6$dam zk9w4`{UI>p`LTPpwEu<;(l<;(T0(HL(`9q=(u~QdMdk%wgR%*AOjn%XpuNqh$wpN? zDx3?8>6{bGGlPnaxA{Ad44Ekt5`5hLBMVu=x~G%SiG%#05zomlpQ|=*HvJC+_aBX}J(pkDC?dbKED|ngOnbTG5@JHq_MlWWyEN^;xkje5yd;d1Nt*IzboCs=Y!6}u;yw5kn9uB#-D&LSVGi*{XXjSGnFOz+PDttkwPVw&X zIt!c!M|(GB(YTq-sBo!y0<+^6$ZF}18s`%;00vBYh7Yy=yt&Q zRZy0B#7Om1%fPh*N!lN|naR-FA*|g5Z;l1)q0vzbODCG{d-lfdq1}@sh4;@W(;F`o z5`@oOmSJ9K$gN?S+1U{@2Vp_)$nZ*V8CU)vQC}Ic)9+=@%k;_h(sopbNppP9*ICOdETUi(=#$k)ZQ946ldNc$i8n#8X*h|J+q z;r7CrcL$qrlqYd<%t!UoWF%buNTBB$nmA5^%!;<2r(w z-)j&86AqJqmYTq(4yh%OZn~R%j3JJ`sXdD}md1L76x&c`2 zN|Xo0SIYd_OO#VTpjTknwv~xq&kahRCq!cRh6QZX%U-tW=hc6=@#gw}D~Sr;y=d8s zmVa4^|0EsteCZ-8a?=+p6p8K?7K(H>$sc}np5D7IvwIVqinGgpdGC=h>duP2;7I;E zA>xlqS7%dP(SxFWvH~#5h%KvWRsZfq8td(r68tK>_Jb%zS0}M+z+yvwTF%CelJg%U zKu<3sp{!J1WpZF~HYeuNoD&uhC`tx~hI$Rm1uK7S|2qiIvxiZ@1LD{9jmso(lv=}Z zpoQm?ND9O^9K3H6%8EJpSADAGs*}cj88mH_I>Ls_HX(lKLxbuB5zIw(8GUNCR+A}=QfAVK_&u_Kwn@@O9yNKr=Bd6~AJ=kiPdhPxCDXl?Y5BA$mJJYG zJM8&Au@(w|M-xJGPIe?3EdOc1SH`gm=>Xp|n{lU_U3q@T>;nI7b3cf%dWS`Vnz8G>t|=_P<6UL~`4*U)^yKo6^)ZR}wD% z3E5tJADK4-r|WzoSLa5nYo#PA@inRInZRzcmtUTNuk{-PRqm}E2Lb%izro?vd;gYd z_+9s4?CtHF|96q#C%j0&*y>RN?9#+hRe*kb0oR)*xZ1(5L|qBTcJlotE=nFkACDsNKsp?c-;VU|dhP;VK}{q+eaSrL7j zy1q$(v0a_`OF+azKl+4S95LOsmz8OrZz4^5S1YsngZ=nDrNjA$_WxTiWymR8h7nWn z@zH?I4?a3NghKo*kwPX85m`&^7o*dPeX);)=}B*jM)A{<>(&%gZdNv9lMZ$CfL#sy z5|@4SxH<#Jmn0#oVz8BtxV+JLN)%#nQF7+DtOFJtgQ9{lNd=G*UAOpT1pSK%B&AUZ z!nIs%4h|02(?XX!o8H^QnM}Iq$WGjsy8ja+`{~#Usr}Dc(=_)5=#Zpts!O~s?+*v` zFy_IUZLtWeOzV-)5rI3LDpQS5F_I;+eO%~(700pc5unJE_mn({7*!_z3W(6<#IL6p z^^{T{b#H|$E8|N0-C}`{<1#_26}B|fK&M&ojisx_<7dvbleQMAM(#l~eH@%7ZoTXX z>Bd;Kl~IAAT6BnP>ZR?^5u#N2=^K_|Gn4-#vp*!#)ZdP6GpT_X(Idy0*6lss92!B@?2KQ7(miU`?-6sQ{-%3dL;p{kD6l88|5yI3==d0$u%EKF#Jj z7YcYPeKl^7CiCeK0!F1$mSFDWf7F9#MQh>ED%E&G zV5E;BYpLxyLS7>;v=$sS9J4Atodp;DMjSt?r|{gizWv|5Q^wI!@^4e^aki$^$k4@I z@@ZEY6R%I&Y{GZCAtievoAf>TF$)9Mqt~~yCLCvagci)9jK+p2)o;~ATU+!jdlG$c zzFDVsr633FzySFF)|jx!{x=TQMFLBd2IioxG)(E+ZJ6(OJt_x#4QMJzj${=TWRT?SOGLM~lx!u&Wo9}QyX7&ij|yNY;CX-U>}O!G$$3wc zkI&nR;diWVR9CR*?^bn27S{%viYMDLXM8Cq_chge^0YWM|G8do&&{StDs#;Wh?4zC z2q_I9p+SveF7JDK8w=77y~#PpU)i^`;Gm9@cql=XXUVP`j_guO!A<68NuF(?+R{{< zq^id(Vtm$^AH=Cw1RI3Qm9?&Au$i_4P^IX@tL*iD;T!yUJ}RCvS`JW|n_VvG#X09G zvX7^>8yQLY%XdyIo;v_9ASmqLiL@=WkRaw?Px!feukq&0hO=ur=R6?xtj+o{$JTf~ zQ!r8bU>pc;VLOJw~tne1>4Li*{$(2h5X7@fM{M2&w()ioB=VK029ORz_TP03Cp zkAj{7_eJBK;(s3ix&QwFKx)OA-mT*B?uvRLj{xQ=qT^2j*4Sd3$4+qK7_np~K1evF z-q!vTOX(>l{GDNMBcL8Go)KpHnzEB3@_oKj-r6>SW8V{n|7WPWVmOpFrSbfSUJgs>hg`6 z^LuCnYM_Czm)5wF1~nBIR)dT)Oq>7JS#wW^Swomp{KIt3dA2$eUX zyQ^p1t_U_`P%w->X_8zNF%=M{+*S+I$FN4LU^9G;3Mja(4ni-A()9g-m$G*y#2|(O z&`k`T7w%0kpTv~qMJJ1wlcMf?+@;bgWuhlPUf~nGA_Lp5Kks~SKd#x_uh@V;-#c}c zB3AD5YaxxQc*(Pisy)9Ykxz#+bduqE10X2FsRoNv#Z!utck7bGS@Mfczpc`nbT7pB zifGX36nz}Qbbao`esfKn=`l?NgI2(zWyP(oxx-Y7DU>!@Xm#=~IDy-o0<1WP1O8YC z2h{GX3TgP|$)9?XmwKZ*!bJygn;1&aV~;d;wKO+Ndt$$?O)$lf+N>jqA0*N9Shn%l zf#(!7AI3$&w5?X1h(N~CB|q`ZT~8`UqCTxaFWh0ACR3J|2M-c~jqb<1x(b=lS)K!m zq{+h9g5^YT4f;?+r5bX3=5Xvf>KHqBtn$S55R_I{ zZPlf_o4R{i)<+!&?H{3dxnnha5Pm~lUTcv^VYnX|BqLj2DHmI@`8UWZP>sFDB>H_I zG|#MS_kI5zv#Y?bg#^>vfsw5}(XhJKh1ct7XYHNO_fw;{%|e@<;8L%v^92C;UNdXfuOJWD^Rr_nKq}>&tIZwyma5K-?py5e~aY1v)@(I7~IsjD$6RX5XR$ zU?SHNJnvWnXWl4A8$UUX5Es;6fxdH+1*ILyswQiTb}XNzr#A>}-eg&^#$!1(;ki)t}}@rOdogsH(#q7>P31)1}wQuAY*I>(C5!ukXF8Z&%OR*6zWxnB}LlGME#2@(2vYMTPXiQ=y7f#tXe%-SB_inxV9W*2b zfu2HKwy;;1cQzcbW@OO7<4}e>DRRz2uij>vKbX!XF(COTtst+3afW42Z{oY|#Vlfl zaXfy7uq(ldBNIM53q{LPoT>5UNN-@3o{!R}ZpZfLp*%|xULv~f>hPY-t2uJ|nLwaO zIojtv0QTdg-YxR$z1sHAA-d;5DC||A<4fbCq8Dm;Fo6iAfuI`B-l^aXsRd^de%=qe z*ab-!l;$Wk_y?*nD*VZy8s)?o_ZhgB<+>XHH9=@FtmE#9;IyecSe$LzKOR4POm-~y zT!d2WOXZ;nvaR_uqS-4Dv6vSCb41HAUl~45I^gq_h88||Ze1&qs5Q;Z&1Y@}5K=6LuuK;?AnX*(?YnB8AecW$Vtb4w`7X{j1l$6&KvpH3O?F{ zo0`wleeS9RSrj|?7DYlemt{jb3^5bUThMiOwP1k`ec-qjE9o8Z1u@g7T^+DHY_ANp z{DjYkjmR{#jU(e+j{)8O@=9xfce&;vO+BFQQ+I63;5z3$hrd5Uxfd^LN>XXyA?)VV zY@(MR&$xv!*)S2$b4_;%D&7bX>5;8%yDSkQ+jEFn@)gM$2@$k4kRH^Ff$;d5JECy6 zDm7Z!#f0z8RvIpNlGrRJ$Q=KOW_S@So`1_$`jt1qZ$j;yKD!@EurfPk`C=%U&1)CK z9__Qf&^pfR`tJcRr0R9Zv^PLJa3eYxfRQ(@ts{?9w&Xwm-p$E)S2_kX>&Wl)3~X{iHk_iuKO%o0 z+As1fQL*)6Ce3nI_*bi&>PKws*&`z-Qc>*U#`(R>>X;_RRcW)Gg?|GhkNGmYUJo^h z%$G@rm+p<$Bm&hay$iO{dD$aJ>WfyUm9ksQhtkrQqaP0kj}rfy?ab!|~1eFh`5S7QV#9 z(+O4kEX8#|X7~)HY~ulmP1A-Oz;-@8-+L=ksM?HMIBQ=%@Zo810jzd~Y3Q0M33Q2f&Ov(7j z+o!0>w3#Eq7OBf@4k`GqF>l;CQ%&{a#>W+~X}?nNRS~!RJjY)+*jpS{_DIq+VM27> zl|{(>h1BX?R@7CcHpIQ99iH@0(&{L)NRuya;7!=71-YcEHe}5@`RYRK*ozHf39HPY z><~Q*Pcsfj7JO2p6+93DmK-)3B0ygkTxygXRSmC->e^ZJiJH(zmiT%rT{@Dmo;sOn zKKI(*eARX{TF6MX9&9+RuIc*G{m+8b;HW89(_7*6d?WR(W1MS!E@=-s=5UI!dm6dHMs1en>r6sr#94mAw=Fk2AcbQ9CC^ z_2z?@hSvC?%&<>6(x|s@q6O_su;D7Xg%k0d!>~YV@RxxBASe?6=p0QT%Xp-m@|b_` zSd`=M4sjaxM$;ojJ#G6#2ijs{Z z$+05;@ZAaX`R_iVV)u~X@9Z^Qahw88?QL%=2yAwSXw8?cxjoPZd@?+s6}s{f^J$qQ zEYFnYaxSiw_(6+N90!!vKHDC)G^pgwSjR8m1X*!La^RbcEPFQRum7EjLF(UW+E+G> z>tA~t;)S~&xAz%=H!Z`nh7>Q;RQUA(rxKa#;s9aMQRf@7XvZe0mJWpZIM=h@ea{g+nAF%Qp=g=4t&Eexez-EDv3=@e2r7PJHCzUZ7r8SzV`_!SR? z@j?lbf+tS_>=2&c&F1$IESGUGIoqW?w$HY(5VOZaF8ZrfgiGV`j~(T#jj=X1{QbXt z6N6l0^Nvf#ZUf^Y70`CCXyj$yFTf9D^w|mXr#4Gb%=B6#-*2eyzNe9 zFXgZ6{~!a0-XSQ(t%*szK2QCB^0PU8Hrm$Y;Z6J3x?rdi#KW^;d(|%lwPGNvR%7?H zKKa+O`FxqRN@?#du$0 zL#t*A%f%;ey3bTm!UC3HSshl8GR&0486c~kGU};Jgi5q1QcjE z#qrngNkC*WjE>*e?u#)RZLGx3gU^fY_hxxaNc)v<=m&#AS|_^N0(){l>>$$@gZbvg z2Ab~B{<2r4e_%LN1PfPJ;>)iT47%PCFw6#ibBEdW79?4ea2ym1X4Lgj3HJOc74p|f z-`15tbvrDiTfGT>6UNCA#^HUz3BT6l7kg^7pQw^yAjdkbj)*N*&Bx8F*5{Pc@KdvG z&WAdxra_h$GVm%gDfkrhf0Y6jjX+bT@ClHV#*tRiR zzK(!RjMQ8><5+FmU-ZK)`!SV20fI=d;v174sLkgG$e`?U$Y-xVG6eZ$^T31MC(2gL zG`zliNGTelLB+D66vHcS#>s0wuqzB*hI~MS*eMJnDScs5rO+$3*(Imwt!h7VM*U_8 zeZ=|K-f6TRc1%4%NqC&DfcW-vKo9CsX?-1W)iBD{glp_gk;k*$kHMQ3Dr>n^4)G2n zQflf7%ds;lLtm`5Tw0l)Cxbi_n!ur77GzSeM_+A#&{SL(@~bx-AJg}fE2nJZ(&h{3 zkO1_{u6tb3?yR`AD4jcx@8}h6!qRqp$=&f^O$8$64<)YF=br+m3vKB#^iY>(hr+Ic{LGat_6%P@@SLNgQjH+q$I@b;~KB}uif#>ICq0PpxoXRS6vExfAqSy z8|In+O-Mhkp2pGi^SOf{1F`3!_Efl$R_0HJC%YQ%SW*(hqQBB}1hxN+3PLt>KB6fBegxOC5XI|QJ;2}K3lC1Rn11D?qs6m%BgTk~K)$SP(B?3v{ z=nL=&VC!mgGJ9i}ho%zw4657swC@B`9m^>*-{-ylxW#3z{EVslRsQA-0~F54p3Lf9 zQ0d}T`B5gO`X3NN29m%4X{1Cmuw1gZZ0Elc^xK>=_anRy?v{`_AqA?=iD9Rg1-*<` z&ZA?S4+%6XM!nr7$KR;Uz1!u(s0Tj;p?rK`=|72?F!T>kaJ-D>c0#Z7nc(mh3QAsD zqwTdD8b6(s^9F#VsXpEY3E%pE96SZuTXtby^#-=%2YUdag6|brdfYn@)QnF0;$ubl z^*O_6yN&-)Y{n9Rwcl#TxI0r)S%saOG>18eW)%fclQJT(@b_6qz!gm2P2_RO&oHhs z#iD0HHhJsvQ{)EvR*QkA*E|!idM_KMJ?rc!?3<>xke$Ro9KUZm^82&U2k>bd_?eO zf*os_r6s+1a{Z!G^fp4XUIg<*3qwNn#f(VVUi`o!ApW?CT-l8-UwEno)cvm&RNZYD zBqAbhffx7_N=g#MaYLG;E)>P+Dv2#^u9Xl}hbhMqd`rt9MM=o0M4NpptCF8`(ery6 zi=HO{Q{%WvgmPQEbLqWvVeS@g&`ZlVWx;HY)s?tZ3K9Dk6`;MXoP{LizjY7xKZG0# zuJ_F?rL_?^72X!U6bX5Iya7HPUqIP)-C zD*EjpIc;v4)7bKc$A-`+<|oum?Lt<*${EY+>hpam7V zILZpeRY?z8{jpc8s@lHmqY%9t;-JF1oG3!pZLmt#GsFlDpWCXwVz)YPNjogmhR}45 zC;c(BV{aP(GL6e9IMQioE&!EjVSMy$WK+_!63#lz@6eDbPE-YRg*si1?;lfFEGWoJ zeVi*wN{aC+O@t$f2v&1)%C;+-k*zYkihoyQd>2rmoZ)lYA3QYe|I>yF4Tr>tCB^5hf`yO+RO2eFXg;cQRm&&d#aF@8xSR!H2oQ~Hhx2EzL1bIPou6;w|HzM?JM0%$ z_mb0wGztC`H0bDXrQ(2|dQS&VD~MZ;0wMOg@2W2Zia48qFTY%URr$g>MMqpW%6my# zkGP>CgiZ}F^&I9Y%Y8LBC!6cAZZ#<&8R@{ar;Pd!hX^UV$miVs6VFDeZ~4@9bW(QQ zDRmf;hIreXWvZ`1u?x@232u8`BZy?l(s_m)WiNnQ2mbKkk*#oQwAV*atqYVfvwQVlDNd`<^XNwSQ8@5&@L2G|Zhs@GMwo>s zGe6~byr2X)8TuuO#j2HqpqG!IF&xoy%M;A)nx$gs1#^G>}pu zp=;w%XDPU}Q%D!p1bc)avTd)y+aO$S}EA0wNz1?UF41}K>O^{0jV=z$Y3^~smInATqT3t#)x;NVoaVeORV!*TyB_o>7U?+uo)Z6N z_hlUIYKlxWIyoJ|4=KuJca{zOQY^G<+3qii4)#$-Q6~A>-7ry`*MZ=T#=`ZW*sHL~ z2l$a|J)Q**+SW>huf84bMOc+l(-s$Ze4&Y?p`TzVdy7g9JizX`c`pQ29ZeT^xPGC! zw;LO)AO5p}Z}N1IeD!z6M8ZM?k=3*<=_c%3p0F#1CNAEY7s>oQSE{K&c+GduV;Xkh z-~-g^^&fLvQsUn4l!`SIW*(93Ts)C`Z<1R3M-;uk!{;-T2S!k_Pb&ZgDU$5jv0-7% zMgbu0fCGo^fy6Xbs70KNG9$?#eXI*DFY@5Q}+3#8=3fhcCndPY)=~d_22 z(}4l8|AJw3lZ*yaxyFwdUw6Fzx}24kjUL@>?aT#b?7I0Ky}UMLgtk=_WW&$iMo;8Kg`w?I+4uz5IF&| z4e#=ym4;4S&0T)tiv`@sVC&#ewuDSivGn}{f$u&Iwr*?#czR)xHw%l)aZM|nDOs73 zja|rL^SEUNfkdOMI-1XPAR*IN&Mjd#1P{dONy60p%+RO=E_ufD{`Jv|o$Jd$oS9yK zDBt07AhX(NW*PEE6>zvu8U1jVHLp7(=Eqmet90ieg5KvHY)s9@IUzwAPe8cEizC~l zf=rMK?-JJ1x^9(dK_Cv@&8zEys%V`F+ra%T-qlF4_tgZ^Wo#=f{)>=n?jCckqxjcA zfq6}*y~CHZWoSx!9}cxHhz>(loe!Z)@6U+?a^>d*(*iVA z3ngX#Qk$UWI*jDsIA+FWP+HX?@({yBbQ|5(Sl&fT14TXv8Th%>^$ZHeF_Ip}o_@a> zorIZ_!b7#&y%J7i-i-u^b>v0)Scltw8z}PAkQg0vEKn$^R?17&KJ8(cMo(sdN>1aR zyWi?ow{_&vV5J4AV?A77EQYZvxy6ln9*TZrx*VeUsSEELmQrd!N_-u*g&x~H2=%?0PST~U=q6VLJZ{l{quQ>^V(#gp2Qg){kOJdGN{EKnp9*v?DNVmHU#jH z@WBM78a1QmNK4Am#W{xPQvUQobCQgv8z+;Dzl{5vgRC)R`W3A{GY&T6?++pV)2OMY zm_NLHX@vRmPHvP{{jo!Sbp83z2>0$V-tKMsUq?~q?+t`P>r{^ITTpbXf3|7KR5$Oq zWx~?MPx-LF9zr=R(Lp8oZS6sqq!Ct; zGrMok)EmsFGMg!6Dlil)3?J(5_@t)bdMe!MKtD)dPvf?LYg#wOnp8IsEu+B;Kyjtr z>w~$v+F=-dDfGqO${KIh)7|CTfY9Kz5(8C}7?$8^UlNLy`jCQ!28qQ}TV7RFPD+ie zk&`PmYdfT1%Op=E5F?T%6FiK^%!N-FSjch_fkYv}f>~c`%gcy}dqK6r!z4FuOKjYu zL{3setiS$ihUbPzIJ+zmvXODi?|)4hM+l#qqMx8imM`h5kMsiPdF)s_QtnW7T-c1I z$3o6f5BqadYD}d&ao=$G_`I?2=r<+L32)=XY0CHS$r)V+%~@p<&WL8UO;9H?Bsmw9 z5NwQ+iUzFzh&o;9-cjAaC7@~t+Y8GH6LC7+i_4|JDrlMW`GCu<3#UaKT=PGQ5Yjk# zUI&z_rb-g}QfO=R#(Z@qa(;Gv#6f0$wM3Q`cKctKsJKp0>o!y?{fNbbY)I`p{#?O_ z!Dv;%n97WGttKG6BD)y~xn7@vyPP}`^{qGY3G-y#EI2upsyRlb%p7%$x_waIvF=`V zQWB9`-2h9AvA5ZrK$1;y$4*p7zij+zA^H1M!`#HTFhQg4w3u&D6Ca@%d7UW<*ZOo9 z8$GdH`YJHPI?Ry+wlz$n<G zaQLVVKfp;d@pYwLd(<}_1SZ_E+r#4~Vm-8#=6r|=M1ExwlLR_9RRu@Y`&?Og9<4*x z)f?P-xII0bcUdhXKa}lO%|W<{nodPffc8AoQnv~R=^`!lgxgli|5!~C`>AxuePo|6 zvfUU5H5qbOxs!w`JzRgPlbO~9Jh~hYKcKZ93FK~YV5g+`KV8wE zbS@qXqGE1Bn=nC6`^}vQA4X%8*1v@iAV?(b$`*0qC3f-vZl(D}%c|(|g=Opayc~G< zgB$uUPQ|a|EK$#lQUSV-|5lJYU7JgdF;Yj#h0(&ME~R(qw|agnPXY1$MqS_U zozj>!3|zBo?K41|2~QJqd*%VZTjuWbQMP11dKD8oF`{y<@lyFsD=5B<<_TAaU07Jc z?#rw%6BL}UQ~AnKv@fgpwfeJq#LKn*-;>TMtI@s~3|u*JgW2ebH3{p3hlrCm>jaft zc9rMvdB4T^oN%ni;tj9S)eF|`$|rUk4}7y#6)%ac`zW$kYR98Kb`^d#0L)wo!b?3= z5C`3bjJIGFC)3)p!YPRezeoIdm=QSS5RzM|u#~c-&B-^P5IRVt-&EKgp;OMGf%uKZ z;4UoNF%lA_lhN(ljAkdW#I9bDkIFdsi(t+x0jAcHixmUDl}D8*yXHrod81zy=^?wI zowSlA{CwQ{Zdpw9s7ix|x!!8{#7Z$Qhw{mFv<|zY*PtS-kOljIgzpb`wiqw7oSb4W zvvl0Dl62b2pA8GW)&VMrP+Tp;yRJx?{g}pu6LAv*<0m^_Cs*okvVf)t*X1yC=uDnS z0{nu|*nf@*bv)Mhv4KVpsL9Fy5+~Ac%E*?B+1nv*7kS%A`GYNr83b(#O+yz>WXM`1 zYNFQ~=&08gOdP;e)$5JJ%NfyKYG#m^Fy{M5=UwPQP&0Zy7MJij*7f+VSEQ*Iy{8{_ z)y>akR!(rOt#%S-ksEARUwv?_K~ao{F50e+%nBycbEDiPOXlCs%BV_kLA^F&9w)Zu z7O6SC4M)STFMla(V4I{Z6ES93B-XRlH-!mp2Hp!cQX|D1fWo zZ`D?C4~Uq~P@7jm{DQk};A{(1Aw6SuGBWDr;by}qPV^|qR(#9et3meS=Nxmr+>fc( zd(xi=iEeMj_JiYrc_kjP@{~M ztfHqs+(0w*@5oii9d9o=JCtaTnMcKG->8}1nVc$uhxKLuEynr+-9?orFQvg@wpCnz z>J=IOx$fwNBE%;0dT73NtR{jzqvDRlze+>3)XWU{tVCQwCQ$#GWA&y*JK;_}3_oB2 z^fv@ZelhQQRQ>2%lsIRhXW5ZF0uC~e{EM+QWcHG}8WkgX8-HDOpK!-}CdQCO9est` z#Ze~Ax40(kfs@MHqrkkz0>)lpqXpl%&V^Qj0#HmMqCgtcAXv{f0;ZE4_l#3F9~xug zsN#fHUr0jmP?G?GDjM`ObgZ$_}{M6CjRSV6B_7%U)ofm$5tO zihsNHC59)XyXGj*D_tN(peh_h;)y+-7#;HDXT=TFy(u-4i&cu18V#DOVK{v&P#ICRHWW5y%m4U3(Fd1U(M?~kuuaR+S zL#)bx6J)aq5K67S7QG?tP1owE19%ubQH0V5t4^|JeUkDrqX4vJN#X@w;FsC)Mj|Y` zM{0xa628w0a3&`0GTaf=vd@0#5ti~BJ*v|MnaPH6kuFaTx^2F(+2<`Qo87-Ru|Odc zpj);?zhk#91b=uG=oLTSXNc+<-PH%@jr2W5J7>nb?ZD>QRJrd}kjiz{Jl(k1ngseR z?$Nk-?4$=^r4fERrl2R`9Bz-RCj=lg)+m7Eo4MAzJ$m4Apo73u?$G8;nx{aMYl95< zelP{9-zA@F@dl7QF_M#BCysboExDa48oTP3ncmLK)YI?ULGx~GZ|6TG2C^x_{))gi zm{3Uy4HW_j6YD@yC5`fO#9U0H&K{Iysxol8s=$eNh(=#l*GSq2<%dX$(&~&JfUO-J zNWBPPxgLOn#J@Oe*HSTy3W?6*PiI7JiVaGX%f4v z7MiHZb!_LH+SSA<-Bw#_OHeT4;SO7E0H)s4LVrJqrpi4C-^TS%h{0o}Lj!9FaJHXw zyK>O7)1fNz;LG2hgt8s>(;r-)rIVVDg`C|oHkg0 z3kX*#MU;zCvIMXQlJ8Nv65P0MRT@Q~hCSZ$!e$FMIFWgXOyL)%KQK6kfp)k#8~mYy z{!FjAauw?{n+&_&xF%^D#n-D4<*6pE3xfMd;|TRmEQsNwK_0O77iw;(}G((Pk19^>SSZ6fQAX%!O&S5&`D zVSR8YTjP4VwmtsVJuKQ^+0cT$QqP6h|ju5du6L-D8~Y%pY|&!8&mKlPnAA zK1LqtI3HEj9Bq+D&W^`x{rA+J=CF%?e-xmu!{POrBazeMRH-v$=F{9p65!&_-(a&*MZ*GpL1GX>GN^g^3%e*P_d%GL>=iYg|s6(MLh{m zqKK_Mqm+&bBxA1sTwQCwGl86?BTH@^=JojK*I6JJViAay5447)FJD> zl}!n-4y~ik$qIY9v{N_w&mUrL@6)-BjRYYAuvdaXRsuwxYq*vwz3waUJO(7~ZwtM6 z6e0kABazmpBAH~4Ruu~mOS1@g`LR-58oXls!S1GHKfAOQn~}lI{h(Y_TusO@d~i%q zC}XZlIv?)<(4Gx3Y;BqqbCT3GUheby^EBf~Ls^w5H9a%7HjP#*BdZixixnth z+?JXVE1?NdcOp^UNsZ0xQS!A2O+jIG?2kj4|9WIUR-K>L`0U@(R!WUfF~YgxCb-3}twZq@djSVrBAg43bHh1oJ^#~1OX z*yPW=GyEs&dseQ{1|V~DKPCn8{)6)c1KYOd_O)tmejC-!&LO26X92K3`W=4 zMuejyrmrt^o36>yX-4&()6!4)q2Hp##*1xDQwprW>JHT6U1WXnlxj=QOQ?>s$*}rb z>Ig{wcLVk7#@Rs$+HHbxZoNv*Mv>Cbzhh3sLb|4(j#P5Eqt_b+Q1w{@mz@Z~&MC=3 z>bUCO;VKr8cyvb&Nv5!`t=cPi8Hb8#C_T?eU8yaP0z`{jOpL!9Q{u#~?#`O<#tkjm zm)U%eGlU1i47}B?d?{l*QJ){O!!XgR3gZ?JY=GdwN4D_uBTP?E&&kONnX?_@Oh)QS zc1-=Uy9#FZ{KaO5j<)JYfZxF>44uE^uI&WXm;>0e9>rUySi6gj4|5)pw( zqxerg7iXV}k7L#^qbqm@nDsf}P;_4(w)$g3aTmX?v9M-bAvhc1S7)$6iv*XKA3^8F&{FP+@% z-;JbGJT;K2I>`%@rpwu1&%58eJI8Zq9D*{v1WZc!|7wQ7-D7fUU%6OG1IfOoPq1&S zgr2Dt_CKGFxB1;sy}gtts|Q@Sw)uq$Cd&}`JkFYHtt^$Wzr&3jBG}|>%9p5m$!OEi zM&IY)SC6i?iiUX4MIy}vg&?&uHZ%|qyprY1dMcp0VKwepWe-9&f-`B)JPA*1`|w0R z`FwWs)9D$f2jS(|O3SMV#6Fk#u1DbIhA1WE>ajt0Q_L9^MiPpUkUA>7n9iG$z84Gu zZx>8YH`~H-0~HF_W)^-EAp`r$+}ewj6g1GSb=$HJiCkmtuGp)Kzv{-jqVPl&2ipxZ z$C!VLGrc$M6P(Mcs0!9qWP?=gkZx`#QVc{Q?4f#kZnVHfm4q^}bS}GZz5yTc+e77x ziuQvrEeyJ@)a5yAx@cQ|Hx5r9xAmaEsX}dm=ctIMveM6YI@Eh-RY>&o;HY@EH)J(p z(PXY?XqS5o=Ix%Ss77IP50pwoA(ykw$Lq}cK#~A1!W58v3vsi0c}W#nxEW|0aQ##C_#bWTnYL4nB3K=OwshU7y@N&j-CD#YzWlIa^U% z{q{zxU9pA1mo-mQ{$zV&lPjsNBJd|3Xd(|!GuKU~Jt0GHiW!;Mlx)w8J+Jr9)_`3jDq-SaMrENpT~%0U3fJ03QsL;nq~$tt89B!_tzHFLp8vPhiR> z%zH~9qGkmXked}V$R-*q_BnzD)6(=si1vExv{%B<7%h!0Af`TO_w?y$(J`;%dwlaE zbD1WQ0*gZ!U0X!fiw)GPTVCL&@7n=iaOh*QiHvJD>Ph>s{PeUG?0W&OsYvABKev~_ zv3@lAV74w-EWGXZ@Z3}aBl-H_DWa1$PabH{o#C{nL27o`b-T6R$@Xil;i${2J!z;t zY8^H1UiaWiiVq!vg4U(*Xj`2xex$Y|z2jNgb|rVSr}oZaro+=zYj4P<*ax%yDDEzM z((bc!Z^T=NQ(2x5f$7IZ`qS}{Oc|Z2oSWPCD;vB;G|=(06|vBdq)-Ju+<47yv`FE7ZBuHNXxuutm%XJX;*m+LJ0@@Wf!=*kik6~o=%%=K= zRl6Arzw>(p3!Cz9_fp2{^WZ%{WC)1K#c}M?CS6(b8cDgu%O&x)0M@K5&=io{gyNYJ zE{SyMc=l`UqdvzFhQiQ-F)PK#JY*F`zo`NnVeiT9tD`PKT2ii{utd-fdC*v#Pupsxpunf5fgb?v`*@ zG;Afl4L}3SS~XZri>5sg6@`RSKF3w8jCzyi!;Gx5w9LCYQK1hM@I8kf(Jgz=ioj%p z47ZBv-pDW!eZ8Hek^|8^5>rZ!W^y`RJ*5y2hVlF0SPhAs0*E{+7qN<}F!q$MzO=0i zmBdqko`FHm%?6gx0FM2@B+P#+x}=$?&F2G`aqaJVdeS;xUD0oTXh+BVoMoQfhkVAc zUPJ!$jL2g0*`?Xg#ai0CmSw8Pj6$dz?G(KXH^3HQ*hj-YsyN!fl^+3MM z7gxYn2LJekq6V9Vtq-wQ-mw$2l$QdPaTApO-6~it`$-y z1V%&k5_ZOP9X4O*R)oTce6e-ysB%Xfj|qs&eGJtNwBih7!IKnNf>khgmDZ0Y->( z3BbRDbg+#@!sqJQd1@y(|M zfZ%dF1OXYDQ4v4zT#}0Fsk@QFD+Csgjqdziuy9EH7FW>v6db|9XE6?~SSjKMTZ>{* z6>BDUOUJ-G##HqZDG5K!8v~o6n1s+R^Np^|h>b=;E$+KML1$@geH>1#I;y4yxRD*i z+Qs0u=Q&U8`H%THOe3|@fVHlHPW@#^tV_JQ(*j9V86qbW6$#I16eS5JKX^ZYaSa{* z)X)?dV>=T`Yx`2PY~pGy@_sRMIz0w&b=s;yEcO#|DIV`oH_nAa<$n~Z%RDUx+R4{s z5H~B|QW%!Ir`Ax!aY85`zw;3bcH+!{t$M_a!+b~FsxDjdin-557e{h0FP*2M#<0Ch zZ{RSK74x>OgHuO2haD{i5TOV#WC9bV$m)DR9tsMls?P=7khu9*8`Iq)^R8A&vppx9 zX+Ail!0j6?3gFV|Od#L8bJs&~*M)zm@@7+}(4-zOJ|6zhIc78m_BCMn^Ld=uh@LF{ z(aC9pwC76wC%zz2%@$hY(><>+_VcP?{$29bA^Qk!1?kcZYiq1bdXGmlDTtShxF-?? zVhLWP-&Q9&Ts(DWalaoQ@-*hnWYHK>%Nn&B87Kn-goUYm6&T7+l-v285b09Z7yIZ& zJZ69NT)+r4M4;qrg7^u&z}}o)R6&mxc>ogv{bllJ(HYo6YAlK-F|sF)uk873n8QHi#?%V zG%`M|IO~jZL?)R0@&d=N45!#Pt+4|Ohx69a;6t~YeMw8tgn9QAF=auhR*5ZDgXA38|ch<9i~rCH!t|JjFDjjof{DIY)vKy#;5HBK1-Bdm$Vu$mB-Yy86))D zz z|IglE0N9b8Xa2C9IPlMgO|deS)7eSY^nz&tm%qp5pL z{8@8Exq@;7SJLes*2Q?Z^6#J>7sKt~2#|(4?A-D?s$2Wf*~Qg=r9OwxVp!^8^wb5( zpL~bawn-feJW5*oqzyE*4{P4rI{MHfVKDEb6K8al+pZq@KH7HxskOa2_i<1Pwxx3z zy(3aql77zfZE5XBO+%mLw;PRpa!o3!roK%8`W(jPK3DA>9+{9hr%>12fx4!4Bpuj_ zc1c%zt9-{u9ywohL<&&8lI1v!>V`IS4xK_pK@N5aTsCx!qP@Kv9n!WKZ~q8a^6f_V z&}j^gFmIC9fqum7jh3>UlD09e?+3pUJ6c;%+d8OFHzZ}vvKW*G)YU(XxR@9WNagCn zN7GW~lD6i~37xaeHp?>am+&fU+tDv|J<64TC*+#EBR%qWqej|nb8in~;?vMGG_4CK z4a+kfU4y7^A5_cigw*A*WN>iQT7asfwF8Y^qms^PRMvM%`lrxP---sQvj+J-<>FmU zQuo~>=cN1x(I{<*@8P&K?ua9ax)>tgx4?JWXifm1dX4y!f4WGpkF8Vl-bT}9YhVb_4Erq8VAdx;xRxbgsX zZg=|hX`MgABj>sq@w2&ixdAF+igOzY51!3RvAp2pBXp8(7d z&Q49}+-+L1hX?yHFf1Kt>E!kejNs(xAiBEyYEtJ0;`}Kbi;YKn*8onR8dFOL-PFlp zj0{iWocNuVhdHOUySWPSC#r29E61%wK;l@0!Tuk@tAi zx{&i`fy6q2Ig@V_uPHO8n|_&Yjf-g)KRjpryw7`MBBraWQy^OaR^zASn${7+w68He znl6ULJcu7HH@T;2l+Q+7^k+z5wWV)LKNGG^zE$F%&(q(yL{it(i8lEzW_-+_rq4cV zoczptqQ1c~{UL(SYaS#lmNVNq2aa$XC6){CG0uUZ3AFW1YP?#Way0p%A59O#lCW83 z+Rp8`HSKaF^m03+c7|ie$>&(Ej8pd}wy*ISKFhXj%FwiFmY0^FeCBnQspgs2M7%dV zd{VdBVclqYgNX4h2vC0<5gq_)-y;WRAEX~050I!rduPYAI;ul;-HNKU7O zNpzKx+h1k}U%9m@)+oVV;OHCj}sZCG82?!xpa&YqpS$94Yflm)db%&j;;mCo|0 z^ri>;dUaJ>=?g35@g6~uCC!$^BbyY@PfwpgX<06MC0_piz-QP2XBya#EQ-D)%J; zX}P7yX`Mp+(RgIFpU3g|!$@nY!>ByVj=1#Wt%bO3F)UqmmtjjByl>KH{16PxH3EY< zm;ZEyRh9?+@L9U%{wdVA522>EUVpez|MEZH=QFGi?khSd^iZ7>j}l2Q{m~Eo5-?6n zfYFBzV*8$*XzD-Xi;MRM1}88)GNozAk9iBVofE#a@;=kUx*?D;Zv+>G7rD+b=#S~u zqg+#eQa1!W-k+3fjE~_n&Ge^rBp(;?a9&oO3kXZY(u8}`MI&a z3G3fEtfM33mfquamOI-s{h4ww@i7|u=@~kW;W7C>Z>mDanDsotlKEvG*~Ya@7^fW1 z4|gCTtpJ_U{#agGk8))CSr43Ns_<#Y!T9KhVQSp+KdyM(D?mIbk?pnU|J}kzW1gme8TjVgTm=N=A4S=?9cnbX)ynC zjaEo!+UCOv;$IC>ZeZ#Zd4!hK+k)hMJ~HWb;RvQ*^BQpdV#p6pDMww+((SQVAfP%j z$OV%4tA*eGu5zwcR2xvQB@mi>=GO+du;(jn1VJjm#An3DOK8@#@|yUO7soZ+6{h6` z)W3U}fO_RDl60lG)`3aKiG0} zT;SxUO$GS1HeX>RRazljzwm12T}ykQp@ud5H4J}fa8g2IB+Bxuf6ap_8v$;Qc?^KC+^3# z!>Oof97I}jA|j8K;6!#2BBBnXxUNqDrlKepk(t#4S*y*L_s6p2}7$j;9}c4Z6FQd5wgTZ|`Pej6Ji3encug7oY> z>^Yc<@~Rdj#>FElA{pHR7HtC6uf4V&vAbWzx-Ey0U)zDovNG&HbVz{n6oz{`@#2eX zktFG!9YXv8e^Ejm@a4sK)X1JVZt%prE<~g%#cC?QcU<&lo0!5{q-P5uaRyn#yt;K2xFm3Jz%#qZyOU2m_$`b|5q zTb`?L?m~i;V@!HEMkOt^#ra4`$wEs_A~~lL#ksjiI$kXKaholbb`zJ9mc}k}aWagG4PwFO8(pVz*5AIJuT2>Y!_Z`GLI}f70 zvk}=DS%^9kg^avvl;xD8v3m$*9ivD~Jc^^oPoSv03h{g1#G8BK(I@58-O+;G(&j1} zdXb-zhV;A|lol5wDU)Sdi35k?a4fqF@o^~NH45Fb!83q?LLD1reXad!|k1&j}`=|$D8rqpG7O6 zT2{o_eND^K$^NsWFC9X2_2sW6FPJ`f&^f2#&#{(_WjUQ6Bd|}e0BROQbt({2$pB2d zOE~kABX7EvjlC<1DMNz!tY?;4{%|_GzMQhs7kvc+s9DsT*NRaPFa9JPrEQFrYs5!i~rSJ!4^ za*n@5T=kZ#{C?o(DA#(Pk7&7M`{C-iACvdvW4B8iyfaqkhO%v4@AAxY>RO5WqmqU_ z0&jD<>aH>T#r=u6M*dLj%xsZA2dGz$TXBmd2~@P!lw$XWrw}Q?P*_!i zgNI@`q4rBcr(b#n$8P7fRQ~ctQ7qI=c$FXU*0Mo96_+S6}dAz>m zfDYNtijTmJU;GB1df_D;JMcEv9>_sl^%Q~}ZW zeer6++Mke$;`&|`WTfCL|Ng)6!mGOlICmqpEDv>^Cow62)ZI{ynAh*cfv990`sL3B zjPh`-G84P^#$)UKkKiZ&`3?N&{tbA0V}$&jOGH#$4o*#-L;R6_*ppR+dvEzZe)NO8 z@y#M~y&wA$O0eVAH&D`j23^gq*e8IN5w{Ho z;!a@Q?o>Q<_wBghdp98|zYP^y+OyLTf#Ay+MF zN7vtuho0Yz;>6AP_Se6TTOVGJy@w+3+KbzC3lsWjt*ge)lpO5XbqFu-DUddnjo0?3 zVeOV|G*%}FTsNVsXB4k)j8-dU{Qj-@!8g8&O?!_bAvq4QG5b-}IDxWqE+~|V%Jf5c zbHiJRN0B_2r`cD$L@$k987P-mbFjeC*S)9);zxr1E;O~ z2aabTde3Hj|Jyg<-X~tc9WTV7uCWV8q-;)$<* z%w3o|IfOfIc@R4yb8+FOw;Q|P-h++%k7MZMd7Q{A##`HuU`KQjGE$Q8 z%!VU)@`Y%OO`kz|RxWlYTRe6gh!;ptLTOY}<<+;b>!l6It7*deU9tGj8@_*^b=be|4FUUp?0MxLeCazs$E&Yz5*Xcq!+Rp}_WC!FR@{y0>C=c#PQjidvDkgI zT!8)n;wnaTk){1x)*~zF2=*kE;Xq6pUVixJ`0BTQgk1^cYK3)sXXm3u0csF9U2@AQ z`{#xWg8An)>1&uix~6Tea9yO#5hkGO8Z^QP3yCLCzM;$Z$Wg;OI^5UA|8h#LwstxC zcqo{B@O&u&l|-+ShQh{YOUZdLm18P}G8wijNX_iVT4{#;`U=f%fNo(fR9Yz#zax&Y zBx%0d0rlewsPbifnE+~u0K@(J%J7v}Pv9RPOTphgnvB1HH06pChx7i;!^v8&*GpX< z7wDPG5nKpytHr-tJtZrFDv7Nbv zwH*4Wyr-XiC|RJ+mtW1&p}f6gQ!5rweM5Y6+OSXH)C1JZ*R3=_ z9hZ*x=s+KiMeIauLMHac#NcRBuG~A1n#w91E$u<2Kx_K(O6=cr1aIz=&h)`poH!AS z$fPs@i&Sh9$SbUBlDteIE#ny8+_@K9ckDx5X&&OG9cMP3Mtwmcq9Zdg5<0k98ar2%XAO2^Qtg$@+|VQvr*VSfmpeJ^jI50um`)MlCW)eEUKD|QPC~w-7SE%{gA*+46<@6P|(_rs@4WQmQ~yo(`!kiG2&Zb{|2~G4Amih5XuH z6sD(O^TA9s2yC{>chLEBXYl5_O*mdqkA&!C?A#q8=hcWh7%33ERp7G^V`GzuI1qto zfwx1Ow`0fteTX}rCwXp>cuP>$bQ1ktz1X*ZH}>q?g%kn*BN36hf9nwe?UJTmoVoxs z*OVYJazB#u8c^DP4oOF&u`A*zGKw29PHS#W1@<32jFjYb?2nc5%a_mF4k0PC44Fp{ zN&Up(z~LlhW)~o*aSGA#$8jVs16y_;L}U99CQpuI5F0Alv- zK)ie}N))JW9YR`B8={ZINL;aqNGd>7Qmx+WXlaoAMrlKq^(-YIt{UV-p0l~v51p8*|%#S(h4eY{Md0s@7;<$FeS4#jkuR`$So}(uw7l<*PD4p$Gh&V$#@>Bdh(3G>k@0o9@K3xE|SFOyZa~5Ep5NLt_9sAXLSJ=UA=ewydF8Ua#(1#siha~ z{ge6}S03#hmUpJD6OA1s+M(_nly`-zOn`(IGVU}uLQ_Aha?=XY|A`y?p@zF(|rMb6Bp!rhvA=-^l_zG z=)zpJ0x(>4n5((!D!=j^?{n4E{{CT9Huj=#XaZU3>DUpGiIY>O(b&|6_TDjh=7O~n z?BoTswRh@@vBPrCxSLzrb(LbKv%Py%!jy86yp2dkh9r{gtSqeGl_Kfm7EVBC_aHiZ zt>@*d1;YZZHMOl;zMMO#&rANeitLy`bVp}DYTHlB`59f!R*F_v(4~4twvDVJIEELrEPFTKu4Pu-oW69 zt|TqRZmpUt@sCI*+5~u6=4^vp!FfdDB4W5$xcj{`tEZ2T%{2hJrj&=8AJZZP4o zuPsMc|Ma8ACA&`^%wL$&X@z7DH$ZjD38;&R&s7XBSeU%7j1a=l0_6v%rk;Xn7WZ5N zK-F>IHnfGbF7p0DX`h8G&+CFj+Avc68UcZo;JVsrL1p813v01rekCnxP4(qDc)pj? z{H_&l;&sMtK5O{~Hj4S_YPX;g5;$~|Amx^iD@`0u%b~P{o=}dftbX^+Jbduh`48=7 zJ7Ia!TKht5tqw1CbLU+Mc|@Rfi$K%AJd>tjTpMP4Ta2!|%YxJmNpo+t^FmB5-g8qV zzVLFEf++!eMFXlm#Fv1&`$Ve;sF$HziGWHGE-B7QWq7=%tBs0$9c4Lkff&M$-V=X( zhEQM@;vtyO53d_PJUUlX<rVVO8g-g)0#*R;v!QF*vY!s`*j;WbUC z`0ABi9mN-524ozhhg$Zfv%C$Y7M7<4a3A| zpZnE~gzYOMxo5=q7%xRX%s;Q2veER&XI}R`YsY8O>x@&c%V%?q&*;&9PSY<(hR5rC zUc=x$Th>ayrv&aMHGd`zzVfugrOflD-U9EN^fGT62A{EyzOt9+HSc_0uJf9CAM`Wp zPmT=R=f~lf`DOejJf>62UheanDSMMvUNi5LNfQ@!GI1_ejCq3j~$D|$7(&-l8Qi;Qu|5kvSa2d+ciuz}367Z>e;t*ffjREN$ zTn>QhH^et{X#lmS=`^~UPwGN6o?AY*5&@N+V(Ck3hg$l|)(WO_&TG0Fsi>r`TiU+# zplyF_UGz8CDxxb@gaHE>ZujRcJG`X=i%=reF#YB;&o4&2mr9yH$+O!%PocFWfNF-} zhN(IG%?!^6r*>c4&7ZTME`89Y>W|MD%ltXSPh7NB`sP8?7Ot3PHwF07vNL63my54F z$wW^7^O5xX{2{X+Zg5}Hj3M%99R~t7#`UWXj)&Ybg*&o2LJ=6W+$lF&khhrb|Whz8;R+~D5+|~;n-y46t(Eet~qtR zD68#2a$+jVs+({;JrnVnWf+k#XeDwB+mk0xmsX1gROt)P2{jJQ$q2kpzox$%%)b+= zmdJ|elZ37PaVLv7C4F?VN+`?joC?V2ERhRTPCqO|IU4?uJ{$LAB_Ay)9rFCFH1KmU zg)HZb2&g_%=Y-)vXbPw(wWgYkvEUY3VHD#gjNVHNEel>`3g%+M>BSK9%DhP)^=05< zP+HcuoaIbV=L;d_tiP5lq>SY`LW^5x{b~SIf&>>2B2>BM;Yt(d*Z$iJ84Bj1+#AV_ z_^`B(`B+b{lkbO*2t>XbS7(O;9L#RPCvQE3RbSYIcYS3me)H=)@SES*f&VW+yvBR) zzHu8?eSQ<(|ARevDYC-1{Y#X<*B{?w!L?i5OC`1g!Y~)i3gr&ucHj?x8iD`&T)Kj1 z&&X8xfGS~U*AJmU-m8*J1*jq)pbD67KHeNQpq@K>0a^RU@W4MdV#ghA*#2NCvW^6- zf_&jD;tuY?kH7aLtlfSXrL_$h*RZEhS5=R`@zaAem+~$71IVpu<^#WIUKo7wz)|DyQ+-|7pbQKd8pxr|Pin z-f|q<(Q4nFai=H7@YZvC(b!s!{qJl*^s#K@=BHr$#+`U(TMQ2G*@bO;lQHQ5>YQ$+ z0V;>M4h{|C=#eAXcPJWLH|@iTqDo|xccY;ny5-B!^kU@Ik03H369=P?p&&OOn>HUp zMqU{zs>{)HQqtgAQ9XHDaau5-{v=ZRQtZRp;&}+o$py|~y`rEa1((xu>L#X}>1UcG zeG4`a8QFbtcQOC|<>n{bkHtok1|FFn{VZ~eFPI3Pf$;~%7dD(y7XeV25NCqbvZoe3 z2QWF|)CW!iSbXMUG?*(v0p;wIu~@TQO_@8&U+1Jsg9-Zby2$A8jgmw0Q(+HA6@f7|oWBdbz-9slaGf?GQ5R1WW}~bL$2XUEGJ*@_z9%h~%l{l;vQIIK-N~3fGlAFkr|5o=yY?Ny=o~92dv4_c zD(CkNk4~bpu16Q%85$fyO>;j6xx%X49~e1@L9W<3c|rVNKvP32ItHiE*EfLL7V$S` zt*+}9wkJ=XF0B>>sK=V|-kYKn*p?hl=|30{`N669Sqvy%3c2?CnQ#B{3M$8Qx*d)^ zs#5QT1(B8%JRM=pipQz2!@U@Q>TCck00X5ySTb@ztDLXC$1W%jP{Y!!2I3Hh6ws17 zV@-9GHLcasXu^Imo%Jd50QKqsR9YYZ~D zmG+a{IE11`0aAg}EP>L@#$lwE^&_T2t_!Hf%Xv=g2y$u$1SB*C)ORoh1s{H+U@6V#us^|wZE}C=n zqx5BG;55Ws%0M`E^Rto_3{G9jET~L5iIflSPqwB(@>;k?s{kf&jmKzS<$SR$s3vjF z;k`aka=?;LT*Sw-poT5JkSt$kBQfG|U$Y<-#UBYcHIR(i)o+P7MEFl{ zKZ0JatRn8wLzVdOZPD|x@Xl9U9O?&eX;dJYX zE`iTpfyz$olFy0a_xN%R@m=5|NnI^t=xH6dKe@RJI6W<7a()_pJ)Ht9XE8Y{d5GGF zsLV3-^>w3nR4B%CbGnrXsGdA|x-zwJK#h_}ULRYULDEY&w0_qyuf;!(!U5#R6Wdm?F6qU6XEX&w=UCZ4rdp1fw zJLe0R=bX;?S(E`xxQ6{_?MIVo?iX&kUyk-L z*?CQyn~x-1U;2##$|`IroKg}74OzUlP|GR92@Kze@a`5ypqzDl=h2G*sEm*z;Mjl~ zxZml_yi{AHc;n|v2h_PFq zGJl_aFbUONV|LEn-Ces%6*%3(a1+}H%a4ly(Q%K{RiE4F3Yv=%0rem5kJT++CZ^8d zORr?kdkdWTh>*To+Akq5l-qc$3IE>@597xh^HI||hJ=bi#8eKWpnVkCwL{2h9Yccv zYIXYv;z|Wd8-`KdJcPKi0pzs~<9Jywc4T+spa5%TO+O;?y0Imz3kQk?R0Tk{W_Dmx zRwts%`mP8-_1wao2dJJrdAe9y6ri%7>XhBjoU@;7`oMPIG&r0(plYFT%D5N~EsmV7 z=%UriInwW5YJOQtI1Ny^R#R%0G-dU$wuuD-!Se@)lRfT#SD8tK0CtAM8a= ztu>$diNjTR|ILxEX<90M;N~dYzc2J^zVQNyT=kc8d#eOWI|e6F*E4|zdyAQ1`=FM! zS^J=J72krUVdRSn1zfq!jBYPeHz>e5pis&ceA5J8mu(ea4^UUU^8nS8Cr=kkOA4qW z?Q6UBOO4ofHB7<2Y!EnAs5Jdo=?lC02}=^64u|E4PqVPAixg5Nu8@7}`N$1UnQzm# zpH*%WFXIjruN#?bKc`&cbW^Z&1jMfj*K%sawD{`jVgsr~Zb1^|$Q+0J-j`$z{iVgv zT$c=*=d3~u2yeN?ieO>M3*rxVcHx@}xD;s%6w_$7m(in$!w+bU=7LjpT4$BLhz-Iu zOe5Bt@o(~NYxx1yTO{dH>-_>-ckL>%bM5Z9O6+`w_fp(u<+km``g~qpKYsVyyL9zk zH^^L!e&g%gu}Z*qb4tC&^;T-5f?=?>yA=B9?J@Yb=QGeh9(n^_o`$Pc|FIFzM_3lk1Gyd2e&vr$m~C+(;_Afm1&acK$6#*wB=?NX&>sK@h5cjG zXqqRWa!zfb15k4WPEXi?nkj&~?E9d4fV$${asVm^Q_zY!>B*BPPY!X#?Q`>7Ccq6) z=j`+Q!Ko-15ILdBW{Sm#edp^QaDb=*R2NWHE2{9poOzi;e)**AGE%Y3d}0|a6Du)% zXBq<2;naM~nWIsi^`yFpfO_riR-(Cm3@xRq`#Gdnj$9DQ0F;qra@h*nO>97fQ3sF) z_2)cigP?-F92ZlP2A$7qUJ~a^BQJ_h-e%*`GB(FBULLvctQRBZI~Z_!fO=&}=TnLZ zC!rh{{NYeww=mqqw!p1xUW>P^nul{*b>C7hYUBo(ixmO&!QDj~S3+4Y{`g+D=j8)b zT4%43cPCCDFqHfLTlx6CpF}9&_l!)Tx!(g+&n>N64nU>5;K`FGPXStpn+2$I^{3e< zrLoBLW!?PS{c~U6mC!U-UpEZ-=AJtH*Maf_)J4Xtd6hi6^uZ03DwvX8+@4tX_`M5uF338l`;<+(trS|Uy6#qUxyq_AZQNaI}rpt%S!4?36E z_p+q=%2|i|+U2WaLtw*p3CB+?A4lDoXFWi@@&WY|QeS_zCIJ}&JHZ?mANtvr!gvT=E|h!nNEP1qvq=16XE9m_Cax+# zt*orX(W6Jv($ZpI@!WE_^h^+NXih-oJzLg1 z)J5`)v;R$*KDffEQ5a>$a-u&Tjre(%*tZv%cpPQz7r&eM+6+|T0o1b%=wCy&q8Ho(ah}TXQ9$CFJfM7@r6)GWjPzMoC&CQ8Ec-y zO&iO|r2#RVmX!#o*6)N@1)xgaI8U-F#0tJsXU^#&N}PA;7Dg~}9_a1%9Ml|v zso(k5&gGkf`lZd-lw7BAy`9>m^Iw;94l0K*6HwRM7UE!zEBSut)@c0jtvodMS%BIg z?TTPp*KHlIL_nRGn84w~hjGg-x8Tu7A4OG_{~o*3Cx=m8R*a^uA^ki#c?uV0bD49e zF*-FFG_I-fQ4F6vX@8o(8yg$P#HndLKQ%Upk!kCEIh_Znp7WgNR|?w6b3)I2`nqAr*S~f$j8jfPrSVPDu&DT*{cpY~m;72R%Dzj9RWL)M zI*WgK=C}^obqgbyxQg$$H{{~Ng$w8&p2Vl`h*-8&eBbl^J;ap1rKyz=Xayy=V@Ls&N^8l(ETQD#@iqX-2lopp@>dYC8_Ou~8vjhX9<7n&bK~Z6` z{!lX3+lJf|xtNsmuG$*ZHg)3cg_Ec*ug0lqD}86Chfr2lj`6Vx^tHF3q^ty|20_A{Ht^q^Co3hPf7{J;8w)3QrX-mP=?KxY4epcU|fYn?# z$I+Mf$rlgL1LWr4$goAYVx0uk(`;RAK((dgne7FVwDMg1EW5PQr4Z(*+(nD=gwcqW zT;@gRxO9Cv7V&}*wA~t#XJFOmHn~FPVnpld->!+lnRDlLtDLW_J+Z9&pc*lbo3~i=UW2)<=`9MV z-+MDx0kw5NpvE5JYXG%c!1PK2)ZE-$6c!euxv2@!(a}20_l!WN)8!rBhqflp-#vvl zwnQN2SS}iyE3j+Bi-=B7#_{w_#2wE>Ok^}(+q@6ahYsLGK|10RlW;7v6vtBIv1v~% z%4=H?yK6mOeeOBQV~xJO=Lbu$;|HiSeQzV}l!5QaVVt)ACoZ(+0 zKvj4$4KdU&Cd(~_H<#;6Emp{4e?_^J3JR!4JwUw*0QDaqPsR9@m1j3cnEK59{4$lG;tifYXJca1ICJFgGDv~4c!lMu1*)1{HyC(tl6Tq%* z+YwdWj{4Ffq*PnO&IdaxuuZO?h}?kE#vY8cr6T%p1X2r6Ab!`Ic>PE^j%Jh7g9d|B!kFCA$puFuX_419N_W8p8O+G6@7o zmNCOL{dhMSa5QpWcb;oG&%^je-g5qk;0$k;E)}3&N|_1^RjJD%#w&$>Swd>y3u226 zZTW}zdVqQ<^)VYzCqry(RwmH*=l3P>3&k~zP~ytJ9Nx->md=X){g+9)ny(vd&R4%B z;{K=C-*O0@gOi%prz5K+&KQMAxAZK9*zW(~u~a>N?!`=;zYrWzcV@O~8E~j`AfP%I zNmA=+$0*9BtO=(j0;ZQ6ppK4?DwL+CrXnLFBP1x*Wgf+APd$ah38@$y=tfal6;h(2 zuyOZx6t*@YKP?GQJ+mJBBV!R)(29}44m1vTAS*Ej8#Zh~eRB)qVh$lHITLlI+1R;b zuaxbegnS+iiTki|<7PxZ@^R z>n7}vOGjHx0~-4~P*$3Sk{ZeLKnr$n-+}a^W__l#qyTR`^B@i%I*R)C8dUd=W2B=N zZ*F)4DLLh6>69{C`yvh=NJLXp73%w1C0|Dn8IvebTY>h84ise-pfa}>x%maydvGs~ z#7bFBEqsu#2dJL&oaa`u3ks+Pq)h)aSijxbN4=Q+Z1(Fp8kAG$xX`P;8_AOLh33ta z0l`gV!|9wX!}PJQPCqOYQ!e&(3j|%H@r4lmFz&$kLWxkXVcYpI;RMs|u~#3UYR2t` ztiy{hRztpxfV#vD_hLz35Ub4m8tGMOL8Y^nP`O$F_3s}`(LFnZxpslSzjz>VLFK^x zLTPP`FSX`}W>)v&_rJ3XzjDJ?SLmFNe(lC>_*DVaw-c>BT)FtrXCF@1cIlRuMN9HR zYv?!E=jir1jECSE%I(T(Q$Xc2p#W8f_$r`U)>8s%QS*?%DRp@OD(9YNW@cjN&K>rZ zQ0@XwO-|yh-)cqYacY|F@B%uUYLOCqP$03>y5)1{F>M3bsZ&$-n=0oF%dt7%^z<~& z2mWqw?lh)P&$RP1)2HOKm8Wy3r!XCa5vQk4ValH0TG!Bks)}-)IqUCv%8{0{r>Ak| z%uHI&pFM*~N%O4qTsXfZi$g63peEE$U~R?#)@BW^Ol1yYL(UMo#)6Oh@SNv7w=$e7 zpmKzxqrV<w?`S_{B{DL=Cycuu)JvxL&PrE<0KFG74vCTAn}HB_q{mm5$kTL<*{ zmFTXPA->FG=TPuY?Wll>u^76AF_*Ys=oeqk!Z=r|5%*?7jRNYgd}XUzLEYeUF8%t~ zwqw=jH{s_Sb9`WO_wEvWPyo^`4ND=0O^a#o=#&oK{lLvpxN~QT#vRP<%WjwaN9!uc z1XC`O6ckV$b5KiKX8@IOn%_8x+=fA9*A1-rIjD?%cU^AuZ;Y z!Yv1&es*0GR{gXXt8Oa`qEcIOeybdBvCqSjd3HX}1nB4T>`f)uQ#xv=*mIuq+)8z` z0F`}C?JwK?P77Fq%K$3j#_j&)tiZD1ITa?PUaMsz{_aVzwwk1smwU+i!VEly662er zE?mTCe9xQo2a_)^(N__mN~X0^bYUjDLRrwZw9w~z2|~#pTdf-nvm*E_g%q|k%xk3s zulEA3uXqImRLO@2s8>Fq(&}gyGIDd1r_bU)Ud&Kyt6LaziQByp(jIlJc>C0WGW-T1 za{+s!zDvM%)t9#DqDvDtH0{W2)p}a4y-#U5{nzKx6)x!>K2VPL-V}kSBCGXtFh}4c zpnu%}YXAKAL3IM^2?5kB>PiMw&&|s%2cZ7lhGwk#MLAa8U+Jzj6tPo(iC53Yccp^m%~lxn%*=e|RGV zpbAL-&a(}8FntgY9O}T&cXr{$^gdkoOtrvfIezDf%t)Qj~3xi$Db@%yfas_hn(wz@be=dDZUtfA2{c%E+>07(qzucve=jem8jEvO4#-nwhsCV0ytA!><`K+sOalay(zNL^Bx+Js~uxUJw$NXJa}>94 z%*6+OxEHIwxLGZ#t8SpS;$0#==QX+aU%t5$Uwi2|GODaaZs?8(aQ)W@6Y){`1LhXz zVnsOppT9h=07=){KY@R^FBb3p$zgm{+CM=rl;buufAEtdcrK;}UBjo;f=X*O!Iay~ za3$aQ0IJSGbpmR2p9iQ{xx46qdQUk%vc45Z>c;Vf4fXh^ZJjvQatbj8L-@BXEqFX< z1UvHvu~~F~QXgI{8NrLkdhtkfJ67FRg?nSiP$g~tU)ML_KlXIvK-C!57mVPUq+a}= zt?fu`7)Mh51irOR(m?B~TN)@2P(A0l%fcz3{`iPLh=mKM)RhjX?1OUzs3{vK@cH1> z`n+QJ!daIltwCfN@dDF)wE(IUuvmG##>%{llZNea*@Q|K)B-D33);ltCiAkHN?Sx+ z7hTTsGrxvVQijV7s8wwqpk7L`&Ny`U9fxJp%{_UfN@2|{jQNNw0n-xui=8Ft7_wI0 zrK@fq!N%k|eBI<9k@!O*Dk=tXCCV&{sO`koF8(uwu-~VZZ zf?xmGG@ANmhWJ|B%#1B~AJhziR01l~Ksco(*#p$8&s}sty{{6#_HZ@+c}FL96c1y2 zb|2Qn_6m3oBDrP^o6>r5!`@EB)=gk%$w{Q-Nql=WZ7w${y#}l-Mu4}^2WBc(? zVi%rD8^C)X;@r||+_0q!KaA+a{V_fGMRYfwPaVKtyxM@TNA@7DYyf|=wjPfhBdE4p zb5Y&WKzV@bInP}dZsYN0y!U6((w8!Q@Ok!)uLeM+m`|T2Rm#c*23GrsO-_zTFR&oj2|;?JQeo_60$w0zd!iDbo4X5_V)05<>z^ES$Q3N@(+J^ z;l$paor%s@)blQiN#}IXfj(J2XW+~U~>WxYx<_SYoiW)Mtf>oKj_VCZYNsQ9Z z-U5ORBgza$q>}biv#)P_71{GEIk}qu%XxZh3R9Q@D+Bshjp zs0Mlg4ye)xBn?I`xhGK*7Qy2IWaVQA0#cCVg=Y@hg;yDV_l-=Vj+);~F2i$L82G}s zg~;!rg_Hbl9ZTocFA;KT=65CvE$^`t(23e~Z;^Xm*y1KHAMhDMx_mhse{ezF?NnOW z(zJOK-T!w}yJ_k(tG#s-ao&M{A64_ya03rB@_wfX2gH>7#2+R7r)8kb{+Pp zbUr6V$o2D7rM!tGC*^m?zbs5;*1~`yD$10cP4NEZno;|KT&ZY>144U9R&YBs?|(GO zR8jc;Px7ZM0}~8dvNWD37NPXK2o8sNW7`O)@o{Xl5|$zDr)ngppS_UhuVSH(Gb$?- zmuE;N89`+Ws)%#g!K&v$I%@*BTsg-iYQWi%YbNoBYF2Y=>$cismXsqHT;O7VyFhyn z4C~$P_>qNv>t>d^@idQ&z&9=!GM;ak0o&G!uo_;`E&RVMqDocdMYiBgsN`lEs5d8w zm95iqKjTO=$UU`~@k(x7Il%2_k#&;II9fV4j*BMtUX{~jt%8oMw(?9%6+Xn2O7ONp zb$`Vx zUm7$Y1~c@GVpn4`6(bl`nU6Io3^7^TdS9jJDEjUGqp=J(qc>mgnY|UBeV||X9~KL4S3c2=eElq#CyvuCHvnHUo-M4X44>+u1B5$60Q<617&D|$+S6Ws(T=P$1T z%-(y$MBaPLZ4r4^66D@(_~gEWFRxz&RU=}4ZO#qGyN3TyAW<``+#%kEJ@)SRZH3ED zyl5?Ww}U_Njb{JsYe%pGRx5?|eJc^{{8<;1>fceb-|6XcjuuYa@b9RFP?X+Esl2mC}w!h;j8xV?p)#E}XPp6=jB8n*I&D zye4{%3PP_v5i`2LDO^UecTR_b#>#$NkuFiP#ZOOL&K6U{fo9GIyknKNa#Rqi`C2(g z0-(qS`FTKFs-P$MgZm6RmT%wx6QD%r=ggipo7hG^VXr-o;IOxZx$6_=4~+TiHXige zI&a51G!Tz+c762(DUJP zCV1@`6;?qtyM*Xa9o+G+OcqMdCt~-@*yPv@ z)#&gzu>`fb+=>ERn*I61BXcnaW3IXz1lDxosp*a2tRkX0%_Pe165P)9v%jMgqM3Tt zWmO^?8u*^b6}jNmMQ>tm!6wY0c!JBNv(16r$Pb4XMdA!)>;A-b=P{DS-4ET^)aywP;v@91Q54d3r{ zAFO|=lDWd}Yi|})aR12gxt-7CR5=I*}i#l;e`n3X4e>f8` z>-9`>9~jLOylkt%stfsT(S|yUmj0tOFKBJvZ)!H3;3e{QKQj=?ifUTZR#Nv`c8boAJ#8_a$_Lu;X1GFhb;lLbhC39s!tYhqTJH@h%l(v|1c+J} z7dn5@8tR2Ea&-g?B4cg;(rl)5T#Z=j^h00h=pa3#`lD&pH8%m&{D8t2<1*97oH{=r zPB@0b-y^TB`PJt?`$dAU&7hA`0T6IL;!QMi34wYAoyL+W1Wun)+v(iT9w=>9^S!yZ+AGNRKP*AbW{IuqSt}f8MaIf3+TzJPJ7=`cyz^N4b=wd|sLEMT z@mz1&7K7y>Z|Rify|lXhKK|D>_Zd35Js(CKJm^6o<|p0n+8P$hXX0r(>Q4wd@J(6t zXQCM}R@M&OdEq>J%^$Gys1P%`8e9*_>d`8f@f*&CD0|SZUVV(A` z+I9b0H1x%Vx0ceLsXA5uWu(bamJ(K%i2Qtr7!>?d6bZf>%cmeOI)gLFi-1WDJN zjsN%8s(P@En}*5GT1vtj4Gmqs*42xBB%aH7@ejQFVe6gB#sXwb&iQ|Fbe8V$1pM6l zyt~-n(xo&r12M7W1$@wBTXu-}W9~s4xm-jVI{<_Z%;zzj;EZ5_NHGizQUE4w-fdu; z12V8%3t#BY->9r9ppUAP{wZ^AawuXePcIV1_avE z<+#SuX2+r)!s#XFP<@!SPSfJ$Mt{DdP0298vgZzt{h;nz;et1D3(_nsMlCk7s5 zt(H6tt>8nXq(GZ&Keq$O-eEfDH`Tq{-!D8oJ{P(8i{RPbKD9UVQQ^-@9QPr6C@5)lVh1}-Vcf$A44gvRWcvT46-T_{&pAo{@dJy4>o%Gw( z@4@L6c^QLLTV9yEu~d$&hSj5ChsemN{MK4BQX8fe0h1SZPF`P}g$pe(8;vi|*9|}c z%K)#f#le+jv_o6pnMUfYm5P}5efui_6)nr}wiUKocWP}?V>iYrS)UeU(Ue_yO+FAo8EKszu1StHWFY*T+Abu%pu!F4Kg~oq`euPcic+a zVb`>hfb$$B`4(wn zG?<*0EY{-EyGpsL#b8pr9_kS?)A9D3I-rt`xF8H^X~Y`Hoq84xOxhS7cj3m}xl1+_ zl{!-JF|@Efg1v*YBrc0pSqT_RaJT*?*Bb=c4AoCxq+sSG2(oFE37hOsb-LEK|3SD4 zA(c?p&wi=+-{bzsj}9WJ9gob9Df6-67EVsZRa~BIx^ZHnc&a4IjgM zjx^2sfdA+EAy$|2(%3_(tCbm9^)xZmo<1?jCDrDa!}YTxK{v9Hi`%m)YKdl$E+)FA zx-2D`e)Gr9Yr)^Cnm_;M53zn_kBW;l@9_A9&m_12d3~%mv*#&9DZE=iTl%*N1PBh3 z-hrwUB;!)Z;SNaQ@+MAtv5^G8dZSW(_P2{>zaSaF9g!IdH``(4;otVm9wp(t+{H5% zR1G`v^?1K2r%GZv@L?SouO(tiYT%^%bwe(7b?0_wT8D{6EK*mCQMLhPrtDEnAu7{T z*6(|8;8As35B&hskBzr{c%3&tZ>0`qMc@{=m?iJZWMTF4n4HAGtt=VqMlNBT1*FS` zg&(`~VRQ)u+@I(Hh%qsW&oM9m%ZHOJPt=KQ3mnCSyplm3#MS?8Xt?!36S5rWuLTwE zCDkk=;S3K96BMSGSseL~jI`3SN1VKnbq;o4lIxp3D@DJ9wlH5FIp^V#H%nx-N^1+4 z2HUoE!$?9dzcWyYiMVbE4|7rqK|EICSA|$0%qGuKSwt~<*#yt9v66XCY(JE8JV4v5 zy=6wMX|V2SGG^eQ&T5=v*Gkm`8b{usvK4TAA5CP&K*ir94O{VDNNB;LE zA|m^iL*QVIHRtO+*~sYVZ)3A@hil9ARr}TwKvuOf4NPt8!cN%F=$XD%IbCmrM=VTC zIlPeEzjDsZw$#(sC)R1Jtn~jEl6o*td5C+%e}_qYq>!egUICd`v8<@H{&s9jG4*PM zFN6O?V7S~ghj6j^8#geM1w(Ut$BYW@TF$qW+NP_cB!xS+Ug-)%Wy-#=)1(W(s;?(hd!=dNAD9`2CNeHQW7=4_HMt6VaL~j&qXpCuckQgrV(c_#5c^Mu9R{^ zLRQlW7jpHAw6zl*s^oOf%v=`fPL3B4Y3wH_f0gU0Z?ig|mv3eIrQJWoR3T6EswFn% zYwpK8kyU@`EN?03EB`6sM$zNlPt^(jmrA?y9%;KM+{5SrVWBKOaM_^qnsCyME=%b4 z#Etr=oamSsl(2ic0?)RFJY8FnJK%_cTL*ZsKxU&uv~VTm7qUH#3DxAx>}y-sr-`}c z*dU8eHK$gLaG|ZVQui0rZ(UJq{>4-zHX@&yKV)C^CAI*G?;__$;?foz?EIei3^5wW zf^!$?)tLlyWJ2K2;P~4qiwD#2D6$I?)3cVTdmKBqEayRzZHsd~Z7HMz_s z-qua(gD*sv|Eh|3jj6by?E0@{Y|F!}q0sNJv6ZPC#iJF3wHCl`wULrV<%!yM-4TT%ZiPF1mbDK+YM?n!6?3@1Fu8@zBf6;zaffM62HT6yX%bdhegAW!_ z;KLZW0+%rYep_%NL0Em*qk<{FUw<4l>q*#Nj}M!Ngqv4&XUjE3JD#nypM9;ls%(&= zJxZk~V2moV4uoYGz~qG(6mX#Tzqed_Heha>^Os|#K4c@k=;lQue-8jvdbY30st6yd zyuAu~RtUHfsQMiVnZWr5mNM!*iLUNL(Ks1=HcVhg)^^11x6F08V%;AB^ng?iEOtL9 zR|d}CZ{gqx?)8Y;M)!;V0IIbW=c};D89~d(J{f7Y!Ic|awe=UKOeutd!4PD<3*G^W z+TB0T!=1ym6p@&1Yr22_{dY|C%o0jU$s(4*NYrEig{iMkge}7K<#*TAEg{!l)VKM{ z!iP(a#)l{Fw5#IkuUpMzrGsVZ@-@fz>=jUL)Ys?vEp$d^;+(36u3ItFo1bLs=Z3Lu zX)x*xZg=abE$4r}>p?)_G_aEEnso2Gyt8eXaEUN-KYqdW$VP33%ZJwq-dm#e;02r@U*;%?bo~-}vp(?OI(rF^ZdQnR?H1D1O0)x#gVUsCQT)@`hVk{_txg2mwpn`81bbm@JiLXpm_UXHV~ zY5RpKoUE7HihJmXa=S1axSF*f{gcDB#uyP(md{bLW)V(&#KgK^6~h;$3D1{Mq&!z+ zH9;X+8$v!=la3qv{eN=?FOpyJsN42o=C12hBIUFd6&1Jkh8_Mblxdof51MJ=09r}4 zDSFO^Zh)V6{l~>lwHUw6m5|JV{D+vzO0C8sxbkzlJ~KnRx^BTA?WVA5WCSyIkOnd5 zM3}P1G}{cgBNr_6_e9v37JuFmT3T&I*tc#OH+vfshWwOA2R?hRXd~j{U#g@KQy>|j zy<@2SyNsnfk>G@`Zj^2=Px`cFW`{s^uF_DfArBh_6s@SM+7-Y%`e-6F&W`(Q-5xw2 z?TWtUts}wiLI;atb(TYk4eK?=I+qsInBuve5Ff-YVCCGPrylxD$xo#?Ei4DeUDgqP z&hogvZKXZUCCXy5v>T8Y!73mrOXxWw_qL1ah?z!P05D^mZxNOEBvmF^t@{(wk3%Xe zBXS1Q?i6U`AY=qksD0D1uz!WR$T33(^K&3fmdHVle-`v;sQ=-;oC=O|mcu(XHfiO> zm5`)1xm#zKZ#m7np~?4d&AGK*NTg?^gm_w~mQ~U=l88tM`24Hs%9^T-7KhS?UQ~70 z(Nt`l$Yx40pM-gd+PN-7Wlf`gE{3$N=tMcSw0|7pK(FEci~%o5T2fi1{6KnaEBc=) zO^ByXY^r|b{Q~~n;zCY+Ik83PhJ>hZt=Rc#N7cJ&CW4%3nXYB!%oM_SR$2!+BNxZ( zf{gTC*^C4$Ph3A$)z=|a)%X-=5?Y#faMeQm^x1dXzD?2ZMm=2*{+e-q5jx>#7o^?i z4H>TaF|#ip!#lz;;e1rD5F=1-yliaVHm022IGb?#QWPA=DV7h9H;oA{Z?7|Ow%lJnxHLDHb(94QPwE#U&PFl-Y?vonDG-`2z z$nG)cFOA*%Kc%pYgK3z=g>*WLAOINZGrL2Xo1xYU3 zeQ&K;b@IY#!2CuffUEBM0iP7s@QJJ=8sX&HPVC zbOyfL7S=1i@262qODdR#?pQM8L(cNPWcyxF z_-(St#oZIIqWW(FkvWpmb<|VfZY$#N@OZzf-C2Jh)*H_9GH=+*3nrQGj}IHmT?8z) zYr(YH?nrDy^$zZnp9Svvon*oU!?`gBs4N#(wVW2k-F^B#3%K8KU)`sBv2hOHA|JSz zhS|2lCLSM|OKK{SBYE=UZah8lPcJY35`8tAny#wZTv-VDv+di^*ko+(jzVfP&KJ|z z-ii3)czR+fiRGtuXd~{%60?4Io>a591l(N#WPsJjH}?{(Gn-1BAK5>r!(*pKon;B( zsT@zOMK`zTJlpuFD`0P3M{Aq0fKDeMk&JbvH4m=35_mR0iN5222{Z*oO7 zc}J=+3DajSx!L3Ylji97B(Y}YmS<{}#>3RJZF*tYctSxlxxnguWhE0K8R^}p`B^*` zZjqNrnB#6++X1(Gx8uu=xB6BL{&1-)?vu33Ar|hXO|trbXzz~`G-NdsUeo->5D1^~ zROw!7ZWo+^Wv8As6$I8YaDqc!q!FB@lXD!l2~(bN(=`#*DL%7}f9~Bca5+F@u4?Ps z8@sy?5$%ApBmoLEe4;)7=|vt?WiIHcwbiPc)d;o1zTG4bU6p!i2`?mlO%D2>$wV!) z^iA!WzuaHyhZkqn++JG!rykk0CN|`MT$kTN=X%S*(9_k05)D;nndXI4)kSCf=q+z= zkZ#K;g&5kXv6p8=5fw=?Xd&OmAAf0o%{Odcgy&^~xV6i1tm1rtQ+S!hzRd$aMWq|E zzW(r+DK?E(>=v>)BA!17`L_0WL*Oh_hFDs=a?h)ma`%jCq1(}9KMaW?G3%cUSR8W! z`-D=HcA5C63>xit{b9$n!(<>>O6fUTsowU!6P78?>hsFH5qe_d6b$HhGw2r=?j~-f zNnEVZ_DT*=~%{4P;%+R-W}dI*StPP(Tyk#sq%9u|Mqwn8cjBs)He)JOYTo+%~vy+ z0b9Kv++AlW1a4drbaeS@js_O(-}s+uoJKzvkp3s}T!jHQ+$~{aN&?K)P`PA%1^a9PPJWgYc!(tv{(}2 zsAH&3!O%cXUKK3jQ7s)^wU~n<+;w<4$RHuAVhs+J5!qrH(Y%CN+*<`{<(TpJ`)vQD z5IO3hf&A7^SPR+O5^HObgIcYG#YQR?4!mF-4P!;3Ynlo`Y5|I{lxi7H)3`2I~F&z?igvWh&HFB0Pay+*71w9iv!mTb%iG? zRAX3lq@>qoj*iwk-yQJC(>ub?3Hub1y`7EYdnfyQy0z*kz8#(6 zueG$RhQ1;JnTTgvB_k;aRT)ivfqguH^)P{1t=}gUr9^(Z2XXDIEv!jnxT9~W_ri8p zfWO>F5xn2(T1AM9Olux8p@iw|TM?rjO#SS{xFDJsxGtxF_x!b|5^wBzE6sEm>wot? z7R=w8nEhbfNMb1&6+OWrc4lVM7oWU_Wysy?6OPe{C`n~Z)m{%3Zr?=#90KAoWqkkv z#kqO{=v?~=c{5_U|Dgp8o>j_3gd9*8v;}wSQI@=KqeSE9B;9T~c#Utda0<{_LA8~~ zm~Y6qMvkkl9+iTGH7c1X>I(}w%)(YccOv)I{@hxvUh!|P1Y^D4{%){CS9p_vxlU~1 zQ^-v9dT?xs{$2K%cnt+Qk(?hcMjg*u4=n`<)Pu~Z%C8zKi|IV3r=W=HTCCbyZ&Q`q9aM6P^UQ7h>y^m|t9nlsMD9eJbe7htSEyhskB{T!m4;Esz3ha!zAk%wi|7tp6{ets-2J}3$Vh*<0EnKFuD zeOF^73bzfE_m`rlybV~tP=~|*mPk0MW7plxVB?WS^{1@MjL`Kj&jK^bqX|;R#o|Q< z&gUHG5|VRD=+LL7^WG;X_`^Tl?@WW6zrFpT+slv&7PIBzgdx9QqIGpI9i_)HM8>@l%(wB$rT;@zSxtG55 z@fKq<qNEhzd4O+X&z2LtMA&+@Ssq9QqKlDyj^jLWZ`>|eV6b6S$9 zBR_IKlPB=a8p9+WeWFAD$;f(GA2LA^tojt(IP=Xf$?0xaQxt-o6+z39o?V2n5B=NLeKVh@#9OgQk^0=&TKs72qKC$vS3 zGnB49xbwY(XaVuUk6f#z;{*b^)%NoPaaUg3Av=_WIsrfMDp$$|c90ddZ!Z^9mB)YL z&@~l49TncSsfe}+rzq4^l0pRl5JkIDI?Hl{6tPnea#Q@AV0O1U;h zD%)M9esEEpw9}t#YOB0RI5SO3aMT-O@;ZDF3T>$9Y)M6xM_JL*5vQo%crnEPR*}yM zhVy>s?Q9~qn14|*dyy$Um6VfYi5u~p>m}7(iOQ$R+p(1(aG@xx`4F2&^+;MK?s2xn z;^OwHne>Ayet#&E+5|W0pu~%BePM`T6C#1Ohvaxm~sm&A^CtOphkRzJ=BzpUlJ4 zV@V}(Y=h(ry<22t%GvxmTbeIy2>lTx8+c##gVcP(9 z^wL7tgJaRTxSZza-}ki5WP@dscKvp;9u9;TUT;pqLeCtJeRM`I${sNmoQ3L6lfSq! zc-uR=^QPiPw#W5QQx~Qpi2?@Fd`UjEfb%rfqtE+dvP z2*l*T%)nG?3!y`li>)ePq^yX3cbk{rD4El!nG{%5(%@ zat(LMhR)%j?Pn2NBjN4V7&BdF1%BQA@M*p! z3Nf+A5?^Y@#@0Ow-#|8|V6>nQ3v{<%-**$=I%wkmP9h`@+L(p)sPeSqCLsS<$o6?J zi9>N(Oy+$~<7v#ruf9#;w7|Ekz1mSRzdIuw+f9Q!!!mh6Q?$Kf(bL+mF_;*BzAmSoTJclq`(jJhYxqow)9tzI>J3!5FQ;{>@Z^>;g}>}L z8TxX{`+faL(i~F8+?-xpAXScX@b=)&@Vd=btaAGR@l=@3m)rR0^vs{NDxFRJC8T&r zy3&ce_Qlb~TrkaP+ORy1L|PYX*lgNtA8JM}r}DBN^y|3V=P#7PzmFIBspZpucJG7R z2ox4Le)bx}&IbcHcm-?g;?b#Ba`9mjJcyBTL|IogI3$Gc>)fIzu$T<0CgOtOXz2d+ z@nKb|{p2ro_cUEMg`=a^lbt8p+4(jszxuA;L`0+k|6 z_#guKVj&sFRxks!vCtkj51UHvs|+f-S%H#&CfFML@tZrNu7eAc3NGiNlf`c->CVSGMuI?|c&qplgL`(`jkDflB% z_XCUY+fkakM-}0u)cn>EG5Tfd)9zJQI%Pv8n?4Ej`RED9K1k%$B$OdmWjRz;o@dfo zDB1~xx&^qcRUX~M@1=g*2$Fk#+;jCy+H0RpVh+cZQ|YRYh6U1(#0*`eFCvBNHr~z> z5Ue|A3rV1~;Ca5rtDvOh^=~N_QaKh%Qg5!oKHrZ?LOx3tpPKl8p46I6TwBY7Ro^>t z3Oye6Y>NbR!bXk1oIMh?4hY!9k zjyD}9A*YdJQ{PVtn_gHWd%Qu8Oj;fF&!gaZN1w(yh)aUAk%e~QtsY-pfFtxu-_5`& ze)lng9&SbjFrnjltj7AhZC=Xhm+%F0nML_r=6_V}>ypkG7BXY!?bAL{t5@GC(wFbG zBU7kT!E*mv|MN54JA&hn4VqK>VJ9C`XL74q`scPHSj>)C9nL#tBRF<*Xc8FjR7kj< z0@Cv@H_|Cx=1p~sw59dfl9RfDI#u;l)*{cIuK%$L-#M0bQ_l8|LI%f}x6e%VS?zdw zlO6SoGvo+Ai3!X? z--T43{h!u(nqAOBLmCfUfOhZoz<7Rv#~qPZ`$w;?QXTunrJ7>}{0Y{VE$=2aI0xqYxj?(>>ZoPJNz6T(W4v+EN5Al;y4>1Ti}r0*uqC^FEIz8?=be zRr#iI!q=DW;uF<{D7BUtU@TF`7^f9pQ&IoSwybPK#p>jX#1q~?iIMCakIV*(M1lP6 z!y64APv*szO~&1C9g7i$y_YT>_vmsHs6)_YzQsu#E~fJAI7mN0799!vuYl7Ww+>uI z140Ey?XrG0RlWtqi4}-2$6rQEkLHl&_;{**i`8OdhnMt#Q3Smw)@}y&TZA?9O&(^9 zXC4dJywrL?F?sXnW!Q$mv zo9WYKq3T5Ase%BeIYwTpE8*{=0l7Y0*^d@a#vZ%}NQ^mExA?LrnxwE1!)*E+-`4%~`H8hQRCTWB4bhs2(1s0X=WC>)UH>=47Yf z!=%@EKI)V{$5!a&zt|b00EfsTQ%$C^QO8fAPGkHYmN6g}B=;%NRgO-bm8M}PP>!*r zg9|KYRoAA0@*;MT*sM1Z+Y386oH?|kQXBL`zB$SGIcBV&j~1>h zDL~*N2@|=WcUJ{5z$N&<5DW1nyLlsi#a_O?+lUzHqD>kBgZg8NPrlP%-tLpdjkF12 z?gxhNx6PF_cuuJ~vg~0KeP3ql&6kgim$esc={e|yGs(Z$9Qz38BXVkXs}Z1;oIX1x zu1t0&`~s#UV3aFuU7=4>GiJO3-BImuV6py0KjHh?7dlI#IrkRCVPT=9viEyQP5Vhz z35+?OyH?A8%ZR7G>SIQW2Q(KpZ~ep4v19=D>?*C(RK88P%mJ;mS)M=Hb>Oi9KS$0> z^Q2US+O#5@tAxnvbEv%`%SD%t;{G})qV$SZ8t(XAWLiE(b+i-hQW;*N^dQAW*q~E2 zOxYnH?5vWEUN(*hCNgM931VX2Mk=ma$>a2jOC-&l)A4qSS9>-m=kk9vk*zFj;!aF! z5euzfz1~w}mPW|ZA}zs~iuHs4u^q3=(GoFsn%gAk@oj;J6ow^omc2N%?y)D7kMLe&fwm+f8Xco-hZK8fgQus8mUOnuy1 z5aqG(l@;jf1iRZZGv->Y%tI+5QSPts7Do{M#c!?k1q1aHzx;+GX&s(MBr^KEll39&ujjA|qhbhCnn%|8?sYHttC__o& z*gCqg5=zfUuTeoOiPW5!w=B!1x2h`Fv9>5x7m30m2CYiTgXWyy?MJ1iWDpIC9~EC_ z9czwtGmpb*G5+}<@$X?v z(@uk|Cij%$>Zcf8<>RE3toR+T4>91I-&0i!E9^YWducNv5CXoY`L|-}^U<5O@o?L# zZ$a?^Tx3y&BmE}u*@#r{ykIdREt1D9ThMwUtV@2%1X9i7(lNmwY|?nEA}Jsb2K~n7aT1&<1Qn|~} z+Hl5|XL3DI*(#+Pi|@<|KL_WBP3fI#|Mh|w{?7@(b+QK9(f#f>6h?gm{bkI!4?(Rl z2S;UJKamfE0Hhlt4_XHWauf%2s?WMJJmIqQF&{GgR@5Zv{Jvnl_mHqYBnC`F`p3%dOibILu!>6? zq!pHwC3?HCKv~^_6QAtsQxUQA|KF-Q7=}V0O=)2c3BT1@$s}Vu!xx@`*N5p=*!fLI zOR{&AN0*bB;zVJnK{+z!?Dm%5E5Z}jI6jpc*NI}g!UdQK>5NECcJPQw4_Mv16zN7+ zO@^dm9%$cGnKDb8eI%CkhSu@!L97tKq0?gHP%ot@iz#f&=1xOUOQ>B9Mg^_*?S$udt?s7Tv4dqEL0J`jRpE z^bwMzFGChvI{KbKaX6uwwH8a4&*Doh9B6ie-N?hKLp44_qI>w%RG&F9h&MOFT-xt< zmag7W4-T0OH8CwE+XW}%)gvo)KO?ycJ$`0aQMtUonfx~T`r>|GjI}G`%%=!A78$v{ zcFZqKDk>RUdJ<9fi4zi&hCF5-+%`0RgDlUg5)u*(@Yk&A2ugA* zWg{Yt+cQmR(4N^qb*Hx#ZT}Io%*j#Z^U?ve@7f?lP~odNlR6LqHBzW=V#BJzF51I1 zn&rWoss|q)lSvqEeqxXdS6;}l0L)sKy|}hYk;z3JTt3ucRZ3dS!sG!Pn&X5kufq17 znbN{HO^L7fl0~_$4_|PiPkYWQT;I8AP_Vb-dXFMWt;VM6`u8_p810s`Y~Kx$4Qw2m z3%u2vO>@^>GQ}`+H{beu!eWfy@fa*be;87&<)`>ywOA$T-Oo%-I@u>;g`uMA)QIV+ z!*ID^RsHF6Cko?t`=9?d3f@5y9*Ex1Pnp`MP5VVdi9l(l8FE*;48hbPpnw6ckr73i z_lT~S@O`=DA24%=QRNkRT;LeMuhZcSlM_T@p7fILwKpfs=v`vHi6NqXDh3sVx;h&Z zb$JROCuzVO0oU4<+EtEU_b;@*STYmN-C6{pg(Z8q+%Q4{8wVKXW6~4zjobwHi?z#LX+hHdTZGm@Zz;2yhxh!e4c1_-C18dxjW%UK!9w)3k!wC zY!DUhOVQ=OE~*YW+$2#7kZMQy@Sf3`6AfmWet@!nX}7=C|fz z0)&LUIfS1lL$_@5zsNhPn|{>ql12GlO=yVAN9L9nqPDke@1hL^`}C6g0(CUr$2?{G zA-{Ym{+*Q>!M(8&03C$IHIv+~^kFkUuhaNkY{e)L()a75){bQLzt$yv3R_^ZtK)s` zeuN15c^Tg77-&NV0IiG z{U%XP9?HF0-#&(%*givawYs_X3vlZ$XVGtv!FljKA?Ck&+FT97+HfW$?`F~&jfEM% zosNT~K7F{tiYYwoD#JQu9BS+LWZLCcCBABU&i&^cnLeB5GOvFg08Q{ei`k^_dGqDM z@MF5W1FvzYZG~@7t3VxKyJKDi&@KpgUP%LHHlalY`&NJcdcBdO8D=Q4EKuB!^)-+dG*B)POCM{V@=XLzjpQgnr%6YkyTefNTb1!DQz z%>gd9Kj@`b$RZv*LjRD`#TqgT1|uuX&Z_d9sdT+${udU(8(7rD^@L`BaBHzyf*80w zJWMaxownu(W2vtG*+9@4!Bo;kW`sdX%77P`dnPHZjlUrH!ajC*6ut5d*RXk0i1^0?UuDmzocZQN8gdqV3~&#Q05*9$aQ*Y}u-F z1)a%dH#PBVn&o17?6r@DRq^7fR)T~T^bI?m&mX7#pgLG*^0A%N2b}kppRRXhROy!Q z_KUQSOa<#EGA8BqwqY13Vm@8bLi$S!Ct5V~8L!?@Zz~){mJ4UfSZoH1FL(l>wpQus zfbYb5eu4&{nE^gy@NBm9320|{7GI5oXF726sY#^GzfN~0(ll--P{Qvr$OkP3@S-#J z-b<44Rv1aT(cn!^tou^Qfv`hRP z)=+@s$t0t1AYZm>zd3&`xJP_&!mo2V{8+p|qUP(G@j+}J3y_i!OBR29`P~Sk4$+-Q zjH{UU8uwP)e7_k0__2;2O=Ln8oNa+FfX4D-;!5%SlE34kI=)Qo>@(9V)4}oZoh1`I zxqV(o3^%jCv$JCc7nq;I|F>lS*mtT+hh{{nWSt_GiLU(Hlfe6BL8jE_7M0Pp0rID} z?_Z9Tw1#?YeiYmsp!Z{^WH*T0A?E~WE$RjQ+`j7KLXv|?jJPKh+4oi>b+qi63sg0j z4UQwUHA>kYK_%#VIGcP2Y4U)>?Yr4uMeCEfQ4F>`EUHM-acxD4Phw-5Bbhnrkmku` z$w3+fjO<=U1Q+jFlTXb!|C@2uds&*PgX|B*7PWp~Ozqb4W4<~LiZ(5IiE&Ajy3P2H zTVE-Q@%|AbT6g=GBLQ2|=rjY9-FM@eaFy+sPjHDBFOx41`RSP3eca^Ok|kBG12fX6 zR0AFb2y4Q)Q~NdgVk~{e?l-jw6Eq>a4F!%Z7jx{9R^CS?7^Od@gVmZ;kqa2UNz}@-QTb;lT zj^6L$i+|`APiFH6)lN^B&p4j_L%jux{h=mR;>M7|W%Aiq=R@DEKuIpP7wMx#ZHLx} zn+EsT%!4mhtx{6#uL)Kpt7BtL8Q~eHn_YEN36)W7`O{Xf=m`fe- zB7Ci2?ZdnI@V+_Rc%qq&o!r;sKKVdcEyYpkrLEAcb|(B?33qlu$3-sU{Km=vB(l4k z*EsH|nJCh4ZOkjjuzi96qh!X|_0B|oRCRVTKQqhTbTM7Yk;&XM^GxQs>%wdenUg2S zF>8Nwq;XgMEL_hFZGm$JEFD&QaVkU&jwsTX89)n&&y* zek3?r>TH}b2H$1JXLGZ@WP4okqzpS0>FIl4MYgryt0R^ln`35tS@{!lK20LbQIHz3 z#G|C0-+_-GW4S5sz6*Sv9*=DG$<0q4xKi~{AtT_4ojEy=zqkOb;HBEe6*@TowGZMQ zC82{@^jdOuwrqAEOi11`h5ZcnNtUdt{*|9brc_#^{!Q#m&nslvKTMbNvzq9-`}Q5` z8W}N*x=uJan}^%AKc|i5nz@N@0?&}!i;!2xzh}#y>6wU7kV(PYrd~x_3$4x8)Y_qx zLHPWE+%L*c#6QuP{4C zvV#VPzFMSezls57@Y=7l@k75Cc?ux3ikW}>szzrLJ^juG3u|YX8h!O)qWgTvmvt*F zioA^cc!k_Ydo}Ml?V&Ht*WL@u1#ybrm%dhF2yA0+{0<9k{XvH*^rMf>tkBK#e>noF^t(hw1{b zJYZY_Hu^mTlG|L2{Nm%2XYh})4L&|TMwrN!yt(|?5yu-w8~1coa>zE#qKbT}w*r$z zAjk-oW*9oNi&+M&7Q1x!FR-J4J8+dMP3Sa`_Y=vLUEUEoM-}2_K=CI^^q;CtMJ&SQ zRrxFxbtuWC_WU{a{7);bW?(ZZ-&i6xWov{kGa5`{5uZG~#Gd00W?%h2bm>T^Qld21 zONb>Aw1CjLV<8cenh>87_kaPgxRH*ae)x)IJV+Y4RE* zp+5I@-+BA+Be$aYFw?CA&2vP=E)e@Z_0`y2VS!7IO|Ue-;7zQ4s1(JlRabw(yY-L< zUp>A{gt|9~H=ku&1#hO0iR!~K`p}%1EQ&LHYGR(-X3KxgIm>{Br$shkPD$RC7$b}$ z;{rbgQsGGiw`3pn^f!bg8!QJ|`PRL~Rp@xrOUNHr`!l@ z$m;(w^_F36bz!?LPAOL0p}2c-FQsU4w;;uf6t@H|l;ZAE+}*vnySs$|!QDA|zjN(x z@89{8D_L`{wVrw3V^kk4sF%0ak(W6z_h(mS>ap=m*^gNFKMliMPHW;kRToYQSy8!h z^8g(HhKhYyIyIICFpz!13y}TE1oW<$;e)J+RR2W#RYrh_$iqLD1#DHBh|tJb^!sTPM494yI-?n5 zy(p@fdf?Rw!ize6&g)I@FpR@b^Rssa&-%3K7J5u9820w`h30=ESe$M_DT(=;xE)Yf zoo&*?gSMt#<7$duFwDV8@z$A`j zM-lm3mxum&6EyvN{UvkBi099wqhoKW4{P;N6pydFQT)pZ_p1HKkKU(YL`zxhwzJY7 zAN{)O{Y1(Djlnc|)qZw&Ka<5eS@mY_u*=vQu&G9(sbEp%^@=(wSmc9wD*HrWiIc(` zuycXP)PqNs>oQbD5_F6^AVYv2fou&K{dR>Ysxd*s2|dn>Cf0RlpiGjHGr_=9)l+d; zjO?k9mM7|B`tJ{Of2rnCW7O6erToIY6J!Mx63z-dUtn50KSFbj*0oTZ;_`<&2<1y>tnakN$w+os z8FVJ~8ZbvTe_g@Z2I{euKsdL;Xtjhr?jPF(@fQ(f6YF)1wtnX@+Z$7B<;;^lLa~cm zRpT-Y!fBSBSU_&vbXaF#p=^klzJ}Mgof(-pBtspx9OGtx7s$lJcxZdtT0h*JMiGMy zUP^{2^I&|`ZZNDKk`aSdaV1zuu{J#XGh;_hT!jbY;!NbIj(0HLIRnjHJ=!ko`+Ji6{ptcC}c0Sn6NZ` zZRZ?5+8v*lHF#fhHU^@8I&i**QeCY*c$G9{m;%0xk2>4ltq0xS2%*js=~hRHi##Z| zJp5$RwR&4wEf22hlz_{KIM%X+1poT=``7oN*h*bHxbg8S9Q2h`fECHoOnt~~^*c*< zA=Vsdt}1^m9emqh$XNcR@pDc0(BSDidwF_a;A??WL;FnPC+r;+M-gAG z9%dQImwrM!CQr31E^&6Q!3u|F@C5KFiBer9Q#=%g3M~qE1;$CrqZh^6zw9{y{%jp0 zY1+N3wwKb9E1=hY4#QVCv0{^KI3=3`i*=S|Czv<;r!BgnQ0x+NSh{BnY?Ul)gSSr! zF{1T)!EGon5RRLA%#Y+E*VZoFkJq7j=)`0VJ*}kc&WJ4Yv&$i;S>hPi?*FwM5MMH# zkNdVHsf0yDxP<4T7jE4#Fz@mzA_u8m>zhoRoZ!}-$%#oN0UoU0Pfyc}tFZD5==$y^ zr_G?}=;Uh=c_@na^uk71>|gQvrY2LRs7(6`VhV~cI<46|GAbUS!F>K3 zr^?q(Sw9?!T3dbSA}GHt9++SY(;1oP#ZeY2_E;zHZVjd6ml3=K;U)zy3w$(nZTPFx zb8wPm?#PMq*y@&^X^K`>QHCxZq@+U>5{f##uo8$7BhMkY(sdh;k<;!;^&APJUkWJ`;jWVL3Zyhdmlr{a z^f5D{vvj6VcYbU-w2!NJ)zSs=l7~alDib_0yM`YuzQ5>y1dnZdD1~~Vp%sggN?M3< z8OC!^j@Jd)aNE2~LT#BiFZW&T`XbLbKi5=NkZ#)YYp^=7pQ`VS?R?IYowY{X&aZKX zCm!J9=pdPCvj?iJIvnk}4wDcQxC5sdXAcrZ4S*5nzQAkRNl{PC(-BWmR2`^6cfnZS z>mO$xpXQ08!e4nE98qqigJtHXGJc?P-Xj-20f3N{fEMs9I@_laUjqYTJ( zf-?NmHo1PQY~mF?H9a~?)z!zOsfqYYbS9tq`_ua6#CdE;mR2^~NtYrQ_%5;yX?~#3 z|MRZG5IIHC)+oEC^s!r$ARKxErAlu6_qVSCapA+AD#u?h(yp$`xw=7Uh(r!_KSA-+hX;lvd3Lm6XbthBN}7gOxm)C@Pog`q2|i*r#M)AAU% zt~Bi2lq52IxThax;x%Gwu9&|=i2hdR6Mkc-U=mULol;DEjiqAZT#fiM^ftG-9bTKY z%X$40frUNXTrie6@;ZYj0aCD~>&-tBKI?SsDSR{%9-9*P=eE$6JWBqHku(7f%??bW z8hAg{TU=SzdvQR^R8=PXS|hiYq@DXwgm4)-k!f5a;2N)`m}%^*f+ZGd{%Kw`rK|x^ z$jsbqdO9SphkHn_qgg*D+ zY7%^><`W^p#MRbRAUvWgr>#k~wk_}7mX@7b5XmRhZc^@ml`Q|P!QD(6mkj#(l_776 zO@(QBB$t7&yE^70s!mV)ZaC*x|7&DmT_l32h*alaxu1^g^0O=R&?$g@;nXLbD=!+i z^qiJ!$t69t88g|pt1z9#aW0rjKt&Ss8c%6Q;NVpzE_QxKZD!uL0FjT8jYG4r``xd4 zT^5WPTN?oo2VMT|s!@(Ey4)p>F2{tk31u(5rjhw0$U-+q#WQKKYI0nab#KV zAd+!q4RyFl?=P=+MQ;~oC`UeQtU5-EOs_|$Obs+`zkm%xXvJRqIiTw#>t7;CR64%K zM?xh?8;<3pYOEEf8Y5?7uaf<=)QOANnXv3Lp+--kfAK?koJ%&c)&yqEqx8|36=E^F z3*1r5716oni_P(AvE-yDhyLJuH9>WMz(Vw2)44A4KY3UmcOo(6#+YTkC{tkl@Wu1k zyoK9XX+*4Q_k@#qU}jd_F>vr1{=)}Fyvj<*k+Y8aT;rqzA2c*WsVt%8PKb7NLA*q5ZDiK`Br^cL4%;eaIb{+h{))PqcNXL*8&^Z<9N*LU?ab%i!Ypn@LGXW>k*L!LivBW za#?H1aa>98hs~z~P9<90r~Tu>)K0^gk|6OK>|qcEU-G6?pLl5);MFo@ez8j*%*I+# z_R#`-PE;>Sfnj2x`Vf#wNJR2m*MH6#oCIf}u!cm=U##li04HRVbtS^iRG%NYsjCUD z)92vBgx@BHb7mz=LWeyv(%c7D=NwKdhbrutJ%jv93BMZzyS~8P_MVeZK0YJ$mBcv(Jv1E6cUm3=dCmChxt%FS+mtgr0eFtq(xvdob7J-oH#-^T8&Q@j&$Lrfgig4r8IHh{g_?Suvv z5PZRu)hIvEQM0}>ns9cD1P=Bor|jMD`E7e#xtFIWv+{=~4pZZA&Iq=Za7WqeWU$(} zpfM|#$2up{G17sxypgROaL?q|1ahH;0ks=uMG=j;j26t(sJYUBU=xus`t)Tf;m)}M z)Hq``ZqQ(&U4=YkNSibBX6Ucrp`9R#?3!KYJ{b{Ts4*N4&+&E+nj=W0;l72-+8~Ym zR@Lq=0=jr-HtT#b!HRQ0eB2X)SR+__Vt>4T&PL-w^~FmH?fuMdY)U)kLqEIqZ!ePQ z&Soq(dW+wpp2St+=ltF)D;FzSpN`jn#4;9-ga5wrktG4nT&|*GDH#8z;Z?V6| zq2|zbHJdWnDS+R-Rmhb>TVq9mw}W`ND`lzZ89CMulR5Gi_{7&zYyWGQY_w$Vs}$QXO5*s;X@U*Wxj{QksI?1e7CTZjH;2QQ#!= z`|sHL22W14dH4+tV7UktS$REzOLcT^;ZMOkNjRV86V0Km4Z2+yT!hhftgEnjPf}u% ziLV*{>}*ke-Vsc4SEgt+nbipIhF@h7Z8Fa}QghU(-v0EDWz97oT_W1@dNf=i!kBlwf^PnazzLolpAN-PoH#F_YR(bgOJrdNbk+X6eRz3Z zwjeHjuFs}8m~8q}^$EqjCt1!#?^;vIKX&!y*YDI%$onys-3oF9%*}GL-xvXP)K|Ry zuBajX&s-R?8@?vQ^_X)xyNL(&+!APBP`FRC-76B<5~zd%PFB*+x5 zcL}qY!zF*g5P(if`s9F*kKy50ihXMPrie#|54EFzOxu3(CV!&z%GyTKS7ludy^wut zQIVR!RIfzhb}WPGLaExb0xlO!$D3v|@lA}#G{UeIgkWf_d9m`v5=qGy7cYo}dzpV; zebM}n)nK{LS^Db9Sn4g{Fk%Pb?tlSEqHX51=J(wzm0XaR6Fr^H`jImVVy zCRZl0?CCPC@)C-7HXPgKs$kqh0(+lbyI19WuQ;k^xM&7wab9=UQ&kKPS5-4+V7b{F zp4BSi*#lc5TB%SMy5n{|Sz~Sh{pdm90EP0_N#UawL!U(>@cmAd`_rm<<7&!6;9X6$ z<>St=3yv}oWEEP}Q|eJ-N0`*t|stDZavx2{weccUFe zXdX8IcmTm8q&w}p;|Oz4xEE@uImUx$pKWx6v*VNI=+ol8Vfk<0^Q7Wbp?pKw1n2GA za3c}Ax4tutR!CT`4Vd8pK-AX@W|$ zI2kK(g?kX*eO)WbJYYXEl3evMu6djbfNbe9;hJJv0;=uBlndgmc&(k?^<^+@`DI5s zE&7aNU;p>}PORwUO?y&+!Lq9RgY_d}99GfsqlMu!_rI1uXE2~IzRukXK|`T~n(obE zNX7eVhe^Y6SX(oChzhViBD3+WK|x{FkljAKZ2PE3zLDPTZ`qTxU5+0JCiJmc61g~SR($CqL!^#S(495gbYTu4BrFq|8M3j5MJvwDGgM6dpJJV!Sxt} zK4bs9Dm2RpND|a8rK?(jSZ+w)R5U2}WnWWSN*A#kTfOxJgO421LhnP6;Hf+X@$Y@B zBbviNK@V2cxXN@&4eH`FTth8IT`BOIo6pMp-}N=uNk91^o2+EmH2fWm(+mV^3O4N4 zXVvti4_CdU-qCRwnQC|Ih7zsbX}hGfljp0NglQT)naAx+)-b>Zn7J_mr zby2TMb3N?neU`*rQ0~E*?Y*VDF$?+bYUsR*xH`?*uu_Z`3dx%GFKK1;g#&@0Yc8orJqZE zlj@68v@c}Cs~(#swSqN;K!%;HVvRLf zYIIN7eT%ZmXZHMmZ1G51sQ8bFEM$L!caw}!;U0z|fF2FM!rEiOv8#x9=TFWOLNnio zLCU=ezNi(d2K$}NoCagr$nLm>Xp_t$luhRucR(}!!23*eE+cR_BZefO{YWb1h#FP+ z>y8O|qniN){lDK!2^rz2=A%6An>+xITYmb1obgQV^F1pya8V@#B6u&v}7q z3_vovU`x39iae^y9(Z^L!+&zmPs3CRpBa*VefQ@~@O#_^etthIv!o%JILV+)%X^%> zNdKeDX-_X(LN=qb8i?z+zqU4;cawY2GQfvNSW(bS%S{2A z6!rR{OQWZRK3;&Nm>=ly*6+s9>3{R}gqX$2pPxlWIt$nRvg3m|WQ9n+M*Vx7>wIly zvfdOMtM+Xbb7zL*^ZlUu=58@T$h)up48r_o|Jl?5Xnpk3$@@LywXlHIvPkyDj0RSiqD5|=;!Bm>U!j2 z%ZXgVTFZu)sB==jk6D7qniM=7wI-S?HJHWwt)E#XV7PAv&e^((uYEC1Ll`mjADk9@E0U^LXvXUp z7XYCiizvw>Nc9;lY&YHxcOSmkiko{TIzA7v*~#06P`lOXU(mYA|1M^uB!7jP$q`WT zp$O`A1*ct&V~=|*IXIOO^jej8mT99>dws>-nMS`bSX%(&UYzasBDt3_lP z75mbbsiQaT$n{pPkqCwWCBg@qHii~CwO|}xWDQkG3ocB(!VQUHn?ZC{5fTFv&X=b zUgwU;8)6dj>8rngVIUOO_rE4WrZf;w4i2FU?BW zBU5(M1$nrFs=|wgsCshe664DD0eu~!{WXm!W9G~ZejZabFf2ArVQ)>fflgnW`E{pC z+8DE#$Ivp62l<22zxVy$)pyhNT6FG2Ekq-NCR@eM?3{czaEST?Z^I}AMp}vL1C_*y z8A9)7MyMYa2nOlw+fN_avd!<^*IcC1RmMbSU7PG%8Y)=y7uZml{kojpMIl5{yRC($ z+ImRLZ4deL%mC4CxKi*Wrx?~R3JK^h@Q3nKgzF6|*`0JK+xhwbY$RzxEL-=n{zAp| zS+FxgLd_0H8^fK9?R0`iBat2n%7!0ZFUk-;yG`~3iJUl$e5&h`q5O(DTQBDf4w+^5*t521H| zMT_3?Y4r;d*HD!V)=ujhMLq|zxVrLD^KoF6*mw;^ZG^pqdgQj(6RNd&Y*j?IAq}x` ziE{^*p2D-4aPP5Bu#4pwO9G_{{ya_|6#Y9D-XltcK6v_(T`HDNtQa(A+(5 z)QQ#73|~oeP{Qz_p>0AkFH$yduY2F$UocggO)6s~PmU!kLv1_w^*fPvPE{3|449A9 zLC22$y-WD?>O#l>%!PL2@lmXZIEZo5ti24SK;{>5L)@;5n3xz$!69J2gp_pxb9=cs4O;JjNVm+1R_xa6G7>uo|k4yw8=} z-4U;PUqG%fR*S{txaqv+Qji+=)Y*(Lo`gd2l_RO(P4%taV(A|$bHSRSczEiIbX$0LeP}rhA*jU< zMdxpgm^+++;+`Yft8-3Aj~y!KX^wO=1!%H|@JujR~6pnWp zjl1KKj@uP5zs?tZ4wy2{MCbc^TiddW{W4B7NR++j#W28R^;GcK+XfpK7i+s>JUzzg z6IMw&WgjbNN0hXD!+~AJXEO!DTSp&6gUvO6ycQwB>s!JfOU*d4yH_7tJjjee=LE<4 zU5GfVSi8Dw@L_lMeMRKbGYeMLZtIGI2_{lJ>klrF>6x_%81yi=s|yf4uccL7UR(|I z{11iCd|HdD`NwZ?yhnZ|)JT#l#cVSHh;*w0cC0STF2H}A;2l3k5Q#M0I@ zAw`_wv(opJz>u(v2Pon;8Z2`;B(DD+vwAYty*L+oY}!rL8eXyX%r^bA$wk_hnmo)i z$FtzfA9vXhN?@la0{W10B{Sn(b48RxkE|*3qMi0R`7idy`cddbXenY+MHLo1tS)53 zOz^JQ4^(9@mFRAxeXQk~Fj>YN@RfHgd8)+k_&Cpv14NrJa#6L@9aoVmZj(x2t+>gU0 ze_F8A&W=uNIDHuY$vre79EfN@`+hkW%gQ~AntgYsB12r}#Hl)dT>G#;R;T}D~R%Y_% zzs_X9=bakO4AN4Wp6zkDDAC_x)9zo|N$iTSg)?9WZPImB_@=xPbjX z@;2u+0{G%NGxn05&AawP^w4Diqv|0d+w!>P6yraBPpMe9k%*CAou4MAC#{;@-+U*W zxh_b!H&)GhkThAgyqn>iE9!c_yf0QZaYiVGFXEHewP%ZnQ2ZNfdSCR9u|=F{YK&P! z(p^|ODJ(WAGM_ysqQ^#+*WUs%trHN3wPCKyfQjK4by-N2RMEp6s;p5@sxc5}3pbz8 z0mlY`n@lyH{b*Fb1E+OFZN0WK?J<|E;hS_vTvb8xam!8dtc`gE23&BlCCo{q?wmoX z;)NS-q(2+A?Xbk|NaWvdbD#otA2vJc=~no>!il&Tff2t!raO!7(28(5s-9phPokNP zhYr?BrW{-)u+eOVUGqmg>O>bKdcgDP_AUQ5TbygY898S@_U>}MlxW;qG^097b)E@A z2fVcJlR-K+_ov^V;WH7#^k7gO>E$qULElf9nJ_cl8AZ=`0PkbJ%0?JId?CZNyDO~- zBQ}B7_-{p}!Fq~?`O_qGEL$BIU~mLFrEU<`9-KGCQTY{UB{5PL{kJ(6?Uxs$YDQ{s zZk%)dJkj6!d=zB8g5nR=>?6aH7BVEQM=)=Z=%448riEDC0tpkNvK6PxXYp+!(|U|rr30Q zMQ5M?ZNkOHnPd3b;6Lu|-*p1SV*EGD^v;f3i{4}ORiM>XMs|Mm(c_r9+$(6ZkN(0X zxCqc5)hYP&&mAD5*;%i`Zu+w$oumB- zf}9eFDZ&TSwk34O!zFKCIUIhs?6HUY*Z0}u6BBd|M^@4o1SvPv_)ShfN`D2y^gLN0W0{$$D*&96`={ao@V)n!mJ_|S!3iU(XM4HAOa z0?9IYGc>#a8US{!>{G zZd53LK~!B|a{m3%l{v66WIs*N@>HaEw}rmN5wT>_Rza{sW2co>IcjEq+zQmS#_nJ0 zG~yEL+mvE&iLcsAyyL@dXd{oa^7QpYCCi{vCT>DmWjmuIZTi85SGs<383LJ@WOLgy zygpFsZia3zSi%ocKE-#XXJrkv9R9?IsaQLQkX$jxkI~5|o_Dgc94HnHjEsK8;@C~I z|Bo4A)JV-=;o)(NP-SdByC4Dm0FX7?S$gL6v_g(iZNgo_kk$5!;1seV*n=JLGfi|n zk#Jm=_?0Q-@YrwkVT>0YSrY}fy!#u2QV3$dj8BHN3c*+VR2Re&Hl{8Ji9^bv>aXS^ zWW1?&YdcOO2{Nk7PWYwj5i-lnR=xp06`Ni79rk6ic%RBj=M$V9L$sbi_%V5o#-;Q| zpA>d}VlED0ZSvVWfJi7usn0hIXRIndO(t5QtM*s%ihWfgf=UX%reSq*T~7)-->2Rz zXNXu_+C$dsa=hQmO2N;=l{BifMb?U~N(v#yHcE);FJfc}hrWBtce2SOTlz$(XGA_s z&Eft1S?J{{+F**QD-sdhC+T(#}L`)%+}=x$6)V#0+Ma}zKS{WIWB%W6z8$iH*qDzx=Eq$X%;0@EQ{r)-OiEHz69U(qByomxu3ghi|Bxr zAQI!>U&%Ee%M(w}jraYp1@kXvqm2X#*@#$SuTQ`7Ov33NLQZRe>HH3ob0KPhC4ptT z!r3)jbN-C|P~JPBY=A;Y)hi4Z4Clt6bfe-&^9Xt0y+Fu^P~e@JZ6idVBb=Wza>vI} zE>xV=_UXSMe?mjllLxB`ofnZE#SRCF=FwKr1mZ_kBdGy@waUrFZv01O0!t6o&ig!_UZ^D{h`3GseSs$)F3}oTkBOdC)+*+GtmJPFshCpBd*is z%sCfMta%)>`j7(kqRK%jYx^~#w%^J5E)+IE#E%M`l;U%em#g2fK`05N-=t93nno>7 zc=58dt^`lEsKy1tT!@z$i^nch^+#53F0>7WmuWziZOxB~kYG)t4jp){&aRUai%Lj~ z{}E#!pxH%L%;Vy}2srmVW)n-pvB=QJ%6G-b)(bPor;MKy!!BXGF+~WdE zUlwjjIdeUc()tzNBc{gFHugw z0k|Vy;RknAkhVVqErY1+zsK)@Me3qp27Ps)v@(Us5#rols*|QWvAb{}VX%BS?Sv5c z%1(kP%Gb%(?UT{0S)X!_0rB7O$u|-2zDbQ_y{=!%jG~U}j;u8`e9KuX&e%r&v)z`6 zIg5L+I04B3Z!-*{3ol;jQ>KAry#vEQ>y>Ttqsv1qju0SYjy75jXb5&Q=6H`ZH~Fp` zc&$yQDzm_4rlxKzjK&Qi}ae%aD3-Xz2OBVcva`uSpNA_o0Tv-@%xT6NU$I(@uHq?@HR^WE@2^Mep)tnH=2puj zTzsaFmv*ddl6;^OE~di%@v61-{|2C=vV#62$vv@2{VX{VR$(AIqq8<4l+87h09kto zllUbbZ2gZD!rw%V^ZGy9JY?k|&$qXs@DO0x-`9tnQEKTqk$jv0c(ifA4NLq$0#Ie( zN-{rPVZe!)F5N=gEP)HIay~lC+vQ0f-$HMWHi`&SaMi2YByh50pxFs$cCh(pp#ezp zL~T?)*;V!F(R+G*p4w?8wA1*rEhp*7l@kpkl~KpbPPEAi>z#Ljiy~`;59$)^eT7xS zcae;YEH*hgJZ{Hq@4FRi%4J(OT&a~5Ce2BcKJaiWW~2*3(@S`5btjRm6VZDL6}}Ez zHF|t#+_w@+>qS z=ejwfD6x-+XpCUV?!4C`Mq&8`bSJHF)nBwKm}ZYs<=I}{p3vh~p61LpZ5?yDE5!iq&fx`p>vi zwKjYH)A+?ROkR@r!w7u-1P?zq*%1?VSO?*Db|&>}V<0W-Z0{#f7d6U!9Iryp0h!`a zZtCRv`9>3Zl-*qKW^hirkGr47G}<5(Vuse2vq)ZwN8h&8w3F(+-plq}N)PQlt2JuC zGQB$pvX_+yYZ{M?KUSO(%0vFrD^w%G!M$C}f05E$WD76Bx*#y(G&NFzmK=B^l){x* zNEHyG6kBm9ZC^jc-!e|JH^CGDCnr!TQ}_2YdAMro#!)0J_ZRMaAyU0ByxC2%O;kIE9h9clyj_h(f-n+~@1lTPP!26OAbBNbf2oSXr-|HN( zdpvfgye8RQM9>TjeNkmPM1;?d%vfX&fgq9ISP`XMWyN;KSBm;nNisve;+AIYRz)h^ z<3`yHFj=mo{>)lMb}X0=wwl?FqwM(F#WB;a3wi&me9r_~3;-MEk`PP~_CiEU(cy zn-4ZZBA53y26-2M(`kMRR!Mj1@|JqTim=OIJvFhCIqyd*vWph0IBvIr73|AFN20AD ziST+!b*b6zf_hVz@2f?=glaa9zNkNNl-X7JUdN^{2gMX$8Uv{{)iVUVnz@)TO^GLGtRVH7%{0>!P5`rRRjAhAs;LaM%majtv@v-yxu20Ep>wS$pTIr zO%jm9*)@SH^2>M)HLZDjBTWx@jrbz2rNyAIVHd&i7ye|55Y9*gZFJjgI9nH-EWc;d z_SVyH@D{R=*HQpuU7tPgP)dJ zI#^ERiRsvCw(8Nt6ovLSC;~uvmV%wr8ixEM>ej1zXdH;Ou4Tt8r4X81sGFqmEtGxy zOZOE+38-e_Og1gAh#<5Tuk`pvLzeBb#pr;_s{>XJJTv zG;p}d=uV?o87=s6o)A)KyFpyETKBK->wqh9>Zj@~gqPa*(fXQW#ItewfnF#2NEfjx zf7_WF;)kOd8L6Y&Xb@@Gq*!*;w0xP%v*l;Z*R3{$62MY<&SPcOkN&FPp4YD6aJP;l zlN>;tudJiQ^|tPlrbU}O}ziWksM6ycKT%oQ+A&3=~79nOiaB#ar9Yq(UM0ER=ioL4NcD)&?4HO~Cyn>G zxp+>Z>?HH^e4alwXa*(`A%)7hpBNE++#R|rE68H$KBSZQy?eMojdlHm>(LH+$L&KK zL!KU-kl@+3Pe9AeL1{C1`TBPJayR>F&<%oq+T~<1u#t&_X6CTAwEkw|;kN>CEDu}y z9T~agWB!SUcLt+`nweB6O0*>QivpbtA}*4Y|3~-_8$M?pwC#-TsLXLR1#4Fi`$Ee_ zwRNR!*>_(5h`MKSHj7Kgb}#cwTv*E5Mjsfn)j32S=A8f0CnpdADzOPRY_fY}bBuxW zi3-X9(1XQETKnxlIyh9~`RQ`St}40fZ5n&@Zqv7}{q`}v5Q<7z->JV*4_yTKbv$o{ zO5l%^IfA;LU$8p?#$FHf7gSqtsGd*$UlJb1qUqP&AV%QRwlnR^$44^9gN985hf0YL z%_orcv>b$WKibYlm;TP~)BewfR$q)%X0y+bX+f3onJi@W1^h4{r%yQ?F>F#VD#-I{}Y5k2t>De$o!?#@?(PTwO;I2VS%G(zku?8RmPD%eMjF z8%2JH*Yk+8q~tDuhx)^LW=m+6SBLMN4SHWT=mhQz;oPrz)5j|5&WeY89KZ3Es4Rsdp^FASz>IUIcnJ}(W#~;NV`9RVs&f~9Uwor^= z=CHE)LoL8ASs8;0Kq9B_qvI$yV}{;o8~D#9xa-MR=QH*`)i}>DGbT&C1a&cO3T@9hKQJ>UPrlv&^k<%8?EI{NSUQ2; z8!eE8aE^A^6(iW)_JIiCYT9xNlEy?oyHb=QtF}ca56~7|}x-{< zOr(E_CRX;t{>3~uj+C7E96WHsS=$VoeM?O&2>ChJC?BJ~L?>$&Zr(pkU;rkF4 zu4Z6=Mo!4Lr2wj!bW}JVZ!%$}1KyNl?(M&+bwm_H&SJffv79+4Zf)g}og~!DHDLgF zpIURPVr;Zd#D*UZ@Hh$hIucwCe#4KK0NsyoUB0&=&W|UP34_LuQeM2BOdjW6I>03) z-|a}MM4xQ0yyt#o?8LWODnsqp0{S4?y71u%1}K_*R-^%a7JruRZ>U|XmsMiT$!yngmIpZeWE0G#bH&|l%gnZ9F z8J}e`lw{?s#0tD`T$ci0<@tH%wLjl&viiNfWYMc)Et0QF+_dmT(mKjItI6E0utg=0u*L*h-!3{db?0#?YeoPXNPXe%w z!n0(}2VxKYSCqwB)phIbb(>`{l4@n7n$-yMzV2)r37Tv-1f9Q7B@j_|RIRz)0l}ye z!uME5ja2S(*}j)ymkH;4@K{d%hzX1j+x3tv*0=gs{jnVgMS702W8{*M$pwU|Qwu}k z2TQA<_M?6llqZ8D*&-`TqEhzYL1b)T0KQB|Brc=hleA^HlG*+^r}a{-H|S~Kq<02O z-(FIXFQ5oRD)cWMmg#0-JDQnf-vqMMiecbUTj>c=(0o!_4wDVZ0|Sg5et|U||D48N zBVA5A%+f|iSK<(4VsUT`R+#uKad@bdyNQMoci!dy8inHb1z6?tMe)G|!2#)?kn^`m zhF56?RRb#hRWjSY-j zndCQ-caxlc(^4(N&)^XKSE{k>uFH9YQ9r?jbAlkFw@`Bj<~JCG%ydBaKp|nzB_vF4<;;7j#kL|R0nx~uYRQ5l42Zs=ky@wE= zDZ3CX$VN`G+Ksc0R;MS^D^Epflaa@9BPttbrO2iQ^7%F;$*y~sMBG(_oH(ujz2dus zH1r{Kh?N`q#^(=%F~3&?e6{lO|M+bK4snv{>xQ=D_ccJlGFUwt2JYg_MvE4<5o8(0QszZBJtaK1jK zf{FaVuG`tm4-$EXf@M9{w-j$bRd3zl+^trH-rRbtGSyy}tJ2;O?rXO(uIzIj^|)Fi z2CN4CC?Q=a5obEz#D$x0TUii08cqSvMZBY3_32ETtn<^4=K$i@#LPFcfnW3ik2r#q zpZm)i+nGO`S<4%)Q#5e*Ki6Ft1L7m;rjJq(yC-%DR@~#yg`>)l=KVPaXtBQ?r+KNx zYvSqzomC%&CNP}%&<8?XrvXekX zJNgINayb`@lQ`=`;u|~}v6JkJQ{&(AS^aEZx74#j$bA?_x0llwJERM>3Z-9Gd(BLILo1!8BV+iO z^TlITsH6F{czSRgtCRH)c6|9V(!9Oiv$2go9ifRpfE7m627kOr-0XqKi?sgtrH_VX zt4Ih=dubH4USD>c2ku5o=d=9dFNSN^iTcgU_f<89#q zY#k@5s1$8Uxk|20GZe)z%wjHyR@xABLHFQ**qX{3xVj6zNnB0KZ`_-YmVH@tfo=}w z^y1FuNtnsb<^|9FnDzT>7PNeC{Nu`bK!2UY0<{5jr`c8rw<;$*ZSzS%wPL;Is_Zf$ ze}JR3GsqU=v;NOeb!#avKM<~WhZOKd*9r)o<|itc)_Y-}wCTlnJ77QiH&ET#oI5fW^#T?`;V^AXSVIF5So5rM)7@x z6orONPMQ+){Ijf@oy@Y)bLNaSnlVrj`SwB3_ zSnEjADj7G6@Bpp#mA8oWjT^;bKYQ?3rzOSS-PJXJOrmKb@^jt17tRnOC5EMU{1fvf z|5u}=X}f*U_OXTgF~|1u@2CyDod1lHcqLs`Rx6*V9wc|Mt}^5D^641e17-IugNV$C zuFM-J|2-CTIWO3NdR*VY8IU{@nQwN><;b~nC#3enpL6p(!lW2Sv}_Vz4g3vqedt)- z>0MH}-xtu)h7g=oq}*HLB=!wC8zSWG65SuFt#n%5a(J+ozBW`QJyIL;xBgJ~9{Ypu zu)0raaudc!m_t-FV~pJ6@{m+CAp~WrH5%bl z`3}Vx$+B6xE89WQLTbNT05lGC^vf8rA+u!J^0ct zzKLu-<4S>i34M1DHTtT8&rLJqeiYb5$96#JyL@?y(;wAL8qXQc+D0x3i3i=OYcFo_ z+DEYj_5)4Tk$X$}q=9}*_uyUx4hcMlU`XtVkrl{H$YaUM5CxLRk#5g@O~uBaIzz`4 zZ}I9*@Xn!38HXwP5h1$6MRoTYtPacj<1e4?;(whlTU@&HT~|#jQMB3(zT5BxMVpDQ zWf`-qgqM%QC(-%e@bokaFGj}1pSSWxA?DSsIQ2fe2;8|N+1&KFdkK6FWy+0E{jVhs zCI*rU_6DBIjUWZPM-{@>-Q~B8&Mq7l8ejO1`g5$@5&PC|O=%hZ2fEieQL8%sv~)Sv zgxjcO;!4AbkdZO(;ZlrD#)|MmjU?MIJj1VM-9N5>>KG6nf!H&9ds-AQhD*yA|DQ|P zki4A3>BQQlrw?`drcxD}6!oqrPksa2PFxx~x^kxGe#F|N=!pA8BGWvIbh(Qe_+^+* zlDD2<+))Tt1$WC@G_vLBO@a;)peO(FV)*%nVws}#vIjJoLLHNZVzJ%S}kE|?3`WLq7033gx{0- zWRT&G$!dl^6S3}SM^g@Sb+*+(8Cn4bpkCsWaIO*SJ2#ySzN{cTh5NV z%0y_jN3iMjdUx?2=%?n5@QG^~O@9(}|3bLHkAcVoA2SQW!5{z;BNg%McmmX;n~}-gSU6wU%rOh=R)RN$ZPuPQvX#RDRyIu$S`k% z?{W~)TVP(O-AZy{oF-G(dlB_p2IjF0P^?mf)--tT6UVLxUoVzuVMO*~_K z?_|=&ilDg8@NxOfh#>6W?!`8xVpPg4*{?r)SN5J&57yoL zcVG zbVXW@MJp? z?VfjC2ta-s+*|**2oO@B{f2Eoq^LV7_v;?M3SN2-#HE<)KF)>jq=C9=>8bumOu;|8 z|AW9=_5Ei2*Qk%MzJjC&#Hq`8U2EH|d#6%PCEUM$-`=3?)>qk=r^>?@gcbGbUP;SM z6-gPL=cS5u7b7k#(s@QocjQ7Ny<55NZBIlz=3a(Sha)<=eSYAW7oJ|el_V;W^rg8@ z?)G53hSVKh*9;}F-$d^~Ja6S+OG4a9CfCvB&1i>j@)K2q1T8(SQd)@>aFxLl81@mfL)g=a8u5Wc%#qKne1n25W4`lIU;BM-C38`H_r+8uO0 zf#h^I?9eI9%h>Sk;Nh)5KJ;QKkyssP{^aC(!mW3Zl-`Q(nCR|l_ljDYlUs)%J(N{L zH93!0JUv|7ebPa2RD^j&_&|rdA$?EN=n`^UWqC#rZ1LTkzqq^2Mxs=3H3rn+^HE_p z-cK+VsD++)NF%Y$9RdxEttr>4%e|{qCNWy)=|h@^unRw+zBYS>2K=uJcO`%gNdsyy zyT9;iKi0unf|jdj>?4)R&Hfkt3bzKTQr%JcLh#E5v~yE zqq4*Zf&OdDu)|W~_=5QrQHeuC;xA;Lg^~s}6*LjNj7Tx3gV6`mHTaxB`^OmnB@d!P zQI4e}xJn8{(B2^v|lWWlZpgab-=NJ&IOmvE)ji z@~ATvrq{?7Ff(GB7<{q@q+T#_CSTjZ+$&cru;DV|WKRdJ?OK=IC^^Wa3jg8ksa?Iu zzAL4i@+5XKE9ov!sr7Vjnzz8g@N?Cbe>4?8^u~!huVgQs^Av?sn*~Mr-#NB71l2Yq zWnY(QDjOBE5%n&L-Ip7#K4$l~=CA3;t6VPYzg~^Cv!mKJ2E)#EAJ=*%S|5_>@K<*e zpV|M?J74#C-&vAj_tW#S0>`zYz;ajS!oecLzGAi5ugV3Gg?s*b@{%={-7SVj#wTRt z2a(>uUb;HqU;PA>34avC7Rri`puHWP#aO#yq%M@0{d+BZm z1pe2H6Zu1@jocjsfc2My9R@?uYh|$xlL-fcuVsoE2U!zEQl2m5Zy$tK8r(tGeM^x5 z3VZeS0>XClznf0xEX~E%QgpzKKtw8l^=FBP@)N!(NEQ%ZLV_NGlQV;@U~Fc6^54OFUt(yvO$;Wrgr+;XnHQvG}; zIZ}!_T-&In8!fBf#QI9^bkh7j9YT3e+~37o`(RfGLF-6p z5oDc;1zD*N3o`4#6*zh)af# zqZJFxn$Jeui?Cg{)bj*i`CR2Mke}Apvm*_l>4k)5o<9C!Rou(k?m`Wio`0^Js^%(o zlt~y8Uc~i<$D20uPSoFz_*DM9Q|>1#t25c@y8?tyLifA*@p+nnG^Lb!6)Ii{(m7?x zrVYvYrgxyykrRjDjtHsHF23!>o3&x>H(P!g#LE}1rL6Gv!c8XEizUp36-N0Aklh74 zm$NhrDDOOLgfiBt9fF>E0dM6k8L`|c{Ii|#_{*Yz0o^5igqtDrAnAZ4bn$);?359t zgS+y5$hrZrOQb|X21$7Z&KI={y4X=s?hsv;xfqcsI_B8XSF)IG1$8O*6c14-5iFGs zZ|`J0LCopY)iJf~I_r!qq-ZR2e4Y-~#l*r%0S=i}A^uV~(cDo*KFSj}qlIW5W)ZHT z2#1_taZ=tj;0)cFwuh>?j>M3SX${C68(rwM2Qf+uW7l_QBbCgiF@4j-eH#n2;d2z? zl)L>?N$3(9wB)gzZkUHD$=?3npYEvyXt4LAogub}D^9gF~B15sSxOPlqwi;tKiX69gw2FBsFngqNk*WTY z@EXWQ1rP^xR3?&;A#U_(H~u~sA)W8LT1p4cXq#5ZG;t-ZPT?{ae69M74F)u=j+1AR z@kE@sj{?x?t%v67H!HkO;4HABo;6DDNPt`PF-G}`g<1V7(@&KS`mPdt1dxgegg_Hb z7T)Pvg6>|%BF2uhLCrAvtS%s^ZaS1^MEr|N{EsG#>@Q4k++o(LY_MFZJ4W^$%!$pW zPdOT>TbmSUP9E@{!$6z`QT4=1fsZVzGxqw7yS`KI+8-T=@9pqnyTQGEVmHVKah3;K*oxc;K?0IfwF-fE`2NjQ*(*@tp+L+Pz#`!}C}-WH zAv?Cs@A}^B!AAKt!X(asuNpnld=@6#Aa362nNx|-__qH^bf5}WUT<1=WIg=sgv`h7 zi|$c|)hQBxH1o5L4W)UTI*dOFqsNY&B^dHd(uTx;(i+gn+|55yn1ReFQ*48hRrUyHsyq~m*_oH zWv<>91j*vqMY$ewr*@T}6Q}D5x?tsWM0l7$JS0$B5`)A{%2bjXFNs(zEvxDun?0LW z{>o;<-?n`E6zIM2SbPVtzqAK&&hE4DJzUt%>&_=cRa@^68Kfj7{dV)pyteCoaszMj z0JyCBr;68DNYj`_iWRq3Tdd!xZ>JLfS`2{Uf!?NVrQ6LOc%b*v@(^}#Yn(S=de7vc zC3`5-p1Dv;EG1|+*}rAu9j)+i@(_IZOdg!EYK}99KXW27cM{6aCUk=b`n#2tEq%Rh=u^z)&G=jM%|;i3 zk7MUconcwwFK+*HbZ$4yo~!Rh&?7CgC^;!HN41~8IQz>K zcKx&X=*zz4@?NNM->p~5)n9vkTEA}a9-~hV?*6?AHuaBKgB98%&kjX}m+>cx>#jnq zBFQ``C{j03%Ae4$$5lIZLw|3muwEn2xkYqvY0*TI@0;X+?XKbYcrAm79- zW{=HzjuGD%b>YIAz2ei|zge>)5U>Xk99b{{TLfPbS-?mga#eW4-eEp&ByDq;1?Ht_*>8qf?D)Fc^NpQrtQ^^jDaOM;EC`I# zlB}kYBjFnF@(!S)P(i~8`iitrI;omfvG-$ip47pASCg zSP4iH0lWST(Dn@rlqa~#jr7owSM5Ig?Sfmc+(bgc{wRz#v3I?0B;R$~JL4i{=sZYb zS*Hv#>a+d@>*2KNzidbAJmsPd=~a5YX+nFm-j6zyy!C+j{m$fZ@7Fc6dR;#ctbay1 z8Xh6I=k|#Ey0txB=8MpMjGb~I3_CXl{-uNvCUVEkF1vSTp19R_pxzpSAdiLJMc>Xo5H^(o*~XfODUP_(p-b* zKgv(;JUptVL;IEkUJ5J0$+(^wmt^7f)GSZa`jd3Y;XxI)`Oz+nT^W`t$Z1>^)sqFf; zu6sWhL0(^wxG3dprHqPWxw6Ne(p?O_s>NmU%|xec~99qUahq!)Tef-`IEH? z&~BfP5@f~-S$fA#Hp;O7(G;TN~DDvFm97D5%M1~{P}-h%AfM&AG5sB zO*DDEroUo)dYAY<-BF1n3wsf~ng+!C_v@;ZZ*9+;C{OHYGV|6l%i=PM;G1Z7&r{4{ z6=E9DNzoiY3kqg*7xm=cWU7TxcRKT~m_xThYwC1&dsIdX)A4v@qdmrb8Hw3{7Q9Ot zvyPTf)@5hYmO1}gsYY8z$XxIs_102YUr`>UOq;nkMEss^%CbWIJ5ZRzwApDZNZ=qW z*h@oJ(`sh)DxB3${=M5Tv?lmQCXz-OcS-PIw4Hk~O9+`#B#zqo>~qdcg7C1DW)nkL zC};}W-waiDazD`Vdy3+33B7VlP(A=q_bWVfznBx(x-pZW7Fw3)AJP7c5cJx<>J$65 zmxd@|qdM}uK7{9cg|kST$;Ax)%ikSzgwDyg6tt|RI)9OO9eh5o9^75no-_2uHPAt8 zHq2jcUv@$XeyrdmhU+!-`xDzbX=$&cEeg=*2G!C5S~C`s`KH^aQy0|1sv4jT;y>4n zQXe|j4uyOH+x@Vc8e9vHo2Uz$C{C2vxmV{hBx=Sh$nilFoTTOv!S;$|% zw0D0i7`Nrtw52VU@m8y5KxZp_8;B1ULhGmfrpk$-gwVpq?cV~%Ho1I~2fFUdr+LQw zgMe;dRc$G+_^apIMe~$}bPnF2Nx2R94RuHN?(s-sxMA)U<;H^u*-V7Yix}5OL}#Ev zDC@nP$g=}#_7Q8xAm-!9 z#bA=NTHO)Np~vw65KZl3apBDP&tX*aT2@~3H+Z9$2qI{^Tg|1h^_f)4H+uYA@iFu) z^VjLAKg<(1E&y)6Jm;%@r3E|qGAM-bC(jYZyOmd4#k;NE{kSo$g*42JJVI%?|CRwC zPX^M%)Ne0&?R+y`7!;{{c(By*PWb7Fmg0uT^6I(e?Q>1e%MjlHGQP>uy%;oTmqfBb zo&w%OPllCUNzwV?2!7Kyc#2Xz=yemDm{>AB`WR-v2|Y*}E9ML&m;a)auWVJ-qxQ9m z`SKieqg6SC+kF$`O-1;w7S@h+n8)(g-0DfeOF<5RIC=JbKhBqdWZP+ysy%Vb3CstY zJKu(Iz3Pq*7*?Ebs!E?mkv)7!f|%6cdxfA&`w3!ij3Im_sc9gxDEljfma%~ID3SzX zptx0H)RfQlz*)94n)0Ux&lJ-ACNUtcq*Dr666j1Xt!Thp*d2pEG1J*1lecG3sV)F2 zkoc=cNqNlP-ro@7}PX;1f;%Sd*%}C};^xsB*T$Oz86Q%mSLeMglnr2e* ztMI~MEi&EH7d@6d7LAuq(6#Q<+IR<=ybnZ7`P5fHm@%DcSw0xFJ#2G_h}5MLL91s_ zhz4J56@)l#hYQ?lXo4IbD5qKV)fd}`cGE@+w^BnTt6X=&`Eb-+=$CA2xpKNMd}Ei$ z3iVNjQ;Oi0nBXN@qKCR9^?y3~0@Wa9tj{Ai!y&l&kL1(?Gj!%c*SE59KsZ zWMln>!@xWxd0QFH+EYBH!Jon6+*MzKuL zdnF3EV+7qV+HVLaiIaGr>p=UU?>iUlDwK{h8H zf{4#!LV{N+#PM+)Y#7gOu)=W@nvfUZ>o^&>I5#dgQ zHj80GM9WKF>x5EFh~fnFI3(yXt;by#=+j8-1C5Sh9We(S>Dtx>b~|~**TFhBm_yv| zpb`{-gn&|6obm~guiO`G|2}UG^q#hdjyy4bU=G;09i9W-$lsLZ%)+uhOCJXlq!0-f z9-<4-`A_-M`(0yaolXQdANGHyC4DbY8M%h9{hcibY5g&vm~x$3-CcY^QkuD|kFI2N#pWC^YX zzpj;2G2p(oGU)Q=aI}MqOOYt8r5>&~F;z8=YNp%{fMpKH4Gg)iK{#FH_ot5N(V3EcL(V7UuMZ z!;|bgekPC*PeJd@o)(pBeTU5)^zdC!0VTh#))V zq4Lz4IJWlXTON%?&S%#Cb6EE1;}vt2a__~i3`b|faW{z!fxc_1G*r&M_L2fr^2h*Aj;YEzzJlng$9Vi?c5tPHE zC?j_J?vyfC$IO)T*uT?pAKe{+l9-J@hnypAm5(uiBM%AcD&g(y{E*Ts0wT0cV{0Gh7_$+;c5` zel2B_Yx|*tlTfhcTxtz2555;A-4cnFK7&Rc3n1XwVmh-pvV*9%(#SfbM}Ti zJ`YY?2hrNy(}wzWn;^SdgsX7MAod1JIZdD+^9@4H^~CW3c0Q7`IOHNRRT=(gJ_CeDj?gyfgg~HOzG&i)0(g5@s zYC4<#B_U10rx7&`q0or-DWRPs>8Z*&A!g|P%u?J~Jm3d}9LSnHFu&574FS;@d5{Ww zKb9=<+g7&SCY&)rX87}I75nlf=Fh^}f%xy~jxTnXM#){$38^9&zs4MbjU$XkG%&ZI zDj3a_mKn?&-NYxe>~+7!RAaQqdL1~LxrQ|0x;f|EaOUXEElIB|bnC8Ax#JAHCq zFf9piweD%x>3<`$8|Kb99o>*jRp#G({&RaoI_g=$_@Gt36*u@;6^|Yw-wf7{8j@y~ zv?7j)AFrm@-P3CQVbbLt=kG-=I12Le+zxx=Dv&S8dTEefXSLjPGTbZ1$^58mIK@E7as;^&NeELkn4BbdzO~cyZZDLD9~Js4f86O9ogS-MuaBk& zy0#u|BS2n$aA>@ZA-0wsZv5j(s>IU$RyQ09yaSYcdHtDTzNF1p>)W66yn9fZ%{OI> zs+Qh4X!m5s0+)c9E{f=3Rkd>w9m!q$xUz~OHC`vv^W{BN#*aNOpEqPl%Hi}6^M*Ap zD=hwY0Rca5oZAUhiRk{Yi#RuR@*Os5@cgpc_RM1n-o(~f5#8vCvhL8PT1ToF$GSI1GT1!ru+BN&Ctu3L)`u4 z>=Jfq)Alcbf@lsLthpZ%ivR4MZL|4ny^IWmv${b<9rHAxBa|b9ZBrz^Tfo>WHLdl5 z*3;t^+?^n%nhT3oIMM6+%u$bJyIaApX`f{MiX33>=)Kq)4uaYFMn(~IvDtyYfw=Y} zSZ#Jl3z3-W3#rR~Gbu|fbz<1l+VU8|=*3KNd4fo z>RLELDQV>7?t12$08X|@(y9W@V1TBp2-oBxOcJq>FYn57?j8}eR4bHj=!30hpa|@z z|A8xfxw?~MJguXfxQ4H&K%h7MEU=MzfcL1D;*Z%jhR0U=2QpTVZ z%am4Q&hnsfXbTdE({QBsXL~{S4+@AWc`Rd1TDAbr15TllpP0PC`u_>+iC4gPvKGy8 zPO$w9(BVd4sxNG(hS;{Nq$uhb;7FSU0vvS7Q*a9mrFQ|3!WuZu05Pog1;v{zPWfu{ zP3_MPF+l%Go&(T*v7S)B5Hy|Pi>NVUJ)`ZT`mc{Ug7j&>`3XwPIdGWfj$IK#8Mfw0 z-hX)QTQRt zhi)R4Y;$aTJy%Jlc>5FV)Z)v5=8sIUPI#InAbNOoq;_}066|(Q+_3HlQoagV%vIEv zl)x?!qGOKR{g%|KSiP%8S;{&f{#iE!Thx}erJM-3QP;wO{7J)vM1)%P5r|V2yxTZ0M5E?E={QC{^c3 z^QDe|S=JG2NTg@7isXf88PT1Ex0kW~=$Y2v4n~{KqTR(qF8ZCECf)j+lS9jb>S$**>rA6jSLhYy&G# z-%2lu#pcjBO}tYADCH_CGfg#ddG(QJ-ZUqapKiJ3Od-gCE!*f?Z%4LBVH44$KP;)9E%>~tA(=4mzk4fWIP z89uzh%Uwlv4$Ns>t^-ksqMktVY^c;V>U6*pG;@|SAk2kS+P~TVOsu!typrw^0n_}; z;G1;|Xm>lZ-*RBB0=WAOr{XKsf1M7P`%JWx&#_SYMJ@y}&xod_D26vN`x@{R6u~Js zWNj5hbmKg#jH#XuVV)sA_Z&mN#2*`*w%M%T{0hSTC`vzkv@9`3u$K&4gppzlapj0% z*n&!)A}Y(=5yEn&?rj{!c;bYP+p5N^ko0@C$GwAOgL6b=T5!y*k72?Lw999 zg0S^94J6Xc@QtL_13Svp@c|U91oC_=5p0L%?oy)c@aFA9moXC`@@Jam4_8HsQV0*g zUPruFr5cZaVQ&aBh|8gC{!+>#$_lp9J@Nc0U&c(uc5kwFkr64yYw6`hth}xQxQh>I za*m>Sb+OUhLM*1EQ)%&Vn{UJU#E!ARh5&5O_UJg+o-0JQAa{%$5_=Q)*6y=ZmP38r z;^TZtTItOx*vj#&Mhs@&En*id6%(b6M26xc6&a`@_ zEvb_F$IA1hezhidA4*dVF95u;C;9Cl3P`g(i@g`=MG`_wz4|dnS*~2>W{Wcf2Q0@iBld1ZpHv* z1#)UK%E+lK#X}OpxhD)Rx@tWpTQ=>91S)w>T(kVIQ<>s!soJR>T?N^XcE@ zxjA|*+Ump$zcHDmHT`wrDzPy(as4Fn;3$u|-etG&bm^g}n|c8bwy6qF-&B z-j@djDOBFj5NGVpYNy+Z5cmMGG%9d&|gDPd>ScLG5V3yGr<)EBYk^RGUZ zN`7cR_aNO%0xx`9891h@dHYT7m{bR!`9FYH^i@yE<5jSo?UM~+T0Q*fr}=KZMo_VBAG7$|gZUWTnRF+$82m|w)7l8ov0~Rga3l@*jOIo(=EO|{?ZH3N9F*#fXh+hW zLOx=I0!N+zPm&1nzZkbXhd9R4Qe^#C`?u}QfGr;TmV{?b{`B>kXNKxn8x3`CGJ*1s+}$S8P*mrUIlMDwhBKvUOmWR1S^ zNLinH4CbpH+VlE(lKo!72S0jl=dIfycCBFL(3^Q|QAJ>na-##9{h=(FTuwvwdw2Vk3ouuz>oTrSo+VJB3AOB34YNx?NFH=Up4hU?YC*0ocSOUVvVMg2 zQtMMNHWf|eCGvERXex)f{GrVrS0t{R^eHYo1bqg(U}&OwO6)E6mb~e#05KC^ab46N z?4lVo7s$}Oz`sU9VjU%6EBdVlXJh*oPIngjh}03w1BAA0`;$HezQ_{X?&~lR(zVEi ztr<0KRR8az@`XJO&$!gS^-2C$UA-zj?BMl zMLJj4uXm1S=T6k|!&LsZ+XT%7Q&!xfmj3mM30bQLU#r~U+IqNk{DITP-?uN}-#_n1 zOLPfLPA{F_9<)FmE0v={rBKb<$ySk=t(b~37dvN@JyS_#D1b{!D25xvmFyX!Mz5us zlS&1l(4~)L%ypc6J~r@`)%e)qy2HCWhCS|kZei_LAQizYtcl--b~GuPl-%>iV$9?w z5+#kDxHV6j>(2Y@mgKsYiT1h`eB_JMWj@3S$zD5wn_JhPkK8c~6%*PfeUNwpmMOL@ zEN}wqiMm;0z7`1*62p=|NSfaLB$1TQo53bOcc{sPFpf>uA}z6QBoXM>JE%0CC-o9G zoq^Wy?mpV=nv1dztQWyU;Wc?kIG6(<)KB&|?GJ%^x#3tuzAl)gN}R9hR}j)>IityI zo{HV|)!OX!6k>;G#nmyVz_)1DCp-K}Q71>ArsWFIn74XrX$7``(G@JDJJdh-&_d{c zVwx6rtgaK9F-YFU+OYZFs*f|hBGBa1*UC3z;(P_xjRBFWP4!zK-CEz!GE{kIpZ81) z;(V*Ew!7cE7cyxRl*fHvG;pt8HO#9hs6eZ0n&wIIpO4ImS=QOO|DK;-8_r0&#C24X zv!-do`PStyoVKc8*&Pt=`mlvi-QSonip%Mrj!y&YE^BsdzIn0jiVZrvH&igD6ESEM zb*xn86Fv7r__t&6lOC6X_9SvYvR_3)h~R(>iYjDOX%gpS%s7{}qD3&f0bC}JuAigu zdAHYjLsRqzV(h)X3mM)HKWEsD_y@AVlDxEg{dpV;H^Os%Vu(9$JQX9X{V((a$iIJoW(s)f+1a_T;eHY{gee~U796aVS!l) zOt>#nKj>+=qVt@In$0Ci1`W2x(fWnhb9uN8b4KwrWjF!fWX}FIO>0ZQizu_&JcV?l z7yiH2qh)svb-y{*zxV6n3!Vg#^S|Ur4jW7yk*la>e{ZwTMKeA_XS!e>Ws_mb4`@Tuvr9-cADThI=B!IxeYrr4Eq zeaSTq`3k4LW!57NNX(m!TZ(Meli`Y+h5=hvDfbEIp|JQ{#+;6`A zU>ooB>jz_=H}S1;R!aVM{$Nnks|ckuA{o5V*tO#EKus|%rr)jSl50Y| zXMn(13i%tnT-&K=H$Tf@J7MisrVn=&Vm|hLP@Hy)f7xG8zvl~6viiqN0vyn(`wuq$ z5kI16IekeZO=r8xAE)$!Cno7sp_HYVT$F}DPLuDWiipK?89B3(bfvLqMd#F$lFio= zRc9t7oyX|kEo(xBKST>H(Yq@%zWHz++xlZkDLXl>9aQbZNLr0Jn+0A$MlA*Iw#c#P z)B6zRNwf#&27mNS{=APB7({SCs=5)cuD|QENHTpTP`%^H+PtAwtB33(xbKtR+9sPV znaGsNyw5#8iuUoJtRUd&!aoa9az(rny^`tXVs{GN)S~{}OlYbf@_^ASn6s;vkbW_{ z%E(*mi^Wdr`?a0<$TK$xvZyuN)e?&c9C-L4`oro6!1xDo zviIlwYtoW~Jk@^jI%L?GdO!b`;zVg7tgE4O@zqrhh{qZPhLDcay2VOaB}*P z*J(g$MQy_1ROcoy*JZV*l_e4v#pVt$1-p8c)P+Kq5?VIzemE>$HkKO!c zH0GdoCy1MSJfxLdnZxhIyxz^$B(NUi*PV`_ zk6j9wm83Vdrhe#7sjTKwj3-fR+c)OBM%*uhsxO02PkgyG3zi_?kGK{ks1aaS#OpmU zRdtnchDW!{WyQu9i$26mC&; z?s?e1p~%7O?XRvD{ZZZF?q2osLOJnYI|Y^Djcjv!H+%Do@-c{gsZSe0S{N;PR%SG3 zIQ3up<`3Kh{*qNMjGxn$)7BBsxO=0#3^>_<@khcHLR_e+M^dn}9{eSN&U$stp0`>F z{MGV9^12g`ev`QMU)8;teLfs`vr_MmLLP@k^cgKYnTYJIv&HiMk?G#IZqo>Ow8p4k z7E2?7u(R~%{7?i4}W)6{LpXsVN(Hg(|I1N z_LJv{^T{7%yXs=jUI&rpe9^eL)`Fh~&qRjl*rK|Mb8=cg%2e0CzDSt(A_b@pw)eem z)s(Cd#3>vDo%Z<7u&u%ls^uIr9SeIvYaa7e=x+>3A^4C5bpwP(V;e&xO6mmN{E+zq$9BOucx z!9CE63j-KO#9koZ`kj4$aadoMS69muql`h3To>tAK++{aXS5v!4Ga z$EF9MHrus z^e@X>;L`)Y>{~6Cicc6ei!d(rN8E?9Q)Z4;@Q((`#^;+6t0FVA?+=`*es)+Uc`Ekm zMSoZE2zAm{nAx%Fvrp?lH9x}D`ny{>iPHH^I~oKYE3vx+m;H@wmMoif^6z(KJBVLI zzmx_&pl}`glcQ*NY1b8lf$$V)J+y#>$0w=L0>+E4vDfCL`Mn#85&7iviX?sjxFgWI z+#We`etg4P9Ae)uTYORehb79GmpSbPZ&Gkk{5)w!cZqOG33#C%~k4$KF19+?*7V2ZYMD|SffA|!_zWJKjAl-9)z9Vr? zU+n;S7fI`TTcU~Rcc=T6G+2}JxScV8Z9KMun>cn(!_jjr+o|dIfK>F6)r=bMMb8={ zsrq>yBQ9I8Gj1isUh_%}+0wyDtyAdkgMpr%{o9;18UDpo;Lo@5TN3ZE4J%*BQ}WZ6 zEM@o`BU(BZ{D3yb-su@>q8!;e@$|$EwA_P>yqA_f)OT^G&uh-7utR45B*=)h#B=Sx zIAXMYG)Fk+$={!XMXSU=zx3?;S66nP3!GQ{U^Q^Ap_a$@baQaxg#44&@bTa1WUH`~ z{2z;Ueh*C!hdT(?7$T2yWDq(*mlMFMShP-*E!-f_Tj;i!m1gbq^D$5IqyBfg15;u@ zyE1n0a@GxFhhICNMXc>#|5_yVe7G;HJA)F;CwoL%jBi{e%AZyKPX0}-ITV$nereo; zNBij~>0N;=@9XWJ7&`uJhtMq<{NlJJe^ETx^su<7hs#?DIad3Kpd5$NJ3+rB!-_n! z;(gkV`diWqq4;vh)%|vNr&4YItJJQ$%U}%~h~SDCDZlg(#io^!mDvkooRuDwJx==s zdPYODE02x=`fuWXncXhwAN%p>pjHL1d=AC}maNBWWPB%hsl!$ZL z{T~35KyAOOJ>&ZQ!Y`OFe}Q)V>IU1a9=c`|?OEWf_RrKw&Z;hK_=SNi`3*hRFVz3S zub*W)*Zb%fvCC+G4!RlNP4q;2mUKeX=r)XtEm7?=nb?TjIL3L{ZhC)CI`^jc7yi>( zwdod!hbq@Y6CWz z9lvPv`4`QGinC>Jvw`;iirFC@*8Tv#$k&e0*>wM^`B434@_y~C@^i?`pp8KM$?ji1 zt^5xLjuUxK*XkRe>rV74v+If6RQL1LIezY!D{ZZJK9w&68nw?7GTOtK61;ssXTBTp z{SW{CJFY{Rx_CVY=yt(?r^U!z6BF1PqS(!K zL>#^tM|%wN-rqWRpAu98AUn#akwQZY*gaU9@J>LP*E!S}Gg_H9Ng^2O!aq^iq!sW3)kp|PBu9Pwp3o^`q6h(+Vuj|zATS+MVpd6Oa4|}AYH2;@ME+o z%awlQuFGmP!RJ_`MLy-7FSmu{;)Aez28#)5f1a;%*$DVxR1S<+{XMX!#~~%Cn{0fS zV$+ydgOaK`?sw)L!ILz7pPTP|@)ic)9k*nVzmpQ!dDH_hv)Sjf5<~h_cDM}5voUPv zkt9y)y}Z{g8oQ>B9o+g#@hUMrI?hQ}u?;)fy$xH;kNk~H)>+{?TvzkE=qkd7u!Auk zzxGQqi`*(Z%XRq9su15@)p@PUdIZW6Ph1Do4uih`nvrD$D91kfq^B3KA;Zd`r=Qno z$EfyfPrrB6?+0rQAJ#kQ3;HGK{CbWL`}In9UuXVP+a`SVd)Omc8UymP`LX%jeVQSE zRk_u7&~-G7udq4f4s=fH+WA1|&fs-^x6fn>Jh=I=gLBH%4uEZG4DAA&Bl^C0{^}7A zu_c=}svhy04%WGYlU*fekf5_G=-pp_t^p%19@U7{m?bYY)4GQV# zj*eD^90Q1d0hP{Tm8~$9y^d{Ywg7fD+j?Xj zBYm4}U8w7I&f4Hus}J3qY)EJ_dR`mDR=4V-89JL+4XwPRb2-9u_$dioZ zBxqS!m7LmnuFo*KiLADWI8W$Q8?a?{K3rtrhsSue*b>D0La$efTf({y$xO1;cGbBp zvS&)O6a4Ab+Cu}-`vHV6dW>kjABR5K0+NdlHjSqkFB$41$5P#>zDG~ve0|@{L4B6_ zcf~d~zRR%vm@+!eziA(cf77_yga}Y9Xnf!Akq}-Y=m*z6Y5^VJBPU61bISEIUd4|M zF2}AT&`}_yW2&dDVO1W16@i@s+Lokp#2!yn32=EhN5C1c7;D`!TRPdYua(lvxGGp))lc*W+`b63CvrMaCtr2+OCa!mpJ8I_ z0+8K)RO&F|DDx*f@*3NN`Ub1l&VA7N^&H=A^ zwJj6WxbA7Zd{OBpXmUG01_B&MXJA{{m+f(^$pY}&!rs0cX1w~YDX-Ip&63^0ZfFxZ z+~Bfrq|;~sw0^EKuIY%(58UDWH)8$YRaw{{!qBwjER2 zS%AmJ5l`W@7BI&sqYpQYttHfjQ#wchkG6ST6V!`qGRWGKA3ph9jX;RNM*6kx)$3*aZsx3Ot#ITH7jptR9l_qOO+M2w(}7pm?l6DeGx2AXr0aj^;Wh7(MK@t*9(wt)^^j;Y*|8D zi-z_433LtmrANDmYW!K5U(fMX`{(NXz7S<0dmu~er1aQ0YdTlsw$X*ZmVC!Ji#9l2 zz4sY^mYMWuoXzOm35)|D=SrR!=aD>RP{so(zlY1dL(%Q?T>S^!H#(2@HQ)#6w?NQ} z-gpyYo`nrfrs*IN*4hy;TbM_y9phwIFW2QYxsFwt(ICToMFU@A)TZ3Q)N?|YI)@mB-3S!X3W=gH+rKp-;kX05U<8yR)+mCrYAfJ z_(U|!iO`MhZ$qfB-VLCS=K5)y{tNyYIgFhcH+|i<*kv5iZ(85G@)1UTEPh~PImSHl z@;J|c4jQ8sKV=_S`jI3B7y}IGn!vqahF1$824~wkJLJHg*E&~x5ttD$=|^kfWTekJLd?;U~ z_IjJvd$-2$?QpL~B^4t=g<&-6JZSMH8gfp381{sCu>C9 zaGY1cjbxbqC}+G-j?7j+MGKgM&(B$V^!GBct0P#@te~#$x@dg@B<^av@uMSl0cI<&f2caUp7WBojuhkfSH z22W0#k3C79?%GBhT%K&mHr4WNcTU?n3{+p`RoT<})&|P+HL~rwP+K;k<5@$NoApH9h%TeAo1-1ciR)Y! z=eZ1wd@!-7dQjf1h&%DTE-#u_@&m1#OatMf!Beqd#W#n_E8E?s%d z8}A3=JE%>8BPPQf67A-oaR8s}ISFh5-GmOqr}`!~*imvaPw+vbpSd?TodIhS@9wFcNY-s|`jw0`;dZ`OciJ)kl9?c8aDPGyPKubUhlsi(Z6 z%CEH3I^)xvtHF62w+`&c!KCPeKXixQ1SJ~CFxq&%4>=0Ll@JrKJ0g%w0p)u?T-VoX z`%3>Bl5INA*9#y>C0L#Eo2~(5gS7=7t+94J0W>{md(e;SJFLd&xPJd&?PUDYx5Ih| zK@0w|qpoSsNM_W zbnXOnW0%RQF~s~>udw^8(TRs~Ka#mu^y9U|Xv_;~zU&101mx>n-(k(34wHJ;8$Z_^ zwzY--X;y-2eq0^Rr~beze0(%N`u3q3yJ*dGyQXVc*cD|{8+GlpQ8ub;Yc$7po_Je% zI(HyPyUoN|W~0_zjV0DZ8^2j@hQ_YPX3)^S;4^vi*i;Xy+$wK?NJLs0=NY5&C>znw zpy|mle%Y4l@^OMhm#?$DzHaS2u6y*f{jopKW!85(X7qJ&-6PXEt?uJdA4v6U<$c)* z!9Qn5LiVS8I-uDWOR*O7*yYzZ53CFu858YqX0os@z^NFO*Bio`!X+?K6-=2dj3Rkr zS0g29lA9-Rqc>zb151s^PLru5&0j&|TEVGw3>FG(L~hpCwO{-lR{j{sw6dc!F7*0`XwI2sGlalSI}m8?<9mTMs?d_ZY~FA)_c`O< z$Nps8chJ0(yuFQ4!GoH(uECK(*U0T;J8h<`K~TWx89PDa^*Oi#jUU=nr}NI>#6ItEzTIRkjbzYc6$OHdcE zN@!`*m?lH*--X_2ccI@av_r_fl6%phzQO5gA9{Pzb&qC$UuW&9e5<;!As%34Kqvb& zUfkB+C)9;LWXXCzS*e9RK4 zuy@OGxrqIc0gw||svp%QjOXw=*67;060&T4vHoe_?d5F@UZLi}7w5z+v6k36j&Eaq z(Zd493;lzi4O%xl`E|`(*{pKz6FPUhCUtfcPd(OkGx)oD%0%PG2BR}SGM4q7e;$&+ zi=C{6*gBzKLG!uMIG?Uld^ih}F)YtOj~!k18p4)V#(Bo5Jjyb1{Qyl*hVjX^RF{tv zB)WW^<@I%I=W*Smr|pmZd0)mcBiCix9wWLyrWs+WZX1OBP7mYJu<~iIr=uTY1C8~2 z8}mMP>O6?p?5t6OieVf_FoOY$zv$)y#sM$}`50u-cRG9l670PFxCjO(Ug_@bOjt~^ zXgG}xdo>vbbOGqlmG!0eXCE|pIW$W_uWr=HbNQm3v&ZV1{$_{iTCzWQxS8_ASH88N z91xtx&Mh#1>u+|jc_b?XyVY;W7S2b!-o_>B*YQx(#pGpX+tK z;@7!g^w{EKgdhLXdd}EYyJlh{WJFtxw%!j`e0l9YzyaPe_&6Na?FU%Td?&Z*Va(_2 zae_JrPsB`cBxPGS!04ohNk~$}BF9r8e`DTm%e`bqU_ywlP9agUfArE8 zH5&VNZV6QT&iAIVzk~=<(wJBUIb0894`^jW$kDlt?WlK}2(|)ISLLkgS}@jV>UGGS z==M6v@pH+cUgOx;R4Y@!&JG9u#JBthtM7Pd9kF#?Z;3d~Q6KSjJbWx2p?4iozYVZ+ z*920@67;ldP~SuJhEd%J$e#U<>pnt(J>#7DG^h_?yLPCU8+N_o8@CDjl?V>&OiuDe z?0yXsRI^KZY<+Gzd!Jif(}%2?zHEA+ldPJ6uF;9VwoM5tJX`kj9K!aBYG1o13E^Rk zjr00SYI-e|d#~9u^39Qg(VUvYPIJ}Q3Q%>$F{8@RuTisx=;4;H$MhSB#FYnKou6yW zVq?JQbYE!wT=jE4wV|$gp*QMUeX46~u@cmvThXX}qDkig?7$|CQTUCIvFdi#c)8<& zhI!?Db)B>_+6NEUnf?F=cqbsnc#L@~t1+Bi+dNv)MuO@SLSRzO!h|>^XqyRyG04>< zw8iHmw%|m-7|4l}E%HiYZQ^D*((e@ZHxVG!#>HI8xwWx8%>PS4_UBd<67rD&5rrt395e4oB9QrTO$G)zn%m; zN>Drbi9YCRV6-ZiO^l_W5$sy!m2OpDd2(Ho?scJvt02daJEf4SV-um+=kpx>ZUv-s8o)?jbH&y_!45`#v}wEv;~}}Vv(G-WNDQ&=_#4n zur3Q3Ca<>@+HLIV0S^lbGD2=If6#WJt8WReK~Kv@`sRQQX>k&I`D>k*fN!8BFpu3M zTj2G>NOr^2e>U<%QTUiZJteR3Y_wN{s&8`4_co)Xk(M-FOdpr9w)5^#UbV#L#9 z#An#p2hfe=z1?txwqtHg)c#3hbO4Qi{|+|EQ9$BgOAdGx^vorxem%FE1Wjfjq2ZP$r-_7vjZP`K*^83wO=s~_ zC?iq8&-~zw+XG}@=$0+ZXf|b#v@kBR%>y(!zP@I;OMsRDcvLq+1k9tl{Y3p`TM{)J zw}bis^_8yL#QdmzG3HaSZ$2H@P4y}HoRNh->f)Xj=M%d3hd#3nSu?(?dg|O39Xba# zHa&KXb9j5YU7%MriBoJ$>wBfN3p>poT?d#GaZj~#m3g~Ha(Bb_GDc&6Ta4tbAs;g*nSZ~Zn_)NdpOY({Dh*j_(4+eZ~X@6w}jjcVzEA+3{9E}R<2o3RQ{w88l5J;|;a@0tDp z2Y4qS#yDf%zcjOw=sP zCV5>5485(3K_-h%2k2=BydiTW7j*5PNA-(r=@+B`bpdEYhxB(o>TFo_x3x)hwrDF! zWuf=QUQhK{@a6AIYB`HMHw2zW8qLN9(d=E>*4Z_pwYsgwM~`U7RtYMDJ=x;+Y(%D? z!-&tY*e2Qy-ALZs5r@*2@Y$&D5HLF?m`{uy=AZ>M8uu}WV%!q!kX-TbE9wp69m(7U zT@aE39|b?Y7_ag8`Gl78sH45KD`*6}R{4r{Rag0@^)92)RQ^h1K#yZmZ*!|9s5b7k zt3fc%?z7eq58%zG@B45K%=O(o{vLud?K)W*ex_eSuF%`KvO&n@GZ^iluG%<&(JLt$ z)s0}|cNcmkFT2R!D^h|wtPfayrE{BXoGWpg;S=4A{c2n0V?4~rio{s!b3X6s&;^i% z{E=?yv2yfaKlieM&XpdkThO@^7|$d{7LRD%;Ee#lz0>L-SIV_q1~wb#V?DZ;a0vZw4i&++S=QDqiS$0Am=QSYOdcd#p^cSlDua z1H4n9xx$!Fu|4;2&7HXzY%W1nfXQN?&jYn=s|19}iD5qZ0(v4wLdwF_64gNLaIEWC z$+2Uyb+`zCH1XR)(!g~>$m`L3kg!lUySPkj8Nup8ZU%DwDA^OGG<{?M%=OA=%N8`eJKrC1>Hrf&Y6saov|6U^8ZXHoN9{zi55( z2QZc1`i${t^0cNl~a~oeBjRLPmQ_*yC z-N}hyZACM^79Bhe0ziVT2ukZ~(*9jL0?Syh*)g>;)^r8$DS&SYs$}$f=MZ!&+Gs;Y z8xz^51G)fXgEo!oMyT}6`Eh*@Xm^pnSJVX6#--b z4yw@)Y^Vt;daUo#`5K+ctol|7YEM&>YS*K48%qP7Sx@5`=-e7MmRK7maY_5j6A~zk zUr0ttmN+MYik()|y{T_D1KX?rRtP`Tx-nmem=_!7$9V1;%$I`cjXshs__?P|Sksx# z>wGpK6TevuNRp}tTiM6{P(c3WA%Lwb0zSqLvn%}2WSWj<2R1^-Sa5o^G0|x45MPy3 z;WSRS)`kFnU_APmM7G`{QF5Bba>plHc+941f7oedw5g+SaN1S>ccA=d1o_(9H7WlY z+q65v{;nNs*i>)m`z4?60rKxeFXz+iTHlvXQRR&5D&~2|n4{0O7NRl;L$Ym?YkfG24WRS^1J_|VCL-i+T3T*hEK_senj0CGUzVcIN$FO&< zfyoJ?7hfRC96{G((C$q%8i*r%%+9F|8OA$jM=yWW-e@bb+;CPB5q z8||3&qodt`dUXEU3L`ln(WpJWOnf;U*{2;x z=DLmVqpV!Pp}r$lbz6cAwHuh&azn)v`)n7CAt331JZ;g$0S&A9ZLtUKHH<`}* zrRhV~obQ;s(FQ*s+TV1Vte|6E?&)>_aukz9(v(5DcJ(Z7@%W{^xj{Q<`#|PK#F(WW zHisf9oPfC{f28&WUxz$5rl2lkL}M^LO1e7K?*v1$k|CXaTas-dQ~j;$vn|2*H!cL? zLH94dm9MRgHHU{|wpJ_omwv`xo$r$AI-4!nW^^qUvSZuXh6Y;M*89--yiRg*U7UlE zk8H-CUCTG>fBL@Kw$4|_X1(ti4cFL2;Li2v{y-TzpbMQkI@ZcL?gd%SeqfWl{nT|N zA=*8{98E5d^CJae(;?bsQRzCe^Yf^ILsCO=MY8X2@3nx;G35}@2= z^>CjD))u}jpGA^D-2AC)d}-(Ue#eaN-p^~mm!dWP&aQRZYRg8MBUrnl9kaf4=mwc> z351{e>rKcqGH*U-yfN+pKAD*t6B!5C8wP${98=aO`tFhiGC%}5)_RR)t-l=${B0Zdd+pOZ?u`tk#z^(w7%M~qBFUIUlWAdd>7wuo~6LBzLHl_ zA24!EVErZ-G~X*21lEs|feitZzp`T+gl#JzpP;Y?%A27bPUr`9>oA?({&ibs0PQaH zdj(IUJrEw&2dK|}kQ0{mhlD51-G;3cc7F=jx+GBYxM%`JC>s8990iZ!{yF zL5Hk--qY;}z3KIP-5BqZ)T2IZy3mJ@bR4u}ThK=AXa1Nx!DKc>S@T1*k>jDDv%`GH zE^xd7QIo?2VhSZywjLYpWPT@m>YCH*au_XbP%@hR3-GP{Yp@uxzB(N~q3zQ(CG6Ik zLcg=_K&PYenD1IUOlNOzvlCe^Z&l~#0mVX9uI^E$PrsN=-^(uN>3Hjd6a413qmOGa zcVB}B9nL5D4wZB6M#+3mP0TcIjuqM5)n5jUYNN_-#kgVP<2_>z2FYa(Y}>{Fc`X=GR5 z5EENBwgj_T;9RmC(e}L}{4ufxV2}J|z-AEFhH9%}8+*{T*6y^B>9rX1uP6H~&f(82 zqm>yJZ3oyWTKF6;Ypw4YGn8+VIRbt{E;g;TaS9D!w_zh_Z$@9%(?@-OE%^8ma=9b( z9AICuIHs&W^x(^b8;yNiPlm6x$DBashHb-=nz8xs7y9& z2mNv@@>vIx-E(O4gSru#{e!v@pxuRjuTbJZz-{)8>Kkk=n$Y>R)~KG=fbDY$yn_3r zZ{x4{N)J2Ml}`8EOlN&o_0_sL-$h&XutVp@Gte1yw2d7`@$@?P0LlHBC8>FgM}kTW z14v?dFlIWXcVOSvFq>nX3cNzh+hypG9=rBB`DE)hj_1%Ukc)i!2e3QLS0u~^9cibP zD`_-)=^NemU7yycCT~?9WqIziK4>SJ-O`qrnqNswKVp8h_A5EZ9*u*#W=jwJO^>f3 z?RCQHAI)Rn_Evt^FCMoSSuU^Hm(O)=bjU@%_p|!L?2}y9b;)!DAO3{L(DXGq3-=&& zKx4F(k8)_Mj0w;|Kk)0nT#x9CrXiQ_2_b8yd{Q=owPjcyK*lz548u01YuUJsENoh^ zEBa*<-2_|E^*-b}+dS#ZULI{v*LJS4G zuXHz?WLJk=m+D#n(UaTJd^UiR4eS}zjj&2+$MroFsO~_&SMVQ;N`@F`gZc*Ao9XmN>Fj_jIA}{qw$?=y6$fJa%+R&&ogh)ECIM zb%h`PYHtv*t}qt>YYvH{bdG(yHPD4GkQF+8ZV4oA=%e1Cq;24jK+KCFX66;peT;Py z9a>XU+hqq2|E$hv+ODShxQE*@&{ON2#`{md+DrU`Z0(x*cm--}jVs!k=dvcCKI}4{ zNKhq9@>nw}t$r*CSy^7PacVbm-7na~*o?7m@+W+WSFsj6(3lL#v2)3qyZ;!!bcoMr z;6o1M3*H9m9nro(t3y{Rf45-CH+#iM-mda@p$~n5zAj&HgE?7qXmneQp)KWZin;21 zvAJU7e-FRnYs|uW+19}x6O@H0zur55wwE)X79P_V_LAM_`fWhtlfA7C(dI0F39aoF zT0lnn_5%DPJMe=YXY>zsTDx>ZA&4Y7@CFGgyQ!QT z>g(-3enTE>Knf%d3CLkSF$Qd2)P#6C2O4+S|%9{kR?l2_4!OXE>c$9zUU0VF;(newaFm;`)oYY#hE*g;aE#DaMm zG6lEY5#_xd0&UJnby~j|6Y%zQJ>ZFTDalxZ#uDR%^DPj;@ThKIK|fo+j1sj8us_rJ zwfxr88kX$W!-ib9V1wjn+*rScPK|*99WwndXD0FkSCCct-N}0vkUXqv+5a zxUl{}7-O9TPWI6b>{@T<_CSZpF<)!96dnB>YxB$>Hq%D4YvUX+`9loo^dnsKN3_-S zk(936!CGK;njLx=pnh4ke_eLWcW-Nk)i*!Whxn4U)6T1{l-G8y{6+aX^nH1=rzZJk zAL}5o3jQ)^O%`p|>+-M@p9Zm%yML!Wqir!YHqu?6@-)u2V#5j1oZ zI_GQw=+1HjR(BLPk4R1V*;J@&feB>})Lt4sdS6{aYwy>*hre~8~ z3VNW;pif3+8GU)C-4)yLAF7UbGop_iTa76j(|Wy^AdUcnWYJ`4o(AUT&aPk%aWmF@ zK$iup5nQ%67R+W3SaNU`!O^B96-?-v7lNHL2YkLUxAiwK5kT_>g+p&o$Oq1cJjY6> z$qKpDA=B(vppQ?v20AApupp53k_<;ePFtm8C1=tGq3YQTx6jT4xn8#5ciQh|P3H(^ zw|o%o-2~C^Fi!B(F7o#Z`ds5`**5tl)A_Z=e@6?jea?Q>G5?~hmz|)wiXQp{AI5p6 zv;JuM=({=H)P)|k+h|7iN86xx|E%fU54b)03lxp_8GH5uwZX=;%_ThmVwX0O)SRet zZufW1JLvIEKh_<2zKkFrIW*tQcg(B$5X#pvx1;T#rlag20i^%%1$t>qv%%%+m_b>k zjWVr+jj!_RC>nzwr!*^m!D;Y`cIa9ECto95^;gh_9TU6bT4g4e_QB714*AHAF&liD z*T?DVyH3m-faIr}u$-VMNS^p}$5>dPVB+ZD8D*ER*uUU&~PrsLkq z%s%V8r3AHh>}L2t@avKC2ApED^%{qQ8Z&5wUJk}ao$+t&Towjwqm8yO#IAyLv%n;o zvY#Wg0j>kyzA0?M*mltfTS5o+V1w+iYb&3$OKpbM?Hgdu5AelLxHk}}+Kp9qKnL&E ze#D3FE7{~Q9}H=+E%fXH8<@3in*)rs1U333m+hr|FPM|FU-I9L$o=EzJCCVd#`vx< zxw4Hp!CdBPee5hMF=Q9FCdthE;ESM0P#N|OY}6vXx(<5rc)PTA2)3qu)PqTOJbni| zstqkkC1E#vV_Z?+J%0P&<+>tJ7a24AJBt@L??e~%^vfK7Jqi-^pPG0 zF#n|QGd+GjvOn4;nxL~|Pq#0S-2IoW z@yR;E-_`JdjqiK&YAy3wb2@=?zN5(lBI6s4_dF*?81WD!YDd)8F?6tgX&2S6^=dO< zUD0`^X>kFzaQ`347V3CftpV85`GKHVt(DoBkUa^C)e^nraE$YOtumv9 zm-ew9GPZ0iww=?=m-&sZ#z-E=Sounh*^uP7bAZjzU@LWsyRdz8Jam8s9Qf|YM$DHO z_g1F)GE7jJh=NUycD^N0z#`BakRUoOXe%gygU=U`2~VEf95={s(B!W26+Fhm8nElS z%Myt)Z7|tdB$B=!-cF9q?veh0wMV{mTNKRl<$qh;1A5(=MbG5w1{l2RIOps8DQuXV zd@QEMr<)is`5SE=0sGMvHe{f`r~TbheTC2_(M0n)~h7h zY|yV?@_HV-sTNpbmj#*HO*VgOZiQUO#HQRnqt^qyNKhO7)Yb{_EZ|ML6aNS+nHg0U zfw{i7$U4yE&qGNNZDLIw)UUw;(f9t>?PLq`mtj=5pRfeA-YYiP#y!XHDbwp2zF5)mh(`xIkw)E|=IcMdXCyE)&7W`0D~zN)ifFy~ivE3`KB^yqOpLdbQrbrU&_#u3-8+~{eGuYvGvvvaDku5s$? zthGPOY4SU$4-?Jn@2A*R^bQK0uPODG`!U z(&c&vGz~FvcbTl*%=|T81#Nxcm|lI=>u(|7+MzaMo64r`5~8h6yJ!#2hCbx8fD_Ed zt2)V+_9POYO!%kmpTO+tT*jPYr|GO{R?AB0yz9^2tr$riC<;QxM5%z!v`Yx-qun7|uG`xrMBBZ+^X)V{pbuLN z>pQuoKZ031S72#86PdKflkJHe0WaSVe@`I2E??k0;AOPGu7i20q(r{vU2=+{K(QkQ}m0%9|J#z~mrK55L$AY4gR# zbP7^hfCac#Ipcn2>}OuHqXzaKND@4ugSD)!X9lya1-O7Dl{p$Zndi(kWCPk~a%*sE zez)LkMVs`CXT`TBCzmCj)?TmM4Z&-m4H^?uoojI23Id@wIjw8~$R`;Z)r}x}!>DdQ zLBFeSHtDjtGNJ3QJLrdgX4;D%G9P_`ERx1d2lOLZR;O`fJUZ`vZvCP}Q~hD`kU!Af z^q@PRONPm$@1gUyDG`qIUgwbv`jlNtvs=I)%t4#Sdaz{kR_jXaSPB=_wTFA9`)4xl zhv?>zu2)dsv4+`NrsUXcX^Aqn@GIT6p6JJil$_dH^5I(F`o?w1|9)O=TlL4(h80%y zD>VDy3tm5Oa+B;8FFejCIbNUX@^vdW9_9k=VT{r@)~=SEHu;Xs$9gFm>`8L$IMLB~ z%r281x+H@!9M?X+hd{>yB&Usw9f96a`3w|`(GH(~>-CgR%k~3v`ZgikJE{kV!`{6@ z_U>Sh+LrqsUguwv)n7YJPanv1a}cfD5Pj75mFm*CR~hHnjUJ8j(T^lCsY19AWsXdj z`8r5xKdH8b8enp0fUu=Z_-r9nqGx=o-O8E(<{eAHlf$5II*>y<&^z?c3bQu-7xP^X zWC4Mr0@_HN!!|$XRbQj)1MzALGVGXbN!ah;U2>c4oxX+sr~?ms_R#+<*wS$b3F-*Z zU!%GM9GMvO*%6VTGA>zcV{Rxht$`v(?I~B=a>jmOMJqTj zzKMV~0!e~gt$(aB1XSqcn*S#ysAPxN>XaO?#vvy_yO7Pi)!eFW)T=jPV~43-@VgAv zug)otKr`q^@>W>U&qD3@b}Yj6TRWg%*|`C%V~n{`-3X$`SA)9!1bsulTKk9f0jaazt>?`GSQoUDE2Jb9aBk&FU~Qez z_fmOlWbyzGpHJs}UL&S+ykbSa4%r1?2IYP(-*MD=M)hl>r!44#HeUod_{3$`A6M}Nlpqq_ z5je^w3x4@-8-W(|@DdE6tJh82ILGhIV{BG{RP|;|cpXza;R^&0^4H}>dB#Lm0(Au=saS9{hGfP ztJvD1b8CZO^Z0!{_==bz0Ilkxk4NKvg!y^JoFeILe=Vc`JGCU(^u)t30sH(lV&XHWIfS!MlH|0Du=G8jWPjQ@Y;C6kF15~f;!11$&2~FB8OROf_F_{wx`XP2zXLlOEl~#CaIi5J ze0-Q?HCs$(w#6~$Z}C9`FLaxHX#LXY8>YG>XQs^p{b)QJ$G{9i|Dp~;={|qqV zc`I?G*zq>RrZMrBfkkyPM=JEACjP)F2W|pw(0T!Xg3D~d7~opvv^TJ#6+L4JxwgK= zcVG1@n;zimm0v)jYC&e(p>bQZ?$Qoqfut7@oVA@+{En>)nAq=_*xTd;R=o3A(Fec4 z{laX+KGi2ZBgmfCo|S!zu%u`wdgRk5qq+vSU9^>cb*$|XTDh%ptaDk0@lSWnW&pqF z+$Kx#xTbuK))2a9>oVVx4X(GfGtN7@tZz=Yd=Pr1(`d3ka^Jch64N=I`-aflpYIiB zv%Xu(-?uno9_xFiB#3}{?r{RI*3wRPH(3it%i(GKtFYDTc6WYOze@3hUh!4vAx;o@b;|a)bgNjx_i2~o&b5q%lN~W z92rN}7A2>|GLmn!8StW$F=>2?cdT6NGJNWjWNQXv7+vCX+uJpAYT1N`Yh*)bHdY!h zN5&eElVk<$8Yp)i!iZ)7@Lo$Gb51Kd2g1 z8!)6xj$2b^u<}XLvmjgO$xAR3cBme=VA_&d|C@k}x6N9d8)^MoFBtA9821O02X z8V95L4G0@%?R2zplrb5L0|ZUa_f{j@bAWZs_-50Xc*~$M?(-v(KEQ4R!6R=%C`gI5 z&fAdZL<9!i+7ZE33$|2Fy8}*(4d`HfV{KzCVVzSzX<*mIY^uSd+Yok`elM=ZToQ+w84Q6*=#!OTxH1Lvkps$NDP0^=W$LDncM7S@)m?AsWnch zzR_#zvSB5!pAUgdvqH6BH0HBvN48-Vr|?>@;-^TIc_3%{-^Y)!(Vv|iJtp>;uj+b| zj&6f|%XmZHtbg6^kmE9j{SajnAQKtZhum)%XKF)zFrvg?z8#DsU7K#`W2~j?HZPj4 z71Tc0RolmP@L5@IYrtg5C+L!F)5Ue@8^Y?Sr)@nb55)Cc{a&Fq0kL36nK?fDzAL5VGcE>7v8!|T8(_j9Z0h9@!0CzK#TG!;1W;e{*TO%2MMd_9JQjf{55Gj4yX!UE#A|iI-$73xf8Q2; zyAddurJP_hf?bolE|0oSH*~Rp_89nDuQklkBCpYM-T_^+i+zIYowaKN!j4V-y{7~< zqvw6Aa3t8U-`LOgEXdk4Cf+jSxyAQPwL9rMl-O$XMdLpL5O33DPDH>HNb)0rYr&h! zX?wt~kxN2Mz!(W?3MOj+Y3rQXMDX}N)32Q>=CamllM!&e)xXtd=-TdEhTD^a&55iT ztjSqGvu6g<8-AYb5(JE{hb0r0eo!|8^h(&aYhSzywpMOqpp9pZmnrxmoVMFN2l@=z z3QB2*mGynt16t_9ChXAlm{0bN?2kIeZ=BILbuJF149VMI+X7^>>E5JIc6<9K7>>^cY?3jmKU5q}+3y6*_-Aem|4fj^ zpT2j>gC<*7G;jK+g1Twjd>D=CJ^+d!$PvL;4#*z1IDNy6ZhHJBn*VE9Xb97+bWL^_Vec`m+8tXdO;; z?RwlE$;SRI>g01|F{Trp>NIZZ_>X(b+PtNonoOgc>l=rw^L^TyJ^#4>@L?*g|`0EBskjHy?I_`I2!;GOC0$ z*`qa1597=R+2S_PZhRDQdnQ13J{r^?fHtT-CX-{xZ+a|dqnvS)w<4(HV$9}zbk2%2G z12&elZvHI$PGkGX9y@iuoVQJanlP+4K|WDOiUOjIg*EesUZ1QLTsxvZbkMmoy4K~? zWK@B{WPvwu&SINrCxgaxw7Rfi2E!l*ezkcezrq$pc5;0;cL-!AS$T15dbjZTMp%0# ztJfd8II4X+=&1v%_S2|-1Hzt7I(-1I{1M1wE03?fJdk-aU0*(jU1&3Q)$?vc_$Pd1 z$3t4w4|T4Lz#PXmTTAMX6IlS~54|SiAKhG!Y{)@ShSyJmM?hKBnM{43J(AQ&Oam6& zw#3FjK&Awh*F9a1WMNlB)vtA(ogp244KOr5>kEFwrhz)a>)Un3q-}=?bWDH5sFp2Z zML(|Z(deyhqxv30dOPR)gGYbd|LSM58?^&ST=-R0lP~C%O6!-Te+~SoP`rAnYqKw3 zcR+64MOv?mha2shcA2cvcMj|SpjJQB@9{ym?!%Qnqnqm+^gu^jL+4DpB_rySZppRl z5zj0-`ufVJ$wD6ez$>nR^AFqHr`#c*4BER5;McF?=Dz0vhOI4Ms-q1#Y*i^2{=tPLZW+&G`CH}J+9&nwKPM{j>Pw7&5m&h)A;z5m<# zDS3`uHi7Ah#1t59?awN+*+JX5w)2TULbe@cXRPnE*{>CkHGLfvt5n}byH|4R^%@Ul z_ys#wBDTCGtoO7)Yt0^pH*oi5Vy$F}lX!AtlC02}wahx*kHhy9DWu0UISb z7)ZxP{r3I+3Fmd6bDvLK*ZXn@iraUG8oVU6=Nh+275OHmxEv}dunn>(zee%Rq*3z@ z3N{M1%07zL_XWgN#HYtqP4+!*{xYWC$#*4t;-NP4-(7beTiOe_5Q5X`jx4*CY?K|_ z>o?sX7JJ0Kw85=Pnh_FOq2_k9{jeA^+cy19LLQE0&4J&Jw#lzlMD}3Ph_yh#ZXuL% z`Qc;E`(EDU$x`DCr6z>?M4HXMN%QNEns4U4xs>Yg3n&M=cAJg6zqW4J%N$}abzvGb zy^n;`t{7$4U{hQyY3`i1Bn%4fZ?YYvt*5fv0VnW6t2fJ*wm#_PiJIOcT*=j)(Mu%2 zt5X4>rGXa6V3=mtTc-k}4m?mD6a>C0!^wP@mb)7K-LK$l6Dd1*q$rUs`qwvt_KA13 zv|SMsdvzIN71;ZArxc{QtJXYWxzQy+U;zda1no2)swlwgi7yAAMrgG2^&NCy^X)$c z&RC;%rq`~&WdlaD@!+@y%GXYZ0B3oF>H=f~xX535^RjCRT@y01u!fE~`BtZ;5EVkV zKv7mmf)!6@nDofVVlQnkgY~bKfz$YX(l`~&-0W@)Or1aD* zoLsM?VtE~8Eq_2mSU%b^FY;2u7`G(4D$=jcWwb}U-EBL$=baLe#mTrh?sgU+-M<0w zoGP#N=)7%&ly4aObe%L~g!J36kfm{!|--Eo6Xh5mzhm}YGN#)F;a>voPe*GQE8$Lp#FPs=rb;wqMp z{}O~a_=4*zA&4W zi>mnL{$p5L{Qwpn9dkpZRfrvk90FnRQrn4SZhUvov{^)3{Cf_*amI6a8zkRlX8q;L z%$%ihn$x<=NaI(D>MfF!EGr;Lymt1qSV3J@@%-VM82c)f)Phl^OOIQ?XF74doyHV) zUtsnn-x}=AxtI!YE6R6JO(DK#-R$kG%l*ZF-A66F!$*~93Hcqmt3FO1e_K!ijHI`#pD29{+rq-M@hf)lsI5U2GM1@$Z$9Pqy1`C7w6B z-Sli_u;=b=`(!gUjf@e4fOBPk@EU^rZf6cMpFix8vfEvmY7@+N;huY@bSfem)LWD% zG&Xw~-Tn0TUknE`ey>aol{G3ET>(Nv(DgH4y`+&M0;;()}rlj@P8rnqiJLGK+G2;yF}Xq!!~r zJESh0I@eZK!9;84qd3Gm*lg|zCx<6rJ>T}lvpwyG9;$r2qwqU8r0fnv2Z@#os2XX- z!uvas*ldu=c{ZY$cF{!#V-?&WLDr&7Cu{DuufFZZVkqxUQqJ3r&5b&496b{K?qwD{ z6k8wsZG^k0S>-5S9<$*537R{?ntT}&_eIXHBevH^EmJ>`-lR8N*ZRYcA}U|4srxAb7=IjdBOw<7PtZpTX*l5mB1G>ALTcz z39v-SCpN4x+FcRHi)u60aN6b5eh-(-A?(;oP@oU$@p!r9funLMoMgRt`C(9f$6peE zXwt?mlMg$$Tx~Zd@21KH?k>~13R9NIxt1-j!=5nM&{E&OAhIvh6z{uJTGj+xLYKH$ zYYO_T%6W@O+Du_r6Xh|`4eqww{qnU1;o7^ZEjpR;DxR$O&f*}Vy&>sqD)rdg#yRh}D?D33Q8IPT$-+d=qE$=l7b`sF+&Ox35$f{j z#8Z#uoXf~ozv7E)W`%mc_~vZvd^3E_@Ftl<#tc4FCLV_y$ba7HPCm_Fpmk2~#Y|=#+_Y%0 zOCx_QW6kQ;43?7f;~a3>^OTA_uZ)wcbSYte+sdAc&}(4O?jv{GNZauYAw&6G#_lz}7){15KMPr$FK8D`?(?A7_!|7q@caHd6oE$3GdZMl8fJ`g657pU00GMs-6y-+u_mBvX&O z?rMtnaIP>MYBH-$mI0UnL(0a9pX>+~|lG$qIn=O-nOi*6{Y2MxqU%J@?e}(=)KKirdif7k{n?bG9BuTRK6MH4 zV99Pz?fGOs+h2X08j`A@`R=($^qmaXc%)}6I#}J#aQ{%m%{%;=FC$qCX=-=%*!syS z%!m31lHovWT;SybsalJg^V$c6^6qo@ZXv~iLX(CJE+N3PEaeW`kvTQ>C^vWOYdHf@ zj+l?hR3>6EXH;TSMc?)&&U91jB2?7MUY_q$+(}5;X>|Hvp%fGBvwGo$vUIMHNrBS! z-nkWi&Rw6ybMZpj%yy1jUM3kDpRNlbt39yJrbC32KO?}6Qginu1?$P6 z25rCDRazUB4v46FLt3EO@Vq0H-+#WRSx*Tm{{))%V-h!-qqN1I?c-eWTM~o|U`1+is)jU|R zaixJ*Qts^$u8YFz()CJbOrAVON$WciY5Tgf8B+&tTh5Gr5be80SEs2J+iU}ikIXL# ztMzy7d}}?rFW&iPcm`o3;Ue)>RA=u|B+sfL2~gWUy(l1xCWt9MC>GPm=i<7K&nBM< zPS;u64G#f$G2|22iX`61cRmxwTB!yNO5D^*m1WppX}+Pn(3NZFvCCgV{4{$fU+>XB zI9K&k#)8j{+q{|F6wu)E0)QEQ{E5axX^5aZ;a`lcI><__$Q5&q|XS(KaS^>^{RtqT5OO>ei9 zXY2``iZ_4m!qMz`QcqHbUol5tNe9hTC~bHM+C}wjlC)C{vs^hGuDk`6(b=IFggg4s zW%8ola(qYIR5H&*qD27A3G<_hn_>i_7^sEgaON=Ht~#ALjVwfG+~MyIju{~6xdwj!(QGZqY$VB1Y<8l^ zq$@Xx@ymFNM794r{DLx=iVn}{XMG;cfJS?8 zg{4p7>3dTb{zIKUB0;mUq>_Qnv?V+q8^M}EsSio;ABAtv0(oK4#5f;cgc|N9caQ_3U)`$ESgEq(vIlmKCpjnRLw=B zu;tW<;dsAesfuWn=k#%@KaCO-ywX$XaQlN{cTxHIT{^b758H0OG&#v(EV4b7ZoFjC zy<LrSaOc(ou<|CXkwU64O>Z{Xw$?dol7?77;Pw%M4*z=)SC`=ksAtXdX`<+`0%V zSKq{avqLw|vY|I0L4{^jRqin4W{JCLFz8m&I!Y2T`utll8tK~p39rafOA}2_5l-}- zsBdi_L*XygMdKHiKxQkkbw_#~wRb-1T?gi{lgZV%E{?r^^Zdc=W8DY30izZiUG!A4 zTlam2`$Tf$c4}T z-9}|}j&@sDDbl4NqzvcIZeZ-d-of6 zLrVt&Jjnhnx2hw&t2kY?H0s8ngJPRLrBA=g_0OLmgTK93M2o0Ccn9rH+&p<)LSfqD;R z`&!oWebC`8hU5GP)Y4>v?#T}*=DddG{r9xkFak4O)Q5zejQ4~tLQ~l*&-PyK(u>CA zBEgF|v8j5Ac}@QWF~t_BCK$|`n*L*se?nYdE1ci@;1j+)ZltTDEGqlI#U;;&?!4!k zYAuCpu`7F3Wuq#Jr%iP(12Ix7{tC|R%BCllJHhjwo9zpA$QXWfkM|}t$P2LHcF!cQ znWJS+1dza`Kttw?}2e|aAkU_Yfr;P8@@4Z zsK8mkDwFmThJ^u$iU?IxC;|q8jezyZ&_2UF{x{r*<_*tprR@VwQvkdKdfpP?bYF1K znc`ji+#_O&C5w_e(0%Vbyz%dYV=DvOjRa|!Wuioi%(RjakjBlOWcQzp0o!ye8o!6= zx3pWMYB}(pYymH^M)l$^pvH&&5FBEzRNn6TZC|tJz6JbGdKln@AzZ(zyCCAPN)Myk zY8<^Z*u1BO!NGsqys2a6AswAj`YKN6`mk7Q=kwy??an!p&2sjV=2JuVp?tXa_^ER& zI5Un4gnQM_aqvc-MaH;qF40d6!6mbr4tE@`27(9~u~HN|!&y3Ui`O2BA3XA#rL2fV z$GC(YqSQ%i%WwW+P8)o7$II83wa47XmAw4_8 z@A9+e8xjUh220GVXM9@A5#*6PxrZm(l7a5EU$oq@s-Wr=gih#?_Ok= zbB$j;M+qW|x4KuNodrjT-Ea2E58N@6ATt&!NE_+8Eo32DO2dN;4Csq zY>dsS`@&J<{H431PakZhb}o%3rl={hz$60m5@GPJ<@61tS0AaLPEfazE-SIa6WGr0`T@b6gopNQ|B7-ZOoWqNZgf)i zw6E>K$VtZW?}}#Kldz1Be7EYW|5SZd5D#y-HyxWwmel!YRD5}6_h<6{vD?*83Hhy0^0DjUgr^OG@;&O) zQxz8xbqZLPypdk&tA*%^6_ruew%{8VKdzPz4kbV4>dPu7KMeQwefhS;6@O?Ux-6IYlGvCZT2Bytoa>e9eLfNqKt4#b*oCiuJ{kF z$P+>AS`+vz9QdyD(kw_b1Fw+0(cCUrsh7-!M{-!cnEj2%5D#qWwcrKJ1hy=)xF{S;p{6M>l1}#lTw-ys3O1qj=}So@!OH}nC|^yy0Kbvi!CBaa8eHI z>Z^KRndpetzEy!dU`84!17(aRi3Ht$5Ufy7D?!xybetbkMD)ID!zJ`$I)THKCAtGT z72U#{x)2NSB(Ln=I#~CKshFskEqNZl)*hZMZRH{QYHUu#h={FQ{NC0emI_?C~!170AQw7n*%tTeDOe9ymzk^ zbRKr!>UFG&w3<&YA}am64K}%BBw0c#BUgUdIGM~an0VQ5gJQ`-wE?R&g&_lUMKRs2LB;Vy(#h3#g^#NdrHgue`Hup?$KK)@_QN zxp}_L0;{(HK-v2|w&dfzc)=aM_HxfCC7w(aYmQ#ZJE)JAX`d_i{@bBj^Vg-#e%B%l zJ6tevde1>+ycE5-2_2uPP)hF84)V$tX*=GZm)cL zv5My85K#C^u6LcJxICfW^Ww_D;I%ezUt^hVoCZ>=WiF!#UNaMAbnv%Re~eOeEfLNND`Q&3BAJe(N&$T;hUFa*QbeDqL590O?Ql%;Es8{nm4 z=tDh%+qci9n%?gn?dFNAVA+QX0W` zWi-U76UTeL)!JR7Mj4=Py0;wRqF!-w)w}_wc^TNg`kd$U92Ht`WPI@bl%}QvQw(?? zZeDCx;gLBuvYs{c=&tGN<+^s$MVxyopcjuQQP?(*((acn1wEK$==OH$R`Hnb7T- zBcDG;=xsdh7xgC8TrGEaC1#^p-NXZVWZqpYd3jK$l*8#s_Q78Jm#|CMY+1SlIK21- z*+zNc!tYbl*SqMzruRfCI)n4>N^U;mKPxt#vb~|s0^ix*M1-_!I0jYBi&x57sa`6VuTwjwcG@YQ=^{`uF%UZW)la8M z`9}wwgctGCv4*$f>enhE>v?5?Z3piGf11oMJ5j%QavYiwT9jqXkEYn2Ku9#F&|PG} zqJv}PJLgW?BRtwqKj6aXq4h3$+ie^)xNf~d<>}NFKyjCWQ|KXv_8ntLievoVR*n#_mNHL~Vdyq=VtPQTc)u#q zL@j~eOKlDeK(B21kV?j91(q8q{>d9TdPU^3J9_^6*P6jkP}St}Kt%TbU{lV#1qkAs zSmYCbe?NcG=)x7yubUv_>t>)FmEaX5pGzssu>ev&g0w7!m|TeJ^oBb{AQ`Ho1l>Y( zPQ$*MSnY?gPh@hKcAUPP(TO^QBcitH*%xuFY%f6M@IzNco_ zRL}Y}v43XtJtr-- z*AFe+bbV%YVMZtWIgdxAkX;4}6uF@VLqtecPG}mpz)Q50HN_ zdohYxTDru$>+wyyJGUeW|bAVYs4wdu)Q)CAaS-bMf%##V;s=ID0N zCd|dN&Bn4}?a~q}E~5C*p|qncQ2FGa&}^6ac!}ku&;J}X&O_ znZ180nll1h$M>sk;gqROgtpEN>ucZM`1H3yKL_H>6SCR;9)A&Al}#Q|WYCOk8aVn5 zfq+yhAhq+9$$WXc{Y0pxRY^t(90qPQanrb&pFRs6mzYR!e;cL^?L-my%&fcbQsSp^ z6zcIik`f&`+Me;*5-b><{=K(*%a=j(=!UMs`xL)G+_g9J0lZ5u_i^g?GKX6Pd{_eM zpU>&}7rjicRu4amGCRHO7~f~FPL}$a7YM3;dk4np#T_o*A{o8rr!;&Q^_K}Vk|7H-1YHo99}RZyAtSyZ+kS8GyVA#8SLilN9LTzR z+Oe&E{2Tpg#-xv#^s58{QIAu_nE6aQkI%dO>hq|mIW)YL&O)`gN3+)``6NWgI;6Qr zr#t4&*5M4af*`KS(7Kv3$%&X1KS|Qtagf{r{apR%rOmq}df6XB84ZA^Q96oJ*98EB z@q!~of!N~^?PPinc@A{lsEK<2F1}UQ)5a(mNz=4sUp=h*_mf|OSg-_|XN&Amp~%(Z z_n_T9RlOmLhrd-PCo0O?Cr*R;&D^nEUhnVx*EtZ>I3B4xI!N-2BM>2`}|*|ejzVy(u3KscTVcG|2El_No2)X2?lx3UCIRw!sGB~ z;hJ~$=J5VR%Uol7-;R22ed3tEC7%z{_^YeH)aTC%e@QyZ`kHNq$s755U#>E98gg}A zkK+mZ>TPP*fo?muimsU_kN~IMuoq}H1cuOQr6}{An2Q&mSN#ml z%^^Hti_GZ%;oNt*z-JfxtDt8#ewC#1mADp|osaF*$cFpc@q9Ox*+-cV(u!j(XObUP7HkVKa{iz;_M|83A3bCbHLr^c}sYyJfxn-kEN!$%oy^Q z7vs-kLnZU7hGvn}Ds8@09yBfV*hxN2Mrm&-7;IVLfmwOuMRv)xrlES_Io>W8UEECq zjdkGMIyN}45)N(}LA>nomH^;;NiiNq3mjB7cT0dbX44PP9NcKt=pJ$7DHaQv88Gb@ z)Bl@|lV8`%9~?HTInSEI+QtYzqXwI%fNScHO65ZZ`;9_xdc9Fo++HK)K5e{E!1m_S zKdzNjQki3_`DiK7y#g=C-tmm;uCm*RLrkzXP(6i%Su2j9z-x^@N-L!G*4 zfAX!c?kHz#u94+EpVNNXxh{(Svr5-?jI|euOjCj^XVW!_6@fqXrT9fM$OcCYTWIC> zDp$N(VltqcTfN%`OE>MWuF&n2!=J-e+1nF zsdhx|jv~?lxx+(g^6OD{w3M}d@rn=SJ^3QEQl<(ZngY1+RYsY5kAhJb>@?#p>);Z} zpNgBZ7rUG>%Fdr4wWD*XxfL*bN1kOi3RTd(1?~W-&>}FXJm%4HUBj_lk93oQ*fjsd z)(6k2u=GsRQ$ET0C;lnWFv~)fgfhInJ8Ow1Blv(($#`muOv;(6U+B1fj#0wK1YeX1 zJUD+CH|BkBKCz~ikL4@T)! ztWxRs3JBWCW4?rTN7|Ng4znQQckD;uS=pW4l%6o9L91!(CZ)+$ab$VRHC#e3o+KML zlPW@}fAXXpabhk?bDKV&I$U-9qb?N3?X@>uaKxX($#3RE zu*9`n^Kckk_G|2oL9&=?%>=e`yQ37%huq=qdoX)~Pru%$=((cn82;Vo_8|7si$U#5 zjv2Hhlu)hhmT#f3OU*JuM>Mih{HPz~Qu>eK-K)^>XX0HU(uaIzmE>i|96Re1pCgZy zBWTE)C072ya+|@Ze3W0`r+gN>8ey*cb)4PDciAj#I}YTt$a2xcujEHaaT07}C#h+W zYl}LoKG4*y&gouVZMfqH;Jr#j`)c^v)^plIz{O+s41OX-kfP-g4P^KsAlH%hho@1d zBBVuM$}Jqc7gh@Y=CP+QLwflHR7A-&YzQvKXdE>dr4{9?PSzOkXZPvVAQ`4^ie;bqp*b1YTbN*=q^ z4}(emY%*56a+9&umYdt}yeF69hB(^;9t#45o6CCSO0^9PXGh=f<31kLNWES|p z)sLddn_w0*$w;UFh!~dw&H5aeA^Pv9{*(6_e45evS@s~7(0!?jtU7MvydaZrpA}gd z>CBWc$Z}zu*uU?H9a4s*pPzD*kKIeNyCg(WKHn=JZ-isF*J&z0C;atd1?-2KH* zdw`?NICgq1YSYE(7~BoVY=*`$y#P9v@W%wQ=GO)2D5Z7mM7G&;ebdzm`rsR~8&=u^ zaCeGibPxJuy8MH@f%srXA0@*MvZTG7;!`pu7J+Xs%kj^;t^PJGQ_rA?{zAsN^*wZY z_D8vSItEqm*vuEgxr-sZQh0m(=sW8o3Ro)4{?WH1s{iQF!<< z;;5Mo*Q>Z`8u!^fKer9 zY;i4`sa?=EuwsO1e(${F`Ngl;z0>hHl^+!Z9m^3HJ6I?qn z7b`iX`?oQs+5VRz6GJRqv6Mu-O{6^K{J6bbXoYD>$DhRAo!jC?F}PP>4&l`n{I9Lv zGPMX(cV`WsEt#6D==dvt>FI|5?R}@!w%txVnvi!#9i7b z4SQc8!pPKvXvlW$wfDrt{VmELq*QTlM-=Z)(>`8f>mMXs^+_%egg_3s}1js_0Lv@0ZCsqEEVvm!X)^`}P1q5)UG-=A+0 z-@eU$I#r6I>EJjF$d0TNSS|dW@g6uU5{m8)U)U$M+t0vaoqZ|yrKoOFYDeIo>c(QG zTheu^mj>b>2Hf#NAy0rLnTp>d_($1U-*4*og~)q}`ureC42ud#%&}BXD9;mw+Nx@E z$;goF_-70e&T5Ykd*W|5H&meHi`3%sWW(&|KQ`kNB|3%&wZv&&zgfi3r*Oc2eT-A$ z(;m;=SEHpc`Z6KR#ZYFI!|zDnSiUiI^f9GBeC6$y>>(~ed6Zr7LQT@JwNk)pBap%s zBL-2f;SEw&?6d<*$XJ|NFWSzL#ld8N4KEgpA>i!|!iA}h|I`5AJ0*jRt3xluBNi9qCi~0B=48FAthNO3$ z_i4K-cKLMKka#)w#TM8UKNZKI=aI`r%}&B~Ekb^o&qz>8dH~VhGCz`*sc$A(*IZJn z7*i$fUrjAP?Wx?$=943m+g{o2l@~Dx&Ho~0AhfJh@)jYFCur|Q%1KfL$2&}kP*T^l zTJ^<4t&A>2w<`{gcm$vob+!P#HXtKbswY|*OufVKSkXjfCpkm7uO)IsjukzLMa}h`;y!_Vxj$ON*@b8qtJKyLSa6`2_$lTb9V4@^*?3L@t z19j^xPwssP|Jry*-lS!hw>=IXC)aZHW3<<{p3Yt@UcOEWr=aTZV0&^dblNmLF~+UQ zE6@3n>#cUhEyvF#imOg$KFS!0FWf#8`?IfOBFr^XrJN6=@s?SRIs1HjT5GRmM9DZF zAmCg7SV@s(rl(J4J)`pL-`-~53;ber$vi2YwNCwJpVG3uKxn7{D$UZO?BOWp z>`6UlGIKhN=4AHXi9ZGMv{_pJI@OI3Zg~*2;Q!~d<8S|;>i)Ta$&KoV(_!;TYApeR zpBb(NN2X&w%IC0819TEFQzx^JjvkFOlLaHK<#p_+N0~&|S`$tow=yD@angNp(Zd>Q zA9(&49!`ercuX)} zd99e2j7Ei%G6fai>3v~ITgR2B#aO%J*P9L^?$>a|qN9JNP(CSE`bFgQ;s+0}a}NQ!zmSSdE) z5bby-6uX^acC13_P|8aZ^qNl4@cmA+qZ{vT=a|vOTUbo=#M{Mh0T~Q>=#JmblZuz0 z~bnnURelm*6dyFT|tYO(kD?fF|Pg zI)N;QcjN{IouqawpGy5+4jVtC%!U5S+JSV$beI{g)l|#&W(PuC5yqv5%sef`EFsN# zuw|kM|NI=b*?4p{^+(@V^tX#&PUx&5(GD-ha&^0U7UmX)-S=5=Dn})uGxz%87;CQu zTLB&xVlqLK1Gj58-JP4bS65?xX_hGSRt8_MIHg-_6V7czb}~GAn@^U#dYH>3UQ87` z$UF-Z1bg{mLFfmvC9p)12GX}<-UPrmE;#3)6vZbw%4f=xA+U_)JQl{H*CiG>4e}TY z1Bw&wdAEP%u6FA${IcsPN(U;)=1W$(!pv2#U|uzSpBzU*x>FI!x# zYvySg5b>>r{p61WKehO_kF6f|6w~pmx!|GqDa5c)xi$n|S=7^B+AeC=vJ3$ZYFov)^IjyromyQr(19nY8RS_-68%7S_b@-Qut5(&c zI`q!w;pO5DGUKJH9lz!CY?HfnuixZY%N@v@e3BHkKt}Jxknh3)5~RdX7&@p*Ljm3G zUpG)i*E+2HUntv*?fRup{fJ*xT{fWmsLE{MV3bUKH^}_P;kP(x%9hr)bDI7)*&_b{%oIi?yR=_PijnN5lA@b684%CzSu zpmpEjo79&pC0^+K9Fvcb(q)XI$%Squ?m#6pxp>U)N}Q#5nSAx{*&~)TD7fJCfPX}2 zV5g7d{OGN0(%!u+i{PvhA}(Rb%|r+mEq^$SIxjjs3QRWFzj!(&?rpA>(6I`y)l^G( zBzpk>eHQg0P@;Cm&d47b*n+#b#Pe8Yy1#6635N`XA$fdhxt2Xtf0Q$w3`BH|eKXO@ zB#(*j3YgHL;3m9VB(H|uty9kA?fI~^y7{oIL#>kiN}KmY<3D0pSmBAJyc~j)#%9KEjz2}+ zf>=lOf9WNb=CEv5ic;bL-S_%EGs}c09>o-ozEw$b&lRwmtCAj~FiBdbXd!Ddq-g%+ zw+aL6RN2JhtwrgN&NWswvExY|hI?D*!5mqI1j^h!-@9;@fyIYMVe>y-M2euQ)qZ;e zAHs;-!qYFp`W*cpN-Yrp)$4HZpxT_O%55)hscFIgyT#<~{ySp9&#s33@N(0=xr=tT z`^EoZg8W(Sli`wLB_E-~cF*pH;s*Qh&90Pc$lX_~siB`km>rW_9`(+zIgcE4A zol(0TMM`kfQwTuZCDM=0*LaFWFMZoH6vv&bOW-dAwxfxXv)>Y)RyWjWEQx)_u_rr1 zYmatWkqPde<_U+c;Wneto47cX+sPM5je^OoTGU^+i|9K)G79F3Rr%{RVnM#`Z5EnI zGj+&~66y(40*Td98(P)3+qqT@XS^f1XVWByrEPEIIAl)U9cLo^0ghAkdvfeiOgeMf z+G`hxbjj)&9(>6tzK*6)_06Syz}-VedVc#!wupS;FI8TTc(>)d_Mejk(YOuv`;}w8 z)$Nuee$R0G@haC3m@KI~sC!X8ba`xF@F|;HL#zu-j3f<#EV_{)OSqA=347M^@|J zk|AdecFDU1^`M$>}X zpMiA9XXOGDhw6Kv&OdF)7;3WO(reZpYC4=ruqSV<|Bm#zio?m+Jy2@UPZ{BE!YJPe z8_ht8QZeC1>%L9km1|HlE4NXit(1y4rIx#&NiUx?LDES!k3hBy*JjrhHnnsTb)GtZ zu_Qh1ojptDmqDg}UB75{Xjrf)Tqr8u5hL!;-*dJ6Q)ht=VG7?P^YY9&_il@fJtt3C z?!^=#X&3$S=ULk4BZo=i7Oisf1{#i-A*J29A~Mo96zCt;bz#(=(bvX8hjqRhNj2FO z09SXY?s;`r%9PKGGzVX|e^D2kclP@EQ1k&DR^&$>)qmmwM&>Gc#?aBar@_H)bs4Y* z0gIF}qKtF^I=6PsYvP8$h6$ZS&`Avu^slYmv7RQ8o?7*o>G`?d&2Z-&xDK?QBz0I} z!crZrUCl)IT?Pr2iAJ#(EUKqG7Z=MC$$P!PX=b@xRw0mbR>;W^}~ zP>#>eDH#$KRXgF9t}NytAK`08w!$NITrAqanahL(7gth$g=T&EI@Yc z=_ns$!&wtk_)H9o8m(D)&UwiHd+?cvu7q=80uEPe*g%7YWKCGD8%Ah7f6gKL zt@VMm)Q?P0ZwxCmDiGxydsK8#7}3%GbfC4b@q--s?~z~o&1oDo14P-pU9 zZ^EwMledI`PJ0q~B1c&L_uk!hD+H`s+WB!8_U?)y=SfqU<{naVxit2zK36i9sSrPv z8Aa)$S^tIVxo-q*0|`BYEc&PyZCzODk3KZdMQHSz38@Z~^w-}nUtnHV*^+Hutor0c zKU(xib$eSO??~($)LTZY&x4*pad+2R0c&B>>M;Ei^L`ESRr*`wp!-qT_gzEsNS(6D zA3O4Jo85=0thf{o_ZtcLcHo5dmWtoodgL**{BV^0b{Nq-@e$1)xn8trt{;-PHs9gn^ zhZ$TJaEHt-l}ljTQ~vThRs1b^>*VGg?W}Gm*DrnGzc6HBECCY2=CxgbFOE**>DKuP zRvu%}LwJtRE|Fli1-o&Rcg$F5VH#g1QaqNPsT$P~Vc~uOq;qi@ebDJkuQ*+cTKM%a z>z4{%ev`|WKaRY8bN#E+RV4$h^xGq%51vPJoyrmQeLmw431T{Fbll?7YwBwKEQd`T zez8*i`i)$N51C|CL6n%j|D))-AEEyLxDl5WE!&|KeX_~kDOyO`>*Pe4+3U{E$c%(+ zDl+cKrgLOxobAlB*V){;;qJb^zr6o|=kxV`y`Hbf>oMYSfR=U3Nt%4VXp*H^X0G_kkHGD|#MBTjkYKwWBf29fXhg1s6 zB~H!KYZQVk^XTTjhU;_a3T|YvwVi)N`V+iv_5FLB_?7gDm(_`6GG9A)_3U}?35##e z6j71hTlS3OgfTF^du!=6Ew?VEfI+w-cbH|DDLH`go=Z}xK^FHc_&a0X;)Ki;k8Th> zt0w+q!sG#ykiYvduD2d7Z`d0xdu3cs8+Q{cOs#be7kJ{1rhnHHCYjqQ3hI|Wm z$QAJ}4F@$W7#$DhTmyzAdaH|q=qJQpVF|^c;dbW?c`mH0f`a>86aO`5}O_2C& z^|fF~&Br;~G&{2|FQ1&R z1l>-xVT$Xb`Q zv`vvP$?I2V?r7{|e#m6Fx+>_OSWI`ir_J|o7I^*;R6PPA(#1WV|9lL)E|vN^Lo>Xu zP+tQeyY2r-U3r2pgzNcNk{lL&ose@!dEw@`OZT0@+y$0*!amjEjPQNe`%k;}Tvay+ zB5wvo{mxyOe_mgBqNJlMba&_3c?M5b=R6FXR5nroKLw+wifgLVA0r5e7gdL;TlmYX zjTL_C_Z6cg27hW>=&t()enTa2QTF#KS#-JlixHsp20rA@o20Us?6rA5{UjAk#h zhV;AnQ_fjO{vRY%(iBG#!gt0z-nk8buUB}Wdi~Y>u;?M;`p^dFq82{zw>D6-2yhT( z7?iy(T~vcDJd?N;=-5CCc!`X?=xpArm>|Z9U)bww?0tumoAQ1u{Nltn_S}wAVduRy z(9Wq-jdudbQu=gqz*02=&Sf<=Mzz@lL)@Pu%^rPl!C%2PJZ4KB3n~vuKGNBxnWJhd zDiqhfHm#5H(-kcaw0pjZX}|z#S?tq8C3Q1|uanp3K`B76_!elj->opYfa8U(>M52U zIr9eBK{k(W_+<+~6i=*ql|1%q`!`X)z&vr6q{JR|CZL{+b>Hn@}KqFfKNb_TdeUOd0`yn;H_ZWCAaQn}NF zZEba?+y#9m*bd)5LUz`TR9XC^vPd_;3W$`NEXycH_iHIDx>% zGoUP8%8E#<|B$yWTQHIwG}Gi@Jk6WIzmlUYGqSZY#PvW%Q3Tpxa^6@6p0$V@lOO$3 zwEOHfDVE*0MHASYJb@2(gG?rQyT3;{r2(_U<~I4;9ir9Mb^UxF@KIy|M3vb$7qe>p zI~nKT!d8x--3)vF-&bVj;B&=9i`5Ihj7ayKktD6!lAc%d^_#vQ2OyC0Du=PpNk0PX zuDC)HyF2LdEEU2P+O1zNzfYp$pS+NcCo9ccwkD%0w@xqB&F-;~rP+U~{;FTsP2F$_ zu<1F~W^e6N5JfD({V;7YasCXXuD2g;ECEfnyye@dcga}nYC=KqWr%XP2jrN-@0Rr! zIy^ME@Okgn{+mZ9Plaawz)N?5Q_5dSaGXvqD9y8FJaoaVT`^s5{hCcDXrxD@yR)H- z{r35|m5Ay=ef4A4ZYfrm11GDbO5L@)V>IffAoufvs-Bb|gig~k>+WxP<+@d|yPnTh z2@Nc1w`Kt($2TW4;V)Pm@3aPaUv1(2qmQOH+)VDgs-X&p=*I6Z0g?!4ZPh8)x}zD+ z)?lpcuI1CS&LsLLFRPjI!lu2)V=5(moPz!_^ht*Q&U3u*E}*e3@Wg5yCmUmq!>QzF zG=Ld=M!44bh%LlktemlcUw5PI0}4(JWcZm_f!b`)!Xn*n7~3UGNXPIsD+tME(hYKZ zgcU3L1Zl*yw%aC2^a7`=SZY~$MwGpQvN6MgdXXxQ-(6UKcg&2>UaQYMD}*ntY)p>i zyP_`%=ONH}LV=Ft(JK(nS|+|l!8-Xu3FE=Nu;los(+raY)klrA zG{===*v|g5aBnz?MpxV}_M*?K_$jTB^)^fVA+sS?80nYo{P0~L7rZJpll#l~ zW7(#1#Lwe<4)v?!YqLb3)6tn?g(mpzTTx$yh2FCXK;G9oU@leT*>=>nBhRkzDcs2% zXubjT^C=igeFW8%Kihf-Cqyp{WbF18yl^`?w$>=}II66?0vzP^E8M;6w|OfZ`E|Fi z(Qm|x@5b(vq0Z?4W*V(j;VV_)UP;?Xo9o7HVcm_ZJLy&QZcY$|4>MseHhK**ePi3P zj=S9SmSKAVmI~WZZbCdiHS55oa&1?hkSI?8?X$DeRXVjdEDkJf3W`CU{K?a@Bh}~o z^GuZ^R-p>t%S?Db8!KXWG__+Y7@%3|%Id(w6Z1^bED!574a-cU7(}f{4zdnyIdy+U z@x2){S8f}M4SZ0hdi{g>4dZjz^t_iJ%p3BhgY6|RV^8UbuTFuhKkLji%GbU*0Wk(o z;%xZL3KjQXF8iNI)UgXTig(@Q%<&kE(KyUHfAw6N`c^k~0fc^P^*e_iMUST++@CBt zB9|e*rvBt+T`sQJM<&?{3B;RAcdK|?PO*aTJ21LF4B1qC2U#a_{PutB-s0f1TNQNI z`_4yx^`>{HJz>e@e@?PbpFPn{Nd7Ks%RcFEGu@Zg`Vr3_mJ5k7q4iXLPgxyGO1qF= z&;9OoZNE>9guJ$b6+zMstsT0&U%+k~1DT&L`c65Otn^afS`LPl&$rVL$FP4XFtsv$ zdf!c&I-E@`O7I+8#KY1V%WpJ$mB7Iz_Ky_{{FQnAJ4jcw@~1H~@iazg{$M>*6jk7t zgdf7$j5k9=0ax5OtaEC$v6orEaKo6-p)Jm>%COoUQosw;UDv(XbhwnD^~FE4hJJJX zdhIPy_OfVWq{O!?CNViGRoZPH)64}=o8y5ZnlsmnFt6Ko6K&THo(kt(& ze-GKFyup1{+x_k@Vm$`ZHeR5=5Jjr_-^v)AMkp*C^q41If1Uae15Jo}eWKwgs^(Yu zef3l#T$txgOiHJOYl%b!6Dw7KUgNI!e|_)uc5O}bk+Wcf)+^XQNpqFkjhFbU4tf<&>E{T`E;L$TKP{JRlx2|Nxq&riG`6d$#^n(fHC z`)Q;36`WfM#Lvt`yVLAV`mcpe4piurNNFXdW!S^E0nhmCtN4d8_oa|+9TDGuvEi+HZmn$4eruCYh3VN+HvJNPcTAd6oG45y` zxpdx9Bj{2rX-l_lCBE*Wfq1&Xru)bvC6B)|Gmj#iRca%@eZH|~pgVS+W9G$N3B)Ep zFmie@k|EQ|V8I41Z0`=2_MaEt)@LuU*$Og81@Qm-rd>ZV^BrW3ckq$WPL!!dcWc#q z7@J;G=s9yQuvN%Kv5^GMcdCu3-&d2;ycik!dUsxGLxx!AQxuE;eHJHV@zt_^{~14B(?Ip&bpteICp$a3E{I2Zhj;8b1o?{yt)sLAG+1u zSL&`8@4PyYDC*f?ey!kgu2+S3!jD3vP>vw@{SS-S!5OXj^zeK~Tx)uTuQw-X|v(y+I*H z0DZr+t9D;OVsA6sr{4DWf2qmgk1JESN_k|1pe{7Zxi4tV z=r0@1QnMXn;dAx)RP)`!V8>bQxS&BIx-w}qfe z@ZS6m%2%WoT2N&*QPtOyi^{fNCj4$4WB)}8zpy-c>k@^&Q>^*2ve-I{?5af&^U8f~ z4mfon(^0>x$Aoo-}3I?xqnZ2OV@`Fv^2uMb@ z{;&DM(NR5-I{#^8xxxuiutX2KLtTtagT<6mmpjo3L~>A$(BWq5Q-ZW}`bIWINloyb z(OZ|O*Uv|)eLe4|5j;q67WPsgz>RWg;M#J_<6{{@vbiQDh9#3L?2nL>E^W1T%ykmX z1Ae)a3)yv{^!jE4f}xbKOEH%fxXf~-7u!!4ZzhmmR;_4#WS)h!K$Gq=P|!57%hiqI zy&2W!vwc5<@;`g5JMf2kpcKRcMMw;9jJ~X(qcq(1l=U8yudjZp7IznCcwB*2-)p!= z>C(7gWb;Ea&8a9kVWuY0ypCe4MInihO7S5w!|bJxXZ;sDG_?`s`z{}>G#U8DD@uKy zoh=kKhmM{m5DqwcK9XO4acapvA*}V2+JpanVa3JiSXTjK{eBwqtiNo%7Mh-KSArKv zL*$+05_ehXCZx8KIcvt>4X(e6L?78Yg4%XR?v`rm6=bQpe zfkGKB_L<<8=-i$VUT_#x+pwuOzdau5T?=!y(iWWG4YLp!)LIbF8D6~yzo(5 z!-a+&2YpL|Zv5O*-7rstm}C5Bn!-=P%j%q6k>=D^paD@H91=0Z_K8>2)`E2ej%N^> zzqqUy5|niCeouvsDkG9Gk9FTA_*7HgRJyJxBJ-O=&J|2EyhjrG1?6E6S#C`D{y$u6Gdn(LVK#EniaWa z?jhA5@!4g&s%?xBgXBpBot+@%2^3U3OQLH&>ny=NZrspm-H#zNI#b>aL6}q1W|PH)>t0YHxx$|ADH05B1frz(vhTs^D5lj6Th)7>Zl2A+nP+@ss@sF-4#UO@LWI4h zHrjRHtZr2T?CcL6)VpHRY-GsLNJ2s2jPPICMF9I@vY&Lvlp-#nEmJ)?eZNKeD_-*n zIb6C3PCOdkiMb^RO8&1W@+_z|?nKTVQ4TT>Iq^_UiA{ zYJZ=K`j@P+Oaxq(E0=5d^bk%q9R`~RO%WT8!h)!)<5@|bkTua`>of^GbO5#wq#fnc zuH>fQ_`vmon_)xiVEdic@&jqse_T~@X2AJ|FOgZ}-QQu$?Mg-ZJ37X0 zsgLc0yI>2KYNUFq8Q1$+iAMUrq7%v!Q|mr2Q>Dv7%0h}4Y>M*DPYXsdOD5#x;LaSo*pT@7PGUC(c|8dQHh3bD$owQBT-ksJxLjT0_sH%viJRgW$M}(2nHgaTl z0Q2sz_;gR<-xB42eCs{F=?u1fZ6P{?WJS45b5>J53H;O`H>I2-IQi)HIHxA`_WI-2 z{0QrV*Q3KU7XGiELPttLFTMdnfAA9%8{K=}R{or|9*LC2S-lX%PV`}4fyh~%rSPZdsZf=WHxK`*1GI?fXpoCadY7ZBG7ZQ}kZ ztXsUbiTH?SrGnopF@R4AL?!06ccY!}KM*Z{ei{ri9lYUH6~oian&vX%sLdB0Eo3a9=~Jn#TR@)%;5#kMC!H zDh-~(*ARL$Neg4npR;X@G?C`TINv%pf~)!$6zwi zA?k2kaqHaaqEk-BlKOICF1BxIv93L{ct5!fA~#tk5n(7LAp-GD2e1SG9>{|rxtuYt zR~cl4QQV9>5OL8^)e8K7t39OEk{>FUyE|^N4IJ9V`j%Rg&%Ac>j@gz`jeq!K`VIi$ z0Rd*0156C;8p>4Lp?%~dXmjMBBId=OttoDuxSemi0TLFE-94tZy}yWk{mblWMg5$C z|J9uM+^fKvo%$%v6JjzXmuWbD=|p|#jiCU+OtoMpo3pL@KV%fsG@$G0g9pRC(!G}q znU}|cuYBL;s!Fl)Oq)Yh?Asu2aP~XDI`8&B%pxE7+0nw+*j>5bA&?2O*b6v+|K;_$ z9VN`Yovt(MYtBwtW|2)|_B7+N5U`qIY-=9jPdQDl2-@S~)#P+m^azV}28M8;*h;6! z&w5UadZ=$aTs*6Kf_JS7H4Z-vhlmb*<`0Uc$F*<(NUol>XtCTF0)Y)9VEAR@aG}f9 z%6*Q@0mAJ=>t%fVCP7i-DR9TplUY@rJ=eZ049RoQajLkh@rl2~oxc|>*bjKsrB@+= zDr|SLGA!RPTi}w9sTxsP@^$kj)LXWp^%Y=J`^c%7sSQAT$gszVsXf4CPZ13wEkvlj zQNJ^97#6H>^xgcb*$|)CC7Yqo>;I1V4y~`&M&7|{2=~UVU#9TwPI0Z*$%U~GM4Jy$ zywvD$u|YVjfRjVjpo+VgV5VY*NfmThnXGcdhgjtMQ=;MZhoQ{N zX>4Z?Kp_9)=C_xpQA|6G)pK#7fK|2bi8Z&Kog1f}@4UHIpAf4(Uvg4}xt4^6sE**v zt>34-Yd)plef{9_H?O7NjGWm)6_$^qA zU^%08&qlssDeI&V7&iO-gAk+1tm=|=0Hns+xca-xkg!+0%O7Xh{Xd$ptzHv;t?JbP zQz6a?K4lW`%K6i0_i`08IDx_MXw3J!Af)ruV7$LMhM&rof8<@u_ebNTnIa7n;e!fN zNjWA33I~kl^Px~%;YcZxV}1|l5Xk=P=S|fBh6wv3=mhx7|{xf+d5 zkAeyDZajFiE_!L|?XP0_U^WIBkI$uPS#?f=*H#g{hD`Po_VTl@HrOh%Or`$No2alI zKj=g(-``+$q=GTk>ixna)Muf3dOMqQYX-FZ!Q95i(?6ada&DV^P4)REh93*)^!GHp ziuSJ*&@dL1R~YMAi2jB%lp_RNsC?PRx-YqSsD0nQ!8~3)?cQTwOLVFC-;s=qG43?} z2eNhwIM9O?Nv$ShEoAw|hPVN$oi0HdW;$nCb|r!{f-(|2ArAuR@>gMDVA?0BG44lG z_t_%vaZPE1hSTq{x#Q;gIB%3XvqG8t$p&#-C7z18)E&`SUJs8VIyc?g|8&h;ga0Bs zO*AF@^6Gw>$%K9nB}1k*Xv;V96aw^ZOllrNf1R~h2?4J8X0M%-(JHG3AAsCn_l{?f zu={HrQ=K<=TcL2M*oL}hIENwfZ-EXMBa-xo>6xF-%X(xPdIj< zVM{^DDsLp8drEnC27Gs^W)G=B4l04EQ4O~=BgJdepD0F}mmfM@t>G-6w&C1lL)b3D z-?2+O$+*8vJT|$HsnIA_yMeo%ZV*D7F5QRkAcF*_6R(ij-Ukn_X&kW@;Y{kFfD_;9 zT_}Joe45Tj;-}3`{3eE<%`aqd?xuoWl^o{KQiif= z$Dfx5j5ci4Ud+_ai%>%qLuw*YXn+rwvC zKjq~6uU_yjZYtLbcaJ3UN7d;A(;J@YVuI3XN{%tg9s32hr#CxuI}KkIpUH6ERL#~e zdqR&-yb*alJv1C{nR9lNCB-Ji+< z5x7C6#nant>vy{hj+9o=W{(6p%lvjL08!@{`l`0ItbV_Z z-CL{9Wn7)A1fV^+$X>D(W(j=dsZU)&hLBf0pR6USlNRWYW1AZQL6eld0GOS|thyby z|CiZDLqC75y@AuEep0M#0w8GnhNpPz??A$l+9TC2WEGeiW9Scs47Ue&o!`PYrQ}@L z**!%=IewSVaDLeN*CT00k;dQ2t{&|;yEjz|`X}x<$m1%%cVYM7%7sC<9PGpVOo1uh zjmeEy6m1x__U8jyK2vw?Ct8hmt3m#UpjI&o$2Z~C$!;Z*j}TnTmf3Rtr0zA&pJ^S~ zx)>VU@iq&6TB~2{Td9PCXZ_S<+WnYx=F0=;`-9IBY#wD*5G%Se$$a)1e@3V5eNL^a zegv&Om0^C75k!F#cs;A-&=0BHtl#MWmWW8CMeHydT^B>rQzhH_44*FwS)DZ9nQ3y% z|H98oE%|TvAx4#Qm)4JeG12N0xGy=Lw=RtgOf;W@^gC70qtA$9j zsjRg$7hdX4I`k2ZA0^YhR?^kZwX$3{Te%#SM|7jeW7I`@V`6+V^h)IlXE!bH2%1Sp z1N@Q+6(RintQ+G~5X2B$5JEV=G+Em0wdz3mI%AL|7Ch0+(h4A>=vD}yLd5=lQ>sFn zVlN8O4)bnnih`I%`-P+!74+~y!EkV8rUc}xy!-FDhFA(}h9tl2BD43#^Hx0rQKe3M zlB{&1Yg6&x)>{1)J1~5Z?U=WK{}iEFN9GP0cAgrsg$k@ZPy_7(5^1gg<9qKI>^9RT zR)6NKCL7%vEdiv)h2MU1QciEC!31E-bWkE=(YyNqUsqpfhKUe--=x!?)3;X7m^2$=BK6){kaOcFxZe ze-fgYY*ZSJsfJU&f50rM%%Qnj!vxpEoTow$V`yVkfFKp2O}mLxib~3vJz!tuA&UjO z&$amDNj6ICgHn_Dd#82yZN%R_Cg}V!M{q3uT5oNVa*w`~ibp2<^na3T%!jBw)pqwr zj86SS<<#4I%|MaP~IktykG$YpOM| zScw2cx?4WWkuRz6MRrX;zfi`gGBIleV)!Qak+|Wv8k?)nryHb^CYaCsJto0%T?@*v zrc>~X@ItqVyj6>grm_>qVUPIAzL}scyAb*l>cRe*zg{EYHelK;egqPj#2vHE2rzc1 zzC4I4h!DBLcAxnA<%OO8k05cHeS5y7HCC5!>NHFO#kU|EvEOTsFfv#^^loc|q@Yp2MNaZ3>SW$wFZ)-px6A z%IxUL2{Fo_NW8dkEn}M-aA`;8F_f~f)Cz7=ap%OuI|0UV9lK%sQnRE1vn4^#Wh{d5 z^fz1SVR`G;jj_)^i;>&pZS_p=e>0f5WB(=VTKO(+-`?XKq_E$rJj`ZYxpzjT-bHZb z2&DH&wVPG-oGbs@YC6jRi-Hy>bM&@@0)bHOSg`O{v-Zr#69T_{9O84Z(5^laV`r&+`zn>tCR)uZ{{q~^Dw!*Q)(=%V@zI=L=T9f$W{rhIYB|Cm^bVtT(K z@(&|=Q_o%*kZ{J(fNW(?W4xZ!FP{D z=Us#ttL$rKN8!xs0h!54oQaz2Z&H5nI~Kv;sivWNn-80BZ$HxHn1-by{{fNyI6oiw z`AvZ|uD>DYOaeT{y10ZA7&Okpg8a`$QC{7|{WHCTvwnn1K?EUvA(joUQgC#`IDVd_ zL`1SKJIo^k-DM15634*f^+3^j(~=k@Z~#9ZGxFB5C(nBuO->V}Q{4foOOCyE-)yOp znK>T5rh|!e)N<03y0Lo_!q>TesIE;0XEbRhSmc~NU%%W8yYW^`gmesT&g?g5Y=rID z@4j>EGoW;-RC``&eWm_y@B67_?N{mUGD@+wND;GJE<)x=PdTb&}!{#@p-0q@JfC zl9s=Im;Yr`@0-moZM}ub5DY55I|r!1u3acx8ovy_T=OuJ{fg|ef@>PQLZ`)!4|md9 zD|a99WKxv#^}bY3&a#%l4mA5wUAFqVR*?Oq$Cq-6{sg6-*TYDK1R%xirH$)Jhh^f* zuWrG&c|F>kyK?CZF!6qGt2_0Bu1CI;iAQF{i^wCKByuEq0drW+2kODAR#X=ag}^^l zLdGK}`3QOW1)QW%H_zo8<4a>>%P;>${<5WfaBe;W8bj2#ZRDK~JuBNVxre)L$|K$p zU1d790U-f_mt*r?xS{4}U+S8Egd+W`LjJQ#kRiOX{tb5j-I|iS9BY^3yh0Xlxjo}B z8vtCZp10*;wY_11Of~T#m!S;laLoZCFV%H2WG8OHxN!QIVY%Rc_LL$4yKCkZnjZXD3d#o8U;WNyB%dy?F0f51zSw%amDUEDQ-H|Rx& zWW#@Q`TR(GiWeS1wU6k%SvdlWzqhuV%wJ_i`)9^q?_8{_`a!#|08d~CSI#i*#)!PP zHeoAzafCj{&f{BQ=;}(JWpesLg>erAjtQoPASNR)-Oua}rmqF-&d! z_J1&hR-@sdSLl7pBKPx>ahNm-{V@(pwQGUgllKn zU~Kgm=vMir#(>8J_2Y;9QGOjq8QYFB-PIF)aveu?e`J#p`Lu<}#U=LxhyNJ4@-sA55*DQ=$EoztdWLWAKZf{ml z=4Bq(Hz+53vv?~Dl2W-u&^~>?q9>3UU$I{;P$}yx##Kc7J|TxWVMf z#xq6IpYq@zH21QQ{O$t4tpBcyCjFeZxKu`P7uTUWdubmHWSQfK6lX6e3H>jvO|&%B zBW4WgmY;{8flNWr;gQ&ntr#^_CZGCnH-EHFTR(OHQeWpnb>-iQ^j@bPhB*XLETTPc zhG-r9;1^TuH+&U(_@h?+{&?p0^Qn*eIanAVYmeSk>dRY9I=nQpx!o6UGmT*E+>xgi z76>N($6&FDH*eBQIIlA{T@7s}tvhYjDu;kG=l3%m(Mc#vc{N{n+ zGn{Sc)u&!zhK95Negox6_R{AtVFFTa5;$N6dVL_#B=F)^sIQumgFk3NZ^!IZQoUa& zX_~t@clk(u7)o27l^&OH=-|)CJ%w>?dM@np%1*FCPHBa_J91SYfwWg!`O$(Rb>9M4 zf=HL_-DB67h|dp%OsA!^V9GHck^c<~eh^%u_(0)pO2zOp3}IoZM#fc{GdNkTlX@ti zl}r_dUdRWG8v2DPh#ZH+)c*=O)R0XqzDI=NLq4y21LUyhg~W~ zf07HKB*ul$eL9s;)PEt%99C?17={o^s@G|#XAjVPmkVTgdRC}BE-la$<=79Fu7!QxD)1l>>^+P zmJwPW7fgLUe-K7OCU?A3D_GVlJ0&5EDSKNO8W9;1&)zZ=GkyneJyeJDbo!&()}S$x zP#p|gkQM3P$8vg$@l4ea8(M|N^m~aUgen7SQTGq^`-~bPJj;e zg__LI?!9~w+u!8bVU&tw0CabSnF+HwM~g;9t`#5-f~~}p2n)XVy=I?jHf%M2$6-Xe z+sId6xBisWf)(eZX(BuD-oU6Fe9H1Qu|C8zsOMO=?Uxm$%4e1SR zn(D}2sRqF_Zqr!N^6cdRB&Fj}wS+Nt&E635Q311nN|XUb{PA|n^5{3r@TNjs;jo&r zWJ$RX>>n?C2g!H0XH08LZLF8KotYh7KDjLy7?;@OeRqt!-Q1_tfY9F;j>$oNF(dHC zdwVLEblU-Vc8)|#4v zTE7%nKljS??DO|a?w4&##o7K1^6>i+yBu~4>Wc68QWj^mLL+to*8LoY_A)KS z=ZUgt+9KcnW?UCfLMF?s%SfZ8)-N90spRxXV$=#4 z&T6(4o`{)Z8yjvZ`LGce&y@MOxuQ>b09kQ;n_V2K@|)%m9c`6qQ0q`9UfZx$J^HuA zau8!>;*BmQ{!9Fw8j9Saf1)WQ-IE{etC@UV;nij;Y+%Xm;8o{=jnsea=n;f7YHorn zea}WPE35JhR=Q)lgYL%LAd)*>u0iz`C-cA5i$XA-7>xoikEUN9@*hIamO^3?OgWb?h1Id_3Fw#Wu|WLKUHwW z@<*UUIRfDJc@9P#@cO*#(MEXU9{<~dd}~84Os*D3U<({ite9ObMhF}kc|6M(9M{jB zV!#(Gj~8-I>7M@BVB-VT(`<@z0uAhKWSx2&)?AzY6jDIz0+goEf5#-dY?M0Lf0$NY zy{J*tY8arI4n*(X2#Po~Er1g=yn3x*FF22->*Pu?-E!zVn&}uLN2*k4iKR-o5`MJ1 zs$Ky#i5m7>3411bQANtVZcFrgiGe^t7+k^onrK*`t8{x|E=z%$M8c|5oR6?5Vr1XQ zx*DqMvcWC=tM_q+VrYwxph*(b@@URO3 zu>zxhQf)26XXUQe_p~&dFsEUk(BItP zeEoN+vHwoOwqwRI1hZhof)A#ne1haOq>~*yamGiM2d#NCY>ckK7b1rNc;;?)5T_>D zO?KYwM7-uy_1C8c;{9+;mRj4~eRxW{$2nS%vX}5f@@zw6hgW`L_Vy%Ib2r^{o=zdX zA{C{TpG^@sem^xa$D;>Cn^VGhWY4FQ7TM_ zLyEI*bv+08miT&+y(@yR)_szP^K~PhX(ApS-+;YxArQa8ROaGtA2JVVrrIfP`y&jF zJqgbHKY}}htZ%FqHZ;M}rx#{S1FyUYT|6+ctI(J-i=kf^PRzQj4ESf$3v%pFmK*Co z;u)DrnortuCqEOdDd&U6i{;J$hYbK= z4(p!KbK2dr(f}x>_7zNBJGT=ueC{btDLMS`jC!a!LqCD*${3*E01=^J+Isv-gojlq zbnm3#A+h|lXfCEpkhF_Z)48JG&Arkkwq6_Ql2Hx4t+}-OHv8IcMu^sM;_s!p0+PDZ z?<%4xI_JFrYhwesJx0De(6v5j@_ld)_JA%Jf86qYcHpi zE6_z=?;?%Bc?W$=o43Sp(EK#n$u4K}JbS7Y9YfR;+}_~Yk2!skk8!3fCExKZ#NfBGWd}{nn_ER8ki338-SOvc=&JIKBSr9< zn5`4yJOvcye`d2~TNhc5|6~Itehq0I`h5>&9Au=~0Of3s^8(f2EXG$*%v1f5!5t-q z!6}0%(}cNw_XI%jwLQ`9G5%UXx}e>Hqzd;#y|e?91V2l8paqJaY^h3kCXk~PuEy>7Y_5G!7 z1d2$XFGjn*OIxNfuww5|+r9}z$#}L7$L#GwE0t{xt!oMn4lmB>4Eyg-xv)|(6FsO) zUdPWu228!JNbR->p>oBy8)tb$Un_!qiWGeL6(cjM8qpa~%vJVneAp znFm&ao%3%!2U3MUVN)JcM)I24$Y`hi*%{nK3=?KA?REIfAyHzMabWQMnba#{pR5h$ z$5GwN{xgsB$uA9@)Ai?*iNc6NE8M5%OnAihv>v(}+<|)wZVd(z9{6a@LmOA2efSpD zmBK`nDt`0X#-+~x1o1Qbnt338^N~q`KQrpTu#lKJIfGr4CdnfBx`gfJ2pTFlj;exbu7A@tfaj!<(}XCFh=`)dtf zArGz3?EgulGCP*iUW5G0);#_+AZ`G=OE6cp*p7H#FWevCqGe_y1YP|uV%dASh1rP;Sl3THgfl<=}>p?K<2&5EZSer|Gx?{LO;O^h^7 zz8}pVp?oywfAx8xmHnY#w#D2+%tRsW^LOO$B%=p67x-Lh?{8|!PIOJ*a#zW?`Ti4K>g;g_V+p6f=k;Z{Ti61hXzu) zT?&tXnD4%~?Z>z6GM0J8c^=ExnT6b^8GUVXra3PF^Sy4D)!&=FOG+q}z=94te@2jb z4%FX)2Pvhsh`*Cdq6=qx2k!yKi_kjd(2>00eH<0PYo~60=I_<2GoU}fx@zfyt|=aL z`=n78LP;UvWvI*Gc5D0OjX5Ik#@S3AO7zrOO%9Da!p><5Teh^2htB6b93duj1KCO1 zO%81irH!ezbFr14rAPbC=k5ZxlfjMWVa(C%^ zQ7H6kS)5fMhQ4V;^qY*7iuB&P3lk~!+&jydc-lN%GXOt_EQB7wGZph|G^Xk|j;CYD z3O*sp$Dd}94g1m{I=>F+nAc|L`!hd*C9dPt{f^i;FOPocOmKH1Vz@97d8Fl|$hjkd zKrOQF<91l`4|3g{+MIr{YszTY=<%$)ud}RY?K}9S-f!y*3JgsC!+>OWKOTWqdJKGK zD~)h*r9W2X;W}ccA?Ddi2cP8HziCK%CMuS4hl%^f59Iilwd86R?V=CGF&Hsv0l~3b za>1PunH1<$%WcvY_4c|VDSMma(Y$_r$i~gXQZ2jJn-JD2G~|3u(s8}*W&Gv!>_N{P zTiNHqqNiL<&Hz=FtlU;O8n>jptr-7I^fof9-0vE+@}y<~=s=SUy;k|(>dsyWad0G3 zB^1=VV5S1ySxhaSI#)%QzATybVCB()L zH!$bS0#-VEX^Qs^C&oh0bvNmQmsVHO13Ja(;#5u{Hqgdn?$rbwc;!)r$}Fx)wrLT< zVH~1^NK|g4pUA1}+Ue%KXRvxN8uo|YRfdivN7iP`e@eYc0MAJb+C7wU+2jPmgfIsy9X=TA$kwjX!;q zU&9{9v@-s!!|V?Zx4nSr(7^TW{O1Vi;_?`m5n-9)zW1qvIXJjrevG2$2BQ@#z_%|iCZr#jSeb|>F!s=qr&7P0(9nIoD1S& zmE)B8U&6q7tka`0a$|8`UB|$uht<)3lZxw1O_%C{iK?We-)6BCYnht(qd5hzo+0_l zXNVHqIh8? zd1uq1G=^U{u>ZY|N~)QPS!3e*&A=qzNaYFn6GPtv)w`3_Rps(sn!f1YKb)itg0(N` zjZMn1-qT4%fv$*eZD?jttDlKsX*v)ytWAlC-S^iRlZCWv*PU~%KpSlIsMOI66z>Vk`JrsBtN0*rz& zf(Ex?;LD7~)G$|0X{xLsJ!dM%%bc-&gNo$ec8m(iI<{rjk`@E)Vx5_eJKKnC?iGV8 zj~^(?l^s6Ef7x`2$(ny1D%S`*v7LHTX*03MPB#^uxNDWjRIT(4rFYmaaLhz5VTYkZ zA8hzfrCwYi-h73mi0+lma?i3g_Xs5`t*-fzPV2(!jsT0w9ZPqytmrPd_H*h`FfU2# zN}wwxE{nAoGB_Q`+B9CPo|q4Q5`j1()<)E>YhgORc1HaQ_kq4d zzK-%prSmctQz`#3(1#xF2QMyrxBKC$7N#YSDa!#9%D6o%&^=9MUiC?-iVt#KE=I+vWo9n*$&1{71sg!(S>sSxghIOTN0^R7Ng1L43=sP}Fyqa^4-Qz@z zlBM;05XLZESMo~7WytsJuqnCMch}8&C*6!}Ltm%%tLw~6eh16$7O*TG0J)rn5qaoj!+e#9}Y|oPqtQibXXfpB8i9YckA7 ztG%hthk?Bc@rKwD2V$;bL9Ei*`9MPYeV=mMcdjFTu;l(P{T2sQ!lE@Np&&W2Cc8Z; zC<+eR0)jvNq$X@6v7cAqrp4qs@E^e#^h!{$k=SNLyDY2Pa(k3rwO_y@EXEv(KRKjL zq4T4_acdl_n!}h^IgD+}_Z_0I(vQtcRYR3s=B92RGx`bgL9asnfNE3d8Fj z*m<@vZp|@jL$)jb%)w-g@PRydy_Gs22%j$zJ7Tc;;{TD=4+dws2;;tt+2@#`9(+Hf st;Q^}ZERc6{9%3Cb@p-Mpa1j!1Hy?H)Jb?!4FCWD07*qoM6N<$f~VhLNmt;avi%I=~J9P_A5S*J`_f z>VX1}|I|Pn27>|skAeXyDXD5#u5I;hTckS<_o*%7RY8HOs%nTnWT3$#<%;}c8a5U$ z6=w;nh`dmrh97LYo^|cCACkYP{A#9~@=%MHKZ?IT&Ac(gGR{J}A>Fbv3g?gJItIwn zE|}#qM_(JuOPv3|0nogqrnu(_`Iy>M06?_!H}Uk`EWHK*BHCt#SYp7{?>P>X*?P$y z?Hbe;UhXVGZLb#PFZ=Ymrk`5h%gRBEn&iB}tYw<}#{LV_eo21QUPbH$UWZ?BD31wx zTlRow)t{dj-4WP$K5X=R)kxR!r?tNx+-W?ZPOck!@_gE#5n8CocsWoVoFNV|*&%1h zo)Vl^K~8wp*GXT-wtgB)uv=G*=`PcJvfR(ALw+nC4p(wXyjPTNW5Za=fRD(@gjR+=lPvTR!?$d|>aX*0Z{ zcY5F3KzreHqT|GbUet{Zi=P9&ze-p(q~-gW4SO`{2nWr&R-dhgvh+yZvEJRzKnTtL z;vTg}%3!zfN;$0y92b4-Y(G%}SBT{#9hG3)kcvXv37kxG#Ws_lzeIoif@a6XZuN&R zQ6tqj33=f(UKp7c3V(`Z6|R+ezsMRCtIrD-Jgk=`<8`2r)wTuAnx`K0*mge*J74#G zvN7s|Zx@6W>Thu$0`3&`u478vxg%f|JX-jaU7AfdM9Td@&ve$Hfe-wm+B z-3i+lI^9c(di})h8E8yF-;bTt0@Oo}YGiv6(KwXacS7n+fIdyhBEEq3f+&}x(y&xS z1gW|$LaUm5-$q}KZjH=_Qq$UOHNz_wA9Z@Y}o9*)@_d~?-$a;NM{ zuyRwN=(csxj!^v@b7mOgxo1&03lX3HrF)QN=$L+Tz zTIa|XtNdL5PrN>85S;Sd6Q{_)=F(D`NYLJ#Vg+eXQcG30AN$H0_5tkp)U>Qqxcd#o z`Zf{tcd3S08*WH4CQ$TZawtd}lHsA);+jjln~(I-jgdNV7L#F41m>_Qy_aCJ?cbc* z_l4;r*Q?iMWfq(Qud;k@@J0=`#2H;pXkHT5;zyi0+Db9u3yb-rTgV|~Opo*Xj63!Z zPxwYlA?uTFeblZ4y@EQ!GTXIll?Ek)F2A`Xk0g*yH;(0=oRe(hMlBJkO+7HN>;CkT zQ;jsonQ(@b-L{~vu|vu%I<6V97yqQwLxNFwS;xKFhY zIx)8++yVL^Kc^xM-F@Ttbs)M2ox;!R%|;dJA~uHUcy|7D(9oQA0G@*7D5wktm&ET; zjsv_&!)qmF#MS%cyEvGNv^GMlGcRq)szI)KQau=t!g7YrT(a%oU7d_wSRyU{n6FW$ z1bj6$fy|#}wTyxFywB7!a}1bDA;FQe#FehjhL!t*DIPM5;nzqHiej3|)psr~o#Gob z=Q|D4EA~*-?nfAdrE_$Gl)Bu%mXuL@(2^UNDr8Y&#;~;;f_ydRMxR1Q5T-By4>fy8 zk7x`V4;HmNnToQmBMVE=deE@n)*$_R82?wKcVc~4-b0RUGvN0uVP90%#{_1tAq+=O zl_7P$ciqb5P?Dk1oN^P?#qs*B-7lAhV2ckXe<;v#h4keV2uyln>gh%?jG zhHn*Sl^+qGQo&nsO0rw7t%P=9V%vRhI3kt60&FEk9nRq4EXN}om~y0)qB)5wrnGz9 zfobmHkPa(Ub|a+oBp}@+NM{p}7YJl}0o{lI;-&{7y}B0)4b1x76X^n)$=>L>|&O{Lk^z2Ym7J@BIDWu^D1J$ z4i2Q42_ButvsRHn@`4 z0ce+B501U7MdihDpenQWo*m7<`14mj8fejwxpi>MKQTN{5RqVseX9z33y;JU>$T^R zOhJk$scSAdMawTU(W|`Ua|Of+3|-U=39w+FbnWvKI1%9JXK_Czvl4mKt^F(%9*MkZ z(SSHgHg61BI!mn$dKP$qosF46#{ z5%mZV|DDQOVr}-GIDDmKU*-Lx zV673wtdEe)yoa*^c?0-EeS0u()rJthf|6heC|g?d)pMl%o8gC9s&V`@3>Yr7ap!|l zW?tLuXqfOt%~NkVr7R$Tz?+rYG{T=u)(db2h;2`5DVv#jy}rdDAA5}7iI{&%CtZ|v zz9d;pAozZRjasF_lchAWl&g2n&NG6!@~{e-TZUcWi^w05sXQ$mXJzr0lydH` zzas|d{hTi(@lbQ|bt`c26sU&A(zHZpX~7+Q z!nu#RAaGq$x>}nJNptD|eA@2a;p^wV+RlT22xu~CeMxYMZfRbA+41iCS|{uC*nU`T^K-tJM1yP*&dP(-egn%fk*xV} c9J72^&HSr*v37mZ@9z&_W@Kqtg>#|)4{@k)MF0Q* literal 0 HcmV?d00001 diff --git a/img/fallforia/blogs/2023-09-27/blog-image-1-11.png b/img/fallforia/blogs/2023-09-27/blog-image-1-11.png new file mode 100644 index 0000000000000000000000000000000000000000..1dc99220cf32019dee79db601a42349c019166ea GIT binary patch literal 65694 zcmbrmbyOTp^fnmW-QC^Y-6dGi;O;QE27$!dSfI9H^3o+tcX7S;}Td9Jygr>LA z=?k1U@v_eWlKtY{%E_%)*Ji-|wdh9YM)RVr<%LJGMQvBI15-kp9My!4)i;8VsJrY3 z%E8c)#8J6!@o~9slkE`hlLB6E7XtGrn7d!OKFB9RU%rAn(Pp&faa8lJg^DV~pn}Qa z-#^<~eS5Bb7Mw7mj;IL466nwp=!0C)|MQPD1UmlTVP**ukaB-3q3cJ;gQ)(V8N6hk z*o*PENaB=39{8__{0_f^7NGJ2%gxR0@cdl%(?}-^5hZnZ z@K5jgm4x**jHpPiAqIcUQ1I54>GUd z-`#(7WLtDQu>*qsj`{B{@PKjb2HT?LG)|hweQ$_-zzh`|P?kBN|NdbBnrH3>+YVR6 z-fIt^;XVh9SiwjWxLQdtx29Rbe7Te7ydH_zc)7Rt!D$$f1UyN=StS13Rj4G`@7j{o_<4na<#pKw<#mTgBpa9azI=og02?B~?)6U; zceI^CX$et&_<>mVgVymjylDw91ghFVZ$Q-3xE$d(R6$7VmjKd^-tu`nQ+DeAn#B^O zJx>|EnvzNB6^R0mQ=pvRh)4uIQQOn~6Ylc8L!bA(i##9F00{P=_$T`o zNQAYJcZCp!`6u_tGjYg!lXqoMg}~&0zZWwhVT>T5(xD#yBAkGUEgcmn&Nq(X9+Q(d zJO{s?mfxM#FbKpT?EmzrWxxq(t(JHZ$TiXojzbcBzOy6=xJFJ%Gbel>$f}9-z&UVS z
  • c#zHW3yf@;hGSDBe96LQ43xNH8ic2~`oQ_SX8x~>lrH&{q^YeV4t<|bdCJ-~- zSGmoR{ZNy=F62sAYx?*WaDFw=F4~H4)s7I0jAm+M^Wo)FjGFHG3wN}BV>Bp}^kk*V?1NwPRL*g~Fv0 z8Z{0UOY(iDO%Z%Wg3kHkMc1dl@!Es;(YvQFx&(&y`4xfJF=bCwY{fPKavhn3228uxCG43mZe5-*?qe*cLV0Se zsFKaQo8e|f5iD+X0Y5yfArHcvT5+Su(j_r78!!&+(6nz(G`2$YSo+qs>FB;qfXo=4 zh|QQ*$agY^tcM+x=vC4a%**Q$f74Y`T(g0_)ox1onTTOxl;eNaP%((XjV1J zdywC)*b+Y+LaVGt%030Jh)o@Ynz0jM;HmCh&Y1I)eU8nyjJae9CXXEHb3$0F&7TG? zvJzbSW)NAmkQR(Z;FK&HpXaQ#WfqR~Ai8_Ka=xkG4+Qa7^wrkc_5{$fzfj?xym9?* z7bgKhWh3}faK=Rc6D&tB{FXug^D2QJ^#Q}z3QgOzl`H64eC>10Qh|l} zB>C_qklF(G?LO~VCe`p65_JT}+(Zwm(iT@{2Jdnh&WpW;G9-e_rK`W*?3nO;C zGVB7O?hB}02#niQE<@ZgXXEf^mQN8YocKu-+$;gwc1QTZ_(aH!9%9Haw4xg|ZZ{MNSP7g8CAP zWLu2(ig9{8;#s^jW8Q?8A}=g*Kz8{An~@7Ud@39df;B@I()ge_Y^>MEbO>W*jvY(h zH>UR|&Yt%z!&`@Wb=Y~6bGyKG+&iD;eor1wHyv#vnPl@zFyzSIRRLMg8}SR{>Kz+L z)Qcr;xjsR{y#tcL;t74{<-wudagcWtA{0S^+-p0TsewcINOSDgpqRwg!~w_beC*zq z2J(+L9z@nJXakMDv=E%gbk7`$JFKPw0fe(4>?YY)5osL5X?||C@fK5;PC`SDo`m2N zu>ol>p>*1(LUvI5xQSQcGMp-?RYr?abnE~~; z-tsT`cxkJH>>m5Y3F%r67z~ExUrI4Uj<#+U)FtVB@j6Nkh` zvV}>R5Bg9WVmV>M%U=anENr6Q7|e#x*wOYW1a6pppMQjr96&pD2EbL9xR(8JyS#d^ zf}SKd_|x8o<%X-3!;|*Dw4Z)8#A+M(>@PY&dsD@)h1y9p^1Z9N-U8-$XUhyazF)d*OSZ&@44EWIBq=b&u`!7%KI!}n0{gd@OCkU zfGLJl+t6zHzCQ({?RKHL08jVQPnq*gAVuXh%j+OuKNDBXvj4%gJ4+#V|C9c9zGKh{ zSO77&JFZ<^EK5H*CcM&E@*2q8#FbRm`RsK_+R+*!dIJAAAQscmFN|OP=dk@npZ=p# z1TB!3v;_Z3i{qT<`x1M~6AX5!QG{(5yFV-eDXFZ10WmHvF65^VQPRPytypUBLs_9af-m(4ii0h}>zsi)jj@f1`P|??ez(MIgQE*X? z!}`r**`<6fwx^@K&wAt24qr`nVxh_PnUjT9&gG6U(I~cqX$O|G%<-jbM9f}CmHT2F zpm8{SwS=??Nt?rL$T*h|cnLj4`~{z=dCQ?0M~3mOMf2Lz{{R7j*x6P{-&*Fb4y(uS z)5XQP<;bg9JOLGF0=Y{kjZ*^Bnmv8z>qli~kctr3U>sY~^NkmK9eGI83za5J0hG`I zk(zlour?9T4OX#)B)f>ItouqYRqhK4N&5%~$4_#DB|jUEv=;&FwQlHj-;^P)(WNDP zOiawW&Q9bk$Z+03C+RB9A#c6_Xrt&t-tmO|!`lxZ& z7ZDbXIoPQW*Hwcn;etSKw2R~-e25T0YMY03SgzEoq`nh$MEpI#Gbd0u%oN6v2Gr(r zi@Xu|q~*_)S1ms8r2}-B7{Mqgh%W6_QII}xsqrNkU3RiC5)A&{A^J^vvWVUSZ$hdu zHT;BbV>n4^hMr8fU4H;8<|>gmFDjw6=+R{HcU?n;2_<4?#tFLXHnSNM%_WQV&cGn% z4Fe?Y{%m2ZF#N^bgh@Lm8Nx_I{eHo~bYU`rfx;6vEs@lvRUfv&36V}S6+Z_t#usV; zw1hCJrxGwA4`EEq8uCWJ^&|hE{lPkr(F1Lg(4|B&>DyV0A%3IPVn@=3MPHJgkRoKi zPL{)96fYifm4Uspso70DVGoI8AN825p%Slu*IX>R(M{dx>$Pd8hib^C!mgIDr*e#H z4U)%88V#LY)h~S4?H+^0od;&Fa7(A>Pz7S=sGa22WiP&AE*8M?h7`Rh$XN~NRfc=$ z1xAtel|dROZdyo0t))BcDu>L(ZUAN>}e_6R5pXf$@;yhslwQ8MM3RD{;85(Gype z&Jfs2e}iB4aj3b@X$wnO8Ccxb3o|!mJz_CKky~NVZ45NHj!gI^tL7m|!c8VJN;Dxk46aq?eJjlM zt^?lBD+(y0*AOwE1fK)iFpOjQcGkY?+n}pWt%Cf&VU1PQ0r)%7iTtNCI};B()iOnX zDX;_GJCHutqFJU1J~vX?J>Sz~JuY22abUIia`m_WvB?8pqm!)0K^y?*qRT7eqMj1mQ2~rOCayYY(!Iv1 zu%hkbH|de-E{RJgM*nbW5eGjm zdH*MTr;VeqAR6`zJA4pXXUGi)mA?~{=C)6j>ys|4gr+QPw)RW%u?El1N;@Yr(L;bQ z`=`&Lk&BEbo)hFpqgtVOu>+F}2dOPoht}3Jb;V|!TCty;rA^c>ETZ(n;zJ!$(0_!p4)ctsqqf*#0bfse z>;YG26B)q6c6Mykio(Nc@dHX#0O_xvNPx3FCVbNZi(>4-_1NCTc`Em!pK~#pDfpS+ z^y-(e6ObOv>-h5kqkwwQN!8PA}+(KiVyUcAn#XZLR`jl{dT?!Q`Y^9aI3q=W8n8 zXOCp45Dh+8CS~x2a<6ymQEll8N3Ze4H;*mvrnhqSy<(_ZxD(JwYaIB815&(B7D#8& z-Me}fg=@@)snQ)xbN8|t$7}i(hlrIGkY*JM!sY)#gQI%=>CNL zGNI?E4*2#Jha6# zb*VyL{~Z|DPsC>C`Wqslnka83Fgp30zOIpBkew*nSzln#Xp{S|3tj_T_AF3OqTe-f z(`y7Q8nF;gTeAwrwlUL(i|O51>tTwLIt3dS*9NyQH)$Cl6$7R}RBR4RdGL=z*WPSZ zgo-1K4Z*>H=V~c_w`a%a?X*@Gnnt|-$|>xw$Sx3WurKm7uQerCD|$?jz2#}t@NzH$ zJDzzPhp}N$=>1rc`3YZ~&enbr-{s@f=3pgTPspkaS6#!-)lY7G`L19-qW~D^&PT3J zFG}2XGp4mkm|a7W(AEHuU_7yCteei%&QLHzcx_r$2RK}`2pVNG8d5cWL+{f{Vej|?P{{y3uZmfpyufU{UE*(7e%EERvQ`oa zuKL5aC1y^;x^}*#^D?LP`D$p}*H>VhdS(m&UI5?+CE<6j|C#kx4wRu=X*EpzE_pF=VmxPWmU*?Jf((pr8l?s$XtQNYOZp;HtkY6;(z2;)`Ku31aZD|cT z67fyIluIB}^ucA-u}&yAXo@tGn~@%+rK6x47DrPMX?2p{%xRda))$g64SJRbXZ$4R zj^AXivZ(cBI>|78yBJ|hc9-wU{SGHJwx(WlYh=e_m9N&hpxXGyS8r1s%yQT}KfKI| z2Fd1@==mX>vWdHn#SP0@30k9Y>F=R4W+5|u3%eczeenZ*cOwT4bvyNNvzR%1P5s9P zo9eWsf(+V&W|5Y@ralk_NV3h4_DExiqs>7XXm0+1%qF!m6kyI)t(rQ)xYtN|yRuzX zb+u{KS;s?AI%kJE)bA_8T}3INLDfKlUi@Afk9_mVfe#5EG9tl3dg*kw{04lmcjsw( z?@RV&1)9if(Rffm7oK2xR!*)czOI!Zr{hS1(_tv<@Mcs~q1wKs%J3+gHimXRdUF=& z2Oc-rs1eIe5N%e`9lTd!x7AEJFUXdD-~hfuX;GcQrH&S<$~8S{_GTB{=@0FT*-|I! z4loD_d<}((A5qzj8{uLGR4tjqR4ne8C1YgP5g6ea@IsBtk{EcAb#+8cIhqzzg#0!$ zySfBM`(OHGx`T~JJ;;~Kd(ZGUxo6EA)ik56f$9uc|pH;;onYJ=d5G%lNtcm*eNDzgcQ2KUziun{oir_Am4nK z_e?AlC7{542H|gscMC<3A3N&TUQo=&yLAqh{)@m48tuHJxV6RdzHm%V?`54uT*F!zL1{S%z~Y&Ejns|RU^U6%S+wo>L7;qE4}Y% zXtZ^7IE00X!8a~7e9NK6yx{Tri9PeTp%k1p51*$LHK<2zzQB;dchJ0cF=lVivIr-h z`kxQr4p*@hv#YCX`p7+qflSWsavLs-c8gC|%uP3B%Kz8)bO0)?rZdYH*TvX#p!3gh zb7WfQKzy^_JR>;f$%##J&=O4ij=LZS7gxy4jQaS*gsH15=BH1eu>VU_fXfCy6O#`b z<=kq;V#LiuG2XQJ&KbI(7)hi6^`0Mm&F?RH9=vB zOWLLUeA2#s2Ks*~e;tUz>f0r%7L7H<*}?d3uKydyorwR}-i*Jd{@-)`-U0J}B8wD( z{r}+{`CpfdHN@ht9f63EK+DsHic>*rTqnwGB)jZ4b?ZG^Ugq!-VZHl8r$4qcaN^SM zqznWL1D5gtn|v+@-nplldFLZPH;F(i&1!n<_wjwUp$2pj{boHKA3N0HkX9o` z+@y(I2ApOkN)sh$Go;EJd%Hs7ybZONNYD%-=%VphJ78q!O@~agFc9A z=eHGcv=wstk+yeUwh6QAi2y_rGkn)s>9u&}Sybs#)@g#=qGP&?vsDz14IY7VY zoratGAuk#;`<$=}T#(z7X6T7i`*1SvelHcOXF^H>!?f388ouHF3iU~VYMI>Q-5p6c zS-{O4te+}k?jbE<8tx61GjDGv_Nb1l)@-IL9`S_jkr~L=a5pxy!4O@$zTfumh3Kt_TEzCFw5Duy(+YLj<8sjfxxbW5|5kD;XfOLrOx+`wTE@ zDJEX6^3H(l7n9Kx%M9qrsIVGg>CN%s$PJhuS|JSTl)+dPb%jW~lB`tNm^WQd!GH&PLgwH4ox4cdJk{J z1vbO0rd^p=JQ8admYVWtt2{c@CKf1PKQqc!2PqJ*;&W~*O^XDwxwL;|l>g?CgVq%3 zyX>P;cm3@-v%ak`MWK*0n?oxrX12m3>^z-Z1i0}t#TPEhPgMpROIXnDXcCfxkA_Ir)Dw|&nP;auK3!Fa^hZ=FNQ?es(9Q9z zdA9%zUJ6m&_i0lRj};HsD#dezKa(z|mW9L|7|ZzSt3Ve*cB8ylp}j9AWKc^*X-edu zY7e|fb(^JrMJhM-&VeINe$nSUA{!*#^n>XV%cRf!xs_dKRvBzxHg@xbDOB? zd-<^h*Z3K+d>FEI6{dB}MU_}j@Kb}^jlw;(#HmOpd%ObZc}Cs;?wFD?S{oyYJx0vi z8hgsJ(xlTbq|Maqp;@yrKcRX6*W}^5rfo!DOFl#!`_$FcrjSU)saC!hcldy^2kIbZkP3|D%`X?8D*Ck{D91Mzk*HrOhOBsqRT z#uL->k4+i9xQjYR^qBn&S0$uaT>iWfchqQ-j%IX!i!HtfW-O0RQDGbU5X~OChbmW$ zRfhaZ!hSfRCQnn*PjaZCk6rkrhoNQ6Xavi9hk6`HIZgC<(}w!tvkjQoX`1FX%a@1i z3Zx9jjfm4~I$y!Nn8;Q5FIluH^+)j=TxboY03S6_w-bkj$rT!!i||@LdGw0#2?lc$j;9`DmIr*!Vf&S zzY#RoBA1Br;ruRFke?Wrd~pzOA|_Pk`%cA6fzrO`_^a(z%Ha3tP_k@XLzz3WBIJQa zYv^4tv?kj|V4meEaSsnc*k;)q>#OBUD`<4dv1)lfs9i`R`1lfl?NMFt5U~?t*9kkb z<#GCx1A)v=#wCDb^lIsaoM)gYTf(pTO%OLNk{!G4F2@wVQrgyoNqF zjEA0?SsbHdqqq+Toc9}MNSbcc)lE%f{nvRKEjJI3-M*ZXGRP>nU__tz z))%iXed{t*&a@e~gpntDvM1r%wG(l8-sbnfcdnLHr|5`?9-S8ML{3?ZYTke}5b=Rn z9Kf39WE=7}hv-)!8JZvT=N{MFQdcRetS7wo#Ix$qUwU0=Kvbo1)$7*eW4?_kTMbT= zz(Bl4QvbXfKAG)5cdH^f=}hs55jLk{NklIg(vx+C&VqS_HuB4d%FMx$*sUqHDwF9t zRz;PYRF!Wuh-2q{_sy#;KmU2`I0wUklWK`OjzuGJqp7kVccoS5gy zOS5`Uj6i@VX&57|BPNYX_!MQgK2n}`%wVcdC`!ZC)JY$5%7~K7Cw>CqFr`w(*prw3 zB20UX6poOcz7+nXy3i^Fq49*G=M|Ztv5`C!NNcP`iYs0${3wX%->|8336d99SM0h` zse{VOpjgXp*!rSAdHd|ah6UITJ7gXjk4()D;7n{0KM!fxr_<0HUUE@RWW9Avn{OiW zU9PxA0wjrLmWPZVWu;x&R-w3judpyO)~PJ=XEzWd!)X!>Dm_{G$VGY}O9ZK`Vzv|i zG=C_TAOR&oT21!Xyj&W+~j@g7J`vY`i5yX;DAuXra zsqU=3rwwN1F@^)XcE8qDlzF~wEu>L5#k2MSFU41|qv}pQ%XB@YxfX*)!x3~L{l1`~ z2{NHkdDe&K zzuN#maG4OPh_5(O>0qb=L(j_egIls?k!!)9b|)|E{JP*^5DBlVn`l1A}otrDaG3+nUyN z`gqVJ06CaSk-c(OiuZGP&Y~;3V~bSJbWkWzmwVFIBzdYt@R2*XLBD?ZoWSuD{TN$E zq1F8lyPtXMDJeN?DbV$BNhghiAI4C=FzdpUcUa-RUB3_zSEN(5+F%5} zEZ|QzWtni<0M4HIMaeqDb7dLg4ai0cI5AFn1jwCr`^gNAq$j^Z^EoR6k+v|EU5`GM zU+#kx{Hw$E-o_9+U95=NEXQURD*KmDQouh9a8hZxFrDMK-n#1|!zFk$uJK&H8$d0W z@X~U4DgX`8@9}^O^<$A!Y*GxBDAqq{DQeBrab90(=8y(#FtkeeMR2!3QHOgEyZ$iA z(K4Cq(|M=rY^HTxr1La-k&&@7R<#(qw&IHFD72yetC5pJ>ge^^400l?!BvrhWtqJb zJh&91f*3>1ESZU-@v|FI4?mT^S!mWd5>YsIgcJ_?%D43iXdW^p$jBPMgSgp=hH9~* zqSF%`v8iA?<;oWYMV`&*@k>JaFxB2b!XcIe%J;4gG|al_zX+3a6S-Ct~GlaE%Zi zM!He7T}v$wPkSUXf~@8;<}e&3z;LUUr zZ9v-~jOVgPi6A7Cy6xFC^=Hdy#_3}RX{$l#*K7rSxfkKryFugzoiKvBiRx{AXVvWi z2$WP3v*)2C1^?Gy_^+o6oZ%?14Zmn~w%57-cz4T$mp=;lZ=Ep^v!+s2ON15Xq=4rK z8g0zd23ArPp3kFva%AHAn0{An~0D>*O@B{*3;iphJhdE!2zYaLL8Tbk*;I9O-(mf~od zNdL~5E#{#>MY(>>Zt!ow$V!3`a4q9l)`~d3!rZm8D5{b}J}I#nUGSMJeP3ks5q_~S zNC)_GPZpraA(1T|P8aqBBkYS8A}Ox*pLP$j0Tv{vhqO&K4jXQe3}kI!v_n-z#~a5x z%8IcNx2{#Zz47csnPOpaJ?d2VcMQB4D^8@#oXhZv{rn*aD~Urs;?OfP%Sna6&ksex zN81f%f2M@DU6}6L&lK=PQ9Q}&W#Ogd7le4Q?o55 z;oWKr9X&!1ki&ZT=P4eagOu>=^1VNLF-|b%;4jRA4C;INF*xFA%g6E#Ij| z+_pnd@mLZ$snkcj$+t+Up~@bFk1xJ_`-$Fw?+zt5OKGGwvO6qL{hs)JeN+YStvGLo z9D(1L#$IUFD_Ti&H#G*c#h>kPjmI8tW;_F&o?kLU5tgSkP@H6kD1NxV2riU-0YD;J z8LjMLaC6SoVsW$|<^@fTLf3F1)bo6Lt+5{?{M--lm*L^!>)7N!?QVDISA z{?oP!EpCqOrieQVVXfpw)@e8dvUM4R9NU11n{&Wn(w(3MttU7>H7>3`BCL$cHm(eG z!baXCA+pk8DXeb6OtTdDrOY-}jb8yp15_JEaZ^UOf7ToNvPY5Uvs*#2!o~r|K#IR!;%N%=G%35( zV=SX!MpWZ~F$5=&))MP^4A1kWmYUd;+xM!I!4DZ<{q*9=0{G(#+}~YypZkno9gqXP zy^#am9U)510{8c|*@J|H;_wK+Rdwd}y*&VM>5vu@HM^sQcr*riZR>{N-aa$aSEc~F zE)|3WeTjR1sheoL9SV%qcoea^@9(H+&aZ2K_vln%s2Hdz@t-sx_Pb&Ekd97mDS9H3mY;(3grLSH@Xd0ct* z4;`^E{d!SSO%{a_hL5Cqq;<_A-(wh{3aoHb)7=2sK=SFtgr3;qcO2+DbnN_6rk8n3 zCPmc}GNY;_*BA4Tfpq)B1gL;ZN=${FN$q(DQSAXt6{s4Guv{O1NB%mB2-GYfoKP@r z`@{+U7w{t~Go7{1-n-xT{R7k=$L3&T#!t=f6VVT8UZq1M6@k}tfhmPGDo{Bg3K zJqar&=b*BP?}lo34aK4bj&xJ)e(UaJWSUvayi*i`EFcx7r(a+i=LS**wzU5uh?E(p zU{H0^pub)TXQ`^%{2h9*$1(-;%(RBF&K1ONgf6uN0EL3Y2GQeZUndqQ9ma_a7P-gd)rHgR{tT^uu1WvA@k#KUi~OyK(6z=a?8(iR&sO zd=K*?H_RE7+}p`{T8C_jM{`RLa7X7jBZHmVFl!nOo5SOwXxEzXgMlx=mP8m z2`5K%%+$|9zf^tDX@CuT0og3Fi|}H#sW8sTgi?Ow?lyg1;RWGyEb4{Kw#d9a?8~2E zWa&nOw0OQNl_bqH8s&i`Iyl2@Mdc?tzM9c?7H{397Dd)aUCYvR-{K1=wg}HoLJpl2_T8Qr z1ha&le(bSL(ff1g0QK*K#p9976bu`}liyf0AWP@tgN1g$J4j=Py9rfFINld?^hTqL zrjF{tWMl**L^!@%S~5^tjbO2ngCyE!$@$JuF=5__K?nv(Clw%CZLL|ZS5d{h_rV-> z_i9CWkxpzaX;$Bvf>Ahs-9lq{n;=XotVqRJDLtum@=jwRhdpNC^CUB87|0`Gm=)20 zbh|&g{!(15cFKOh)tph1su&%sAUw2AAAW}g&^cI8DXtNKGaTkR3wLdYl-xgnF=SUj zfUwnuK^oz(0ZCUQr)VVb{6K`&)@Bp=?vLf0`RZPTG#^yjLMgqDC@byHyxniEKGy@e zs$z6bQ#c}SON%w0-?1J-yqFz{qq3)K5ni2JCJ9a(gGW@A(mr(zwZt}0e9F~{s6nPB zHw7Jjwj5(=qM)+);$Mj7Hor{otr_$b`a{hRH}3L5x4MHD31cfMGA>?R-i8}-bqF9Z zepst0C4J~9Jw!*&!2p@zv?zIe*I#Z5V8}A2o35IHR) zek&#udl8_{Z!I**jJBu9RCy2!gJk&pR!BzI>`_ENBUVVSTG=2>OO$Y*K-+#&+UVl3 zeSOz^G~Copu%1^pYDvEpopj_nn0~iqs_*oszmi0H^pbSt+<06lTVTo18=Ik z;Mcivy~{zvk((snpYWz>a%edj3JPA#zP=>-b3@FHt$huYl1N87N?yXGEs98G*1j_K z@&5RupURBnX+d!CS^!zch|uaZoJHFB&r(T&Dw?k}pqr7sJeb|I^# zCOCEegCo-9qP3Xq2(5r0O$1D*-|OSjD8r43S{#r&=-iHJTV8$=2YXx9e^xW*k74`5 zA1%hu5%uO`gKgZ^6^ja^x@)AnpxNf{MjRMm2OIds74l8U0wM1i1rFljX+;hTycWjb ze+P!1)I2u5^{E>#kQCucpw#a0h@k=hKpVa^D@U{|$GphT?p-|Ai~HqdiBoCozXe8q zJs)O%-Ty^h(}Zv0S@+cBO&It#gWc`z24URE9#!(7DDQywYX)_RCg{4)c1Eg%)4}MM z;H|EH=J4Dgak{pp8qqt21BqBxTWAv;tcd4sACsA%+50@pXkjOE<1g_;L3i?P;RD(@ zA&)X@pj+&iQ#k9TBGeWRWegX7L2mzeG4X=j=(nY)S-YaHr^nC_qC(uREpN{r7|JU1 zmDibBSW>h);sYwDz8TeHWxb=boZJp{Ybr>qay#p8JM0bb`vKmI#!X762n50~-1}05 zS`xuHh=tyE)0V!;Ct@o)S`N1KK6G!G=#i?v(KUYBAJ!D!>H(BUW%+E^V$`M%X!FhKLv*9-GZ@ zeYGtIZMd)-ri{lIR(7GiNL^1mw&|)2Acp@GoZ;d}%aWXoPwGL9n7H&YHLxA7zA%*A zd^~aUB*BYi#JM^3|03)M=o_~S@%`~axkQpLza(d$edRecZYJ}deb*L zJ90XQ{hijjzIfx35hI6iTY&eAEym6|3ccbk8VA^9M!t^r&lX4LR5}eTTr4cf7#8p8 zwN(7b$QAP574sMpEk_qQ&h7-7kg*0wx{g*|z2A~kqPeMEGRYM3AW?$#h_{JGGzXVJ zd^w0=oR#TfETXCcXM950%)wX18)xj%7PNAc$U22|QMTiE+-P6l(1~2$HxxgF7!tpE zVx;>p7CV9Z*1XS#tX;WW4|8t+I$BujlRm=8L8B;T)ef)J>C)>MZuXLIkS=~No-T{- zN7{;1S!u-`cR0=K+_&{WM$;W zXwvZAVY|dC`b(}MNw^;QMCwlqQp$SPuUrFh6XYYVShV9K+1>R3vL9T-r1th(Mgt9N z5z(mz%A!sOw4~8`oT15~nsc{YK}7a)R+`+=f>v4IU~%6=o2I1R!=8k|xpnPgN7kzT z0D*@o&9g9#UnqNP^so^JCf)|lb?YVk9|h&p2iZmQ73u0>z^W$%3*Tq-^rM4k?c7y= znXH2@--%%PGXH|7?wE|wgrBzVkr0Bx@4;99AKc9?*1ts>X+B@}nc{Gglh5_%(Q1|I?6ZHb`P4&%;LOgI58uHv!u=ze8<_QN}O%d-Q@I_sUVKpq(~T!TQ2humd^7A6T%uBiyh zlq%Om#EVzr{O34_i+K<3W5t0DfxwJ{=FEyU!y7QGhn^JtuQKetvU~P6y>;SS$TkRC ztjDgI(5RLVu>X$@!4H2L-3#LQOzqr3_Dt-iPWvzTEHVTJVT}~^KYu)bW@7Kz)_;*? zks%2GcNM??;PJN`B%+7;r}$tO{GY?@!tN6dIkL}`s#eLP_KP}E-XhWY=h_ZhxiV_0 zEL@5N(jM*gqSWM_>ThVQml&}W zkrRY}Rr?J6b{Qtt=O$;Sw#*i;B8s9e3qP6n&hbw2!PnK6Xy=St3FaUCPDe#6h$=%O zVnbggqpr@8yj`;;sU|4+xRT`Y#5rl4AZ~j>IcM-py7b+kbl5defU?$jFph|#?aP&; z|7*?xd2(ivA)&ZJzeldHD>9zB5=ZB^ul`NgWSy6MV77uMgqk64Gnv|6WjO>&Rn;w&O#Uc<|q@U2G?GUoomc zZ%SyH6R3#2p*#_tudccc4j<5kQj1&3_rJ=eu;49}5W9Ff| zRFH1X;3SQnXuBV~S^;;@GXCI!TALe6(}lvj*L{fiE0ej|+B&=%YaQT^Ec zmm7=Saf?pel4A??pT8iyfxVomiYlP3ji2rPcg%3US)tV}1gD6-B6fo%5Ur3EQ%Rrv z{O?N9k!we%b1p?+pJU|I#e`zAQ)9rdXn~PrFPC4W(@;!*GC&(YU1y-L|AcM*g0hzp z7~S(W>@!zeR=2bo{IlZoTnqNj^ZgrS;8hI#4cjP3stdfaNLm6<48~|0LlaNbJFg{n zI28*#OBM)4M%^q`wJ6mbM`zQX>k=j8dqY2&FY=e2WY7~pG9_|PoSl;cQ?z=_&;LKu zf-q0eC)?)C6Zw&&fb`d;;AarEa&1*H+aCVwG1CtTVr}{^~3l-yz9aBlo;Eu zoIVruP>03~2i*Ur=l99$C*)#&G*+!Y3Ll~7CH)HErSxKJgpAe{AQQZB$OkGtU@a_b zNB&{_y6Mf@03|4b*XB$7vgHLj`xK_jUa983O?G(@vCYxWEPJ0P1#V;`ky!48^{qyQ z?FtHuM@T_!sPTTjbncGAVldRrK9Rb~dHc%df5NyTE6x8f>IGW+Ev&2*o^*AWaVchu zBy%5&PvYXd71%=@W^_d8`zc{3jLjdb?;~bai|me2!5iU5l=E^@F9?Xq$HNm=SI2I; zmSa@AxqwwB{9o|+w#k(yj8iug5#Htlg(MLTJ5h6{qsqW(GyDvDadL1Z;+^L&XYV4KbTT8LB_bqTiv^61jN}CEKHTFBgM(w%v@P9BM z_C3HsZyg588H43I!KS=|k4i2NKa2>WSXPa@DS;ozM;sW>CCekE@xuQ2}NRbmJB>D%HDD=(8#?6eFL1XxD z!eR*zrCRb?{MD=zrk?8KEp#CkmKcq0O1hSTfewEDhRHO_amXen-k3^lg;YvQa>1OJOX7*4y!EHBl zcrrTw?-9R&j)x1(s#S#9^F>O()MG)u(W%r5!aE%evwiMzc)b8kF_xvi)|)*?X?p;j zH)e-7x9;jPAds%1PWD-hD|#DTyHT5EGi$b2txvEP`tIb0GISnjVlEEc|C#(7#(2F5a#ALuMF8xnms<^rX(0>3qq2 ze>r~qneU{N>H+;7de47R^nVL}{uev{9?6RLU-!u)MTToc275=WMNYN={JeQ867LD* z9&&Z?gUlTzU-|O7GsRTBgA(9h>YpeQ7`df~5-=^8=k&)18{3cL##aOxcHsLpJ^kJ1 z%jc|~36n-2><%(bS+*qS}>OWxp{7 z1HK+i-6UWAfWt#%q$)su7EbAPgbDzr@<+Wu*Zv0Wdj~I}oi?dak)qB00_~Cqtk|TI+Ycm!wUiKOr<}ie6wLpFwzrOo>)W;l z6Wrb1Ay{w=u1Rn&+}#RyNZ~FaxI4k!-Ccr9pm2AWKv#bE-fz5qqkD9Z@x8D9;*2_n zv+Ky-Yt1>=TztkNVo>)k5Iw3>T(=!IWV;I@kUgP?9LWkX6hOmjwiAbJ^Fk!Be_#;T zW`kkQM#P|+ z8d<=OM7}z7Q{zGrH!g?@_Gox?>z9~6&8TRCMvoZP7MqMr_2Fy`&+Mtm9mKViv6G@!A8xUm-fyGQ zme{6r*RA_pKz2iFt2Q8FVq8Y=-5g9KpGOqzTAC(x>lPO7CPjVW8Ptr)0d4_W? z2A%g5+rgT^GrCo2KlTXZ>o`3+@r^u6wtKS9Wfk3tvv2FB!Cvi{h0%4@b^bQ02?XiS z4_7)M$nCO~0?XCW@?Fn-SUcRpA3)vp_`pgk6rRriTS@DHUO&ttQW2Z4*yr7d#d`n1 zIBUuI9gSu!e9eyL3KS%$2~vn~-FE@)hQBXYP- z#*x#YP*kgieE_ zb=QeKIWoK9Srnh+fod7?Z`Z#jl79ALYb)xP(?m}5qR;i{9c&^6ZHo-DLJHTN68rr7 z++1DNhZFc--XMY3lI^FTFrzcy_wbu<$LMY3MAc~92Bo3tj#cAktFVinHQJPPHA4Jg zKi!+~hgBV5!`K(FArx{d@Nqkr$SI0DChwR$&Uz?g2eb zKOzygTeEFuR5QYCk{zfxc#wL;jD}0dLp>Vtcqid}o(RF8Vc;kc0Uiko2I zdm`ia)y*{Y@Ad}0UeV|Z`C>Lv1vnnAsrUbg>DkpVDPh1NClLu9OOB!Lv_|=pr@Pjf z8O|Pz*aw+Kz9+)gFrSA0ZvK{Ukxxmq*fZFe;JNMFemjY{7F9M7AQl-DWz(Fvf~rqd zQbdgDdk9q-8bY=6M625xABdTV%jybGHI>;t5XTS{47)v-+lMe|KX`?03$sz$ojPG| zYGFam%Bo;F4)#>C`Y>j}sweL5-bCTNySv-$ypQJO`g7(#AS8DU-uq><{J2Y&1 zmD%CLK*0)PwrXfkEkl8ebug{(7|ZbFm_+f9(>FK}F;h!;#vbLIIUQBL(VlR;fko|u zz@&a_%*B#Nzho%&hb}l|=1=M__|z;v-{pJJx7%aymE}897NVMmqDItM_tV_>Y{us| z@S$trZdbPmt+F0}0XL3lz)aQxqM1U4yS>jlq8HZ%!ymjZeemx~RLTz|GC36x~) zv6M%dtQN0yHHMQCqjzNThf$#OY+$zlQs$C6F%F06?aji*6k7j*xw7{OyVmNMu4y$_ zyrL!P(t=8zfUf!i3V#(F|6G7^k3)G7Nd5WvyTn>{jO=?*U5qJsCsnDQt;R&B|MMYB zM!R|#|1u+Eufn#9u~5qgIX z8kXG`d>~ypLZ6MwXU#NB*NFXXXMkDacXSbf*b~1Gren?o#OWbFc4zb*p~V$RrCcTm z<{-(1_H63QuAz3Nr2m3vyC>!QiCHYmaWv++fvuBZNtBVO1Nlz_1uS@{SllkP>^Lyw zy)!99<;^E4!(GAoGE_62mO*1S(#|Jebma~L23RGWU$ioiK6V#qz-Kn&vRlN0r7!u^ zw#;F&1wgBb*Gkr=2P|m4@WpvEtxr24XSV9#2ae6n&1Sb#B5NC)V4I|@teEV#m;I8m zvOoxoBy0f5+J650339N5hh5>o!pNNY#y>)&(SJ=)GA5-x&xZoXEnT`Dx4!Mb_P#7= z4;L>deO_@9R>2%-#C%VpkR=c*+Z4(697lE@o<|X6%4?+29x^|V`x(a?(1C}NyqBNj zaiO1l??V{i)M{9-l}l13rMp5QIGs~miUXTh*e$;LH_m}fA*I004s^#OafCdokKlZp zkpunfY*Q^~s7Z1^^ki{rOk(oD;pwtw7f!SZSpm7lplxvgAszj5oXaj&trzXXjxrX^ z?255kKdJRch;{q?k6$Vc)}08+G2qyfpxY|5U*`@Lgq9KS+;jFE#cb}=M}hH8my30Ms}W164LIw{0#(<_j5 z83)f}BRgo-_>02LBxubiPA-LdgNI8*pP`*Kj!hh2RX9S-;BL%y`v->M zG(Kzr*!9VR?k5Wkhrz+k99ToKw1$AO-LZ(IhuP+|U+{@eP=^-6qJTq>93Q28;U_4x z;5lPYNI|bGemp1Z_%~ZN?0rS)T*t>{Y`*p0#^&Z>8V@;!=g0e^;^LXPIhY1*7G~;% z5jMVIn&H+2KSTaB)r5FuUgC#wHaF@IVc}J_sDX(!<#_}i9)>Njsmq=RNe;FkXTzWo zEo)bFZKQ;F*0f;fggcz|U?Nps@`w5u*!q%X* zE=O|ymWT?qnCysV5pc!+F(n1=cdA@}<*sqWXbDVPTQ@ah#(vqnl( zDZ1LdSdrmLx{`%WVug#wt*m5}hZ6vRexKz$=^BE(bnN%jH=qEOY->$XgAv<32U77# z^=G(vT+E0yG{kY@P$3R!=Z~A(Us37ymUvR)u5N$Iv1(-TQk^PL9l<40>T5}PD_|%L za6h&w$uMHZPmjUq#%`99&=j&iYNT(jn1>QWGK=MiZo*<@;L9JAuygR)DaeHazIcN_ zVE9N2fKu*;@A+PHYiwr98t*o>rx{N#?1Qk=Li@Lsomr3DfkjLe!LaASA*_DZ8g)>h z8Jv_6`KHXq^ZWG(>i{r7pVMo03jDBZ>Y^Z*-y}#jr05e7zrCHO=p@z0Jj7oS2*S1p#*&qIN{ySy#ZH8E@c1+?SX?zJxv5ah zs7JeH89J)J!pGrHS-0jo4v$Vq__@7LD^`p9Sf4}Dgm>+g#ni&K@jWnQn4w?~H@~P7 z2NstUu*sid0()BcGYp|~f}<5?hi9t0eIkuPto-^gI1PD>bayx`Qqskx9%IC_fgr(w zK`0XmW!{XEF{p6l0#Gm?B|5}uT8!me_#q4pUBc25#px&{2StzqYs`AZteV*v^?9q(&&K|Gv7aMVh1v6cQ$$%0n#LZmH&kXi`zcs@M&6Nz| z!b%RHP`)$}IE+hyEPsg^*trdx>mLbXe_I2*Ev`m}bRViY6VY@>ee^h6qh%qM)D! zOgw8vk}A>)$00`!H5>xyCJ_iO@E1`?_>ILE>xg^WVuTPrEP5QZwSE4BGE5KR@wcRS zLJD#=ZWV^sO6(I)KJ)|q8(MIa2d|+KxC#IcTVS^kA^g?}{asXd4}8;al)8x)9U)sq z*neXS%Pf$S7eibIYqFOgD#&&2y}yn^q<3Q>3}c#bCz7aqFTbSvGMsH zH(n>{Y6 zii(QPylFE3qFoT5nvz?)d$8xQ{T&a9=|6+$ggAtDLJW{z1`J+cU&s6(KR%ql{ps>I zep!5bGPulsyY|OR+U>*&6O7IZryc%AisQWQU4^+H|^?9th1uD>*U z-BEmnc{}a;!z;GKs@qI~BB*b13_-4xIbyEHIb7t2DRqt{49*Ga?Yk>8dec@Ac)5WX zQ}3pJbhogiuy#cqQz5nmlErwMy-WPr!*{iJJxpZ5JFglvrsL<=IsK~(?aejv;lbnP z=EgRy=X>`l83cC|+Z07uyWAeSB!^tGHMKPlpyzhLeW?FKsJIO&`P*G$l@ILO-Nvg< zF0rc&3y_so;JiITa~(xSeNm83hchl-BL)|~I^~|x_!usySTMu`ZYiRhzajGA_+T(< zYu$8fp85T6Pvl6WCm>qkMF?V-CvSE1} zvg+4l|B#`epIfSYnjLPM;PP6l8<#tyFHNl0UN<}aTZ0pll6p^!-|5LLE3UP3rayss zKGg6nguYst8R`iyRq&*^;1TT39vr0yJ-zmUTwRfTC1&Fe>@HFgr|mBJ1DBVz#Fpsj z>4O)8`v(Rng@pcej&4YQ2D|+&?9Ty$VPnRK9jvy07l((3gB;$=#E|qjyzV)iK^#Zz zKd)dp?sjwyBwlHiTkBm|(#Cx@11~@N4BlhtuR>?~8VWLkUj){#!eB%<2i&hSeF3 zC-?Vfhx5+VAlsGB2q#Mzp(sWFQ7gS~BhVIl3N z*iSIn5K^qa=B_{<1Y>{y&#VA14fa4LKF9lm{PAZ5{^Hq=sSA3g{e&V>Zpbjwoq-G(d8DK|hBpMZ)F=^$=M?z- zNH(VwU__pW_N$-{qaUD0%Fqeu?I-qNa{8P?e{3&(e!vKcN(LE6wO01-%if>zi#;&d zfB(ZGwRy_K=PCh{I1q6OkxU8vcA@ zTyFz>vr2uB3^9``{DRnb^{%!`U+Rpni@qJegx+)bpb}Mkjtro&jCooZf}P}z8NQF* za$(qjC$68pMG$_Xw8o_iOTPwg9ph7K8$DMLi(jl-#3>0wr<=hwi3mjOH1Y#%&E;_Q z2682;^5n1p`*`3AyLQ;O!ess4r6qvj(H?{Z`Pa+XjX;vj{%U&om$?lje-X8GU0u3r zR9+W;UY^?8_|xW34=EsPQ?iq_8*aHJrHKWAjrTQYUGU;RP!gD#O<12@huIwrj2eHm z*N+f;Ci*DILNFOCYAMOFN3QQkA2u}E*QWFc&sntk>*7quVSB02V{XdU<2Kj`k2LtQ z*=Lha-+x=a9d~7jt}>T`6_zk@td}aLQim^b1DSiugjv=y;+c+P;G1tf#_|a7^!d4w z88gLEuw^|8i*e~4Qh)#r;COs!q{|nL{C8_aWO5aFaOQk_3SA~w?Bu-iHkF(Tc!nvt zHY#>qLdn(>5}t97F;^Gycx2t*sevSKIH}ZcSAg#(-U0~P4w_>F5JTO+s|_aKI;q#u+y|o}d_261%ySBQ`0NJ6mjyKb<0AWdh5pqFUV6*EdMBLks zgJUacuW{E5gkuY2PEe$xr1fFOg}Uk(YR1@UNK`D^^i&Sd)#|X8SsBTMxW<;2%SOj< zi@2LV3|#1}Mk*M5>`6qjz+D?nrG>>%eLP>G%!3sDfhXuvbX>kgHYx{;93zGtD>-ra zYvJC+Nwe`q4XP)LM{!9_EoRJ%J*(Z&+SKqDUpYk zo$;xu4N~kTLK-*w>oX6mmFdw0JSe``u<-n4pDVDOmnp69RF6Ly{k;o~TL_@t1C=1?Inbcu= z`$kao;JEy*Iy6*l=?6icaeT6Y%VxnSm;EjHMQ8R=a^Z_o<#Z>^s`Voy1AvsioZ$Be zi{PsCv`Vn4IebAtw|u_93GuLLBGI`E9BZZz8T%;_V4QAPfco+X*6m}tb&ZlA96qr+ z+awqD@e2D~g~zhcQLtGN^sP`|9BdZyMe5=-!K?b(ref}jDpJob22Ls<9U9`e{cl^rHu z>^*fgu6Yu$=l;1A!$)zb(0j;h&_CwT6-EBGMWeE+0Y+LQ7mh?B{qm72+itXNC1`bV zT?T;e@IGC7Es-L_?_MH9@QO~~dshzzU$J`x?eiKSvj2W8=cWp(&x(Ec=bebsj@Gfn zV1vZNML%wkj0^?OgVc}8r{rfE54h2(W%CP9cO4F1NVMqEnp z{HiKN%ivF*R|OO{K2Oq~-fE!hlA)O&m%^pcu-`iAv6c(1Qe2^GN73VE*xxgaE#caI z;Jdokyu6$8i!kuVx{~;uQTJuPxy> zu^{zj&%_yK@@jU3?@1dPZvw$)AijOEyvJLv=Kg14iM=Wf;K zfy?n{#cSX&Ocu_nY$tSh+n-*<%)Xfc(A+i#~g=OIgy%=h|e)HT_?e^~>3`8l8Y z2Mb#41QimHuU`$GOJq%wFK|B$^U1MU&b4u^&!RwNojj{NKHZ>+tc0<3wyjP;L#*iJrkX#b^~ zFLNLiy}*7KL-(wJ!w0W5BcnvB^;)|ITj96(UMxV+r1pfh+y@fBy0ua>;Gj1La(l_ zVo%ZkQq)UwU_L<;@U@3K`M@?}mb=o}>KTOOCc!}qf(+%<-ONlXY<_-TG3wTg=b2RE zpFy%gGR+Qz|IDt`|nQSz|(qYZ+x-2hNvlAbFumpLR*y}3WBNZ zpU8=P2p9DVCg8^RBEWyC0U&v#n|29izy5zy2|zB;ef*5#IKWDY<%4Go!g1PTFsQCXB!Jm~LqBgPl{1*isCsKhpC2O@SNffck*? zKFgBBZe2s=WJk=7Xl_f=_}&+qE5v{$D#*F=J&-1%J1~xUP4R?@_gW<_N!6>5M$CuV z*M3D{(vF!zsUjFREnDX{a!iNSe#e@di%`9G#uDcLe_@sul6(M zHw=`{4H!9m+tDVLmUTu?l2aq3f@F&SjDswNyQAcTLr+HHVphR{1A~m-A{0tYHx?ktSjbUB<_M%*8&+15fG%i`cl zBz;^}*$ohZ>9W>oVsgPDM5`)bVdtAND@f7=lrZ5AkgIMXVJq>_GHlnu1MK8mmXt{D z_fyrE51u<6)ig2(oB8UH5xdxS?J(YDw(SOD<0YW7w!LQz#v;o9Ly5~|-1X0GVg>qQ zs5hBtSM;eVQXgs4=hnd4)0YuBL!Vk&*~!?*a(WdnMISnf3ao<`USSD>iGM*4Na8=Y zqg?y)LyUX%8+qU+Lw0-U!dmxpkbjtlT$-!#MoXO6i*w9|E)?O_ySwD%+aMTcM&puk zpm1>+EBy?W;tamKW4H8Bxl66)ScyJ!45tXhyM=mM)t*^d4@pWQldB*s0aLn-YallS zp9!cnO!fIKj-SM#@#Zx8LV4Ydn0+^DFGICs)@$TxGO<(`!5M*Xk_b-iVWq5SYG}-J z_dzvZSJ|63HY(}MuHxm*a|WOUt|BJ7;Mg_DTbj(mCD#Ko2t?0=u*}`YSniv0?11kw zNlz_pti%?BNbuSSZStcT#>Zr|Tv^}-%mH>|2R6q;jH^*YxqT$7ezHufAMu3pjR8S5 z0$6V34f$2Y$iFiyENxG^f6RGkN{9dXtH*)Rk5-`+jAf!`day{P0yW0S<6EiyO2|as zbh(D^9qI!}RUrZ>Un{D=$V@9Of?$Q1I@geH!mXzpa@x8=WH4e=D}?(Eb%-TYeewIX z*JI3kKC5zTTcDPU))|@^!^~nqw~36WoR$Wc_#>K^u&wRj^VRua;SI=_!JbFr?F!H?_O16g5gU5+_loh-)NgsGB!pZ9Lxwi^PWQ@RFo;rdN2O zJ0{*oI^nT2Or*?f=V;l=Sh?aakdQ4UBBx|%gL*c_1(_xCjGEa3=C%BQDZSKFAXJP&2 zA!b$}d@l`+xurZ#(Iw~WTp)J^q4X|_wvEj+Z*kx(leCOFVVd;vYsy^8ag~(WHpZL_ z)2R+*S{zw`nM98C!@ymJWne0kRF(ZSh+8CD79e+^P+@z}+YP3Gdu~-W&N zfo)AYwv!f{xm1~J;of+@xs93w!MF?xzL-R!f-0AI-Yi4PS&uj=3bcn@hIBn^LU3|! z1}jf!-HM-M0>xX)3XGtfTfF~`+g$i6T_62rdC`1YS2<#EQW&N7*(j&#eIcybLE^@u zkralG`92gp9k9>Rk`yFP*Q2EOp;Wt-z5n_UWUq#cpXWLXm*9dVKR<7Ic0SY(ai9^4 z`$1q?B9;80ADKL4A3SdrV%rEUFT}|PQ&fe@ogcn?FP)CAdP;S}T)uc#0U)&!{{!YA zX}@i)LBvL?V;dTPsg#Cnfb?!}g-kYMi7c<@Q4nS@>VEZo*5ax%fg_f?=7f0lM*`75na~ z$Ymy^mV_ijK!1Ae&;d4ww-t>3#--Zs9@IyVPelVDl;Q(|MO9r%%%nb}C4Dm1Gqg~F}-4#wUD zzZ5zm9BbV3PXf!FbSQn8`TnSbW_ijlWPMQGIY|qmV>467;7g?d&XSgk*Vn(I(gC+wEDsEV2F6CiL!_g|Ud&MOn>FZw zoL;ojp0xfwjB@)7%RyB`ZV~+ikUWq+xtMS&%lUhUR`pv5yv{f>~$O&r?(d6$scV%;g(&2OafM) z%Bkoig5^&FpG41*N|8Nz`?2~9rJA{F@>eZZ{8e)(LtL0I%#!Z9OWhywQaMjom!6M~4C_A8&Ofu&EUP zz(hfuMy#Z#8XGf%t0Cqn@o6J=u$6JH2CA&G5h-uoQv*R|1tJ`REG31VUO#Yf^Gl2H z8XD$HDs99!qOe{Udpr#j6lzDmqLu=L_2`YSbp~_HI9W!&aH(tLROY_FWqz|KO9@@r z4P32{B5U-qLoF*`T*xW%B(M&}jd)bxa#e&(4-K1mccd*8Ku$Zek`nXXcRHGS>fA@4 z26J~}6Q67WMAlMLQn+6Da@E@AGpxpcvF<`0DFcX4tYrHOgM|Ds!ww!gb_cD?e1oB} z1hTHP1b)(I@Eyxvrric==uD&vnkU`2FVbf@+JY0}xrfNj_^M)1@f9vrd#H#%IEg2F z3O-gRre;B)K1x>fRay|Skrs%;>(2ruTgTk8K452YsMYhU?kjx408-oR#IJgIg+_e~ zuv90RMhYRS7lFJv;1rE-iKCaXopotko1xID#6^1P>^B!oja{R5NeihPKu($fkT!RW z%BD@`(^83#R`%YS-#9hayto@S?iiT~S}4-ba;%vWm+W z;E}X!m3!BJ$;Uo2)IG5wVjV-#i1hcJmIpbf%mdPNf_g=RKl~N&Ld3J)(%^P|0vskK z!u#${tSOqm7n>J0h9V16veoZrJcU3aCP?)kn&w}s-v7ex{9Ba!UznW#!CkUMygP1e z$ogORQ?dC1dnit(>xj@ow^RRG?vvX z0lobrV~4mKzw-(M{qx2DzYcv{AGR+&g6_R87sZ!sc>v6Kn+~rcZ{u%`tEZqsL`c3# z5eAL&kpQC5={IKo@wHIt^OH|!w{C!klbpPKZfz|VH#au}Bcr&hD>p>hrL3$B+S)QU z(zmfGi&OXzjYaZT9FInW6)wp@>gtGz$T9yQZTW0^xk@=rP54YqOgeFV=(cd&I8KB(5Tm`U?i~v>UR!f$uCKvB zAEXvYH5$=i@yyb}HPW-O;|Z$R-@3pp(H`VZNRPlDNlAIt)fmgm%e^l89wDKj)B*xT zEiEkpV9fr6yp9giv9Yn<4M-zG@kkQI1c*0sz?!4Sie55bmau2jagoQ#G;2&whjNe? zo&Rl~S3TNC(d`R3JdY$LllFY90jb0i8%uEsU(%!WHZ~UYalZhFr*2f!LY{jMY08!D z^#RoWFJrd?{E1ttu%n~t>GfF8!mhn)+IEd$i%@nxY7jTX3I&oQ5|EM(j3m(i^+L|b zAUSYp?Iy{otHb{N`?vVw{6so$aCYgkumZEZ24$HL{4%v(%4%GV)~*o4(YU0OCU>iP zQny;Sd5W}^49}U%T%PO`VuimxwDjOt%#EzrhZsh#FSh^(CM8*I_Mhy%1~+`wRA-XU zqiEV#6S5_w)}qOyVb3?+CcO{3$c(fr-7q0C;gy$+YgSC6lS?FW$Dg*9j66!-g`c(z zlVk_YVWzwmINGpr>4omx85i8RirTutnpZnlW=mRGyg#eg%ym@G5*dY)a$e~)u4GSo5cpcS5b4rKH!bs$H)1#U-wME~0&br>|hr-fD;!3;e zFp*6UmsuX7Z#wFn`H%Bg~6}8?jkr?J@xiGiej5(8$Pq zJ>BOQA=uiT`h@AcN1f436yCoB17jc<;apRgG7rD!&&XImklDQ%_H|7*4>Fa-k_)f1 z(9%kUegBS?A>jIz0U?!L^vYreZhMLOezQYUuc9~U1+*R^=vKz`CYBnKFpf(% z1pSOKBXJ)MaBSqlS}dgk7BRp=Kf!C8Bq3jzUs1jkFubSaHsVVqj{qYgVi$J5kk%@L z8^!hXNYxnC!Lxsw%~UN*O9yfuvc7rQd8MRIdyvpEN7v`q#E%$h;;(#qs@5!pI?R<=`lnmJI zv*;f+6^co*f=3+dq9 z6TsLbe9?5sb6MFqGS3?68KA~j;_4G=$xS;GHIhd9K6^z$qzPvoYCjxu9G{RClh;cE z>5&z|L@1wYG=v5l!;zUlt9TDpUa)J>l zvII~aOPYu8=c3)vl_`sLNTg#2Gm_Px=z~;y8M$4v42ykBo@u@hsgp9F)fp-m%tUEA z7L8lD zW=joX;-(3ecVQFEn4P~h({bvi3o*IUcOqaKLc z`yA-cE>sf-XSh@_ELjubY$kUJIZmIczNC#jbx{MFhY!zq<3f(7E^3=OnqIU#L44|_ z(<%kIwJ0g}r%c~Kdk9kTQO?j@9zA&|JP{;4S6izy24m7#fz{B#R9$=Mbx2~K%#9AI zQOZBXWvH-~*7O(g@_uIHYkbEdlBvMC_e34>f3-JkgQ}Z#jyoa+>g*EO zl}fLoU=vOVYBMp-@K*%PR7Xr6QT=F=ld$8ct)B}xdNL}yg$wg$QU)wW;${-!Wy1V! zP(MhS?>z$=ma7N7!ZPX1zat`grr@r*Hd^GU!Or#7b!%#{Z-X?>p#1m|T~98}lZo;C zbgVukq8>yez_4i6Nn}&}eNQM)o=qq3rcjpR9@wP!-{8rIfQ7nSC~2P}y=OAgg4kB0*D$tI}vW5hhqur1!S{ z0|IVtVfeNE@Y03oVTMt@?lI5{i$i z#H}O+ukO|iXX=JnN&Gx*7*Wo8Ga+%XmlAUjVBOUp8W}}WyS`5JBTZF}oK7{z(ET2E z(uuTl_aoQTlyJc=2^OkR{U+)MRUQo%W?*DBe+7U9VW@22@pHu&F`y?DhfMJjuCbFz zhP1LTRPT{<{POp2W%FiobmR;*15$iCSb?A9lIYEK;&jsk$_e?`(76a~lZP0AByPX! zze|nFq2(7B;<-i(0g!HPTccIvqS5k-%b;&@2jr@gQWp4XmT1Mt3gGDaI94R>0QCYn zdZ&_}MzCpKUNe39ohY7Wc1nb2_$aaR!NK8_H?HpaEv*EJEOJDtJ66S?lyp|Z8qX-@ zWVt}n8PN=ED;f}QT|c_D>y-4&z5u}F&1&pwb3ks0&yL!?q4Ft8feQIJ5<6Xp_ss8^ zsI&+t7f8yBjosWhsL)7aFc{)UXOemCcj-$shK$tEvp|hbz$(xcN@dU^8#|kLUf=BqYWtQR4qLtlh$+x12C|oujKLw(L6*WJZMdQ!64{7a z3_81(KLlk_gNN7Q1ej;YutX+Pn<^@tH)Jf5Gszgr?64Khh?M*G67{Y66U$^a0{Hr0 z^d*25L`>TFo#br+i@z3fHhv02`VH)Frfv@phh6aE^9!%rQTqsz_;a{s;nI-2njY=! z6kOcj0ZoFxjJLF-a9MfAIEb1#68&nGhBoo-V4K|8)_rCuZQZ&IMunm$pOn#eM@`~| zh>yc2JNaI;W{n{a^D3Ld{6fC^QMb$W*u&Vx{e}4`Z9ntchF_DApk*0?)Gu(!G?JyV z3)uV%p+=_p&E5W%kGv=tj{{g#ghKir~o(YDg_B@0#|rY6K=!P#PEV?JVI`G#*n=tVHcrQGzxv?3uW()W;Rk z^YU}i?5HE%cgLstT9y8IL}Q_-6;n4fL(39%acOHvPp>V-Z1S@&^w$yCmPQx>T3jp+T!gB8|NcD)9`HROA-JZc-7F?@z4$Vv zRVgq|gd%YEG{m!z9FdomqQeuWkCqq6S`HK0%fFaQd52Y5?3(jY1ZV%Fg^>mT1Sdqu z3*^s56Vw_d+6M9$VW?a6G^Y(0*)=`dL30EWpPPQUgdZ}$@QcBDvbg8BhPdXuI}WA@ zjrHC}XSzW7VNop@n3b7{OQXn|j6AENs(S9`U<;q0hhNjO$&gs?-o-zynjAB}-XprX zJK!_L0&}aF@6ZlI9yS>KGb4P#rJ#Yk&e)C`ZCoJ{e44lLjNCOZp7FCxtZ6dzXOSkb zpb%Mgi$`)VG)KI3HVdw+yAVab`=Bnr;fni(f%d&8ixpqGBf&;kjN~6ArZa9-J{_I# zK~cquKEa^jDDj)mPjPx4q`_CBMs0wo*?B2TB?kiY;Y6dy&zd z-Zy==!)FB^E9PFg_c*3v>zCC)EK=Y4`-7^-=@B!e1Oo4p6qrlOFD{(HY9=XP6o$&- zymsG7s@nmuqGrQVW02I`CdGyZ`X?sk<@rY=WoJ6ZnaG4wOtCFX9iYkAhiks>vGcSc zUBj6)SQgDFEq`;|B>Gg%p&J?zqE%w6QLYeWs6RFty@)oT#>r^yO-^vmIs}yEmkh)r zNR?;~zIdogANp3NMkox@}#XF*K*(@{J`C|A-6qe$0@zZX<};q|z1e zg@x#V8Irx?UG_*bOrYr3=&m#*f46_5CMS{dilw@^8JW~FLZq*gMIQ--Rz?iq170k0 zTr7q{`Xc4#^5&se>1jGoln>lcg^=rDy)c7cYx{3?&19ZMp3Kzjy_#nJIwi2UE zys8h}p{%qei&shUFUMAnSkjG-?~0Znra$k`5KtA6aJY-WU-J!&@mKQVxfjq|?8)To zj7#K@qEPlXt-VN|nr3}>btSF!-~yiMa%+eTr_m{BXt9OhgiNm#bSGE_Qna88OYw6H zFT>K~O#Hw+_S_mKy(OUw%u+gkXh}}n-o!9AR9F=fi*teV??`p3#MePNJ0vK}WL4dE zcV)=!zu0+r=wAMWvj7!H_u$f$1`JTfVY(Vykz6wqrfY)^8f@5>^#e<_c{*y|HzeX( z5^A9demU;5_J!5Jngv{t6+S* zc^Yk^PwahI%|o3L`gGNQnBbP=#}2Gak4=tk%c~?@A$ej-hIm_8<$^t3rS9^0%7LZy zRKS~+b-F!H1q-#(Whg-ik&=pbbV!kaTjespHF@C5o=NWkhBg?k|3q+^E9J0KMhbOR zK@WHIxRq@x$O9e~2&KE#TyHn7_)ukgfv3)DOd`x0(-r8nop71mpO2hjhV(ci#^-;= zQK{b+A**S>rSSL;2J@bSv-0cUEiyjv@fWNl&aK#l&YYmY4Zif&Rtr;@oSe8dN8}5S z`5=jq_PK7a_qkM#MIC(&1jTein4)O;aapYBNl6IN)Yscy^nQo;j|0WZY&vv_IBo?| zcxAu#&Y?bJ95{I5`x8D2=#L1-8Wi}1CAbEs?EDEWryw~*>G_DY(HpBe36@?A@|R1V zifM`GYEQnP)Bh|O&1C0`;q^@zj%oS_Y1lA7h@8eZXg@)5Th968j>22-_9;1~s|pI` z;tj*}v{UHFZ!e}Wuy5nYevR;HW9ahhsFY-EdA0K-l65w$s_z#M zp z@&yWIWG%>vO}&tr1lY_VBnMt-yWw>!whx&)HqOh(rct!1@2<%`pE|DXDVZ0|x0uaT z$o=Hvd+$qzpTTDquTzxLU3}>mP zO>p}19`Wt!-q2~!t;eXT$GibagxmmR8+kM4@8^X^cGUb?XEV^$o2dshS<*o9Y7vQsX)Qx6C?Oz47JDn$8D`FAE^l2PD zGjE{`XY-SWl7nM1YnSfqGrw4nxc(cOLWR!#%;@FiCCOZxXt4gAL2srcd(P%jdcC!` zio&jZcU!Y|rp;IS?Namh9nn+y)AIt^=LyBiY}kHea^?{qUu0O`#|Ux7;J5S zH$65c_4yRiuZy_y4IwUShbNdHzf+F}L$I}`%0-zss!P%smkN>^8&ji~ijty3mz~9X ziO=L&i%A)*zSX~~opCyyWz?UpZo`|{6xw$z)+7iL#d@kdTSK#leYd_Zllh*ov~A197%C)#=U{<`@3@AM>3Xp^@X6yr^QQ@9 zmyj0Up-`@!TWW~5772IU_|xIbFS#juOy5B(%L^k)Q*xeXosUf7X%#H+q$r~<$?F)l z;rX@dmEQ#6s?)4dU07Hc1exV~Ta#KtTuNZf3H4nB^S$k~-$Lx1px9NBM3yQKV;4G^ z%_@YAI~R!bbG9#{dO&5b%D)mDc)}tg zrxzCkrp#0Lt$gneU>SN~^-$L(lNhQxljaOe`pgxcK$;0%dMFKBNl#YVchBStYbf?& z4}a+09&+-5ZD_>rG^ND`hYsE6N49N3SVw(~b}IdQwr&F~o9xQZz;-JHO=~vmW`~=4 zu8bBQd`)~Iq|}zC@yp&zN3gxgeY@>sZc^}#*UM<|jBB1LvuygWwTbg# zhEn#YbR7j4_EF*A$N+F~@C+W0B#C6&$@ReqOi&+G6cnuQoL)R{pL5&W1g_B*%T=)% z8Iqo!+_|~A2?+@s$wuzLdS6r!i-8_n9RM;H>)i`E!OiZmPTiX(h|;AxHHN`X2DTon zfkStvo#mrCgdOmjp0m>Wv*k}Q=Cq-vEs$TX<^FE%G8!!7IZ8m$xbrY9P>Vxi=H*Tk zE}E#m1N|cfcUd#2JcG@`p&Q;at34VCDgxMj(*9Qja zqXa!ZaW@&%&p=0Z#7pP)8Kf_OOdQS7kO&AAEIYx)OXu-<*oNrp9(8#d{7Fl;p^l^I zhNkSOpLxH7Hk{~4!15%Q8Y`qE0ju_H=3d54K_jvmv;~i*=>YakvrVb=o!Pe8ra+ic zPv?8iO!$U2r>y*3g9r9J98YUGMzvZ@t-RMdt#zW5vkDL5*^<^$ZM#nP?!o4C;yVgCYLA$Vn zc4;Xosox}t$;k>zN-Q>siHUyQVq#)}AtBRQUOuu(AKwqV8?6pM>{b^SN$(?qa&rmK z&d)*hnZ9GnvLagEXm^AIMb9n7_1Eci^q&sT3ychrJiHNgfSiJ!m-No#qB*#8IoWE0 zY`1QSgu(ICB}~_1SS^DvQ>CVfrL~ zO+%p{e$_8`PZ!;jm`7CC?2ov~-;I@2%E#k4g*oJMI7YRD5Lp+2WNI-hmdXmb4h>zb z3=6s@z_RW=3M(U55)GKT@pPUwNVANmv)zhTT~niCe55p(`7sHU-$33=_k~} zV<}{tvF$|JhS}5k;?c3Owqhb+_OBhdd)9)w^QUaoZB8Oel--mE(u@~#E)VH)>=Wf! zjlUB7iWJFlW7soMIp<|$G-n{g)0%tk+Moi)uv$j7mL6w>Sw2@((Zx%%Dt4Kd+1&kK zo}W~GsPUttqrX^6L#^Y%<7WeNInv8VDbwD9v9Z;3WHvUDY#SLMIqW)d>J>{1dju)|U6G;k%;f@@C99aEXN%F>@R$WbfI zYqG#fpF^#m^SX)!fLOP%2W}?MoiK3l+5 zH{cx>kPe_E7vZq1FBj zW|0_S?KZ#F6A*n{g@49WWy>uerA!B=c_buV?7W+algs)*l5NE$;vFJNt%0sGB`mwZ z8oAFqjhT*YtMcNk-1G9mI|P9%+Tj_QAWev*sP}Wt+kfR=pEFowhPA+sgFUV9RT?Lv z3e}w>&3vAnC+G$&gk;Y0Za;lN!SRsVj+ExU5z}_p6Cq&VIamV*nva0rS_|_MJxmiS zy0~sVvX(Q$fGFCLmFsngy~(FDq|<$I5B-t2UJF|h)QH|~l@=ju zJ;r>4N@yHeUUU{|@pAC|j8qpAMeT#LM$V#ET>Q&wo(&)z9jQ*a?yZ3|t@!?J!$1NXF8Uc=3K`2LQXuO^XV_ zk{DUtX`Aprj#F-7kdOO0oKRsWV(-8M(-|+y-i-$>CezStdtp{(#H^c{Vz@yWD6NC3wW-xmlFwm#&uJBm43W+hlrk!i zZy}KB>`wzpG(veXxRUl;){(Wce_s;pNTJep+r!Y0P_YromfU}ovZ|;wH}3tMP_rdL zd^#{SZGeKm8l90u9T1NXzj`c#5~offC_umiPdWgb>q4p0e;|t;`(2{h0&AE}ek6z~ zHo^In-uc*`Of+zHM#4-VNrV_CQ1vRexH#>^U>JJ3RD@4nU}RMF>=ojal6$ynJ~vQk zi9QcN%*mK>p^Q6^`oyM<<{;%hn<#9{!5M(jk7f|X@Dp~baQmp&>f#sM(NBg4_FxrhwmO=4fC-KyoAuAc7RRgCIFaO=Jkr9XU}8_X zmSntal=b)^(ddHDwHK8zI`M9@S{+N86cIJ6)mvt^e|ScqJ$kH?=mA8_rS|8s>l%#( z*Ig#)i(9Ct+Bm%29`?xI^V_*lwG;m+hRa>6NGXS9P&&{no6uRMG@h7K4BN~}1d+b& z)GhT@aBsxH!LfzZ#?i18$5xu6p`=XXc>^kFYDyX#AEmj!CO9xLG6GlW&-niIMA6Tr zjU5quB^7UZ#~|`PL+Suq1)T*5jN1cpD?mN*(1P05!`Jh2DXnq8UX$5g?&E!~Z;G>T zch~9awG>z2iCOEOtVk)eg<^!*NeFjfPy&5nJVVt1{fT3%Z~Jw4uk+J)U{!gEe+iupTh6MV9Cka{hIQi z{NoqzHH5%dQzuvNC|^UkM%!O$(h;-v=YoFu(}uQiIECG(X-`BWiwjI4smRM%FDT7P zEEo&`QZKiO6?%zF15m)&^C5r*m=f4$@$$x6WHL7#P^RlfSanoKtrv)6-g$eg^Olh(A zmyVk)Ag0*5e};k3%JLq8Wta--lOq8R^1pg zKYP-r5jOhh%!O2+E_fxK9WagbLfCPVM4Sigx_DB5OF4id7dzXVWr9e%c^|^Rq@<+$ zw$L~YQ{bga>@QU)2I(iZK(`6s4D(I*2+Mto=bL{3+MIZQMyp=HQGx(1Nd_}9GLi@>=cDg7RKiY#NKfvh^znd&ouzo0 zQW9g))9mT0;Y$pD3fn^3_uBYe8etNlL=lUAP4^%n(mvzMS3W|vp3z9y`6<>0sj=-W zf$q2N=on9E1_4Ha1bosBC9buLNbaXZu|WZTu9-h44iidTaU7kwS>YsJwW{BR9oR%0B14xu zrj=)GmW(wyiUI^sIGj%|yU6rRQr9vv0C0%%YLy+OLO;q$PZA52hb~kbcoYvbkH=yw zE!QxGm12^L0`o1o(tamN2lXyi+)$K62#j(e*>SY|oLj=qO`dArRH23u zd?ai?U%@i;#jz3|GW87%K>IGw7R&vb$I-fr8y;0&CnqnY!8CgWaX84xf}|>Ssta=?yKJ=e*j;sY+R>}YCI7Wr{30h0si~=1l>x(%y}Z1v&rZsRVn00e^9afV z|IQVRY`Rbxe48gAA~JkY+&;}G)bp@FTK7_Xg{)kS9LvDi?VLW#IP`l>nwJ+t{7-vo ztsJp{6=|SnLl9nw4Dxp>iuPtpa*8^rC@sZ=UX7QFeLD6m`H)_Erj6H)@oYQZwort9 z3R(%s+z{=3HlO$Kg1SEy`DAe8S!Pb{xFA)Op5%~3izg!FH?5P*Mp}AbVeD;AUIznO zyPBervS+OYLRmZKo3RUKBB@#U`6^yP(|oIc1Pi4KmVTWcH(j6lv8y{-7i~1(aJ!g> z`_7lp)*L#5;PvNAx*n-N=P^E<%Zca^ADI8>|2+K-&LxX1P*b-cs*Z8GY|!FP3Zhgk zrMRK+Y)M0su{OZfZeY5^9@K>^vK==TYj6s;QVF+YgzWi9bd$<)P#|Lesn0w(sZJP5 zRzoFlpyN5#d#<(TqMXJkeLb2iGGa|I%x3qsZ#4B2Xg0Ja&)#}S%&J8zVB!S6^yOfx zF$%@^M>Psrz={!- zf_xSw*wrCB||bjK#pT@mDCWEMw^hF1rI$ke! za25fPPPuLOb4MS9?^L~z)N1#H|#w2zPahjXA zGeBY_I>Fe{^E>Fl)XMD!iVVl*S{n@PqSz62rPs_U73SsSfp&UZX zFRvgey{slQpVo2+a)+|xHk+DN(-k)P{EV^kRbO->st)=t>?Wg^o+KA?yHt^wRYslH zPb4-r!LPLSJD*X+%JT16hagdv5K{VI5jindCjkTY2~N97#~E|RTduqN2IAo5e;5T`tWO+m4nuWeOw8ix++3EDs88_Aas*RAarHJ;H#?lk(-%Kl znVe6}8?v3-Nu`f)btZ1AgwKQr&n1k+L;*tFp00Epc-tYj7hZ#s^~GP5`s(h3f*)2g zq1o*;52%Dv0JXeNF2WgM$F*wMoXtEl=f=ws9R+y$CkrZYFbeJ`%3VeO-h%vYgU$5x zb~{YpXrb7Rv@7P?o$Fhj!(HEw(rv!2x0@2cc3*`FUFNXx$!NFL^&HN7GTeM+CSnM} z43Oaj?RGgkKFCKo4_ztp2qgXRlDQaFEL4OE2J@^_HKw#$Si2p^NJhp584El5Y|mE~ zlPVR1pN&;-hd~_e3O|ZdEcDY#SJ{LU8>z3FO;m|~nPkbVX8s9u=wh^o+2x2*33M_a zeZW&kCU$+9=!%WiArdbbDHQFtUKpsQyGi@im>H1g-FN~3&%MV~ly9D3EY+v7b zJLZ{oGm)0DFp21Gvqs3{iLxcbET%P&hgk5c!g{8#!fVnp9>KQ|l$S(qVecpfF7MpT z)H8}T1>8mjQ{SAKW^En5w*zSg(n^sK4T@3e?=Tc}C8cyE;NI$n0hasOs7$A{q$PLQ zDLAv4V1Q((RI>z>etK|Q9~=tmfD52i(Ful-vhT`H{j`#*FwgBbbAq5|cuRfyTPcO2 z!ZsI*s*g?Kv1$asNC)~(e7kZGDA)ryHD)tE{d%;yxAuzdx~!h7KQ)6p-EHaQZHOf| zdma3urRS@%ImX?)3V^%tLDbMG&HyEU*~*1#(SVnrRKN!|`YOme`^aV;zQ5cR6c&PO zSIA;uDg&#jxqo6}V!A?$&mujA`-JZ$45sYKc+=Z>iI>(dycE7s zM$4j>nz@3L<;--Fzrrp=+vc1+eS6;J8~4BbRXBYs}#y19_e^$b%MWB ze19m6fzHp*>xzMMhKZ@eJt7(N=s(97!9scPB`<{~PX%qVlzM~VXz-&9K*WVPbJ}YC z6{2@H-*sQSd{t83{TLum9(QQMWw{4W?|W&Ib|nGl12Yk-eC9>n^? zSqE%`Q|g;-8X7pPgGj4Q$h%P~BC}D2WnIO8Gcl<({q{sGxxXORwf|*k-xDOT6rqLW zL;Wly2fkU31&SG=Qbn;9zHh@6hDdYxrtewW1QYEjzfN{C z3mHoM7vzBthHXzZG2%`pgp1lai^Hm`p{svW!!iJZfSZvwOF~_pyxJ?_TE>^G;M%0@ zp5jg6lf*sclUgOF(l@(pZYBN?ucgX`LJ_(K@HmI7KIf+GX`p!P?W*ZcUdW9>vDKDIzeQmF&pK!rAgFZB?;(7-;?v zH-7V-{Eq*jD!(tQT(tiG81mDPLJ=!NL&L}UOhWJtZu|=+uM`$#Y96bUN@wSgsLqC{PX(~Eq|7PF#>C637842GU){R;}eB}#ZdL0m5qYIwVUwhiW^-Aompo#di)xmc^DAc|vP=NH&Cn7$;H>f3=en4W8gG9! zc7`p0BJqS{V;8Z5Ej5XcdMqpSE=~G)Z$N5ZE;Doemd>BU;%dJOV&nKKAgK2H`>L_f zW9&Sh$)!vc&TBYSw4qiCD|<@j8LO`PN*eb!XR3z$8Hz#YYa|XL3tjCVV7k`~2a2vx zKb5-mw=_}b5h^y7upFrp>5-4EiH9EbhG_O|ZYFP!TwtpeLb^zC&%=#oLo~L>LYKnV zXF}v0*1?tSL*D7ZHxe@Co#5^8hSC))T*9-0kEi8OwEbS6ox{i91ks&AJsfaaecs^J z=(_ohJQ>6j+}iXBq8a9TX zTKW}$=&=}W;^Uy=kx|x3{7yfIUx-pVh^TjoSp+U3glC8dvgI1N=WADmG|wsr#|g72 zjfw9aTy&wIpMrBb?wK8Iym_2eV$2Bdk>~@lUxVPBm-XZ{J#4&!m#vT#v?WErHD_@u zpwL!Y4;F^tk{?L}70FXz(LWEZ0TkWZJ%Hy7D>Gv9^FPLv9EWg;#(4gU_SR-Ht5oj9 z4ndkmuaRZ7`#+Za60dXo{xJmE<>^C+UAZOJmX9O)!6;coR1DFN2LXm;%sX8#(OTPH z?>Opgm%@o_{tl@~PK=I}aqlv9rP9zuhfe0Wq4;0kN_mcPfk zk4bd9oOb8G;i81%DppgE3eNQYuSZeDJ4d#S1M)fQv^#LEbbnK0e^Sq@Y^bV-b~mmW2c)SuY#M-w3#I(9d$nOQgg$7)2$stctCmZcSSyrdtNHA>x|6c}k$7|t?#7BwYL z%QZ$n&tS{=o_!~*2pb-z6P!K9jqJBPd`pHuv(c7lC*Hi70m}dxfc932BADjE67~;F zx5w{oyRU#3cX99$-NP_s?~8xQiEt+l;V-t%S0=hGuosKaGkR{7!+X4LcX3!gK{MRS zu{0kO1ZW86_BB#O`9jU168F7s*~7neZn4)QUIp(AoQjyO6k&6>8~wRpp;~N+Ou#m( z`A<@LfR-}(x_9FTRw!;3>lk`saOuW4*Yle%2|OQ|v^*cIgQ*eo9`YKZhe2KmR$oa) zeV>CyYg>N=F4hA-mzyb~aiJoC8$Z8`-6JvTd?f5m3vGQ}3(a*+44-6$$$7+=bWaV} zOw0p7XpQb7eI>Xgj8o8Tzo@-cL9EB5GC2c|^I*aNvZ&dL;_K=bLr%Lx>KkFT(*vR! zpu+RddCC(FS`zJTzFpCSC{vr>K^YF{E7p#?VArc}vrqRs^l=B54u%>(_rRJT?p(!*!2!(5VhLOniD&md(wn_ON z|3XSy-7WMG#_XO3Tk8F&MafTzrQ~f$TWq!riPrJXS%?|s4 zYJ;2D3g3b@%r`5%UxYYN-zoIc#SG`C%Q35in0M037yn!yBKi^WyqRvJkR&@Sxh}(= zxDeoXj_&ClnH8<{D*C|+2Td-^^M!&ghYq(8DEBs4pFrL`9crA6RPE!V68PXY7Kp`2 zBWh#NYc{s!{$vRn{a-W1L$e0dn^=<<)g6|W$ol7TavSIZJwjP7@IMx4dHg-#p} zx`sJO1VHFJ%Vs3>J;#}WXczm%PJv875Eo@(SVp+RTq=5A`|Qqr*`ZGEN0gPN?|gsV zq(8f;PLhSvKpnIp$ttl89Po!Xs+j1I+xd(19=boKdifm%0#;aP zOd*8~>{&gvRp>JR&?!7`M4)Ab_M(Uq#})`1_?JU56paF zRIv;HkC5eZC3wd8kZxQ!@Nt#=wmI(0CcX3`%T7^Sj`l2HMt-?%!R|$HzYc#h2YT6- ze60(;?UD2;h#<-LPC9qdyrd}t^0I`KUD_)KY~$=iF(@Gp#rk&WJ<#Fye;xOrR;Q`3jCF zcbQ`KhOMkj9aMljzcQ>LdsXI^V2kW$ znj8RQFA$Answ4XoLwx#$xCCKf9^H1qeT}zMafjKqW;(^verHw@5&IE zv@(9rf}LWke$s*=<>W}+xvR&V!7DAtul)NLrTB9yO~DXM1kzm#q~g~OnU$3(qkrk^g-e0uYr@c9dUySBEbL6XLvI&cn$nO` zfW}#wP1GJq2oq%u)MBIR+;&>n+P8a=d5Zeq=FTRN-5I)>*}9|-(i#?dZ1=#kh+ZOK z-YJ|+9=D0?z%+e7M>0Th`asjgfqpf)ivHI?kLrzdTooui~$)vYDaJl^rb}pun zu?#Kz6RfUENdEJ^{^0Y*YAj|ERt(UmfB9R%{l^1&b?n0lYKgQkLj|(GrMRZqkxo%)j11S{Ahze^p1$%dCX1tgJQesz^)5GeZ-qm3GKWg~xP;T&=lFcrL5 z<)2~Yx(xa#T-k!WGvfgY7qmBE6_=YLZ~DPDb~cN;nT(RHoI zz|(B~EG12M)_D(K@b~6XtxeE?*LORJj5KlYdhD>XFYXGAZzUOvnW$c;^$KJvie{hU zwA(Z8qFVoiRUH$)l2X7t|Lm7yP%w+)ig(d)RLZeB9}wqkUuJfO^KN{pA2B{w6|JSK znlJv|Wk0Y0B}E*-Q5%#tmdfRly(y^7-|HEZwmnZauw+m&UU4@~zhoQOQ2Z5?z!#=` zCqWnYG(s2p_TFZy^HyUN6ni@y4@W^Ry_my`C+QGHq$sFb-dOLzUo@K-B#*U-{mk6| zYq5CG%({EF@6oM+8TciWN8lOPm#mX}Jqj86G4G+6oT?my=um|eqa&|jm#<<_Mi;-} z^MwwW=8@T@s{Z>>??d=_#f620zgjFo_|;e}=5@hwrXhUuYPmwQ2Wvv`(JH67G|A4O z(obsNk2*Vf+K3Le@sl{nhnSDf0NmubUsW-Zvw%YOWt$2Dg*E&oRJ=iFX zBdVuA2w~3XaV-F^tf{%V%7mKDvm}n&aMtQmLvlJ?t}@`^M17Vt9BAN+RWucMk~th- zEMn815-Q15e~&v@mPOrO(UbtIrhEJp8@1`?Mp!HIQU>a1lcjEOg+}xVgo4_(wMdL? zKf`S*qF;U90~!KeR1?$Oq5)J=$v)khJ)N%VcoyIpEoiXhz~WMTea5Io+@-uGDYeD3MNt2?L#xAOz3(W%>0q*N?gdD=--(1u1K0wSn~@)Csb`_ui(|uW8lsvS$`@QJE9Dss zGfAAzhZjsR^TgoEo~pS@X_vQMfu)4*sF<+qUQ63z@6V`Z`mk8i5=F-2#|N4gx`OlK zn~lHJUn}zdF!JBwV#~4FlOGr%*4P&L7xN^Cw^-b7OAM2?T6x!i zSQj(~TR5h6-x^Z^^&-AG7g^ z-~0&dv_j*fSF@a&b2Hm=nn&F)Tns;)*qnIA9G!a`Ks{XW(KWQmFIBpUK7LcXzukMr z^d^)h=51=2YSAb6E4GA9^b^d>tp!%LVp|CCBz2`Goi=QWe>E~0ekp67V-n;=pOew| zof8xjKl9jUyQW-(_(;T=(qI%JKvrSumWAC0>@qKHpCqO=xhQzuo1hIW>RXxhE~$o; zw&D&y6Q#Bj&4Y%O2uk>v^3uJfo`{ouT#1~NnWLe*!!E6vPw)izwMvO|v%hlIGAGKn zez`GbdHdg@l$TIIoKC|P=Q$5Sd!*6*#l>(D=z%_EP{#d}>wa^c+-EyRbx=e~lfGU; zzp5grw1M8XEVuODCDwtDsC~7TQL{c1)>?()55X(r95ngs&;! zrHX@N3hfb5VS?lx)8PG2daY7{6~I^_zhp*w zh8c~W#kcZ=da!jdvMQhULB=EHQIZC^pm~UALDJ~j%zbIi=~v*(PHMH7mk;D-xNN4; z8fReRBI~;EHT_@LB>!vH;9J_iP7DYRJ1sp5LQ_{$v*sDGk@yBb4nqV3j^!s|Y!*39 zyIzrutf&P0T(Jw0vEiPoYl{_o*=U%P(ob2AP{FgUe4A1P79Og1+488L-~%3_D`a_910;08j}=77n0CX|zK}#Xc!m zqR>SY4!ME%Oj~FYqqs7$FCzpat~+vZ1c;bYJcm?lw3Vg>-o#+F*BXHBj3HemvUe^s zgx6Gxd^gp!plQ>x;fs8PG@=t5_NZOM-SR5oNj9+I#ohuQr^L32c#_ZY zFBRsZFVUs`?%mg~bDgl4?~^9iwMt#8*dzxX>q&6L%&i< zm`>tm&~-RGlTX}}0*mXM5_w^|kx-OWf{5EgY3UHHZq05HyacRDzx|Zu9E6ySmWfZs z$AwfB`%A0?%?^!G`%T4Bg~}-jtgLB+gIO~Sn1IISmRJsMoE%p^{7zPM`+IEh{9G@> zFaSR1=(Ig)O$g$BmavlfHIQi_{slV!4drAjN1i_2@J5+IAMr*(C(h!sM~T>opvqtA zyO+(r)8xI>^V}5k;=LX<$~ZxxndO-NqrNNjREBdnHIsxFSqM!~bY>EhIgfESTYj|l z$P@?Zzg{Q*&=h_|o>xjry`^H%?A?CQ$>ri>>l2A4Q~w5TgCBrobRp42Rr zp76FuM6Ouxo;NWGgLU+Ri zSGbJ8aD)`)$K3QHbHk?BdBb{@hAEO&47N_w%eSQ-e7PpoanHB8S4a-lE;z0WN_al@ z{Or<8s~(sw(VpS5L+x;9nM5yyL&U+hpfaJqg(~&9t8EvaAz*yaHSOo#7yaJ|#M7Elx9d($;xW{8UvlDf**2wgoC#c+bsZq6u zIN<|(G7<9<-Uo7+{y@4>s&5ON^PJ3JKB~7s>-K^zZ))}>+3|J!!u@0tWOinkfyu}R zD|*0|uSY>_8lwCOw)0LVR{yM;@ObOyfi}j|)#7fQO!!J_0hMVn#g_{9h!O)927eLK zpcTXot|eTOi5zAUQJ_BUE6#57#2ELq*^mTXtP?d}r`&Vot}rKKoLQv?x0arKyp*>n zIBhJih2EIDMDKa`uaA=n${!X+d34ET=gTJ=jna*I9GHSYHapYT1a3v~)o~qp^_+WO zx6~R0#x1~@?o1TBeT-Dt(7qco-!Yas(o1c1)Z>g6k2m+=b8%&cN|jdcN1%gdv5rq=^xn z#cB!Wv)e3Bh0x(tqezmcXo3{q^q~!%-XewB_ftc(n9R_<+5O#)_&LF8ckxp^{c4*$ zvd(L=>Pi`X8wmmQxdw5I+35vSE~WKP$)y0qxOOYpK}bQp&kK?@Ir9Df!-S80FZ-`s z6-A#9rd7J`-%0_H)%`=Vy`;B%u8|!k+P@zTvPEAw(eelTcOI5UJfD*bKF|KldEY3= z^R0+ISr(_(6Q}Mi6ObMrBmQlq3E!2=y?9rUjDDQK>&UL?=9cP(m5G0vkvI?u4>z+{ z*SylI`+EN;m99gm_wWwQ>kU5&-7nP@gdjj{eboJ4q=5GtiK9;p{B^ZR$}#iJcOm;L zFrk>Qs7bUx5XpdT&_>w74R)081KX?{dHQ3gTI}l?MSyG&`sD`hQ2u_Vzu@CQ#L%-X zUAvw!+Y%udkSn#JEI};d6yiRrjm)rRt_9Rh<=q=U`5npbCUOIg@|Yk$nrUQf-sigOY|t9o9Oa` z2@a=|-*V*y$~$~w`!gXyL_3G1%-dKDX4ZGk@RIa6j3EB8eEaXsHT0U3c+B5qhS?O+ z&s6r$H<3~A1HCz)PXk32d+dIp;o@&g?VV0|2PN>PT<7oY#dZmm#-1-rDcX>tp7#8O z8($_NB8m@v+|2`&W^Z6oYT9+YBkhTm4s`c)5a5kjt=sEwPO4{rZ2TTG?JDT@Z8*e6D5d!%{_JKeom$r*( zp;old(;qjUAxyQ@^9B3&mNI#~DlYvt&lzywUr_@lTLo5K+&HHjZ@kN259EdvJTBd+ zPwp3hvGDtd>`nPUPW%v?ofhLduNQ2g>vh;s!jCJ zTV}J*FV@1;9+|d!F?P2+j4;Y#cV#$}H8$i8(BD_s;qD8m` zjVEjMvv%upMt@3E-Xy!g(9uq+g;4>c<0%?AgAo=e{Lboms0Bn7ahB8UgJ8uQ=rUSU ziH^>@bgN9xxGlu6E?(O^TyVXEAS`$KuB6fqJY(Uw4s^YZdP&~PvGSILM$d)ATXJ+S zz^-sZWJ8C0P5+(hedsD3Is#>RjAf7qGejhZTVQAPJJhy94k{g~>`q8?8ozD*nKOTc z+cpuHzzvs4wK1$1sLrgthBK^e_6thT74)XGV0sSNvFh1OExqhd7SxdsfP<&QXmP>K zZcPfN3Mr*l7y2h6@HV0b;}n!a|VuL`;@WDz31so~h? z$-agbE8}_k9X1nSv%UAv!n-x_1qRX<$lxKjU8og1Z_#@tEH-#6txWLKvqqdvE8FuVwQvNv<_{rB3k z>b5fZ?m&*)>{nP)hQz!>w!8L+y^E3j_tD}J)Ma^|FA_JOEg6f&xMU_ENZNfTS{I3W zh77O3CzDLvTd@jYRk_78D({LJ^+Z6w1ff!Uxn`mb%c2O_zdY)_2fm;}rP30U`5B;6 zI)18kCkxyh{{6Nz-oUIT_~1>4-H#P7z4$T&|Adh#*BL>n#FC{%B}s#~es@A(zY)B3 ztw%RnXl}E%rfvLOOoS7${22dnOm6bNG%u6ers(T|m+Ia~RsXFIH@v@G@Pj@)di!oVU+W6z+DCsy95 z{vKQcGxZiRw(Zj5HHmtyjh2k&zVK%*Nv+2q%#gZUR$L^`#NjfaYb%dh$8kMH!X*bL zVxZ0v9uv8Ki1IAHdR9eAX#N51mFswKYP?}EP@)*MXwaw*%(+Z;#(qh|bt(MRcy`7vgdWzPOX^-Ha3CC6MYRWHZ<;KUWdwd}C^&mU)jb~n z%hp2EWMV2XYN!btys%D~IW8wSIdNM^|0QJYDXbUro^?Z;5xOl?S7#sL!}z>=g>r#` zUN!Z1EY>+HxVzY?xc6`hmhBS;O=Lv!Sd_7KKK8r$$7)xYh{#B<46}9bU5FR+N~Avh zct7QnjWOys>vEHv7kI&&&Bi43YRE~wrlv&T9&+o4j+J%mXZx^=s&&V9gt011l`grJ z&#jMX!3<wZM_wG(vtycF1VzuulqfJDj}aX)(z^^wU|A z>zNr8vtY&87}GzkG{LxAoXyN7Ky-Y`UC3PeZ$hDx3OYc7&~Z^!cpO*(dIAO_fMDfy zla&&!Ta)MHoS6yd3LfE~wYhxDK+q=FsuT>Q&6Ua)Ikr!$Gx0 zJWIwtcuq?$q{TPHy7S<6$(OgB2A`Y#@iV`>39>$yd z(M(KI(weXSeuLxB&_pDLtHf{t>mBxd&(MX)hBCsQWd-@N3&-EPxV=a&njOCtuFYI7 zqOg3}lPh}Cm}gj%9v+TCaVb0eAnJU`vmVI)&>Kg|HUL=CT=@bazW9I>?9k_%{=nk?FR~s@nWTbQDAE;9o<@0PseBtr)mGW@HN_HJTOec%(xBPH3p)NC#zrV_ZntT2Gh>sUlx&x4+_pf2!EcBRlGU-KnP zyIP@Cw9_Kt`$f##v&(_!u|^Ilr#isAE4CdIGi#LvOgkAhw>)RNO7P9Wf!)ZUb(xcO zZ9a<;rO(c^@;|wen6-2TB_RSTWW}k+!;L(g%25n_CTYo@p z&Tg`AFW=Ye%1x{k`#GHD`su1wvnXPCu^3xHWo?R#P6ZMPLFKLup2(IF=i<0q*)rPAH@xDS+MYm$IZ17ND z(=L;hAMa)*P|-1D8ydFMECW0`qt+)4C%VbPFzhU!vdJnb?Mg;gDPIPuvePV24GeiB zU0hX-=-r;zjY*3iV#@$IPnDphB&MVzScs7s_JE z6SY~!oVE?r^c`R{H%ZHX%Na_BD;Xj4B0)wJa?t1<;;GdhLLjS(~5s>wA$4D8{8aY2{Xbf&juC;TpEf!;g)O7U&CH8Fd*3D_? zHb(9V86?PGI)YVYYymZ&hxaP>q+02Jbkmffl^jWR7j(nD>&(wcA=+i};2wJNo{-4c zA`pJ(6KX|w0PSlTG{#KlL6CI@`(yCs044p-*(3hZQ(_h5>AUnLFR(R8q8x<-N|HS-2ECP-%M65?CJ8IwA62{I0bu={K*Y||EE zhgH08eYoSO3hCH_f|9YTf&y3qlwVZ4o+!=}jR71a_n3ggTjv-*rwBDUEI_WftldTy z+bo>iJsnf)zS9V{c+z7`Am^#JmBA`w0~SLw=~P0b8Vs&@#=w;fA|K;$QSTd(eFSYB z-M}#_uw*Y?cV5AUY%x!kcj7;a+=SLWHrK$56T_9{Fgt&3A)o=t7$lFn&cVoxPsb$h zbY6uQzdE4)IiUl0P~~&)mYyL^O~i_--0lu<G3PfCX{C0J{>Je2*re3@Jamta^kFwx!wmZgL02a8dY#y| z)|8h0LT)b%q|`G@JKFu;u1gF8$I6U+9FHrjZCI;o0#RIk6aqfQPDwnaLpu8fQ>3`W zs~# z@jX`cl!~Hu*aMFUqB6JNAOO<0a2@>O43}$JbqcrmpPTN;joQiF4=N63xz2iY*j8Ej z)YajUPmZ#_vU`ydv@+Ho3EPQEZ3jn}5LrH9n>rfXUhyRMf)Lho^0qCqjO8CLq+K6= z81t6VOMH9zN_V}hi4`|k6u4`8_WZaQbEyyhQX}j&@pD9_X20NU4J7XF6ZaG$l+SI# zcd@Zbci_h7PfsXl>5S0*xFpX@Y{Z|Z5?Ot5{~*YqHlgZ;Yg3LaL0e?dA0gE4pE-1V z7qMSk1DuH;dBQm=;ialB1Ph5uP>NMJF-yK*VD?W`Xw=HmqDOt4f4 zU)fh+mmCDQKE?gD?0g`t7|3C|%BB{6Wf4a)1+EXDYkrnp3xAoy)|-otKB5BWPr;N_ zu?6qz1hPM*q*PxAZ(N=OPTCjH+TM4{%0xQ~>%2ZKZP2QK|SI4%B z9(*e=dVur2Iq?YpQJS5Irzh1Ql3LVLoz`ni0la=9Lv@7nE8Cm%8_`9rHh~^@Ffv}z zWd@db@J&?$+E{ixC`wn6L3hSYLX7BagOAsu84MJ;h&eSyEiK5>WSdXMZ&kifn~|9Y z^on|s)J2Ef$wghsi}J_h*&FyTk+9)SRmdw6_sf5Z&oIxZ<$1tJmTvYj5$Y{!zs*hj zjViC0G}fyFsmd_`#Y{s}PJ>8Kmb95X0pSAXQh?IgJ6PM56Mc7UQWK9&Xn?}7L{1U2 zUNm_4d%kc~B5>se&>VmJOwwaoigGhY{rSwVtlYNvercx_sqyh@%YgZ#xUyxvk0?bt zen4G1JOk|l`5?SsQ-lTXAbb`JPv`=%Xi~l{xH{+KmA1G4uGj^BX(ZOp%23 z^8PlW*j8fxEl$ghaBc|>_9ml74u4&&;`Oz2V-_7paqv(h2D$}PwhgPzh7aVp}G|zS{nl2yyjY^SJO90nWGnqfeAkf&kzq22IWB^S6vyS64{TrVpw|D>i z-u&Dip+76SO?{X1!s@^Iz6W#60I|d8nh^03Se?Rh5kKC2lb$XACogrvKj|zQn&T;k zs2UH6C(R&IQFUPZ`w9dhIAYw=eIKT0w`*A`gm`h^d>ow;s&h$AFri)quUeYDP(A0w_;9h>{V?r>NCCOWgd@K{S;2dq?J%q&u_jHe zpdzH|R7!t09gFG2BClz1gLT%+YYwe`om#;815_Rpq^BH^5t}-=5sMPxIbB)utZ{*W zr{2616E|~emvY%vMX>3xZ%u9+ z4olY)>rYAhuqkUk9-&sV{WHV9B~vGwlG)~JTE9s=!PEE?z>y|n5)V|64`uFZ?v1SB ze06olU-gPBxwtRiExkHwhU=#;BMbvM0BN_GN{t{2Ir6giimd%};fWr@Ma!Pm8 z;|7HgKe$a6mhb`V-H%=cls++0am)F)p*GBI$)VhB712K7zn&kT70(Z-G_Gex%9ihb zv>h%3q!q;jP|?mpb^qDplSz@AJe%au)jE3{b*6T~#Dx=8K}&82^28^$dCn%NvN~)> zEWaBr{fJwL0d{85@ur|lxj+>dTb$ct=`+(hl~2aHSSgInEJjReJG5oK;kWwZC=vd& zEumJgu>(n?f{vZRh6XPp%c~XHeFshov@&(6-L8Ouye^qPBZ~1BQhD@=ROkI_qaGfq z)t}Ga3ypWl>8bt3=#j!seqs-i9X)Sx<2_*#@MUUit?dZr1wVY$EBMgJnN74_^On%O z5OaZ)Z8fNjDbhPofroEX6#||dzS_$Yc6?bMFw9Vi zv=yxKXzFv`UNC&*c&6g6!KpdxPtjWxOFmSwoO7wyHTe$y(Q|t<2;Q-B?WH45KjO~h z)B?HfIazjJW213&DfXCnsu|9a4f=uT#2Wms(1ymvA3MV#94l3Dg^N+3A&)kVDhS({V$JX|>UE+LlLg$N<0QL=>>CUE7 z(38D?QP?qgBe2RKT~aG`9!!MmR=r<)OI$d&_&J5mO?yR#baPQk-JlPJYL^;oW_EzDYOf+!Qh$ddAOYLDIc6}1x`4~hC_qXy!r;FubRvH4=F;iZ3cLV@uW>OUyHev+lq`v`w>1^e~p7q%yj-*Zx^~=|4Sc` z9^vEAcJt#~?6+3={vYJ9xGjodz0eUl9RT>==oB)&*0R;X=tN60bL=8iNkAXffo z$$3s1;PVyQ7Yrl~48xVwdCgie*0&)uowtzuc7Snor#ygXl3``g%IEF#d#$T*qR%`Po6lJROb)S%USid zLMzHCNhQG14JJ>J11i}(B6DAw>;1w_qow|db$0Wi8~m61C2*U@jUb*CnmHNluDm4| z{7mP4e06XhD1moP2|co>_|wvT%QOl}KlGlB|CMwtxrmq4JdL~t#;rBHTkUH0z598~ zqzo}=c;V5t%iS`%T^><46U&_w^ug<;>y8gaaAL2ZdbZ4K1j++jC(b^Zq`?eCT_S|B3FU9Zebe%5qd{9a($RbI!u<;l zJLY4HufC-AAkwAJ0(#d|g=W862f_r5hi;Y?iFi#)eSOH`;3^VdEi; zb(EdjBc=*x1)bcpBOxUr*XM5x*_Lgw-K2T`1_TqkW=!xdoe1Dn@8S*bs=xS&e+oN? zT5-Hm$R-X0=xg!6_7raU`~V8#hRu5?g{m<&Vy85i!g0BY=f4lRkOSrWy3PX1goby-8Pa!ZFe$sOaKQPt_fWddcR;;4cZbafEe zqVRg*uN?3+5WWl!9}Ymbtt7?7X!+nFn0~)~jc@d$I(~69pI2um@uD89VSd}NmB_$_ zzPPqS_3Xe&&16dWWb<)Y7O5j4_XuUfcN9_tueicWRfJ@&#$?<#cxcXnw*MoII;*Tz zzZ|s+OI{MjCl8o%Kq%TpJA@X0leP8z98nN$RR%oC`%-+k=1m3o&nKR{v?OldhCtko@WeK$X-TaPrGp zb018yN)^1Mx%{%L-N_%xwAMoBbeO9q=~Q_AyFf*E6LoJY#P0>CSNb?bD;6ii>sfQ^ zy1RpsOJ3^H-NoPq1<)BaWt@EY=mK;32U?5y4Lv*XGcDSH5_5Y4x|Zw_1uuVA8lLo^ z2r=}WqP2viHjXHeF6X+4o|`HLAaEa(s2ZG#8|8n>%0T5WHL!|vx3kAycs(k>>~n*& zCNMU!Bq*LsB|EjE@wEIcW9RdtdOvkdGt)1NIAka2(l$9XtkXLq)Z}5Ex|gr&iwHxv zu(Xd$t9)f`*~~xD;PGk;!7bXWc5$Z1m|$Mb^TuIf-7lBGJHtZX~mT zj$OqV)6LF~9sd*FETC=Rf6x#Y)2f&8U|X>==UVM)P>2<)f(7ZxHr;t6Wv*gcp`-3v z;2Llr=SU9`N@gI)$2mmd0|4j5S?{$ms>{+wu0!E!M)E_s>>9JS#%krN=0l>uo-sG< zTIMF5UZs;Qd{(w_!mF%X=v6}qhE0=3dLuz0f>P4HI_d#E*shtS`;tpPUh0J*!u(=y zSu$op?ZtP3`rZ{)_VFL6?f&l|&{ocNlp)RbZULZJqp9I}Y!HRHFw;x`|7eJ# zm(0+Yr}q;dFDPcmO|=M4>6V>E1lqYt=i4Ty^IP#G{ZTvNu|+E8G8z=+H*6^9u$x&H zYm|5KfJQU8oX5$MpO^(J}0lJg6CKLfH`l2(Ys<^CBP9n zO>#)XSl>-@=Jg0wV!$Q2kxnpx6w0GpkqL~3wI!9Y`%6Bim?A|!@%YRY7qi*LMYMOi zN8FYp?pXEmKj>1dS8CJz#Qj#X1@}Ryo)Adwi|M!7^k*xn05mXPbt-}MFbUeNYw(`PGyY1Jv$Noja!k{t_Z1)JHTon0sq#out-J_f zxS5P4FYNvB{XX9Ccr>@m8Gfi-h(v|Xd3SoyM(QB)l=zA^*ZpW&5piY+2|^n@vSq7P z%V>u0N}?CzW|t}&F~Gl)HU3rFI)7c=v)51-PINm(0!6Q27T2w7k#niiOs;m;|B9vd zc+Fi+=e2IvweWuqJ#@|(hs$~?h!ULjBzHdj zM?#flVd@Dw8ag)IuaHAM(tWmN%5H^SMn=sS)Rb~@~b5^J#)K(Yx(4BO(C3i zm3fvxVvikh+KV7`X6!NA31+R+t4V5or&e4EnPZ8r5_31&bF7Sd&iz}enF$MYAR@y7+dNp`6SGBZrmABYQ{EMWf zMc(uwL2TRpyv?A`?M<0ywzYQ`*R~C(i^v`DDeNCpPI19%n42^9Ht4!5A``s{=Dqgu zuEO;ag@F2HhJa)x6+_ss=mXI$q-DuB3g-+i1qmH0Enj|^FUV=!j$QiFAF#)Z^`vT^ zXt#^YZGa~PkUG@4*wArfhZx)agTa1=T^RGmFi1=M(L3x7CqJo;!FYhCze_s8)oSbM zWyRxEP~%5m&3$=S|+9*qYHwqMTSSU~o|e#N!@6x#6Mn*SsNCHNZk(-CoE;kyV?vG1|^ z2wwPn(aDri1WKOa_+wwAZn&J`XhwiTa7hCKxEqq3zF({Go z!&;By?`!r1yMd}g$PKYf$9P{exL4sqi$+vC!^^C?^a1{Xl$GeDP~@mC->yokB{6-x zlSo12SL#z23zWCf7Ja*~G69KIn~*8m%<(fkcGazHGSxAB#|B|-)v)xhWhul#m+HN6 zb4&zC>5jm4eD?DVfiXi)j9>~ktZ1P8oBDcv0iF-dqciuB&piudsb`|#Hm!F$QCYzi zKU*(yGy7Aw%TlBxsky~&8AriooQ~c8@8}6-E+K34reJ;Bds5a7^S$E?oaw&zR(`P) z+${~RSF(!koOPOgXjY7H9X;#P*A~`ZiSaBKWGYw(qeI9*8H6X_>mr_(vIGML9J0{;!g{ zT(B9cu@7wA;_Bn8!h!HILC-VnYev{FQGAH!{C{E5TnYRe*JoeSiA|b7VL8w|dVR&l z=wW1$PssJAOdDX?IL(BpxRu$yX2EdNmyQ~3@y);qc>%{As#T-?0ps<2MVa@BLcz03 zN>|&*ugzi#o>D06YeJ0n!9%K4SuU4!1e&kGN&&T00f#zUhvZW<_IhdAqL?c!9XtHnkcF@x9L0Z^UNDq6k+3$bJh|1fTA{ z26MnI-ukkzqy>P4E+x1iFUEo$hh}2}E$l*zc$m#rxzfDsO8dT7d7X0q3tp#A1O2n& zdWt2oQjrsPp+CT#m%LS8Mb>kbM1P!|_Qw2^R5+)8SL*t$8}^Krg`&9he$v_{8(({& z5=QXnva!;rQ(pLOTx#UKa7coX9DhaO^`Usi=tx_dV-Igq7)sTnKBG2qfKhrThTvMo z;;WcsBSF=1;f#Z%8Bx}i=MM_w(S(j??U&y*;))fR9vxG02(UU(#eT z1(F`l2!(~+rzlpG^`v=Xc5+JvxpQ+u+%>7T^XhTTn`pP`7A3wnG@7a6L^#R02}mR(LfD3OqG0(Y!0Tqndr$0a zDcJG|V$rvO)d!1VzAlJ-47r@#;l%8(`}NiR$|@i6gxYu{usNscM+EM~Jk14hb zON5tw+WYXkrVt5xpYi6WqAYrFX-$E!ZiItz%!^o{nA#N%CGkDys$Fp8iN$ zL;KGP0ddBoZ!De(C#A}b_+oc>b)Yw=zOr0=;h^&^4uyLdP+`F}%)N?|+>(0q$=Yggj;3$A4~RamAv zwr`el>!ST!2G!L_~&!ON=?6q0ybZX;a{PmOUzm!s@@& z`X!8v_#Um8;sL^Ua-NfLXX!yz52mCCr*8T?nB1%J__HK1-Bz?TGw4%CEXv_uDPH|= zE%yg3cXFIdy(ogh=7WfH(dzon(B*6Is9rbNXdv|9qpDcikHywkH&K~y1%u=UOdT<- z2oBVo3p@_VzuGj^LiTUiL6tM>*G(YgfIV*mLHxxXU2?HDXEJfHa8vYJqCLK0OYg)V z>am`e4$32aDoN z3DqZ_ZvTD=d1h9Q49{ioNW9ifsXr zKy>Bo1Y45okg^rHk70+=B<}su{@2=XE`0mcCC`o{tX@()2E#8VS=zO`s|HRSd8TQH z9Td@P_%65a7(J%QeM1Y9Ne{CrE=>ewq3ATsptCT#6&lCM*~@867sR~cQe=0$OL55E zI|vJUMxT?ygL3l5_WUT~)vb#MQ2`REpqW6RwC#(d?!}mM9hRQ+=m#x&TfL9Tz+kWL zEbuEb%rI~l6=eseQGZcn8{f1#<-tW95LQKD2SGr&Hp8Re{1HMT-}HI)8eQeq))VTqHuLbQ?TH zcE6_@9oC}Xw_wp;=E#js`X5gVXR>vEmu6${;@TJIfY3_ z{cW;yBXiuj#l?oc!&vl_DH*Z<&b?`5UNDKBmdp2*j%bq*SG@k)O}8cytak0}<<+0q zd4;CGJsd)Vc6Ifs;bN>O+P($#D&f)t17Bit_vGULMQC>*;<6?5qb!-}A#;l9x6W>I9bj z2YwkI9`1xl{j4=c1lG(E!pcsU=$p_GQ~lM1PF0@KeDpFZeLtmz6SoaYN>f7)_}A;X z{WM$Ln8L1KxzqQpqJqLAzel~IA)xk0nkX`-zMAnpLINGKtMKl8YJ0|I^xkJFW6xRT z6Iy;G9(y90X0z#FEy-2T>$dPzrbF+_sJgUDY+)UxA~J8LPlA4YE#%Q?f2vP`9vUvp zIg%Iqm}3%Ve-UsoiP^6ObU!rU_&3e zJvBi7yew&L3rX{NbV?=|!^visO{J7kaKRb#z(BIxb8d86&vczU&=*#RmW^+6Y}jaa~-n@0|J^HiRCPmN@y?b&&)4okQkUk+KlFvf*HOMKC04`Ki*6r#0pk`cK>#M#n3{{--*lbRArtk*(9I05MSB_ z1p!fu@-iuDa^Djz4OL#(V0*WNsPsXw2wMWt=#08xcJ zubiCsEr9VJyZ994B-kc#Ogp$Q2LIs$(@GY*{O78G!jaL^jq_;wtk#e~$>J?9yFCR7 zWv!8yC!i#vr=N%d&$+9;gwY)dbBkl4$!-l*r!%`!547(P(l-UdE=HRPh6%DC@H^ad z5OwK7F3{c$OUiEQ_0lg9Npzc8%Q8CMCbo7oC4k~;jFmmuS4|0V$$+@fKjN$6xMMl` zVQ>zRw@^q|O)-#}_506Hm^D+ilDea-Go^|5|GJYJyFHAYc-sx6S%%ci>T0!Vu;E=E z9O>pL5Y z{+)W5{Vc-bFn{ZIJi_k*^}zPkDw#te-xdAUQswL>NE-**;V%sARva!NDg%Cas#LkH zR>*x%1EM|glUb%R6TJIK;-?8wQHl0GUC+&8!87&}3zZO4RjIeld-KstpO-kAnMl02 z!nIf+rG(^USkmLSugl^hYMeso{uY?#F|#f66M+X_NM3DQhAc4J0Vn3ags|*U+Gl#u zCDdg<)7m$2>5)~@vs22hA%aMY@(~t3h#4hYi{~Pe40syj;oXYhPDuSSU+z@S$`$e2 zIX}`jdbc^Jgr89&9*EuFuUn1vQ|&BnA|*#zWjjV7OUGtQ$Qh6Bjs6QG&fdr#68IdC zdhFM0+luLNdu8>xyZE-s!F`+QAFfDiy9^pgSKtDg?VR-pt4+G;%T?>V#tbdstNHEy zn@|U?F7T(%7N>eTVD7WFuQ?{{eyKOe>!l*TwlWb_9Xp{a5&RT)w@2TFLi$|O(Ra$} z^E{_Keppu6NP~H`5Ep-cSzneD7}Zgl`?fephx#V&M2)g5>f+o0 z_VT{&U@tyg_n&-#;;P3poDGW2L4WM4OOdTMAz%EMPe1zytu|4e42cr2T&?X zKaeQ$+Z2ix7#JvT4a6lrzPPd+Ey(Gey}t(BA%e@z zQQ1!k_g7Vp%?c&hVum+!_%tuI-Oc<4{bl;@s3Z!6!D=C#f`OH@X%+(=*(qkVFK0x60`qU0( z-cbW{5-{?dby)Ywgo?E4LhE_P4u6ZagMwV|LieD}IA zrhlr`^YgAtwzS;6fY2qGX#L$-D8uKB#7+_vs=AM$lE4b!Yc7#3XD#S~xqQGC+CFmT zHrU4>+ZZdqs=@ZOwc_e;nL!~6!b9#YN~8pBaZ%M)$VkV-$M72CJ-ag=%aUXRn~-WG4s!hx`SJ)^co zFUHTdA8+CVO5AV)?NLFp%tIU@FWz8vl#J-KYDcko^#i|^FCXg9^c2g)eY1u;7_d&+ zdrr6t#@`j*?Rs)QdxnHzZusk#s5rRiim1+Sk6Kb`j^o}xPyEksBN)%^>syB*v4bq? zHZD&5oYJq8BFO=_Z?|&&<@Z*)3$`t%N&WbbQIZ(L*g(5>ZPR3RUSZV?K}e_N;=@kd z^3em!`3D<0+<^W9jYA_R53ocUR8L1~q`wjSx#gGF%p|B2N}0i+ydP~hRNKpSZB$u- zf4EDBxyMXw+-bJOlgCmmE#giVgZfP0G=r}{^2P?&f8Z1w7El$R&v&R+@actKxNrLH z2yfMKmD2J{HuTGm*Kfs}bvygKtwT8FaLOs?^5hqt{!>!FyHxX{=c=f#*c8I)CmWtv zu9}GJsvrViey3G89Jrr{z<%>e{<@5)?_|tJUOq$BqtXJ(TURQI6vP6gFedJQnehJa zi)8)(C+pQs+R5s_|Ni@-AS0>y|Aff?p9z%xf1mzF=TFZwRR&6mvp|fN&&z54Qvj&S K)JU2B_`d)-RCKZc literal 0 HcmV?d00001 diff --git a/img/fallforia/blogs/2023-09-27/blog-image-1-2.png b/img/fallforia/blogs/2023-09-27/blog-image-1-2.png new file mode 100644 index 0000000000000000000000000000000000000000..0b28a9a02f41601e73d2cd0165d8ba292ead2912 GIT binary patch literal 158984 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

    ;5@J)fp-?z_|VPU2)Tl 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%0DfSaah0it^A%l|k@Ym>(NpA;_a%4z4wwshh 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%0DDKvhzEiU3 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 s@c%W8+UhiXe4Ok?$i0cd#mn` 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 literal 0 HcmV?d00001 diff --git a/img/fallforia/blogs/2023-09-27/blog-image-1-7.png b/img/fallforia/blogs/2023-09-27/blog-image-1-7.png new file mode 100644 index 0000000000000000000000000000000000000000..e2836f617c0102cb2514e8526dffd9d08b5b14c4 GIT binary patch literal 81291 zcmb4qWl&t<(kAXO!5xCTy9Y>ccOM)QZ1BMbhu|&=7A%n9!3Ki6Bn%cL3^ELa1PCsJ zZ|?nS|Ll+5+EaDvRKNY!k?PY@Pxq5#pr=kuKu3UvhDNNZ@zMwl4FiORhHj6C^KV5? z;AQo{3%b9Nx(Zt348xy)0+ySyt}+^0O9tV+1NJ`|AEaUFkA_Av{GStj+^5zV4ehB~ z^QE#0*zWKFyVzzquMx0!2YfH*@5Y(qtfIHguJ?^XF8!Ud>su$z$$Wm-{P*wv_wo7N zpNAz`(;sizt=(xUj`wOB8dZxB>mJ9(muz@g7|}1DKf`O}i;E)v9|1b^3`6hV(m&0X ztSX-LfAv@~2ShAe|9i98|G7Cl-qdecY`PG*t^c3Z$R4g!QA0CFWD!iv`lx6@-Nxn1uLuEs3;?- z1;rp{1!u+Q;#LYJl ztzW3*FXqI+=br)9CO8MQ)liyjW(LVlftj2p!mz=>g6XY9tZV(^fxQCxY(V-On;hfd!VZA544AR9x@o)R-w8Hj41+T;y2zNsy&EG7z}{*Ht!Y zHnJswN<(eusf3XxRWr080sCCdyj!JV2;3Te8yF-O#2sgm1 zYp1yRF><`1JGNU5noDh!A})p&ieKaC>qd-|mCBaT^ZOccY08Dlwn_MV5FBBviX9OaW*~BYhtl~Eju_kpc4}l z7nbxYj-VA-6aH{&{1RkhK2@s=!f(yom3;IXei+{|%|*9<)1|oXu*BMq&-uk$<+Jf? zahZDN_XTl~fCq}BPVZ`2G46GwcHl90yR|pwU2Wfa#6J%|>*7TZ7I%F?7rufq)Oy#j z_-UDBM!SRrc9M~Bt%b=Htn+=5wnZWaSKOR#C;NR3C==^B4EoNFj>6RaxwHmxA#UAw z3p+ctkPuordU^`CJ_2j>ke&>l1cr^{SkwkloZYlQ1}*}>M0N_H{^0yRU; zJ@D+39%D9+d-}3r@+R$mCU|Q%eLQ&^_Ik>_C~JNsA6N00o5jRPCSLb&EvZ#NPl;?p zm`)K-sxJ>eq!jn`8J&jIPz6GG^wj+j?LCDV+0jV)Y}7PFX70szs#2PLRBEdlC=xMIerpHHKnGe z?DxV91Dad$S$|ne6OCBN{s)uN^9zPDw*w$cl4Cmcu;V^Wbu>1Bki0J_ZhmyBg<}e> zBgzlL#OAR0a+#J&$}EH}J1?<;#EISAQh3o&!7-AKTS;2y_=|`*i9kI*PaqkGpY}Xn zQm+4Y`oeSw4A$~0>j1%$j7doG(cjZB^znIKyeLW0oW8FM`a6}&0A_rFk45`8)H~O= z_yas=+OONUQ9D61T9@U)j-JC+>r)wjTy`%l>gVEwzDW1XR(?#y3Pd z0@@f;UG7A8Jc2d_(q$_EssYbIhm}xTRdO2BH|qRHggTtp%Rv(}i^d9V3(e?%O*z&=JuS)y0c5%jnMy%8% zpFy1}li!s#T~+$WBo?n&qk6GgI-$~dK7<5lslTdp8S%CA zE!qp;0k0T$L({Gd-knNRbL6dYb+)EyJg93lQRq)`@(9A+KtETkra+AHS~qT5fAf#s z97ZBIJ8LjaVE(TwCR;K-o=6vm30R*1-b~xLPMTb!WcLV%F_P9q?i}H-M8)J=-d2KWF=h7*xSPKI z$ETpY=$&OFOGQT|*&Gglz&3s9Hsd{Il{DG-oW&3mL$(d=5^5nzPDx#)=6I0iz5MY! zOQ4{y_(|l}CL3{(JSHKB@_=Jh{I~ck_=)J7KvRzQMMYKVK5X#llNW)R+aqEYlAEpW zTd3UY8I=s#=TFJ_BZlMSv0#}%t9KM!%ycHD{;vyUy{H`JL1y#(*{|q@T;iAYCa`&J z_h!=M=7m&3tHjTuzE8Wsqk9h);`z@q;^!s1%WyFha995ZD8bs6hfBCpgzWU}>t-Ny zcpt)^Mfa;xMb1t3(A?)KUG$x<0I%1I|n5H z;t#UA>AsQnwY^_edRDwX@#s5T7f7j`6_szXS_h;BZ7(2i!C&_EaP}5aL+Ob25##(k z1CZ~zgPATSQGpjh;MiAg5_yB>lkI5kA{<7hZ$S$!xqFK|t%W=2Mbk0oh113HBGzc* zd-=WzvY|}%U`wzV`nXCV?vI+{pViR`ZkL`7(yBLuu!h9xGF)OkwiJ7+Djx$#oTzxh1S=mNKM@sEpcO{{x zh*NbF>5E!ue1V33uOyeHP-ha}E7-y0-7_qAkS#gcM?^nEZY+3xE z5fN)WS-Y6fmA>J6UuGX=W6&8nkcrlf2*6!>mB{{Z7|G6F-N)pF4n z;#Xwx(eo$m5?3B96xy^cX{zQGeL#JU3s(Zk>T1;c{;M9wFRPP(axFh}_Q+MAMa;CT zS}k2wUOj5sOI^}yql$k9`Cc>R`82T`SfZV^^4CJwGrsX_I=CD4BJgW+r6{Pb$#BaU$eel$lbH-!$Q1Jfn^`y@HP zWwkJVslGXc8v@@V5DqWZ;+%Z{)G^{&=YxZ$OTGvaN6Bl{w9jai&;V5U16KS-7$sm> zd7#AV&u^K7ZF+hpPdygK;pMPrQ0#+gxY{n2w<8`#&D)RNRHw3~ zkdq_|$QQd2i-SQ-=OIk)dCi%S6|*B)M-xubLu{X?8%J` zw`vf`Be+Z`O%*3Ua5gY;BHGS!Q&}P#zOg}Y2m+`6LvFl-<*v>qUzs~o46!Sbq!1%|yN_}Zt+T9gA866CVShE(&+eS`4 z)V2+@n6i~^kDq{lnJaSIx*a?pW;H9ov2XPZQRG4F$vn|?ShRqgS|%@#*xrb#CQArQxFZLQ;!*I$G4h9O0Yd?FZdER#^A2MDMtD$ z!W66odOjG%s@AZW`9`dm`kqd!%lyFPHID!-r?{~1xU?=Km-Q#8u_MbkrY~1uK#x$W zT=RDtmQyNf^0a8kew3u8tb;Z&00+*~uhS}%P9FRX{)u89?R%`Ekjn{CnH)bAQI8=! z5yy~eXBVQQF^wihxLCe0FldI}0d58%(UTWgIv4&)Eu?s_H zQL@SO<2iBxLUv~rdL@W24%1|WI9ECG94Q2PgoFfFiMU)$JJN8gt3(_DDh_^Gl-M2@ zK+s07c$q&{I#R|5{KD*ZJl23wVVH{4hqtV@8~@Wc8YcUd`9YT{#7Q6HjIkZB0vQ{1 zOfldz&Chpycd{aTI&RdHI!~siES`waJAIgfv2`mzfGKZ;c#qIpb@j{8pKa(D_bug8 zB#kgCJ3%Q!abqN@ce>R%eTIv(g5huPEfWxo00|e zOS?Lcn#{VEBsr+Mx7A2tFTl5S+=wnDsjy(6>*1d}wTyyRTkLmb$8Uzy&vOcfX{mgy zHB9)fQ~cift|9O-2{|F@=kJPS37^I_f49;&A|}mDtKEe!AzC7%8^2yefG^$6yQ>!Ln7AMm-P)w-dZ#75d@VV+y#XkU<~g;Be@Urh04Ex+z2@cH9Ib(xTn@reztxQSnwk2 z8I4^#8`I2s@f<4sFByS9!Z5kTI@?$+mvR{egKxjS&fHt&3r6!KZGjaWdu`ED9cOHl z#1*_4`y(qP1sd-wlNB#b^1`v+*miWB-4yCn@x%$H;-8@@ZScqa4s!d(NH3&%qA2pu zwLIlsb51W2Y+xd*!0DsRguS-n&sqz&EQQB|`G~W_zrFtTpa+lkr|UxgswSXHmY)EP ze`8z|>@FDP+ta4Om#|AmG%K6gCiGP2EZj4br#eiuf1kEvP^3=tGh~urW&ihw% ztbm=|9|oVyJ$WZS3ZX$B#9-|8g>ZOD0Sp}F(sIU<1XW;1JK*V)caQjVl8B2&HTjjh zq}Zn?cdViS7lWe*Hwl-#(cF`ATND}4LgVa#3qyri<~STM0yv3M&>H26Njg3^OS`Nv zSC3{Rr}K?S3eJ-bR>vOR7UNKzGPrG!4aCZ(_;rx<=wHU+3U~Ompxr0xQ(qb09FhFV z!eS&8=@5tLxQwY}yw01v{^kqr;mUFttmg+L6l8B0-d1yCHma5x_x>c`;Is70#iZL~ zA*63~(}8f|^AMDkeCeU>z~WiR*Umw2^pH~ttJ1_8o<8lln8p;io5VO_)M7rK{HYrl zdrTX z!C9IqL#{)!!k^8l=TINRnJY~oDx#wzc`13ICzcaDu-5A`)#9-zjBE061%5Y7y` zMbU2fOavCcl3Et9qxvN~EQDUTH?bx;98wQYTvjE2N##~71=r^p%^?fZTK47zrZZbh zgpiGpTMA+xk%x#nY9V+JNIM+_wW$n34nqNHw0eENw?$8T3iqJD*Ci^Hip^p;3za?8E%4SXRIoyzA_A7nJl{1GK_W zN~X9$quV#Lw>HaS;?Ee>L`~TQ{SK2H*mlAWx z-$rbAD1}ZvXZ0>6=5x@!aYX0cJp1RfR>M{JQ(jrfZH;3fdV{HpJ8kY$$1WgkwcKon zy1>ciaiM;b;C$F?g7@>tNHMCHGy25|EdCj2lbkok-IXU5Q? z0pTaZ;+26n0Qh1c>HE#k(u4KSH#9NTFMB_rN=^>)m+%DO=OYIb2WG`9FSPBr_5Q+> zY`+FMNOjo)!xMz`ipoP!bxORET^FOjw>ts-X%$Cx*GRtl^a0~13Ze+HCU@3$sBlI# z;ls10f6%%Oi^@QRKF#Z?=*DDD$7H!(u6&o1Wx3RhYuXC+Pi((!ps_4O#TCLg`r;(@ zsh!z)M$umGO!<8*wz5(`<9D9n9-{7YP`gieKH&}wVR{1s>4WBBxZZt;VDR>CXAAN? z<78jy>UmvYwqRpLr6~QC74GRsiPTnt9Fw|D&V9@2l6d$doDucF2-rquC=HTFcHu{Y zQ35Yb(hP6|GL{aZv z53q7yf5C=3`;%S$m5+~U^clOART6?oSw2UV`+EITkeg`-JGO7*H9t2lrm$gbSW$~X zbeNlhT3(qB?lvbgEIUCjGp@JdsmsP1`tJm;tU36w;Jj0@izhX}^*FYxmQ0?p!O?e` z-vnk)dRF3wVMt>J1_U&a#KC)!@BuF?qKY@;@LBVh>Sa0fT*?DNmfPDXbH=YK6 z{&Fng7cG~F~z$0w9N=_J9KBa3M}=dktcJoH;Hz zEP8ue))n{J?rUsD0RfBdgz{i3NVn)5xn4P~g*juhtW=bNxwfHwjgO?CjE9 zthvr3HWzM6POJtIC?Ts)GT`!imepJ}ROPa(p9&p4+MSuEvU?ZDR)BcrhST0twbg?5 z_(gM$Oj}zUf+9D$!rn>#W*?WD5+o!a#(M8FH| zOA9-SUaO&3X#_>9@1@a*q!+!2?Z)L;kmUH_FiB8???1Fy;~D1m82{ z%t8KPi6ocge3bU31=CQ8)DzS7Y`ZxSOxL*{z;+ag@Mo9Mn^3TzNvzxFLJc=T;=`Cj z355Eg*i8=+gx*iPk%*;WaSmnCA7sh6PY3;=>cY5pOM?HUC;gIDB@I{gvXhJeg4e1&lG%f@hcDfURienb^ zW*~u(P$`wqY8*<^-_dJ;gZ+=FAXL6FhHI!p+ny;r^sX0t({6sUgV~9^!nIR>fsjF< zOn+q}J|>SJ^^s>Ns=f}{4UtFfe0|%Pz31Sg`3yhhD~P)K^4}MZ*h)R&3mwj?ukqU! zHx8<2+*2v_-#PR=U4^Zj4QbDwe0$-ND+xL6f5Ha-{HP5PUjO6@J>7i5E?aYU>JlT4 zyjy=7dnkMNqWqNsrALa}`w4 zq9-t4-y&e}!i8TXsAd(>M?ZrPT}{X830Px3dG`_1ru}OB6!$TP3m6WdQkE%{f%VI* z96^i(w=ANXqrz^3QRn%X?$!%$v_T4I1dy|}aZ;m}$tpxPMkC1`QzY$p+BcBMZ;xxD=!y%FBk%D_vEqy(c zUDz_94eNjVFs{FKa@4Q_S;6uu>NRK<%>bPbgU=seAT?7$_29_2o)4Cw=8Q&Fk14IGqFj+baZ8x#TA~0s}IXZsqw|KeJrF!nus>i4tmK ztzrupL&mE!kE#O?V+lt8?zHrR_nU$dCFJRa72R{$+8>(SHjn?d76WYcDD#Orylt8F*-ExVP*q^mg&oy%Watz6!Nkq`Gc z;UBko1pJA2LJ%!VkJOL1@3IyLud51?>A)ufb@ zT|`nc!SGg2hOG?4t($4g2Xf^f9xz7h#U}q=prWRMR`hly>rL!e$f=m;s#jdTGSmBf zLrS+qTI#V z-2L`~o>wF1MHlQlFspBIJoUcFJk)RH z?#FVHgh|+L{A#;AqTi^Lzuo@HSPJ2G-Iu5p-{RfVNsj51`!k8^Fbp9HKmX|v!2Qis zU=9cRr=UO9N;oO|2gzaBgAfWk5F2>D~f)~Z{R`diKKHQgn45% z5J!;247GFf^>hKajxnt9o}pd@n?62D8^Ox1yw`N42)SL@v`=iLrn9!r+-gi4KfV`$ z_#T|&32cs}2WX-%M>$!S4tbfFABDIu%SwHgN{(GVy*`;IS&%4ynN4xG0mk_JC}E;x zb+Stc*h*z?S59(4F+>vjFa%e^{AWY2d3wSmO(02@#iMYXx_2W}7`I`eyF*D9rGFD| zYhZ|yg~$0*6)&RFhXVoByu9~!M=R=&X}HYb{5V$T??dN1Vc_i&@veA%Tkkat^$xGG|bi}-BvmJFHa06vQD9}+EL z?GVMe1TYU3CBnHoVOg%dxYtS#B*{2kX8Hr5H6_q2yB?%PiQH#Bj7xFu!kVw((ZnBq zxh3|tK2M)}JlXbSg^ju51Y?b@DQCiXBi_Z^o5a%6(!$pERb-@+-RX zjHm@&DTM8JVOhT6RrBK^=En|7Q>_IUo%V^sUGV({AX4>Zny8K|*%-4@DiD6INcWMG zwE98u@lFcBR%N94O=E)KB;yW}c&8{~f(Lb%4;yZ3ap_0?JX;FaC+mQt=wY0>5668+7%yT?X?C9BwXLD0RiO;xsvc$^9rhA^Zz95ryz zd`_$!)z2IgqtH@DqQC9ZSQ|?ptDqZ;fZ{`!Yci0V?oyYe3D5qrj6*2*MPnBSp%<(ypqMLD; zxWA87PQvyX-EVzgytvq3d(h~YD4u?+sTG3o$5{o`c|#}SU%HNxD-8wEHtbFLR7M@> zw~a7+qhv~NE$ANzMrGD%;srYIF)|=;R8-ru12Oar1*q?)%Ud5P?@y)rN0^@iaP5BH z$^`XXia&ymr!~BOI=9}sSZrM>bz$Gl)FNZenZ^Z?wGz?nB9^g#g_M%70kmw=vj7OB z4-fBRsH28?Fp(>w%DN)36Y)dnV)ryZZwpxfjKU-&&tBk>OguSJg{h8Hxm1rY1FwoJkhne3ocl7D=&DO@WN3It)dM z)Z&%_Aqx1M;EU0eo{_C^GMGXO^$%m?LPsf?q<3A|H!tn0slJx}#Hi%%7~*|Bw!u|4 zYNeSl1IjgRKrNS(eSAkiemPlA z$~gi5;FDhD0Y22un9OJA6-P>YB<`2TH24#a=wrMI4WrvVSElxI_9NG&Jxg+;pzix! zz5AW(;6X#h5-u|Yc*_?KzDv>YHrM(f$q;tr4Zpo$66c{%>p3;Me_V3QT9rt$=4(J8y;9;o_^fVbMIb6FT3e~5`kH*QA`j;ff}$dOStGd`ET&&^N_)TE$VEv-&Cv?F z_apolwXL};-+jKGGe1SH*(rw}bf+w=wkwb<@>D~OdweZ0fy-u*?s6$=MMLZ57Nec#v6w_NAR zfsg{hh?a4tQ8s|m%Og_xhY=_6-xT9odJcuz<+#IK6K7}F=Owbo^J+H}-Leu<8eCpI zS35uuGA(k1Z*As-Sx0KAxV1d^JQCS>$2{hgA5x%`u@}LpK}npjM70U)jilP!HAt4ISK^)8JiBQriWQbCvPWGi{Ly3r8nO0%(h<5Bz!!h+tgT z_+#PNLK#+yVmpd>#FC|``PX$Jo6e~foKd!NMaQX0wC_CUfnO~KEw)Mou@!XT&PX?s zu5PscKE zBiWATFT19Mdme82-U4f2RQt%2DS!Qe%0fD%Go@lB*sA)!PUZ@}sQ!Y&bC`D`QYF*@gV zJk$U?eAVj6brqm0Ui$S;k3r;XFd z^ULDPuwv)vugy_)1Ju_~_}6`(V%h~mTCR$tjDN2rO`*v{4jQCg5plLc8}a>hxKjdD z)3LKRTTM{J;I*Ob#Sg>K%i@n++^gy8Cv{KvQis0$L*d$@rS}W4(<@7*5E^>grr`qE zW~wz%A+^K*m~Z0U!AMrY@t8l*eRzLF4q7tRU@DW}Kn^&{D!Nq*p5xwZo|ge&%0$8PG4SqU`C50 zdP;>ay;kM=r&(wk2cQJ@$~VXp7Z=}}9mH&IYtFnH^jhnneEkXNCHC)sEz4=*+A^yZ zJ6P3KPz*6X^I>1osHNn!QB08{3ygm%QqyImEAIPJz86zf7!mz+ePCUCZrpd%f$>!V zfLM+U#W@wwLSK$i)!#@iDqTtX*lrkvb9DzCW9(dyQ`$HuQ<)ZAx0bA4K}2uf({w7{ z;?(y#Ws=dNk&F3Hsz@xnPXjP@llJ$VYyOtQV&P-Ks8I|dQR+oV{*I^6gaN4TwojEF zX!;|!h*xk&SeJ(B);1snosWoR@NfV4D37!e@_rPyo#J+o-az~4)++D`19qGvRKBEs zfCM~+Vx~iIK0yC;$oay)OJzx0bNw#bKHc$B8qWDzUlrk1R~aaM`;!rMO5Ev~@ln7iC&%8$ zAvNkyl0WT-6LZoC{SlZ*O^|c~22=NYORl#sSMC;0?mv(7Gp7Nx40|G$(;=s14WL{V z96w2Xx|P}Z>GZaiJMu8>FXaCAI%AOU30_pdzr`Oc9|MFxif2-=DDz`pkN9BA@iBz-~Rkb2oe4gxSckBFoY$Wo9NQ7w0!3nhU7D zZO$q9BDkBFDSV4`LX=C{{ObOZ~ z*5ziI-mQS(hg}}%7*%<7JAOz@9}2a)cCuHa*uBLB)u};(5Bsi3O|25X%p3V}aPu?{ z&5E~I@@5F7SeXNQM84l9CdR(?zGM4+oyZB#V z2H&50Gb{!xhuDf)?N8aGc2iIXbzyVdKUOJU7KKOOG2P3B=V;B%ZzSvwfe*vZHN!za ziH)nzb^mw>XQ~PKO!*FSw-XbsH2c z9^V7O6L(vvI#nWKVqrx^!QPcEBvRLLhCy4rg#r)j={{TmeY`$9`}aEfVzznOlk(BH zpSeCp<5^_Zfbnk8&b1w<+KSyDo!z!d7T(36$;*M1611rF@@C zIyIJ=!u~|8s8cm~eZZwMpmWLyF1z7-3|J^T-8lfV^8aLJR`la5U3tyY`$~G9G-KKO zEAesNZ6iK(a)!MpaNG-gR}~J>Hm=|}y8ByVaB0{Rr<^JE-+1^02eyp@iNYQ4g{n3sS0O&M3?+9Ic}2d{e%^h7waqZI6+g~A z*ecYQDqw9lpl3?h#tSTGM^$G5m~R+MH+>S@;|Ho8pta#z+sD2=_h%{K(}o6+e!YBT zMoIQA{>#FDm1txb9;^q&oBqyv7=`jaG|f70Evicz8$W))O!7l!kA$nTZiGB=fT_7( zeuJaM=#U_j5}7K%eaU~vzi;`}r57lo$f~KE2Pkp>Am)_TWBMQe`hefUV*&8kup?9@ zPB{&-G6+f^eq)6iOzKp$7& z8t6dxCCeezfaaXuZM-gphM942;w_P}e~~qr`7cfpdH!Jr^)5a}{mnD-o}hVSGS9kc zLYjg}VUY-LVA>fRm3z<7Pb#bRs1GYqs+!SFcPN~JeQVX9{#Oc%>z4@$no$W$%73|X z{i~Mm%EfcUmN_{lJPgze!#AVa6PD!7DWxs@34cFwO7roc_{qc95+U`12SUEEmp3V6 zCL*SMYVkxg9HNZywJUyFA>bTyHoE2NKO42wP8OvdT-lMuj>NBOn^Y5)xHJ7T6zExa z<{RqJI??19^v33MtcOp#QEwT--^9n+si=@a%8v(~EAJRuQNP^!w)_#23M=LNx{6PG zdmAbuE&7=EXYO-7gEcUMj%t!V?si^YEfM!m7euH2{6Ix#0%uN1npLPSo>qr@e}8`- zgOd{~pp{5wQ^qwm4)bXJw)p#5=pD}P-#Wx0uF5o=b51TL{<1JBOs+)oCHw0&&5{v&*ilg zL`#|wwt;P7@dw$e+MLBlPHyk>1M-e98i568$R1Rqy=N`+aAgjVN9-TF3~JPv7Uh(W z6ZNr3qvrug=JSO4`(Cc(KL6{dABKfq5e)rz2XfpS&YR0pgH!YT#GVJQX4o;>6$fEY zNSxZ+rp_un$cc^7fGz#!7?aaQzPskfUb<)oDv&;;R=2_AKWsRk#*!#&T1+}7oMerj zAI)#S(iU3x!%vA-9Jp>)9IxeI+rj<)*xl$Ho9_c`4rc~RgEt2^DtUz$m&Zy0z@g)( zJ1+38F>ye8gdD38s08$hx%KHikrd_7@F>v}_zIsUT-Cwmc&CeL2)Jo9=;B%MFTund zODr1L>MF1lvhb&VtxMP*szlLjV|0j1U;f=3@M=(1&lU|#FpN( zttp8Y)o6qdZ}CW8t)2WBGn@g1F@Cm|d^kdRjDaJlFt$@#q($O>32vA}qq>ttiBcmU zB7)Y5Dcz3@@83KGe~YQd6;Rl&iTW?k5lXSFHz1;I{Njh@hZzIk?ft^zrdKaBmEaMq zb5iQ#PMEbUgGQzcQKf^VsTmnD{bO%Jyvdw$atce{sQ|6ntnQpGM#+A@nKIlI0rjD* z-@i2Td|oUcZtzpPR1m<*{v1!>AK>^hGV!7)Cc^1{N8aj9ox$c$p$Nbd(P(*ZJN^${ z#=_j7w?Yn{U<%|yPG(=in?M|5)7{$FhuL-&7EA}PLS9KE@ppI&RMwS~taYfr5b#Xr^ zARIaxBgi$Gl_2qPp&AywDS)3{U+e0@97FvmYz5McN(F>qm7Jwm9lB_oTvCwZ{QnSB zv0^T5tkHvYEtCUY$v%I*H7KCYxc?=hb1lIppvd6b9r~8L_Jtq#*&7YQR0(GmUIl8G z8$)4H&dP(uIel`Dn3ygb6*C9yt%sp`gJh~+uP;G^AJo+dVH=R;Ae_`n6aZH6GJ~hDL1wcIC<-zkq~D1Ah2n zeU|NV;#ewZB#$_A{&fP-g6t=+A1P@;+B}%KmR`Ez^C$EymciOTMFoA*!JdwEU-IQG ziq=>aEAiOa-KXb=5dS7&t>vh9@|M^}eqg0%y?%Rj`BKGF3ICf~9F7_brz}y|<-gey z!stZv=h=h@bJpew&zbe1(knv>tc0yo%b{+GSxk&*VgAM=xj~gpf&eB#@3$J?)HV&c zvSZ}87%Ac_)X180U7499<8W_So`0fQSf@N8`320ec;yg|=ch%Vb?8N6y(tWplX?EN z0<(Hf&_F6+NO-sZe~d+V{y+}m6Gd6l4+)77ijh^O>UFI^$579izXq-oHh8JZRT3emo z;@fd;xrRNXrc7@evbwmG8vZeaXU8&!cY3Yvs)h+A89KS!)BA2>~O;nVLlGw|G3Ob6QLV>Yjy-eRJmvS>$ zjA(?&JBogu&83X4;w2LyWW-C>P__=?jLGH``C$|@E52ba9lMhG+@$T|ImZg-qll%u zPouJl1x0e5_46Y4#FTRM9;fYR`^Sm(F)EWkR|eO9tJOWD$$vZFgXL^?l?h6D5gtls zn_+vmTAeLyO|x>tA}ERnF|;&zM2-A5Ae?tZZXG)k7G!F?#J#JuI!&?oG9?XGn~sbt zK~f{Bu>?t{EMDH=l=sNfM`xWen+fTYsC)Y|j;`bW$VC^soW#@kH2hKl|HrpWB{RYQ z#%X)8zF-XRdLG80^uCG7DK$xaF*!K^MFI8j!W?baj|LjrvKLylaut3ahI9w$87LwO zmoe6lAJWRyY7+^Kw5;c(xQBbTuZ#0t*z;{lo)u|9Tx*8c=kQmtNAZY>RL(P13>)|z zJwGt|GMSwa=)X+u$IYHzDI@&`gRv~Zh+k#1zEkh(r@UYc7j8xqcaCFaRb(XXv(w=7 zQPb}y`tp-s`YUc_c}?QTA`#V|+ni+{F};}oPTK?}?EVF2c=!;{Se?e@wc6?lfA=}- zvr@~%jS36sE9de)d2A{{#BXlhM138RF{?Ag4rMH$&G25mMR+Oc1v5~*)xjf>M8?6)pp#_$Vu&EQ;vqBBIu4}dvy&@P?&eMu zn8ZQ>k?jt<+tKbK!)`(Dh(TIv6m@k3x%lBc^quWlo&X~a9lfei ztg)o zKNcDsMQz|iAIJO_gG%l=E|&IHsy(X9ZUq3q0Hq;|7_BIu2fR)Hm z8S#cGnOgRx| zKLPQ#Uf4Mvp{k~KApF2&eZK|R7-gbYx}~uN<*LC$`Z1MwY!G=~LnS>YSHwUOvWt=j z^El^zcYwsy6B_|p@E8FvW*}3Sh7N|1V+g$mQDOlal?Rpb_V!Us)`q>b)GHq02s+PS zeCc`YrH2)bjF_MgejjT6^c+{z$ps9YLe}XEmcs6m9F`}1C?4VQ8CK#2`d=YnE&FC4 z@4plX-|O`MpBdr*Bg*#wgL5I88qMCxsgZ(`PS8}8v!RhGgJw`sTFmlfX7QPBwa=+*wOVuwJT-Q6+{B(H?b06=*3 zBg^poOWIqB3QZjq+)95L8wYJ0j4H(;K#x7B4(hx4y{zL%FdX`<<|Y>ESWrIWcTca9 zlBF9iR3E1`tgRrot`2)L1BA(9hRDtndo~Azng+08oj_^>TMMuejY0Rc`$v#oTjS>un3R|1D5b;ti`cFEedaIU!=9M4vt` z)KT7tB3zz_9?X;e+p#3r*1!NT87e(b^n~$uf_o~j-=gyK?4fKFTGzo+EIG~w14}oF z#{SOzwV>f*@2zSo%%?L;&I4=ebi?`Y2n#~e?bFB83;+mFPNM;F($Rx!6WkDu|uwuZj$-?i52*J^RxFe{c2vYcf3&cqMY z8c+Y8F($<%P#;)g6#1{8P0OKHlHJZd-9*Uq6<`=jki`+#mL%ll`5nKo2ZwVM;I~#HXo2 zF7i9frMzFyTuIa(#pPO(iwSs(Fj&kuM+K2DS-0l6m?8C)blQZCm8yU_sqW+B(?Kw| zxmGqq_a)lvv21Vp`i3Cy3w7-Gg0}4?t5at5=$?cgUh&N=ZB^?}O#ZXA-AET}lN>Adr>=Ht}v;Ygsr;)gY>cOQ`>-!~7O+`~2(1xs{ zqEcR7F0cLO>b_F~!ZmOd^v828@>@lq);MG{DrUodX4^T%&k=SACjMmFoL5Q!mV$6| z_u?90Wr7a6da7rf4=Ia-XoAP_mk|frxM1GXKMNpWHv@>G7*;cbeDLmoanYNV7QgOn~8c8W#sc{S$yTx^(&&x7mr{{c9w=w~cN8fHv=Jsq?OpRnQ= zasa-})<)Q~fSs+^mZmQ1SpcJF)xpTh9AuZGW9g(eGB7)i*@lAD_qRd;V*OH$)@B*R=@g^8-kLI^KEz2Ic4V|;&-Nyu zBa|%I3oyXL0J+lQT5gHW@6dz|LwmV*VHpg<1v|&Y6^P=e<=z1?u)YOlB7+Ob%p1UC zHnnG10nVDl5G@l!ys`>}=c{Yq(k$X21-&(UIVu3_WxT9P#KQ^Wt7|(q-y&@5fcg0R z1rqiYG|#gVB`zn{1_nAXGJ+H`{S#3lS?q1#GMOxTOK_fbID;qbkpUjyJ**#97_>v=sN?74vZbn#MqmN6`GsqfFK ztmqh@W)FKeM}BihFw;@3ePhGm`b)uL(DlIqYD6Vy>jY|k!+2zEA}NI=M6lU(MNdc6 zhyY!RYvrjHH3-Dhqwqk)pT-@Cw%7fqz0oGb{muK1U|S0d-?_Pm3R+4*U0vLcyVq)Q zT_>wCtK3|uVkftB_=7SdGU{8Gs49kqq{viWZilb?g8&me0u8^fCRc%BG8`yEwzZ<# zxF2RnFo>)RU(=KVw-2#lRacbdnxI6y6F?s?7zNctsGOCu3rB^_>ri-jKn!YJP`EFU zL+%-S!I2ybMu7&_`1EfhQkf3*%$4;wBL-_8Ln8G4or1jxW5>?DCU z>j4D+=ol4vAb12B+_%yXAM$Kxwa;$`hMFgw<8}tt$axTdDY2rBQaArO`X?t0L0~o` zXB{M`59!RelxhSBpZfuc#xS$^@Hm1gd%FXBM&S;Ufh&MHmQdW@MN|@`8H^r#XlX31 zP%8>aU*@2_Lra?SJJpzdkQLFst1Mhk8?q*YU-yJ2u!@Xia>SHodLj&#Y+xE_UBu(o zxn9UnmbB8Ua(?AIpiTT7rBNyY{R-;k)N#pJbdWu_hK(80p;<_v(_6N)Yc5P5ojv*X ze%u{B5O{Eo`hC(ahFU`~CofM-+nVP5;ygYlCuCtk5$Pohy39&O_XNhbS^?t2JELi8 zlm%w`&@_n*C-yommffhF6ZLv$oP>XLzp^Z>D^(ykD>ooP48_suUS450&>7!q4dVU? zxE#hYxKRz~GlxZg4@v0dsIagUtUHC0Vc|LcxyEbR-_>63hzPOBi)B&8PyEaD4@*7D|n5@0D8E z9e#h%)#el@t#veG7}cB%{(#*xW+C`m5G7=jxXT&^ItG)W=|RxK6lO`zikOfZq8$v? z++rqVT!Y#9ZaNBlocT_HK^;8yXnP^c&!g5K_!SpcNF2$AOa>6!yFe7 zRNiELbc9}1Qir(12|i}6H6$%Yhcks6pPK+Vm}Hi)%oIQY!8+7Tqu)b_R~EZ)GKJ$= z3yKSJGGyl9PA~vmxMx}lTUQOISWY{);R5O#fjKrlDQ0s*F|yIHe`pMkJ^BMip@D0X z837|P7W$_4AH!(f>?)dlj!_f#kG{-M3I9?nH-cj<2Nt zx9tD}6oC8$Vcd~TMJlK(7Iy)eNZ-^HhTYB0Sk=|2vI-d7l3f4B9DbX4R3Be6{5cc! z^g^R=cpKYdxjWrXGN?8b`?Y(j*4fQ{)U1G$7!tgr#s1+@%;5n+Gs#E)GG5wkifBd< zA6&M!o;bnmC_SLPf-OCp8|H7Zq%uly-`E(at!>X(omMMN*BUUoj@vCNZAyhx?##3VYN0th- zZ>@sgIJJswt@LCQ}Zlr z{^}scshaJ5)WqQa3dCScOu}k!f8k|FZ(M2lO}Ts4}2N(Dz*e9Z~9mrJKnOpcMkzLMX6%2778As?R`e_CZ zA2h;{C-I*xVeKWj+}(q741LH8B5gUmkknN8bbMvVUiKSvc($^xw~@4c1Fg(*h>)Os z3_UIW(6zbzk`Kob>=Dagk4;Mjm6d29$dNHKRgLC$)dDImAfaRyZQ41aPBO5Wnp_N= zLd-{JP|>p4ja#s8>_r`=P=}39={VTKF!TYm^!nD;IJ>XxX^<;zia;+qO8i03XKg^2 zF{eRlXBcSocQK7pPDp#A`bM9HJcWS9o1)L?4!8ah!|Szb*EcbXY#eRJVEq`57~ zi6}<}?Zs%ujIJH{2Ptlu`B+fuF2NH|g~YUozy(WbifA_-7gUOAxWJwsc;jr3WO|dg^)cGYfwd`aGRhs~Mw-4f zMouQk@llP5&OqxUR8_;^_prMHLg|@FC9W-vsLM~JCg=F_--0?ALM{@hqT@HqXFxys zpx=g3<&E2ird}yVitmZ)Ah_nXm?ha~3u;NwIXs3>6RP8!Zd;i|JhAIIX$pFqfrgjl z?$dJ9mfURLNf*9d6Spqyre1rXeB8C{Krb>3i)zsLV#w(l7y{cvU^%;o^z}6ocVR8x zk%wZnveiCb)%g@NAgO4k@~aEN>aL4!&nm=e#-=|4TFMM=^ePc%|7r2cei*oxK<_g- zm49Zh?*|j}0;(9Fbm|G&-1>t5`v_X}RQBJIrJQpLb3l_ zDUUhriq6bUrP9y~{xIOA^Fy;kMm``<`d7VwZD}_IBLCNv_Z^UJi}-)BGxqbrehR%6 z6%}D%V!KagiOJY;5QDFzU}>JF|n9}jCdvn=O#6(Xgm98po9 zIe~3_7%x1j7zF%lpV{zn*Sz8?m znkKo*9!h#_YOG}O3CsMtg|OFqzFwrm zJOd(8K1(#e22;?8NZ-;rXyZ4V`zZ!dI|V-3huZ*K0oGMW|J=gE(7^#WH!W@g!dcXz z==JFhO=v+WCe?)&qM;4Al^DA^S6hBf5k$x4PN{{#`xh!;cd#mkf2#k!@#!go|yFQk9)n#nZJfxp!^tZM?gxa z-DgUnTCrFT|K?cq&%_JR(Ae1qWnI&UJogZXQ(O$azJf*u)i^A$Kq9l~s*wQ>8(&-r zn{8l>ToM2khB2AXcrX(qzna$I8iOpwuM+9IRbR6XqOQCURfI-y#{tDRu{-C=<`r3m z#qAJZgGK6v6ewOA2CFkg6z6&VC$;eQ8|+XI2Qlk6C}?I?RaIk?lR>q$bWP38q-I7& zK|8wbF3SJsWU6!z%`x|YE=D6_kUj00EAzpLKK8RTqmLFr=;v^Sjzg>OCPyoi^6MPK z$l`E;fH>hh%TDa-=3I^i)W+g^o}B5sfM@im0$e^pn`#0X0P|t$Zhp#evyv}#Y-Iww zV{kT~%?3Qf_Auc^0)9Zp2{QoKdlY49oq101DK?XVr4{r`liW01?}np~OVC}~#>U3_ z*4D7xT)gO*7}Bzml8~Vx;WJKX?*C1>$~S7hc1X*}^ndFE?{I(;e{dZ}-l>5%@NT;J6V1IwrHQm?ZAH7N&H{R78tf&T+ro`1XSfd`HaO4kE6 zMGyH8xL;?vIUUt5-?}aX*V@?QklF}Q*x{#YgS98F5GuRGP+cQH(oMYx^wH9R6fyUu z+YdmIXWI|>K*x?|gJF5qf_)RyAYFtOAg=zbOB85Bg{yY@x}Ck@e;I2!kPXmx>Y)dY z#a?=?{$IU^?|LDm!vv44-!!3SsXW1IK_j=eLnu3Nc|&|-cPI1*WeM_JfvEuw!#R5R z8d8)Cj+Vd50jf-E%E3p0mc#SI7PU0|Dq;07zIopR{m=D;pZPHZmMt=apnHqmU)$zz zDVNz7Fa+g|!WhaPD2AxQ5&YZ_HJpQP2<9+?#Q;C4fMaJ2THxAaUOYr63scyrRtoTo zeY*p4ew<{`%>fRp_a0uKRuAbt##A3(p^OFFh4@~BfBU))``zONWR}brPTLc7ilI01 z&Hrnfrn;}q8)N1Zim1ToE&OcLlmL&1hs>J-KU%d3h~cU-fG9Brhs=9mKPvJ(xyR3UzCcp zg5d?Xw3npe_Yei5vPvrGc)%#U=v2Txhe6(y4}MD@Y-Rh!G)gOvKKJ@ya|nw^B#)u<((q7N%G_(NGmt$$q^e!N5mvY9bh z&5^RQ(dXC4;9+?#GVt{#KjgoQ3E1~Fzve=>f4A^y+b)H6+i*Fg3+c?-BChR*q{rtU zo%<$ESoi@0cM~LLNqa9Coo|X76VXq#rU@{Vc7UD(xlkG@*Z9GCz@vYV5Yf{DSn_2q?GR|ek0$z)*kOfE*RztK5B3*_2c*5du)t`tb1ay4TO+UcK@`Hdac~n{ zJ43k~e88_D+Dn%$Mv@%N(6SZ~OR_qJnV)o#2>;#8Uvpb-bbC!o@AgR^*w^)EeK|kf zvRojpolXUnj|n#DiT2*5i0jQxY)`Q>`j+@{^)9(WrV^RdvjG81u@2JkFoJBVu#3c+ z6gXP^PRYzHHHl{un3uEJeTb~c&Ql;Vze?v`Bt`sW5#X@F^es$KtB6e)_^N=QUl^pT zyPxVK3AA9{iA&~eB7W%@Z%jM}Txh8?ZtP-KB!t++YI>TLj&hHV%%SI9ezD35Ut+M7)Fh%$U| zSs?@{IrD$ckU09t;GgV=2)Sdtg-Yoyj=!-0GJ3&CH0IYz#qT{d&+ z>)!i(@~*Wi{MK$w_IT3av|U^+#*4>aWa)GIqciDAdc4T`c%#`4y<7y%QNdTj>E2!I z?S1m^RiE@$Hxo$Pw7eozRy}Xez!&(`lHu8YXgnO$qL^n zh?L$efn-&t{OVFbL#1ldvz&pxGhETdb*CabV{2spJ|>Jzt@jNV6WR}H9QAx4gUAGSj3Rkr5}1$hvj&%zUu7p`;t|cR zaEh4Kx;q3jv+`}*K~4+4hOvv`);PTE&%cO?%8cQ&07>nbqO-e1pSCQ~b)u`6gO$}WX= zhNwrZ<(WD`rLW@`ChyLyEd^vwP)tvG zpOXgn-Rtm+Q+z4Eelx6w4HL!Z9d?ILG||W7Pq^PVX4f_Y<~hdgBKg9&#(fuDEbNP& zF4=cS&ki+sKPEkfOvc`3X6<)wT_|CGS~Yd!k-OQ7$v>4peuOeR%SJMX$>0OxUUg-e zKl<+s`2e~~&V0L*Pg0TSqhUmh?3=LDK_B}_^CTs8MV!#7q6T}-Ar`|t7Z3Ygj|v#p zIeQn@{R#ImbaYd)#Dp9mtld(IuNOCJrCvVW$8{%{((wJUK2#JKr65FuqX?VNc%zu8 zsIO)#1k5BTxdy0vFJCFgY`q=PKMohye1yTUEHmYoUj%s~xM!d{x>Q$Jv?@s%*rP^+0{%kdRkV3y6$lETo2*Tiyxns6Bhw{OO}Q67`jEj{G*hFY%P|Rpc%s zBbVP+7&M?O8eWA;RCOvExJWT9qM%peO*A^{9oEp5d=-(C%!fi3M!&3aua_~a*xPii zE=j*DG;5#T4!>{Ke(j(>ac|UHnb>83s7U1VyKArOm|HQlbBDHeig;uhlZ(aR6boxS~uSQ;Z>R|M)$S+?0qaKRSvlsCt_iV@@&*0NguIh8k zE9Q08W?H-|3&Ib*>0aYm?_Dsf0IOWh< z-Js&|(!5s4S`x_e1MOE^Q)I)Zb#Y3IkSH!PbU4`kveG7&M2yj?xs{;HDvfEWXhK3} z)IzDkBP@K8g}&^0I2Tvw+zwx0qV|1J8)gs-XX9;VZmy7s1?9n_)I3{5;9UhKOA8Be zYq8L|z13t?Lh@=jVI%^6<8oc67i2dwH3n{$pw}*nD7l$@a+1WK7Jkv3g;RUJ1tn&M zc}7xL8XlXNvGJ*(#vIbiBu)Uid3;V_+>8Rw!iy03ut{|Ibz_Qup$W`H;-S*iqH5n!}P&LCn-HEPt3p-F43`yQT#+K)Y!ZXKB*XT zqhpjo7}3(|LO?+Qbz?;JXdvZ4Gc)H0F)J&Er>RnVO<`>z4Aj}%7@f7S(<~$>L}>{_ zXGd6x$#cLTU6izyLI?eXgSF_RCT|v{orfP&PazkQZXQ(9F3ts3XHj&VyE-}GMpr)` z=bAC^t-Kw7imcyr<7?!d=rE@j0^6OLi`n|3PFR8y8u?Ln*XRVD;$`<^Z1ZjH1|=7> zps>m58zh{oDgY@)Z4`gX!+B4#ppb*T@e7H7TsurB0wix+{Ou?`7!;w9xwPE5!25`c zFS&u(JDH&Ql$1zX#y0IL%Q&KeC5!tzT8e+nE3a;dS>&t1Tpt!&c65lz>uN?<-_yyj z7`mFqP7vWjwaesJhTglQTW=P`cDZJt@S#!py+g0Q~!R7b3iCR+g;C7#q@EL<_ zi%ZIi`EJ*fWOH^x%*jEejEm0mG6Wf{1I{ll67<%C5F-~S?g3jgmkIpHmDe-i$}TfT zsTF5f&+J4-vH=AE!3z3_wVI14BFMsJ;f^OURP*Bx~ zZh{_rm{BKI)LCW~vn5@EfTX#zJdGE(Ffp-2j>Ixr@O3;EVsMzWPjW@s(R9=}N3 z)lrR!NSlIrmRpyWmCi3L1aB>@+%&t$eygM|vri;@>2a6fBo~{1Z;aa2JFkSfmITFa z^SCrgfXH+HdVyFc9)D2GJ_KQ1SbH%lPj$`B1eVmGm_6Hug#My@#$^-6*udG$B;R583Q>6m*b(6vx5C zB|INa^XbR}htuSz7rk0KSE&L7MNm`%B@E4!!(KSm=i&XFR1U6NWR#OrIK1o7oer17 z1`m9UN5ZS)sP0Mah395X61mqD9YOpD*8P(O&Oe0 z%f=ETo$tBa?G-1)<{4Z}*ED8^|e}l!F$A6=@vFMgES}40C0=Nx5?jh0 zhM;v!`OamSk~!*6B%Ry0l|@KG0sMoTpHE6=6*HBDO`>#(SdB#a26&>#MVNq04FZ-5 z7`BnhId+yI3s|Hg68TT-=7f3ZCNqygG64P?2$$MoaaiwT|fvW+cazy*QY&y2a!1y+J7@T=Od&`ev+|}w1?YfOBrF@4+ znCdPi1{PMxtiim!LFAP&as|uU8ZcTROKK`tic-#lVjma?*>yrnF6igqrSd%9C=kwD z^8{K6Gyy6Vy6!K{@Owx)KkS@jF4Y)ACw>XpN7p3y5^sqM;flpwhb1WCeSgU>w-1NY zQ|yB^DAe?I3`6+lGNQ_C#cqs?fc1SLfWdKOS18bt*oUA{&U>}bC}R40cxJSCHfIqW z^ow}$Na#SQFy>##(%uZzgDPtME+=^98sK;hc}OoIo@{O(1gcQRF=@DRg?`eZvX)x7-As@3Q$ZolZYsbnJ#RqLz)auPVOUsJpVQOxK5ho|m=LJ7 zzsF?^Pm{vzE`y0Gvg;6jasQQhWcs?Tw*6%L668Oodh|H|o)}#v<|oZ8x=ao^oPB!u zS|ivG%CyfdvI&rHr(IjY(bxUB{B&f0oT7D5-G&Sp*SKwJ389B2!S&f@SpUlicd^mW z|AlfnFu^E$r(60~7B?Z#b-N)phD8$het&yMzG+}?AR#z}F^?H?d-s^X?uf?iZI~dE zQ=C8wyT{6+RSIx8VWYYblJ+N;W|h;wa4f*ne+<+Ol%{y zK-nyq<#L>ayc2ZUgVJX3*2h{V%&d-b49xL}NbXKe5S>pn>>C<}bbGxB?6N|(fM1^` zUV|EDH}5C%Qu*ZK(ToJN@&%RV&@Ra(F-Xt{6?7PhH*7iV zX8gX1=}U)k_W&;-podR|19I#=@zl351=fO~cPDk07r6`xslcD8bfT1@JIxP6ACH5I zJd}6gZn5DD?5l^w*=~f8)wq8}Asr^{_W4P>&RFj%mqHWx=l5@MD~!#tXe2yCTt3yvaB>7GaCd9@g&&Yt{!-k;!NErTo_P(rj|R=~Cv7N{Z-J9Dj%LJm?~ z=g^e1yE}Bp?V+Klg@uCoR+uCal?Up2fDe-I{F)yXU{ANtj-j1ZeAPP49GS`yBL!a0 z)Xx>`@%bu28uGtLa@)NliLQS64)JGPu2tdnbWu z{}jdVY0acPa1Q`CpNV}t!DUF{&c7(PV2%o?UoVm8t@T!(Os`Dd>AS;W?aS-er)uEFWgFQa`f zuAQ(S%phyRX@t~7kP4(lBtzZ$$dldR-|ve-a^3tu%5)tojNDAP4sGS-f#Jed!da~s z)7Wh=F33t)9-QC=E>dm{tfrhcp-#7v4Qsb!=!$8&JH}&8Pd^`=Bg4tSM4W4sE34lu zsIrI@jTA3VffAe)J(lW5=CLOaxcDaiOwBdLu-%O{0#}_=TMf+8qp7Byim!SNtuL(> zR|k+wy6~+{pfQ>Z*K^v$ka5<+G>)vq!3{2r;Anz>n_3?Njhs1{gtqpQv;+Cos2Ybz z&eLS~n;_4W7tap}w5>lCSeIbcl-S3r)=8|*nXvb6po}JrY(o@%w0Hks zq><=zP-Yo4zVEi}(w6kwZM{xPP5I+4Ot=(qp72Vv1QbQM=;I3%Uy;J*vbc=q^WI8*t{G!s9B@FRiQ!T?`4R4^4pK z#JG*!HwPuCfm1CG6queyER>SHW?&6)^hj!o>S&*a&F>7}idPiIYNB%5_Osk#69Afj z+VBjDJFtgUx&Wvo04=Sl0yo9r$9l{qI^dWx%O4+H`=u^FMrnxVn7P9tu~jZr-2|~I zv3EAv*Y%*j8nNo1CI(&~A#olb_p@}v4w?|sH6e)Ts1P_u2r0(n`U8qmDJsT;a%X%o~`ftP*A6V%y( z^`aW}g^XRgu$7&+6lCx0u(K0xe2SdYNIDMs9FSj=@?v900FEPg9AKCXsxM%h^kx*7 z^LLCWV(t4<6A$(76Hchal(Sr+l*Llv7*We>r9#&zl_BUXM>B{y0YFg;Yq_C0J<%wx zHRI1um-tDYS#suO&nAA}`Ik7sT++ZGB&x+7Ea{tPgcS9#L7!K)lw)NlrVf))=KuLa z=YEbitWk+h2#0rDFy8+v4MMZrwfF5MXjD}gi*pXkjAafC0@+7c@n1eeGtXHOwpS(2 z>ftEQA5W|-fTzmD+PAS3 zMeb%nqLUr7)f3dxrHHNIC@imlwfRz7aHP2*&B7rH@Ls`qgWbMuVHFi;9d6|W0#CL> z#DgOt8dTU^iSSh%L8S~}aHj2hlxKaE!7Zsfyl0;;<4(A_$xhcA;CCaoc99hv9>aDe z^Ry@4UtU;_`^AXK{iJMwm>X6a%gcgJE)XNEZ|i}{0-r6QZb4}+yIz!g{~hW}U{*0D zg}pf^gKO*&jSS-*#jvys)R+`zfTuuom0(284ehGSX_7ECSrxUI!?4^S>bv^GoA#Ud zTqPiLKsa;mm55^kzI3JlW6xRXefA=#SM2|2Y3_|~9w$V;GauXT_&88)aaRx|e1Dvi zLAN^Z*%k6ut_bg%-&GcQTqqVnzO&k9#qdMe1shk_ilrYHeNfjO#;kG$rF3a#D$51J zOw*2Qu2|-SPMJTEm^ciy>Z|1jI*^EhXTa z%~T3*PN2thDQn?H1y!=0mcZ4gnhsG_g4;vW3**a7w>#nBpN3+)1fGYM+W({y)p3C` zY``wgxEA-~sg`MS(A0rQ2>H67-adNIjM6Wz0FwdH5-w3G1d!udT7uid(+60)=7N** zmFLO_^cc~2nvx?No5I4X5MwO`#_O`vxoB^3ys)E4twdC35*mkc9u85usC$$aq}>NU z*oFQqBjLc<$gE@_AWYZ*L|k`Bo8xnVA=iOE!c7yc&HoTvqKRK85faj1a+a+W9ArXZ zwef*`Aqg-$4b85UEspMP2GDiptdZ{WQs+q?FzME|z5$={+Dx{=?Yx%A^W~G%{ z%~WgJA9vW7tVWW8&)3Vq9P;|=tURf$w+Zgu!%l`_}e;Yv5Zw8UE3d#3VsJeHyJBr%T>`4sY*N= zMnqEqR`EcRQT&Zf?nT*DSlpyN_2kzd#a!$*vcjGMG{!U31?*}mYAJEJJ5lL*K&piK zD7k5&izC$fcRrKPCCq07XF2zUfoXmKb7rV`H3_1qf%KRs0%lGap$Ib0MY({!Io{96 z#&`wVq#df`k4i`Ac&#n}rDT>PwrD9SZEs%cEo4jOG&zebLYg|Q1`62cmA=gLX@`lZ z@&i1H>iXLZoD%NBdUwoUEp0!}P=m}Xrb7eHHL{nT1Po?@f1i-;8C5{P1an&GEqzCU z32sfDb}%<&ekf!l#Z^xjSWX6sM^lLkX^~bxJ?2-~1=SxJqNJq~HkXG9k5OsfBj;CE zg2O7QCCg|1ZsC!*%c_zay)GIlKmN{y5_YVIRyMykI?DW^%WGRV5j;c5gMpzzkomLz z7Y)3Gh;rU(CJ8XQ>9=_@ZsU)^(3=+_K{M zaAxJxn;~dLJspsD!z?@+*#T!6MUWI(@#ionRSObd6kjC8_o8 z$r9eCc4>PuhmhPvFD{9n{@hzX^srl}w4neNE%W1(5-k-EohQ%La{#& z9=@sU-AhXG<4^oi#Be@{vk@bDo~ecV`Xb!j^C zUR3XRKD&~4U_Ay4Wi(7DUM1e-45TayFL3aELj#j?_kogS>)s~s4%)e^1pfRU4xQ5DTfr6UlolE0S#x@`Gd?%bYjpv81HbwV^ z%{7u8#{=&Cl9Mnrq`ZmN2=y2F#uF|SWMY~xsJ%iXWbC5ty>cm8wQ*)ZgPh2J(cmb7eLcj4#n{CRkJ z!wJacirHhNKt4=gpk4;;X#_i}ob(TIgJv8;`qqzn>JHWURgH%Ri5Z|lly^(n+E2nR z)!k#@T?c0uVKro+PoO{j1ixc8+B`CWN0a4>_Pk?1?{d2=b9(E#wi6|Vv7CXQxwOUMkPEA zQri}LX~^REv2jW4byB3W{kCaY zlWwh>)v?@aaZB_5`!eUVR6~=amg4*5_X}$%OCi0F83$9dx5Dq#tF_FdXCE`5IDW+4 zGWURIFSz~i8w4V$Hy-4Gy@@tFu+t2uVxBg;uON8F}xcDG@?VLcdF#{6C8A-(?@pq|z|;fSbJg6i%1W?Z8x$SnGtmNta_4wksh%1@BxK_#C zyxe#{d^$-nX?Cq{>*1BwwWXdF7ts{yCCOgdZT<70cF=zJE)vT=VszDUPQOI=@;AEYM-8ad_#0$QzNSG9r8GG;)zHAFXZr2ncX6ZSyK}no@oT+3W|bX}8n?`^z86)2u>EwO*DUFzl(Ksr%7DULi~EJg z3mbQvy|*d22?3%!n+HsW*z2fuR`LtuB#7y=y~G5?#FU`Mn;%K`V$!2qX{Xoq8fo-W z%|#Y2(P^`!yJf;k@6Igeu*WuY!&TUt=^Q+D^}V8QtF=FFl5^*BL*!aXm+PkEe#4~W zM!8RB{?Qpfg1ywq7^;%6i}WFERM?KV66?6m8M-$6J555T;&5bUl47Fw(-budO!wTF zvAioR&cj||#yjQ1`qlT$<9v(+`o~v^7z6P|P|7uC@k6W5+^}!}rMD2Mre2uT%YU=!MrB)1-oSxIA<-Kv5#Ts@s3y9uxm_ zuQ&W-z`)5HAmc529(`8FRQs9sbz?IYRuVy;*qD@WV;xp`jXACtTZQ7+Ax4Rk;&Aes zI`rN*#*6lO<)xEiF~@u>48b!9lFn3@qKe(4r6eF!Zx_Vi7yD$n2hoi<>H@U?r@MPSgj6fk1NAS5z@}mp5le6sk z<5?XG0#&Y#o@n-L)^Awc2UZh+x9ou-^Gsn$dIa$#I+0f^i~QMaw~5=5Pg1{?1p&X&kIWL8_Yh@{Hm}6{ zfEg(h$rtL2EG8N4I*zNvyvT;%R@t(YTQ0s%8a4EKChxC!zOlAqdRBDKavlD?&SATv zs=o`I`G(DGeKB>(MiCT6bCGChiks#OPl=c%AmOuFTm@XTpd}xO3My>lDg#8u zvB-zB224ME+8SB2jWS83ct1$Lv({)I)h4XotE9Rn_j*V0T;EuKg0qiGd!{QS&kpoA5F9ZXu&3ED)1DOUrdh&ggbA@8AP zMMf^UA1zURs$T~r70?|_9`SWIaVvmbA#+lgpSa~6NwW?Q_Kg88mki%NMhvf+9Pd9( z!;AJQ6y@b+i5NJNJSso!q2$Wtm~ws=QO|8D|GX)8PDVEwR2IVRXi+%L%oU1kMTeo( zTXM!hMED(Z7$!TwSBC#ZOIqK^7W&(QwtO?V(90OqHq~?NN|#^A7dYBQ*D=V{XJ+5%mlObk()l%4V7Xa~$p zY^g8ngrAL-^ag20c^f>O9FX2Pg|f6T=xM3JF{p^1Tah~RiRl3Z+FC+Y%K*_u{b(vo zfSr93N^4S~dFTwR{IW1KF^W(}a~wK&7>3RX=$TrP3lQ=nDZrfa#u`z%?UXmcio7Ys z5#VxW3&Go7H@cHL)*dzeS$*D*!??-7W31nk496x#nA^H8}S3ewe)NuL)|9XWZ zvRfCWtqy~Ynb`Kqi`c2@j>5tmXdG9EmilRE*vFu6au$`Dk(35?N<%LCXfot!W`Hwl zX2@@!L33Fa4D_jQa*ROB_>v?J#`{{~WM_q(ie^~rns( zXE8C{01vlVH#zrXTb23CLF^v z;O%M-w}4n`uZ5BzOZE?t-Xdq=!D429Y!J3aG#)k42?Y^W<_IpVMUsan^*cH^d`2Hd zJ#)yUzQ;h{79Q~x(sqLwUq8eb4@-T@+}s>;!aQ*B*m0=px}#pUNOd;?rUoWdE@$Ci zFd+T9OqhoiT{lBiUXRqyIZl}YzVM4FK~j_ptbB{m*U)e|_O?~S^_DdfZiLPuL2o^cK6t}lh2rV2DH!_h(c6KH3KQ>ToO z*(O6#QUr9=)nMVBi{aTZ1eseykAEe_!~vCK`~y4Lun$O+evM8q zpY*Nd3teTfF*kvWPqJifXwC|OzNr;btL0yyTALLC6J2ZMHp{Ru(GGt*JvauZNlP3` zmM2@_Z)plESAXd%8)S89urf5D43Cq3qGh(f0rBy12y`@sZ$c@?C%Ry`bqn6uu8Z=f z9#ogaz>A*EuOGl~X@;XgM&6amN9 zi@vfLZ2N2%v}y48$EU|h%(o!eS_^xQ7$UP{98*2@us2n~2XF2|SY-q3&zy#BWEGN} zsWY`nLREG!3~a+tk{N>|N3~HsG>%{=Q@Drb!jC%0guG&${cIaf(E#}A+dB|IovgIq zhr*|-ifHPPywW=%qxx=b>Wr-7OdQ>Q3MnlzObz$I&P*R!<*m@%w-Ygqod~uyf-emi zy4t2Bl*UjHY>VT1)P}etrAj>6UrvLJJ?aN!a5dFNXvHj6mgi8M;0IH$G}II%!^GAD z{d8}HqpCE>x@qr$LwYxw@&d7Y+vhl~b{3y}vL9*ftI`aAxVsjo-+u;M&-zH;kGIlS ziNkxoz_Bx$*!IOv7}~qS!X_F`t;I0Vb3+TM3bsNl(2x*jZHe%_Qn;Bpps;TdRcS#u zesDYX9&tcatS@u|E2({UL)XF`X(_3&aE(B7a}^0OdlDB5QeG+dKP{1up>_^%bn%B* zL=?hP^WMrq};78kWR!0RNzjGML?eo&UxVeQ@WJkHCuOt0o19wtb9~ni|-)?I66o1E5YKq`tA9U0e+cBHUo+nvRq(Yka)@1T>HD!l&wfNDQ>V2|ai8 z&n}>;ECu?9_uwCUv{2FB4M!tOq%}+;Bgh)g2~CKzJ&UvM`KZeYg^_b0BE8&U=$M4T zkxu9yKZ?MjDXC5^&JM!C$Q%)+GW69Z;J^`6)HJoi!qf(VVPQDE>oY|f4#0}3bsX4EVL1lC zQCA<~Qdtu;Ug&;m)E7iU(>4N;&YIY<=QvIs+<`BT+n`M`*5`$JVehsbunftgdNu-!vj?#2s5^Su-!%eomi7Jo80R$NRhkP;Jb>N2xs_a79hgG9HFGtu`+=m z6HQo#CLt{<74;3du+`ClZ*nHm;<8YdA4(mW4~j~2VQFoR_{1m}(kwKiv=;rOm_03M zCK!;6y4GIlwPcCKKDe7|z{4*E8L9CoF3E;5&4wb=vk~jA2d9_{bf&xD?XCNfl9~aX zBgYX{(~5A@12}0Kf&SrsXn*+zbi6Xr-jWX!)w2jr%t4aBDQrUvP@C!nedlumZoD-O z2I`K|cM9dxtT{B7fA?__5$^WL%%Pc+sRgP=RjP-&->m#hnI zR0oS9Y+z?+4_)&>Y00TOF9O=;!RRADd45zw@%rquPob)=j_n_ABY~8M!JZa)c*N1* zLLFIc5;V*`q$R{4i?ayK>P3iyHZtjAbBYJ{pEO2Jbv>$EdeA>1x4`u_mB7K$75*+R zIAfQ90h+*_ICu)dIW?4LT^Nv!A}+uSW~Pn^&Ffwhs4S3MOQLXO=TSJtR-!sT0qSNR z$WD%c%2^8p1^dImw}N{40caW7A~P)&rZn+tZ?A(1&G@@0gW6kKq~E{fL6&#)b`%8T z!%ud@J-QWr&1u;G#V647t3+LX2oCQzLRNJH34tE;QQUb9caJ~a3#_k9WaDih;E@YEEu+s3tz{)&)U7Qe-SO7N{ zH`G!+NOHD9WQ7dPg>kU+NJmnTIri!JQJyxUxo-p|B#cv5;{U}b3( zt`-h7@KT)XNEF&6)9AB|KxYp)JGtNl`JYrq^{|QR*Z3UT8cJcQrww;+Kb$zKM)j-~ zl?|Qf8d;DpMal+XyypeH_=Oqjr=8Z##%#c&lfZDb}^!B&I*U28{GflD9bMh#xLJCpc+=lknQW`(3&_LnLx95yD z2u^Q6PErK*)i&@9uaMdY_i5b);Uu0DCF4qOVKgiqoZ&%YFQRY^b0d{7w<1Amss?r2 za8%d-|JnNw@Jf;dzYpvc$&)CZBI*I)5q>m!GBDqU)$<>nL?oN-N z>7KULUEaY|6;OqO_uhN&z4zXG@BPCA@Bq9A^WcHsMdSmjt9z<@x-nf{sIK^Z^)I~4 zjEszojLi6FWJKl&!?W@Q+m`D`OI8&B=Rf{uxaN^+JI54pgp41jpF4SBn;#(bNw|uvUQ=LeMP$t9GZ0mdW6#0QcOQ|{)kB0sH1gXrrm+@6{KE(cNu_)BSXHADuH(L7C8kvGxB6XFCj;_GNlueb>AbSCj6x?Bs#!2~`b znc@yI1UGRfe~!K*UtCmkwBJo}f#kL?i70xp{`4N5Gjn9QS`k$<$-!zj(Y`s9SHzHz z-$Hv~0-1%4q{X;1D7Izp&&EgW`?(MOzVfCRIIWL_VV+2FLAdwCiHNK!n)9;ojw_IY;fsGS>i52t>j?vUTiu}N|eEcxeRUqzKZ((a?jg4a*gG)0c3e$Ms z){&f&ZW0s1MfeG#!-5$XX>Q3D6=cvI|o@sp2`o=i;H!B`>J+s zZOwM$>f+3!8z1q@kM85-9zyfT7=ca?xqr`*n>Vgg*u6nsv^y1(A}>~F@Nu-|@e>z9 zlj^mX49iDL`iU-r1JlqS?i1ts0I!%*vZDe;o;l(jny9@Meylx>t5;k_W%$|0-?ct2 zrUk6u{fP8|9o8mmu)1}Zrw&foIz-YvJdX42tJpub=6$;eI$Iir@y%z^Xd>9|4&E_& zgo``yYLPZs9<)IUOKn{F;2K2(V#fA3Rv-V0;F?YLH|Dg*ly-K`+D(>DY+o9<@#YL} z-?GM0R1#0Tlc{fRBsQXg=9YY(K77QJpe%+42XMEw=FWo$*f~Yfr`yvWOPbAl+B?X~ z$ItS{$&xqWsr4-Br|@}rkE?c0b)TF-9A62u-VJWn+P0mn0VV z%%sJ~X&;PFh>4?Ln9_>aQ1ZK18E-EmD5;H=Sz(aG`VXC5g|VKXzdDUjVFI_zha^RK zQzfpULH1TyxqEQ?+mzJ&SSLwj7jgJ_9TkK{iJRi7b{uT&uyb`IyJe29l31dO`Z4O~ zalCT_XOE|RZ0$+6Zkqy;KlZ+<^hxbf-r zTjcicX$J047}~-p53HS|XliaCAfi%K`1``ZB~a2jLVU23xCs&a5L?ZtFfg$}uH3bD zqOf&H)Gxl=6=lySELj^Qq}>oL&x+$3LRm#Kj!!%&?d}x$?ts0$3nA%^+Rb~F{{yZD zlxqFjI)sUmt?u?-X-sdPYK;pctwq9g)`>hlrYkp=+cv^X`G(Lmxk+nY5V!6)i{o>f zn{LTWj1Tbi{tfN>qId3lQ#aU4tY0oQ?K#}NW{0btD07i16la!@kkzC0gUbqYqC`|* z+Y=q!xq3spX&S5+W=`A)Nb@E8fywR)UXQJ?du)r1?R`SC1~7~IF4jeyAI1}gYm10a zsG_JigR}+%JL39t^{N$Bi>DlkGArLBw`GFshqI}z0c~UpJL~&ZYFuex0mV5CJ zE~c}$P8?&AMkgO(1jn^6(nu5Al$}YExN%I3_7dr;qA5R-yIMYk(lE8h?#e87S3e@9 zZ&S-NZP3)xgeW($)O7R_?ZDREhcJxwv`8>pw~`WM#Ub?H*){}Lxxt5IoaEww>XA7 zPc1EEn?32cMwT;BnSga*o)*3#E|T2FaT+rm3CbSQ?hL#h-=;;JyylS{Zawg3O{QnE z1UGT_(^3$@)6h(9UCHWTF&>U7TBX(y?MGhcJY&Va{PI_~aCCO!vCT(ZwTWVCwu4aj zOt$ux@Voyb-m^+(W6wZ>k3FLP!PK6Ff3mz&{C@K$4PuZTYE32+y#r(7;<3uYl8Hr= z{9?nQ)-l(xu}%N*G!u)P+Gn6v*Y~t9I;?N*vAlVLY3ERT>-tD-GHwB}w2e$LyKcGb zTNbyqMsY#i+BP#N;vb#YQ&EshM(Nx^uLrDcnuVblXK-d+yBHfb z%&cu63nQ?L_{X}*tPN_|+1+D$c81hQfBa&bwC`(fi}WptK(aHj*2!B)FZN+-UdPb* z3_XK$EN-2$v9>{9|18tXo2;2nwQa~x7sWyQ8-6dAnL-;IvESeeOxvP^Cse#71aFPBE6$JbnE+?fnL%r z&_6P#4Z7M8HS6-mzV_OJmE~ndX2i{>mfx0iXmN_$dc&Go+sDn_6P8!EwB_I>s4U*k)bKUojnOX;~5JkRvu%SH$w6S!S2b zY>0JbKTf$^9>Y~(o~1e26vwDvl()XIW$h~xo2CsWCWZ)db0oRe(i%2BDQ<}6{);;a zgUEAP4rDr(Hx9LQEa{fCbd2dlUT^M;bR21MFE1}?=fjMs4>rWMH#UrnPU*$*6!B~w zvnCR=D$3ZVI4|@fUfDXfyrQRHm~$6lx*Bxi`Y+;<>E1AHF*>OeW%f|y$$>atGQUOn z70b3Zcjy;4JALD;A|JOI9ubCidP5r>X*TaNG$hL2=z`YLs+BiU_LoJ1pm$eV3 z%krQdujw`Rk9JA&6*pPMMjQrGI=``vjIj?3c_tSr%nk zlHU@ZZ_CHdErmsKJWb+6mE}Y`j-pJ+o4QTo20dE3*Vz{5)6U)rE22QlGP5C08ktuH zagxY#Hrtenm1mAt|DB(A+KtZEmUb?TENp2l;cFsaW%)3P!mStQo*eYDw6I90D8J*2 zmd|_1G_J`qD$Xa_$8&tns0{*`n6Tu7>>Ij1(ZmynV1`V>D4yk;%=;CQo|Uyt2DEK% zTFRkJizrMh;()G;IJNv086f*C&WOD2?4My~?N~G5Qxj8kkLVFuD6T>4^op`KG`qpl z%9^$x6RU^f`X;Wk;vDOpSkuaZVbws-@SHdocC>4=wjHq#hr4S8h@0EOksYlb(bD)e zb!iOTs@TKp@vEe_Z8KvyVnlbq)7&*m`(@SfVj5C?NUE2Q7cJB#`4e9@!eniRFkCmW zeG~D$(nwiGKQs2TvWTXdS1cC<1FMan~N6 zgHB$tHTLK$4(Il#cX4nJWpZ_u${1I1r+iCH`!b~8Lf?^#S5lQ0x-%!8vW`WJV@2G) z=SW@m5Xn(KbT5h0ePW^}(TiI*@9@OMot{M_i)|SMr*!zts6Y$nTS332EVlwDKXsJ)d{dSj)dMedlq1alg;g z@vMxUh1Jq7m&nolKCOm)AmhDrb9@!!05)h@Nu!l`o0}e1;bhg`14~Us{`%4lJTEy z_k2I(GHp9zxlGryb3(2s!fX2{*Ol8ki%-U(?Sn|09MGfXgV^5b@gao?-aPcrK`*W; z=lguVf9LBzzyHsUr^rM3WrWt^1neK$V*ALS+A)*1?Pq0K=Ed3eWcsub!9v=0&LrcM z{p^+oYq5Xyn3Apy%gA1_>^yJJb)L5KYry%u^L^Kri*=X!D!Bj9ks9Hq&bwuNF^=YAL6&Ro=mft*79416Ti>XDVJ^UZjkEs6!)Y? z403%j|2!REN0-LHMb~ZOocH_F2Nc%98T}FE*xWXABu*B&%qWkH;Lt4Y^4E-7^XwNfZ*GV?5Rt=YY>GIx<=G+; z+dern3eqr~dxkypHhUt6pPwh$+7R29B|!4rx)zRT$_U0UwVGqO-pR4p*Uu;D^z=yV z>#-%?vw7#pd7IM>Jar0UMBdVV&w0}f+_xgxf+5l?FHGm7Our~;u;fKXUNFD*zKL-1 z9ckK&YQ=9Ehpd$3Ab}NeTe~70qde1|zX*So2C9 z$5_OzdcE7L(Ljl0~eLd z2kmG;AhUq>RI9dR|Eo)P6ln0$HX_`-1CP%@sC#U=Wujhc~? zEen$)0~8dNQrR`9Jz~+#FEO*auU+h46vw&b^7VBilMAcN&aW}OBr4Ujb>wz*x)r7t zx3vqtLELmrOp6;VG2gJfO5emH6H}{*did8W3mM0^wZGq%8;$J)^v)PxXsTtImXEY#g{oEj zB2J^IohPPsTKZmwv608~yoKaN%U`O*{>bgieU|m2ykVc1UPQn3a`yGhB==ovQ`}`t z&FWa#IMFKjZQ-;hr}ZovkH0cKvW&|-Ja4@+i(0r^z6@G1KuzQ(gpK%Iq? z_d@c#mJ+ixr0J6vBYB&o<;aPND4zhb+YE9_Yc-Rt4sx0J`Rqa~FVONLCy|d;wfIFK z8Mln<`3tfPR})~%Pptx&6lr9Chw!I2x$PK;tK(y_tx1X_gGi{E)*d@(ra+`iY*s^> z2r;Q4s~k;p`SHqPM%meJbr5BTZrXl=A=dsaA(G06lu zL@{s{u{4s}3t26b+O({s&+|`f>5*v>l6fTO%doP-k$+{LXcu`gE%Qe1w@jzpXR(Tg ze5tE6t#ZB$FY`*qxh(?=WUIccpyYBHr)&X~i?pyJ(&3(V>Zi6j-Cm`-Wq=hCw@kha zBky6gicmN{d~LEE@VOSkp~ikW`AJzVKIyFZlW>TGjbC9YL=V#ayJ zcitK!w=4ThX{~)?S_>z`oUN<1+KTmL*^=qj9$Sg{o|Wt8_e1T!JNNPf$db`DA8KFDp$ z7CpJ{vsQBOM zd0Kjh496_@RS=w*KxT9{ak+iZ44g@9)4bfzQbu%&B}WnGP*U5&04s7)nxVj!1LA_PM8>Z4+hL zav!?1K2_R&ZZ6|_{T-}>3K^f%F|)GA!onmarPb7QO*7WpPJC=8EyLr4Sbe}dHo^4k zcQrG#K081_Kp=zq9qL5*N#SbynyX1jt!8|2mDav7mc)VD5c$~LF{QP}Nb@Vr+E8yd zgNsM(@9xvuJgl&!A5ImB$HCZ-vh+t${T#@W1Mq&PE;xQr&| zR#qu5$|oVCLNg9>z(!YFx5&?QO4{bMfhy9}ZEmd7HXz~?`#m)|#mJ(W`O$77W74P= zc`9EdU0<9*Qeg}8o0h!UKia1{)`5Tdb6ZhycM%@yOiW4%X~nIGY+`R`gOY5Kza>2^ zi!vciom_8futSTts&7RzJUxw-M5k6TvU(~E_yKjrIYh>lG9k`G-9#_3F|lM;jc6U* zr>7>Vt*#PzSfYhLJUybRG@oQ~Zo7NAQ#yR08Iz;EWrBr4Z=PDGqp^ne>3xig6C|c& z3j@Ev$Z$Wyx?_>Qd$e>7FeA#f{9;03;2nN(+l2vfPOVM%6B!v#PR)omh-Y$aN?a?7 zsqJ6V&PRD;p`RZoIXa4*hDmMT8p`tsiOQsN{!pB+JM?r8P>_>JUgHGoDdKvWQNsU|Jm4g^>=TW8x_55jP?G zW-7%wpI*?xir9~ZF_F*lDU^0Ev!kAFdC`y7?&?%Io!Q&RKk5#?9}p{J>u_{1`XR}L)QJkGAy z--VQcQ@r-PF$PYYsPhAj-2U(8o*K$yw-^!w_^nd`eYjB#&JD$trDRZhfD+%pg8`|5wrvV1r9apVq1%HPMjD2k(7IQdK)~J|5WG z+49jlpORSIPJW~h);_UB2RZZUjZoVA>aewWh|SIGTz8A0AR~qk-+e@Eq!+iJrZPI* zfup@xe(xS1+;=5kRD?Hw^eJ)G{VeFFi14uIT@iO`O)t@|clp>Qi{aLCJR%FIFG%2{ z4{s16^2_Q$7@b`$Jic#FWJV6w@4t_K(S(-X15u&nggoWFo8EMEb@1rcbzCEocy!O2 zqW(FGBD}DADE8r(9}$u>sI9ZTwL-wXkFXY%ZeDyK;l-m=M?T?~_k(F@t-=2Gr+7pQ zb2Kwc)Z-g`;tj&C&HPu(dS-J!Q2 z0>{XD%V>RZyyWq2NcY1rp%>FaFD}l3p|Bm6KBnV}gk& z7|~idIyhTK0I5%xQXkR?kOw`Uq zBX?Y5SP=!MI5~vCtOjx1ukj!vpAq5MDw4gqZ~Kr>KX?!K_!ezY&w(&-b;%yQeZ`Sx zaZb42eIF-L)*stE5~in@oag}V-G9i(?|zJT$*9&kWHv70as5LcM&xOu8hfhKxq07? z`&U1~IwX^-f^>0B3L-VTir&61TpjI*PD;kj*`2J0aWcK{h&+zK-`N)5j8;19vw2|S zMQUOcS3kW^#pv;wfmmhFLZun;nW1Q>2{z)Js zVuy}|fh&s;=kz^Kn(ABea&RR?++g@T_8`A)hy-sNKDif6>%_8lL$b9tPpF$c-rrHdbbcme;JGT_sHz#+K4Vo;(d9J+GD#aV>Inb|fqzmDa&Y@?rwTv3bPR_pjoa zS4D))JsybsA6(Fr^7H|>oWrRfTGVb#Wy|AGV-8o|xgxGdgX9PKU>A}|T!=p|0eOgf zV(k-rU4>!%>ithB5C*v<;vv@``Vb)Y%Ppvs=K2z>?uqmJ(~tSsEu9rnSTE9-?V?f! zPVw6FrWiP_wQAEyY?#0HI*C+KSv8%#Dly*s3F+Nt%cFyZAv`>T7!en#q99Ll`*)aa zE5awLUfem?5*{1FlbbgQNUtHu&z67ukAFg=d@7)8;{EqNz$YRBUt#L12Q7nn)>o%V zk4hxU&z)b0N}##E9`8W;vR9~0@gcl?L94`0L`4zra)HzT%-vg7_{LSTvb%!cgCIsl_@UBlqD95(?s%QMuCYW#Mv&gv zM7(nlHS;H|4A$V`mW_O2gER|c^{IG9mkG1D&68ie$K%j=B0{1lC@RO()0-iA+r2j` zD%%t?Qrvmxx&sOE(S&4_P?VHNaBQaxK%`R~IoT1s`|({OW8?6T&!n_SRAk;o+J5xc z7ZDy1Crt1sxM%dzS(-vzw`_7~$1|#sx`HJ9;;S_yk}3?Ne?TbS+49w&P$x`RMDeiJ zLLeVG4%FupnlmM8r3qX;LzxjPb>xNIjDzl+YgETTnTB!uJYiX@Yi6W+WT-*Oc{RmH-eHoMs1dHSCM2E%T>->;w4rvVbR^sAs8OfjE zA4GoVnszLACY!MKPSH$cb!G%1BF~x&G6;{)r>S!gpL-v0-$Rs%ut1W_2ensS$d}9N zyGn3Q>kWaZR=y<0M-iKphKq}*C~I2s7%t5xETdWbz0^~Jw{tS_NdX9N&CyIFe$k>V`NdEv z3$Gx?$DP8#eaj=sBNKu5f5{*It54_?GY*FH`Ngk3B_ui?->5ViTG~Xur;?eIN__F8 zmItz|O*Ul_ncL1pV*&p1)mDO)_8k1eA_)(Qqo7B&fr+r z&t6=c75JFgtRlR8Gc1o3V?`ON?ZqyrD7Ltd4y^fcZOYcrG&=D^5oWS zk=_*C{X=NAyj_K^%qYAQ+AI?%%h+7|5NnTU!o#D!+?M8GroE8sR~}PVlg}0TeZZ3mvzkc=_od>jl9m_596vW=DkrtL_m1{R@^{3+Kc4EA zUP@B~`Q%|Zl{sPDwGW}Csfi#_2_}bSkd$3RRz`-n_^(@r?F;G08~Ncs{8N&|ynXW^ zKl!JBihqu*^mTaO^=D9QqctOeNMXn_Vghi9Efy7b54!DDA|H8EIkU@@Ft;vGGqsls zOS3Ri9gk~huBgn-M7cd7D7jix%EPR#t&`~IghxUF+426|wThy#p@4@^Lnv!)VO+OH zeMXX~u;dFl*9fo)p{k(*hesY{HMY<{wa7qo8Lp1m!cgoHe(w(MNfktRSmT(|PFG1J z_dSc4?5w~wJddiJ7<}R@*fJjz?emDF%nIC|`jTH;jpL2$1eJ(96zP@uIMk4h)uRx) zI_h!u4%Z&j)g}89RM<*tfD3_%RkU;uGrNAIwGhbt%oi1NeC<3fxxobF4p5osLukb! zr{b6l^|#<{{Rxkg8)?Xh#?~{DQNzAwnm6VK@qBy_7mpZ%5_8Fl3*oVMDz(CF4=-+G z-Z2qobAzYJHLQzmEzT{oBwUd+-bY7=lx2r->xLhbBH`^RA>4D1qogz!=O-bw%?M*` z{fJ0m66|l@AgFjkGn)sy29iA;a0tqxzIl+sXjkq&jwe4YjGK=W#f?b`PTnQLAnX$E z=0i@$s%BpHHl}d7XG>IeA&>5Uif`d4OB3A$JhkDGM+(uAzIaFH)7mz`GSS>UsJ%l`>sx9*V0@$zOzYMuj`ZfCX98u_9rSlMVsB?hMqw!dUY;bC^pFwhL0YfKqd{RD!_p~E zkL0Py^U{J;K7RiiMMFp0xg$UI6zJ%Ro4>0#&Z)FFG~jISMrvIP9Rt(COde8_8OZIM z0SpVrA-{?t4ct&gG;Yx)ERA+!XX8Xlc^P5Oo@CUt5hJbvnaz5wOvo0mmDw(yK6pe_ zN-^z2bCg8|Ve6YjNl_-w&M`D}*5Krtib>qIWrPF^qcli*s3$&24GeTt;O>`CeyTqq z6-%5h_2TLuOG8~Xc8?rM&(7fHhc;AC&JgMHgvjg$I{GJ=9`DA@-ky|#Dtzp&aWkk! zyD2<4*eBfS8qV^X?B!Hj(qTmKVw^#_qiFDCvms275hI-s;4tfae3q=N8#j^%8WR! zvb?X4w_2V(<0bWUw+k6D-Ir>XB;&?-*J!tfAvEu(Y|3^1ZU*80-O3Szjsm!6qT zt$&Mbl`*a_ks9WYZ(I>W^Gg)O#S$2tOj*|`^YhDe4vsR=H_hPq0v#QF49uIgcw`@^ zEt8S5%37@t+uq(brN!m+$`4Z?Y|~mV@<423c4U-};SEe1dXl3<@bHVIVQhuIwjn0g zj#yrtqq%jOb-Ax{KW4^h>Y377s#cc9i1iD=BRG}L#S^yHbY!Q-5D_LS`AYm(*`xjesIQ( z6|xgzC}|v`rAL@=VXh~K`sk7$-(H<0&R_0B3awLH+TaJ-S7)TBkG|P$X2*x<7++^% zs9%H^CT(j&RALeMMCLKDe1b_gL1b()%@da6ys@@MTla|eqSvDAe7Yy}lx8L3>J>;% ztL0mt(ayp&HLi1dtS2fcgYmObl=4jM&JV-RztHjy!Yva8sZnBGVcPrGw9e)utrhqM z1{0H6P5;ccc1*NaK`c!Z9Tp_YW+h__^JK(@i{ELKx6Gkinis}=UhLNqUG1_T*q&Ct zWVeHn)^Y;<{fNt{qkDRVn(PF8BU0#`J7n9aCoM7vcduA#hYXf-C5-0Qo{_@%WO8fz z#J=`1FY;6Nr)nQJVqBagJR}IOkOW#M&03yp?QBt!8mqV}`^&`gnzjn3m^g=$T`4%SdiwH2#4pUzu&D%3k*?Uh4($cc{at+wo}E+LHAc*v=xxN`RhY}lVK$6AG%c5(J^bJqpBD`-w-N#mbIHKpJt$=b}ZMl#fXd>_0<+?IY<>M6bQ$xlhDp9K=k` zpv&zDorgak<)?)g>z?31NE7`VsPK23eEa z6X7P>s&KG%p>Wt>DYGIjFGQB*`V#ud7jAS+DKlxze4BNB)M3|DYt)?JS{KJ z_eYw4S=P_vl;u_Cl|&xfXW>KunRaboh2*}-b)G-hpFgfLjF{BYCHG4%J3j|x-pFMV zIq2d%-tS5>jmp3&UTfYY19wr$yUp+N&hYyvvv5blUCqQsCy-p;r#&A0t~xBsieh7P zm6D81!sByjo7{S4?kd|V{&wq>iDtT5$c#`BgStrbKplhRt;#B%wFtEnQ7>VY+_)#iNpd3gCa z>U?CUl9G~=((9p1W#Cq34vD&@$LYs?#Jl%mTotv9Lh&1TlMI|RZ|m!uOf77%x+Bcl z!7;P*29}H`nh}%VlGLx5SXe*Q%$YQK#;qe(#l)KIlO#^|r6mK?`faVxhsm_X#G=VE ztp3adDk&)`DP4YD8UrWIo5v65`RMn?c=8vmMB8`LoV|9oRH68C-V_5TTiOf@gE&91 z!rslFj+sr`%Cd2KV2{0LGCgxTV%=`>$jy&?PXef&+Gl05mC%SXaKi#n12gF|z~5BCEcOmP92bB_*ZbPOsd+EspMMEpJ!<&N#k5nWE5RnnL$!GF*p= zv+HKAU%o^8#h8q}Wmr^g+ctc8p>!xH9ZC$+9a5vx0)vQjHwX;fCDJX;kRqj|FvQT^ zB^?6{-8J-(-^g`6_x(QGw|(3DtsneZ=QitD=Q=rJ-;Yq*VRv4)U(q1i`n?6&EM(&S zomE^o1kGLs6pk&U2MFXfxqUuuCrQHE2|L8g0~@N~WxL;aKxwmSv*`|6w_3%o*9wYh zVJb58vaWx+^4lVF#&yX#^~4j_n&F5_4x@!U%^dsr-|Z?g%po+jFw9< z!)iraj#h`Xdbo`8Fqn54a_E$UY?+9#MZ9YVjV^MY_D0!CrvuEQ$4iBPfs>C+j>k7e!m_KpubN(e>z4#;YsG(@l~Iu)q!8en^(q@@lf4(d z1}!{X9|$-adg!kq5(4JtUl{+b2R$+BdS5dI85 z&{5IW_ph;geluHhd?p`B`b=|}yw-@&UDoW^#TtQE_*A6O&qqU*P6J1moJBeXvkFXW zc*1K`$WyATP&9F+J2aSl?ITgrhy{`(G}aW_udopt*O#knbc^&I9OzpgfxaPXu2#A~ z7zza%;6+BCQw3Pl*yk%RC0H{e@OXH~ss|#fb2&lHC5SxneH?IHaxs1Rn{Q4zv`XB= zh2Wu=@Oz#YpI#Xq&@PmJH`4#O$}C!G?zyy>@uP9~J@WEchC(>o$s}K?-yh3>`_snK zkP;kxeIcz0|q;Ryr{cvMV^CpeAa5R4?>DoN{l;%RX zi@Uz*bpw=!uxVe@z(Zce7JDZiGlf_UxU z{kiN?o4PRxqY2NZ7iNtu4i0Uim#;$qdv6Mj?3#c3N2<{@z- zkBTqe{b$MbBqrF|`Kh(FHF_dN)w}88;h3xQ189A{7wPg&xd>m!#lYpv_?nrQZP3JN z2J~lH>j+^4Uqs5V#P}Vru%I1xGpu98DbvDBEe7mzY_oloji3HD`~AzOWE(Yt_OD*O zTHV-CRa5JP+cihk)Ci%=ylC<8@GM?P3?t9PSZ+aLF3xQ;6G0XGW+jq{QA?`E_Wq>T z$tjtUmdm`N!}(2^JL1B+Zsw~xlrBf~kvEUToR7M^1v#vuPTM&OFaP)RcsNI@*txQ;T|-u?94bC6_i|dvR6p8} zPFPypl~d6d)RIQ$eC;R^cJNidk?E0Apa{d6QA`6FB0B7KsHB4^_)mtQVl!{FWuDYQ zWjcRG^L|%#B%jPMyH$8gAsqDQLvjuU5zOuGvU75B;u8>9jg7*jWo1oFOeEdhY6K6Y zZ9oDfv(mw6!XERnd>xR{+rB9GKL zkk>WFLep77`t)nCrUxI?DwlEg3G=Y#hh+?8Sbssy&%b27G;kw|QZ#42moUbC?V-k9 zZo^h8zA3KyWSz8}E`|$TDTkhtncIC10bcNcd05GdbOIWQ%gmx(zU?J_34J2Z?LT)K(Adda2D7h^eRYG zS;$!7YFgW7=~5KP$e;HaI|S?Ds^DlQiKM+J+gQNb!^`duaT)InRI^HKr#QtJ?Tu#wNJFkdpBN z_r*jYU_Jfu)p$%@u<`CZ{jZa6aHdY7^rJf5FY|k703t9zP1mk5M(H&*&TY)aY7$$- zFs)T4?}VPx2gS$d5q}md1mC&H&H&C_t;>mbe zxPG~JJ}q0arKuO4&EqvnnFD(~38H|_i~}ZCZb)`4;Jfj0P%G|IHyu1KJ+7nC&F{_I zczqp(eO2=w5_;?P%Af0|Zf&?nI9`iw-$JLSV?({{Gpq5({%XzUajuzMT3%u}(>1hY zXn_aprhFtGW9z~9YPllvmXpU{-tInUDPnJ5laH+SjD?Dqa}?44v=tV^w{We)0(!~D zMTlPLGOQ{5cW;FR?G+Rh(E5T`*pf%h!^7j^;=^jZ73FoYd1eU48L<{OV?jRB`w0MMCRJOht3h-iyET~ z+%n$-XXQU9QZX32fw}@~2L=HBh=^a_5n3FMesYH3n6oSDv9{;_oM)yMW{1BQLZof? z*>5>tM1q``9S{+{H+tpDt(piQxtT*sJeMWia-44^HObi{Dw8$CG;26ozC?#ynZNUK zzhv}$vZJ4ygk&md?#M02O6D@%t*0FlfQ(@^nIYALSi>MS)8Jou`6N2V!QuQleF^TEQvfxtq>cM|KFzIiaryCpG0M=N+vvTh zM$k{59r7^-oKhqq1#BNV5c0MAsY*`rYCM`%x%tB zU)A^+@tp~V2$5}nWxikCdgB?A$)HEFAB|KTbbGSA6V}fl%e%w;KN}r)6h)9uZOdvv zLeDnW((*bwI(jI{TL~1y%?{!dU6E19!X6+gV4|!JFACyT-iKqy~T{r zk<7gUGS#_;2h7`6pRc+q&&*)77RCZ5BuerQ5Zx?INVJNoUcY@u`NZ6Hak<)IP zxSlo)PxIjA1mG)g4dMTOp#ykQcFnS^L)JXp*gJB@%7Aeb3F4@@+ZAA7HVqW-1m^gc zXR8QSSwISGB2Ec21x6tda!NzT+ubU*kSm*mVm{_kp+-xOKp)EmQmY#Box#Lcn3$N5 zNn3kw@0Q7YEd}_(BxybG2Oc{wuVjp4Op|sD#R0!03obmg>xPRqaWG-BZ474Fw7#hj zZ?Y7P%P@P_w$>#HWijI4wh~hxPa9VQ+uVVpI_y6CO=Y^67&R3>9aRlXx9qObK!p^) zJP`jXESUA-$0A z`}?lVA`;;=r7CWEa_O~gFBK1Uf?E!fDQLX7yXH_igC zqvwVFQn|Yi#AzLu|s7hZNmVX|`^v}cc{NrVMKcyH5+VMdvmE@9R zrfGI}3->kKu_c9}?qZnR zQfBav=zug!K;M_kNgQAzQgZIy;|&!OT`Rbmr)$97RDkd85r%@;otx;BA>Y6|MVl3l z@+gD{|A60_d&=EnEB9SVxA0xb7`7j3PRv*De~Vb)9}lp`k3lg2Fi)>FCXFvY(cL9Z zvGT1mYtjqZS9hRG?2jjWZX_}&*+e+|Kq=Ay3luNj*d8+% z2&rcnMH*%n){0jEsx7I7kZyaID7P#S@v8>O+ZNJ1AM1vUW)PwwtocRcB3%T6sU6TJ zQuBij&R8_jaz{^B>q|mXaPL?*B%wnYcczBwnD+;Shf_Bja+wPja?Fuh7vy(A-MkSx zUe^b-D`H!-c`&tMxEdfIFp1(iI6aP7SMv0VlBxj{>p;KgOwo2t&`>*0{Y*BZ?I9J7 zC4|4~J9YeV%JKjb-*5`z0&*`vc6{K6d1ZDQ^oSZPIn)FWXqod{rIG{ z?1!Y-0K(3{@cSj+tDx{H;`{SBHQ6$QjW&6d%fr^yJml3ED#iZbdBEMz)5jW=1C`Fj zi*psyvKzQ|jqp~w1TZ%*Y$#?_!)l5SH^73i*dqkz<^cQ~!qPa~AX`kDfHZe9#ynA673tEvt z4q8{T?=8i?8Z_|a?XR2$s|~W>J%=AXiz+fQp(ki-u)+lUXIEj|iaCBEnsuAeon6kc z5m)N>y3pWU0i1X#V_pgQx_=_O7;XeO6dA0@cME#96Z);v>m2KUJ>!b!wMVNU zr1p0-o1b{7O({H6Ghm)_$K#k8iD% z4v%j_c@N^4daW54vC+7yb(@+fcQE#9R!Md)f_lxZSoUI!pN7ty;|Qy11>wzD_`jr^ zm3W(2$29q*OE&&2{ISuHDO;+ptIl=efuom1x_4C%s?O1?B1n-^1a;+f;6dWhd)|-M z6CFDq*BeOPowYL0fjBX!vh^KnGaHtQTsvSTwtr1@E3@-dfN4?{T}tu(r9KdSkQ#{f ztuf9E85QRZTrw8#LNnIvderM88qW73(9!juRSNg3&}vj+m*-Q7HW$BOvf5~6uX>$b zdxN~*&dOdc3QDbVuSvh~+J|ejnQ_~%t-zDIGcm?4{P7u6-%p6t~XnM+!(#V{BMB#HzpVlkp^6pJwU7@ zOU_Z#vspoWByO;p{w1f=ePk|8#wh!A~Y}#k7SXha#y~aEC z82eRozO(N<<((;rSI`}jF6-ldh&n6?@oFaBSh{VRPF+n%;wLHQFYi@4>1D_L?TO7#1pc0Ac9m_$pU&Lyi zkvf@(J+!fRu)aE1FggSBy!DLS%@-MRZFjdi{E{*evrld@BMa$vSK|kg$$BuRi07-k ze>@VR5n7aKZjt}xY6`rxXxJU1u|JZJu<&YlKqRXpak+@gqn5LKQ2O|ClQnBrY<+Sf z28U&LWz{e$@-y=s%z>olhod4DiMy8G$`T1ysctRky&`q=xx`|f4|fqg#bqY_?ml9* zI#IleM20j~73OnH865*Y8u0sWm9Kdwh%-0m_T zIGhHYW3y~qxMlmF-(A~|rq+j6UE*Ykg>~)CDx-cgCC+r3by?@2w%}R6OLPjDS6B|_ z5A)e)AVdc;mYAHQ`{HvS;8usI6d=hlp)jC-O;W-mwWBT0eFf&3mG^`7mwGzjC=iU_clwxi=c>)n|$ zFxLbmyE#v1&jm{)ImU#xag(|mEN#^Iu!Nm2GYffZ>gu)K^cfhE>2$v@$MQoisEX86 zp$>;{zibZZg0DBZ`L!2}S;tXU%YJTq}2 zH3dMzDZiGGb8l>DV}RP*2vVP>4HsRK!XJc9y+BlJ_rnX+;rK+_>@2-anQpcd0cPBR zQ+usc;6fW}tcY>l)N#+?jz@^@YI$Wn(9f8OKx;u4N;I~N-+alCj*DeA=(rKCS|@m* zB>5jm;1T{DWsHP-+UG(hqs+RCwL279$LiCg)!D0y{KT=jx!#L$Y7PM=ezk%_sNW5t zgLLhj&-q56`7`1rdjk04+b$8+3n&ov>?exideh+c0il)N$9nyZaEerhX z){YelmODGlLp!8GnKC1zv#)m+)A5a4$J^`e?oxuS7&);MSlZ8$>0P&=k936_JFdTXDBzb`^~a}VTMG@d#k_vE(3_HvIM-(A zSrr7Sj4ZKIZCtZj6~s450WEVmRB17=HeU%>G}|^)4lpuSnvaDlIW|eF>V0<&;qfQQ6|j&;={95hKx`7N3TMGvc+4VZd9|uW0u~ZtLm;sn)?jQ1@CNVFs+?>%c!J* zA7#H9{Poi+%rx1>(b-c`0nDH6U^VT1A{etwDZQyG@{ZWv)$s*q0VE?s`dvi@1WtT4 zsVC)o8j1~7P;&(YD1@XJ#}Kb<1gE+EwdZ86E}PcN5?|t3P`M6J;rYd?JMcHogXr9k zXGi(s(rW*QPCvhq1*$8({U{hjVbpNmfBtg6Dn+fcrhARsp1GJ~iRM_{aELRV@@#TU3TxCxwIsKRs-76k4bScFJuy_libbilS3FbiiKo-I zzm&@!stQx|2|zAoALFP?->l(U&x5>af@b<1*-_DJuo==Q*jqM=;m#ON&>?Y^xtQWA8r!usWjFAmEof@e9kAg?e zqfPm8qWvrUIod}{`VWKL7q_3bHe46Zm=h67s0@@IIJi4!1`~WKmH-r9cE-p%{ax}; zu-@qw{*Pm_g^|3x|E_;oFhi?J^Hn#W=z{+shA3}D(NAlzg@>wyqIiA_A|o!FjL2O6 z81~Mzb9E-+UQKdk)tE}7lo;6-YHMGSl9DDY*!Mab@@U4a4Ohj%$7f{cj0#ih&zab20thFXNfxy7}RQx=0@A zZJN@YozbFtkORC6_S@g8H0<=0V{!_g3%elq#6yo1Lr8>o;I|>LfLVJ^t0qL2_}GfNQl7Pf1uM zFzsA7{ejb3+yS=-A=KZlgzO2C=5msbSMSn8zJT||E~)Tb%8jvW*1ZIjUI zFL8GMhqe0n;m=j~D>p126AT8c7nu|?2l>}elvTB?5!oSscn2BeaUBIrYwbVVDreDJ zM?@!cUq!_RT0@0wg+6iuCGh0YVwyj@CtGsdsl6)y%3@>a}F0PNf1E z`B4H$uf&o;g2*;%alVgS@>IrY)(Y{Jlao9-WN;v#5nv^i8{A*axnp2iIaKvx0Dd{g z9Tla-9(vk5+JWO#U0W@sphbHsZeoK=a*pCQu64Mvrf5AVLjQ;Sk^Zu2OMzgn@c}(t z=)yWiwo*3N?|ug0qvcM zJ}rf$*!f1i=rgxz-I~g8y=F2wVeRcVP*nfy$?bl0QdmfRnhmz9IX~qq2?-+&MAUge zcjb_+;R3hef=)hvTa-1OZ<|sP7Gtto1plt}4jt`&c}-O|L}ccLN>hZN-*;RrW;aY{ za}eRc_fbDi@gn*rJw93EMhvLU+8E!$RPXN&cF}sccDm!;*`TpO60>~xNr9L8{z}>m zF*^9aXF<=E49ph4iwa(FrOTk`ya7KTyiPf z5vYZCAL75`I6FQUE)Nt?;h_qq!m)>Tj}2ob%hEb?QO?Y0nw%RZxqHc0PzFyU5Tr6T ziD!Mz#3tW7orTbPe z%Ar1Tax#hB>6od7;ae1G#=i6vkGVL3nfhklpJcM{b@KW)OC$va&Q5;PDN zjf9wBH#ZHwg`@MKXb6VU^dQ`LrkZ+FWPoDIU4Yvl3V>`C{bu;KRJi%g;#>#zg%ISXW%J#W(>l;08wVMmHL zE2G<_q&IkoDDP~v^t@`W zkXOr6CjcWYL4jD_PFdGbF`8dvamDxvJFSTlVMCRDdzf!IQ5e(z4Z5z)MtjLSmw^ z%MGj6giA*)oFr9GS~`v>%lGmtIO7L(pu?=FCSxb^g)7L{bajc6xL_=zqR$+B zmftO_rK>;HmeF0qFwk>3%1#nLW`j*qX{$@R)FtgrMXxNFl0|9aY9wtl75PS}y<1qn zi&zU{0TgWT*zOqcs5DtH_VEDOxP?~@atKCw za&RT%17atYPKMWNk2#0}j{0i>OC}GbgCtan21Uh;T584;~zQ`L4&t8^lo~`-x@?%UH^ac>!r4xZA z^3ctHU@Hz{5*Lp&_QhKFS_Kwr4>}EX9!VI5@vn6+K1m!tF$~aj#khn0oun?vT~Zd_ z_bFqmiM35E%W-Ai?NHu2n*;b)$TxnKt2CC-!_N6`DlQ=dQweIyOKb=G925PI3Fw-8 zf^*x9P>|eHbbEZ$VI3(A#m^q(${T+jjWM{d(nNVP_1}KP@O?4zaf4}bktY1Q3pH!_ z;sHq6-r)>T3f48_6e(vw|KXh` zUm8Vmf5((iX#U7E^)4!$(^JM8aLRntBS&Kl>?kfn4~wYDHuvklb0~Dq)E%!_ZG9?eJyk z%kLC=`eubiALkA)KFk9{RDkasL$S&|qHM6CA7e=)4Ws}O*6_%x)fKj7YuXGGv-}Kf zJLQT`UNih#ETBVXZoy>f6;5<7Tl#b&7mP7-QqNH2F=I)CUdn5XXDn|nWG#5v2IgPe zvMw|kIV+KCrhQ^o+*PfH`SVZ|^b&6vn|{J!Yin>#0xXg|elwiTo$Ryr(A`UQQeyCS zo!Y~~qlxjj;oZ7NXwR`)l2j&Eub7=D=Wu+47AbNMSMvP&(Z@e|Kx$iQDYWsGUi0jp#C%5WhFC8yjDPRmoD7hCFh~|W$3qJ*E4CR`ttGU zok6Nqf%Et+aFDDwMoF!N*hv@}#FCMV_iIn6TsrOS2cg=iQjS=*gSi6qO!>cS>VXPv zBxJav`O<>aPA^$=Uv{$vk`lJ8|J5|~=3{cciOFf8?>r$%-W%Q81CXjbtB~C^cT>5f z)2F|DV_DF(;K<)X8YykI;q)0{@7Q0AU%mKQmGQK0b|q_K;I=y2lF0fUl_%2MJ#jSC z%xsY}+aLZU*s!~@@3l2Yl6Bnk&6@^`}?Qev-LO`gUY6nnsT1H8auP#bSCA!=4M=3FK zYetY`WaPcP!a?rYOXrHRpwvrs^{BLBMWXjQYW=xGpFMPx-Bh0`>6zH^A>L!-J3qauPK z9u^td(IVU0URv-4GZ)ts?bxO;)U)a-Qp=K(Tx&~ha=j-8$)RPnX%i(Jq|9|cO2LM% z9`S>TTB)B{H=nKlW*;hQTBl4E!J6T>;{%SqQL-gDwoWVFl9AFbB2U#av$NiF@+hTB z_R!}#n(OI0(#ll!l^7PPsjiA$8#Z|TCx=i!v8k`66C2}vDl_uXt$|s>ZgI6owfy?L zvCH8WM3CMT6JLl3D7*_CtnGtNg*FH`Bv5y7BQ%tpEqVK~y75g>hI^E6R8?OMotGNB z3@1%DU{(_{4^UE2 zH9m8A*nz`fre7e&6x>$#W)_bD=CUVAI|E-?r5^Yl7i&A%vc-b!xQK3M0%NF7`#3EH zPG{Q^V4$Xw4aEu1H%RGSXS9>3^9?B=DshM!$cNHr?9kmrV!4&ndcajok67WCuGht2 zrRLXqiX0_xhMGfCrG6hx*FUK#L8k9*o zeDA;2%ZgDxJ0(VDoE}sJLQibr9;D~Ta36^|4{vi02?ew-!#rkBi?FbIwp3kH<-+cB zkcn`c!2$(~Mkoi9Ahj+Re0Z9Mk2a^4Nvyn*WVvR~p+1+RVtz@xbI_cOy&5o7z|=3gHO)` zO=0{AUWEIK1Es%M&);;|5pM~Va9COC3KT=Dkh=Y|8w)4^;+2I*RG6>PZCpcnXcGRk+|(J@ZW zx=${jD=-hhS(j{OBC@?unEQ$q6MNRx4L&|46;>7a>h8!RjXblxye4?QotGbiT@Qf4 z49sJq!)w|!{U-vwtc#mRvyOF{6tKb#g7mW<=g)UNzJXZ<97k3QqZ`glZef1pytb&y zB+Y^?Q676P>8vVk7N0w32!48lPTavWuaVv#|Lm*_69V0+b>Rp_DvTQOoGAoErF)C9 z`Wb93mBbi4N5x)2Ws49>cADFn+_}%2Jb&(MNYoABo9_=v z`i$F|{NKe?RIrsX$^0n2ft>-yOvs~co_}OZ{+N^0g5&f?J1M_p(V?`G8^N5Jhf0`l5RXetXuMx#Zos?)x z+}JyRzwvmH7Ej>*>XOfl`}fz?V6S%(+!`gTW)|m9Eq;YP41Mj_EmTW<@x_XjG7$Z>B(C2J)wz_^rFN=!#@O z8+o5>z3jL}$TC^%z`#07{Uy&QbYGg`vgIl7oaMa#@^t7;ZYSYWwbqM_ipCXkdyN`+ za>w2&w{gT%P_IKC8L>-CF^t}n{9XWkJG2tfFAxjMcl|(XpJAYd;^QAzUYzevP-K4( z*<~Bhp=45Hs^nK&v|W?lk)z4F-5QpE$<>Y6r&N(PVXxjmPK&3{u79GI@J?p6bGcD!C#MTQd0zBtrz*3f`oQ@g2k$mm=S`hM>ZXvtSEn$z#PWYpD|9{R{ z?Pq_ug=wDu2#*3@y3M0BaWCFizD)c#-5{w7f^!DmAsP@>c08TOqOOSX7^`SY`}{%&1XCAGZ(+-3_?O&{_^CR`W^T7 zJy<+37Id*e*lQ{0(jY0|$+V;0*DHznB;cn1q=1Vaz!wl(oXlR20FQQS7YD!c;T^OgWW>ztLxG^ z+!69k<<2oxPxfiM3$be_6D1WLh~WLI?r^>Cp?NFRrR}0;2ISCD+ zulwpE+wgInE*5Fe&W4^=M)Q2kl6QBG;O^j`koHVs;Zr8s~Gr|8zp=+$bdqxYjhSA-E`HW{@_? zH5h`}HUrm(SeYZt>L!7)BeX}_D?TRZZ%%upUz8))g z)X7@xWM)!3Ionv_zMAI!#XS>qV1iYMmD_j~8IQrYlh0w>cx+2;Q)0~#oSz}iF|-18 ze~O?+75|NoXttX>@sY6)3}PQ>5JU$C54AqY1Q5wk1dMf6~GA zmH)4X4Q&+n4-db_#v0X$h;Ht0|7$+Ntz1kWRY%|CkkHQx{bJGXsyped zKz)~*P6085Y#gXw;od(1o!^lL$^8DaJTs_P3+?#b*A69c=DvENl{`K*RZ*Ecww*Hh ziqCF-f1^a%%VVb<93ip&D*nPp`nnO2BwnT*xKH@I=xWg5?B3j>r2^YH2WGeW?lFzj zuhaY~mSM`nQ}9jrR#Ol$Gec1l{WE=fsU*$k4|U+x*?%EavN8q>cDteEtirtYD#JmC zs$x5>!=_BmMOj^1P||LVfA}8?{JD$imjM_)y9ayaja5JY^*3mC4@So3d*|kg)w@3` z<6T2rOTprlw~eVkfPONJ$Ji8EN$5FZhnjCW;^%>$LVNxNC8a| z-SZ9zW}lz_^+K;dc4OYvuDtZyLt^_X?YK{rsR&f%P*mwWQn;Q7kxU&y=g>H8p+TRtV{Y&w1cbraA0)VX29CHA*ly(#vA9lhx?q zd%l?#nu_wiQZmpWXVzsiQt zEAbLElV&ANLd8bUdWnbPlD&2__Qr$;ohU$Ee|FrN;v@}!&hG_296{1Gm}fp=JPtlV z)|Z-exTRTUA=W(o7l?1osD#sIU3PbXe6rT{1r4e&(B!m+-}IHcQ=GlBL0ytt)t2PV zW3wvT6obV=nL@U$)OnG#%k^>Q+C%=@H5**Tx(9Vv>qGfZ%i{V!ExJn8xhk@q2xzDx zW+NOd(t<_^S&Oz)^m)p&>GG<-r{BtpuMY~;rLkIB(iy@V0>IzX3i~JlNoa6P@x6hT z%y3Hdd*RHQV=n5wM$;!~Oin=E^bvW(2&jp?(Mh=zzGO6sC zniJ)0qoCL?&a2BN39!=Wm{L}gMvts%8cd|1Eb7~$g>*JRhYWQH1P14eZ4WAV`ChiE zaRZj&f&*h8^g!K<3EY}8G9psmQ5nU!s#U_AM?Wf}DQ@yf4I~q%-6#-~tm08>+O&|s z>@+GciIuJ4(68Rv<*#zqj{?Suc5Nv({Wyoox=k~u!o`GqlQu|(H#~pk>4xLzGOr_DPFi0@?Lx_?IlYVVNKtG1c=knkO$514Mp-c`}VAR3(4~sbzf4k%|0zLp6*yA777bWD% z#;fZ}`epq%9IrtduqIEmK!TpYwbST@0G|&#{yxzWJyFYB+M$!_PVmoLqWt3B!15{6 z5REVnyadv#{);fS4RhTii~vfU(_{J?aNr+=(F6lW;pIOF+PldG#PuBE&ObDY|8t4Ow1 zZ_SQ*+DX4o4jJAU%yrm*OVe(aH;-WM*so?#Y7UlMyVRKkZa0DX=jTBG6HwHu0c zOWIK29WFA<3>lTE>Km0KXl+i~=tP51t~F*wb7cl{f2=^0^$h^yJPRl1><*dMvG)qY zO=A0`*@3G4XV*7KO|to-V}UD5_EB17vD+B;L1*R9_zDVx;HkpRNsm~On!)Cm0u{$W zM1EP&R}=5vO7Vxicq26#9Z>qH`G<|l3-+(O8?yRFOvu~Tr{ii%K366-lDJ!?p`GH@ z!2x^bzPIf~C(0ZuNz_V4S^~Gqd!a);^b7qNu-OOeBFh)O&9^CS)wJQn0Z7n0Ka2Ltgp|U$gfH&J60wT*FpN!h)1pBpL0<)>Fny8axjQ#?eSZ}iVCt;jZSTdLs|xM6iC5$dnM#O>sz-h8yM2nd4UtUI$A z*PG)jKotiEaO0~7^Nr*q;J-MltpzKDMO$~&q-*nXE4WyyTjkOUC3K)ztXA*1wj!HPl;>HOR6fO3w z-w$C;y^(CY7>UI!W%IVB4z`$@KeM1?;bNUxGOtECNd~hB>?;}LD)}r_kQ#>FYuuj8 z&@mX0TEY9Bmo3GLZFT-!f?kA^^hy~B;4jcmDYWMm)mSo2|0`MU6P1%g#d$uuC++wb z=Ogq`@~9tBDVt3uda=0FaCR~A<4P^b+S2PA;KhbiQncmRO>K4{lZGmUTFP?!vmh78 zbnI?8W!qfCh_#b?n9I3QORmyOed6Sq7WrOZvkjkaQ}l>GaRoX)2p9z)CEGJzGQES| z982hC;W1zN?`59SkmRq|+Gln8kb)u(rrLXD%d+s?XtTH@>dniC7NX7HH>apm>_=Et z1wwO`Ba}CxCt8 zz!z_m*v1V(PuAL-B06I#wZ3szh(qcCHxZ74l^*tEvce#hne*&jbtvhb4fJ$D;Tl|H!{(#9@x3P-m660>6OYUU|M8gKA*8tY?JJbw zH*apo3K*`N=eto_+h5Q}g`?;}zqRD*6(Z2+N=JVMHc`^(jlmqN*MUjsF8JMPj~vk4 z^?bYpBKTcW^|8Kk&hK*!-_z$5gh~lP^(HINSx*k1u2+L7xWZTSqcAXv+Po8C$dQ1j z!V7KP`=q@t{lA*ueoua;9%JLNP5yc((m&|T+1rO5@QsNrrKgWpkX>cr08J&k287VGm6ION~)B| ztE?NZm0}$Fx@vIj5YLNkC9kMP>p_N@z zNuZNAgZx5 zKYvf@PlA8>ta_CpH$ZaF1x+ z^n{u`t{Zgru)RfAUz`u(w(gkRuixn!;)`(lf_BUHffaPsClMq7kk7jvYp2&Ht}oFp z6+LXKQ36c8{F){He>2sT{9n>em^`iGBQK_2M^GdK?9Cd zy5h?j_$0u0g2;$S4qukUT83Nj@$m`AExiLq%_R+}Dfa_^)YQ@n=}@X5kE}dcL>fgA zc*$%`44njs^eFb3$}w{+@YEqyJheadvKDs3ygF>O{_?vn0KS0QCPY(cKt39S?#azKX>Aa6-^Pb99KsTQm{V9y1A#{ltJ zev+9oEMk~hU^)|iUQ&+ka;GbQHDw(oF>U1Hk`aKQFEW*wnwa>YrsWTIh()1123tD2 zyM9&Gx{$v61Lh=0p+N)8j=kH{SzGYgN0t=PA(p@GwR9h2&9*D-Lv4@&f>zex z!_PnE)THTsXZyH6eK{|>0K{8oe_1C>nOk^<9pOD^7ANr=HqEawx4&5%cHbn4$~v|c zqc0>ciCh~-_=bFmev!J|hK%lK=DXko-gN{eRkKpl!Jsc$$41)7y}v6oAx*S8-P+L* zoH=j9{EyU&fgg%BYm4sZ64kUuk(~Xm3#$1##|{lXRKqD^=v;--Yg+q*Y_-Ey+d@Ovk`#F8<4V*s!NLuUu;q3NW<%YAdo|EIIB42mP#y1l^(5L|-~jcMEOdKbGcMlDJiL< zlA=F8B!nm6IeEL>w;kZ8ntOf!b@@mM*AjwX z9Mv}`NUZNm)NsbnYPVEC&h($G#ve;`x%L-I77i*>KSJsIP;z_uS_5pI$<@jiA;qmC z6>ZKJ{wVwcAX|Hys}cfC_wmK$-Owb)#N;`5E_%r!5g;j428r4tuY7G9`)&Y|d_9H3 zF(!BOv#f&`-q@fUwoCL1ezG=N0dwH58>*%0%q#G-k9;KRmi=`2m5e*aD_;=oqs~a$ zcQdn~;vt#ShP=iiG_ZDt*k6JJA991KusuUa^A{|;8ht6fzlg%wyqC){XA5JUsaAYM*>tE(29g zXvrS>wb}e72h0+|VSJ3|W4-}}FnadAkrnHFkPct4x#EW?^y(!lwsD7*1;t-5nbm%6 zW=u>PxHIri|B~_&XWE<;ivKi%)8;Np3|IZdClY4>J%)5!h-`89gYi(x!fKPu zYgt$q1M}MZA&OoGQIqj&a>N#cXckraEl~z}KVw+tnyQyRMloGvmmpY5*S|Um?VjVx zDL|sxvrgMR(+A5%SAGeXmTq+5du#@-k!o_oTq4eC!|3};y3HxiKcwyIG7?2^^@jNU zi)9!IHNoTa2fnwOTbB3CQySks^T3WXmOc`Af(IHP^mP(`V=+#*nCjvRd+g?OXozA| zl8MM?&zzi(^$vOZpE&ew`i1F;ru#A4>*qu1N(xzN6Mvo9 z&Ee8Dd7laeW#s4x=#d_*g}`VqX>%1d#{b10kM_PQ4lo=aOFB3ykDlr(9c1X5W|L!9 zR(Tb)TsNAY4iBENf!Hy{n}s`>3qdJ~;%B^4VAk2lgh(Vc{ubwc*o`b@5nu)LoewP8 zIM9(?kf!K}ba)~&Lec8l9MK1DG-C0q$8Qo8r>U6~X?tLyuVJ30?8-u;L~I&PK%6TU z7=*Oc8SK4{5an84l%``^O_`!QFL-ds+_%V-*zQMowsLFo!z{8ZoW?))ht=TCri@;0 z=ySY{E6GUHB~)r!>cMdxUR!=XWpGGv<|`8&!yh>Jf37J1E^~)ZmTr)TjjX1dR(&|| zu;x!V74Jm*=vQe_Sim-}W+!!sr%h@-gZIxnZoH#YBd;!qdZNC#>+DHjm52yAirLZ4 zqc^cg>wb58Ay-g7E%DYx!#p{8wi;a5vsYhq+q=?t=wOaQ7M05@y6Sys7=xX=RpST| ziX+|*wQFgw4$LiuUHQ>U>VT^{+|#JrV`8-6MpzZmrP_rtMHs@&Y&39v&9W2`AC zDJ&Ts|3*uV>5wdc*J9SFt<#%djZ?&&PdBknkl~i*3NZz45ieq%9Xrz5ncs|#$*aX3 zM(bKmHgJ92Z>>`hVoUJeVw;-ACp-|=E%~d<9~rzK=_E>>G~5X43_s80Uhd4O>F~T5 zTf=p2RuG%zF{qfkm-~yL${d;v zsm||Mov*xbL1MW}SqOeyQCTVV92nna^oQ??{-5}~jiJ1Pps2DuzpebV4Cm?efA?&t zObQP6DBpeqDRTW6X$Hu_^)%nr+(g|?7<_vFpN{eBY)^fa$jHbNKj-{>P_JRYW*;y-eSK=B3^JTs2t)lZLY~*3Y8^_-#NF9fp*X6XiZxkU zTB`MsVDc0ygW$*Kuu03Ehpc6v7gv^g8>@p2Ms@XgQ2VPc1Hr zxR_HZMt$1Z-3v7+@ynLqkKi*m~GO>=rqE1>ii z#pGiNqSpnw$RXqCC^O5qq@g-`5|x$gT$&YvjtDNr#1MNIm;A;uf2sTPU&>Rx4>JsM z!>#}N0&uq5pc@{s0swOB>w_VV2dM;~?OkGlI_+Ff--6pH;LhURow$py=V=XHEKZG{ zY~CpMZ_uVwW7?P>|Ge??!x15QXXq3W9Pwjh%a8PG{?z#wB*Uh zkV5p@(~-twzXf9j*E9c3s^d~8XiaUEY(dfTvNE(_TS(nn!3-jzR6mFaNcd3Bcq7pT z{(|_K8jDddU+X>oaCHw%X^2aXEu-Sr^!Q6-WqfVkjrH=y2=}!aVg-fd$P#q!jk`1# zJz?I8_iBU$JjERDY!hcH^lvGXhe4-ZGf#5C0n@d)Iu(H_e6>lnIlp@k2TI!TUNSYH0; zdj`oTac=0QWk_mH^|(SBN#^?d-cu(LG;`-Om&!eFsHCm`8O)CPu<`^;!qZo6`Eo+O z)?$*8iVw8KEy@j*dIq@2pb- zT>W88Z4VQ|XgzK8?D=>sr8H=LlKo{g%$oarBTjiV+$U9h0)D|gkDbS0BFz@0whh}Qdf_us{8C6ehc00>F$ScZOU?xS@v^>SZ?M+_~w!ehSVY;0V4`hcHu+j(qEW*lJ(L)SZ%m=zf z&BdenZ_(3^BKD2(|11mVsu#YF3CWt51LzWoso3 zw2H+x3pE_7VX|saS&cjc=f-KmkfjBpOOy}?%0cZSC##S6%$QZj#3E~inwnndGNZL> ziO%4K1692Yb0Wj`^8~#VNQfP%iohGoEXTah8_N?t&kIVSHb>pt} zZ&jQp>~I>aEuf;JTIZ_3!^78guuo4<9Qi|5HJrRe9;05a7h6Q^Bgu5tGeC<|p_+$! zY+4gMlMopR{8DN$oqjT%ff;Bk-NfZCU>E?BW2@;J;aQDLx>gI<=`RYuqR6{rhjy(I zl#F084ocX4&=&ksUJ=P)2&idJ>{TT%-n82>$KJ+GkhfAH%o4aTcW@6k`mEWk5E8+) zhqk$|q}*!Oa&kl)?nuc|ker%F^PGk3tCQol>k_YMx(P(UR-#9%~l(?tp57XGn$-Ss` zrRebV&z?f4(B_>g1mhKNMp> zzu$z`H5}`N)#jZv=iB}69SeWnCe<}cMR-K37)m*~9y1t^@dGmlQNF04Bmd{cum5E7 zz_R7Pk41-I8)iWqTX6RFhsGhLipu1R{tBic;Ra=ie5q3b74^iA?DZ%le4e49L)P-o ztS2V`FxdU)qK&zwW$4|Vi;Ju4gs3-u2(l}uin{*5V7eJgSBzCm!SKiC{-nCY)mbZo zAEgHbI)4gySy*QlpPEFF_Y!1|2#yZ%GtK5(NBW09N`g-oU3#01g973(&7V`P?Ci`$ zEw&ykNdoNf8G*+EZq4v)U`8Jjz|{ITC0lFeWonaCg#deq4RwJ>afywQi+V}}APq7I zir`swbMu0fv<#KMDO;)@Re9>fpAmgg)#*ed z;Ws|vv-?h)5^kTdR_jZ?xzCiDAElRQ)~$c+18hO^74xf^!kxf_!;EI%A%(|+pE+;z zAE>dapX*{>q?IIuZlUQdrYD>_S8f>6j?}*V3#Y$8W_Dlhax1WqB;XORGMMu%__@)1 zqhgQoa=kfH7fK&0YLp71?k?i`HS#x6_c@PeZ7*92!9hm*W~`#x*R40oAtfcn{Vt2r zIP39JW^-M(3U$QD>HSw*n|`9jfVS4M5EA+D@*Ul_>2{eNvE_6~izne`HC3Ws`c?~nwuZ0%E-#U_j zVV*D)@8^rg6v)T~BJ!s0k}d?NOsQJnwb1L%$GuP~cTe@sl#V-6{sL}dEJGfRXLEQtpfEs_ivF{-b_w-i748 z`%8rrl7;I9TK_{%OiG^*@evBD1W)n&u7p*dZbS`(bmqEjk6_WCS5=ID`Jw)hhex0n>=W6s&F*J)9~!)flH-xMDEx9|9(0;QMZDfBCu#h_Cu*)Y z@l%b3frXDpM9d{6`c`~OmtyLYC+m;1v!8V+m>^8=THr0%iEO{?tslzvc8noiFocb< zl8vz&8F}9Ro%(#kaK+ETDJdhv+xD@-IB_mm<7QbZ@o<(_Csj9D`>hOC;&Rmi@|r}7 z&&2J1zJ0vl9>~)Eonq~gjZgl7Z8Ir4G`9g8_`%x;GCyHHp#5jF?LP5h%@g9)xyH5h z+nZYe(SIkm42p++Wq15Xbq{H966!WeP6>FbHAVfg^kmeA_k}7(nj*H;f${^l+eNrx znqe+7aqo@xBQxvgbmA7hfSbEdam`xKTR+<3#L-NL;dng8FX69-0A*&3Sw4{ShUqLy;K^x4ds|F+w_^@ zR4O_NJ^SxB6K>{nY_ocb;(CIL`*1-HDtY}>vA>o&myRe9H+?7y3#S{5zFm_&S~U?v zodq5RT6#3Bg+G|GnTQkbC743M99ZOgob3!P-^=QPq@T4qiOq5sG5}R2qKVlU9d$)* zXMx>UkK2_sW!}eKGc1Yxsj60aeG{0vZ5KB)Dt`Km$T0;BF*3aB7c8@QX$p*?!odU> zz3A<9UoBzvj?`~3E51={8+}8(O#>JCmni$fRam!L{E|g2xVv?n6tIH%?laO-!MH<= zXr>>kICMw9krpyl@@}U7wZ%()?p2(?!u{&(>mTwvO_4y<%WQKbNopQXXxi6WnL4IX&v#u2t0CZCuQ8M4#nNW`8-x>`?D{An+`toMrPc zr=_1@6ll0CF6fC`Ykr$$JqBAJ@Pq)nNDEX z_(4ZhT2)p@fyIc7a#p71gkh}HO%m+N+tYuF!}`2@H?oBuS*^;ga{@dSJ?n+fgk*Ni zCE}9j!)M#2{sc^1f+i@*J41@5M3DPsw!FoLm`i^H?Zd#iW}@e?Z0>swvg);2d;&$ zmx{-%q%bIWj<_s*m|w4lc`z${iA#r1il;H%z+yEszq%l}-p^gpApQ69%I?Jr*{a$I z!D|5d0Sc8%oyKjtm2E5>db_oALasNaSjH_!eYBIkH?W&}Y(l$J&=qfS#=FH?{l7g+ zhxBZ$hds7|sMYN@)i}GThM|(j=P^~AwDBlv?c(W5lfVbdX5;}bu)1Iz5a&hrk`DXd zX-AR<`Ds?XCUccmd28dstJqbTJtIuIw8?~#ku5MG@|M$TDo-Yk>NFr_YW@J&Oj7RK z@&=`NCF4j0vr-``rY^y&pF!j-8%L#(%3($FC%(O)xkfa!iDVzQH1B7e7gj~U&$N^j zbEDseW1v;Lq<_0j2_+K|6SH#h(b??O;65$?f*eq#@e>pz3qSGgO~{7pGsJ^=ot<1V z_cD6iJBMocallMH%6It6Fz^0t!pE*4aW}(>WGyKx_B~JLxoN4-`yr!^Ib!yQ>36r} zu*=I4g9CurN1h?6O&ug2hwBvi^R=H;AncY||1P6W20PKxzMJv=cSCcIV(zLT%g!%+ zF1rBmfg8#>(lcg?L7|yuX`f|Hkq=AQ%fXCO^L_9;TEXh7L@9UcJ(z?e7eA!OPhuuP zl{s8X)yL)h!k#3ph-5c?(x&gQs&tnP?@oM`7SILHV}k>H+xFaUtHEDbo&C)o>a}2t z`#5#n%X=D9YMS3(+wIHu`YimOvLvCeU7g(LP4(tuh-Wob5sg22d~2EDsp6hl;ysvk zhpyTwBZOE{`ctiU@#(uDYxZK9PPDz&Sn{a3%Y0I@j@H0YRdo}`SohHVdO zdELG8v?sj_Gq#}36c7bJ!E3F}gz-q|aj~nlpVc9cIjze` zV)0msQ^M5|i^hEq8ib)Qw1;$7ZDVOia&=5h-8AhG$8PTtoL%LmU=PJ)D~lBW6^jUF zyXt0^iMdSJF?niU-r2IxL|V+pySDmXn!bPu5TD8xxy7c=9NZ5xlR@l3*cmyCD>x; zeBpPXyL?f3HB7v<<;fTH60XGc_0^m(FO7+O)QIixb1LvahhS8%SW(&37DVN>p+NTDWQ`^fkVMCXH> zW9^X&Nvtl_mvId`Jk+jt9+u96EnOU3f)oWz)5W`i1B%N6{(u>;TB;fgqUZ>mb&zuc z(vQ9RS3fg3kAb3qN+W0Ep9svb6rF1&&A!XDs?(vCjO}WhCvi#aGxYjTZTcQ_m!`f& zNc*apc2W)3RC5fxT)~-NE@mpWX4nBj;a8zPw|La4X>85@;5<2@A(tNJ?_@MW$IwN^rDwcZ$JcsUXOgPiat!lo@%xX07!8LF`sC}E#2+K*NV3$3SM5ZvG>I~Mj}|Cor+X0nm|e)-3Cp$;Ak)} zC8+56e%B|bqzJ7!qjY!=b7Njkfz0Ev`2DS$8!W~#1shvi6?JV9Y2`1pLo#LM#TtVt*)Pv>7&h2MftjvNJiA3Gk_JACAyJ82Mo zxb1H*dbamay04qHw6Vy#j)e?9#5jk3=IlEs>pZ044`!A#l4$M zB^exBzPIkv1+FJ<<>=4#wjr{cnpLyaw$`|m8Fi)kN`Z)-R9cl*7g3%@x> zAa$8(Hat}G6WWY(VxC>%O;8&&Ra&>PW=S?Z1M!ACVHVK3sB8EhaA^Tvp3$&h~Tz` zK`9#j&>Q4`#%Qpx7kY6`PRbjwG)K&_CpOwlWbSc%dFPxDcuj6f#q&~2ooNQhMU&+h zeIDr&QjAMX9F?|PX&EuS=PBUAk-qjGw4Hx$Yzmrcqv*|Z@zWB<6=+*^XgicN7UNc@ z?OJI%AMbfK+{82l*;!`>4t|lXiF-_Xe{@|zkk9RC3Qiu>y8??zS7dIySVq3#1Q;nfBOUUdb-++(fU_ zGXm;}W7(#LLXpWU?GoE&(aBm#E^bey@{(Lx*;S*n1_X&c1@p5l68(+{iCY9ag^QDK z_2Y_!sh<{^$X?pqosa27!7a(3E2s~OskwcxeIL51{gz?{u1bOzg{M^UFeM7n`h(CL z9=8hMVY}g>IVUch-A6GeSakNdtC2Q~y}Vb|QX@)7h|U@%*qR%~MQW*C&sH}#p?!5U zdO=d2IIhAi0na_U0ym#X`DUU&8?F>oSwseGLvsaNBYcuEoo$Vl2pw&>FZ}NsiK+Qg z0nQBw3v>9vEVcge>KXw@)Xv98B(TX1G3IXwWnT`vqJ`M-wi-u!OiOe~3}apo{&Nu_ zrLs$S?Z5X;$pk5ccR{!3IL$D2xp4QJu;2Ukz<2Y!%TE5-Lj~w(7iiPhRfZpd7dq$U zUsz%<75EE-c}+wE4z+8QiQG}oYc(RQ%e@{!%y?*I$ zLBZ7C#A86LiuDpoh7M3N7C3|*qmX=3p5hWWUQw(0oWvtkRmC?q{0gvBU11YRGA8Baj5G zLrz@wo!(W@GPcDin%297!Pw8&PA+gGmzSSRUCv>aGlL5ld|Szet2%tf#I?y{cqwP{ z2GC+B`$6*|@quoLm-Am;sy-W}=${-*xgTvwF8{&m5<_y)B;`ibfo<8jLY z<~9g%#hWl(B{Xe#2aWBx9}nXC(^MAwa`B(3(%>TO+e~H7(CMa$aYtOGJ~;`|TQvP9 zzeW#OjQwq;KVZ08jTXymQJ6F)Nw$Pf`MClq%iR9Sf^Jv1-QzT7RP~+%i;H5iu%iK1O~);OVX<%DlrW(!1Tx}(RO7QTSgnIBy33P&BEO68hClNrA%i` z#_x$Z%b~lksxWmJF;hVWW07v4lqoDnR;b()L>BrP=0c zMQ+D!)Q&!Cj^M#xELU_bi)e?8%uF@y`6~P07LoZW9zK4Uuk2&h?_r#fmapvMNDV2q zW}%guje9k3D26?15ENGdxrjvAjxE>h?fCX<>$Y4z|Hw{1l`=o34d zcs;$rMNf&5msG~n=A7Od#V1O+{EjmpgNxn`p2NQ@U(S2Qc?C?%8~hh85JbCk4-Dp&iFTQ<=0u;=gi9nj(mMnOj0wA5 z*L^^q(s@Od${4czECA+M+`|x0<&SDQ>>(js=1-+zmq_G;%L!?&WQ#()Ii(aaC(SAj zn3+=#45{%o4&#O?Za6zi*9&YZq^LCbTt-<%J0lamQeWMW(Q~3c9zRtR3R;-`4lRsU zTM)#&zep3~%alMPm>&$gSX+}vVr8xVhGP+Srx|(qJ)kjUt(N(7N{Lecd`F<*v;e8W zW`x~|H$Ol*wf6W209MacbG}bn&mx4cLG4g8vG=;9sYwRK^%Q0$=PjZN4jvU$7C6Jp zRbbp*E|lcm}8_j3xWYpZICVRF}d zf5#O^aePjok$@;T*)XH9vNZCWu+j_o<4_n%j;TpPPV!ee_h$bXNnk5L5*Yd`Co*77UenewLELhA6d$M>?LnSz8=}vn(M!K9$UXw5QV3% z*B5Og{@Z|6h^eSp2;#70k0>B(Gu92by-U?I7*CJHIgnmT&&rl!#P`WZ)1Ecu4o zdRmQ{P-h2^*6pJl)itjDBboq&qUyNmcT%6K88s1f<`vWNR9=HU2)^B&A9o&UO(i0fnIY~vGX@TN-Xe1e35A|UgDNI}B0W!m7 z#@>Lm_C(30A0IDKCyWLz345F|g?v;-f0Xf*X;3|-9;E2?)Plw351i!+EkmXA`_*-Tebb4xuVQmTn{;LLey2@c9esd8J>iYFDDCHHvI_C0=# zSfaaE^P~Q$REkN%6P4D20k&Nq1v*g*4?_jdhAGDOK!=VO8&7X^)^N(Wcu{m2S973@ zR|~8ud9KGF#M>UFgYD1(#}9m;Kz?g?RFXJA!j$>q!3b&&fiOQaXiv~9g=7c@VTSm? z1A)tN-$#hMipkpaFa@OXGSIE$s3($S0x^Q_%lwDQ z(3pK|=si;ij?i=5h(IbMMoOj4M!7BJF=iPFeSQQ}>_~W+WJPVj|E#`|TIZg|&)4UI z7pnN>k!N<%i3usLbO&BWSq>eHdns^PI;?vN^z%6cMQ1E%ursF1#-jc#2?lo23>#z4 z*MFP*!#~oM9acS#14qSWzYtN2)3;56BemR1g)@fCOE!+?up-1GVJMc}IM7{mm}%ze zo({oq>oxZV<-)!Q@CFp@sYpHtSw@C^;bV)RiN)bqPWvVdBBc}5TuIAd09fH2m036& zOSMY#EM#K&fUs=Im@?kNU`gjnlGkc-4GF$*4o44`(Qy=bCqf=W$cSIci%Si0pf1K!=;;pk^%9JMw)HR}M4 zV|*vYm{F6QKYn1LCgWL&~>VTtnX%wEY2ySE?&-i@@6mp)xVP(s9l1 zmDUxly*_MuO;S>`>q0wGu1NHd$v{O}1$!zl4_}uRW=8nK%grNFI=`A*i;vjz6=^G9 zmpPXSR>t+@5X5!hIXRN*lcE93Y`R>^LEt@VwDtj0ZdE&O+-yaGSsg!fU|8rnZJ@sl z?>^7YpL#hb11O=cp;0R)jd-6VDYGmFaxT&mMMb5>271iz3OuOMJm^;ZM8kBlC;_&! z@fvd&pLb;JJvebf)G|oSSP>n**KAbjR)>1=^xb-(XKinUiWSyKcW0l)6{LgDNs7$4 z5DaO2cm;%-q{S|Kc7=WnA8GU{Nl_Q36VmPCxW90nR5xRTYTi?0GaVyq2(DY)?gNt@ zO4`{GNKQzIK-N)A-d6isVcQP~&%5n*>-T~-a1!RL{4;FoVH=J6 zHB2FmEn%U4bJiSOAt!_^`na70_0$2fcPaQ!z`D}e?1#HR=*dqEqD6FDInAxfu`gIF zD-d1lA1CKl-x%f?R5OvEH4?#Mi_PNk>n7{Ck349oHl#BdITg1aqurJ8qOCNg0Cf@` zR0-__jy_H@ls?0Ofe_rdI|bZRh1_NjGOn&rq)FWTpA{Q^(2GpRzId&OB)+2)X8!7o?U%vTN! z!&_MHn)RPl?v~WNDm1++o)n66#ejj|9_4TM%{+No5|4erAD(EB3u?;? z5=2@j@OTZ>G~*1W`VYel6`l?4`7$k;1)+@r0ni#68Z-=pxivZdMtKSl@F8{gd_6hwNBzWG6k;Z>pa5FwlcTrdI@N2@j<|^7kflY$_rR1G~+TG|><)pHP?EFIs z8ZB9C=zg2=y2bFVr_NKFtc-#JX<@DQYMlIPz_Isc{_AgA6H{xu(3}%2RW`>1G+<6B zb-vv?uE7$v!et>_gUU+ukzD6?%ku#eXdQ2;R44dGt@bnwS1{Q;I1$>Q#3lZUzCEq>@kh9qf_Z@5 zAc`3s@8XqeRx!zZ*gW4Baew|2lx@Wk86AW75;)vW3rEj(aSbA?tcl(paib7s{!6xG zrRC?gDR}d%CVIaE3)JriR3$C$kcWx&&yk$erwvX(d-g@-fZ_4AbJY8Ng~b#@qDyo@ z(6i!a&nFW8=yyN)2Sa=!=Q9!nUxNrC{d`3T`*TOKEH8=KCb%k(Ab4$RPx)b<#arGP z41L{2?_#@Oa!9macG|WBcE+T(@b$qv)BkYqAAExro2oIByBIA0u>{VlGCcS9^6yN~ z#}(P^X#S3uRfQ`!jlL9iS#p7)qV$c6p8LyK)LtN({qwl@l1{2sQDts%wE=`tf{;(o zMg=#Vj0%wpWq^>XH?9hb4V}FxS|Z+7fu|^3=JkuJ!R|}cGRf6-LNfNcmt|i2-OH|> zC_S(2=0!D0MGbOyZDS55LCuLSRtbZKheF?%*x^Mal>vH8Mt7A!Nu1mSF?(S-$d&UP zDb=iWd)4`$v)V(>DoiC@@{^zceFW-Ei#_ms^;w{_eOnW^C{w_ZR>X zMS%roL`6iftvxkr>8-U4ySwg?y~%qbZe0)@G>1>lLKf=9uhbxP!aqt`bAK3wN4%pw zgW{FYhXgn^t_VstA26c{+nFH$-(6&(6v-ZSA$Zh3Cb1Oq#ZK=39zg0Zi6-)q=Pykw zT@i(49~JQRDX#TLBqraFi4Wxmv os2D@Df3YtW2<7+R6GFYC8km~;$an)g{{#8RNGOU|h#L9-AGc;bIsgCw literal 0 HcmV?d00001 diff --git a/img/fallforia/blogs/2023-09-27/blog-image-1-8.png b/img/fallforia/blogs/2023-09-27/blog-image-1-8.png new file mode 100644 index 0000000000000000000000000000000000000000..2ffa00d316f0056bd941be9be678997a9aac99b5 GIT binary patch literal 159949 zcmce7bzD?m*RO$uw1{-$P}1Es64Ig~(hbrKHKgPa(lIpBA}KwjfaJhX(j~&o(CCmu zUVMJfz3=nB_x^p?XXebDU1#rocC59&-yN;1txEim_Tim7cZk*1fG_Xd!O^^P=Uy>B z?rjKd{}d`%#{w0LlPPvE9z2)c8JW%}>;E>2EE z?wva~CF(%MSH7mZxeo$f8_YCcAleVa-4-1-y-58?lS~8S zQD-x-a(~joyYRAc9)LBiiu343(`|%u?~MMBmLS-SsQza8<+BeR@E;g%c8qwQ{AJ%& z)Gi9}f0Z9s#ozkDLZa=Y6uuMIJS<+(7V(-@T6zn&T5Soay6i^H>-Tk!m^rqmxUMOI7ddMd`b8)*sLz8&e{ss&uE z$}#bzFUlnon)ozD-0nZ#e=b_gVTy%9^KUai6-|oQ&HXA4B0O5QeRjiV8Or}rZ8R)y z(RUn&;%|$VH2)U;za>`SU-?J=@4y%0S07(~7ZR?oDGGevZQWB!l-VGu4{#kyRYw3^ z5K*nU|JB3uFsm{%fj`taR$tFB1$SXALoRQ0Hd<2}72es}B4>=5 zWM6mVxPLcMv)SD45VG%Mq}g@)o^{1tXg+ZwNuFKH<)k;0R-)wzF4eRIOCH>`Jo7&@Em8&Lcl=}!OP&9tW%{ZdoT zf_Q~%B7CtQK6O83!_ZFN4czr<`~34F<5b~~h)hG6$gHZ)bAfJx4dL%v94<+!+1g2T z6$yz(j`7K}Qllv@hsj=7Lot9AJt6`=2?M(~?bID|CMwm-HOQEzmE94#y^!ZdK77&UV7alc>}+-7M#!jkSV=#3ASEd1}_ zyOkHsQ`{MstrNDea*fcX8L#Z61<|$ekIt^%7nhaSrJo(rsV?4(5_@P3RC(y5abTsr zZ?Ng0YSxH5g@SyJbR6QX&es<0JxKjh%c?P~m(MrUkdJk4i%&19(u49v_-qXPvVzQUo% zDNnfIh1$K-f}RLB4gH?Ju-W|5F=@X905=R-JIG1CDRq+kbL95P?W6RVR`L^Inkkhd#y5adM+H;|y#i?A(y0QI-OVg2YBDcO}eW8n;x9y1ZCVx}*(;A>V3-^GV?y6En|n=G00tZAG!T0=fV>GtSwlFOV`o64DMW8j#kgxsYVQV zk@c_Pn&u-d8yXD~>(mPjlM!XsOJ}!*pEE#gyxyM>&@A$KT`bKz8`+W)-pI~(O}zoZgQxB5)Bj}euj-wiq85Hji;8di zR0w+zKD985kKv#SXyQ9Rv9+~Z)K(O*=uAdYG!GIw+rDVqGO~GZ++>tanK!_$EVtLC zWa_2OM=~3YPb%@sYNiYh-?M|u%WrZRyrShacty{7Q!b?WPA3lP;9ejyN5b)_On5Ts z`;ZlQLn{3+eW^?EbjR)dOe=5Rww0%s;|dC=7%h8oiscfLy>|PtjuUtFwr0TF0gHS# z)edy`oqw|v?G=J-)nS(JgXdz`&urf1eG|J}_*C>e7|_0{;){elX8fJ47N z=toq|!^=BrC(%_&SVf8&E7>}+GW#Az>d!x66GV41?V0=Fl!wn1=+{~eG<{JWYwOh8 zRFbA~c<^k&uZwsG1wai;FH;RE$8ik(Oq+N>vD&Y1Dcrc)u@n?9vugV$y8Q_>Zao5; zrdE`CEXP#P@M1sgYxPZ?2_{+EHZ6q5=1q@9vrcJ%oOPq|@gD`)QPEwGX^yZBOw9hl zKv?nlxTp}DTpN&iB1cGsMOHPM$!irYY>p9QZ-63u=ovQLSB{1L@>oZKd2)kjO z2ccJ>i(-=TszXKfjp-gWR{cr~6DB2yk)Q5$s3YxBA8&uV5uD=fX3`LNDV0BKRTBGP z>Zo5xb{`iDgQNR%Ay0XQ&4OlqV~u}L(84}p{IEU)zJ>?s37#a&5yzHF}__7gq$-P?%q^&&cy6Q~Y34NjoA9-e9?V3|* z9$Iv0**1?+??UdMDth%_N*&C}&N#&c2>KO`%=xwWnmCAwifL?1;BA*~vn|O93VAHw zrD+LhJ~C@Hf&hFW$!#KJnXvq&xTke2IbvC7Xw+Svv6g+YPAr0THr*&(~k>`yTw-34Mxg*??G) zm{Ba`A$OIfoW@$s83WH#yP15B&hKZDfURV2P#LLKEj%eYDS-jGx_RT6kB_j2UL{_; z*YB^F{(;sdy0V7%Z((!|RjHt&sT`A}DCnL81xCxX;{y=(w#@nGObS)7 z3`XZFQI*%gx62&7#nx%;XS~dzTdiQCA{?(gKc61hK-y~4DJycbUz0vo#o-+M%*?yV z!juW8_tc+n#=_AF4kHIY`-piIBE$kvhBUed=>cXQz*XAxeCT`2y8ST1)+N&7OMaN% zpuRntiPd=ei$oNuA{r(pyevra9!QjAH5(u`mz%QF7>~!NlX4e6xrA-e*kR{v*F9~Z z7$7J$X_qtZoHfbJa}-wY(*f3Uywl`0rE-@4XA7^dcio4fMGzLD`Y~)*LtGg?p70`A zJ4?pxf&~qn$0ny3a5{Qh!7hSizmIH|*vOlZKgEQb^Z)r31P$*LDmhdV0+3>#!MuIB;T zLDYzfQo05=xmNyVj(OOFNk?puI(!gH$z_P4!^s#^+Pe%ih}UIQI-!7aV33Czl30eE zQ>1h?N$^JcAE-V1cI>5obLYtJz#sK>rDg;)CD<`&$V@_3xZaZRG&6-<)0Z%w`K8Yf zzbl`ObIC;za;-n5c;>g)_J`-y2_meoz7U%}6@Nun3Mw2~N#B`#`u^$Bn?3T;fk(NJdD_yfc_;~Q z#-nWBJv;Wn6ofCZm-zk38dx`Egx# zc<5t>hv9d07pJFlz4`~e(jdca%T&V(&Aiy%4mSu11#9k>5vgM)_pu3}rp?pso4c*q zj)V?3sc{JwT(Ni$+MGD|VcmZU&9?Cb3dLQwG-gz?RIjpdG`UuRJ2x(4z6!nrU}jet z4y*VGH{@SO4lBn2wth#KuWjbptH2g?Rsjj(+1xkRPPOgJ&IKp}4tn%e2V-obOoCYM zGX^_n1HCV+$tD)aTI$qq;Gd@JP=pV3<}SONIZ5j;#&`ry2A9l8g4qKX z&by`~;S7xfFAk%>W~l-bzf@mkXrTA~tV)K?wEB(_atCce3g0J}%|52h=cRu5&@ zFNqm7lA7s*qh@$|)#vd3gj^ahGz=r1OD->_x-lwtvrnpT+$w*0d&8@*trJhWsEiF6 zUFCScm@A2>&ccQ;l{>CCVPgKRNKY`VH%Hgc#UH0c2d^@f&ctZ4tD?n6>d&}b8(GQXx~PB1E?uKmy+>WMLyK6bx)95N8FiZY7hUQ!9%C+lvKyx7@Jb6~Jfn1e zg&2cGvX<57p zM_R80f70>s^C!BzY$`5BtJeuf_8QNKiO{2<#NrNFi%Vb0f+hBw0%4P$-dUF|rXSno zWS(1|ZoFXBV|o6Z?QS%9lGw5BZhH6?ENcL0;Myj9i2T3`Mky52}yR#_gP*|KrAFl5;sg1SBrN)|%kj&TesP04~ z>6W|l8B!n9Y6G($96H&e&cBxfGunUAOGU1b=?O^w1g@p7zalT6a9b*+h!N=iL$^VH z)UC9O zx?b-IJK9kWY;^tQ!pQZ9U));-YQ>!HAxGJ#rZ?W1*~Rxrdr9-$5YB8+h)*l;j{^KC zfbhK@9W{aZ1NuXEk4z)8A<68K^-wjvf}H>~z0Pa*GSu%z52L+_EUNIBi|vG23j*gd z=d|~TtvxaR=R4aGP4ycySw*kyE$}D9;s8To#2hbHpGxNzb@oofCCX(3I%0HEPp9KS z4Scxvj}d%T%{yO+reH!*@!bR}znSCutBKwA>Ec$4$wet!pXuH$nrIo_-ejHpL0zZv zpa`-oH$tOyEw|HUEY%|PfMNI3ngZL4B@>^@8Ky(T54uLZ!>|v>uiy@tmlyMsh!VYS z$yHL39A*Yv7X!VmZd{7j?Vsm-GCebMIemW6@Yb{lv2^P7C;0KFL9#VpLYUTm-q!5c zSN2JtdRB>e21}2qjh{1{M2^1pM!!&dCNr6r zGcXG1AFi*mdqFpgB1|THWenV15BP+LK2fA+e-Xt=y`2@H7gNw-D={bLW4%6^5a?jNRWQ=_GdfiPUF?wAOPef`p#bORMhh zz&eb_EJDpg-zM+o9O!WD>T(49&bm*_)qOs5c)Bf+&T+x<1z%5>voBQrBEmoO5paQT zu7aX_WyU$1y#)Q9{RQN?KXOlxYtjk92o8K($7rYjE}mpZ(br-X3IV_wT$sL-hvR*Z zdB~WtFUj6Q)fmR5KbIgnLyHqhNMgux%wUQ)S- zGsG7ge3WVK|HpL}vcizYjdyEdnfb^>vP|c-YOuM!8(XCKQp3F?>K9S-+uXHnuX&F& z4YqQ$yg*gfFO(27sDD4Xfn>fcuEL5txm6{nVq-Wx#f%+{c=FU%y>Uny`Ni8kt1GuU z5akL_HZacZL~e6;Hx8X#fm^Rbhj(5y)nv|d;R7UO&7@RM%b!e%vRZpMZE8uxlOZPY zZnXlsT(skNC5L#2;;&4I6TFTm@U%2shX#BS#tSz3pLf=M1()s(+&o9)%gs1_Idb2b zW3qePJJh*$NY0mABIM&=Ew$M01b5} z#SfnXfH@x%hQ^OCFM`kwKDp4!z0eemRBdTAa21`t{_|1ehx?bc8hGfoMo`8EMv0>!FFXz@uq)CrvWVV@|Nd9(Wgvbbu%r`?SiJfJu!**|axw}9gB>N zvpx1bNOV`eguQVLwqkq&%v{n^J%M5Ll*FBDm}4!2CXREC;gH@4Kg~$$Bza(svdg?#)<9(BTS+@i zdNl#}qtPh%c+{sNRt*6h3OlP@wpXuS6_fMOhwKjz#+ ze_%*86J07EI0_x2xJtkW@S^sUmO_;r_ODqp0wla4B!R4xk(=2!UW>ZGqY zN20}RywY9-rtge1(i`RI&`Z*PKS3n5!zDAc08UAUiyl7=*?Rg2@8~pU0A!8*+Gz$t zZnqA5aM>cO*P{+ARa5uRuYYWIDcL^V_;MIa_J`J|>*r#u-;#_wrr{tSWd z$UMLGTNg%SL4l@GJyIL3$)=_Yz+u6_gNR;RDlOmcXdJVxmli*`Cd3<)7;q@UQbb2zlfu#;Es7^q=BUtwPM4K!in)JDC_Y0+?wFYdXFnQzR;g z3OX*{NpKQY=CB$a51NDL`jf)MEbU3im5J6Y&YpNFwdVMwu1#I@c&RllQv^6uDz;3zzI1mdN=K zFYsX~0Xa>8VA=lBa`INr@mSezYO`wTCQeyn>Fy*}D9V;oOJI$nBcKi%mBO>gKvP(V ztZCOiUCl{fe?v7y2~^e<=OgW%Db)y4Oj6OX&m()bB$ux}$CU|=m<*o2n>$r}(y&~& zquWU@VayCqRwjS4T1CmmPpUq{fBez=))AiBkOl)9`wQX?HeG>}4c>A>o1&*g#KIq> z$cx4jH;>}R2`LI>GG?>LJJw1z1lPt37{muR#iY#1LSEz=sHwX8e2T9c4~aOTz~X z-Qu{tF~0FN;55gBF&( zzm*+)N>%nESIdVUWzEce_fJGKQsRdbn&a8?2IgPk;uwi?U;>e0W@!?AyWwf*Pz3|( z{g1oKWKxJFfR7YM%XLOvYaZsO?|RgBaSZ~yyPu=D5}+SxMF0RY2wdJ@i)ir5OFgG} zwb8A|kx#u?$8;f_$$R*Y`LhUpn>yq|*HvO&IG0Tw>t1YlQZ4OL6G5cRa?hqps$nCu zhG0#=zdL+0A`2mC6O8F5W6W{O0Rd^QUzXtmfJVQe1>CA=eq{)X*@?OGcc#sxHG{J7 z8J);>NEiMCiM^4)kl($!03fs6*oZEPEMj;Z*zt_*JiMmltbttN?6GJMe&E)vXJA@y zV2$KfA0;8GVj1q4rEp+=w`>&LuChZ`PA+9S{5{B2q3|K=E;+$_QDES-;W~o^!Iohsw#UUCGLMXQx)9 z0JuJH>9)IaAfJAogRC-}4w)vkOPGK}_wn;#O;43F$!L!*X-2cGf@*R;PCU@}M1VsD zY@O(B9)JmU9qHT0KAL)Qi;EU7e44I~x7)^POf4DxQAPXpJFqtOE}KpRgY&udp~E`> zkfX|KRy)U$NLhKX;D#)%@WM&r^Dk1b9#_nF_rP$1z?|zXX!bw;)-?WqS;CvMU=oU3 zAmU(EWaU|~L+5kLDGzJsZ41M^)TizU^_!H%NIy-fM1wEp9(q%wv=2hfm4HzX0k4%p z?>u>|c|9-1&ivaZSFu95*MU1ugP7PvY0zzPHnRy{V#3MrWN!*T|Hgfwt|Ms*I}`un!%o^}-X~j&yz{piW32#)~T5aw{VpDBZpkIFM{xn1+p7S$e1D zhMR{T{+N~A1gZ(l-tB$i7LMONK=a-0Wk>H5-Z=Y6wzt8%Dsed9wk$2R=v@I!RdYV& zL2)=N_XY^N??NhP3twHqe-p9FQdfs%+`=S#w=4DiA6tDxs6DL49_q7sk2^KyLs7pj zEeg*z;)M;Z&?D8O`B=?oOFhNRsA{QH*f{ zEK*i;@4S46I9N$Q?aYNlW*ue^_~_>u`S>NSf$ND(@zYj{U#h1Hgy9*%)-@zWEq@M^ zFfIvwS*8Oti=xD=^a0sgp8eiLOodlNJV<*9fJ&BD%rzG3YZ~~ zK!knDE2j^4rS~Wd&QOD@(JXwxikfao7_doZEQaH=qM!xZTRg>D*=(U}8z31I^um)Q z+teKjBK|c(5hsmo7Qlpjv@t6&t8f5}jBoRPs?CRr1ihWfUOka8FBd8t3RW>WPcX|3 zHRvnZGSKJ^T~BQ_Ig8$K8Zz$TEf=_($Wc44B^{*UYJRmm9Gt3?1s0VNm&A7lOd@E6}(T8KzW+_Wi+0 z$qyheN=fa3#-0v6=UZl)j!RkFv)sd5^8b66(mA03izqLML?4T?2|^c(4#Dk|DmKZFG+!j%dT4d?;CYLNb{lBrlWC~Km!PoBI_gEjGvT(8#<#+n*yBnLT$z7LIKN) zvQNB@ouU0BXNhx|>&;hJ=ZJ`Exu9vcdgDoM#2)$_4BoY}wVO50mmu%9o&C);kv^J{5RtjBO18e5)Jq?4*KPy&6}BczRArt#5*#$H(?t?SF=pf*S^G2G?0Q0h z!SZ+==%=I?`1rHQjP5@K~R^J;`i2rbQc@VgD#-T|~a`^^%JYf_py%y(U{z zkRx{<#On781jCAunk3{`GM@>R+JD z9lDKmZUci6{@&kt-H3U#hD?JfvNWMFi=N}whcqR{&KMBCK z&232K=ZSN>in6E0A;KiwZj75$!w~y{@T#MAU1^;k_LPxxz=r^qyB=jEZCHpS&vuMG zIf{ejOY~4G&HG*|kL%-t3XS32CRihiGq)?eeX@GJ z+*t5@X%BO>N(Tk7j+@#G>tB#6uLj(*ApRvtxVB^X(BD0-P?dVJXVy4~>RM($<$f}| zaGCBy!q|<8phYif6tRUGk?|}Sdxn5JPa$qf!^<&f!xHO`596CZA2wr_Gr>{?PJLg? zF>QcsFW-m8GwB2`W*up}?7yJ}NVnwhKo5@`1@V@@Mx&^=7`4gO_*|*VQtFb$DDqf; z3vlfYtiL>N@hUr*DG=Q4y(VI_6nfM78RT{0i_IUK*$Q~})gQ#pQKNQEOQv}}BlO17 zJAzgG#D?b&s;ql9-Q|&N$fQq}56wq*CQ;Vz7b1>KB+^Iv^7Dgk`&EVq}>c zB_O%~9%|P~)bPD3zl&F&O=pER=@%xy%#tMkDd1hirBWaqdm^05%aoc?0@F?Qj zS2f5w#ydxbjJI2cJIIJ0JbBs`)-M%IAC#g4=>6f*(w-=WWi^>RLRw|u+#>qU048P+&)?=4oH?=+Oq1LbkC_A4t(vwawf?`4}N-VR1VthReuF!e~H-StRykeHH)h zE|ruiXMO|Kz?9TG^5NjmdB)kT&apdU#(cve!U_11Q`M|0K(o^LIldOs0UG7&Wj{yc}ZqNv-c~QEr!hS5Exr7 zWy~!>gnu{60;nUFUr7EwwY%oulp-E8RGzr@Iz6BrHW1S9E=yy@bP|_t3l>0!-xLfz z@#+!ZMNHU}h2!9`7K)!aRcnWDrwgj@oaai`iB~Adi;i^0*+!r!9VCQDr$Hanu~fw3 z>Z_D^uLM|LSZXVkYWKeYexEFMnuq4{-`G<=(G(%2+vxEVeCfE&cNIf?@q2#dRHbY< z!b`cQk15+ZU2y+y@*01Xbij+#%#i4Un)#M1a^C%eMw1A_6LfutEc5`{CFX-HAYKw9 z5`UMITB0W;1tItq>PQE$L-NQ)N#c|5QSEwR2?zlVxx&E5le;axHJ4G~mv2>9dUrB6 z(%TbEgU8bZH4cYP@C7!g*5L}!<~ZmL=tAf+YK(}<11`6Qkj*+P?Qcrbpi+k7*hWUpapU-*DM(jRiIHmph5PK+E!uM+@9;=YkVTiIr)q+d-m zU2sRPhaWy^`04(3Y!Um@@4)G6u36+E+zhid`y553I^kM&A#sS{Sgc!dg?lGmK;Kfy z#ft}Bcy=U-Y1<0i?CvB`R5^8f7M0M9=yO&@1+*MbG}*QN^w2z1&BaOo;~GgTNFTx9 zMJ5aSA;>!U5XFT#%NqFIYJZG^T6NnLgDO1(r6#rq(NK{qAzlNectl(GI7QBdxxZDC z*LS(B8wJ1a%R^U_gJ3+&pvMVmI?LpXjf6dQ`L`%eEJ4%r4Zjl`*+t3&E3OQ#$kbkqmPWlsy<@jzRX>xE)hiS#Y#!b#}`(%CA>;tD_?!<&^g&3*CV znfe^&yGM6oW)uUCekj+&Y9$)?1`@pVltXncZVGF_SM|Kcp3@5{GhJ zpF4LUFLeMiF=UlUcsgo_BaK7ES)oLA_t}7I>&$LmWEjIf9QnTy8lTslnx&G z%RLkp{wMO^UQgD40_$tdC+Gf&{`dQ>%`~^X-6G%cimbW!X13XwDy3`A=lmsF{#(5B z7B{pY|4@8dD1xj?=-ipOw;T`;(rqLo*jwi7oFr2ivRP7zgOve z=OO~Y-{kM7bFeyZ|80E!?buv&6XHK>^sh|y$xSQ&k3ieMq|W~)hT*rI)lcUwRhX(Q zI%Fuf%U!Fp|KNoFtMv%SdVGq175iV8dVC`@PsOnZLm1ziJ6kU$OuCcgx=N zbLD9@Ke+ZB8stj+xTG{B@;{PDkMXjVecBBDsEpSqK-BwxKKObYEkA#Dhh4I@5B{G( z0iyNGejJMbN+s|S|F*~eBU{wB|DVyrx2t?#FZTIMU0=_I2N5nNrb*H+zTLBA!|@EU z^v#TiMhwGTsrTm}JSrI;e$ZfWzIJauEs5U{TurX?=tOdJI3vl36ZXKaDRF*(?!sgl z1$_y7Ci`<(g)XQ1oZpR7#gGeBdO^2mEWx1L6lnM$*(XwpWS}6A(;z*ar~Q{BQDFWd ztbx1KY=R`hr8f*h*ccS=70AYb@Y+7mEI{YOjZC@FjIl#L4rno?hR=nnv=3LXN{dda zcl^YSdcJ3&V(UhNqoqhZG?;X!sF8Ajmp2fD8k>H(gOF0Fwjxdl{r547>qqQ!DlWjN?7(zaTida>rzkrvd*Od(25 z-%l4#J=%uJ8I-0T;b~m+nC^^M>hKX#ey-pR6iQ7|QSoecsJquaj;pP>C+e}fYzCF- zyX1h=--BXar3Y_R@5|A2=`(KEn|L$CbdJs2B4#vUjgFz0X_kiYr`zhBbb}i=2xe#%TsujpK01te1)Mc*re?>BiKQ$ zPDYwjeZ>u#O_|RXf&K>KC*3OZD}fL5CVolr8BXPe^k}@8w893J?;w!5G~k7q*BpB? zrPLnG6$*@=Xuk@ue~-p62~H@CAoJzW@tz$n`xXouz+Cek97`c>etZ`^kG#4Pls;R|2{Q>nVHh(uv41I^64O6hTK;R z?mlt5#s$2fFz8x5WhytTPSS6-9+c(_9G>+=Fsc_{D_2mO-E;;$ah6XGVo&LD7+rmH zejIMa!8CYFr#NxQmO~FuyV+)$-UPJeIh4XcXoSn+QU{xC`^HFON(vhoj6BRc_v)3u zkU=9-tOz{YmOE~a*M|IpiA?eSPWYmBz0yzu*JM6d3J$herYpDay<28kt`m@948GFP zuA#6^cPMz@V#m#QD>^L9y#r)^)7+1H>eeE^eV?0jyvw!S@IjQU1==#i{|+M+X!B#I zmmCe*;}yoVLWD{ah}oNA7;1_jSO5{Nu~`IHgOna2f2Du@Yl%DTq%gw6<@7XKQJ=YdS>jK-{L8a>EW zx{KBeJ3NeN!%_M>ykU;Wq{{_uK^Y=Poy~0m-A(cL zcS*VMcbP@ju3R~bacI$wy#S$4O<~nm?WP<7vMeOQn`bE3j;!BTzjqn5<8 z1_z&bna%AJ0%#c@-_^hp43r?C=YgvI)q)*(PB;-I5qJFJ+Q}-9Pg}s!mecF^oHvd>^l!*N7r=v!pf_~Vu4#6- z0QH_u-E(~ITUdd`$5Urq?glf?4$HfQ?AT4Bl>MA9J$tlesW{QSfsbPQ=8NxxCtbQt z|GGOb+Ei<>I!8LmEx*n=u5w%3k1qo;Un+KPS}s8yQT>~7kOn#1e!EC$YIu;i?Yg2* zbw8Zg44~HG&{k6JnBV5_l+q=?7cjWBj%Ln#}pLa*Ow=^-OjNKok zT4p5~SH6yW0Eal>ULh=bxr@x23`LpDoX&n;@dy#}nK4=h*8Q4RtaNN6z_8 zB(J9&Dk?QcXI2&3e!Dgu!&xVe*Mjd4L|4L32v4OTpo-3?sJ)=-`bniZim z-8PIf7TtBvAMpm78#;Jt`6Ug|H?o-UIn(hF+ZL;5?bNiBm&pKC$z{nhyQ~;yDGXgZ z=@ri?jf=4lZx(xlk_JfmtZt}sQ?9pbAFHeL(-_{ZOsFIb!tR~Hw@u+E@YO8Wq(9ZG zcr1SF+b4gviIf`ZF0618vET*{Ca-VkG zN639Is$@Q0iLx~-FdlT7tT1L4_C}JaH1Cb^GUC?R4p_iSt=bz*Y7I^~v?gad_pjqN z-Qy}q4~X1zTgngL&@}CTszw|8Rvvg$>MiM%OF2Mg7R2=qhZ<8XUUtf;@k=>}u(%6CClCg`flC8Dgjw?1i7kGNJ%d}2^aytT>NjLJn zjvTJF0*%+VA?F`D_YX!*np#23sllx4WWHqP#va^nt3h7Bc6_>)D zb9I7qDiU_?ZL!%epV_pl6k|aTL(q+DT<;8$KE_G1^OLBj&l?pH0_bm6_trNWEr#Ct z&_g*H7Lqx$<;7#ulB#Pa{R=&B9aEA~Gi7WSR@OHpJ$^IpadAz=7q}C9y*IZ;i0bZ> zw2=`a8Y$1Hj|{@w#Ro{T5&qN9gUYyv`-hKV#G4p-92R@K Z+Xf?^L*c|XFQkajeCYb5%plni#g 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 z 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 literal 0 HcmV?d00001 diff --git a/img/fallforia/blogs/2023-09-27/blog-image-1-9.png b/img/fallforia/blogs/2023-09-27/blog-image-1-9.png new file mode 100644 index 0000000000000000000000000000000000000000..9924f2436ca5d0a229a59e813e67bb6a86a5a424 GIT binary patch literal 2678 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

    w&_{(=U0rnLB&) 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 literal 0 HcmV?d00001 diff --git a/img/fallforia/blogs/2023-09-27/blog-image-2-2.png b/img/fallforia/blogs/2023-09-27/blog-image-2-2.png new file mode 100644 index 0000000000000000000000000000000000000000..3f657e7fadb8503a2c7f3b229e31b85070a3b0d2 GIT binary patch literal 69516 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 z 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

    Y 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 - - + +

    Build Intelligent Apps On Azure

    Combine the power of AI, cloud-scale data, and cloud-native app development to create highly differentiated digital experiences. Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure.

    Azure Kubernetes Service

    Azure Kubernetes Service makes deploying managed Kubernetes clusters easier by offloading ops overhead to Azure.

    Azure Container Apps

    Azure Container Apps enables you to run microservices and containerized applications on a serverless platform.

    Azure Functions

    Use Azure Functions to Build event-driven serverless solutions with less code and infrastructure maintenance costs.

    Azure Cosmos DB

    Azure Cosmos DB is a fully managed, distributed NoSQL & relational database for modern app development.

    Azure AI Services

    Build cutting-edge, market-ready, responsible apps for your organization with Azure Open AI, Cognitive Search and more.

    GitHub

    Improve developer experience and enhance developer productivity with GitHub tooling like Actions, Copilot and Codespaces.

    - - + + \ No newline at end of file diff --git a/serverless-september/30DaysOfServerless/index.html b/serverless-september/30DaysOfServerless/index.html index 3f46de5268..d1b4c918da 100644 --- a/serverless-september/30DaysOfServerless/index.html +++ b/serverless-september/30DaysOfServerless/index.html @@ -14,14 +14,14 @@ - - + +

    Roadmap for #30Days


    Welcome!

    This is a tentative roadmap for #30DaysOfServerless, a daily content series planned for the upcoming Serverless September project. It's a month-long celebration of Serverless On Azure with a curated journey that takes you from understanding core technologies to developing solutions for end-to-end scenarios - organized into 4 stages:

    • Go Serverless with Azure Functions
    • Deploy Microservices with Azure Container Apps
    • Simplify Integrations with Azure Event Grid & Logic Apps
    • Build End-to-End Solutions using familiar Dev Tools & Languages
    🚨 SEP 08: CHANGE IN PUBLISHING SCHEDULE

    Starting from Week 2 (Sep 8), we'll be publishing blog posts in batches rather than on a daily basis, so you can read a series of related posts together. Don't want to miss updates? Just subscribe to the feed

    Here are some actions you can take in the meantime:


    Sep 1: Kickoff

    Welcome to our September Serverless kickoff!! Our Serverless September officially kicks off on September 1, 2022. However, we'll be sharing a few posts ahead of time, to share more information about the many awesome initiatives we are planning for you.

    Kickoff

    SERVERLESS SEPTEMBER INITIATIVES
    LINKS TO POSTS

    Posts will be published nightly on our main blog page. Once the post is published, we will update the corresponding items in the sections below with direct links. You can subscribe to the blog to get updates delivered directly to your feed reader.


    Azure Functions

    Welcome to the Week 1 of your learning journey into Serverless technologies. Let's talk about Azure Functions - what it is, core features and tools, and best practices for getting started in the programming language of your choice.

    Azure Functions

    WEEK 1 - AZURE FUNCTIONS

    Posts will be linked here once published.


    Azure Container Apps

    Welcome to Week 2. You've learnt how to build event-driven serverless backends using Azure Functions. But how can you orchestrate and scale more complex solutions? The answer lies in microservice architectures and containerized apps. This week we explore Azure Container Apps (ACA) - and learn how the Distributed Application Runtime (Dapr) technology can work alongside ACA to unlock richer capabilities and simplify developer experience.

    Azure Container Apps and Dapr

    WEEK 2 - AZURE CONTAINER APPS & DAPR

    Posts will be linked here once published.

    • Sep 09 - Learn Core Concepts
    • Sep 10 - Build an ACA (with/out Dapr)
    • Sep 11 - Learn About: Communication
    • Sep 12 - Learn About: State Management
    • Sep 13 - Learn About: Observability
    • Sep 14 - Learn About: Secure Access
    • Sep 15 - ACA + Serverless On Azure

    Serverless Integrations

    Welcome to Week 3 - you've learned to build serverless applications using functions and microservices, orchestrated as containerized applications. Now let's explore a few core Azure services that streamline integrations with Azure and non-Azure services in standard, scalable ways.

    Week 3 Roadmap Week 3 Roadmap

    WEEK 3 - AZURE EVENT GRID & AZURE LOGIC APP

    Posts will be linked here once published.

    • Sep 16 - Logic Apps: Core Concepts
    • Sep 17 - Logic Apps: Quickstart
    • Sep 18 - Logic Apps: Best Practices
    • Sep 19 - Event Grid: Core Concepts
    • Sep 20 - Event Grid: Quickstart
    • Sep 21 - Event Grid: Best Practices
    • Sep 22 - Integrations + Serverless On Azure

    Serverless End-To-End

    It's the final week of Serverless September! So far we've talked about various components of a Serverless solution on Azure. Now let's explore various end-to-end examples and learn how we can make these components work together.

    Week 4 ARTICLES

    Posts will be linked here once published.

    • Sep 23 - TBA
    • Sep 24 - TBA
    • Sep 25 - TBA
    • Sep 26 - TBA
    • Sep 27 - TBA
    • Sep 28 - TBA
    • Sep 29 - TBA

    Week 4 Roadmap


    Sep 30: Summary

    THANK YOU & NEXT STEPS

    Thank you for staying the course with us. In the final two posts of this series we'll do two things:

    • Look Back - with a quick retrospective of what was covered.
    • Look Ahead - with resources and suggestions for how you can skill up further!

    We appreciate your time and attention and we hope you found this curated tour valuable. Feedback and suggestions are always welcome. From our entire team, we wish you good luck with the learning journey - now go build some apps and share your knowledge! 🎉

    Thank You


    - - + + \ No newline at end of file diff --git a/serverless-september/AskTheExpert/index.html b/serverless-september/AskTheExpert/index.html index 2ee06c8950..474c2ac078 100644 --- a/serverless-september/AskTheExpert/index.html +++ b/serverless-september/AskTheExpert/index.html @@ -14,13 +14,13 @@ - - + +

    Ask The Expert

    1. Open a New Issue on the repo.
    2. Click Get Started on the 🎤 Ask the Expert! template.
    3. Fill in the details and submit!

    Our team will review all submitted questions and prioritize them for the live ATE session. Questions that don't get answered live (due to time constraints) will be responded to here, in response to your submitted issue.


    What is it?

    Ask the Expert is a series of scheduled 30-minute LIVE broadcasts where you can connect with experts to get your questions answered! You an also visit the site later, to view sessions on demand - and view answers to questions you may have submitted ahead of time.


    How does it work?

    The live broadcast will have a moderated chat session where you can submit questions in real time. We also have a custom 🎤 Ask The Expert issue you can use to submit questions ahead of time as mentioned earlier.

    • We strongly encourage you to submit questions early using that issue
    • Browse previously posted questions to reduce duplication.
    • Upvote (👍🏽) existing questions of interest to help us prioritize them for the live show.

    Doing this will help us all in a few ways:

    • We can ensure that all questions get answered here, even if we run out of time on the live broadcast.
    • Others can vote (👍🏽) on your question - helping us prioritize them live based on popularity
    • We can update them with responses post-event for future readers.

    When is it?

    Visit the ATE : Serverless September page to see the latest schedule and registration links! For convenience, we've replicated some information here. Please click the REGISTER TO ATTEND links to save the date and get notified of key details like links to the livestream (pre-event) and recording (post-event.)

    DateDescription
    Sep 15, 2022 : Functions-as-a-Service (FaaS)It is time to focus on the pieces of code that matter most to you while Azure Functions handles the rest. Discuss with the experts on how to execute event-driven serverless code functions with an end-to-end development experience using Azure Functions.

    REGISTER TO ATTEND
    Sep 29, 2022 : Containers & Microservices

    Azure Container Apps is an app-centric service, empowering developers to focus on the differentiating business logic of their apps rather than on cloud infrastructure management. Discuss with the experts on how to build and deploy modern apps and microservices using serverless containers with Azure Container Apps.

    REGISTER TO ATTEND
    - - + + \ No newline at end of file diff --git a/serverless-september/CloudSkills/index.html b/serverless-september/CloudSkills/index.html index 886e8988f7..d7634e21f6 100644 --- a/serverless-september/CloudSkills/index.html +++ b/serverless-september/CloudSkills/index.html @@ -14,13 +14,13 @@ - - + +

    Cloud Skills Challenge

    Use the link above to register for the Cloud Skills Challenge today! You will get an automatical email notification when the challenge kicks off, ensuring you don't waste any time! The challenge runs for 30 days (Sep 1 - Sep 30) so an early start helps!


    About Cloud Skills

    The Cloud Skills Challenge is a fun way to skill up on Azure serverless technologies while competing with other members of the community for a chance to win fun swag!

    You'll work your way through learning modules that skill you up on relevant technologies - while collecting points that place you on a Leaderboard.

    1. 🎯 Compete - Benchmark your progress against friends and coworkers.
    2. 🎓 Learn - Increase your understanding by completing learning modules.
    3. 🏆 Skill Up - Gain useful technical skills and prep for certifications.

    About Microsoft Learn

    Completed the Cloud Skills Challenge, and want to keep going on your learning journey? Or, perhaps there are other Cloud+AI topics you want to skill up in? Check out these three resources for building your professional profile!

    1️⃣ - LEARNING PATHS2️⃣ - CERTIFICATIONS3️⃣ - LEARNING EVENTS
    Skill up on a topic with guided paths for self-study!Showcase your expertise with industry-recognized credentials!Learn from subject matter experts in live & recorded events
    - - + + \ No newline at end of file diff --git a/serverless-september/CommunityBuzz/index.html b/serverless-september/CommunityBuzz/index.html index 6e71033f5c..d9ce069b76 100644 --- a/serverless-september/CommunityBuzz/index.html +++ b/serverless-september/CommunityBuzz/index.html @@ -14,13 +14,13 @@ - - + + - - + + \ No newline at end of file diff --git a/serverless-september/ServerlessHacks/index.html b/serverless-september/ServerlessHacks/index.html index 727d811e3c..86c1f61c18 100644 --- a/serverless-september/ServerlessHacks/index.html +++ b/serverless-september/ServerlessHacks/index.html @@ -14,13 +14,13 @@ - - + +

    Serverless Hacks

    1. Open a New Issue on the repo.
    2. Click Get Started on the 🎯 My Serverless Hacks ! template.
    3. Fill in the details and submit!

    We'll review submissions on a rolling basis, to verify that the submitted hacks are complete. Accepted submissions will be added to the 🏆 Hall Of Fame here as a permanent record of your accomplishment!

    You can submit multiple entries - but each must be associated with a unique GitHub repo and showcase something new or different you did beyond the default. Read on for examples of how you can Extend the Hack.


    🌩 Join The Hack!

    Visit the Serverless September At The Reactor page and register to attend weekly online sessions with Cloud Advocate Gwyneth Peña-Siguenza and special guests! Hear real-world serverless stories, ask questions and get insights to help you progress in your challenge.

    • Sep 7 | How to get into Tech And Serverless - with Linda Nichols. REGISTER HERE
    • Sep 14 | How to DevOps and Serverless the Right Way. REGISTER HERE
    • Sep 21 | The Serverless Project that Got Me Promoted! REGISTER HERE
    • Sep 28 | So you want to migrate your project to Serverless? REGISTER HERE

    She will host weekly office hours where they discuss Serverless topics, take questions and provide guidance to help you walk through the mini-challenges in this year's What The Hack: Serverless Challenge described below. Plus, she'll share her own solution in a series of video walkthroughs that can guide you in your own challenge journey!


    🎯 Complete Hacks

    Your challenge this year comes from What The Hack, part of a collection of challenge-based hackathons that you can complete - or in a team of 3-5 people as a collaborative learning experience in-person or online. The goal is to learn from each other and share your insights with the broader community in a way that helps you build and retain expertise, while also contributing back.

    The figure above shows the specific challenge you will work on: Azure Serverless in the category of Application Modernization. In this challenge, you will build a Tollbooth application using a serverless architecture involving multiple Azure services.

    Don't forget to join the weekly office hour sessions if you have questions or need help. And make sure you submit your solution to our Hall Of Fame when you are done!

    SERVERLESS HACK RESOURCES

    Here's a handy link to the Resources.zip file that is mentioned in the Serverless Hacks walkthrough.


    💡 Extend Hacks

    The 8-challenge hack provides the default path for working on a solution. But you have options to go beyond this, or do something new or different!

    • Check out the Optional Challenges identified in the Hack page.
    • Implement your solution in different languages (Java, JS, C#/.NET, Python)
    • Extend the scenario to add another Azure Service (e.g., Azure Container Apps)
    • Explore new developer tools or workflows (e.g., Azure Developer CLI)

    🏆 Hall Of Fame

    THE SERVERLESS HACKS HALL OF FAME!

    This section lists participants who submitted a valid hack solution, with a link to the repository containing their code. We wanted to celebrate your accomplishments publicly, and amplify your work as a learning resource for others!

    We can't wait to see what you build!

    - - + + \ No newline at end of file diff --git a/serverless-september/ZeroToHero/index.html b/serverless-september/ZeroToHero/index.html index fe9d9bad94..de6e077a76 100644 --- a/serverless-september/ZeroToHero/index.html +++ b/serverless-september/ZeroToHero/index.html @@ -14,13 +14,13 @@ - - + +

    Zero To Hero

    About This Series

    Zero-to-Hero is a series of blog posts from our Product Engineering teams, that will be published on the Microsoft Tech Community: Apps On Azure blog and links updated below for convenience.


    Azure Functions

    Published OnTopicAuthor / Link
    Sep 5, 2022A walkthrough of Durable Entities Lily Ma
    David Justo
    Sep 12, 2022Building serverless Go applications with Azure functions with Custom Handlers Melony Qin
    Sep 15, 2022🎤 Ask The Expert
    Live Q&A with Azure Functions Team
    🌟 Register
    Sep 19, 2022Error Handling with Apache Kafka extension for AzureRamya Oruganti
    Sep 26, 2022Monitoring & Troubleshooting apps in Azure Functions Madhura Bharadwaj

    Azure Container Apps

    Published OnTopicAuthor
    Sep 5, 2022Go Cloud-Native With Azure Container AppsKendall Roden
    Sep 12, 2022Journey to the cloud with Azure Container AppsAnthony Chu
    Sep 19, 2022Observability with Azure Container AppsMike Morton
    Sep 26, 2022End-to-End solution development with codeKendall Roden
    Sep 29, 2022🎤 Ask The Expert
    Live Q&A with Azure Container Apps Team
    🌟 Register
    - - + + \ No newline at end of file diff --git a/serverless-september/index.html b/serverless-september/index.html index cec90ed5d0..ccb8720be0 100644 --- a/serverless-september/index.html +++ b/serverless-september/index.html @@ -14,13 +14,13 @@ - - + +

    It's Serverless September!

    Join us for a month-long celebration of serverless computing - from core concepts and developer tools, to usage scenarios and best practices. Bookmark this page, then join us September 1, 2022 as we kickstart multiple community-driven and self-guided learning initiatives for jumpstarting your Cloud-Native journey.

    #30DaysOfServerless

    Join us on a #30Day journey covering Azure Functions, Container Apps, Dapr, Event Grid, Logic Apps & more.

    Zero To Hero

    Get the latest updates on Serverless On Azure products and features - directly from product teams!

    Serverless Hacks

    Join us for weekly events at Microsoft Reactor, as we work through hands-on challenges in Serverless!

    Cloud Skills

    Skill up on key cloud technologies with these free, self-guided learning courses - and make the leaderboard!

    Ask The Expert

    Join us for online conversations with the product teams - submit questions ahead of time or ask them live!

    Community Buzz

    Build interesting demos or wrote helpful articles? Contribute your feedback and content for a chance to be featured!

    - - + + \ No newline at end of file diff --git a/sitemap.xml b/sitemap.xml index 10985d2eb9..ab5989f46e 100644 --- a/sitemap.xml +++ b/sitemap.xml @@ -1 +1 @@ -https://azure.github.io/Cloud-Native/30daysofIAweekly0.5https://azure.github.io/Cloud-Native/30daysofIA/archiveweekly0.5https://azure.github.io/Cloud-Native/30daysofIA/cultivating-a-culture-for-intelligent-appsweekly0.5https://azure.github.io/Cloud-Native/30daysofIA/demystifying-intelligent-applicationsweekly0.5https://azure.github.io/Cloud-Native/30daysofIA/hacktogether-recapweekly0.5https://azure.github.io/Cloud-Native/30daysofIA/harnessing-the-power-of-intelligent-appsweekly0.5https://azure.github.io/Cloud-Native/30daysofIA/how-digital-natives-leverage-generative-aiweekly0.5https://azure.github.io/Cloud-Native/30daysofIA/kick-offweekly0.5https://azure.github.io/Cloud-Native/30daysofIA/page/2weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/page/3weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/page/4weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/page/5weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/page/6weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/page/7weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/page/8weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/page/9weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/preparing-the-path-for-intelligent-appsweekly0.5https://azure.github.io/Cloud-Native/30daysofIA/reimagine-app-development-with-aiweekly0.5https://azure.github.io/Cloud-Native/30daysofIA/road-to-fallforIAweekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tagsweekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/30-days-of-iaweekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/30-days-of-ia/page/2weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/30-days-of-ia/page/3weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/30-days-of-ia/page/4weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/30-days-of-ia/page/5weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/30-days-of-ia/page/6weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/30-days-of-ia/page/7weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/30-days-of-ia/page/8weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/30-days-of-ia/page/9weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/ask-the-expertweekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/ask-the-expert/page/2weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/ask-the-expert/page/3weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/ask-the-expert/page/4weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/ask-the-expert/page/5weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/ask-the-expert/page/6weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/ask-the-expert/page/7weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/ask-the-expert/page/8weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/ask-the-expert/page/9weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-container-appsweekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-container-apps/page/2weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-container-apps/page/3weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-container-apps/page/4weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-container-apps/page/5weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-container-apps/page/6weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-container-apps/page/7weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-container-apps/page/8weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-container-apps/page/9weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-cosmos-dbweekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-cosmos-db/page/2weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-cosmos-db/page/3weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-cosmos-db/page/4weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-cosmos-db/page/5weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-cosmos-db/page/6weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-cosmos-db/page/7weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-cosmos-db/page/8weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-cosmos-db/page/9weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-functionsweekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-functions/page/2weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-functions/page/3weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-functions/page/4weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-functions/page/5weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-functions/page/6weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-functions/page/7weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-functions/page/8weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-functions/page/9weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-kubernetes-serviceweekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-kubernetes-service/page/2weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-kubernetes-service/page/3weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-kubernetes-service/page/4weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-kubernetes-service/page/5weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-kubernetes-service/page/6weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-kubernetes-service/page/7weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-kubernetes-service/page/8weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-kubernetes-service/page/9weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-openaiweekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-openai/page/2weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-openai/page/3weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-openai/page/4weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-openai/page/5weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-openai/page/6weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-openai/page/7weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-openai/page/8weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-openai/page/9weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/community-buzzweekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/community-buzz/page/2weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/community-buzz/page/3weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/community-buzz/page/4weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/community-buzz/page/5weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/community-buzz/page/6weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/community-buzz/page/7weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/community-buzz/page/8weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/community-buzz/page/9weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/fall-for-iaweekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/fall-for-ia/page/2weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/fall-for-ia/page/3weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/fall-for-ia/page/4weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/fall-for-ia/page/5weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/fall-for-ia/page/6weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/fall-for-ia/page/7weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/fall-for-ia/page/8weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/fall-for-ia/page/9weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/github-actionsweekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/github-actions/page/2weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/github-actions/page/3weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/github-actions/page/4weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/github-actions/page/5weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/github-actions/page/6weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/github-actions/page/7weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/github-actions/page/8weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/github-actions/page/9weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/github-codespacesweekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/github-codespaces/page/2weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/github-codespaces/page/3weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/github-codespaces/page/4weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/github-codespaces/page/5weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/github-codespaces/page/6weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/github-codespaces/page/7weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/github-codespaces/page/8weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/github-codespaces/page/9weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/github-copilotweekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/github-copilot/page/2weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/github-copilot/page/3weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/github-copilot/page/4weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/github-copilot/page/5weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/github-copilot/page/6weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/github-copilot/page/7weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/github-copilot/page/8weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/github-copilot/page/9weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/hack-togetherweekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/hack-together/page/2weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/hack-together/page/3weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/hack-together/page/4weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/hack-together/page/5weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/hack-together/page/6weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/hack-together/page/7weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/hack-together/page/8weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/hack-together/page/9weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/learn-liveweekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/learn-live/page/2weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/learn-live/page/3weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/learn-live/page/4weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/learn-live/page/5weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/learn-live/page/6weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/learn-live/page/7weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/learn-live/page/8weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/learn-live/page/9weekly0.5https://azure.github.io/Cloud-Native/blogweekly0.5https://azure.github.io/Cloud-Native/blog/01-kickoffweekly0.5https://azure.github.io/Cloud-Native/blog/02-functions-introweekly0.5https://azure.github.io/Cloud-Native/blog/03-functions-quickstartweekly0.5https://azure.github.io/Cloud-Native/blog/04-functions-javaweekly0.5https://azure.github.io/Cloud-Native/blog/05-functions-jsweekly0.5https://azure.github.io/Cloud-Native/blog/06-functions-dotnetweekly0.5https://azure.github.io/Cloud-Native/blog/07-functions-pythonweekly0.5https://azure.github.io/Cloud-Native/blog/08-functions-azureweekly0.5https://azure.github.io/Cloud-Native/blog/09-aca-fundamentalsweekly0.5https://azure.github.io/Cloud-Native/blog/11-scaling-container-appsweekly0.5https://azure.github.io/Cloud-Native/blog/12-build-with-daprweekly0.5https://azure.github.io/Cloud-Native/blog/13-aca-managed-idweekly0.5https://azure.github.io/Cloud-Native/blog/14-dapr-aca-quickstartweekly0.5https://azure.github.io/Cloud-Native/blog/15-microservices-azureweekly0.5https://azure.github.io/Cloud-Native/blog/17-integrate-cosmosdbweekly0.5https://azure.github.io/Cloud-Native/blog/18-cloudmailweekly0.5https://azure.github.io/Cloud-Native/blog/20-events-graphweekly0.5https://azure.github.io/Cloud-Native/blog/21-cloudevents-via-event-gridweekly0.5https://azure.github.io/Cloud-Native/blog/24-aca-dotnetweekly0.5https://azure.github.io/Cloud-Native/blog/25-aca-javaweekly0.5https://azure.github.io/Cloud-Native/blog/28-where-am-iweekly0.5https://azure.github.io/Cloud-Native/blog/29-awesome-azdweekly0.5https://azure.github.io/Cloud-Native/blog/29-azure-developer-cliweekly0.5https://azure.github.io/Cloud-Native/blog/archiveweekly0.5https://azure.github.io/Cloud-Native/blog/microservices-10weekly0.5https://azure.github.io/Cloud-Native/blog/page/10weekly0.5https://azure.github.io/Cloud-Native/blog/page/11weekly0.5https://azure.github.io/Cloud-Native/blog/page/12weekly0.5https://azure.github.io/Cloud-Native/blog/page/13weekly0.5https://azure.github.io/Cloud-Native/blog/page/14weekly0.5https://azure.github.io/Cloud-Native/blog/page/15weekly0.5https://azure.github.io/Cloud-Native/blog/page/16weekly0.5https://azure.github.io/Cloud-Native/blog/page/17weekly0.5https://azure.github.io/Cloud-Native/blog/page/18weekly0.5https://azure.github.io/Cloud-Native/blog/page/19weekly0.5https://azure.github.io/Cloud-Native/blog/page/2weekly0.5https://azure.github.io/Cloud-Native/blog/page/20weekly0.5https://azure.github.io/Cloud-Native/blog/page/21weekly0.5https://azure.github.io/Cloud-Native/blog/page/22weekly0.5https://azure.github.io/Cloud-Native/blog/page/23weekly0.5https://azure.github.io/Cloud-Native/blog/page/24weekly0.5https://azure.github.io/Cloud-Native/blog/page/25weekly0.5https://azure.github.io/Cloud-Native/blog/page/26weekly0.5https://azure.github.io/Cloud-Native/blog/page/27weekly0.5https://azure.github.io/Cloud-Native/blog/page/28weekly0.5https://azure.github.io/Cloud-Native/blog/page/29weekly0.5https://azure.github.io/Cloud-Native/blog/page/3weekly0.5https://azure.github.io/Cloud-Native/blog/page/30weekly0.5https://azure.github.io/Cloud-Native/blog/page/31weekly0.5https://azure.github.io/Cloud-Native/blog/page/32weekly0.5https://azure.github.io/Cloud-Native/blog/page/33weekly0.5https://azure.github.io/Cloud-Native/blog/page/34weekly0.5https://azure.github.io/Cloud-Native/blog/page/4weekly0.5https://azure.github.io/Cloud-Native/blog/page/5weekly0.5https://azure.github.io/Cloud-Native/blog/page/6weekly0.5https://azure.github.io/Cloud-Native/blog/page/7weekly0.5https://azure.github.io/Cloud-Native/blog/page/8weekly0.5https://azure.github.io/Cloud-Native/blog/page/9weekly0.5https://azure.github.io/Cloud-Native/blog/serverless-status-postweekly0.5https://azure.github.io/Cloud-Native/blog/studentsweekly0.5https://azure.github.io/Cloud-Native/blog/tagsweekly0.5https://azure.github.io/Cloud-Native/blog/tags/30-days-of-serverlessweekly0.5https://azure.github.io/Cloud-Native/blog/tags/30-days-of-serverless/page/10weekly0.5https://azure.github.io/Cloud-Native/blog/tags/30-days-of-serverless/page/11weekly0.5https://azure.github.io/Cloud-Native/blog/tags/30-days-of-serverless/page/12weekly0.5https://azure.github.io/Cloud-Native/blog/tags/30-days-of-serverless/page/13weekly0.5https://azure.github.io/Cloud-Native/blog/tags/30-days-of-serverless/page/14weekly0.5https://azure.github.io/Cloud-Native/blog/tags/30-days-of-serverless/page/15weekly0.5https://azure.github.io/Cloud-Native/blog/tags/30-days-of-serverless/page/16weekly0.5https://azure.github.io/Cloud-Native/blog/tags/30-days-of-serverless/page/17weekly0.5https://azure.github.io/Cloud-Native/blog/tags/30-days-of-serverless/page/18weekly0.5https://azure.github.io/Cloud-Native/blog/tags/30-days-of-serverless/page/19weekly0.5https://azure.github.io/Cloud-Native/blog/tags/30-days-of-serverless/page/2weekly0.5https://azure.github.io/Cloud-Native/blog/tags/30-days-of-serverless/page/20weekly0.5https://azure.github.io/Cloud-Native/blog/tags/30-days-of-serverless/page/3weekly0.5https://azure.github.io/Cloud-Native/blog/tags/30-days-of-serverless/page/4weekly0.5https://azure.github.io/Cloud-Native/blog/tags/30-days-of-serverless/page/5weekly0.5https://azure.github.io/Cloud-Native/blog/tags/30-days-of-serverless/page/6weekly0.5https://azure.github.io/Cloud-Native/blog/tags/30-days-of-serverless/page/7weekly0.5https://azure.github.io/Cloud-Native/blog/tags/30-days-of-serverless/page/8weekly0.5https://azure.github.io/Cloud-Native/blog/tags/30-days-of-serverless/page/9weekly0.5https://azure.github.io/Cloud-Native/blog/tags/ask-the-expertweekly0.5https://azure.github.io/Cloud-Native/blog/tags/asp-netweekly0.5https://azure.github.io/Cloud-Native/blog/tags/autoscalingweekly0.5https://azure.github.io/Cloud-Native/blog/tags/azdweekly0.5https://azure.github.io/Cloud-Native/blog/tags/azure-container-appsweekly0.5https://azure.github.io/Cloud-Native/blog/tags/azure-container-apps/page/10weekly0.5https://azure.github.io/Cloud-Native/blog/tags/azure-container-apps/page/11weekly0.5https://azure.github.io/Cloud-Native/blog/tags/azure-container-apps/page/12weekly0.5https://azure.github.io/Cloud-Native/blog/tags/azure-container-apps/page/13weekly0.5https://azure.github.io/Cloud-Native/blog/tags/azure-container-apps/page/14weekly0.5https://azure.github.io/Cloud-Native/blog/tags/azure-container-apps/page/15weekly0.5https://azure.github.io/Cloud-Native/blog/tags/azure-container-apps/page/16weekly0.5https://azure.github.io/Cloud-Native/blog/tags/azure-container-apps/page/17weekly0.5https://azure.github.io/Cloud-Native/blog/tags/azure-container-apps/page/18weekly0.5https://azure.github.io/Cloud-Native/blog/tags/azure-container-apps/page/19weekly0.5https://azure.github.io/Cloud-Native/blog/tags/azure-container-apps/page/2weekly0.5https://azure.github.io/Cloud-Native/blog/tags/azure-container-apps/page/20weekly0.5https://azure.github.io/Cloud-Native/blog/tags/azure-container-apps/page/3weekly0.5https://azure.github.io/Cloud-Native/blog/tags/azure-container-apps/page/4weekly0.5https://azure.github.io/Cloud-Native/blog/tags/azure-container-apps/page/5weekly0.5https://azure.github.io/Cloud-Native/blog/tags/azure-container-apps/page/6weekly0.5https://azure.github.io/Cloud-Native/blog/tags/azure-container-apps/page/7weekly0.5https://azure.github.io/Cloud-Native/blog/tags/azure-container-apps/page/8weekly0.5https://azure.github.io/Cloud-Native/blog/tags/azure-container-apps/page/9weekly0.5https://azure.github.io/Cloud-Native/blog/tags/azure-developer-cliweekly0.5https://azure.github.io/Cloud-Native/blog/tags/azure-developer-cli/page/2weekly0.5https://azure.github.io/Cloud-Native/blog/tags/azure-event-gridweekly0.5https://azure.github.io/Cloud-Native/blog/tags/azure-event-grid/page/2weekly0.5https://azure.github.io/Cloud-Native/blog/tags/azure-event-grid/page/3weekly0.5https://azure.github.io/Cloud-Native/blog/tags/azure-functionsweekly0.5https://azure.github.io/Cloud-Native/blog/tags/azure-functions/page/10weekly0.5https://azure.github.io/Cloud-Native/blog/tags/azure-functions/page/11weekly0.5https://azure.github.io/Cloud-Native/blog/tags/azure-functions/page/12weekly0.5https://azure.github.io/Cloud-Native/blog/tags/azure-functions/page/13weekly0.5https://azure.github.io/Cloud-Native/blog/tags/azure-functions/page/14weekly0.5https://azure.github.io/Cloud-Native/blog/tags/azure-functions/page/15weekly0.5https://azure.github.io/Cloud-Native/blog/tags/azure-functions/page/16weekly0.5https://azure.github.io/Cloud-Native/blog/tags/azure-functions/page/2weekly0.5https://azure.github.io/Cloud-Native/blog/tags/azure-functions/page/3weekly0.5https://azure.github.io/Cloud-Native/blog/tags/azure-functions/page/4weekly0.5https://azure.github.io/Cloud-Native/blog/tags/azure-functions/page/5weekly0.5https://azure.github.io/Cloud-Native/blog/tags/azure-functions/page/6weekly0.5https://azure.github.io/Cloud-Native/blog/tags/azure-functions/page/7weekly0.5https://azure.github.io/Cloud-Native/blog/tags/azure-functions/page/8weekly0.5https://azure.github.io/Cloud-Native/blog/tags/azure-functions/page/9weekly0.5https://azure.github.io/Cloud-Native/blog/tags/azure-logic-appsweekly0.5https://azure.github.io/Cloud-Native/blog/tags/azure-logic-apps/page/2weekly0.5https://azure.github.io/Cloud-Native/blog/tags/azure-logic-apps/page/3weekly0.5https://azure.github.io/Cloud-Native/blog/tags/cloud-nativeweekly0.5https://azure.github.io/Cloud-Native/blog/tags/cloudeventsweekly0.5https://azure.github.io/Cloud-Native/blog/tags/custom-connectorweekly0.5https://azure.github.io/Cloud-Native/blog/tags/daprweekly0.5https://azure.github.io/Cloud-Native/blog/tags/dapr/page/10weekly0.5https://azure.github.io/Cloud-Native/blog/tags/dapr/page/11weekly0.5https://azure.github.io/Cloud-Native/blog/tags/dapr/page/12weekly0.5https://azure.github.io/Cloud-Native/blog/tags/dapr/page/13weekly0.5https://azure.github.io/Cloud-Native/blog/tags/dapr/page/14weekly0.5https://azure.github.io/Cloud-Native/blog/tags/dapr/page/15weekly0.5https://azure.github.io/Cloud-Native/blog/tags/dapr/page/16weekly0.5https://azure.github.io/Cloud-Native/blog/tags/dapr/page/2weekly0.5https://azure.github.io/Cloud-Native/blog/tags/dapr/page/3weekly0.5https://azure.github.io/Cloud-Native/blog/tags/dapr/page/4weekly0.5https://azure.github.io/Cloud-Native/blog/tags/dapr/page/5weekly0.5https://azure.github.io/Cloud-Native/blog/tags/dapr/page/6weekly0.5https://azure.github.io/Cloud-Native/blog/tags/dapr/page/7weekly0.5https://azure.github.io/Cloud-Native/blog/tags/dapr/page/8weekly0.5https://azure.github.io/Cloud-Native/blog/tags/dapr/page/9weekly0.5https://azure.github.io/Cloud-Native/blog/tags/devtoolsweekly0.5https://azure.github.io/Cloud-Native/blog/tags/devtools/page/2weekly0.5https://azure.github.io/Cloud-Native/blog/tags/docker-composeweekly0.5https://azure.github.io/Cloud-Native/blog/tags/dotnetweekly0.5https://azure.github.io/Cloud-Native/blog/tags/dotnet/page/2weekly0.5https://azure.github.io/Cloud-Native/blog/tags/event-hubsweekly0.5https://azure.github.io/Cloud-Native/blog/tags/hacktoberfestweekly0.5https://azure.github.io/Cloud-Native/blog/tags/helloweekly0.5https://azure.github.io/Cloud-Native/blog/tags/hello/page/2weekly0.5https://azure.github.io/Cloud-Native/blog/tags/javaweekly0.5https://azure.github.io/Cloud-Native/blog/tags/javascriptweekly0.5https://azure.github.io/Cloud-Native/blog/tags/kedaweekly0.5https://azure.github.io/Cloud-Native/blog/tags/logic-appsweekly0.5https://azure.github.io/Cloud-Native/blog/tags/microservicesweekly0.5https://azure.github.io/Cloud-Native/blog/tags/microservices/page/10weekly0.5https://azure.github.io/Cloud-Native/blog/tags/microservices/page/11weekly0.5https://azure.github.io/Cloud-Native/blog/tags/microservices/page/2weekly0.5https://azure.github.io/Cloud-Native/blog/tags/microservices/page/3weekly0.5https://azure.github.io/Cloud-Native/blog/tags/microservices/page/4weekly0.5https://azure.github.io/Cloud-Native/blog/tags/microservices/page/5weekly0.5https://azure.github.io/Cloud-Native/blog/tags/microservices/page/6weekly0.5https://azure.github.io/Cloud-Native/blog/tags/microservices/page/7weekly0.5https://azure.github.io/Cloud-Native/blog/tags/microservices/page/8weekly0.5https://azure.github.io/Cloud-Native/blog/tags/microservices/page/9weekly0.5https://azure.github.io/Cloud-Native/blog/tags/microsoft-365weekly0.5https://azure.github.io/Cloud-Native/blog/tags/microsoft-graphweekly0.5https://azure.github.io/Cloud-Native/blog/tags/openapiweekly0.5https://azure.github.io/Cloud-Native/blog/tags/power-platformweekly0.5https://azure.github.io/Cloud-Native/blog/tags/pythonweekly0.5https://azure.github.io/Cloud-Native/blog/tags/serverlessweekly0.5https://azure.github.io/Cloud-Native/blog/tags/serverless-e-2-eweekly0.5https://azure.github.io/Cloud-Native/blog/tags/serverless-hacksweekly0.5https://azure.github.io/Cloud-Native/blog/tags/serverless-septemberweekly0.5https://azure.github.io/Cloud-Native/blog/tags/serverless-september/page/10weekly0.5https://azure.github.io/Cloud-Native/blog/tags/serverless-september/page/11weekly0.5https://azure.github.io/Cloud-Native/blog/tags/serverless-september/page/12weekly0.5https://azure.github.io/Cloud-Native/blog/tags/serverless-september/page/13weekly0.5https://azure.github.io/Cloud-Native/blog/tags/serverless-september/page/14weekly0.5https://azure.github.io/Cloud-Native/blog/tags/serverless-september/page/15weekly0.5https://azure.github.io/Cloud-Native/blog/tags/serverless-september/page/16weekly0.5https://azure.github.io/Cloud-Native/blog/tags/serverless-september/page/17weekly0.5https://azure.github.io/Cloud-Native/blog/tags/serverless-september/page/18weekly0.5https://azure.github.io/Cloud-Native/blog/tags/serverless-september/page/19weekly0.5https://azure.github.io/Cloud-Native/blog/tags/serverless-september/page/2weekly0.5https://azure.github.io/Cloud-Native/blog/tags/serverless-september/page/20weekly0.5https://azure.github.io/Cloud-Native/blog/tags/serverless-september/page/21weekly0.5https://azure.github.io/Cloud-Native/blog/tags/serverless-september/page/22weekly0.5https://azure.github.io/Cloud-Native/blog/tags/serverless-september/page/23weekly0.5https://azure.github.io/Cloud-Native/blog/tags/serverless-september/page/24weekly0.5https://azure.github.io/Cloud-Native/blog/tags/serverless-september/page/25weekly0.5https://azure.github.io/Cloud-Native/blog/tags/serverless-september/page/26weekly0.5https://azure.github.io/Cloud-Native/blog/tags/serverless-september/page/27weekly0.5https://azure.github.io/Cloud-Native/blog/tags/serverless-september/page/28weekly0.5https://azure.github.io/Cloud-Native/blog/tags/serverless-september/page/29weekly0.5https://azure.github.io/Cloud-Native/blog/tags/serverless-september/page/3weekly0.5https://azure.github.io/Cloud-Native/blog/tags/serverless-september/page/30weekly0.5https://azure.github.io/Cloud-Native/blog/tags/serverless-september/page/31weekly0.5https://azure.github.io/Cloud-Native/blog/tags/serverless-september/page/32weekly0.5https://azure.github.io/Cloud-Native/blog/tags/serverless-september/page/33weekly0.5https://azure.github.io/Cloud-Native/blog/tags/serverless-september/page/4weekly0.5https://azure.github.io/Cloud-Native/blog/tags/serverless-september/page/5weekly0.5https://azure.github.io/Cloud-Native/blog/tags/serverless-september/page/6weekly0.5https://azure.github.io/Cloud-Native/blog/tags/serverless-september/page/7weekly0.5https://azure.github.io/Cloud-Native/blog/tags/serverless-september/page/8weekly0.5https://azure.github.io/Cloud-Native/blog/tags/serverless-september/page/9weekly0.5https://azure.github.io/Cloud-Native/blog/tags/studentsweekly0.5https://azure.github.io/Cloud-Native/blog/tags/vscodeweekly0.5https://azure.github.io/Cloud-Native/blog/tags/zero-to-heroweekly0.5https://azure.github.io/Cloud-Native/blog/tags/zero-to-hero/page/2weekly0.5https://azure.github.io/Cloud-Native/blog/tags/zero-to-hero/page/3weekly0.5https://azure.github.io/Cloud-Native/blog/tags/zero-to-hero/page/4weekly0.5https://azure.github.io/Cloud-Native/blog/tags/zero-to-hero/page/5weekly0.5https://azure.github.io/Cloud-Native/blog/tags/zero-to-hero/page/6weekly0.5https://azure.github.io/Cloud-Native/blog/tags/zero-to-hero/page/7weekly0.5https://azure.github.io/Cloud-Native/blog/tags/zero-to-hero/page/8weekly0.5https://azure.github.io/Cloud-Native/blog/welcomeweekly0.5https://azure.github.io/Cloud-Native/blog/zero2hero-aca-01weekly0.5https://azure.github.io/Cloud-Native/blog/zero2hero-aca-04weekly0.5https://azure.github.io/Cloud-Native/blog/zero2hero-aca-06weekly0.5https://azure.github.io/Cloud-Native/blog/zero2hero-func-02weekly0.5https://azure.github.io/Cloud-Native/blog/zero2hero-func-03weekly0.5https://azure.github.io/Cloud-Native/blog/zero2hero-func-05weekly0.5https://azure.github.io/Cloud-Native/blog/zero2hero-func-07weekly0.5https://azure.github.io/Cloud-Native/calendarweekly0.5https://azure.github.io/Cloud-Native/cnny-2023weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/aks-extensions-addonsweekly0.5https://azure.github.io/Cloud-Native/cnny-2023/archiveweekly0.5https://azure.github.io/Cloud-Native/cnny-2023/bring-your-app-day-1weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/bring-your-app-day-2weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/bring-your-app-day-3weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/bring-your-app-day-4weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/bring-your-app-day-5weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/building-with-draftweekly0.5https://azure.github.io/Cloud-Native/cnny-2023/cloud-native-fundamentalsweekly0.5https://azure.github.io/Cloud-Native/cnny-2023/cnny-kickoffweekly0.5https://azure.github.io/Cloud-Native/cnny-2023/cnny-wrap-upweekly0.5https://azure.github.io/Cloud-Native/cnny-2023/containers-101weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/explore-optionsweekly0.5https://azure.github.io/Cloud-Native/cnny-2023/fundamentals-day-1weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/fundamentals-day-2weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/fundamentals-day-3weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/fundamentals-day-4weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/fundamentals-day-5weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/Kubernetes-101weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/microservices-101weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/page/10weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/page/11weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/page/12weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/page/13weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/page/14weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/page/15weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/page/16weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/page/17weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/page/18weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/page/19weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/page/2weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/page/20weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/page/21weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/page/3weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/page/4weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/page/5weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/page/6weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/page/7weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/page/8weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/page/9weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/serverless-containersweekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tagsweekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/30-daysofcloudnativeweekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/30-daysofcloudnative/page/10weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/30-daysofcloudnative/page/11weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/30-daysofcloudnative/page/12weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/30-daysofcloudnative/page/13weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/30-daysofcloudnative/page/14weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/30-daysofcloudnative/page/15weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/30-daysofcloudnative/page/16weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/30-daysofcloudnative/page/2weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/30-daysofcloudnative/page/3weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/30-daysofcloudnative/page/4weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/30-daysofcloudnative/page/5weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/30-daysofcloudnative/page/6weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/30-daysofcloudnative/page/7weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/30-daysofcloudnative/page/8weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/30-daysofcloudnative/page/9weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/addonsweekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/aksweekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/aks/page/2weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/aks/page/3weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/aks/page/4weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/aks/page/5weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/ask-the-expertweekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/ask-the-expert/page/10weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/ask-the-expert/page/11weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/ask-the-expert/page/12weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/ask-the-expert/page/13weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/ask-the-expert/page/14weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/ask-the-expert/page/15weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/ask-the-expert/page/16weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/ask-the-expert/page/2weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/ask-the-expert/page/3weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/ask-the-expert/page/4weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/ask-the-expert/page/5weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/ask-the-expert/page/6weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/ask-the-expert/page/7weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/ask-the-expert/page/8weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/ask-the-expert/page/9weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/azure-dnsweekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/azure-key-vaultweekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/azure-key-vault/page/2weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/azure-kubernetes-serviceweekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/azure-kubernetes-service/page/10weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/azure-kubernetes-service/page/11weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/azure-kubernetes-service/page/12weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/azure-kubernetes-service/page/13weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/azure-kubernetes-service/page/14weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/azure-kubernetes-service/page/15weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/azure-kubernetes-service/page/16weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/azure-kubernetes-service/page/17weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/azure-kubernetes-service/page/18weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/azure-kubernetes-service/page/19weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/azure-kubernetes-service/page/2weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/azure-kubernetes-service/page/20weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/azure-kubernetes-service/page/21weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/azure-kubernetes-service/page/3weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/azure-kubernetes-service/page/4weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/azure-kubernetes-service/page/5weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/azure-kubernetes-service/page/6weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/azure-kubernetes-service/page/7weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/azure-kubernetes-service/page/8weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/azure-kubernetes-service/page/9weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/cloud-nativeweekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/cloud-native-new-yearweekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/cloud-native-new-year/page/2weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/cloud-native-new-year/page/3weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/cloud-native-new-year/page/4weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/cloud-native-new-year/page/5weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/cloud-native/page/10weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/cloud-native/page/11weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/cloud-native/page/12weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/cloud-native/page/13weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/cloud-native/page/14weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/cloud-native/page/15weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/cloud-native/page/16weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/cloud-native/page/2weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/cloud-native/page/3weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/cloud-native/page/4weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/cloud-native/page/5weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/cloud-native/page/6weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/cloud-native/page/7weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/cloud-native/page/8weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/cloud-native/page/9weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/configmapsweekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/containersweekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/containers/page/2weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/containers/page/3weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/extensionsweekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/ingressweekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/ingress/page/2weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/kubernetesweekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/kubernetes/page/2weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/kubernetes/page/3weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/kubernetes/page/4weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/kubernetes/page/5weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/microservicesweekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/nginx-ingress-controllerweekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/notaryweekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/notationweekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/persistent-storageweekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/persistent-volume-claimsweekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/persistent-volumesweekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/secrets-managementweekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/secure-supply-chainweekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/serviceweekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/windowsweekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/workload-identityweekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/zero-to-heroweekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/zero-to-hero/page/10weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/zero-to-hero/page/11weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/zero-to-hero/page/12weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/zero-to-hero/page/13weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/zero-to-hero/page/14weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/zero-to-hero/page/15weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/zero-to-hero/page/16weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/zero-to-hero/page/2weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/zero-to-hero/page/3weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/zero-to-hero/page/4weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/zero-to-hero/page/5weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/zero-to-hero/page/6weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/zero-to-hero/page/7weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/zero-to-hero/page/8weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/zero-to-hero/page/9weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/windows-containersweekly0.5https://azure.github.io/Cloud-Native/Fall-For-IA/weekly0.5https://azure.github.io/Cloud-Native/Fall-For-IA/AskTheExpertweekly0.5https://azure.github.io/Cloud-Native/Fall-For-IA/calendarweekly0.5https://azure.github.io/Cloud-Native/Fall-For-IA/CloudSkillsweekly0.5https://azure.github.io/Cloud-Native/Fall-For-IA/CommunityGalleryweekly0.5https://azure.github.io/Cloud-Native/Fall-For-IA/HackTogetherweekly0.5https://azure.github.io/Cloud-Native/Fall-For-IA/LearnLiveweekly0.5https://azure.github.io/Cloud-Native/New-Year/weekly0.5https://azure.github.io/Cloud-Native/New-Year/ateweekly0.5https://azure.github.io/Cloud-Native/New-Year/calendarweekly0.5https://azure.github.io/Cloud-Native/serverless-september/weekly0.5https://azure.github.io/Cloud-Native/serverless-september/30DaysOfServerlessweekly0.5https://azure.github.io/Cloud-Native/serverless-september/AskTheExpertweekly0.5https://azure.github.io/Cloud-Native/serverless-september/CloudSkillsweekly0.5https://azure.github.io/Cloud-Native/serverless-september/CommunityBuzzweekly0.5https://azure.github.io/Cloud-Native/serverless-september/ServerlessHacksweekly0.5https://azure.github.io/Cloud-Native/serverless-september/ZeroToHeroweekly0.5https://azure.github.io/Cloud-Native/docs/category/resourcesweekly0.5https://azure.github.io/Cloud-Native/docs/category/videosweekly0.5https://azure.github.io/Cloud-Native/docs/resources/devtoolsweekly0.5https://azure.github.io/Cloud-Native/docs/resources/introweekly0.5https://azure.github.io/Cloud-Native/docs/resources/languagesweekly0.5https://azure.github.io/Cloud-Native/docs/resources/serverlessweekly0.5https://azure.github.io/Cloud-Native/docs/videos/introweekly0.5https://azure.github.io/Cloud-Native/weekly0.5 \ No newline at end of file +https://azure.github.io/Cloud-Native/30daysofIAweekly0.5https://azure.github.io/Cloud-Native/30daysofIA/archiveweekly0.5https://azure.github.io/Cloud-Native/30daysofIA/build-your-first-intelligent-app-with-azure-ai-and-aks-1weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/build-your-first-intelligent-app-with-azure-ai-and-aks-2weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/cultivating-a-culture-for-intelligent-appsweekly0.5https://azure.github.io/Cloud-Native/30daysofIA/demystifying-intelligent-applicationsweekly0.5https://azure.github.io/Cloud-Native/30daysofIA/hacktogether-recapweekly0.5https://azure.github.io/Cloud-Native/30daysofIA/harnessing-the-power-of-intelligent-appsweekly0.5https://azure.github.io/Cloud-Native/30daysofIA/how-digital-natives-leverage-generative-aiweekly0.5https://azure.github.io/Cloud-Native/30daysofIA/kick-offweekly0.5https://azure.github.io/Cloud-Native/30daysofIA/page/10weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/page/11weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/page/2weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/page/3weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/page/4weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/page/5weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/page/6weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/page/7weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/page/8weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/page/9weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/preparing-the-path-for-intelligent-appsweekly0.5https://azure.github.io/Cloud-Native/30daysofIA/reimagine-app-development-with-aiweekly0.5https://azure.github.io/Cloud-Native/30daysofIA/road-to-fallforIAweekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tagsweekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/30-days-of-iaweekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/30-days-of-ia/page/10weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/30-days-of-ia/page/11weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/30-days-of-ia/page/2weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/30-days-of-ia/page/3weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/30-days-of-ia/page/4weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/30-days-of-ia/page/5weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/30-days-of-ia/page/6weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/30-days-of-ia/page/7weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/30-days-of-ia/page/8weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/30-days-of-ia/page/9weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/ask-the-expertweekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/ask-the-expert/page/10weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/ask-the-expert/page/11weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/ask-the-expert/page/2weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/ask-the-expert/page/3weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/ask-the-expert/page/4weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/ask-the-expert/page/5weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/ask-the-expert/page/6weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/ask-the-expert/page/7weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/ask-the-expert/page/8weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/ask-the-expert/page/9weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-container-appsweekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-container-apps/page/10weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-container-apps/page/11weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-container-apps/page/2weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-container-apps/page/3weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-container-apps/page/4weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-container-apps/page/5weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-container-apps/page/6weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-container-apps/page/7weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-container-apps/page/8weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-container-apps/page/9weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-cosmos-dbweekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-cosmos-db/page/10weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-cosmos-db/page/11weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-cosmos-db/page/2weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-cosmos-db/page/3weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-cosmos-db/page/4weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-cosmos-db/page/5weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-cosmos-db/page/6weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-cosmos-db/page/7weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-cosmos-db/page/8weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-cosmos-db/page/9weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-functionsweekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-functions/page/10weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-functions/page/11weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-functions/page/2weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-functions/page/3weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-functions/page/4weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-functions/page/5weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-functions/page/6weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-functions/page/7weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-functions/page/8weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-functions/page/9weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-kubernetes-serviceweekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-kubernetes-service/page/10weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-kubernetes-service/page/11weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-kubernetes-service/page/2weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-kubernetes-service/page/3weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-kubernetes-service/page/4weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-kubernetes-service/page/5weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-kubernetes-service/page/6weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-kubernetes-service/page/7weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-kubernetes-service/page/8weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-kubernetes-service/page/9weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-openaiweekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-openai/page/10weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-openai/page/11weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-openai/page/2weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-openai/page/3weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-openai/page/4weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-openai/page/5weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-openai/page/6weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-openai/page/7weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-openai/page/8weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/azure-openai/page/9weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/community-buzzweekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/community-buzz/page/10weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/community-buzz/page/11weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/community-buzz/page/2weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/community-buzz/page/3weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/community-buzz/page/4weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/community-buzz/page/5weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/community-buzz/page/6weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/community-buzz/page/7weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/community-buzz/page/8weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/community-buzz/page/9weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/fall-for-iaweekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/fall-for-ia/page/10weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/fall-for-ia/page/11weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/fall-for-ia/page/2weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/fall-for-ia/page/3weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/fall-for-ia/page/4weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/fall-for-ia/page/5weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/fall-for-ia/page/6weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/fall-for-ia/page/7weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/fall-for-ia/page/8weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/fall-for-ia/page/9weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/github-actionsweekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/github-actions/page/10weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/github-actions/page/11weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/github-actions/page/2weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/github-actions/page/3weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/github-actions/page/4weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/github-actions/page/5weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/github-actions/page/6weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/github-actions/page/7weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/github-actions/page/8weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/github-actions/page/9weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/github-codespacesweekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/github-codespaces/page/10weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/github-codespaces/page/11weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/github-codespaces/page/2weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/github-codespaces/page/3weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/github-codespaces/page/4weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/github-codespaces/page/5weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/github-codespaces/page/6weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/github-codespaces/page/7weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/github-codespaces/page/8weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/github-codespaces/page/9weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/github-copilotweekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/github-copilot/page/10weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/github-copilot/page/11weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/github-copilot/page/2weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/github-copilot/page/3weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/github-copilot/page/4weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/github-copilot/page/5weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/github-copilot/page/6weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/github-copilot/page/7weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/github-copilot/page/8weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/github-copilot/page/9weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/hack-togetherweekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/hack-together/page/10weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/hack-together/page/11weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/hack-together/page/2weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/hack-together/page/3weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/hack-together/page/4weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/hack-together/page/5weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/hack-together/page/6weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/hack-together/page/7weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/hack-together/page/8weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/hack-together/page/9weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/learn-liveweekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/learn-live/page/10weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/learn-live/page/11weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/learn-live/page/2weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/learn-live/page/3weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/learn-live/page/4weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/learn-live/page/5weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/learn-live/page/6weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/learn-live/page/7weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/learn-live/page/8weekly0.5https://azure.github.io/Cloud-Native/30daysofIA/tags/learn-live/page/9weekly0.5https://azure.github.io/Cloud-Native/blogweekly0.5https://azure.github.io/Cloud-Native/blog/01-kickoffweekly0.5https://azure.github.io/Cloud-Native/blog/02-functions-introweekly0.5https://azure.github.io/Cloud-Native/blog/03-functions-quickstartweekly0.5https://azure.github.io/Cloud-Native/blog/04-functions-javaweekly0.5https://azure.github.io/Cloud-Native/blog/05-functions-jsweekly0.5https://azure.github.io/Cloud-Native/blog/06-functions-dotnetweekly0.5https://azure.github.io/Cloud-Native/blog/07-functions-pythonweekly0.5https://azure.github.io/Cloud-Native/blog/08-functions-azureweekly0.5https://azure.github.io/Cloud-Native/blog/09-aca-fundamentalsweekly0.5https://azure.github.io/Cloud-Native/blog/11-scaling-container-appsweekly0.5https://azure.github.io/Cloud-Native/blog/12-build-with-daprweekly0.5https://azure.github.io/Cloud-Native/blog/13-aca-managed-idweekly0.5https://azure.github.io/Cloud-Native/blog/14-dapr-aca-quickstartweekly0.5https://azure.github.io/Cloud-Native/blog/15-microservices-azureweekly0.5https://azure.github.io/Cloud-Native/blog/17-integrate-cosmosdbweekly0.5https://azure.github.io/Cloud-Native/blog/18-cloudmailweekly0.5https://azure.github.io/Cloud-Native/blog/20-events-graphweekly0.5https://azure.github.io/Cloud-Native/blog/21-cloudevents-via-event-gridweekly0.5https://azure.github.io/Cloud-Native/blog/24-aca-dotnetweekly0.5https://azure.github.io/Cloud-Native/blog/25-aca-javaweekly0.5https://azure.github.io/Cloud-Native/blog/28-where-am-iweekly0.5https://azure.github.io/Cloud-Native/blog/29-awesome-azdweekly0.5https://azure.github.io/Cloud-Native/blog/29-azure-developer-cliweekly0.5https://azure.github.io/Cloud-Native/blog/archiveweekly0.5https://azure.github.io/Cloud-Native/blog/microservices-10weekly0.5https://azure.github.io/Cloud-Native/blog/page/10weekly0.5https://azure.github.io/Cloud-Native/blog/page/11weekly0.5https://azure.github.io/Cloud-Native/blog/page/12weekly0.5https://azure.github.io/Cloud-Native/blog/page/13weekly0.5https://azure.github.io/Cloud-Native/blog/page/14weekly0.5https://azure.github.io/Cloud-Native/blog/page/15weekly0.5https://azure.github.io/Cloud-Native/blog/page/16weekly0.5https://azure.github.io/Cloud-Native/blog/page/17weekly0.5https://azure.github.io/Cloud-Native/blog/page/18weekly0.5https://azure.github.io/Cloud-Native/blog/page/19weekly0.5https://azure.github.io/Cloud-Native/blog/page/2weekly0.5https://azure.github.io/Cloud-Native/blog/page/20weekly0.5https://azure.github.io/Cloud-Native/blog/page/21weekly0.5https://azure.github.io/Cloud-Native/blog/page/22weekly0.5https://azure.github.io/Cloud-Native/blog/page/23weekly0.5https://azure.github.io/Cloud-Native/blog/page/24weekly0.5https://azure.github.io/Cloud-Native/blog/page/25weekly0.5https://azure.github.io/Cloud-Native/blog/page/26weekly0.5https://azure.github.io/Cloud-Native/blog/page/27weekly0.5https://azure.github.io/Cloud-Native/blog/page/28weekly0.5https://azure.github.io/Cloud-Native/blog/page/29weekly0.5https://azure.github.io/Cloud-Native/blog/page/3weekly0.5https://azure.github.io/Cloud-Native/blog/page/30weekly0.5https://azure.github.io/Cloud-Native/blog/page/31weekly0.5https://azure.github.io/Cloud-Native/blog/page/32weekly0.5https://azure.github.io/Cloud-Native/blog/page/33weekly0.5https://azure.github.io/Cloud-Native/blog/page/34weekly0.5https://azure.github.io/Cloud-Native/blog/page/4weekly0.5https://azure.github.io/Cloud-Native/blog/page/5weekly0.5https://azure.github.io/Cloud-Native/blog/page/6weekly0.5https://azure.github.io/Cloud-Native/blog/page/7weekly0.5https://azure.github.io/Cloud-Native/blog/page/8weekly0.5https://azure.github.io/Cloud-Native/blog/page/9weekly0.5https://azure.github.io/Cloud-Native/blog/serverless-status-postweekly0.5https://azure.github.io/Cloud-Native/blog/studentsweekly0.5https://azure.github.io/Cloud-Native/blog/tagsweekly0.5https://azure.github.io/Cloud-Native/blog/tags/30-days-of-serverlessweekly0.5https://azure.github.io/Cloud-Native/blog/tags/30-days-of-serverless/page/10weekly0.5https://azure.github.io/Cloud-Native/blog/tags/30-days-of-serverless/page/11weekly0.5https://azure.github.io/Cloud-Native/blog/tags/30-days-of-serverless/page/12weekly0.5https://azure.github.io/Cloud-Native/blog/tags/30-days-of-serverless/page/13weekly0.5https://azure.github.io/Cloud-Native/blog/tags/30-days-of-serverless/page/14weekly0.5https://azure.github.io/Cloud-Native/blog/tags/30-days-of-serverless/page/15weekly0.5https://azure.github.io/Cloud-Native/blog/tags/30-days-of-serverless/page/16weekly0.5https://azure.github.io/Cloud-Native/blog/tags/30-days-of-serverless/page/17weekly0.5https://azure.github.io/Cloud-Native/blog/tags/30-days-of-serverless/page/18weekly0.5https://azure.github.io/Cloud-Native/blog/tags/30-days-of-serverless/page/19weekly0.5https://azure.github.io/Cloud-Native/blog/tags/30-days-of-serverless/page/2weekly0.5https://azure.github.io/Cloud-Native/blog/tags/30-days-of-serverless/page/20weekly0.5https://azure.github.io/Cloud-Native/blog/tags/30-days-of-serverless/page/3weekly0.5https://azure.github.io/Cloud-Native/blog/tags/30-days-of-serverless/page/4weekly0.5https://azure.github.io/Cloud-Native/blog/tags/30-days-of-serverless/page/5weekly0.5https://azure.github.io/Cloud-Native/blog/tags/30-days-of-serverless/page/6weekly0.5https://azure.github.io/Cloud-Native/blog/tags/30-days-of-serverless/page/7weekly0.5https://azure.github.io/Cloud-Native/blog/tags/30-days-of-serverless/page/8weekly0.5https://azure.github.io/Cloud-Native/blog/tags/30-days-of-serverless/page/9weekly0.5https://azure.github.io/Cloud-Native/blog/tags/ask-the-expertweekly0.5https://azure.github.io/Cloud-Native/blog/tags/asp-netweekly0.5https://azure.github.io/Cloud-Native/blog/tags/autoscalingweekly0.5https://azure.github.io/Cloud-Native/blog/tags/azdweekly0.5https://azure.github.io/Cloud-Native/blog/tags/azure-container-appsweekly0.5https://azure.github.io/Cloud-Native/blog/tags/azure-container-apps/page/10weekly0.5https://azure.github.io/Cloud-Native/blog/tags/azure-container-apps/page/11weekly0.5https://azure.github.io/Cloud-Native/blog/tags/azure-container-apps/page/12weekly0.5https://azure.github.io/Cloud-Native/blog/tags/azure-container-apps/page/13weekly0.5https://azure.github.io/Cloud-Native/blog/tags/azure-container-apps/page/14weekly0.5https://azure.github.io/Cloud-Native/blog/tags/azure-container-apps/page/15weekly0.5https://azure.github.io/Cloud-Native/blog/tags/azure-container-apps/page/16weekly0.5https://azure.github.io/Cloud-Native/blog/tags/azure-container-apps/page/17weekly0.5https://azure.github.io/Cloud-Native/blog/tags/azure-container-apps/page/18weekly0.5https://azure.github.io/Cloud-Native/blog/tags/azure-container-apps/page/19weekly0.5https://azure.github.io/Cloud-Native/blog/tags/azure-container-apps/page/2weekly0.5https://azure.github.io/Cloud-Native/blog/tags/azure-container-apps/page/20weekly0.5https://azure.github.io/Cloud-Native/blog/tags/azure-container-apps/page/3weekly0.5https://azure.github.io/Cloud-Native/blog/tags/azure-container-apps/page/4weekly0.5https://azure.github.io/Cloud-Native/blog/tags/azure-container-apps/page/5weekly0.5https://azure.github.io/Cloud-Native/blog/tags/azure-container-apps/page/6weekly0.5https://azure.github.io/Cloud-Native/blog/tags/azure-container-apps/page/7weekly0.5https://azure.github.io/Cloud-Native/blog/tags/azure-container-apps/page/8weekly0.5https://azure.github.io/Cloud-Native/blog/tags/azure-container-apps/page/9weekly0.5https://azure.github.io/Cloud-Native/blog/tags/azure-developer-cliweekly0.5https://azure.github.io/Cloud-Native/blog/tags/azure-developer-cli/page/2weekly0.5https://azure.github.io/Cloud-Native/blog/tags/azure-event-gridweekly0.5https://azure.github.io/Cloud-Native/blog/tags/azure-event-grid/page/2weekly0.5https://azure.github.io/Cloud-Native/blog/tags/azure-event-grid/page/3weekly0.5https://azure.github.io/Cloud-Native/blog/tags/azure-functionsweekly0.5https://azure.github.io/Cloud-Native/blog/tags/azure-functions/page/10weekly0.5https://azure.github.io/Cloud-Native/blog/tags/azure-functions/page/11weekly0.5https://azure.github.io/Cloud-Native/blog/tags/azure-functions/page/12weekly0.5https://azure.github.io/Cloud-Native/blog/tags/azure-functions/page/13weekly0.5https://azure.github.io/Cloud-Native/blog/tags/azure-functions/page/14weekly0.5https://azure.github.io/Cloud-Native/blog/tags/azure-functions/page/15weekly0.5https://azure.github.io/Cloud-Native/blog/tags/azure-functions/page/16weekly0.5https://azure.github.io/Cloud-Native/blog/tags/azure-functions/page/2weekly0.5https://azure.github.io/Cloud-Native/blog/tags/azure-functions/page/3weekly0.5https://azure.github.io/Cloud-Native/blog/tags/azure-functions/page/4weekly0.5https://azure.github.io/Cloud-Native/blog/tags/azure-functions/page/5weekly0.5https://azure.github.io/Cloud-Native/blog/tags/azure-functions/page/6weekly0.5https://azure.github.io/Cloud-Native/blog/tags/azure-functions/page/7weekly0.5https://azure.github.io/Cloud-Native/blog/tags/azure-functions/page/8weekly0.5https://azure.github.io/Cloud-Native/blog/tags/azure-functions/page/9weekly0.5https://azure.github.io/Cloud-Native/blog/tags/azure-logic-appsweekly0.5https://azure.github.io/Cloud-Native/blog/tags/azure-logic-apps/page/2weekly0.5https://azure.github.io/Cloud-Native/blog/tags/azure-logic-apps/page/3weekly0.5https://azure.github.io/Cloud-Native/blog/tags/cloud-nativeweekly0.5https://azure.github.io/Cloud-Native/blog/tags/cloudeventsweekly0.5https://azure.github.io/Cloud-Native/blog/tags/custom-connectorweekly0.5https://azure.github.io/Cloud-Native/blog/tags/daprweekly0.5https://azure.github.io/Cloud-Native/blog/tags/dapr/page/10weekly0.5https://azure.github.io/Cloud-Native/blog/tags/dapr/page/11weekly0.5https://azure.github.io/Cloud-Native/blog/tags/dapr/page/12weekly0.5https://azure.github.io/Cloud-Native/blog/tags/dapr/page/13weekly0.5https://azure.github.io/Cloud-Native/blog/tags/dapr/page/14weekly0.5https://azure.github.io/Cloud-Native/blog/tags/dapr/page/15weekly0.5https://azure.github.io/Cloud-Native/blog/tags/dapr/page/16weekly0.5https://azure.github.io/Cloud-Native/blog/tags/dapr/page/2weekly0.5https://azure.github.io/Cloud-Native/blog/tags/dapr/page/3weekly0.5https://azure.github.io/Cloud-Native/blog/tags/dapr/page/4weekly0.5https://azure.github.io/Cloud-Native/blog/tags/dapr/page/5weekly0.5https://azure.github.io/Cloud-Native/blog/tags/dapr/page/6weekly0.5https://azure.github.io/Cloud-Native/blog/tags/dapr/page/7weekly0.5https://azure.github.io/Cloud-Native/blog/tags/dapr/page/8weekly0.5https://azure.github.io/Cloud-Native/blog/tags/dapr/page/9weekly0.5https://azure.github.io/Cloud-Native/blog/tags/devtoolsweekly0.5https://azure.github.io/Cloud-Native/blog/tags/devtools/page/2weekly0.5https://azure.github.io/Cloud-Native/blog/tags/docker-composeweekly0.5https://azure.github.io/Cloud-Native/blog/tags/dotnetweekly0.5https://azure.github.io/Cloud-Native/blog/tags/dotnet/page/2weekly0.5https://azure.github.io/Cloud-Native/blog/tags/event-hubsweekly0.5https://azure.github.io/Cloud-Native/blog/tags/hacktoberfestweekly0.5https://azure.github.io/Cloud-Native/blog/tags/helloweekly0.5https://azure.github.io/Cloud-Native/blog/tags/hello/page/2weekly0.5https://azure.github.io/Cloud-Native/blog/tags/javaweekly0.5https://azure.github.io/Cloud-Native/blog/tags/javascriptweekly0.5https://azure.github.io/Cloud-Native/blog/tags/kedaweekly0.5https://azure.github.io/Cloud-Native/blog/tags/logic-appsweekly0.5https://azure.github.io/Cloud-Native/blog/tags/microservicesweekly0.5https://azure.github.io/Cloud-Native/blog/tags/microservices/page/10weekly0.5https://azure.github.io/Cloud-Native/blog/tags/microservices/page/11weekly0.5https://azure.github.io/Cloud-Native/blog/tags/microservices/page/2weekly0.5https://azure.github.io/Cloud-Native/blog/tags/microservices/page/3weekly0.5https://azure.github.io/Cloud-Native/blog/tags/microservices/page/4weekly0.5https://azure.github.io/Cloud-Native/blog/tags/microservices/page/5weekly0.5https://azure.github.io/Cloud-Native/blog/tags/microservices/page/6weekly0.5https://azure.github.io/Cloud-Native/blog/tags/microservices/page/7weekly0.5https://azure.github.io/Cloud-Native/blog/tags/microservices/page/8weekly0.5https://azure.github.io/Cloud-Native/blog/tags/microservices/page/9weekly0.5https://azure.github.io/Cloud-Native/blog/tags/microsoft-365weekly0.5https://azure.github.io/Cloud-Native/blog/tags/microsoft-graphweekly0.5https://azure.github.io/Cloud-Native/blog/tags/openapiweekly0.5https://azure.github.io/Cloud-Native/blog/tags/power-platformweekly0.5https://azure.github.io/Cloud-Native/blog/tags/pythonweekly0.5https://azure.github.io/Cloud-Native/blog/tags/serverlessweekly0.5https://azure.github.io/Cloud-Native/blog/tags/serverless-e-2-eweekly0.5https://azure.github.io/Cloud-Native/blog/tags/serverless-hacksweekly0.5https://azure.github.io/Cloud-Native/blog/tags/serverless-septemberweekly0.5https://azure.github.io/Cloud-Native/blog/tags/serverless-september/page/10weekly0.5https://azure.github.io/Cloud-Native/blog/tags/serverless-september/page/11weekly0.5https://azure.github.io/Cloud-Native/blog/tags/serverless-september/page/12weekly0.5https://azure.github.io/Cloud-Native/blog/tags/serverless-september/page/13weekly0.5https://azure.github.io/Cloud-Native/blog/tags/serverless-september/page/14weekly0.5https://azure.github.io/Cloud-Native/blog/tags/serverless-september/page/15weekly0.5https://azure.github.io/Cloud-Native/blog/tags/serverless-september/page/16weekly0.5https://azure.github.io/Cloud-Native/blog/tags/serverless-september/page/17weekly0.5https://azure.github.io/Cloud-Native/blog/tags/serverless-september/page/18weekly0.5https://azure.github.io/Cloud-Native/blog/tags/serverless-september/page/19weekly0.5https://azure.github.io/Cloud-Native/blog/tags/serverless-september/page/2weekly0.5https://azure.github.io/Cloud-Native/blog/tags/serverless-september/page/20weekly0.5https://azure.github.io/Cloud-Native/blog/tags/serverless-september/page/21weekly0.5https://azure.github.io/Cloud-Native/blog/tags/serverless-september/page/22weekly0.5https://azure.github.io/Cloud-Native/blog/tags/serverless-september/page/23weekly0.5https://azure.github.io/Cloud-Native/blog/tags/serverless-september/page/24weekly0.5https://azure.github.io/Cloud-Native/blog/tags/serverless-september/page/25weekly0.5https://azure.github.io/Cloud-Native/blog/tags/serverless-september/page/26weekly0.5https://azure.github.io/Cloud-Native/blog/tags/serverless-september/page/27weekly0.5https://azure.github.io/Cloud-Native/blog/tags/serverless-september/page/28weekly0.5https://azure.github.io/Cloud-Native/blog/tags/serverless-september/page/29weekly0.5https://azure.github.io/Cloud-Native/blog/tags/serverless-september/page/3weekly0.5https://azure.github.io/Cloud-Native/blog/tags/serverless-september/page/30weekly0.5https://azure.github.io/Cloud-Native/blog/tags/serverless-september/page/31weekly0.5https://azure.github.io/Cloud-Native/blog/tags/serverless-september/page/32weekly0.5https://azure.github.io/Cloud-Native/blog/tags/serverless-september/page/33weekly0.5https://azure.github.io/Cloud-Native/blog/tags/serverless-september/page/4weekly0.5https://azure.github.io/Cloud-Native/blog/tags/serverless-september/page/5weekly0.5https://azure.github.io/Cloud-Native/blog/tags/serverless-september/page/6weekly0.5https://azure.github.io/Cloud-Native/blog/tags/serverless-september/page/7weekly0.5https://azure.github.io/Cloud-Native/blog/tags/serverless-september/page/8weekly0.5https://azure.github.io/Cloud-Native/blog/tags/serverless-september/page/9weekly0.5https://azure.github.io/Cloud-Native/blog/tags/studentsweekly0.5https://azure.github.io/Cloud-Native/blog/tags/vscodeweekly0.5https://azure.github.io/Cloud-Native/blog/tags/zero-to-heroweekly0.5https://azure.github.io/Cloud-Native/blog/tags/zero-to-hero/page/2weekly0.5https://azure.github.io/Cloud-Native/blog/tags/zero-to-hero/page/3weekly0.5https://azure.github.io/Cloud-Native/blog/tags/zero-to-hero/page/4weekly0.5https://azure.github.io/Cloud-Native/blog/tags/zero-to-hero/page/5weekly0.5https://azure.github.io/Cloud-Native/blog/tags/zero-to-hero/page/6weekly0.5https://azure.github.io/Cloud-Native/blog/tags/zero-to-hero/page/7weekly0.5https://azure.github.io/Cloud-Native/blog/tags/zero-to-hero/page/8weekly0.5https://azure.github.io/Cloud-Native/blog/welcomeweekly0.5https://azure.github.io/Cloud-Native/blog/zero2hero-aca-01weekly0.5https://azure.github.io/Cloud-Native/blog/zero2hero-aca-04weekly0.5https://azure.github.io/Cloud-Native/blog/zero2hero-aca-06weekly0.5https://azure.github.io/Cloud-Native/blog/zero2hero-func-02weekly0.5https://azure.github.io/Cloud-Native/blog/zero2hero-func-03weekly0.5https://azure.github.io/Cloud-Native/blog/zero2hero-func-05weekly0.5https://azure.github.io/Cloud-Native/blog/zero2hero-func-07weekly0.5https://azure.github.io/Cloud-Native/calendarweekly0.5https://azure.github.io/Cloud-Native/cnny-2023weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/aks-extensions-addonsweekly0.5https://azure.github.io/Cloud-Native/cnny-2023/archiveweekly0.5https://azure.github.io/Cloud-Native/cnny-2023/bring-your-app-day-1weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/bring-your-app-day-2weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/bring-your-app-day-3weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/bring-your-app-day-4weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/bring-your-app-day-5weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/building-with-draftweekly0.5https://azure.github.io/Cloud-Native/cnny-2023/cloud-native-fundamentalsweekly0.5https://azure.github.io/Cloud-Native/cnny-2023/cnny-kickoffweekly0.5https://azure.github.io/Cloud-Native/cnny-2023/cnny-wrap-upweekly0.5https://azure.github.io/Cloud-Native/cnny-2023/containers-101weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/explore-optionsweekly0.5https://azure.github.io/Cloud-Native/cnny-2023/fundamentals-day-1weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/fundamentals-day-2weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/fundamentals-day-3weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/fundamentals-day-4weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/fundamentals-day-5weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/Kubernetes-101weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/microservices-101weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/page/10weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/page/11weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/page/12weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/page/13weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/page/14weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/page/15weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/page/16weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/page/17weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/page/18weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/page/19weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/page/2weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/page/20weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/page/21weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/page/3weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/page/4weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/page/5weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/page/6weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/page/7weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/page/8weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/page/9weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/serverless-containersweekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tagsweekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/30-daysofcloudnativeweekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/30-daysofcloudnative/page/10weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/30-daysofcloudnative/page/11weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/30-daysofcloudnative/page/12weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/30-daysofcloudnative/page/13weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/30-daysofcloudnative/page/14weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/30-daysofcloudnative/page/15weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/30-daysofcloudnative/page/16weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/30-daysofcloudnative/page/2weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/30-daysofcloudnative/page/3weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/30-daysofcloudnative/page/4weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/30-daysofcloudnative/page/5weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/30-daysofcloudnative/page/6weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/30-daysofcloudnative/page/7weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/30-daysofcloudnative/page/8weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/30-daysofcloudnative/page/9weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/addonsweekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/aksweekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/aks/page/2weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/aks/page/3weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/aks/page/4weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/aks/page/5weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/ask-the-expertweekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/ask-the-expert/page/10weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/ask-the-expert/page/11weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/ask-the-expert/page/12weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/ask-the-expert/page/13weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/ask-the-expert/page/14weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/ask-the-expert/page/15weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/ask-the-expert/page/16weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/ask-the-expert/page/2weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/ask-the-expert/page/3weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/ask-the-expert/page/4weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/ask-the-expert/page/5weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/ask-the-expert/page/6weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/ask-the-expert/page/7weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/ask-the-expert/page/8weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/ask-the-expert/page/9weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/azure-dnsweekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/azure-key-vaultweekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/azure-key-vault/page/2weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/azure-kubernetes-serviceweekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/azure-kubernetes-service/page/10weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/azure-kubernetes-service/page/11weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/azure-kubernetes-service/page/12weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/azure-kubernetes-service/page/13weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/azure-kubernetes-service/page/14weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/azure-kubernetes-service/page/15weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/azure-kubernetes-service/page/16weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/azure-kubernetes-service/page/17weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/azure-kubernetes-service/page/18weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/azure-kubernetes-service/page/19weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/azure-kubernetes-service/page/2weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/azure-kubernetes-service/page/20weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/azure-kubernetes-service/page/21weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/azure-kubernetes-service/page/3weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/azure-kubernetes-service/page/4weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/azure-kubernetes-service/page/5weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/azure-kubernetes-service/page/6weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/azure-kubernetes-service/page/7weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/azure-kubernetes-service/page/8weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/azure-kubernetes-service/page/9weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/cloud-nativeweekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/cloud-native-new-yearweekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/cloud-native-new-year/page/2weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/cloud-native-new-year/page/3weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/cloud-native-new-year/page/4weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/cloud-native-new-year/page/5weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/cloud-native/page/10weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/cloud-native/page/11weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/cloud-native/page/12weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/cloud-native/page/13weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/cloud-native/page/14weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/cloud-native/page/15weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/cloud-native/page/16weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/cloud-native/page/2weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/cloud-native/page/3weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/cloud-native/page/4weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/cloud-native/page/5weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/cloud-native/page/6weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/cloud-native/page/7weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/cloud-native/page/8weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/cloud-native/page/9weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/configmapsweekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/containersweekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/containers/page/2weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/containers/page/3weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/extensionsweekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/ingressweekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/ingress/page/2weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/kubernetesweekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/kubernetes/page/2weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/kubernetes/page/3weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/kubernetes/page/4weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/kubernetes/page/5weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/microservicesweekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/nginx-ingress-controllerweekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/notaryweekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/notationweekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/persistent-storageweekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/persistent-volume-claimsweekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/persistent-volumesweekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/secrets-managementweekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/secure-supply-chainweekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/serviceweekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/windowsweekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/workload-identityweekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/zero-to-heroweekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/zero-to-hero/page/10weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/zero-to-hero/page/11weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/zero-to-hero/page/12weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/zero-to-hero/page/13weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/zero-to-hero/page/14weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/zero-to-hero/page/15weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/zero-to-hero/page/16weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/zero-to-hero/page/2weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/zero-to-hero/page/3weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/zero-to-hero/page/4weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/zero-to-hero/page/5weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/zero-to-hero/page/6weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/zero-to-hero/page/7weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/zero-to-hero/page/8weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/tags/zero-to-hero/page/9weekly0.5https://azure.github.io/Cloud-Native/cnny-2023/windows-containersweekly0.5https://azure.github.io/Cloud-Native/Fall-For-IA/weekly0.5https://azure.github.io/Cloud-Native/Fall-For-IA/AskTheExpertweekly0.5https://azure.github.io/Cloud-Native/Fall-For-IA/calendarweekly0.5https://azure.github.io/Cloud-Native/Fall-For-IA/CloudSkillsweekly0.5https://azure.github.io/Cloud-Native/Fall-For-IA/CommunityGalleryweekly0.5https://azure.github.io/Cloud-Native/Fall-For-IA/HackTogetherweekly0.5https://azure.github.io/Cloud-Native/Fall-For-IA/LearnLiveweekly0.5https://azure.github.io/Cloud-Native/New-Year/weekly0.5https://azure.github.io/Cloud-Native/New-Year/ateweekly0.5https://azure.github.io/Cloud-Native/New-Year/calendarweekly0.5https://azure.github.io/Cloud-Native/serverless-september/weekly0.5https://azure.github.io/Cloud-Native/serverless-september/30DaysOfServerlessweekly0.5https://azure.github.io/Cloud-Native/serverless-september/AskTheExpertweekly0.5https://azure.github.io/Cloud-Native/serverless-september/CloudSkillsweekly0.5https://azure.github.io/Cloud-Native/serverless-september/CommunityBuzzweekly0.5https://azure.github.io/Cloud-Native/serverless-september/ServerlessHacksweekly0.5https://azure.github.io/Cloud-Native/serverless-september/ZeroToHeroweekly0.5https://azure.github.io/Cloud-Native/docs/category/resourcesweekly0.5https://azure.github.io/Cloud-Native/docs/category/videosweekly0.5https://azure.github.io/Cloud-Native/docs/resources/devtoolsweekly0.5https://azure.github.io/Cloud-Native/docs/resources/introweekly0.5https://azure.github.io/Cloud-Native/docs/resources/languagesweekly0.5https://azure.github.io/Cloud-Native/docs/resources/serverlessweekly0.5https://azure.github.io/Cloud-Native/docs/videos/introweekly0.5https://azure.github.io/Cloud-Native/weekly0.5 \ No newline at end of file

    =26xXL=llz`nH~9Wnbppa#=ha)`=Y9o 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 literal 0 HcmV?d00001 diff --git a/img/fallforia/blogs/2023-09-27/blog-image-1-3.png b/img/fallforia/blogs/2023-09-27/blog-image-1-3.png new file mode 100644 index 0000000000000000000000000000000000000000..2fbc0b38cacfbb2db1b530922c8029eb61d26eae GIT binary patch literal 53757 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 rCAeD39yhgrpD!|@=6MJ7wlHj!J#~U^E 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 QdEef;VgfU+G=e|;Cm0)qZ~y=R literal 0 HcmV?d00001 diff --git a/img/fallforia/blogs/2023-09-27/blog-image-1-4.png b/img/fallforia/blogs/2023-09-27/blog-image-1-4.png new file mode 100644 index 0000000000000000000000000000000000000000..b447e97788aaf0a733a1fe42ceac18e4a43c9761 GIT binary patch literal 68277 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 B7D503 literal 0 HcmV?d00001 diff --git a/img/fallforia/blogs/2023-09-27/blog-image-1-5.png b/img/fallforia/blogs/2023-09-27/blog-image-1-5.png new file mode 100644 index 0000000000000000000000000000000000000000..c170935a3bb3aff8f3621c9253d93a47b5ce74ab GIT binary patch literal 211808 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�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

  • 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
    3=_k~} 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 K)JU2B_`d)-RCKZc literal 0 HcmV?d00001 diff --git a/assets/images/blog-image-1-2-49fb07b13652bc56001521b3312fae9d.png b/assets/images/blog-image-1-2-49fb07b13652bc56001521b3312fae9d.png new file mode 100644 index 0000000000000000000000000000000000000000..0b28a9a02f41601e73d2cd0165d8ba292ead2912 GIT binary patch literal 158984 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

    ;5@J)fp-?z_|VPU2)Tl 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%0DfSaah0it^A%l|k@Ym>(NpA;_a%4z4wwshh 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%0DDKvhzEiU3 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 s@c%W8+UhiXe4Ok?$i0cd#mn` zsi`|t_s?`yS0CBkefB%|Kd;o$ zzc25v=S?D8IjJ9N?1vgy=c->iF4!$PQH60t@)mKD?>XLtVN0V>n})-YQ$x3+G7Di8 zP@r2hmW-V^&=X-%Bxuh6Hi`HjX|juq00OAY|LB?-Mxm$g=j5xEHP*tLPmm2YTIRAO z{gQlDMvz@3UN!oF@}-;H*xnS5$C0EW2Pb(ZB>uE5ZsR@YN=RxLIy$Qn%!)zO^`*@F z$1H2czt=vfwug)evgHtZTIqNZ!N*`VDPaq}4SYqmMw~T%R^c5U0bv)+O2Orep7nNC zmu=iSi|WA#VsU83iCdMJ$*I4ryv7*$zrdvPdvJb8?nDlJrl_>JFS0vF2O&@IU%5T5PXzB_~yqgcBXRw^~MV!DOYh(;Xh*}kxPXQ9@p5b3iEs|jE|TD%aa>L;{6-+1laDLm+YeQ8H2%pn%aD$z8~DYy*EKdLn?H#KbkQmT zTjA@=Fo9m-wLK1TpAa^0PG11{ZAaC#tH3G|-(?sFd*yng?Zkz_-puw&peLHo-U{2*y~QQ%%G zhG4g>jo_?fKn#cYE3px?a1jj$>1XcK&DXoCSByY!BV5A+2kbLt8X7d`L$0-M-`3|Ea_s?mH<9ojfP0 zvUT`D<+W~wz*oQt>453|K6-3?`yFHrS@FmWPjA!|_CzMra%{`F82$yR=I(-pf9=TA z1bdD-OX8nV_FgxFWRW2RMau}Xh#nmcB6hm zpSa;?8yehXBFk7SX^kD(M{CT#0!P*6NyL;wv`Aro`S|QqkTD5cS>fzifm~0}2=|wDKvwSz#5fBC3{RFANENr!j%NO~# z`xl{k!FDL~okoH8EPfsen8Tk-P*f_qw_pVzqu zR&A(B0eh#;%!dl?YX*Ldd-2gdh+%85FdYxEZ|!p+CQeQvdVvp#ccX$7OO(rZ`#c!4 zqDrQ%bGF)kPYc-xJ0tM64 z%SHg~%Cu{aN}AL?R!6Yze9*q@UZw+wyb2ImKc3)xZ{t;j%n5d;KJRYD=`Hq#KM7bo zOzwoYLH@G9b6dub={IVA81AT8?dreNdmgJb{8yZ(#0Q1@+4(fR)SQomVDau!Tu0(f z;R$&#?X9@DW^5m!1o@&ZN_V`cxQz2NV+`aJ3@^kHmtRh;@oe^VPD_WNKu^n;oc5iU z7o)O3TnF*g-nsz!?UJ`<}IRiRIYI z_7kOR!-3`-8#JR_BcjvZMnR!`>$rB~tg#Y(g;LDJxqm%oaB z=-jld?H1ry1_{}TQ<3$~2A@d0#OZzRA_kvaniArW=5OOM0d^YW%*twW!a=y@KxzdZ zi^;pkj;)Vv*yfrIG+KW)&NtS5qh{w#W|0}UJv;7f`in<>yQmQwEC#z{;UoD7*weDdV8=E6Mf80LE@} zGt$wt)2)OeoNgJ>g5n=xQM;d5J?LsI_!Ktxsey)wc6=CNe4{3XEExSYo-W!S2(#v; ztn(x+?IfW$9sA<&DG26vo5J2n1~`Q!5q^7CZHquC`;7X}q>gP%hFM72M6>uQv8nc& zBx8M?Q^xW442n02)7#GD(K>1g7K*er9_@8px_{;d$96n|;9qmXzUa{iqWyaehX)`0 z_keUSI^w^lxMW}x{*JZmsQUk@Ns@rEw>QbPdwto@$|C7-Gy-N*OB}`qTFYo%(PaaPU!w99sH0RJ z25y?l7GiTK3+Ts?r_#2ID4}EN6Xg)pvUn_$T91kUmd;tB58uW8a)YvjJt5igo}{kW z_wTic#Wl{E8C$B&KKm8Ry+n;WTAA@Y9_=4kcP|bn0+d@W0gHN{rCW*t^fh0qJ6hvH z=RQqMo@yCsM|u%13yUmI&CNyMG-&Ne49n2#!V#4M8YlVcBl~qxGyOJpU>JmzkYMlvzdQe`T<80ce!m?Hz zWWm1mbQolYZUS+M^-yC^A_l9{LC=&BU1^bd%YIH_{@7d=ZOB0OB{2m>*WxN^g6ztn zsS|-&aWQfJ=kAj!ri4U~>ZZ2nKhZYj5XkmUsy{3wkU;@i5E}(p-w<`4tT9?eb)GMQ zTTU~xg@k_a`nRiQekZChfqw*vnYx^ezgTbd0c^mpw4sk?TWQE zOx$+ZR+f|VkqQq!-U0A8zDj?4@-MGz2i2S&ob2kYX=!WA`iFsXi(AXl*A4Tqf!A-G zA~P_Y2eK=G4`;&WSA6PT_bid)x7FNyU6C(C-*iXEDvkHRD9FBgiz0-_;8Ns8T*&s- z6?!BxoV{bESu%yl(PQ^__d2eggesiTa`9K1eQQ_Llpb392MApFoZA3IaD7FB(C~Y1 zF3`*z5D5X2s7yze_=@(^mvZuR2mhhiJRcCz>6u+bkuM16-LU^;%K!e`qGp56xj% zv3gc-KRng&i}xNv5ai|iIa_LIo6AK1Bp$)@w>3p`#Z6Sz>O83q(Dpe`@r5)`yX$+h z5nR)SJ*}!#Pp0MM#2IK+`IE8l{3!iFof2N>M4JbDVvYd&AnZIwigRr|KA~eF6oc}K zVdt9TvTdG>X7hw1DA=K3_z2Uyj?LsZUFeFDVPt|84k2bDP-F!y@bVGl`^ay8{FRf< znBex}O;JVKd0H=pqF+&X-HC=K9r5-qyhA4QK26OH&eQd1(sWbMdA~LBx>iVZy%4_R zT3+6X-P<^J%G{2$ewGi0kg5{ zY!-t<%#ypyMrY$9SjqEz@gb6QvV76(!V%MjyIG@fi=veGziPLo0VTIr$;*w=q4BU@ zhpS<;$7D&fb#l5JND^zxBFb!0ymLWU+KmOK_#sk+&CG4B*ti=ZX8cd~?EsvFEXL0W zmbGz(1tpj)2C9SWtr5PT1FZEW%t>5fo7$0*l3Sz6K94g}SrX8-F#g8i9#BP8T76K* zJRzYVc0>!CmEj{EwCiT021LK`J7d}Qjvr7@A3JRa2aA=@oq$h>JWWz&BbMfT7L5Y2 z0-v5w&+Fx3mP>Y%59qN<65n z^7B%Lg>5q1u>R8`XmLF?PDxt4_{H}_5be%hjGds8np$LJqzHn@hP_~uF;UXckP;^k zU)ShpT-m8W^fBA)d5pC+vqYGyBtCQhot>E_aaRogAjDv+W!_c>ynh%jP6!@`h!0?V zqC$7ELX#qbnE%01Ums&uW>L&0Jlno}k5G2*heDUZ?ELBU9E{zsZIyh=oXEi|%{gZE zgZggx7j=mPCEj&A=*8FjZFLHE>F_OVwjp8QN*$XDMu{Qf;ueyMc8`_<>_HD4x;*iA zyEn})2<^#@Fgd;CY@Dk1#RP-*^+{+H7kM$r3}0s>;@3y6hRU7V%WX5aW}kuu36u(t z1mc(&0*rePnb6Rv*mxXwR8PvvYjbZUP_Cez9m^As%g*uH2iOxY)bodTio|Q(Zn`K} zbL3O*^%{4k$lfnmK=jJa!!8m^A>}t9L*8yl+>@mN6F*DLgAxIk5gNRwKi&7Z(-n0l zdW@^L^RX1hhv^{)EHlS3n&CC>%wOomAi*#g12 zb&NaEtIVO>y*ZO9t$RPD~;Hq7)f_LH$+# zPWA_lU24SNqZ;iJrEEKtknPXaqO&oQVg!tbKE;+Xat)V4y_vsgPk$aaCDItNmC3os zG)ID~3Zg+bLg=3mdQ`;FGZHzSu*|zumXTNSEGPdilSGu(M$A#O2_i3t>A_A@*-wm{UQRhvIp zafW3VCfAEVE+gqNRv^B&e+tJbm(N6b0P=(M#3R%ZXmz* zjRg`53yaR`EBWa!FEi`op@2paT`VJ4i+$41w8hyi__zg=O9Zf7szrpL_hCyv3xX&v zW)6HNFWwELm!Ucpt`f{+N)hK|%T!f&!?S^jGK4O(|+W zBH8^YUDymdH}zSjpK}WwNaA7&eGqKLB)9&Q<aIfOW*T`TT&wdICh>UVh2PP|(q{%5NRGNF^}F{2dV_9Bveva_zMP0yknMYzjI9#XZTwA~F9FkN zrANcgG0Jj=hLn^9_(kOoMZP1Eb zYabg3AB}1NEN$LUPrv0WpzDkgR9)EyzJD>-#f7rAIFk5)w`U9vOAxH;)VWwHq!gSIp<=&dO|q6CCmmulOZa z{Tg^S$PS?>DnMf`TUzAHGhr3gM4F);SHF^jL@uoIysx|7H6=^O%J)5q+bb^Ei&jw#D1Cj0 zUlQ?q)7G?4Tq0CPibO?XK{l_<)QH_VEZqt-J-|F@Woc;>Uf>LenF*W^#M15#tEf#L|z?`}+%^l~W3^SL4Vt;>MT05QR?VP>(oJjDnBt?T0p|PI{ zCC`j9HaVI)&adD|$3BQ4u|i&(=*!!q$fhQeUJ=o-_pN89n_KqCBnix%af|~uH-rrh z2!j44Q@388c)wYG>*SwW4a_)%)m4}Z&fz}ncI{_o%K4^JiZ@Xw(xg|EeB?VHLgrRH z&hK$ELbRWo1*b`b%@jMZvR9e9woz$B_yD7wHP689P>&A~q(II3&9Dok_88$BX-B!6iHB|dsLSBojAcZOMWb) zcN8RuX-1`X3eCx}UtC%a*yC}Uz@32c^oY9Q^(=e0)_J`0Rb2p8cI5%}wSU1z4UL!J z1e{fan4`azl$jH(1Phf@(UjE$bVd}x^V)||W!W&d)=PfXROa7Lw@!|I&&KzAPnHnJ zZ+DZ9OG2_6ouoeW!H<0$u^ze!cAdAVUB&<4u&Ticic8L7)LH)!5nKUF_KK<58VmHi z?_N{7nGh$MnaPbhua!gfyuEzF{zSc;l+hePHoc6(MB05W3%=g%-7+WnF$a69LI;D?2J)qKXP-el;ND zqZ`Arq}Bs{rSc(dwF@N+#W^PyQ!nNQ94NLK#VM@R&Ft11>u18k+2kyaB`!3=F2Uhm zJRhIqBJ#x>bpF(}ht)PITpxBp43O1yZB&?8;>t#V|I-5UP)(mh{h2Acl~AwyeMN}Y z;A*kA&zyk9^y_5Ppr`mY9K5$UFsd|`NqU`s_5SV=`^XBs-s*}@^w_P)ge?PI(2oB4 z{R^86FJ8gB$=bS1ftH1Y#3m{_q5JIYkE-znzt_(U;&sawt{3BgX6j;qS=-*|Q*k8n??GFQ(w{ulk{Bivh-^yp@&5EYfpI%ccF#~#zX{vX=V4BYos z4x)dxp`~TMOvlf-|0xdrO@BpsK|2`0KR!Nw_a)%PlIjp286ONH5&}K{k)FMdi**e! zYQ9VFf*M9iw13d=U%yP#AQH-Kz6m|K?yO@IrXC(ht}G&gco@_i9$kpQVp@-rrvO-c zGc)*b#QP|8EQNv&s;6f^_0J#TLZAJoh<9|iaKY^33Dq4fvD?}KZ;lJ8=P}neF&2H^ zUssFh!w8H>*0nC}g`!gL=t2LGg_X5;b(K6lJw0H$1_X*48&jRi>F8S`H?4C7A7$ev zr6hMQc+TQz3q%M!rJqW)jli^v2qd-_sFR;U&ek~aMj7bovGFLvcX#O_HF(eK>+G-o z1UnKTRf$CDi?{OaO8RiC=h8x=@v7>tAu^|tf)Zlg=jTi0O8ViUEjl`S)b5ktUgUPv?3~;K z5ctQcxS#Bn?U`#ZP;D>MW=PK5{Owmed6(ftSQN{ZBuawlOb`va6Xp3cCSgkw0bYFO zqF^eP0fzK_V=eAg={_om_(MyOa%rqN2@&?Y|~q|F)r#LsO^S?*Vc3KVpX5HxkU99;;7uX7ylBa*yQ?IMiJ=q zVig&gIXEPf7>I2U>i25z36*(EM{jKIm3b;IBGQKm2wV;5T9Y4x z%9me0z=4k9&vaDkcVbz~1!Ju`7JHTJ0#8f5MVIE_ z&335fl~Bw(NbLD?PJQ(h%L#ldTA42L1!p9kslF9i#IN6|N5@R0IXebBU;ELEs`3vXJTi98(RD^lZ>o$@H;1oBUUrPM)n) zW})~MTeM=rEyhiAg3AYmx8};^*aNU=2@jLLu!T17;Jd zm zHW#2+e7Q|J>v+v)O#9lSD9t@?bY^enZNv0V`5mRiB|SC}TZk)Ih7d(aA8=gUDtdK& zbGEhc=~o&fN0I4nKMT zgSJT7#>b$V7;#a5F=Lwo1SLeC*@^1YeIQPWkzcfkZHcpIeQ;I{4SL&`>N?~rSvwgH ztt+u({%)-Sl0@O``w_iBfbc|O) z87v?JyY7AYrl}Q*Npp=e8xg+%xwf#>JRPFgoCfDt0ojplq#~JF*(2#GQ3^=TAek~} z&^s@x`DWM6WAkvN_V8nDf0A)^v{OroL|SzvO!;bqRTy%ty`IlK#Bw8k3<&~STr;@H zZNBMcxO~pDKFpnrF0zYVE3(OL;dlXByaRK;C`3NYal?k_}op?WV9Gqg*YysSC%E*eJ@X! zvlOntMm4;P{ren-zsdE!U$oQr}NoOiIKD~1;%)oqgA;j(rL$)Ci zxe6qCuDXxCMjqSIHsR|rMyd#LH{Ey4&_7a1EmTM^%Zld3LZn$2QpEZP5iMOZUKSH5 z$T^AB-STAX7$SmBSA7`X?wV&r_I6S88o|2efU>p>>v3fNxmX=vtV*rRHEjzSlDX^IR*A z9#<99t{}!x`J<%_BH2U83kJ0T4Sn5ZJwl}K!W$geBUZSzW7yDrtD!qGm~BDb{URo8D< z6BkB}FImq~YapF`Qku8>7VQ?#3mjH&IT#N+6Zq_bntN@xxgPmyr{HhxzFKftDgV&!D@DnW~*<*p@%Q-v@@d4zGikUoV_C0o;s!U@?~Qdi{Moz4ZNj3!I0xzoR8u$>#TM< z1|_e>n4oXo#b2a@)(#Ya8lPmS^YfcGzr-87SxXJcX(h*Z3JAgC^P3daLE;d?n-a9= z*Sp-esgQAZp!J~#`L-&QUARhGThcx-_Y@rK4n07&lH(~Mp`^NWP4N+u{!q^#1}beZ zeucobRB%l;Q^iznN-r+86!>WgI#eQ7(X0bJTtXLZF~U+U(9`clTI}__K;1kbq>X?! z6%=ZdUt?FV{Ag%fiRTsn)Z;`p#VDgs#j)zY~*CRq|h#xgn20p4f zFbF{-NN>=k+h9d>icM3jOoR~lW7oc*a|9(Kx~Ds(ZSa05rJ!?7_|LkcfAW)p_x_0w zE`3v^*vQB*u$~SbB{C9OQBB$3mYn9Q8%DqgO;wIfwn1Oq+Bc$Sh?|(%hPrOErot9! z>Sh;(BdgoGkm4;xO<18HBi=h7Ek-h{jm>**Q}Q|E9Ii3?DNHg{0tA6r@eUohk9Z9D zS5780bNJ2GMOG@>BgRFxkug8TAnz@Eq;=uz{phJ8J93bon0=_hH`~wf4IETvfyHnh zo^HuJ0d)4_GL!gdcmwKp#Nv{~fzFUO3-Uw_FZJD4vftY7eFUwE-vE8kL_(P+HPGc> z^(o5T7OVCAn8%LP{I;Vjtjd7@B*mo|E8ull(n^q;?OaSt&L#< zB_zn(Wxcl5gM7osvE*^ei$cB!Xb`k(B8MU~JDX|0)K@2{&)3S^S`yaOxK4_7L_Sxb zYX0~^EyjbBfb=Vhm84%q4-KIA1$d<@{pB4>j%s}J)KE#I5P}*HowZ&#EiDi6uLX%7 z1EUiXkO@ecv4<@%2upU4>wCPhx@k0wV1osGysGr?(|nj~%Y$Zu=E z&!goUz8bG~R0Su637QZ1${dUwIkSBm=sJWUpKA{a%su1vGN~*(k=^Sv2q$%?LY=mm=hkd= zY@9|kE?2TNydv@O(Kj`mcY!3o*$^!D~qQ!gTFIR@{WdfU?|5#?vm0pRrmao?t0X>fKUg5<(7N2F+7LXH6j zz_v#|a}59F#+H^LL9HhPY_N81_s%>#P??dATST@o zd$EpC1ORrhM@NqInyvfvCPT#46hl=P(VAS(f4Zn%JWP{{mN-kdhe7QwZgEOl#570c z?tZ03w#ffaHbcvc!a9(fFGS(9aELp3k`7NqPur(if|3IFU8UHyr4KyP&_uSSY1CN} ztbPOC340yznk)R99PzIs<>V>hAwploaCS^izAxc{Knj(LH4VWt+hxY_@d*uGU05m( zwYY=?@SzjM!O?MeV=3~h3i*9WhIe#ui-8W+HnK9j7%1Y0S!y8X$Dk6gz(wG1IhK4K zw^-5{EuVKunoAt^LWai*T2dta{Nqcv9kB_&$nmeMpYMQ(3DfjvF=xST@m!El9xi$x zc08-2vUnGh$7qVLlmtZut?(&NEDNQXkP$v$qIcZ+~l_ITz>Pqwo_!Fv_fxc%MtJe;(gW!wIPjhP$HhG__@7RgHckp~{7J~}+?WE-+~ zwwbQD1Dh)%Q@NUl6j7)p2oVLlv)M3$4#h}bh_H=X01) zq_sOA-jQ>gCO9xv2@}$`U26FGjnh0Csx#5R;uj zpJlO7!WT58{<~XqJNwx+`5y`fvmqg=)F_d=lz;Z#y?DGM@?SMY#>Cwt&6k0j&kiW$ ze{c8tFHQq%XSQM;O)6`qNJT31^B?L)2VcudApF{_`u4C268ION`KuNE@3G-QvO1v- zjE%1Zf~l1>1!ha$d6i0Ii}GWEdxl7tLxANcuiH;&nXye)7Qol0IR`l$8{3Yq#6(#ntA z(yn{dY5d5I>$K42VsO*U9jhDK0-6A@?>W3C7H^i14g4F-N-RS{>5u0Vg=;tX*Sd z%A)KY&DJKi)dDoz?qYDy%%V{*#}LL{P_{ z@?r~1fMqi6{4ixWz}kEP%Z|FFUzsyYLzv^E#meRDG)!{!n3A+`~e_4Ci2p!8ON>E8if8&^x#;|=HdtruEW`&M)-BOmb zDD&Y2;f2p5x{m?ue2a}i+roHg*lDmqGFu(6QhWrW5{O)vnXm4^fbY;K(+1p4e7Sr| z!~tZsOz(TPH%B19d{kT_q^}zPOn6ZvFg0BhWrX;i+SD8?ZF_|il`>dqoALpP&_%+A z!0flk8FOy1Unlu5qT6I`F2KZ#_N~HS*vwDhuk|<*?Unt`?Oyn4w}^M`vxOk!jmOEj z3QKf)ld({EC>F!tAz7|xnQV#kaLZYZJAxUEb7kK+k4v#F*-5KM8tJWG1 zxCC*K$wvfDd=1M_Q_KmqU7{xh=>ci_SnhzqiZmh&)=9 z^$ePB9F|^VeE<=Hiv0x@5_=8~T|FkS8#+~KeYHXOv=y}eI;Dn z;=Vs=JiYprXb5Q52BP2#R=s{sajic|{$o6tg@0Vzr8X*GcwqP~ASl`Fj`s^`er?jtw_Daj z_8_xh7=EteJG(O)W-Ja)@wx=E7uu(*j+k#!XOm2o;wAlvph#_a{Slft06&-KmyrG# zrqk)hh@GEd*!!KM?C{|)KGU)?Gp45 zEjIi9q}VmyNi9^;IgYOD8_+%8GkB@bfA-6^>ap3o)HJPmO!a|>IW`It@8_^U%LFn{ zyCdv)C~QE;x6H;CD?YNzbpz2uDz(yC6bzP}MfdV^jEnoNUb&Q z(%OOCHBUdkuuxsy6yLVZGX>P%*+L&s7!VXAdhr7_#>((xRj=y*B}MN`FX7=nf>YAN;ATu**42z0I)poZe1d_2@kSp8lu>4(#Oz#;8f$BRh(7O4&XmU>eqm z5-}`(*6SRe4=x+H%*C*jQh0%-X~uGHUI(mHZG=i`@?gsK{DZXy#8KgcZT(I*Cr*DB zJKjekfarR^t#->)`Dol8&12RkzJ}aV-4!(bAepI@uG+Dw7VX1)%j~y41`ftx3GXVLjOaIY{6CwSx<@rve!5e=9&j({s%rY<1Lh zO)w6}s)Mmt;hwU>!n+*BF);pf2WRfC5xzP|4-xW{S$~4m0&W3Mn;$LiWC@JlImb06 z(1bmLy61YR4$|5dx5a&`BO1CjcV^uR+c;sx^V$_FC_}*9Cb<>GbR2}@j_=PB*t)M8 zoIB^$5sRKz!6#(VF`EwU8#BVjFZalx19j>{%lB4b|5N{``${tTyP+JjhBg@Lyzw&> zC8sV6`3RU8T&tkE{YNUwHVz%vprxPf>2B(Ue7GD)Z; zV=cNak&!{8Af`<%(yxdvpzaR*#SxEy_t zVD2I*v6Ff8+^4P1R~=c{y?6Ivh=O7!wk)2f>#^gR?!`w2fb(rElgv@!do5FWPMb0F zBW1vOB{}W`f}j&dH^b|*C-658*sX$0{`8sjaMNKii#hTp{??#2c%3g-=~n&I5&67@0v;TfTSKe0u1RyiL<; zw9kRBd^^uaT!utJ6C3v^H;A=4?jQJmt$rcK8l?+$tP!|KHaI@ju;c|h0kWUB`z z@vj$BE5<@@5_d*tD{6GkY8sdq9~#ex_PCu#Ojz8e(Tl5IB79#co=V>%nlVP52emg~ z&Yo`cQUzXHSu0#icf#fD%)#8AR27m=hizZ_Yer;BZjB_Kho>TK9a@Ds{rV@t>W5Xu z*tsU9IYKDmCa#JVco&H<9hq2N-yY$g-i{eLLw%oZv=u?Sa5#_ksCNnmr1<6J*%wp4 zBj|XZbCBg5H*w%3!rJgf{_uX)s~hI_hX$bs2-V50^X5S$>9SJF=o^FhOOFItlQX;+ zVC-ZDzY?Mp_OmUqV@Raf%m?o=+b(l|zi)B3i>IT#7@m-|)1cNbtfyE8Qu)4+uZJ-I z`I>fdN2R)8u_yg>;!Z_`+@B?KQ7^a0*-2uzX>Nr*beAD8B-IXnTun0zXR1pnWU%%LrAw^zJC-As(c#${fKL;Gq9{$i9l! zzevoY$ki15dh?wmJWT+K)t>0 zQYFYP@?hfyu3YJ}Dv>2;ulA?twGj9w9Mp=!oaLBz((5y$x(5Flh7#qZpy$O^2rT<~)G zih2P%mT8+9oSerFsbDMc+Qusb{V_go38ekE9zy_Htl=5l*KWHbZri!MBy^MEo0Osz zQ%JD+nCiQYG3cimaUf>w+KR)p344YWBP(@TcBaKxD={FBN0qP$ksoz%EGlIsDWu*U ztZ-29AEMRt7_Y7NnAH0g8`6)u$u?R1sgA&^u-q=8<2ojkWLE5*thMnQQ&&>pTc30H zrbcoloZvGoOc{AyBL0MY|3Q0-+;CmRD0BsDYsR9j8Fh(Pc2bW66ugS?C^S?_N$0ne ze3&K*S2bPZvViHL?qto`@IPap>UIaH-|~`%V^EZ_jmkVv3cu%9P)|>Zto(zQLsePG zB!E#LYA3i#BBT3`_~|(m+h{U|0L|&200A~Y^52RPYA4+PyJC|6Mi7-Gu(P!Vx3{GX$`YEB7`|5}&gcSHuA1m~%_--3S* zDQPVH`zvt(XQrX(B6jF1dMg6pJoE8mQKdlWKjg=MRHFC?iHNQC-ORJU59lTmhSCYZ zmX_GuT#9yb3X13m>e2?Qm$@D6E~FWb_u^qT=kFIpxpSe_XtkK&ZZl@SC`+qDq-ty@7Tg;@oKf2Qsl z9)>pwG%y=Aao8iCFeJ-8aWb(nf!F3Rd+)Kp)zQv!xY#di1o*7CG9|(r+2(5E1nq`e zlueL`f`0+KW8e!S{wa&}LnW)*T1xeW|2Rig*VY!zmZ_P#-pBZvnUC!o=jU9_!Xn_^ zt+4c6%XTX2s+CyyRh;3~RF}&UyC*LC-{vu(U#L^S43`jTWG2uaTTFAlP942*76SXh|n;^${R ze4yvTaAqjGsF5{xtOB~00vriRQGODg<%YDw-8C)uh`$Ad+No8(M#hAI?VS?uE|_^! z(TzPE@QzXp%E}>;XDN^SgHaKsp%jqNuT#};?C12TY^0_9IkINN!23IiU-K*osXNF# zvw0a<$Q&Fh`~*D7+#MXQ&yQ!;*D1r0kk5_Y{@Y9O7;;q{!xwv>WhuMQ2%-=Om>?xK zd1UR@3)kDk&>46xG6Sc!F+ z?NO17f#8mpKZ;E4D|rX-E4H*DVx`N#$_l{1Ah$?-qpqd~oy?A9XLmR1!7pVI31myf zf?)0*Vz(fi#}rarO~S2k6MSI1NJ`R0%bkhHg{Sk-Z}v6Uobb@BpwcX;047itff&4e zYMvy@86Ipeu=)`NF(X_7Kyj|FMf74%&Vl@qxH-bu8ZYym`T&)9GwfccGeXvHN$DlZ zx!R%?v5lg(3XB7rpNWA(lnWR9LPA3l@SZ<^{>1sw*9WzH!+dCJyKmeQg^jZNBNl_o zUguaBRiP9tP(3ZYQh{&2Iv7pBHB;yD&09{&KL*eHr-R{BM1kEHeA?#o3E~uB+wMa4 zy-O894l;h|iZibshPFE*2wzoH%m}*FAR!?q4}<2%g+)3@wzRagj4f=U%gPkRMf}Ll z+rmk|M`q^-gtT~CoJo$sHHpm#f&^tNTF^CDhk;}9eBS^VVy@pk>uBD~P`qI18(CIS zR8IR0Lb7|+gC*+RJ_4Ec!{W0DrO7hFwvEvX=Pjcuat3rN5i5@#O!J&P$bw>7v$gu} zJR7<{X@4p#ualkadmriRYwhURe=+^%staxn;X_J_oIzNtI0vTl0HddN|Mf(BP--f| z2}x7YmmGsQNR_G0QB+{11MdVDJyI}4aY-)b!~kTj29OvQkuMD8UA-ifT~l*rbGH4` z4pD4&3vQxO8+_2L&QIjpNWpNm@E#l_rm=F)&L^UVQu1+eaX@UV;MP@r4JzZbaDmD{ zP;K+#yjXifUIl)Y$<}Xd6F)05@5FNP*II=C4f=*e&mDiIK?|Y~3CW&b>xbtzHE_R6H;K8jEy%~Z5G z&XCVd6f?<|-fO$6oVO5*MG-rH{jmc+5rK*2!;q4rvoQ?ndG$ZmK!gl}PX!To3)R3p z7t<2w8+P7oB4T=$CLdM8BMxPQZqE1AxfA<&MK}{0es3~+B2T|V8ayHdn#lyQ#)<&d zNcw97_br;l$w{ZhiSgZC31ij@I*|nnpXY?G2=8U~3h7N(_A4Bu2PbA2e;Z+3U8^VM zPi%Z{Lw8y=)8?S*Z&a_q$(-r&WNYm2yekyRLL{=jnGTk7qs6?ZrYQ||&iERxwpIf4 zt;a_)5|^FsM2~2KDNTu~SmV!RV(h6pyeK>}LC_(kt+a(L_U2FW81CcB2d5{Q?nh-# zZ=Rw%Ljm8XmC_8O5Xm=^78Z!Rd%Jgzm_+-`EF=GE3BHV9Y9#fyYs#`+qwHOE7v()K z4S3{WIe9Sq&(eG>8-}r(W7zjagcK5%+(F`dbP;Py+{92Eq;6n=yJgT*Gt^<77~$w> zaWR0q3(d%45D6upmJX`57N4KHWM&b|d^Dionwk;}j6UnfS%J~swTD>GjAwbFH9Xz_(g7J)k%l1w3EkpJ+a`I}9c+>HNC|=`6eWotN z%9yb{&BED8gm{}3Qg78y)7e9jFfR-|AMeCp8GGzWb2$p;GgU^)^FhK?5Me~0iFksC z_ws)19C(Kl^9y!=Rj_~B7(*Ju3Wl7Za$pIR9g@DOchS0%g8B*Xtjruq4UH*)Q??;p z!$c?BRb7FV-~IXVw@~Q1!koa&18Wg7E*;e&;3vA4q%$4P_F__RcC|auIk7^vH22ea z7FAaFRrG(c_g6u2ME(CDjJuP;-Q9v~@FWC+ySof9xVr~U2=0&o!JR>Z1_@4ZX0YHk zxP&3Fljr%rzjwE4Yj-d9a%*m;s(a3K_c`5tz8^cH6+uR$RMF=d(ahH}Z=-wjxF1Pz z1UQ35GQx0MTSBz2HE(29s8@v-}DB#In0W~ao7CD=#`cymh__{QI>vE%DR^C$w>(_cZ!@F%I_-y z&yH$$JSGWAyfH%~Sm_DZ;#M{FI83MWP z+8E74a2g1EFa+@v4FbPEV)E@q#5CQHGWH6gDgPB<2Du<+K0DZaeXOiM{+Ea|sYUl6 z#_I}}JJdH!OYP1Fz5tLFgX8}fZ?ScB^4M**47)0Va!0Gm_q1!m2n_GU%2Qq>%mvkz2s#<6_)l)gV<)6}aM_k2Ug zm2xPRMbvVCwZh@VZLz8i?dN|**XQS1gnns%%*ct@`GNLhXqZ7#h(tLCod^r9v=Qr% z)885dT8QjC7ffU%i(v{ZZEr#j@qz&Ru*;8s5tHe~ehn){YL_#Q>N|h!&7c3zRlTi8 zIa){kqpmWqT`uZ32fu${0Ar&k7G{>wg*Up71ZO3UvCW0Ptsep4O(EzG=au!}q)K+g za4g8i0OKYZun&y$lQyfCnosl_;x}*nzwCe&bse? z);m^=WE*_ETM{3zT-{w6CAmmGZdr*-1c+ovGH&ed2jTi46@EH8=c6X~c@8e1#ba25 zkh~S^37;RdG;P~10;V#ILx|cmdd_dIlsz*9`fBmXndddA+n;Z-lN9s%d%JwW@v7@8 z;QG1|lze8G+>FBE45J*a!l+za!64vs3YyiirPjRa>wjxz{estT| zS_zCIu94U3ZsjL(Sl8wGj%O0?Q`mk5yZAE1VQF7M6__8D?xmGA`bY42C2FK7#Ja79 z=H@|#qmwgYEa+`zcYV_C9@*^7zLaG|{pKulOB>Ee7Z}_Jh4P4sF8*4a+mGnPhT*`l^b+bu>rND0lNi_kv~O%hQF~%>!TL2l zsF+Bk1ZF;l_uU8cT#@^Y9Pm#l7^{$5V73h9D7-uh`X5!zvfjo3?#RVxsim8M=?X(LhbDv78|4A>mpGpSF;*A$~T2cJjALI z(GkZy;n$sEt9X8As9?Z1!nXP1=K;7zmn2#C{g@H{TVfLq##$^&+a%QlXD7v~AKM7{U*lc0ge>#uWH_8oYjw2U0KZ{>2VDjh_H zc6;D2`E<|7vi2?b`!Nw&^04A#KksIZKEdO%d3i^$af3Euf~I&SxlkM&2B^uQy)R$B zG&C{W?UL~F@_POD-OeB_quf`-giIoOK6mQGWK=6cH6%K z{+%%-24)lzyHQ<#dS$Xv=iqe*%h3K`6BMzrs)8L230H}VE#-#tI+Lh&wK4}a@yZ_0 zqN~4Zi*IMJm1$)TT;9Z$rLYO9ya)Ztz{6Lh-kJBYX{dOyXAy?Q8mE0?L&8@S8eXp( zIXkzJKtXL(nfKMk?GBHV%%YFUl_n1tAkgbYs4w_6+YBPFmIlyG_H2+&Pl>~6t2DJ% zy*zxuWcF^$Ts(2%1B0WaijB83jcZO_gGW+d-xRT^J-eW!aD2iF>q${9)3`1`KXizK zs_4hbqhv~I#Ybm%f-^)2QVJo%!X%aNH^pV=n~{C`VCvNQlS;9qyf$?h{Z{5h(C@+~T%3o%+05*is(yLA%ov4nHnI>MQXsX&;HV7FcAf(f zPqQ5r<-qwoQ}uk&{A(L@a{*1)FHnh?CQ5R5Y@D`9)G~g zWF^B}oGur``XT$q6)U{NfNGC$s!?F>!{w!sC`_X2`{!a(X!vA~Z+6I^Ajg_ zSgEd)1wUo;P+gwJ#>IWi%w;;V9uM@4eCU+KYs7*7dU}#AWnujBfMkaVv z1$slgo=rXFZEeMkHD@{!3gD@8DM>R60*YY_K$BzQ)GdYQUPxZ>)YSMeF8`);gJwh^ z!T@f?Fo|GR_|Ie9{FF7`{c{SWJ%B8&Eu=|oKAz}|l7Vp&-zSk8!hJkf>>iL0$1oY& zCVy>YgY%a1v_=8F_r%(aOjjz+NLRy@nSBU5Q7JGwP135`QZ<^nMZKvZN`%FcirK{y zm3q+byunK$%M$k|)i2HF6h&7>)FbO!#;?H0-;IJh%ZKuN!%LdY*u#D65|Y1ftbmj$ zV{M^Z&Gx9@G}8>iRfEx~sGNzM%L#ms(neNa$N6t+C*7R7^5|hGm_ta{#%m&XY8l5(F$wyU zdBz%7c>M;4UQw}~477i#!Hd}QOq);HGaFYrOK`dFh!!xsri5;Y_(sk8`(JKjXMw!qJ&hW+fy_o}ZQ#iegpONbX^lN;^D zsX3WCQj4(OLRl>-mWH0YwU2PrUHGpj@R&EBF#Y?uju8d8lF|4!t%M&QF#mGfL&Ips z*vZvF9)==Cnr`S6^pMlGPf2s2`quMPr5eR%6*u?n2D>qZy?r1R!umXqq8jSSPrfO< zS-^(}3dwO3SWJ3>0WQEUO(H6$ciWhhGj|+l`dUt-zCKEprkG)ggeT8ZBq&>bC{R&c2V^0tcvq6xo;jH9 zvcF1kdMz&bCeX73k~(-ER-77+Q@4K|O(D$_+>kF%RD;St!3T*`2>xL*ZdIJ|)}H z^|36jCkzpFwb^IEb2ANCzT=!m1DCytre0TmCnlHX(>?%;l_iPgGkGXN1CJY&`wqKo zx|ibcD%$viO0>iXvRC-JscOv>?zpWkqG;Qm2Cp)pIjsLrD`uBUQbcy>!ajiC^7M8;`Gjl zCYI6y?IoHP+ifSWA8JK^d;MzUCuWbV?D;M3D{p&L{W@sVJtG2?Nh{3!@RfB-F&>ej z%9Dr;;)g_O7<$hDc07E5Wakj$Noq-B&UmZHr*!+huL!cPu_y&{d@O%rAHF zWWV>A01Cmb6yX>6S|J{zC||#_C+&XO)M3UO%)_^u5o~%j_sGX}0(&*v`I@4fY)_zB zI+Dl0%#cmaDd*X(;$YG@uIFw1Icw~dqVPS3{LuH5dTK(cNJd_}$cI1UWL+Y*xz7E3 zm0KR+LDs~A4;b+WrMF@T$ zqcC1pDoG@$nZvk$s>fNX(l^!~=ymFB9g}Xjp}m56OH)1<9ZLcEoS`%=w$mw)F$W3tZ^s_qe8bb|J;R!o6!*JPkjg2 zEn%oeN!?M2FWm37CS4$&(#-lC`8XH{N~s=tcoMX~>So|(#g_;$8g+&+H9L^ACnKi` z5CoHKf@0S>xP$+8Fn!pMnLer$i&k73eW4EYqMc~%SJgpj2iP(2TE$=2p#yn*`!BZ0 z(*n+s?f|x1nTsi>?%zYZff2ZX?=gW9gFp4b-`4wSC7hr=^{EF{Ry1;MkHO{WFd%9& zc1qgE^r%lQqQNg$y6-O#YBEwk?pB~MT3|qLUzt3i*2WnE_OBD1z$Z?XjHw^+nnJ)# zfuz4;bC$(rthfOvll5*}DPJV77XVgMC@Z(Rn2*5Lv|0X|NiGy!%dExm=WfG8Or0!- zcTL!I6Yc$aH!q=?X_23QnEA_5ZP(j})eH_QvatPN7GW-5JUmq(D9%8)-%PObE515kUH>z#)iwF54{N0b}dwH$|ru zWOjWqVYUN*OHy_h%|&PMz}Z#VVS_`IZo^9E$B85T^Een17~9ZSJHJInrY~BL#-=ZF zT5oCZh8|ZM>VRp2s6hbm(i@qzd z+-+Hs&$V%UnH_9F23;A>^!xo&O3hNy1`R_pcDy*86J}QFh~W-Lg>D@f84&K%UW^9Vv_0 ze{@FB)d7hKt_b?=5me98N6TL(G(h5c`!hQrCal`IxgCja_^!CB>O)+hetfM35k3ND z@cidgb}um?`6rF)7W}6w!*zX!ofcgG?tht@T^1~>wC;!h<|+J_IkO_e~}(bCyys6X5Y$QcUVN&4hhLlXdzg;Vua>RLoHtX+XcY&TknbT|1xv#{{c<-|LyXBlIZtO_ceqL z()f=J9YK#?l9AnRP~Uw=g;JUvtub4||4V}+q{6$W-0%^1*&iWiNPgZJz*D)fnE%kJ z{~)mS*Z~p8?{7Bb`Uq>1{{6*9v*!P7*CcL6jGq4> zX#cF@6FBJN|De79bhse*i_QOO-P~$LB3Qdn>{0K<|E{TeFjh+RZor_qGD1rDKZmb4 z&e-6Ga?%{^2^zlXdKK!y3Y88{E7S=mC2J{!chDPg^!;bl{Trl@eNC-l5jjJ=c**>h z{?^FUk<8vV$^E0tG*((3mN~GFB6uSrSouHW8F5r&gdAP$eJ8)jNInUr<>-V%Beg$& zUSg~-#si2K+7<-4X#X{`<2=Z81-@xp|-99ih}Pv7)W#-G42C z+@#$e_tpO^4gc4bLVaKDWCuDok*K@avn>D&61;JLgKyKGWh`$nIFw;EmKb`$zW3J z4!E~MBTT(T-8+)K@3!_P&!V7BJv*Ht23skOXRRR?O{Sq?P|*;;@gyKL7Z}hH5~-fc z54?PQ3hE2pgwaX+KpVCbr1}9a%IHXuYYOXdtCJIhGT9W+7r~)dvW)1Cpy+zK+w zRcB>7lQO=atvjFr=?y84UTN499F45p-e>+jUm0+3|4R8;%-hQG&-K^EL`eH$Ci%>I zbG%+1JZI}YKomx&c4Sj#bCADI9(%f<%+3Y%XcCZ#DlAdv@mkwxoZ{z!cBg6xEUrZz zWg0cC?Wo3-(fZ!zP}8f7?7+C?dTvwR3$D+_bna2|G&}Wvc!(&ncjp!&5lZ`H5DV>= z{=xpDw8;`WbrOmW|cvm#oEXMWjZxpTfVq2M= z_wi4^dl_5<)stEcW`0Dr=w$PTi;Qoo->lJEQrm?Yu<@|_zzf<<&jWp8amUtfRt<+&s&Wo(YvjlUy=v20JcDmhYkag$5nX&jN;p zSlphUa@vgT*Fg@%|Yn41Jv z+$s6AD_pN(Fn_jknLKTp@;u>CciVg7*O3Gbd6e<}wqW`>dghXI(Tl8~;6YP+PI;#c z!C(Q3y%haLZo3bq>*>WNcUKr+7~t6?ba|S($HrumYMU(f>Hj%Iy^|>FHKu$2yaMCi zwm2Oz@QpOn-DhR0a)xnN9mWw~mQuN)l0Ga{{Xdw|qfB^entggCVY}0& zxT}YI6u~?qE4LyF_WQCH$mVO9bgA4&JtrfrEI@T%CSgWQ_4JX)Kr@1{Bd;3M+wD-l zS%0$^x~;zj1|H;seus8XLGMjh*K-x~vn0GpR1D)Pdn=`HBo-%=*I%5WtHH&un_*Ga zbD?`)S#lP>a#95CPjT#+2OMLH?*hrmRy@6GH>Tmxh0y8K>5ym4ib}l2SYO*B5M0*1 zn|>yhJI-O`faj;Yf-$>&M}Qe6B2tc%E2}6-dMYC{E2hZaNeI|_?Qu5Tbq^6yc663x z>S@C2dAP-zq8?y_tV5MwkT2gZs;1GTMI1SiY1YFqr{87q*T^kVb%@Pj(^H4sFP&^h z##Ej?xD}l~gEulR=&}%3lJ*Hq&uuDp*VUo|G0}oZ;ATo$7-E9v0)44AMET@;DmS)P z=$z-Y)67NtkCtdZqgDT&qLuogmtvLdis{zdN%MTPU$3zpFjz?S#Jl$XW*Y}ymqL)K z^`ap5#NM}mYHzysZufkAI(Xa5;K9r8>>7#JY7czXhh=_pzqu6UI_Oa0H(fUZx7*@8{8YOh3sB>)H-L4j~e9H zfd2CB_w!;>t2=g6P>Duxkn(Yzy=r*}1!v85NavXHZ7h&cyRGV|3u?|Lji8A|)aox& z1~PTI^N^EqXYM)KjY)z%YlU-HSF7fEzXFFZd7W-o)$V zW!88=M{B7-LaQoL4lQ zdjCR?$4`@BSK%icjrutBSHj}OSW98S$gb#CDt=oM4IhIi4tyebtjU9+DTrd+-`J^1BNO z#7R-A5QnU_EJzW=97vQ3&%v#E1kL(C`flwF4u5|rxZw4HUe`@*hZj&* zF(+BR5bK+MZW`FV1Hin1G;S;Re;UH*!!q;esP8;!|NMy`{tj(}PD4FCkNGV(es0?o z0Nf}Mx0;znsBF!~_Kn@PTvyRWtD2$qbyntlkqr#)ez|>pK8Zh`ty_F z7EPm*2f5m#s1?0`cTvLFZg9tJDjA&k1Q@&7i5j}n@ifgx+8$EJC)Fzp{L@jy-&K?= zD|V7|w%zo+?bJNriYkix8E!l%T8Y;T1sjz61O$7|ZQT*hN*Db#FEo>V3Ld#M zi|h&pBqP+*kDu?ofK7}lUoO@Nx^@6FS&9nXu|n-BH$DB8+Qgwt?8-u;jbx`FStaf`{g z1)0e#neoj{f9nPyr$9JO*itT@_49U2mhX{9VuXKcJzDIlUepKb-1q+Us&yZD8nA+1 zfzloAi-K;CBDqg`-Ov(W6Qh;M~<$Do3(B=@`*ctp~4Uq?c8L$_Z_4zcPM9M65@s zB!$2MW`tXG?IJ1oFNo5aX|fuxcmL19hpURI6f+ld3_D?>Tn zk$vLIlWOqGlH9EFMD2wy6nSij;;h*yo(uWKRWysxag8dh<T5>Hs4;m15NTV^>HPu|e42jS9M5e9y4^yWV8q{il4OB*bCl@DMq z1He>@hQNm^rRN>4qnC=rdhIdI%{ndn_pcPU*xX)M^^2BM9s}tT)ep~;G$Kja|y9Nd^88#unX=z(*! zR^(|Z&=v}D7T@#sx2f%MME2B4H87FT)e4@`3aPchF!-qjNUfi~_Qcb#vz=lQ`1kDX z&))4C|Onk(l{-8$D-hfDes~*c z#O(XDPX@E-52|5|MUXZ8^w&TQ=sDdWyeEv4t&Jbl-J0Z0=nCx|+&kRD7n{@d=HV?X z;UKl5{7cmE*Y%q$rSn@HaEKb2$~t;55@14fU_*wESXKq>Wk%O_!5i$dX6=fitCzvR zUyC<#Z$CkARxCJn{|#y6G^?81^<3K^Vuv@tP<7FI?;0h~W&t##6_XTA2Gx;lnCNMr z!gFXdmlas>$C7|+N*}EbQA+6~w~{e&G@`K@8NGgeJS+)cCzom}uIETJ5o;mV6o|xc zd%rZi^2$hygzz1zrl@DicDjdBE`rS}7rr)qIXepHN%xHFvX{zu94(3HI`eX<6g8wCfoYA&;{3V0< z>C$M5heZ78VLAxC4SGEM&R?=)k&t25)&os|Ut{TE7iy;Wpa-a{?KE@ubY#9$VnOd4 zS|$y6L152Fxi7Rk}V+$8YETr<{Z>rmK);|x!U<9EFrX?s0c=>3su#^fyw(8K=qiv;YoL2y`bJRl{MJaq1bAD5ag>9($`YIw@jw%px8 z6abo9zv|4l=-PtW`BUK+vH_yglaH?b8DscLk}!Oib&T_LAiS*p&lZ)vDml z)-c&r;U#}vJl^7pRy<^&Y#~7v*vAC-BR;of9J9tE8_`)+T)lhq=}}r#$~qa^>M=uw z7nu|BH;&^CE1zLTDx6=$nd{SMs4mHznqB-+U8`&}S}!A2U-1pET(m$9kkne^PW*rE zPx}DL2UbmaX=raeD4&)Gi9&~7e!(w@1O z{92;99M-C&p;B;o!*fjsE(`P^Q|?KbX;+g!-6RTBU26R4o4+!vv14mHu(89aYiQRh zypGFHZRU8tU}Be=fcTH?28sER}c5f&h?l4P*aG5dwd6L`Ao=Z9U zfaUnss3ae<)q$EJ#bDpHIiNx1n^Qnx4(-FJjzAa{xBr?9zdoZ-Fo~syQKA=jy5msR z-C6xkPtHHl&E{N>GteMk#kDCZgC$0m$a^=V1_gvr|LIv3cvCQBZy@wG#fOOo@Y34LPX9DC{=|C`52Z= zsC|dJvTL!1Le!aU=LszvT5;O!RnEE0`?YhVdLG|lgxaBUsC&m|wvkq?h|1b;h~3pp zd4>U!gjeo9F2u^;7N~YnDu`8KN=2b-{&}!*jGZqo;iE1mq+Hyr3n(emz5OT=fufU9dn)vzuxq9s;@q-tM4N^6MA8sP8XV-r8gJoW}X6FVgP zkSlg|lF)K&U~lxdsOXAEf9p(*7<+%;7&V}5-|;+i1WvN(KQ9)~0*@0o=N!C8>C$f3 zjmF0n3_*>eFoO;`+K_lpUKz5k;(4h_Mb9_wOtiTLL<3-|Ne#s>QiGf&Uw@-iayP^H z{DsBJq#^b~RYRZ!BoFjuL!rehaiU3(_5FRNs}<#=z#aR52p(p`m&oHuYCKECWdRBb z>QLjaxD+*mmQ!aB-~3qR>YK=dJb$t(1<{8}6jUaAMnrQ-AvgQDz4hyF?R8@C3&i;I z5hifsGIHxLd%csqX|fPk(r-^Tqi!VNGIU}~U@O~+kr>T)O%~lu+QaMlKmZB_f2jIp zYKys|_;!vsXD#pGb&EgACK&Xy4sZ20KEpTi$lOBoiP|%ppiUJ1)y(sxNqLPH_8r}K z#IrwAd{QCzZ-0`jZ_C6tv_^QnmRVfx)O*P3t81YAV@?M;uNj8EAvC3!?VvjK-96I#B>);zF|!hYIY}^9cu>u$p)?rbno!h{!N37*eFawNje$7# zmp%McCV9m6Us02bHBL_F2VW0j0@j#c-)Z-wjwoEK^Vx)6MTe~;YJuuCO>%d9R`vIv zeF@5Bmq?qI^ghnCCm#_pI~ppNIawmz1Z)WiG%Ef)XKJENw%p2yu0hDgxE%*2I?)6P za%Zka<&WouAoe4Vo}P32Dbp|ozu<=;rzeUd4L{owS+YSj($}qvaUV>`j13D6(`k^* zYKdBau1$x>gMEx-B6B7KWFo%r^55&dQa7W9y{XMMG#r$adQ!nz1Hr#fZ{=>p-R=n) z9)Dx%j6#2ObjUXR!zd_)O2;NS)6St3M2$s5l@K;l0iH{mRvEIwglozr! zO<{S=^B22juC~7FWvxu4zQ)7n>f;KY-Kccym#^j~I7r^Lf;Ko&T@m52ApJoDu@J*G z2@H#us?mTH1~EhWw;CQbQ?yDt<#Kcsn8nFvjrfSXt1?3RhJcikLi9|j@Cii{Dp6KoaFDi zTvRFg8^)h1*bnBr!7^0tyr~eXj|X;`FUU6yQ%2sJv3!w*laT$at>pX=S%{N&$J6!7 z68^cnp*|3~>ty?2!c-nt z#InJ^q)4n$lDfI=81cp2D(uNsuAk{c+4o}3AVNYGxxuc7!(~$=1?PC&8ZYuc7slH> zR)waCm#UoScP@MGsi0Ux!@1^a1s5Nz8rtC5slBi!!B|VL041M>q7FN&jLF+Q91_=s zVh7ULlUQUPU{S-^ zFOznc`y@G1&4f>wCv`ACo~foJnXkQxAG7 ze032nRP36a5<3h88|Ibxr`*_r$NaOB8`d)kyhdK>rsNkVn4wV&<6pG1>bodd9(;_y zJd(Z*i789H*>1zU>$|tLq4A#ZY?OqcP_z3fiFxSl5g(Drlob4m@7xqXV-vZkeIstD zOjSKmiyJ#bRGFA}65SUj)Qe6dcx(y1o8F_c2G!|cxc1)Y5z!tpe4uZeByEnNI?&bR)okX}%1Kbs94 z(y)JTjCg$rn*a#(TowJ<=SzYDF{NCw!M=Ynz5w)!%WLAL?p)Ir+hUt6S;g(&UADi? z;*PRBA&KlPwDQlvD1%$$ehuvb5mu^N*kHjr$gcR!KoI!nQwdJ{Ca|v}4&kz|&gULP zxQz9+n8Cn+CiSfqtK*499~Y!|C2W9Pi|<%^R!A0tLjKhzD!g0pNz~9m1C1xyOYr-~ zFkd8!mRNUE+Y1xuoS(u;nF=01{Y5GU=r|})GFhMkiRJSIj(Z6&A=8f)GC|t!@jfX^ zMt7pUc;ZEh*=3k0p1-zOm}4Nn?uu*bP*%F$o{yd=YL!Y|`<$*Q+ey1Pm9A~k=_R7WD7KGq z^i$eYi4Pr-X!AH;%-@CAqQqXBTAh-`xb;{|D+K_NXqbKxIn~CP6usFjDd*Z@Jsac_ zMvrK#!x}x?~tzZd}nSw7yBnO0DKRr)@ zeOBz)i#j66$c$S&EDjm#dj0TkSC|JJCYRfGZjbjI&9X5K&gDZJ=IKd#pYc46oi#hd z=-*1Nb5wqdt|*|?##qLh81Tmi!eW|jq82u4dg3`Mm6KZK#>6yYAhcIlT20Cdv|aI> z_;Eezvfv6Xjs5ZB`rXvd6~!L9-JbEkSG+8_IZP#fsDA;<=XYb#j#t)oMYLCh;6To- z1vOa?fSzF%e4Y{R`$#XzRAxit^QWe;fqyUcrtFaE;8?Y+v5Xj5LnZ$7MQDnN=o490 zG9O(l!NB-0+?>-BOMAGlQ@0#Yp|fF?7E|$=Daqq|&h6({;QQT@E+LGfunezptyef>PxC!G=X+lxocMV&EY3odFRC*2P8AIR#Mpj3*LWt!{86GM=-1<0*50@3VPE=1bC7J|q zBS=z5$@2H-^sN>5`rvA04GHc0w2#IUV{^zU$#-(i7mx~#`)N5T^;>!jZk;V#Qi54# zq?&ykp@J5x%;8(fo*=p>$y}_ToG}%3LfuszE+Qo<@BE}cTZ{GS z-T@?XWNI=;u&N>2@o7UxS&Bal$mkg;j!j5LQ2G$<(xgwC1j$`@q%yTco{C1V#`hi$ z?Cax3AK>v$;-TlxhWlEAMUft4(X*`~SD+egj#iroK>P zh?OU4&7N#ZQ5{&U*J3n+Q&8+|5?NO+4PG%Swv9#5hTu(Ecrp^XE6UuC$CH#HLT>xf zL5OR-d3m9lmP>hh$peg3;hGzjabqst%)MMpwY4~P^DA0kn>%+|ul1WMUD##clQ{HR z&nP@k1sCon9IW=gdXo%fT)@#x}tGjtPTaj(XOk!hji{VgghgFkrDX+w5T zq!LsJ+chzx&h6Q=DkEvc*L7aK@cZ3O5trCQsjy;Hvz3FuqjUy-7L+b4(+Le*Reqc3 zL-p|!_FyLCjEB=q&GinjU6=o}zA9RU!ZVLgza{Q#!gt?S_nh0K|!IGDizj z(>*|=%p@$Co{BxG4o!ma3mp ziAT>7!)2?st}UQ-?D+@Dd)av<(0ICgK7{NsFh6zh_(`!6Qf`^g%e>?~L|atbq17Px zdN*~tAlY90=bz%HUg8q{;Tnw>Zl9wleNVVu7_#)xL?Q@4iLQHtWOv8!z@5PKzvIN- z4zKR|&9Zmht=i^s$M$3ow#A?XpVsiUKCgt^aw{(X@R11}PB!^K)0QIHqnye=WBB;0 zyAhRsGt)22=pbEHsTw4sBJ|w~kb}r_9ST=9c+b(wYw~ImiSc&uHK7^MB?$P1PU#7<0`r+as$_6&~l=Pa#d^fINlz zF@`q2Z9dNL`#BR`7q38&V^n^MQA?Im-)ISiY5L&0H#T0Yk$5l3rf;eNu?sM~@)kd7 zes}Q43^9J`+vcK}H$JF!=3~T*YA56V!R9taA(DMD{qr69gdnNy7y+RqMK3?n$Lq7f z)_4AHziU!k2S(mv2B5w?Uuu5kT>!FQF=v}0JnXj)V-jvroMBr%OO(M|K{uBTq)KNj zql=f{iWr9E)yFn|;{2dAt$SCo+JKw#+Pg91JJlwhY3c0nLqRQGBQCyIblYj^;s}Yo zhe#<#3Lx4rdyv_Qh&CW)hR|h@4HD75s%;d0rHXFi?iSV)Je&$~dlV&_`$vq!aC`&y zX|yB8Q!T9hX*Nrvc|jXkAfU8cFwAQju7`Ibmn6cXxZ6y~SnYzRX!BC3q4@^%hD+Cu z1+>wJgh5FEYNM6JOBLn%3x<4(S;zX)%Wy?R))2R6F}DXkAwIrUT`=j{5u1$>!z<-T z{GKO6+D5(Y%9Pe{b6YX}{jM!nf5N6;k~1%b!@E;>o$cQX8ttI#1i7~~**LpfA%(Ji zig&aDNJ!nV<|aikPysn3}wz4!NXp8`jU$-6Hwl3N(I^j_+_|bM%?O zeoU#U=#}qUV^2f?6jX;-1}8V^mWxq1K z5JelPfvDoUXR=$VIV;=USSDFLKBjde(kXxg#iT$?f&89aQ4v_1f1hEfsD;f7w!q#TGj6OQJD#Nj9`XWlc8*F3{FH4v5R1{wTXsnI(&PcYe zm1S^dmD)~`-4Sap#vCd@Kd?U@yrSp8)S0!Y`Jx>2r_0fd*o-{r2`SJyz2sY7d`_aw z{Jn|d+uwZN>i8w2Jb>5bt?(%o3FrzOQmDYA;Lp|y)#R*mKz16LV=47 zqJ`M_t(xAR`zwD#!4wkAO_juNGM&NIHH6$R0l^;Xm|7V+m5E}p=ARFEb=!c@m?4ld z6A#a_=K;mwfE;ENu<+~DD%j&aW@o3Qz}2_{p$Sa;{hQ@4$;~&r8sR)?BSBja2A8j? z9i%XhuSO@)Z|U?5WpfpC4;4*=it%~XFxnal`XmVksIvH`W? z!BI~Cs04DsppSq-qk{F7Q12w2T*G8$tHc=6Q9o~~N#zxmPV1{z>+u3gpphtZvaSQi z*c=Q9;RE^X=WRk%9bfAOp;#AV+`W~K%%HZUAU4N32@=W651PM(qH@ZGU}M!xRi|iI zv|E~;wMee6ywaNbQH^1I_GlFcd>%2s@qKv?o9)`MdD5tvEG!IxYs*YPw$JDKJD|z{|XdyvJ)5@px7S>XmO}rD0K&Iv!{in zrHra!I7JHPk+wZ#r37D9P|b3sb`6Sd{u@>*@mM)uSRk?pUv6oQIllsEtNn6p~8YNZly}wxjMZqvKw|uISH22-hcqaj!doO z?iB{x`rpT&MZL-6i*`k3ePrn0wiapCDImtLVCi`c;}m^d@EN;s7d)MqHlEb zxIHI9S?TjJ5eb~Y!Kwz|rl?&1_lU(gnzj>TK(0O&NOPcuXqCs1pEanfwEjPS^j;@c0j;}04F+K*K2@2qDAThlxs zS-7B#{+Skh<(E>oh^)I!_iD_I&X>RxG6fTP6e{DBduwuJE&>mp`+^d=bu?$>)LM3F}{-yGDEkZgdqRzF%#lV@PG_j?nZ0sGe2T!^D`{>ub#*nyej@iwUl=( zeIA3)a$Ka3_tQZU5!}!`$>U>gM-FZU=Rkr_zpgYF+Xv-R1s1i2o+*=eY9#TRKtrs5 z%LY-s7cv-RIjM6p$_#BCk|Hv#Ma@OWjwH3lRd(uxmNMD2a&uEZ^oUGMyen~ujKOWY zs+&k+kYh!?I84?2#c#R5)D>&G8QQzZLuSO<&b&N0^NL<&MQfxW8jR!M;y{|rnwpmV zIR(1MS<_70CXyK=QI9A6c3QzYl$ve%^1_|glVAGtKC_?*^-rhP19OS3jgx#>t*WOd z+b6nK#1j{(Io;-ByS9_4@V%>_9prs=IFBrKrfUB9=oKK-osRDoEwQ(<~p)IxCT zR+RZ=L2Z~dBbOw3!|>jajNgC^yiq_ZwQ!xars>eL-R;&e-i3?fm9{e*i5Hb%L+}6> zhyG99&&J z4wES~v?lcQV<**{okOx+C^t3uOt`N&rYDt5o%t^F$nP@;PFwA~2{mkDU1;sNrDcU8 zpT6RXe)o8~>+>MoD6G96oHM1S`wDq3W?q!7cQ;>z-l|6jugbgB^UUrbXzJF%`^W*lxEsF&K}B{oZ;IEN5;X8l=$Uw+J?}wU zZ%AJ8Yd*sO3gk@^*l*m?Mn^}pprJ&wv@nu|q#Jz_0IyZ7x%qW1JUvJ9Yuee_BR+Z| zV`E&CC8E7Vlq>w2D=(A31TF0wN0yh z(L6FT0u$T+tuMRG(Cn?r$GD!Z!|_2ZJF4PlB{;^V<8f%qLdE8zDWJH(ZcMbN!mE*} zX_}VWQlVUVR1jhL7f}nWRQwQ@;!Cn;#`xc=d#k88ny76w3GVKLyUXAjAV`29NpNTI zAVVOyyGsNK79a@;?hG*K07G!M!QE}pAoG*=`_}qz&(&Gy^y-UR-POIicGa$`=h?gW zHqIvn!WBxXRXY=?`uHnVUcuSEPuO$Wt?#5sFQLJ^2RIq`p1Tnu^8G&SSO-Pu_z8^+ z=$(C}*EDYG`@I(xNr!RDgt=jFEl-or_(Oj{cbPdXeCkg(ArJO>J$h7G z&aTdYu7arGd0N~srmO?v&x$pS_SfeF>X8x9edV(x5Qn!J?bxzl4?2PyI+SxCk?}!; zM=9fld&r*qk-w4Pb)G zlgeb9)CaiL7DH7fW4L0ZPuJ!=ZEKLP1@{3p*4(Nas(e-MJD`IFss24q*`c-tyk&O* zCeLmg#vj%+XANuH=@|9TC~j{0to$a$YDM`N<@1d*HG2_{!KPcwJWt*ui`yJ1%0g%*t&{&*rCK)p=BPK|!5#h(#W~b*ZMMB>~ccG%(IT?|jnMH01 znjH8(nzKza=s#YE<)!(pa@*&654X`9FqVG6xqn5`_0V6=5anVJ^B4(YmDXmGOV8XS z^5Bm2ZCx@*y^PF)r|eDj-&*XQD)3+?mBZ;@ zyQkx|C1a!Fp%s6mv}(cIQlPM0V6)oiOcPr>hnbc!5|Zd*!zg53StUl($lOD)9&Fq= zD(2A#wNb!;(1PgjK-Ax;b<=$cF?BIVExCkqom02tEcbBeQGK65)kZ_YV39dqvGRPd zu4)i=^Y<4|ZjeRx`pR{(5zY0>?0X#L8Z%k>cI>LDE{v=goU0i>KjY$6`lOgz$1aR7 zDirC)BWt+s4O?~_2)ot5y3~BhD3KbW zHUbO6yy-5H0Q$qHBXz>ejLFEP{ktcto1W8ADW6XQXG<=cv6p9!RiWBMC390X6ufBpRuvg+khY=lLzv-UROy( zrb0b;PPd=joI^p9gv)Z%h_7^B9-XEE?so#cH!8d zcBnyv8>j{Qun!Slyj(?){&qH6m!XY-Ax3QFul)9=&1xWBAR;j26l4!F_q~ba{Anyd z+6FSyUMxf>g zRCWQTk^>_Hy|-E5L}6E#fQRmEpw9M9bVf#u)PxKoy|J@b`cwq_Hi94d(7n%920EQa z+A{i5YjSLC+qKvcp!&IZ00Jo@blO&TD!F0j)n=nijEElP%=FEJ z@&#tOPZ+zQ$q@f^*b<-84jblH49FU=0hp zX`lL^TtoSwj3+f`vezNoet-Pjj1(Ne)z=64_nBaqm>8+6v?_YI_{@IS+L>wE9iV6E zYpwY;>R3iTxAFnZKqM=V5;TJu1DG{7Hq+qHm-F}`9ab5>F4)rtRjrJ*Q?t&OEba4 zp5MU&Hk(_2#c~qVD@sR(JEVHrt6R`bV*Bn>!q;5oK%}~%{%G6I&DFrrfy};!( z2@COUL8(BmPn`t#0~unCLK{bby{*P4*YYc>pO%CF4tw8>O3Iv`epq&>S)OA^_Lr%5 zS`6r)V{kz#Jt+Ksm1E~O)11N;Ty2%@=XrbPdaoN$_2KSPkdc|#ZRj2lwCcW7d;}bK zH)U+Kydw=7swUW*`JP`gGTjlxgR^`>Nu`&5fnLB)=6*C55W@&+%cXY54sl;4~2EHvbjiPG=`S6p!NUMDwx zBRQ*R9bm1oVPGh$uKvkysm-o(GV8V&5PfA!SW|oQ8aZ2=0gfwDcnGuohsf*vh<9q< zFhNU}J1!)`Q#bh4n*AH8vE<1W8~o}9Uk793*{cg0x6fkz9^c{UYn#9Brx=~=(7Kd? z?OXnz)Y-E5l-Q#$OH*_}7 z_HXLTY1p`}b!R=_m=P`dQ9ycA={#2cRcRs4G`5r$UKdPQ1oG_q^lsr?dSIxg8~lJ~ z)=Cv7ULTaDle5jL6)t7(Rw+72Pc^~5PiA|wLsI$4Q&fX1yC^p*O}@?2D6@)4Vmggq zW}tlYsXtEPv*UGb8ww8l1D$*+~F4IUAR zgTS6<>gZJmUo0V}FqD-oL;+LhE5n zF&x=rrb#PoEyXhz!E3pm6B-{&{1`7?mn4Wy6*Le}u+~OMIURn)%>9=lA#;o$WN%Mc zb3?Iu`PqVB4P;IbUn(;?aJEWy<5y1$D#5`2P*(u0U}OdI3AB_9_x0+%F-`fXL22kr zF3?3$0AIOv*+>cliR@C1PLC(9m56)A)+reqP;QTn&&C?M$OL^dFc7zAyK%2dCM^2d zMsE)L)Ky9aiOYTiT<4fk{4uR04yvZ>?~xJ}6RnYz8D%#nZzv}xD6Cl%v+zj-#s4rB zrx```?3vC*_C6I89i(=Fb|9~A1~1508&U+{-!@@XWtXXJVH1>!bmz9FY?$EK5Bm50 z)asgW7)V#~s;@81thX-KiYXW;UbswAt0Wway&W6v=^tA20J_);)JQ)%Du4g*`1=(h zMfqzkD&qjKr;T_)@$az11XV9yzAnJ$IVpNz{HGm9d)GpfC>4bW>FKr}u^^>vnWU1= z=ESC9*&HvKBsvfJvU1n(mvo0NicahZ?G%k)rD;tJQGw6UB8q;w=M!6eVjlFcR#g|5 zixD0}r+t?_DpQYt_&M^2>+=?M)=WjWUV^{IqEn){tGObzC+L;-)+8UTrJ4hz$E1If zSr~C2xvCkf5YWc_j4})tVGAlo1VDiTK+vunkEKTUOBCjb5it!J4_`AaIW%x{MG)8LPJ z2Ghy)j@E=J#Lv81Vg&o{JPk=NB6^!Cc=h8-rff?GVO{)nybIP<7M1W{pb z5@wL}(7zt`gwjB;|CKjDA(+JC2Gh1v^WXH~!3ZXxJ*l!{#9PD?TPZbuOy=S2#4 z?1?nvswJFI5t2c&B}yl7k&03_VtV$p|Q@Q)y`hb)UY%9ELde zSa~xgH|2J9ewrcH6CK3QB_)X3JvqG$hc^vCXg;S1FOq*8ZeE`~#e*CKV>$@>GF8V& zJuzr38Rdnb%AQ8y6r9Al*loQ{h^~^E^*q47Y?-q(;bWwGuG)vT0C~@_E$Gku<8^%8 zPcPp*sjwhUfewk^7!xibu26`eHO4C%J;>DwMz8U9t>pCSm?p1ntlaIBEP^7(a*fDh z9h@!eE-O{)WCoc?IZj#P9EIqIe(<%bZBdascB9TQY^cMKxE6Y?o;?Ol{7Ox9AnS)G zpOL&;X?di)qe)VS$ttY@JiWdoFl+V4%?LabmK?x0e~EtSCKc()V9{rfSq0ScqqTyv^3=v`e1ivY{`;V%PDU%WnK^Kxd- zGrMDLlk-Tb_aMxSp-}ykcorOKp^(^G-0HD;*C^>F@MJZZXtm*3(?wx3#-^G^)1N!< z#K#X+%g<-+#K+F$Ggg@dT~ z_D}FxRZg0{@NVw(w~SucY49;vdcrbphgs@DG_I$`OVpC~HiP1Ld5g0tpqV_y;ZH*N zuDjRvaBWCH4AWf&*29N=#YoXhrilpxiO6Lt6WGA)6JNDUp#iqubyE^Yl0SN1Nuy<| zTQg~bl55>oyL1@mgx7D%i@HqzFh<98d7`udUOLZh4(M}aw}{|6TR6u7YrS1kxDGuc zdv91zJ1VhB?Om=%4zXSTriZ;N)#&7~eDOV)}f+;$StF}$VIgBA}$AkiZ+pT^!(ab!Y2}&N3g;xm)y4)Vi5S#ETy-QOd z%*VwVS$fMDE!buseWJ$|Udh;V+%6S0DW1-)42deCQMGzbOe$kE`&!{hK+Sr1PL)0;*5QHPK{93D~ zVWV*&gWMdu-Jj;^)FmSUZU2?SmIMh}jjAu;FJ^!o18WzG(7U$HpL-cy-;OB|6whS0 zxiQ$+oVu}{@{Y4{<=t@x_2IG7WG=8T*9!A!m;IXEP~_8gf&wF6PwH4+^i0~ZLh~1m z$2qV)r>`UE+d+G0R^sgpGLq58a#D0{+ES;3Q=H(pPChfR5uX69^7Q0H`SZ|hq~9yz zjFrKAThVhw$cx2xgXHK;w(nmqD^RMIrUrnc+t1&A?BZ0Y=__^$BpSSAWl%U*)<&`Q z7u|!etfgz#pMh7-^<{t06v%bDPGAG_XMO3*y5Q)kgB72bku8?f8tj_yw&k)rgz=hQG9P+6MR@%d@2M?_;Mj7g0uu#IX``c;Q`sTWQEQJ$sRu? z%xQ``>2~?EokQCq z*4@e3ukHMp1XMSR8blb0h97fyM;T|_o;q%Go8iT)X^8VWOlwZ!|BA9STyZVk<5o;v z4tk5YXZ^gUAuF4kywxePAno&PM!6>K%a^Fn&>2U=OyTo2LN~PyoOYd3hH|Bjo)&D4 zzUT8r$m=}=0nBCuanKCCAS6uls$=oHbnKV)U&Goxxx5Y@g7tdTVe4CIZbcs-x7321 zB49=?w-uB3pvdKw4Kxr(E_Q)K%33EL2_-(|AkDOu(EiEfwxk<-qmppBYS7w;yPH{1 zU5Zu#*Cg5106`{KQtv8qiEICjBKw|d=oQ5^1y?~Ces>l;ikNOL%J`i2>R7%T*(|+q z-%Fs=TsNB78h!g4hFv;UB({a^raFr)vLf^Yghuz8mV zxi?gaR0yvN$HlT*sQ=0(4i0zxU`_5(ZCJHFkj=H51H(=9r9o0#jLBv04s`F{OQbF^ zNBhR3>OMbZ@67O6lE5mewRgnl?UEJ99gCN0CGB~sD7jA=J3Ph^`GUQ$YQzdvk7G}s zKI>granSKcMr9}Kt)kR)QoP}&W2PSS>bdLp8G@o8EDQ`jr3L8*D_fnO*j$G&hz7xz z&iFLLFv#vS7wWOg#5q!}V}r`^I_VvmXW z3Bx&pV4zgfu^*==N`}45UC256qsMuwATFm@nS5DNqIf4c?rjd8q(0TrQd7jq3|h06v?ROM~Y;qoc$o!V&byV!TeQLZX+j#r-QJ8KpC ziMoQ8-RbHDdjq{$F!{ctylCfU##2K9R;EfS^C3UThie?jm8nIR;`kI9ByBR;Y0Jdn zqO@y=YWeu?aLXWF;5?cWDl-2qC#$n`1#h;26IXdpTUL5APOS%&X?JT0W2uE36R)O*|>(_4+>13{~W-1P^ zw;+uDFE|E=pwTl&i?a>)=tt%<35P!8Sz+0Vt_DJdB^O5K;)OzMn1sQ#7^dEU&Z%EB znKm2_B^)$FulFK3AGB(;yL-C}TqfhE-wGZqwoLzKOte z7^BF*CCbSnxeS|egJABqMAz~%L{B(!G822_vfSFvILRReEmgv$XYV5^&ESgtOsSQ5 zbs_%+!#>us&Gu#G3XNGD4wCkJ&?YwWPmhowD{qX6e#$Ktk=4wl>#Y9?WuCYWV!NZ- zHOp08{i|^kA&R%*P$t&XCt}41h{ak4eM2C1BH2b}+TaQ+@bu9g)XLH~z&ftIt=?7@ z@P|idRxia>`W$pkB~W>Ylwhp0-uu#CJh6Lkb#3>=25B;O%7b%D^cYkUAxHez85_5V zboT&HwUZhMhL!{1*l~}8G*~_@?3R12h(T-E?%{3GQeC{CcTb)}v~}2*Pbu#uX@>j~ z@vdq|;aXwH55$3yG0XC1LbDSmvbpYCOPP&ADT#4(q6~&0BeHQ^WHMks$D^rEH9$%< zS&ywPubs+OF6Rf8g=y!6oBH-bG*H1>4mH@VW>}{rb$ElTI@qOldte>dt?|yJR#2v& zGZEjzbrv(@gyX$~i3KI4!y7ZWLxCKGdsdfmyII23p>n|gVYrS?-V?|DOk#e3$K}&i zQRA9h$vWaj1jkQG9`6=mERddYvLvMz>^^gKSah8weWXc&$|!JKd}g2@g%sMN zJoLLe4Ei4=r(?Sk9G^o~M_|2hhiyZp_waVZD2RbU;WB2qXdmo@RMN;IH_2Y_I#3@# zRTg`0T^TIxQ}rczw>hA#l*;*A$~PZ zNW&sWWRqB3-ZU+nz)^U+^nTJX@t@|X!MeN@;E?IQglmG6+(N-CKcH*kK3@be{M4^V zJUV2%7N~O>A~!-{4K%LS3l6eA{NHM<=C;clJoK86qKqs}3uz-9 z-*SSF7l%Dc?XJtwHlgeA&7%~%#QwDFVj1H-O7*(zzs^{lzw0Gf;L-A@r+j4q-S;Cv ze8ot>Gs(=Lf65rAU;4B;O2udf@RTvs)s^5KuR`$qLX&b;J?p1yAP@-+Ni?Tknxm6j zS3lN<%ANO(J~gDC!l>M!EO^JGra6&)UnH2F2=v&!cZHC$FyK4=M}9}iK@DsE7B1Yr zVN@N*4aN71<-|Y}l2H=3F?M`$ej&p}=ZSuYu0=~RNsCsS7+P}0{S2D_&8M^N2;PxI zrv?;FOxy69HSkM5hL4UiJ!Y&LU0jTcio#GNBgLr!0@H{Yv5LlaVqXRxLG!~*!!$%g z9~~>|h!PcDZMB_-(Cd|wdN%V<4!b+&eWjGTipIEn(C^gKZ!K3mYiHs zDT3@jwo30`Khw~Rul~!5&wPs5#3ESuO1Q4?0r5SM)CNe09;ykjR(!oG7g$&fz?y1K z5jtS!?QFdb{*?#jLm6QLJ&=lQ(r=UPlJz527qD}{GU3XPi{SF^fcoz|Yu@yNve?cj zRyf$|)&hXUR>}TrZn^)cQNb2WC&?w;~W6wyQ7 zI)2w#wh;573doLVi+%?3`zJoSLUY7Q-D|C{zI}ZD3A|&v=}{e#fODO8Vf}$YuOzu4 zrPFEpzQ@dxF(oj#@WWjc=@OkcFimZin0DNk7>us(efvCg`N z-YB9uxI|xdBGuhza1+E^DbF5g$=NO8R3Rv}oD%}IB;Tx_?B_52v4J)zX{mG1I`j}#dn^rd43fV+~_j;Qp6^V-uB?ZrIpgT9t)oIw1jY|8GB*hFyoKlhSRYD+MYi;!sy(ls(# z!M+g?c(>xL97LQogR!dG7mw~yKtya@*`vN-akpxDwW8=kxYt}l?0?S3&_>-+^D0gj zg?9pFNZi>$oFwygTKPP63*3#H`KdB_%?M{ijVNAovUj4Egbx&GLxbrDOc&!r_cK<> zXyYO$v2$+!3WAZmwv>Z^cz(t>pIVJDR&92Oeb@IcDCNU;Xup6x0fM6+Sj?LIDLhW~ zup#SD+abjy-V=>SCsAvAz?w$Q$o4-3c;I3UfRIJ)_rpn9P=5hoV#*^e00m}TcnE& z@LKayyp6}GkHeIOgF0RaFC;UnYQzNDI2%H#v;)38mr5gQoVXg@l3X^LJq53RWu(2v z&auYiv6dVRFA3#^&?9MSOEGpc+PjkQZ`t%U4FQBXI9-{rEdIAKYX{|A=Wf!}uA?sz zB)fjtx3d}FV&8Mim_GT0A%EigIq@Yt6`^I$c69T%Xd7+oHz~SeApZ4-dBi^DV@5!F z<_EtAcx;4AFM|Ovc*{UI5I{n=K+^U~-I;#~sOZo)x5L>CS5l zk1e!FoQYgFax5a=>n;-ryYhBhIsb(NO%7nL_b&QL!JNcjd^u7eJW57_alh}0i1uV5 z0i?$y`M6%I6fv)%SH!%qA%_VfduSjSbT74E{-r0KTf@q^RZFGbE{;W5BgX^)k7c?1v z$eBMjZYz5p993{6>5Fcwvr`a1 zk2kD+;b`<{LZFBA9fenNaj$EH6oipFYS<^I-Q}-S;)jj(uL$l)N?vP6!X7^qzLvDT zsut8g@||J0(DN%@obc<&*cTv3;4VwNWh?1X&MD&O0v_)Lzm`@rO*YEUW6a z-;O{an2M#}n$hPyHDv8+pA1X&{Wr#FdhNkqKr8JtQu!7)Df|Vo>uW=$%+n%z71dFG z`;|oMmy#F~%Pes0oTq*kYOBFl6oA@2!0d_i4zZi6D?=Dr^}_q%+z3WsYPOM642r== z_RxD_1|NBc44KsV>he%tX2?T&n{%}Od^+MRe6XiS*t+voDAt%t%gAz~`Q z9_2FU@EJnEx5&pBc|j)^(9ji=N1GksIPT4#hz?00I3rM3*crtl@;dLCg8j<(8UR4< zD9Ik)$oRqT31Mz?)XzU2Y`Kduv-U@W2D)T{Xjkj|)m7d2IV0M#Jb9#R`?Rp?{m+c5 z0CS_90J7T!#&038=a=#Jv_L#}x4dLqxh~@rS*`wf@YcCp9desyma609s3s(^{k(iV zlL>n*BZdg_m&L|UtH9Y&I|8%Kq5|A!z$E4w6rZplDZfcr9zVjSdIWbt#+iQe&N5uZU#AfqNjH-EBROK zvWM^qNNRT5o2EzE8%@&a9NpqbC(HP&>ZY6z00O2;fvu6ORMT0~x5sca0j7KeRyNA5 zB70(BpnU_gB-c>yKe68jP|xxM)k4JMQ$5$o2`&7vQL&P;(zO(pc{obP(Gg=juFC#> zK?@;(j$|bKMBWeQAu6b<2B?yavI8KYF=|)tuzPzZ=Wh3WxyS96JOLRQ>F4xx8@tW> zK{~jddx7>veUW7$`PU4ra)S$q{OEt)nU?~rWNS&l6m zWxdU7`Pk|ChYQJ%Umbi&N&4~4$Z=_V2F>=yET^7~EnMkn-w_Ym*PfK5I?Ne|Cv5BF z{$%51MmkCV{p){<=FY+_&$_ftSyEDp&Yjj-wyet30`<8s2%US#P}fkm>3v62x);3B zgTX4oLn~Nnsw+5-=Fh$N6;Uucuk*s>hO7Cj%B7$wCdt`&Au)AU^4V8|^ArMvB?`D4 zaSj37uZW~t8pRd76uTX-f2J28)y%n^_)8HOS{5mUuA-pq3gY!wVHtEivo1{)r($J} z{UWo0^PWdF@;Ak^zrs-Uq!-V9ah+qF&gc}0E0PjuA(n)Q5{hA;^!d;HC+`))uKT%H z3nDFfCnhF#9HL#nwOgiI2tpUan5O-XrTziT?n=@}_mI%)D-5^1r3mvDZyUrVDu#g)Ef73?V!&16RXA4aWs)rJKG5B-B`wc&J- z!3c_E_J25Qs!z^b`4RTYOQcg~=xG4LtF4&M+Mh#S%O>^CQJni|uLssHRd1=) zG(?v)bNh<#Rc&C;_wgWx>!IA_C58ZIMx1PtT@|3CP=(a9wuIWD_ICXlp@qtDoh(WX zgTGy*>>@n_e4qa@1F_323iBO9@uHHqT5?NTaV-=f?QYb$)(;mh zb{}1J`%d08fBc(Cc~T}yrqXLvGJQE}syW=x{#k}~Xhqp_){;Ar!ArLc*@ zSyKsEh%yGUHS0^dCK@+J9z4SdW-NZbl}Ym{K{GScydfod&PDh4P-^y^S`%i-%a4WX zO^Llc+z%(twL{hA^WUue5`Oak+6UR~0-qlVWlG~WBh7!LNzw2*rmPFBegCZJsbLg;Y;yeDGF+Q1T z7ge?~)=xd}GV@|Ap#Z@Tm(!xVT3iQKyI4t?b_1FT94~AW*YjD(u@4_}8`?+sjU^f` zmbtY0pYILsIO~lqDhlO;snMVcc%@VpR->XUF|3~=*;>ueQF5t%Fae|v) z;PG3UHLRRYc$m3}348v4C5?Kpab@XM8KoJ?#+CmO0sQ&6=8}d9Q#LX4t$05749Ht* zl7mm(Q_r#0Wz^4^MH+dLy$XO%@KAZMT9c#Zbi$F9+b2-vu>FzkZyUTBhG}oE~*nl#93o55zKIq!+m7AdS0+C2DVqs=M*->!3uyMVYoiEshS} zk6DJ1E>Gimi11xyS7Yyx(F8u>piMv-bqtUKlFACpN3-*-w}pKSCh?GZeW3MmUAF{X zBHN2P%edoaRPT;cLD)OP@tf6Fb%km)|E>lgj%=0jje~GqUHCLDx3|MEaKb^=>mxM@ znkF@s6ZrF{XQb;kcRDaaSB>%?7))i{`O^PjT%U-Y(S?qfhS2K2zx`ps5sum z>-=mES(Ys6QnUI!okxTjI;- zKzwajL>A?+Q3|Pq#h`K~E*#&i7mu5z`v4h04Y{o@nl-Ly%vMfd;AHJ3DXOvgmXK8k zW|xlY7xsSvB{MA>|%B)I>Jr|z;(e_tnJsku^-@PY0GVo5(@gw#X zV(8bqkL%fG#(dbn%li7$Rv4E=z?2aT5;ol@*;Og?yA-vvk3WE*@kAsoWcMc?rS!&mL)sS-#jSX--gi>$ z%l#k^2IMW8cp3chC^kr0!=zxC9AYgrN(!$qUf`XwS~${}*88B-Sq@H~>k`8kLi6Eu zXW(hGu_G8cTN>#o&nm<2OJ(vM_2Zq0&fs(H8zq&V=UB~>i+RZaZRp3(L2t>fqju6; zv#1}I@7l4oVL**ouWUtsk<5`MX!Fan^!tt9S)ZWJpKk52UP#=ZcExiV1q+on&U~|e zM#kPuKQP$E%%>9f(-=5@)_Z#VENj;War35(I)q9zOsk$CqWqKOy$gd)XV|=uLV1|l zAUg0f-!Q9z&myTwR+!9zC^*groroW#=kQ|I6X(Gl0ysTmm%sjxwe5BfB@zX9JpTkh z<$Qm>gU9_0>rg^|CjAgoz_2!P*dBxMhtB#%B883pj%_o=py`YiAh+lD_NNoyir(%V z935V}S^A>qs`+8YSwk+OVC6+zv!N`XL?vUAk78j}SHh2MG6Q?kx_=Cez9Ng{Gfz*g z6mRo9r^nWA*41w?6&JsFj--UjPG5cpbz>zY)aV#ecWHx z1O@3?9m@t)R7mgMjBTCq2@I~ANdlXs)jAIN2-W&dOhAhMyXXxR{8ph~k{YuDJuspv z=;rtAB$0uNLZAJo@CDfW(-eBveu+xb^D8rG4ydh&(?Z}ugs|r3@Hl3%2Dbjxfqw0O zJa*(hvJ=xyADB|*EhE3d)zx;gP9TjLjCQWmF}JglVUi6z~pl>-&+0PvvP2T zeu6m(+I9XPBwr`4i7Dp^<8i#6_|4*8MJ zch>;PC`=SFzhXrojo1&;yP&15VqMpCyX)S>6(6TJefu-4@;=W(2J^zuOLrW6!BC}>F!54&mO9DhjB_fYE%f>)2L7L|(~u%gUD?|3rl(R3FL=jzU7m7r z=Y;xX(~ynRK-6kQ#P`!AL?b?>fv(Lv?a{FIaSJVCPczD#pjMl40o*+yt^BGFXSm&k z+&RhmOf>-{KZ-CXU-4Vhh$nh+S95B|Ws(4U)qZ+py0V7XJ2}D0A5O!lX$CvQrq!!i zgBYAMi2?jwMFM!pXby71T3=cM$ZCzJq`3Uq@9|A3qGno36kOS0rDU`Y@TW2mmY0nQm4ATj(@kp)V26k&zqy%! zqzS&sd$b=ev3RJJ(U4r9Pn71FXbsl6c-PyuoxAn_RMozpNHPl3FTxq~!!0mp_~4xS zrnKjqRk-2Ulw3pv_xr%?f{rHI@}TZ98$~k=phpxkHOpXZTt2Cl@34`50$Xs*YGH72 z8G`weUX|MDc3O0}(89W>@7vc+*xC=?G+FB(roa9zsi$KU2!u`iU{MBuZ5H=nA@jMIp~2KKSY#SlA-Eo$b$w;8>C5+wZVR z!9zNn$5kZ!dI*^<0XC|&ws7cK5Ii9iE$xYa`Wt6Rgrf-qD0;2>PYt?9I^384*NSu3 g`~T3!gS&!46R$i)cyRHUJb64`s%XEcP_hdDKUDhTasU7T literal 0 HcmV?d00001 diff --git a/assets/images/blog-image-1-7-1d88b437ef046ee6829ccc174cc61f3a.png b/assets/images/blog-image-1-7-1d88b437ef046ee6829ccc174cc61f3a.png new file mode 100644 index 0000000000000000000000000000000000000000..e2836f617c0102cb2514e8526dffd9d08b5b14c4 GIT binary patch literal 81291 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 literal 0 HcmV?d00001 diff --git a/assets/images/blog-image-1-8-f2e1dae9a99355268c4fc214e858ca32.png b/assets/images/blog-image-1-8-f2e1dae9a99355268c4fc214e858ca32.png new file mode 100644 index 0000000000000000000000000000000000000000..2ffa00d316f0056bd941be9be678997a9aac99b5 GIT binary patch literal 159949 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 Z+Xf?^L*c|XFQkajeCYb5%plni#g z8`!3sWCq$BuZU?gZq=LQ(I>J?_l6Ehp#^@9Fmv7kBiDik7d*GNhS5bjB&f%q$tb(T zRr@$52e`^;#POOqTJ2QL)yPOW+9?4?+2>i5`30m?LCbyy@?o>ZJ5mdys?QSh*LEh6 z{G(RLIC8FcH^NJu;h)$Z{z0V%B-St&gED#0eb-%%VSjHg0o zwLeu}B}woJ{19~i$6ePQ@ZuhviKq>|2e=|Bu$?&3B&NA`ULSpFch2V};C6z7$y9n> z9QZiI$9$GHmoEH3f`*{JnB!{(L+w~!Z)RVwB{Uz+dM$l{zXC~FkiTBqq9$i(4d#+-~-7Ndr{61x7F{OjY#C^wIRwm`MuZ`+>gFXShK zB}s8%R(0`yWiokt2pIi-PWe9k1a?l`=n!u-wzws(%1))anc7d{W4urfaa4`hxH%?m zY7j=S#0#PY(NaiOsRt8@SFN!1mt#4~wd=Zh=@gKB;?Nn$)Y?xAqtCr`ClU_+%i)vbs;s zDbOULM!)^~8$&wVCZ@SaT$`U>7zd-JXsUm{j55}uyPa2|59xF*tYcIVI1sf*cUuT8Jlp-`L z)`0Eg&p4CoRu;2YX_}1OtZ3vk+354fu?fu4?Hv=#TVqWJHua-d^e zgldsf52oK<2xyQFb~yYQn5)&e+Uh(dgb^9Tf6EwI8Sio7Wzssf2S1j4HDGLK*LFCQ zpWv{+Am%cqXt;;a8~gQLlOg@FmgmQf-)p@>o`dEzVxF7o%W%nNiGCS_NG%7*@ydW` zW&nsEWjZ~I%tce=cJ+Jl?F+}UMY6Je(G$P8=S)vLiKLw$HA%pQUDH#=_Y~7csjHj#qY&byC4g3|ouy6|8-g3kK|uI{U9o?D5{eAVYT!H@pP!D`f@RMJ-?P;^?H9B z&3O7#V!@Q z5Z{(~j>^#{=j~i^A8QGElsS}X+<*>c1GlFro>o~R>j|V|POYK{2Yyu@Ox>ptO*n;} zv=8sCGiaZaUfuSy2@~tajg*~Zt}=m4_{)82wC^E`O7H-$T1LXO3cZ)6%*P~QA7^)< z_ccU(IDE|CE>--y1nnNx{sf*hqixMCk=$9~=E5J&i&)c{y^cx29+0@xde>;XDkOeT zuI3lOyX(5eb--)#XhY7cMcD=n0)AOqANFr{R2}XM1047E7D5uyi_7Pf`)_4(lEtry zqaL3gYmSQn6N@^@D8aiqG{I^v9yV5TRPljd;XbD&{>KF=EJQyBv~rbq7`>4To7;pT zYi8A*zP&-u+;r8cEJD3Gbb=kpD=zf-KN^*ps#D=*Vf})Z-Rcol-^fT7&@NC{)*7=8}R3OBMn2PeY1AsG}2KWPfD{H7zo1YwWr^ zON0sg_AT*oWc)34zRA}f9vy1sSLA4gBg#A+hb;}&z1g6f;7pD{&}_^Ri68xp0 zvI;Q!GJi`1Z5N?_m=qBAdK$HwL5>9s9?o``Elcj8{drH{^Oz z*naeG6%+k8H8T25im+ud#&GFL9j&pHcT?VN{stajg*E+EXXGzDm_9yUTzeZzz)wI; za#US0HR-!(;@_hE%X4KNAJoaRJe zMla+-&&@RNSQ>O>Z1Z{Y3&XshE6sL}or1Zwc4oyw-xk?tjU{)o1PO8V^#ZxYnCEKS z>v&#btyYl>At!zy-#W*Nt+TX?_`2Ct~ zhRGA&-0Ht4*3oQ++@B`mHOhJ1iaPZ^b=`;H`%BUPIChQnWr>tSRX;V>xnA3^Fa|Y6 zJm3XNvI3*o+h!S{L28l`rIgub(z0Ci8x8e2eI{dMX$3G|5Jjw>39ED4R4gv&_wg- zjkh*6DWJW|T-H@1Xn*o70K+%GKDoAb`GDTjCk&}YN;-Z^2TSA1Cf(|#4DC~0_Z~dl zw^)A*ash@(Z7s-7^@pGT-O=nBSi;~7GWP;r?TabxA@;WtgRd0Sy zn7_%AYtVht(@8EM#kW-9dJMi$gSr27Kabx1EG5bQ^0xqj4#1c}W-^zL!F-6f(s!OD zxw>li&+zM${1U}ipq4gHWzi%xaEBum@{Up4K1`zdW5D`mqTJ#yYcDq&R{m$oIZBU> zQZuSZC>+3K1a6NAlI&(KCz<1J$BeY@^B+;6zC`)N3)rk*UTN=9=URpA8TNzYKh>=^3cIcc?=574 zT_s%A!#G*brL-xL$Q??%1lh3FNZnT8S6_xbSt9-`$ljzjDs7a{r6B|NdwD|K7myS`{5^B^|V$Q)Zt!SPebppp0GJzzuxD zJ%a=fK`ec-@fDq2Vf^SK^u9S1JBK6IaEPp%pwA|$mTB;%yK?-M1prRS`%;RaCJ(u7 z5JHj*aTAj(I7x&3qhf0x1%48-#Jkm|{6{##AM1=(CSE~eq$D4F1G`E9irJX?@C)dQ z$DtMC!rrx8odI@p=jHPT0qD9|Jj*`lZp_;#ZIh75@Vj%1uXV^t@u9`CZ8Adj4G3)? zG_#9=q+aq50{g@&mT~5?m$o*K8ezfT(!)CujXJ1*&HbwdA#PEkjU*?}83}koE7#-8l3V=ap$v3+Vyc;r9Y3foD?$<4YzPlR#m@9lC435#NTs<5d34q zbeiIht>Ox^w?2lK7|f-} z&M^9qJhW;)69y|!h&0%rwv0=ZhY80?N=HN*aU`vt`Pg9}0T0b4Bb3?|bb*$#e+S`6 z!YjMQ)|2ATHk>O&)e#C@BI$n5hVQ#jE!`RsxrcT{tw5}^u?pP|SX+?x7IssbU7WGaVn&}bnLG(JPgXWA3%k6)Y^9h4Gh@?i)S7l z2CjDdzDfSw`ojdd3Xx}W-L`a1t`ATb4Dl%piB1OdAXF?&ZSO!CX6HR~59yF8RKTZD z^oh#34j>or=`7zQ&JF2l87pq1ZjUS9;eQ*vGC1Ri?iD;NlAfhv7p5QGA-iivX-$FN zn2_ATx5fLp%rC-8(6>WQx(LJ3k560Qg-%RNhWz9r`5joFHvvJL4(_vNp611%?DUi+ zFoYgrfUsCOWD(z4a`Fp=y^j}a^K3EBi!iG-L{pu&`aJ+yo(KbD4@@WNmg$b#(LAjV zKV30gU6G|SJ|J&@%ne}u>w_fD8pWfp%s+(g2u;uGpzd)rUubo0;}0~(RHZK6e&|2% z;!8EQVSd5N4je_xX-?+74IvKCB1#ql!?H_aB{^e_Y1$Q_*-z)MY6|t=Y7g-N92+tc zD5)P5RNII!qaOY2?|8fvsn(Z^@m>CW19jb0{Vd*(H!6zG>}+tyY1S}muS{5&1~z%k z@;1~!0UFy>$b>RGK}zFAv1?H4y+`km{{#U>&?V3*MMh8AC%_xjTI0--ewT&dP@V@f zR#v#TL$g7bIw@Xrr|oR+W~A!vsIS|3823Xxhm$7Ow5qEkc3r|mrv7UwJ*OuhdTem! zOe&ubOdKc(8WA?{;}y|q5FLJMFs4=z(dlP)cb@l!A0es_TEk@a%RF;vM{xDav}^+^fKnSvU+3PYc>XPtL9`&cxzOWE8hPYoS$>N@B{@1Knk zdk?vi+P?y)t1l9i6MyUa9O^NvUKJMi-eciOWlkT`!ZPjfd~;XjR?yKp>ZC8G69;es zBEg+UdKVB)O9$9_bdj@;533Xd){mN&_eG@u=&rm@DQ;d{*+R6Jyp(5Xy`TV!Q!&yH zBH7q!E#{b{W8jr}aq%lKJhyewlN>Lkqkwv{glJT!~N@{r5Pn#yMj7e<= z;ZW*G#8T~RL0vfI*wf@glXOcSW_LSq#y$9e*)Z*fP&oK*Tw$k1L70Kr^wFvv z@m@?HSFF9_-_)b<8YhI(@n$fPkhBLNu#qfiVKndeyJ+jZ5XtnxPn^teC^s~jl zC?D}!G4D2`I#rDDvaT+HwzBQwWBYRr^AIz~;N_tP#yPDgtOCcH7#ppjkxneyEAZ@o zkh+#{6*KcvF%V>YXMwh>*KkhNKp6lM(qk1qC5-kE#U!(OTcUKgvvRQSZInM{IHlJN zf7ZJ{tD!JiN%d$Ks1J!n`ofp;10eNIH69+Z4t}01PliJ%0pf>MSR`^2CHG8$dem?t z1Qf06$})LEV1vg>UI#`o%02H(BWES2bQ%}gY}+YuoTUCTUS3Cj$)Hb96B7iQM<4Vg zBX#+;gXI^QTH2NfQ&$b+Rg!+@s>MEJX>CSb%oRzdw3rfY6ib|`6SnUgbjSnu;!06eLp0i& z!6+u(->SfWt*ycGOYK8J9O$c1202}5)U%$isf#dhio9tgZBbvqWc1U9R^_5zEh>W5%9km+OwJ=1Q=@Xu<82aMlH;M`}DiRTITOv~q>K;YZgLI(FE==S#yZ zW41c0Y&C3Iz4WG+%!i-W+P%9?k%IET!0@Anx`5keG@kI}+xY2*ny9SNLdPg7GV*7f zA?lUf<<8lq0iqdWx0kXPCF|^bYB)_w5y=>B`H+mf=OWPvt?FSTW~Dz*IDqLhE) z089oDin@|Fdvo&G8_1$D5-*vp zsXGR)e5t9+NSb+B7Z&FH>A@@fm6R8@GyTBxb(5f-)4pqqZxn0?!}}$aQBZ~MYcJI-lE4|anbzt-y(Zbw!*VWj!rfdlNGr@C%)Fr_8nyIR#H#g|6~sA?+}T^kYAwtxaYg3sMBGL6*O?a=_CMGy%~}^}Y(>b% z1+%_@$b&78fTuO*4eSbB+f}mxVrriDi8>yh35P69R&2Y7@!Y^usW*qJZZ&8!&2I; z*GA;fB`tl6t)^W!uv8JPH16LW3+SU-oav(8cEG(kqT+71scLpAXfPho(`&%!(d$Lm zE18SNz9{o8gA-Uq6xSbO7vhLwZMJ%S(sJdT`5Sn~l^D!<<$}+TrN{d9z$)Nxk=+t{s8oSnQd7v18v@Hh2r} z##5J6#BU18sqoJ5z2{R7h;*3&Q}{X$=iD5x@USvA(04~tU=)wWBeOk5#AH%~!}!Y@-k zh>985yUKZ}#Xk*iPK}7wKQCcpd0OR3|b$| z#^l5QJJ!b=XsLqI&}lDSdvN@dkKL4W*G&1Lg|pDTH+9Ypj3G-(U(62|Wc!zBBZ%zq zi^jDb+M|^y2)R z>HT3`FVPNN--M&U51 zCRp!o*CQS1R0!B?&Yv+W_kht-0wRH;)k%mGi4%XvlJ)U?BhT{o15xo*I$u7OXaBx9T%XZdGk<5t7WbQfAm-VKtphZL!lN-$nDScfL-$$ce&%DbR(r&E z?pNu0SgA_38m<8C8d_}e;!O?|w-cYkX_5C@EKjzZB{jH9T3CkPANwe&_dKUY_3Fmi zmfU%Uy4^13CIP_c)ah>UUrIikr3T5$1&JHd-&I% zT+T+o0xw*9nj|Yf;e?*aI{?Pr8}$t%ohg?!uMu|V#fx>BzPOG~ic`-O!d5qxu3?0& zoI%crv2>t(Fhf9c*y6tTkB}?R!fE+aZgS}=Q?@+2zH@Wp42c|$^y(xKN-U11JT5ukIx-qm4$wr&ZJtB zzK&$w^g+Ro6zHVf*I)kIapFjcsUHA{skgrM+Wy%5;I}r+DV&AZn$Vja-Z6E$NuehI z3Itf;f2;c_VXuZP{cid7+z8;!{aDNCK!>gLVsZ1QIq^<;xXbZtPFOIu=qkotPu{7I zcID@}ts+Vz!sDC%CBfjGqQXZnPyClKe44SQd+(){UrFom0fyG4p_?xm8N$D}ZexmT z^m1L7mOi{+tc0rj?JI9ZteCEXXBa!HGWR-bvd(W_6Y^d3X{CR|s1xZr+X?YY7wn*3 za^mjI43UZIu$KH$(M`#Xq3@ivHiOFq%n-!iBJZq4I0CLjiJm@BV&~6(jHIfYmiZt< zx7}K8*K#5kGwKbh%zg>#mH)Gvp^{%icYftt8cIrh=@|snYnPfXrP82z-(%B^O;r+D z%MUIG-*&f~kws`Mao8!yT$lkH;$KJ{&v*g7oIl61VH z2PZkFqf0*D`|i&(|5ap}7$W6k>XWI1k8>BO!d_{85|usVa}(pPwJ&xa)%ZB7SMO_) z0KWZg02+N>uo5r}CQ4qJaA+^-soONVQgg z!6AK=G_$~<>IV<=nU7~mr&6OqpoZ+|$n&es|B8ocE0~X{FiSBzwtqGTOxtj8^jgM_ z{Stg+f2jGpJ+wCM{y4DSMopdDgA3C%9fo|1iJJoz`0n-6_UO(xA7!hMSky&3bs)}K$q4oQcU>|@W4 zkLa2DpK0y9gN*1%DEIACgLkw9)C}3j5Jm~sL5aEuT~QsF^A2hebhUV@0s9s1(4Q^Xv7ajye6l` znZJxw+UNN|ih8m2rlcyi>UO!@J>!7%tsoWcVOD8ZY7%K*TV4nV+2$9& z`>V)=!ZKZP?1MYcqUKNN=)#vK`w}_Hg>QZSSi?#&z& zDFu!Np{6_(r60(d-Wh(&8xcV@{mvs6NtYfHw}|u*`U2lNU{ce$6);Sa2Ivh{b*qNm zXqfk_x{SD~;-<`*g5oDKq*YU48KJI~j}NUX6I*zxVsH3*M%~=G=vQ~PO>X>f&j6#h zqY5a}K9^s31d3&wn^kT9p9HmJNb)|pagpY~eyV0H%GlpHu4;cFt*XAbe=7lbt7GNe z!Rvaui*`EK*R)Ep)WP4Pc@%`E+l5s5IIf8|&1pP9dd)*k7cEF1L%ygzXc2QBBJJNX zfq!}Y;|}rPj@WJUdp9Ts^un8)NTI&eh6zY>UaLY*Q&ey6^#TX}(hzBU#0p|X+3qhP zY5ga{r`ayAYj-e3+V-5q5`>^-;!n*+QquC_xqC;D&sBUdoZbz_ZyH&G_l{3jhFO7{ z0KqT#_IP<%TRw>}g=*9z&9bXgKjKXJ;(a3y8n=)Orti(KH)KXGTqne4NmBKY$=l(A z+@4RJBQ{dgS?R%o9dVbRJwCLlKD%ekAKQ7iB8dsClN!|V_fLJ&*)WIItyqEikQsA- zBOfE?mA<&?rKKcuS;F&F6!#*<(Q1+L^(GMWkiyA0y?0X-(4mD!d;u{}t8>^MCvq>o zsiz1JPAeifyGg1IzH>#=KHlsy1dbw45sJX*zayhc#k6E9HC`ybuzE19z`L{as~G?D z1%V$WT~rWeTIS+iV1l0aBL5whV$Re+s-(-Qw+90XKH47*r^X~UXqBZpn4@S1S$N*f z#4bVkiSm|Ir6!FKt3UbmQ-70Jy0Vyq*Cm*y_G!?{$9q6^YVa)#wn`m9kIn*c<;Q;K z%*p%$++*z5Y0zOUOnxidDdsZ{cc%0{W#RT;e{JL7vFXL8tjXQ{-CDlLo4{7~hWlme zEzIda5ZBt4C6t+kraSylqL}825K7HOcg0Qqa@2gYd%d(fMV?ORocVBzZ7J|)k#KdK z5l>)o+(fSPK~`+T1gJM(KLWaf~m&|adq)tCxrh;BOl(?f^Zqo@P+6bxR#a0gp^ zYm-AWYJ)7A2)RR?u9SsixaJ8;H@bI$sLzbQO6+SdtQv?whk6m!d`C(s%d0kM`m=Jb zp4#ZMFPC3xk?OC!ol^fSy1Ke+tywtD11j&s=n@)ZV3Q)}$WUf`zet!;8)*A)+vwHV zu(;qoUDjUD<;r^4{*K%Z|ErK(>wwqC&EyGB`FT!~q2?GyBdG}&`i{$YcHH{g)0o5E z1@k&FQA8FR0uV=QwF})>uNPcb@NJ;=&DpZv`+N^DxKPDc($N*CUCIQ0Z;v_Z_(gj@ zWSAlUSjhqKG+3DI_Dj{2J?9PbrfLNkv1W2uMH*>Ta`977;Awq#8R^)e**@N%lmfC^ z-Ii73!k){IA6kzh8`l)S$OFK^;9=S!_ipqP+`BW1W1Zsd23msRid~Q#)sDd6?2p&s zeM(p0iDx%0rO>foB%Y)n>2N+u*z@$u{%;WHqW_LwTr>{M%}DoeuW)^#H6$3aB0~*i zQtzTg`n+ZL21j@D{+@3WI{KQw9W{88IDuGtZ4m2V7+nO}_x)BK5wYQSi#NHC6eaL# zX{kNZMbiX2F33#ty^l&GN&FN~kru#tgzqj9EHyqqmB%lNmu^oK%L)hKi#n@%rQX<@ z+p|PR38KCNH&O(q5iac&M#$BcA7TzK1#YOw3zd6IOzAJr@F-kC-tY9AqY?(D{?ttA zb>TW{S0wVbMK4{pdchrVD(AAul@)SVW8Ua&zr262*ce1Z16`NBA?DfB%s?9NOnwou_Z<9P0)nbNjg^Ude{>$^-jw5Gw2 ztU#~t3;VH&`6x$9z)brE>vqmgk-(UM$U(O`fycD709?ax#4BSwrT@M+{G~vPI07?c z@niJgUk=mcg+qw~<_O~~Qof{3y;s2TLBn2E8dBYnKYnCC8tWAC1CYOfqN)$nAf)1; zeRPwCxY~^H?JBVp_}gtr_}+y<8{<^k+KgxCD@SIq{U=a}Zc~k1Kv?nKjc!$zArT9? z3+_R+0({>Y#VKG)`fI_iM`%7Gi)zMjEm>_Woc+S{zhM24OEwH(0?eFWS*;BHbhYQY zrSn@3_!lAOFn{Zjgiiv@TB+4u-ZrW}n{h>nQABx6CM6DbGyu_@9UQ@tSGtndnJ8Wk zk}aZx6-pzcoUx{g)T94X7p`YJK1G8;gotNZ)Te&;W6io=0D>}%ZzDn^6RK1aB3v~S z+zPC2CLE*~@7Ow8^QFykuQCWra$XO#%;P+#zc$9U$|mf{`P?Ha?tjE+5}kODDuq+M zrYYBokh0>-g_F?b&ejqpxI)4K3x+24N%}3@AxPM$uv*KZWBC4P7JKD(WwP{Z?-cs33s?Q{fr8xjgo-cG?@e&n2X#Kii@OXlQ6s-@V(|RF-J@ z4=s{Ekb?h!WARr%yjjT6FDqR9pj?{0Mp(ID==Gso6?1;rB1PkBX0FRpN6hMl`q|$J z^VRW)^ZSn~ZCUJJ!F)o2d}{utAF@^f7H7x1S!iBA19RHp+?yFDvKP&ae2StixD!FZ zC^JCD_BgrDpN$cd9;^^P3u*k7$Mfsc!<3qun!WMwy?-Bn$E6_oQ+pD58a3haEtCJx z#X0=%;^ZB*BN|Brlhv%Ylg3%tL}Ine!P;I9SVBw7;rfH%`{4&$4j*s2&syjGA`G4F z{*d4}UplUS`Dc7DbwvqI^l#j44O{I5&S&M_4nkyQI(H!GULDT`^)p@muz!w0bJ@Ru zv29LPifNthCzqP<&M%4ey8im~i$L-l`3MRgZmY_SaDYFg)4TC`Z!Nd_rlw|#;U7iV zZ~0TKmTb!Le3+sB!1uc|b)LBTw$s|#u#vgS%11F-`TQXEwmUUB{{=f|tnb?Y-RGAZ z_d@m}&sH5SNywIie>2r0H*dDA;TG>6)ne^Mvh0X*0nCGEU(v~$B#R3IUaOz5I@n>7 z1bhl`ft{9dh>3}*wj2VsuvJ+|ewa4j*uU+Wtgx|l+od<50jpMRq?p6485QzPzhT#8 zy7L3ie|o+f{+TS&m#%s%UDeR?`@*-sn?UraGKSgO1}9g$;7}}~v&ou7?ZEA6Hv7PI;6`1=6q{(e;ry}X8caWQe@gXhD}YYALSW&iydC(fQm?NhL6JRvXRvXP&B z{2A?x%thfdqACLoF?ASq(Z`~PFImRgge3R zfVI~l+vJD?wq54cy!bWVl{(S)5g}c6SNTUVJKh2Bye|LCN3_nyAFO9$R>+< zAPNm33p4+-znN{d$lGdNe?&5>Y#}MWQwtz}IMl(%>yYB%8H~_K<3; zRI;*!)pJ)a75=aDJJSkm#h+f>)@nX%*N$)bK>tVRT6*4dvxoAH_?_m}B_x0P@82>@ zWvUIeQFJ+?Uc) zIq>g&JJk^lxI|%KjcL^SLO60>@%g9D-mZug^6Ewvni>Ab1$<;l>pH?Qr|g%^Mrx1M z`8dt#Fa|UAER{d-OMNLWxeu8-}OfmcTBoxww#KQg;^p>BNyhyivyWhq#dlVhL>TA}lpMy>!r|v*7;48{IK+{xX?~ zIpVXs+$M8KqD#|zuC4;_3(ivf)(%w2ymco!L`H_M&L!88Cq?C7S%n#{{D|Uk?21&=*rX&Ctp6@+i9A!l9a! zUi^ylX41cbSKBMcB|FC7Ydl`%*Nu$f)m`w}HMK~8qqG_&yFbEb9&QEEsc+v3RyF#q zRFm^jPXgEWzdu!Hc}5q5Ip?whjH+L(>b*V&JpI4Jr@4}!f~j`Ax6W_6!fiZusAV)Z z>0mU5mM(Sc>At7D@!_8G0ewnMHQQaz>%I#=j{6?&6rG>q^B?wlt&a*3aNy*@S}e&% zg7k&-$F(}-KzuGAdBMy#JxB$dtx6N0$4y0?EYViKSiNHhl2-m zps-jtYCF-rI4Ib{wP7y}B|6kIZY>tUyHG4d@ynzc?+JCgpzWK4zD}TVbD-zVHl1wi zZ>}T!gaK(ekNJ1&DJxp%hxf-@;mzeq+jX8ZwjunFdf$g0jm+JO+bsQ-ptdZ{QDF7? z{SxV`_kz9qL{sgQ^yw0Vf0HWyLpQLB`ZiQ z*_M_uUHhV+>UiP9k+J=5(*g5o!#%NWjxpYQ;v~}XFDs893Jdh-4QAWZuVyOhS@C6$ zzrU}3*4sb)gYb|h&8|7{(~8lL%Y%Z;xA+2hPu}g^P8$P>n>Qa$*SJ?$0uQ^%*KMB< zUvuBURu!m~J5aiub4Kf{%kE5fboK$eoI_=B*5H#=G{7+>XxA7rA zbz|ZK-|BkiRX7rb3R(#1{pIaOX){jVc(XBa`$hhX#@0@CAt<{%F2*k(u4<_2ZEZfI zfBT5#@yU>1?Wc!xjqqmMpt;)VRyOHJlUpoNbZK;1wT@9r=k;wD54Y&Y3Lg$;R#wed zxfJqmeAvQP$kp=KH*XmZvJclr+;>oEY9%Z)j0}=a4_vaFr^X#bXuwGUSi7M0(}rig z%y~A{ONnYLyp5f;&nlkWwBx#!5jyAGzye-w-6I`@@?9o>ZnN=f?LV5Za-J+tc{Q3L zmip~kkJ|;LKAOeSR)jxzkplbA+a#*_)=+_p`TVE7_wNS-Hd(L>SD862mWjGEmS)oh z9t;PaZ-XjNanDF6`%5w-{P#bYj$QN}g*0y!{6C4*c=r3+V^&l8>QXlk5viOb8>yUA zzm!rh43CCjYTgZfkA>9YeyzYkxy<64jU_%aEr_*xmm=NrcKzHY|Aq7?@J|E^R-LAT zBk61Z!SQ|_D1d)5;TQ~yYywaSON zqwJFYmk={#M&sgl$)@tvkB@5a>=RQ&3~7uhZ+nG39M^WI2`{GRAX{OoWstLi84=`B^w%Ik~sN#C;>hs)vs zlpbQ=JJWl%!LmL^{4Tf0AIL?+w`UzDdd{vc8&;|yI-@sD^mPl}5ofva5-8b}4vzEm zBH6zaAD2P$Z|R%Ag8t} zr0VPYZK%$(hUnBZsKRWi$^K?6 z;#iIHd^01$m)NQVK8*;g4YzuFJNF72te?LlwiJ>wwMq!AP0G(MYWfhP7;Nf#p&a52`c) z`(OsXNB*xbGEi;TV8|0|7uC8SUw1R3__Qsn_|HO0v3>+=zXsu_ZH;#;>+tD!*1H#V zHkUzCcXHY7{X+j#yS!1m50Qmzv`~1?mZuD%RrT^d+ITUm_CTNjN{C&{e%>w5PkvpY zn=`8X>Ao+X)GrUx3*kL}e=Q=er3ci$m{Px9?KHgkE7Z0tcB;ZA&%!zkG=JDi+?38y z8UM-EzKlrO-BDb25`l>A4XesA4~%)qK$2q=f z3w`h}fpLAoVF5wrlFTFndcL{lb-BXKAIcXEFThp;xD=hg`t=Chqj2e8?p~59_LJAJ z(`O$rN6;Phh5YnEk%Or}Em^BlchM9(QZ6T~4`R&L&Mg1b=fU3Q+wyU1f2ryH?(z_L zTepTe>MC9mzcP>^J?Wmvs(PoyVL`J}BiEyg^DK?|j9EbQ2Cu4bw1F{=kZu%_G!jvn zakUc8VjwACFv$w%7E1L~u+9(In(ALk)_C+6A@IAct-%ZB^Rwu(x0_?DwdO32tbQTx z(PMjT&!S@?Y^o2a_v3EqIua){T+fM2IY2_R5n*V;yk9Q-LMhRfAu|$?9Ap<}cZgj0 z*wEFub!-yU`(mo*qwU5x&bBx|DyqNK82qrb%zifj`@7Pvsr<^?mZ0pdsiS(8!|w|L zbG71}bDfPSqx~(13*GAvKb3EHYpSXuYv&s3)K47OKi`Fi9$)vh-xrhaN;cQ5d;r6A zqE-D4Lw~A?50e4vYy0Sap!+pT{kE|6$g`tq=28T$kQ@YrN-$ck& zFVYG#!M8ydO z3vC7R41V;`s(gh=t`&dzF!xFpzn*mno? zAb4U^VEu>VvIzH8#`UTsN()02rIo$!<8P83HYRAg!X%&43%{x3G;vl|))zlCE=hg_j{1CgI^a}{S{kDA> zop?)3cU-5tYq_pGp=2~q5AwX7lp&riza+lho%h-yy1>AONW#HE>+yK&M51Qug8 zH-8-u3*Hd99(zFbO}fvuApCmU>5<&(5!N9Cfw0Jr5-eV9z<#!}qG1r!Vx)Q&evO(t zqLM}@IAm?(!>WYV#h5X@_@G@IgIyL#yCIM!Q8R`UMBR=6NNomgjF&By87`96o;)h* z$W2AVaNozHZPmCR?h^*9f2q!=j7oeIfF_&Isj^FDgEDmH_Tn7Y*yEi0Q^kmJ&aqcz ztmU;@9{6)I>!DEV`<>JxTw6qmI>#u8nFi?QwGgIaImHKWNX%U7 zkQQZXEUU0B$P~0Za`nviagK`Tr{kux-DdMM=`qU|-o@D`t*}P>+{YNJiiY|7m(g~Q z2bTX_QD8c7_v0VGK)vV3sC0=(*#8fiIH8$l4+gl{P3aZO+`{xy%E!p!ItGoQ7{!VU5ZqCocgbFOT7{q;i8u)>%S&^rz+Ujj>{!)d_W>24#Mb`uX;5=%K12-Pf>K1 zhC{dYN{7gO1+Dw9>TZRtn7Xi|jI32`8?nsvo%-XL4~{m3-J_k^FftEg2VW@Prnn)Z z7PWHf!k=Ue2c<0Gn}sMT4KGvQT)rQ>J=TRxi)q!rSVuxa`umO5x?-F7!3uxrbrw0a zEMi+OCicSrQ>2+Zou|1RZS^^D*780m<8Jil5cIq(_G-fI- z(l5>F?qTv!f(1_O_-^d0I-k>n(JByGRmRI=|NlVgHQy1mtA)JOE3=pPl%$>zZ>NZ9 zy)9nVVtIV}Eaf5Q(QTUpKl~DDEM$=*?#szHR++OnR+)>BtTO($uzwB(?u`9?lV|wt zHTe|^;risAegvO8A<+$C$0^9b-H z)Zq`f)LUo_tQ~L1uJ2v7$P&qJSP?vlyan; zpmmB|u||}x1vy^v^uYZZsSUe;Ul{~4c-Yuu=1nrX12*;HrxCPulcBfZ!qg_Xq`)x#4jB$uQWV#e7s-74w$l=ByG1F%L8c5r?fQ6;Ey=l5l*IZttTA{Ro;-_D1KDMYt8}H^JJ5YNHPY1uPOE7Tos|I~kvA+t^aQ#2Rr_N_YnjEG5KFpT`Z}c-^$0&-&N3hKuS;7qIkeX*f06%b%wCx%u7`<}>NK zBA31B46Ve=D5cdej~dX6w`_~*Ucv8=84CE$#KvyPr-)Hf=qWs<1S1He*0nDl<~4u~ zsSJ4-hVT1Um=yvLD+jT8>Ix)5$TtsV5WuGu6p)QAt#OM!-=WB;A1O5n|3L+2H4O&; z9bD3J+mE7Kj^t2Q$H|Fmk}4+w;DN2Y3L~HSAC82{H1ocsy;1r#c;Pl`c|Sm3En96O zlJ;W$N2ojebBTr`uJ3@(wwNuL0mDn(UjNaMLENzT^+_to&wX)y36&;pNr_{UP+oUS z7ten1@t!KKtGUju?go*^-_Z*wepI#{l>)VwFZwcMbB&HUj$XX-rD^)(si?Su!;HF~ z4bjlZY9Ro$xvq5Ec%o5mU1?IpQ;+Xzq6^9i9Rc(0(j$I38~R0I#`eVrj?~16eojf7 zB|OhNMXgKM(1fhCv={wLk?f^fD)XGF%ZVKkz*4m`-yA=ZAfD@2MkMoVTyHT1P=lwv zUP%>m4jQL59pJmG%S>?m*yD!EuBNT~?vt0r^HLFyPMpaiK>5?tk?`x!M2cP2p??%^ zFtYU5PgLeL2RyZ#mYbI6A1O}}sg)&*Ldi@@V!UGt<;%C3GB$3944qdZ0dz?w1v$5a z@@1tHZV|q&`d{X0IZ%iNk-Wo8PTM2OYpuZjwtm zyy$?lRYdV(2Ghz`?Ypz^`}fzxz%7Xg^Y|7w#RFZboCA)O*;KRBkM}a8=;Sw~%(#Jj z>L~tK7@lW+?_e1!tCMih{QLcPkNHPWw=NT@{?@zLw$cMIf1u?6deGP+gE}s;=-QA3 zh)ZZ$=o+F(_a#z^N2UWI*OQ(>E|LJUF7azC)BGokqK2fnn#T=3PH|25{Mw=m{?VZI z1`PPgZGCAmi=W%5>`}1f55T@LG&*LS@&dUOHvk|TCxu zmZhqq;;Ia(%kHq99`5hrBF}cD^JhBrlt&ImG(NCgd&4fvD5v3jVyhI;f%T!MC#-gJ z*uTBBkNELLqX)!ZC@^{pM#*V-9!bkT>hW`5kb5b)vVwTLwN>dXR+Z7hcDdIH^zRs% zU~!6C8MqlklF^9${WI%TIH`o;NC_)3@a~!^`L!C#^jqXHM4D+1#5ha8#3Tz7YOnO* z`aSu^3*zN^R#cQj9(@!l_G^1UoDth%11#{wiaZod;8A&$r7xnQVpx(AenD=HU|RX) zG5BnNASCP8AB1rX*F~Il*UhsZK?_sm;LIa3x7(8OKTsL#dzA)knD19wKPz3m7ADLS zvDh*$5N|4$is(xPJp04xT9S8{nAA5e5O?GGwciCMV^dc#Ca0TeD!SxH+4AQCx01YC zkY!u^RA>81t^d;p2nJy_Gn#qSfj$THD*8EEu~;U`v(G1;7mqk|5wQAH`jnz4i$xf@ z>Z~epjh0r3mO|v@W==J`sSiwwwm|JU<**>5Rw$WOtO@rOG|9!|ppNU*2e}|S`Orh7 zb85v--;*4q+~*I%M~{>K%M z2*rL~>RSjut57S@FWq}E9gwI=Je)9hpF#J5s5f&1f2JfOj6|pbt%cV-nbXqP$j?p@ zmx#5$y~g1)_2uz4aW`6xclCj$wCN$e!wWSQGktmAn^i1~ZD+&YrGdjwy779T>DN`g zd&W=K{=~7Gf+QR4nFKV|TWUnp#LXs);u!@J;#3uDQsVAL9OpU=;tQ;9Z$4Soxzc!) znOifLt<-a$oGr1x2q34_)YLC&Ea0MFEsvEFduAGZqnXDVm)b5f-rvnNGQ*5Hh$;Tx ztXshMqKk{wRPb3!-^#ovTOE6sS``YuZcFk3X-OTe%ipDa+G{9w8*)#Guxq3{M2s1{gR0h1jUT2r{ zapp|z_&1c&k=xb%hJC~4dF}VNi_QRz{r>OK>8(UDTURcb3^pu*_RlM1~d&QH10q1hq z%EJ<`xu#8?m*m&V=mfXxoc4-kmFwMY%}$G184rg|XZ&;FOOZ^IxO-iQ7tIhwXTiS4 zprgDb%?a_~U*lUs7xOYEcYHJu)M;6sEdh#seo52VQW?9O+1D*W^D|=K>f_ zmrASEq__L=HVBb)yby*x`W*2I<#(M$+;$6OR}FOas4*|4!3V0|Y!_9Q=<%$dD-M|W zY$ccYx5(2t_aJUb!1S-lIgIF@K0=R_Aa5~Z$5K{MAr{fRLHS>@@C9Ly+N!%wIn`C% zyr+a572>4uQl6{FW#)?SZUpplz@pAFV;prGwMdIpd}>|P-*IAca`jNY0^<|5n(J=8m^88vUJBtWe)j*xcO5(*`0ai&f;& z%@Ya-T4prDOw6(ygu>)2+C+$rsaw1?Ma}M>lU^<3OY?M^4!Riv)-Y53_^;At4Q*Ud zz-y($T!+hh^L|NBN*z0_M1p6M52Ex~{9&)1xFL0nZQ8aS{fA`O1Enc`zVpgcTQwVy z>H?WSNdYzYUNak?UO!KVzngq}=;P%(Nv5JCn39ci_(KX}OaTIf<21 zvbx0Gvg({|#I*Z){u?Z~G)?v`)}0_1UqAp%Ar??nD7dWiw{bhu;*hBz=DpS^yLEk~ zT}{GaZ!bT5QZ=ulENFM0JENP&N8C+DhO#v5*o#{MdRzFl=R&JY%Nk%PqBBGrN&`kL zixO6ah|=fzBj>~rqa>OTL18-Dd?#Ut@IS^+YTSw(3PR71u&Y8@np-D|7NA(T`~%uC zW$qgvNPxulgD9c9WTY}xS17laAt5eXX;oi28-C`={q1MtowntP776-;ty%c7-K&1F z2<&;>p5yPVdX>B%gaH#pkF}JL8=zTP(h@L<+Y6`TAKsORWyCWu1EIW55We$w|MW}O zrz&!5Jg{aWx7~J=HG>!O^sM^McH3;8&I?s~qFr$*O*(D{U5f|p(4>=)Xpmo^{*V#P=hM$)RZ=a;ghPX7`w~$}*TV`Z=>h)o~HMP?<<{h{! zVC&J6n9w?&tgQKU5s`LVqEY;9!J<_aMilzjhit3)MG?iTm!5PSBlN#sG~s~e_(l*r zH5k%z&v`3DSYaXco_-pq>(*RT?Lv_GDiDR^fcP5aaJLXPZwT1ZJIreXjV*`Y!tlOWpte z{%*k2J3wF`%pF;zqO#zh7T&)60{?gG-|>72m_pJ0sVL){_Qx&D1e3BSwVx`7&N@AG z_sVDxpaH|_lrcttWU|)3a(Y3CR{Na2za0WWfzo4O;p^y6;CHUeP(k|0 z`p^F#>fSP}s;vzhRY1Ct?(PtfF6mB@?w0NjX{5VBN<>Nl>5x#mQKW0p-3@1;@7~+* z`*D7r>pJ{caIxkZYs@*ve8v;^eLs-oJQW_%QIQeE)yrIlDi4(q1T&0~4;(`D@=YrT z6uO6-8-_`bYU~#ji8-%^C+MZ5@PpUJHx`Mm!d|A0vv$_|kh`(5!i@@#&Wlf0sH@d| zD~aNgw>n&wm^M%4vuHApCMY`Vmj=6)(b%i2VYvOa^{n}VYqZ-Mi&zqEnrUpf+$H0$ zGiMKV!Nk4U{F@2v+KddSi35^|!! zXTsCTk7Q)J8;haQb=H)qx#FSiDhNdcyldsM1(@P2-K$Ff-iyPPPVeA%{sxZUR@qIy zkB+i_bsAE?@_-2NB@uRKz&{lV8*Fg6wyl!^)#op{7^4ArVd3uiWx5TrIo@YlyRj28 zg`<@iDpD|^?dS+02k5pum}Ka!J44n(rHcam0nkH*HR71@wLfQ=>ppz_^4*r-Y8hto zC%4>>kLt3z+&nmmt(Y>Mv`v>)c(!3Gq&G!3qg{$4*4c8o)+^E6x|M-Q*ACe}%GW@4 zO^3?ld(`NO^~?Ka@oArkt;g?-cz&HH+y=Y##ZB6i2eUP>{7=1W!&@qXZ*q@Kme6MxEK z$J|6OkGb)J3FrwsT=d#vsGkS!n#B4p=V=F*kO71PkLymI+&33@jJ2FNaOO#Wl*bEv zG!g+-cf1)%=dTW4T1tFHKkWVd^ds0!k}_A4U_E7((2~hB-gQ)2UJ8>GQ=}=g6c ztl{XVynbY6=4oZ?-`q$k_|v(}cd6O?am4X;z_l+0278WS+gEAUj3?vB$wqDc)-H~{ zxz!L3Z3E;Sx$CptAB_dgjYGCdq-%KXN1!p;Fyi>o1hm=dZue)|(s8o8n{mI@vmzo$ zQH|d>M<2hy+aM>$fu3y*^cabGpKI~_-6B`?tp!h&3f%vd_b)pHv2rBZVNmTcBAQ-O ztu=wv0iiz~rng>t1hoZX7IbS@AZ6c7e-C%>I>!sZfQI@$wF=^`>L)5t;!{5Q(Y=Cl zl7jf_T>5Oh^A;f}e*)qHCf@5ngq?lLs#Oh&%x23YvG}z8$Sys~1aG^y)XnjQik>FC z<^z_*ES&<_1LN`ODqfJUm!SwWtC7wUY~+XmM#Nca18T-TEo{HJZ2%KUhn9raPUT$D zld=$LV`w=PLh(%4<^JxC^iDK|r~HOH&aoRJdf-qhJOC0F=N_2N3dInw{9G|!8TzuE zL=V7RtbNsnffqf>2o$w^HTIRh?>jQ>HM@RJq638GAJcgxa)~ZqwHDrWGbBjuFJ_8j z+#5n+on3wz(epD^;%K~+FozIb@90ELY14-q_kP#@iW-Y1LqI3-CfP!$@|F7CRjRNR zF|$?`Rr3Uo&cSmWp}IyJtxK%Ri@tHf%C`3gnnproG?=R#4Hl=cqV9RexdvQ=ACNrRWidZw(TS;K0BXMq#iTu92J%revA-yX~wl{Lgli@zF+ zb$OKVzw=6ZzADDX%c*0!N-8etBW8+V+^Zy>a@R9eDUO+$<%~h|XF>jc&6;_**S@t+ znVY0dFSv^=+hdC?dzFeTYy55+?q15{Z6mJ7Boc zTfLil=`}Ye{P|BdJv}{f-y(74D)n0+0Edz^S|HVbcMCSqVIg>Iv&$ctV{3`!03o?V zO`T`Yq+72Hzzk*vwEwC34WYG8ekSm_bA!O;vl zwKk(lO$7hmf$x*qPLydPD$_pn8IDa)nZ0DF7^$0Uc6$2y5of`d# zk2u_E2Wa(5JSLrFSQtk7etGhmoDnos_|_&Uq|nCfFG!S1{;U<{LXHZ~)2Jzin7{AW zXOq>hZ=k>BBUmMpf<`6DE;o(RjzQ#7?MB@3-G`f>qFJH0r($XXJ(2A?7v80<9HW=v z{xL)kuRN&^cYr@6pVTZyaVgamTooK10m4IBZEbB28JQ1>pyX}^BU>ODJwYR%_O1gO z9W=}R`UqQZYqF9R8V)rHRHNjI6|)N!v-#LP4h*qbq{DW<$C9!NdY>f$V+AZ$;Oehvyxwj}#RRzXEYwNV=`2+tVs;j`zl zIks6iULH}|@nsQ9c-#x|yc>9-)~p-QJ&hioFI<-KRoRHEh#-3Wa@fs(21nAf`?7Jp z7*MZXvYk9xBF6+Ibae6fMO`vvlO-S$ru4~fYode!=7J70`0-sdITMpkZJ8$93&;VP zI9VE*9a|Kr)pPO1A|7zlrIBrsg~zSUw|EzMoo*4(ccj|l?R9*@r{F>74}W=hEg?#n zh&E;%jv^NBJxHL3olEG$ zuY*i@tTE9=Lp&6)PM#USAN!Npp&u@-1Emi!JqC>mT_&G{C!}47M}H;A;DH;JtzR9P|Mt1}IC+_#XAr ze(N+#6AoTUfSZ0B{!|=mj7qjkDw)ij>>vrp2+F3@5icgl0~(Tig02k2gDK+~lW<4F zcGI?ljrb-Ipb$K}dax4P&H&=n!|v&k$gL;3kNI$p?H@kO?`7gR~iL?l?V z$*tTkIV>DCCWPlPBxD0E6LHDxwICJR=;lCgHKGk(O3*4^fLLI@pD)NSZXdMr)QAe@B zC_NI#Y$dA((d|F(*TX)$Li80Tkpo7|QJ(3) zqYJA41ykVCDJHXH$yc#+6luinu{(9KrL$Y*r>AS-&jcTu6gd~8R{37n9%xzqYmu@T z%xKbf7TOH>+;_;l&kAMi>?*+V={hkdJM#)$Wd@`|NdI%@!5vd8l7Es|k4Npk>oPV>uGgIk!`-jN~0)cCz&mV9Dn@^R!8$n&S4n>3rgJ;E-3yzp~KqM5+Sx zfXLCKoAL~C@~RB|-k|80w@p~lHWIcWjCUI*wBne#!PUfZMBRV-CzXM`GW)>vx*#wL zlyRZ?PLwdyNZmu4+1>XIXd>?q`AwQeMrr!9HHS#2e!GSo)jE#ZC}x1;9bJlSmt#(HV}DBjkl~-39%*s5Qmu`e_fQ53 z<*cbSZQ$=BYtsgt13RZvTvT#;i*)rzv8MQEf1G9y>ymz~eZ?IAvOtUIx_FJ|#7{^QduVX9MZ%7L^BC z+?$g~yoPsUjmz*@!@F~}y-fvbt|WLgq1?$a`ycQwz?dFJzNe`2KxP^|}sceac7} zI@|xbHJ0|}2nu+f>xiJcT zPitofkw!k|d^0$E%=+6KOstK^+n|e#S}_g3u^u{!2XM;CPPZphg?z7idg3XI4BGtI z&HA4D^6+DsnwoALVpt)s*f=(V4C>eVUz)uxw}hBjb^XAR9%`stIJ|a(fL0c(_v-qz zVv{6sGNA9%K9HCjk~<5jLzm%AV@>@Mk=lJt+jo^6=9)GX0X#1{Y|u_!=&nA&Wph@G zfZLAhA!i2|evqh$S_`O($^xla#Cty8TAL{*Qs0X=@~)F+;jXtIRlmI*E08*`d>Ws) zeWnM1aT6c4)E-!cz}=Gc7~G_+e5b6BpF**4V;Mb>-75F0$h=n}8w-NvzJF9V1UmLO z`4)S4emdt$$4@1PQ&MHST#od5fI>(YD-x?ac!T6`A%JWbOUN^@QURV?WsacP>Bk>S z)U>}j7CDbbi;hzb`e^sE3hc8PfOk7qWrkFuQCV@aKdCYAbL(27Q(UWm{jl$JvJEvF zcKA?Fx=%AaYbp?CD=u8`6%@qeL@eU;sMf617fk1MHa{d(Ci(i=u)*U|ep&Dw>{2^Y zJEarx_0@>w*(#5aU`q8ZxrR`i|3f5!K!)=3bF@*GjAF5&s6Q#&yS6f&rdQX3G2ei1 zP5tcIv*vFNVymE;GGT6c;_E~=8AVMW;wl!sHG3ppiEv8sl$-aX=9kuynx59CA8mvk zmgArDe6ep)e*pp)oW`?y=Qgcz8r3hdW-^Rug>@8O8qI98b5;_qs7gEm@@kv z+qeB&yW2O=aI#FpLqlWM=#zwJw7Wgq4F($1t-1?Nzy~>@2g#?b2+Z`!)mV1 zzH+U*^Uh}Yl0M`8w4xi2&& zNdR1m0mL}t8v*X~o;oy9q@?r9C(kSu8gJeWNj6;e!%QXMFr0%06?vm>!l_c%SWN?XumQqD;yqmlIJ(3 zcRevMwxlODkCqCLb?&W%* zAH7yDeN8wzq%Uu(2RR$HoG$zf@FY2B2oy4muWpoaSoL*9lO|gCj%56hki>lF&XdV* zXxb+5*XexIe3yN-ZRrZn7Y=%jwlBcm8gg5X5PZ&0!K^=gbX z-($mS!>ZQ6`{e&x2&8D zA+Fi{{w1B>AWL1i?dW6`Ymw_Isqb|t;hOJ^8b%}%bGVwC{lJ?g$UAryf>>pI`F7cw zPs|8J8;nXqhxN?>1jjzlTgp>ZCRMXBSDMP{Hrd9Y&d}7n_YhWFlaTmAj69E{C(Z6| zE~nw}Uc3BA*66XVo2O6`xJw&I=g=8nsPe3yDA&^=@!NQDxp;eS1|YmD*YJV8VWiEh zE8nACCx5*1lHbE3In%1ONtZ;P^+?xlDCd*f5p-_;``8N|+T-~hBqi;kPh*R;rAonDxf;&r zQEN*E`L`=A=kuz<9|LwnVsWlt-xqgQ>?ex{OE&miHXoXg#v-S=Zm&-h zLs7Q+LDIrSa96=}c*b7cVfQ3J^5w`+w(hxw3~uZ9VydqvKCqz8`scd1-A1fDzE;c? z(wld^y$0a2QCke9H%p8))S!vmYN{FFcU1vapw;G;(51(KtNut>&B4IcPX!>-$ z_gO)#5R-lLUE^Dl+3H^BYq#CEbw!mJz2gQQa~&8QR-c2+T>>r}I#5nfp*5eZ(DSmHuOQEv z0IVQT+CO>murF^xl4zFkzHhF%z|-(_!^a)34XSkqlJkU9BM33#0D;Y#BmupL~xMk|%4g?x7}jrIy7JQGNP-+x`R33u1N+snubjA4?RvgrO+J zAM;@*QRizkZZ;@gPwI2nay$6#+o*?yj=?ioB_W|lfyU>$^TB=y3$wNlRD@{NXu?8k z$!KO>C$srtp(m*-l*9uUC#qOG`Opn{_Pi#!d47V)pEmZQh-tKi36@`T)5%WtqYF}iN z*)ill%1;*s>SS*=0bu94gI^vMNl&7n){}~uP9&v(jOQP%Z`pAt!82L}NS6!@qwlj4 znKQT(VNbV9ABD0hw_L4{eX+*X1JHb>U5I{Xn|)oxQQ;GkeFyXurYe|oy22>-|wb= zIXu)k9`wc22y^e5Uh!`TUg8nHaLGp2^@R`#lT1u80_vgt@P6CAP&AZ?!}1lh0s;3= za!Oq!bbUuJ1HVm)=von*#X+H=e2a-53;>MMR!=*jW9?R=P& z$kLiWTKNiv${N@QPRul?$ql2Vojs=CHPXb@azWo8>#)78A=I-Tl|s{@%ysC}_x8&q zX9Bdta+BL;**)ypHyN`uoXw>g_8nL_Aem1$0TYK(g;2p?z~hx@mpF)wbrgofsruY_?a{*p!%=~^*|Qze9W+5!+-_}tNa z$Aj299C=uLletRv_o6@fb$Su2c7DRutZ~SD>E3OehGSJNh&BJy<+3O$-e;;LQP+F0 z?TgY-x*#p!eNZ5+L*>I;Ikr`5)Ww{Q3R+naC`bgOlF_Nqwy$I5punOKeON`9KmHvA z@wD-71C7&_My4rNZufbHI#;K4X;1pHof*0Ii)*VrH!!?UWti821(e)P%gZLu3 z)D72+@GSa7TI(*d&Nv(v%@5aJeX4Bu&0KnZFS#Wvw$WLw-IDxJ53H6Z!-{*J?3X3f zBFRdQ5TOxyO=mf=$%iiSh_gds$W}Hsl4TUsqY2z5xmYeJO2AP#1d64+vl^$F90o%SB(Y-k{9CB4GcV9xAJJDbZ zG~C7O*m$jTg<4$0M?m_n!&;nD%&y_moY+mA?-xf!%*`jN&||~!v=hN0ezZ|$V7oO& zW8f=$uA6@6G1gfv)Y;m3H$OR4D%MJK_J-<(nH^+6j~RZq+ib9DQ~c;o(fPHPA8o#! z{U9_v9F0hbLNpwCX^`4p$rB!8guEWWy7>TYA-&KX%0dxOe!v-_3!@>RY0ovfjxQk; zt}NA$!X$ze{(~%#isB>@&52d0j)ULgSMnrus9_>Oc)E3hh->H7=@^|=e_<^~J)C{! zoQvo1lLe`vG~4UO1|$hoO{m!y#Y(qCG(P1>A1Lz$E})QIdwnn!GC4=BA3KYYJ#vVK z5Og^_$@)snFNz_^=5xVz2}zXY3xiX!_VEpCv=EMhhho+D)h)>%!dlX5a*IZ!;h92i zF%mp}d$Drm+mJ(U2m@kZ)L@dnc3-0+XK4&(2b(w2p+Ku!7tdg#yj0QL@v1^sA08pl zP&6C;S)j9Gu!4rW?U@QsGNZ;Tsh?=Ekc%|Z(9Tb>0A~MMklkfWYQh7otX^mC{K+ud z{(GMq?Rn>OMj>xf1m~0*olhFAmzS`YQl*+&NqD|#R#xIeMjex;;h$cRjcsJTCG8Ez z=v<8}#o8;ap*+t6GAXOozz^YV26D%M`kEiu`vw}cu=d;9)6;V$J(I{Ph{1q;uapTI z0YSBefm}W6m7~Js8oXCyrL_sEU`H!RzbC??w9@T0%}PbUZ2kOFT1#vjxF~vpT&CS3 zyH#Ivt1EnB+s9*a<)Uyb-M}t4J1qF1&l4I=AZw>sq4+4$vV(~4YfE}~N%^6U+=;7y zONO9z3aYn>2f^*n{W|~q$JzZb?IE}HjjQm(YnZg4suf=Fq=VY=T2d1-?Hz`M$*_bzFY~=mo6&HCZp(;PKSlVG6!lff+ zVjYDXXu>N*;RSFPzH((I{>}s4RbIhfTH1U>eN2^sqLZA#Jd7%vfb_8<)&9pU-HNf| z9uloCw1?Ob%vg@&kfT44opBX{%NhfqM5O|^4{{a6KEC1HGstQlE76mu<-9R=fpU>c zJe93iRr>bzEWTHpv|AHP0ae0SJ$qA$@4MS&cBTr#?g#%L#9|u9CBrp)!*dL%yGZf@K~YZVW6sM60hyo%Ch5#=L0_6d zJz>hJ8hXhBhvo~>i9RHNwH|$@8!1(9l9+;Pjb46xfM^(QBIqvYyU`B!g;Y{B9k_*9 z_<~+89=)4pvNoZ*vlOq|YF+$6w?+r=_w%J2!o8c9swRDky9Q;&mD!N2O1=eHXHSnY zx8h#fWQLM(mxD!eV$_hh&(`aT<<=Atc&eQZJ$~|meYauW7{@UqcF=IhBi`OX2jC^o zvGdwUWkQERlQFv{g{fHWvDw=j)#RO;p6>ouQqk5zY@y-q|9IwMwmG? zkG%Duvv7Jvzh0W|Y@;ioMuSz&|A3L0!V_Zvep zb1PH#CnMtL8P8HLyoMqI0=~v!k{Utjx;r@GlZ2Z{pM2l0Y$3SP9_(lB4`1q3-&viG zeAl5INt^2-9|p^wZUGVm6EaV_uxa`sj6B1_=Ve2T?a$uyzz~_BU87TXxFV)?B_pu% z?l}(&73A`t9Wy*@SPe#y66%Z`?}L0)Vm~S9$Q%>A`8ki)Yg8rQ8rxGh6XQs8dlC`WRVR-*&wCzM489X4~)^d zS}AeqKiNWqp?bqW$}!(fAx}@~DnFS;mv*t|_GKQWZdMsr47tLMiqN z(E{G3UgF@<@*EOwpP15cYtOIEC3gtv1L9eafK?Y#NVUEBgjeA#R6cv~ zOeWHr#lT{!&feYd4Vvrg))8)P##(HZW{-V? zEU7|QZyFEHC;hoz-{-N;O$ok`tWXl(97gc2s;-XN!kR!k-R&2jyK)mxLaco@qntXM zh*?MbF;22DBFyc*!c;2XnblFM3vWu;LVI)Y8H$8Hcsa#SskQ~GA362hRm|I>R>b(< zyM<0zk2YuG-56O?bw~!}(2vBuUhuH~>Mpn)KP7FY`_^bU&*!%B4hnU~qX*55LP-ei zMA(1+PoBuya=eEqgC>Hv$?n!xV75I^6y+J z*EZ#@%7$*MSs>*{<9!#s&#U0uM9-3?4h_#4Hf?f_+EB``X>=82pDO+Bn0-IwSqVgt zir`wxH^MU@#*!ZrtbgUFQd!|Qxc0!ODbDrMZmX*9ToY1FQ~K!F;&pYJZKYP6Lg4El z)cFas)vxPQ`a=RPtZN{H_qb+Za#$S}b%WJ?!ME5ig@^s>k96LOW|kuW%|%sp!X!GQ zRkVmdVS5^2^msIMJ8Sr9XxOfU(zDN0e0fGM^OmUaPR&MTq{!Yz8gbF2{3%0M0jCbn zfbuWdLmolUUTbW%KZdXU-9z|ZKOHA)iyc82q`~04gPJelM)?oL~AyMNmfIjQLHD~b0It9&d;nsitCvNyx zjU6;rgn4+|QL*EB>AQC#>$cB;oZLS@{BtR(sTL21uaf`~#gyHdx_-bNq+($i8u;&L zr$*bxrVIgmX_of^Hj8-T&W_DLBY@x4<91qFTED-<=H?((?RnRKRa9q$b-0JR>G#hM zf`GrT9b42f%0u;fDP4vS@{|uC+^DyRjM|!{^Ve0MgPX?#M zo0UkYjq_Q-lK-#4>%hI}x>QMsSEIvk2vPkS`^a<8oh9Z~m(yIgt$)vMs)mEtu?gPy z5-dG7dKX&T)-rugxvTR%@}Ta4Bw}y0&9nHKjQ^(JAeoLp(}cPzbK2WfI5jI+b!?dP zwDANDvf5u(gS?5?ra@pQn|QXjFMsasU*Y_5cIn)FO)m4&dv$KKg#9^}S}K01<5>hv znn+(p!`O8{*I1zzht<#KqNt3|x~QhB)lw0*|G`35bPEPx5^Dy0v_1){p^~t~?F3Tb z=OnNUGbtnxu2J=Tsm6j3SWNO>S6<(CC5Yo^WwE4Rh7csPJ9WNDuepqaWEdOctp-ZB z2U~s$3@TywQf9RRN-7{nD63s-NzOd&8o!#UO0`hV$^dG=|J+*VR8W)0)2^naGzgoo ziCkdh9-5;`rA%UyPa)JO>SeY*-(0Ys+h;VlPrM&ehpJZKdbOVH=c zp0#(Bg-paxNhx-8>^W|Sw=Q(DY24nGohtF9f!+Ux&1xpUgwuXh6_gQ+e&%~RUsmnY z$xQo-&;~yLYZ}<=aLLhzqOUiIgA!0bXgaMn?>cQa`W{Kk<_RPvv7*`bCx#^=LPjS< z8+>mcH+cCa+_(>MZC3lX4H-BwXd@_20qr@{y;)vRW;9Lb@UDGOgCnlx01!^b%4Q~j ze?w+{7Ui7z&QKK66XRN4be>vXWL3TP%~+;KTNY&h%r@%)p8{U#+8?%#+KN^z)tKo!Tk;e=Yb)u;7E?aMQoWkDd+DT-d3mJVraoqKOx{p(3xzGu;_*K9rg4*r zINw=GH$}INN?k~&O^r4{Pz4=Z-)G5lbE=@}EyW9mpFow1+P?X)@huHDLo+>qiy~BF zPt`jXGy_eFsV~;4Pa(KHWkC702-hGEIK8@((F9sdUS*n}ML)hOVJeub(g3on4+@_^ zoRt|4m1yL2Vxit~rSR1t0jLX5B+p&}#kVa!m{?xtHPBP3jVV^}>_Ze0uWATp7cqs( z83@v5zJ34R2NWDAK;`X<@XeZSY8}u(+_3ND(eY@fGWASIPA7*$3k~kHbOS<Xi0c&PlX@Ll3~Fntl)Bvl-7zL0LyX2`ti0 zr@mQ`YcJ7jN|6jkCgJ>3gR8NOFVky!1<2WSL9nPVM;~NsfUqQ*+?S8)vu(KjRc3M^ zZA6*~u7Q=dcnT1c)nRw+fVn+?5UQGSjA{>plL0y;oAzC?Aj7UB8B0KYczF0AYTL)> z&V2W;zC!zs2Td-(Q_n-B zK@&cCC!fwP@dS@T>gr^Za=-07r$VRE1@!B@_pP#0i+V3Z)h|WrO9ybb&i6y^{oK7A zO2S2>)Dx@YN`fW%h@B4eKF26PD$d8Tky0vZL>iYuM#$q{FrCveP%_I$r80-pYD_bO z%Qjzy76b)}By&yMY{Z>BnIoXbPj7F=U%RArQo3P+h9(tr?M}Ro#`|8u!>J?7*YUc7 zEc$ocMCuP$KwaQ+fg85Bjkc@5fL_AM{>G@mYSqrFo>!p?NpZGabfo?~r^##z2p3UP z8_}T}OR1_R00Xrhml+MfuoHki)>3*+X*%n&f^u{agw|6%EKt7O4Ya`CtI!Ns0Zn6e zVwS{|TK+5s401{c2(^>vn%rdC{Ctbl`*HZ9Ldkg;(wOw}yPNmtz15>U&(bY8Z9}i) zaF%5OHlWZp^CxDtW5G+HES1jfpM#1a3Nc#NKC;8JApFbbEj&0}nC#Awy>GovGw+eJ zPfp_tMH_6yqkJmI=q?u~VSC(#iRe>Nr|8gV*Gk_xt*6L0n8{QzO}jf`U~8qPt1b<4 z7aDcK5{zO5SE-k4OKkltDUT=Mu}>DhehGWBPa946JmC#A?1!&!ES&(AsrSb7_${sL zHvsvmu?rXVEERn+1?t*7W~2UXK;7_xq!9g7tBH$q51kX{jJ6WwOMx zI14O_R~WSEOArPp+UYfgt_hdC93{4YBiYvVoh04<8`}`h-+mQnsx4FDoWM{qN6%e3 zPkT-c=_%lU3mHuUTu~M&VcHuJpBO~?aO$!)&q4i?<|PP?kGk%B#3Rp>c&S3M>odNI z5OQV&ojTd#7~<8<;@q)IQPE7CO#w=A$L50@=PG7_b_Rn!&)Y8=5BMd9U@}WGHvn>0 z%NC>%41Xv(huNX=1cEwF%K5yn#=P%3F|iXga;1@z#u}5+9ag^bLt%I`o@EoRPQt|# z5W!FJ!nu(NuWpWCkJZRv-Hyc?W!e7cnXmW&uLdg=0$IbAdF__d+)SZvr(b8Vr_hru zrTP|fsE1iKM-lr$Gl8+rMmp6(OI%_v#ta2dGhl!?-TR*R0a4iB z^^4o!_;$}4Xxhqh1uh`d@ zj9BX4v&-n#JNJ3YNlCO~wuyQ2b}+tarD}EPht<(h-^;0l_=1_?@cuZf&-e*C4o998 zfk7JyH8q^rk{84%DI5#ZR7?v^?y;z&pdbCZ-jB_Sogx<(n^FSnuV?hZFM%vB`B-uI$JRL2QVKp*XeUQqUF~np@|jwR zY728Zj*QJtiD%u#-AjI7Jx*oiPgeynoYiRSmdcNoxJNYrY9bLR_TUocE-T8*Pn^CO z?fT0vd!A*W#ubQAVz!1O8(JOMfhCtY_$%jD(b)~T^8(Sq7yd1&cmgsZmwX1N6$JpL zGuJ+p{T!_+4<D_4Hu5RXNu1*~Z2r~cvP*6z(7aGRIQ9Mns*|jk6*w`vnDv6Sp zp*Cx@$XISV=gY{vetzB5U_c5Kb3rxlpgk;C=O7>gP3Yp_`{HA)fB*gP-qJdbfZ=iw zno#pIkFQ<7kN)T7qTvZz+QniJnmw{G?N>;HV#sh!N|*P4+`gHm*7M`oN0S-E}>z<-eW8<(f>_G_}Ni_%wZ@06Zsq= zZ}MX$fh5%Jy`Da~^r}ts7@KBPv$iAmpu0IYMh%uOGr3{)bt-1sO19ruNerHZ?eKr6 z4My8sp+QS~D%`C06(ZB_yjdmq;$xXYvX@R(Vf)XXuIe>@XVdL$T6W7@iJ6RJ6DZi} zScC%#etw;!qgKLtgzwj%ZwiJQ8X8EGjW#=nX~`D;ufeGw2LC{OV)r;PW(Kopc8im~ zjz+JqOg3>$_6FsSrHN1Pqis*p^GPSu#YR%NG=S^v0eoY~atPAGjC8>+IA!|f$8G%X zTXdzb%9Dzfa;ICQ9|shegq>BrHlxewUk}3z!)4WvbF-c4d4O;3Uo3WpVa3^^rFr~w zb3Knv4in(y-;z$CdW8k_j<1i|IX5Q(GiIhyESvnrO4W6)^rb|}fUGaT_`cG+Jq+k= z1(cMRy_jP~&KA8xL-ftpbL0R}dv&@^Yh`8SrU&}3f`m#kzV7Ja%fe|5L+++}wv$cRXnNDhHS(s{q{_qNEQy zogN=gPESu?n+-c0N62j_9f7&f2WrU!cwJwEXl=eMc`CXMmEMdl6h2{dJiPE-A0ob7 z%dA`lkTlrVM_tOrhGcEhI`%_3f|S6cNrFQoc{-2q1;~>B*h0j^BAIpW%O8q$CY=F~ zpvTPP%kzMUhv(&g%aD}S^K0S7i`{qbaA^X5SGpOM{$#m>v8mJB3Dmsj=@3D0jQqfn z1wxriRX2$QpK4B-6urJHl>h`ov7oC{$WA)PShbXUI=6iV1a64@vCFR(?>bJa*`8)) z;3hMJtbH87dGz{UcKgS%wq59Ea9J7alYqib9B6{k2gG@cjDlb>v9Q$AB707)CkrKx z*PL#zp5qHxz5~um|JQVa=O1;14E#@vCd$>M0Q`vzsIss*gaj<{SPa?VbK7OT;c7=m z1yJY0hZwFq5_9aB73*)i1 z(?OHrvwM8H zQD_gu4{WCEWkBdysL}~rWq!)Af)V_T$EX9uWHKo+#C&*~Z`P$pI$?&Fed^ z=a-?Dl+de|nxpXg?P!{!VU^qa_%wUQOjH^y)M6XC(9qKM_h3*KnnkW9eAp$oon+A~ zRM>vywEdHru@tmaISO02SFF%)(M~G>wzVZ-*8Bfl%Bc@a`JLNH(-V1u^62D+K|eD8 ztaIf%P7N6#gLRLiH|$T>mI=HA&8Mu9tfn`HUt!`3^ym1h+y-9*_(-=@2;wT_AW!U! zh&2{83VC3L?q)1bBa^Ar+TcSo!au^z;&xouUm@e7xq_}fc4SNFt9*eOJ07? z*KKhsIw-+&2%_!1^Sy@HxzxNYQ`Tnzj^M>hNMCY4fFvht>|HK)&LI9@Chd>CbpBd+ zw}p54BTDnq^i`G*JBOlQdDp_?z5%^2*M1%oPc93zADN-k<(S0fO{IT4fbL31t_|9F z_ZR2h$#|XZ7+FL)pu&@Nws@Z-sx>3~q`#L>ACrKqz$E<&Lk80-E+rxJgCUujc|8k2 zIOYWeJ-*ix;>NNA$dVRBr9-e;9~O;(3bbip2jVYUWufMU$I{4bR%5?vAw+y0Bh6BD zlz4R3r&~XqwkGIHbn1o}4JO*dZ2`0TF)i3qYN4xk{@zb=g!CUal%VMs`#5YREz3DK z+Y{GE_Twgy1p~2rK4wWjVOqttY`N*nB#auq>=hhP=KB8Zd9;4w4DD!AM8F??e zDhms#50FwgeXZUS7J;@;_5Zf<5XkNK2`e-*-ghT?AF)>E`Dgej38mvxIu8}zy<_I} zFm`}Nut+dWmN=-X5~2E=3Acz1q0 z5n>#8^Cf`0tK?PrNBK-XbT<$gx9p#OT5 zWvGMt6_$kF_v$1b5Ny*wOMUeY-W7s|JxR^VB5Dd-m)X}BWG3+x0U-{X5>Bh;O6|nX zJTSD#ts}>LLMxYJueLXbRF)oTKO)i4@H1$3$fLUj)Dqhnj7JIpV~MQ1B%&ok_IoF3 zIQ~Cv=wCYtM97zA6=yc(e)7Z2QCmy{<^7EHhCjZm)SZ!NP1LV@>1k#@=1pE!;U_n~ zP!=(t%&1if51qTp zV%$eAJ0g=(dCbD`P&ssup1tTy;A&`8b@O<~?2&n}({XvMTI|L=ld%H)MSeCv0mGv+ zIu4Qe9PhszirQXinr~>Y;aYUi5*zyqR!P4@hl}?Lx2EK;l6EhoD0Sx~G{D)(`L%bO z==y3r8d*f2+?#9c?Fd1lBssehgMy{A$om8uPKC4)F-3MG8%6`uy1V!|kyI(2@1~a! z3Tx=qa%EF@Kcgz&f7@-&JnWyH28srBq&hAkYRP2`+h6UApB!28j%6OYooLQQ)`Sw~ zkk#LM&U@~7}3&h9Qx>C#Fxi% z`hUPFt-8465XJT-j;mZc@vfE3%K4ce(h(X`3$*;T-3h1sWALHn{r2AM^4XL)TI4jB zix`Yr0B%Z(uz$l@dzd>mxNe;YN(mELxK+2R6*qn&WjC=X`#t^(OtN=PG%806+|dd5 zd7H$S>vrd+OH1-HeO^ZTwDU9n`AOq5zisUwZ{EY?AfOkGQa}#ORc4$w;>h2YfV%C2 ztB#+_%B%3Ljs1*}HqgT3UaoUS)#`?lmpGeM32{EASmx(Ky9qVYS1I~24khv<0pRkk z%M5f2pMKt7uhT%#zsVAmS_m1iD7Ax@@LJm(ggj1MhWjU401ukb68#a~ZNo&?ne-jB z2*M&fS;Sb|Su?F(Ds69ZknyVU)A?(Oa-E|EI^Xx_inG0m#lsZqgoX0Vl@jV&j4Dp%75X9}ZyWTbtGp2ZY%CqB5u z{{%c9z=MHV9CbE(dsBD%SkA8BUyU0k%Aw`br}n9UH@U|05oo?>48c{m56zi;_0dmF zn^`(3ZNlP+XW#=OrYv-Tw7gH_tZy8}=PT|tmB!ie1vE?!>SuyV?+3FO;bO8{;9OR>*UI`=!7|on_oPx6NsI9F6uO zGwn{CgvKIs`A(?4b5h0Ub%hE#(brG1(%#0QBK7jka&MJ-pU*d!v-i?6U#APm4n1pa z^4V=Ah)#07H~KiJHJ}#|cJQV(@3wbg)Njwk=R37&pWxuiJT&#KoR)w;)~}=52%#Vf zK_uEO)LniT)mLRxrU9yj9;6)47RGTiYj^207AU@XYk<_$3)NTBTpqYv_LEWzb(MZh zDvB9HO99V&*55uSTn(%Ye50=4 z0L6-xLUAqbR@|XDg;KnDf#PmSai=)N-Q6v?LvVN3K!F0m9p3!kbI#0}=lPm1cP5#8 zU)SDy{npB~aph++dJ*7+uh!|_72nw1$6Py%rp+!BFnoKN+O_xy|6Tw59M@A^c82an zaCF8xbUtg1Byw=9dtN17YM;ud^PQM4kGQ!5_`_vs&5_M%W_YSz_d8#F47}v8O^Jnl z>LN2aWhe^w@GGBCc0&$GUqqfLe1GF0@4jIoVn|6I9aIB8ZFNw|gun7HRx?eWGPQX2 zSPchi9YFiw>53cuQ2WI-bI7w=bTngoK&Pq=SIfo&<-TZQ5&h+fM8C zu*dD&yXV%r-s*MHTVWuF{2r?6V2N@Po3_{+3W8{+ol6mU_88bxz~ZR~EUS%jjOP7x zvELhVVUNTKgH}=jDUwsULAuc`ll-w7#xQ_m>r>w-BQt-mw_My&FsoGkKKmcs{J~|f zot<{}F9jEU3d?MvG1>9XBWJmNav#PKh8_+v77Lwre_o99Zz_Vk4O^T)r1JRI?%ylh zuY1$>$wtNvjB0fdY(Zac)=>*J4rlldG%Iw}n6=7mpVsPMQ<5&95?ec}$R&4D^A`uO zeD`;zSoV*|ER(W=V8kY15dQ8Kac=B-S22R zZttgbh}+n{C`Niwy&n9crlKQ41r(kgX()`UAxkIv$(zG22De&Y8Na;$6JO{Zs={q@ z@rifn57fjtgtkC53GyXvBz&Iq^EJo?`2hJa0p%snxTel#@+;rl(H;J9y4&?K{rBE>o{gg(Zq6 zQu1YqPO#3Yt|BkqhF2-zsjL-eNxobpbL*2kCNf~(RvuSH zU}ZWxP~4etcr;$ddE0kccib!JznOp@2bs^d*u!hbX6O!L6TVT1FB&hDDd(&E*~iz0 zYV6EFulDQeltF!t$haM47WomWv+Z#$uz8TjN7zTFwRf|sdadzMsf$zPTz;1#DB~Uf zQr8+zl7rKAn#FcMS&i5HulwZJ-RyDs*4(GT*achY)-;chs$0_hanp*)bvpPoE~#>L zWWnH(PKa;V%=e~U1`;n1a`A<(e3{8Jw`p;NM)%XvkDhp*uT<9;*sr5lr<|~G&y;dp zdvHVd#4`e(4}nVGUpm<-8M#h=M`jDmL!7fZcgd>BS{kA)o(kcvOajzimp-4L6=8K9Xd6R_U+EimX6)LYfAlP#-*;JX^v+ z1p106q_pPyD1q3-QuEl2VO%$~eRBA}@2)mzth*iLvD-4*RA5?MHL|PuJ~{T|qQl_G zd3`dS*mZ5idb2@VlO>%(>V29+3A+mY$HvVPn1|Xr#xY5Sr<#7@;7aj9^;#19`meO) zansDc>doKr2!SDK6?obApHh;K+aOTY^sH4*KoJULLQBg3pPMzAOiG zJ&ogqPQDTH2i-8Zw`Z>pwRja%vdy0VG27~`eA`Xt6@`^_JLW#FBBhqrn_m_82D4&z z#V$i5jp>b>QX5t>Za7z_1y>s`mvsMK^?H!##oz-i(m?oGEf>Tbzk_vmCj&s^Ds$0$ z3H=Gb{DY&CUaQVCyAYJ9CWP0^YXLaomXCR{|Y3>Z{XLBLY@K`w;XI0Y$11 zVw9m}CH^tCySbKfckzOy_So{Kv-IODxZoH%6J@E+2n@lkGrg76EmRzamrP~cT(X1@ z_&sjv;MKQj5obq%wV!^j(dtdBjt$k%gAE?xEszJBDAhy{P4W2OW#7F715-u{A@>FS z5<%KzV8v8si{m;zhgRXD{Te-K(@7E|ZyAunR^0w|a8f_XE!|^Di|R;z^=q%Z=hYrE zZvi~sYN!5BhueVfT&+iaA9`^c6BTHf~NR#F< z->A8_Z01nthXc2=6o+Yxg!n=Nq>>5rW`IhDl}gVmo+)DdPT+M_YK%fymtaOKx2?A=5KPr(^3Zu)vlj+v1j@ zXz27=F6y>o>^u_O<8fMT*i;DfNn+Y>0PQXq)EVCYx_>ZWqbbONQ-^KXOJGDy{2lrN z=*TwSLBkr`%BI}7rL!W7`Ma~J^Li8*0sv8&Y!>WH`E^%z4r#g0CHp*7sGr_!<+;si zc)|Bd_`YWR`0fy%2=+K~M(BL>q`6DHfed5OS6e6>{sXlccQJuY#m!W|8vl+^<=+ke zV1fCskrcel$L%+Ox55wqwz})~o2oV3r92{CK;Dsu`-TzZi`Fw3lOXV48_iqt<0&JGC76Gg58imb-nf=mer`S2a-UH_Z?^DO&7ahitU@j`ynH5Y;)MOAwWC#dzjX49{S+oR{t*&F596P3)iVd+G52u{1wd^Dx7Pq^#f zxd90@$lvk5y-ro$bq8BciKUTY-p&dKwH>I@rK}sDs4O^b^n|i+h>a2Q{U;Vp5wPaBT9uuo|>X_)(i>U)+?F(G@{; zyEwbZ1VM(o4!p)Et6i=e;^m(d|Uo88HFI4N~i6$!$ z0{ZfFu`VFn_s8wXd1&W|&?V31+so$@PwLcAZMjAYkJCXtu_1uVj-VY9+!o(S6`M?y zO-{3j$dj08`qyh==&)<5h+EP%p@?w?*Xduv{YSPHj$@5zmCUXpmw!CRf$M28niG>w3KJw@sKx+@TLrhF`xpvlB^=F;(1>o%|P>iwNY%JAk+kXyyc#`~)G*NmGwV2*?l4v(l{}}Ua z+|;~dLVMMnyx~M_yM60H@>>yT7G}6vUN?D9oq)`#=(%XS#2y-*#cLR1b`#ujx$ZU8 z9x}$6tVZDkBsA>wQ3ZJ}j2M$q-8RIzwjXb==H^={`!I!e(y&jSBj$g3#L4CpbI$fk z-MuY|MDz)2qLc5J!PzEP`*JU=ad4H-K#ZDery8jSuzX-)rXPL?yd6l5;7<(oX!ONN z#inMLG-uA^twIEB-)>1@!RR&Fhvlk&|_|W%RH5| zGM`TE1fw-n6SF!V(`xnuTi>L6Q`UlYBW)Lu~MqU5AU&xiNjlv zImW2IH@haXYSVxf;(z`$`=N}8D1wzBw@h?a@fTsn^>jsgb*{$`1IX!oB~bdkv30Ig zPaCTva{=WdM7vKmI%%cxn1pc)49JnU;=cUT^S7_&vvV@55!*8=cARjTMxLLVY_-^P z;cE_ck)JvPW{Yf8`bgNcX`#|>){N$HP+rn~K{sX(39t2V3D<6+_SwtD;-q+TstZ91 zwLl+vKBWA4%S}aojY4;O;33O<);d980M>iF3-Jnv-#Rs#ItCljxvYle$uMWji zDp`C=pyl-!E%-d+0fAy&qp!UF#v-5o3zo>=Ro@z>6UMFP3Zx1wa|zASkt6;$YH9ccEKT!xh{YFuZuH+xLS&5 zNp2)Aia6KH;|-;mUEf!8Xj9=g8a{z57V1%H>q&bt5b%Ft72` zY*P3Kmh!kciKNORc51_K_VOk^dFP?SSGt$;0C|4DA1`?y((wGIyi>b?En{g-LwoIT zRo_Zz`dCiCjd2?ZZNf0nII_kqxOG<(cD=X>&*RkIXVd?IGTq(NS76r`&HRX0;1d`E z9^X5>)Yeu;;Ntq@Sy~$z*QyYe!#*;q=RrvlV~CnUrAJf@L(9S<;Z`1mC=Sm)HI<#O zl_=mr`z#a7eT3}Wh-)NQVP&P8IdRe1MM5|Yi9XLL!@9h!R(Qtfppy*`<$X3C>)k(& z`@Os;x9&4V-l(gxoIkFBEu|=Hhc4QhC_+|QfepTL%kBP>C_1>dG}ne;>p)PP@fEv% z-M3cvGjl377o-r5MNe$9reFG+rmfY{SUXNt!?(!FLheUD&JRsb;|cwKlrNlM{zcpy zz$i2=p4cDOEVdUtEOJ)KBw{I$REbps1D@~eC&w_1Yepo;(;Ch9{~`0qAzpnvW7BKo zJEMj08}c-{l1(wV%?Vhj6squ_iZGVp+*1g9E2K5q(mhv9%9gsxzMFUoDO>_tu2ckPqE6?TlUfy;W>uO1 z&#p8;KkVtqbC_K|W;p-~uC$lypl~n15bj@VpOGZCi;CIYoekQfNps^_rY${tz_G5cDqGHolw_`A0HO~fu zj;syH!1E>v1&d^rPmqAH)$NQ8J~Ei$F7d=bA}on$G#HiZgttK~A6PVJ?!j#!6*Uz~ z@=Kr$<{a0uItcEi$hP`D1of?-`ZWo!`P~rLjP#}w+iF8%+lT|i(KIuexR^#Z3Ladb zM?_@yG{-fxv~Pf!a=0&YhqHuCw5og%!L`eJ}5F7Ez@qxEa~PvAG%Oc)}tD4d$b zGE>N-F%g!sq@&yVBa`?Rh-qB&C4*c2Ljj@nkP)W?ad*@<&0rdrAMPDvu(YuB=~&e| zqu6h&m~m3XEo7Q1f94IKcn;8|b2Qc@|C359vm(ek11k=}dd;xSTl;jNza($|yR0Sg z9#UKr-vGYSh`Zl#z`&^Tw0UD{;skbPYZ@+P_8)d))wi_tV)J)`_PzL{gfUr3l`ARy zgAZ)NV-nr+aQ#E0=GeaVSZ=#joXknCSHg^aS*+pn)=^*T_)6mv+?^xU0UG;jq z`8XcO2kY;99~IHRB1>b`U~yvQ&={H{Ri_m+=(E{`U7OfS1yoMVs!n8*FNbMM=f#lx zBl`A6gyP|v@<$dO(G5W0Fjbd1ImT}OUDHF9H6U9f3xT<&E+z!=)w2u%ZA$W`8RYUs zoc1s($F`ePD@}0F;xL@|Pgi(E{k;_^~g}cRqcQFbB4V2O3n2PWo_-|{y zN6c#wZ}rc61kT(s^|tyvgQ_VaeSm=eE9FFFY`bx_`(fS} z$NiCx$6MO(P|uWToV}16Waw>ev=QV>U&t3)&HFx`9={*+sg8GdAggNc8p}g#o_z#7 z0LqV@*)f;HP(*qd#x6w;3Kejz-|DAMk%hUnxHuo6T4^y0tclh_g>{XxWK8zsj;_!` zp>-l2U^=9k_Z?q-u7+mv6V#+^ggy(<@ra#JI(v}zFGgdxu8m|~keMHl#BMaA(`ME7 z$qHU;chX24#>7>}bCBf$_`g+$xnI8dJVs}Z*I4}1Z~nFFhb;0QPzvDN{-Vc=h`

    ci7MPqUVu59T|sh-1{V#&h_g|R9?eJwAM=>wEa1qt1P+NkOMBc z;F{v!L%7pF7>|N@Y!DI)`M^f3E6zk#Bp#PrQH|lxwv>8|yO$Gcsxbyof>1ZYT3I|? zF{fhS;^*0i_BX5);=Z`wb^cBbi}=>LVHckbVZ?+bRM=kHnnuD5f4rFb(3}b1nqpF+ z^dr72j6ulNu3Tl-6{J=je@~~l@<{B1_>OPxV63ts{o*_WsQ`7Vzy8=>|Ic`Ypb-0^ z(!_I*9@qEHE?^9w)r!>6S=v;_E$ znf4F%Rk>%j^2x2wKb@}xinq5lovk=rvS+bgu}M&x##mfUv)idFOQnG%FQU0;1S2}P z$^ShNK2yzgV&7oz7&JPM%Oc5vZVRC$?2gBsSEuVno2g1{q7h8()%TetXUxfqlbQc* zll~tq*LDIPCJw9}D$)mT2I!zJzVZ z?l;KAYWYP!Gg_zQ61}m_IzYF%Ol;XtN7~=&%oH^HI~zCS=YUe^l7cTsqt#|1%)wsQ z0J;y**Ugt>U$*eqba-^PN!gvr*IdT7U&@++C!-vl4IjwyUGagZru|QcF}?{LT~9MJ zuTT!(xDm~=6-9zABov`=9#okh{k2lQTQH_l!~CSmirOD3iG(YpCumTTYCON(_jF22 z%q$c{%{uw9+sFZgZ~WbB-(R1-%91MTlKWhj8>Pu%vTRM<<#!T=5hcUky>NU(PSTXp z`lXhIAlIq!U(2cGtikkC_${i=%YRYwf9Mp47L9z_d>>{hzc5)e#?+ga;9LiDx?*uo zdrAi4WOVNB&Y{WyH_&ZCfl>(jfe$kqvlWEj?VCni&3pISnCP&okQx z$+j>_LU`F`@`fRj7}NGb*4|PWf>=HdD^0j|L*JbpQrpF{B>vs#KF`?&y$!k5SZM<6%Xfub$V$#`o_srnk_far}0gy6!)}TpWnoWP8zdJ>B{? z%^$$W*^##}9ApX_WTapTBgAmqRZ#ax5G4S3g?<9ikrYFN*<_HAzFr%>4%}(%tU5q% zyPh-cN}X3pOEB7@HB#*nFVO5)1$cD0&HeYo?3YpTGLt2xEJhqFJ=*R!)x?qPk%W8h z=QHzv(vvwXI5rZ5u1PE(ebgCUHisx*VW_0S0PZU`+4pu-HOQFKr~V@xEJq2_fEtrA z6Gjt94#Uos87hXKtTr8-?k~@qua#dZT&wQ(7JqKdXh)yH@FWmuP8Wrey`JZ_FD#&s zXTG<3m%xtzlAV*DyY6!I{Pk|(FQXtlY|dPxA^xWf{3AT<6spI$WHz3P*!sl4m((W5 zmUX!zm{yvIdC`V1I%8~ft$-awpJ#=4gZE3F0m1tKS z27fg5NIwsp1eqxuUE#y(7C4GH(0`Sbu4Icvpg9p*QGBg>mis56r!{pvSIO}uy{W;D z13nrZ?)I0)a;2|ngsW>VF5qJDw6z|aeRTlYqSzn@rrios7{r}QByNdB`t?==^m!qc z%gl9q=L266tWIhJd_+Wr(~dWkE*QZVOE4+9GTz7%a|p||*{OUxJj z>CEW)BKZazu03+n4a`1l~k!?@jS?vW70QRBH1N#|J?f!2%bJ@@b|!N$4)v9 zgS)sHTe>Rev0;C*oRart7>2O5osdIZL~ddB{iMM+(?$#YLe(&A$$bRgSf&;E^ptwv z%FCQ67jh)uyXUjQtj^>mz9Wbqfx~J9z<-H5P#NLvR!4*VE;h@kzs?w!?U`w$qp|S4 zPwv^5hxc#}fCgX);r0&>ww|}q@qt~2b?WIgT3PH4CQcgdYiwYRp^n)AYGIMc;l9tw zzDW*USmWy1rT^eIS(E!(fX0qcDGJjegzGnBxzT7e+wRb-I3?|vs~<{ zs9p2z+mqSikM$a58p~iDd(^QxFsIMbQ4l{41}gCVxeBDOP+~(N8^ukW-4yW-lxz6DGN~-1Sd3#ul(yh`)kL4?<7ovIX9jdA*D_dmO*IORs z@(!S^b1j4NRY-7`PMS1eaD0dhuhI}D&rTDf?fZ$mGNA1>4Or)BaMO96i zsP#Wuq|cK^9i~t~iX)}&pc0yK#3O8~n*=K81%Uwt?ei+5hCW@p?4rQlQUz&Rh;$Os z*e3SXAjSGO4H2L^(ien?F-4AX73oa7`=RUYaZYji1bF3;52XnA_3tHhF8sopX^kNh z?dg=lzeq^2KcT{IC+D(m2Pwpbmh(VWw0-J-d=W7N#NtW6>%&)VK|l&k(f`VD#A7wW zx=XnGp(fEu!f{tX{)+gLSpq>%@NdMHwlkFRgv)q|LhjSE16NdikGha$4r zisyjnEagBa)uJ5JACzT>h0U3oi{kB&slKIK)3I-o_kzGaL7ZET$ zb{rOu;yW$Sp;r@tl zS0Fl@emnOY_en#>%YBZ<+GtE3EqR=C($xJYibcL7xyR*=pim4$uU%Ik$zp%hxp)^1 zde!vO3$dP!0r=dJYwBI+XOetz?f+HO=6n)TAG!B@?LV+}m7KrFychHPJy7bSPCzm7 zty6A7JmYK0(nt?NwPbhDJg*jfJE67#(~>w>8vpe(gti>Y$jkJAZA^zMjdq5Y@Lqq; zDGnlFB78tq03(EK&w4opb<>6ruynUPL?$6}G$=wlmfGWu4LI*Za4 zSppq}(J_mtLhD;~R>y;muzDl5?RF0%PL)<(WL}2p-uC^UDhLJz9dN9sHTan0t%U!!wcupYNhOOuzPLpdCh!!0S_W_UchFU#|S`U5*iE6ry&O z(HSp|``xrYMTD9^yrRZPA?E+`U5{;Mdc`rLZpejG2eI_nmiT5`4LmO^w#SfG`uy9_ znM&QKXe}%pv*a%KE5`%r49}2$PIvhs0vv0tpJ=Uwm+6Dgdv5UYR~yDyA6HPHprZ`a zwZBnpbN}V zP}UO`xkG~O=qHIoZkreOs5!2XCBRu7iNyz&}(4G)R zih+N}aa_Ka<n z8Dd%$*+OtL)x$K!2burNoW4)0{JK80pkTFnP}xcQRX^|{ z6+r^vg$(T8vUov?+{w{ucLXvZ5;$pT2`4VIES@cUL2jf(xDg1pg+ndt|KgsS1Tc_V zOwk@?_@SZ#xaJ5qZr{QlNxjkRh6PlV)5QWKHaE_J3` zt&fWHi{McdL_Ir4_OqTZPu~tB4Cay07uBK86tM#nzaxrf(*T+9#y|X9^H{?Ey=$y4 z@kWcY6yq(A{TA8O2!iL_2f9eM4++}&T3?8{y&A~x2w8EE-U|vqD)kG>~iaK}C-CNQ%!s znl(-*Ro`0b;L(c1B6JstJ&ZF%M9tLoGZ1T5qulQKEKR4*A~l0iGZ|9z)V^b#I80_I z02+XKuO~M>1GW1|aD%6(T5pWpLxgF2( zd-#fAaT2jY6iIjc_ z`=2BX9zw3BtjRHe#c>Snrr3K_MtgXENXBn*nEb@{e_VvbGMUA<1i0@=2K11=@r|-r zsrCal_@BtY@LHgeEC8a3Yd^WMYp-ck9DxU{z;?-JS$E*4EE`*=?)2YJsmX#P679ZF?6iW+Sr zY++L&m=-I}GlS`pZ!uk572W+SHtS~L8@JoahOT?=cUDB3z@+Ial1A(07Q^-~4#O^+ zR=^X&=OS`j)ga18&8*aj)X+F|38^LliRBEOKAruSs*X@hV;9qC4l9JJ2CGH2P02X% z7_#46lmPZDH(O91#orH+YG7zya!m(z4(sEgi>J{gMX{m%L{!4rWZeIpI2Ma&Qs5Z+ ze$2l5%@zA*svm*=cycdnMVQ;^K&iVw>u~e6MKi%xAf4u+lYf`(+s1a#M?V&8nBONf z^S8+3Oc?z=bV(MBOpMt;tS#qp#SfnNDR5U4^Oxq=^&SvLgf3=T>B8n7-Uw=doKE~_ zwotkAulwt%YU$0RM^S%MKN721|9{zJf~^1jB;O#SmnzFyD0Kp#3Rk^HQy z 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 literal 0 HcmV?d00001 diff --git a/assets/images/blog-image-2-1-974d3a64c66cbcd6e014779113bc1a4b.jpeg b/assets/images/blog-image-2-1-974d3a64c66cbcd6e014779113bc1a4b.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..517c681943bb4667894788a1ddb8b5a791a7c45a GIT binary patch literal 31313 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

    w&_{(=U0rnLB&) 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 literal 0 HcmV?d00001 diff --git a/assets/images/blog-image-2-2-fb13fe692dc47f7d90de361ec6d5b771.png b/assets/images/blog-image-2-2-fb13fe692dc47f7d90de361ec6d5b771.png new file mode 100644 index 0000000000000000000000000000000000000000..3f657e7fadb8503a2c7f3b229e31b85070a3b0d2 GIT binary patch literal 69516 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 z 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

    Y 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.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db/page/3","page":3,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db/page/2","nextPage":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db/page/4","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/00984d5d.9e3ddb4e.js b/assets/js/00984d5d.9e3ddb4e.js new file mode 100644 index 0000000000..54e146c0fb --- /dev/null +++ b/assets/js/00984d5d.9e3ddb4e.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[99205],{40161:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db/page/3","page":3,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db/page/2","nextPage":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db/page/4","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/0099b9da.af23b11b.js b/assets/js/0099b9da.c06b6456.js similarity index 73% rename from assets/js/0099b9da.af23b11b.js rename to assets/js/0099b9da.c06b6456.js index 38c621f7b0..ec28234b34 100644 --- a/assets/js/0099b9da.af23b11b.js +++ b/assets/js/0099b9da.c06b6456.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[27078],{33006:a=>{a.exports=JSON.parse('{"label":"30-days-of-IA","permalink":"/Cloud-Native/30daysofIA/tags/30-days-of-ia","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[27078],{33006:a=>{a.exports=JSON.parse('{"label":"30-days-of-IA","permalink":"/Cloud-Native/30daysofIA/tags/30-days-of-ia","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/00ac6f8e.e3c9792d.js b/assets/js/00ac6f8e.e3c9792d.js deleted file mode 100644 index 780c50ac7d..0000000000 --- a/assets/js/00ac6f8e.e3c9792d.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[39761],{34087:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/fall-for-ia/page/8","page":8,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/fall-for-ia/page/7","nextPage":"/Cloud-Native/30daysofIA/tags/fall-for-ia/page/9","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/00ac6f8e.eb6db636.js b/assets/js/00ac6f8e.eb6db636.js new file mode 100644 index 0000000000..d19d55f45a --- /dev/null +++ b/assets/js/00ac6f8e.eb6db636.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[39761],{34087:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/fall-for-ia/page/8","page":8,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/fall-for-ia/page/7","nextPage":"/Cloud-Native/30daysofIA/tags/fall-for-ia/page/9","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/00ff3ab8.b7b3802c.js b/assets/js/00ff3ab8.caab9076.js similarity index 73% rename from assets/js/00ff3ab8.b7b3802c.js rename to assets/js/00ff3ab8.caab9076.js index 299fc7ed2e..a85675bfd7 100644 --- a/assets/js/00ff3ab8.b7b3802c.js +++ b/assets/js/00ff3ab8.caab9076.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[85992],{92649:e=>{e.exports=JSON.parse('{"label":"hack-together","permalink":"/Cloud-Native/30daysofIA/tags/hack-together","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[85992],{92649:e=>{e.exports=JSON.parse('{"label":"hack-together","permalink":"/Cloud-Native/30daysofIA/tags/hack-together","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/010f538e.14e91648.js b/assets/js/010f538e.14e91648.js new file mode 100644 index 0000000000..1f958c46d9 --- /dev/null +++ b/assets/js/010f538e.14e91648.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[997],{19881:e=>{e.exports=JSON.parse('{"blogPosts":[{"id":"cultivating-a-culture-for-intelligent-apps","metadata":{"permalink":"/Cloud-Native/30daysofIA/cultivating-a-culture-for-intelligent-apps","source":"@site/blog-30daysofIA/2023-09-22/cultivating-a-culture-for-intelligent-apps.md","title":"1-6. Cultivating a Culture for Intelligent Apps","description":"Prepare your organization to adopt intelligent apps by taking a change management approach. Assess your readiness, upskill teams, create a culture of change and overcome resistance, and prepare for future evolution to pave the way for a smooth transition into an AI-centric company.","date":"2023-09-22T09:00:00.000Z","formattedDate":"September 22, 2023","tags":[{"label":"Fall-For-IA","permalink":"/Cloud-Native/30daysofIA/tags/fall-for-ia"},{"label":"30-days-of-IA","permalink":"/Cloud-Native/30daysofIA/tags/30-days-of-ia"},{"label":"learn-live","permalink":"/Cloud-Native/30daysofIA/tags/learn-live"},{"label":"hack-together","permalink":"/Cloud-Native/30daysofIA/tags/hack-together"},{"label":"community-buzz","permalink":"/Cloud-Native/30daysofIA/tags/community-buzz"},{"label":"ask-the-expert","permalink":"/Cloud-Native/30daysofIA/tags/ask-the-expert"},{"label":"azure-kubernetes-service","permalink":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service"},{"label":"azure-functions","permalink":"/Cloud-Native/30daysofIA/tags/azure-functions"},{"label":"azure-openai","permalink":"/Cloud-Native/30daysofIA/tags/azure-openai"},{"label":"azure-container-apps","permalink":"/Cloud-Native/30daysofIA/tags/azure-container-apps"},{"label":"azure-cosmos-db","permalink":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db"},{"label":"github-copilot","permalink":"/Cloud-Native/30daysofIA/tags/github-copilot"},{"label":"github-codespaces","permalink":"/Cloud-Native/30daysofIA/tags/github-codespaces"},{"label":"github-actions","permalink":"/Cloud-Native/30daysofIA/tags/github-actions"}],"readingTime":9.215,"hasTruncateMarker":false,"authors":[{"name":"It\'s 30DaysOfIA","title":"FallForIA Content Team","url":"https://azure.github.io/Cloud-Native/Fall-For-IA/","imageURL":"https://azure.github.io/Cloud-Native/img/logo-ms-cloud-native.png","key":"cnteam"}],"frontMatter":{"date":"2023-09-22T09:00","slug":"cultivating-a-culture-for-intelligent-apps","title":"1-6. Cultivating a Culture for Intelligent Apps","authors":["cnteam"],"draft":false,"hide_table_of_contents":false,"toc_min_heading_level":2,"toc_max_heading_level":3,"keywords":["Cloud","Data","AI","AI/ML","intelligent apps","cloud-native","30-days","enterprise apps","digital experiences","app modernization","serverless","ai apps","data"],"image":"https://azure.github.io/Cloud-Native/img/ogImage.png","description":"Prepare your organization to adopt intelligent apps by taking a change management approach. Assess your readiness, upskill teams, create a culture of change and overcome resistance, and prepare for future evolution to pave the way for a smooth transition into an AI-centric company.","tags":["Fall-For-IA","30-days-of-IA","learn-live","hack-together","community-buzz","ask-the-expert","azure-kubernetes-service","azure-functions","azure-openai","azure-container-apps","azure-cosmos-db","github-copilot","github-codespaces","github-actions"]},"nextItem":{"title":"1-5. Preparing the Path for Intelligent Apps","permalink":"/Cloud-Native/30daysofIA/preparing-the-path-for-intelligent-apps"}},"content":"\\n\\n\\n\\n\\n\\n \\n \\n \\n \\n \\n \\n \\n \\n\\n\\n\x3c!-- End METADATA --\x3e\\nPrepare your organization to adopt intelligent apps by taking a change management approach. Assess your readiness, upskill teams, create a culture of change and overcome resistance, and prepare for future evolution to pave the way for a smooth transition into an AI-centric company.\\n\\n## What We\'ll Cover:\\n\\n * Role of change management in modernizing to intelligent apps\\n * Skills needed for building intelligent apps in your organization\\n * How to build a culture of change to embrace AI\\n * How to prepare for the future of intelligent apps and continuous evolution\\n\\n![image of modernizing AI solutions for intelligent apps](../../static/img/fallforia/blogs/2023-09-22/blog-image-1-6.png)\\n\\n## Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management\\n\\nAs we\u2019ve discussed throughout this series, intelligent apps and generative AI are rewriting the rules of business and application development. The first article of this series, \u201c[Demystifying Intelligent Applications: Leveraging AI in App Development](https://azure.github.io/Cloud-Native/30daysofIA/demystifying-intelligent-applications),\u201d explored how intelligent apps can enhance user experiences and drive businesses growth. These advancements are opening new opportunities and markets for companies ready to embrace them.\\n\\nHowever, embracing this digital transformation is more complex than flipping a switch. It requires adjustments to the technical side, which we explored in the series\u2019 fifth article, \u201c[Preparing the Path for Intelligent Apps: Transitioning from On-Premises/IaaS to Cloud Native Applications](https://azure.github.io/Cloud-Native/30daysofIA/preparing-the-path-for-intelligent-apps/).\u201d It also demands a shift in how organizations adapt to change and approach app design and development. Let\u2019s explore that mindset transition with tips to help you make it happen.\\n\\n## The Role of Change Management in Transitioning to Intelligent Apps\\n\\nTransitioning to intelligent apps isn\u2019t merely a technological change. It\u2019s a profound shift in how businesses perceive and operate. This move toward artificial intelligence (AI)-centric approach isn\u2019t meant to replace humans with machines or radically overhaul all business operations.\\n\\nInstead, intelligent apps should augment human capabilities, streamline processes, and foster an environment where innovation flourishes. They expand toolkits and techniques to include AI-assisted development tools like [GitHub Copilot](https://github.com/features/copilot) and AI-enabled workflows, helping us design and deliver AI-enhanced experiences.\\n\\nEffective change management strategies are crucial in helping employees adapt to these new ways of working with intelligent apps, ensuring that the transition doesn\u2019t disrupt productivity or morale.\\n\\nTeam and organization strategies should:\u202f \\n\\n * Assess the impact of any proposed changes.\u202f\u202f \\n * Communicate the benefits and reasons behind the change to all affected team members.\u202f\u202f \\n * Provide the necessary training and support.\u202f\u202f \\n * Integrate the changes into the culture and daily operations.\u202f \\n\\nBy incorporating this robust change management approach, you can identify and mitigate potential transition risks early. Moreover, this strategy ensures that employees are engaged throughout the process, fostering a sense of involvement and ownership. This, in turn, cultivates a culture suited for innovation through intelligent apps.\u202f \\n\\nThese organizational changes focus on the following aspects: organizational readiness, skillset, culture of change and overcoming resistance, and adaptability. Let\u2019s explore them below.\\n\\n:::info\\nRegister for the intelligent apps webinar on [Driving Business Value by Modernizing with Cloud-Native & AI](https://info.microsoft.com/ww-landing-driving-business-value-by-modernizing-with-cloud-native-and-ai.html?lcid=en-us?WT.mc_id=javascript-99907-ninarasi) with *Microsoft* and *Forrester* on **September 26**.\u202f \\n\\nExplore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries.\u202f \\n:::\\n\\n## Identifying Organizational Readiness for Intelligent Apps\\n\\nThe first step in preparing your organization for intelligent apps is to assess your current readiness for change. Analyze various elements of your organization, from infrastructure and workforce skills to strategic vision and overall culture.\\n\\nCan your apps and systems integrate with AI platforms like [Microsoft Azure AI](https://azure.microsoft.com/en-us/solutions/ai/?WT.mc_id=javascript-99907-ninarasi)? Is your workforce comfortable working alongside AI, or are they fearful and resistant? Does your strategic vision incorporate AI\u2019s transformative potential, or does it fail to acknowledge AI as a critical driver of future growth?\\n\\nThese questions identify the challenges of integrating AI into an organization and determine the company\u2019s readiness for intelligent apps.\\n\\nEmbracing intelligent apps involves more than being ready with your technological infrastructure or having the right technology or tools. You need flexible organizational structures and processes driving your business to accommodate new ideas and capabilities. Your teams must feel empowered to own and drive them. Much like Microsoft\u2019s shift to AI-powered services\u2014like [AI-powered Bing and Edge](https://www.microsoft.com/en-us/edge/features/the-new-bing?WT.mc_id=javascript-99907-ninarasi) and [Microsoft 365 Copilot](https://adoption.microsoft.com/en-us/copilot/)\u2014embracing AI organization-wide requires a holistic vision and willingness to innovate, with teams capable of delivering on the plan.\\n\\nAfter accessing these critical areas for organizational readiness, you can start planning a strategy for this transition to an AI-ready organization. Identify skill gaps and work to bridge them while nurturing an environment that appreciates and harnesses the potential of AI and intelligent apps.\\n\\n## Nurturing the Indispensable Skills for Intelligent Apps\\n\\nTasking your AI specialists with preparing your organization to use and build intelligent apps isn\u2019t enough. Accomplishing this goal requires a cross-functional effort and a diverse skill set.\\n\\nDeveloping intelligent apps involves data scientists managing and interpreting vast amounts of data, machine learning engineers building and training the models, and traditional developers integrating these models into usable applications. However, the impact of generative AI spans beyond creating apps. AI fluency across the entire organization\u2014from sales and marketing to accounting and legal departments\u2014offers substantial benefits, sparking new ideas and solutions from sometimes unexpected sources.\\n\\nTeams\' AI skills might roughly fall into three levels:\u202f \\n\\n * **Fundamental skills** \u2014 Being familiar and efficient with using AI tools, for example, knowing how to write effective prompts for large language models (LLMs).\\n * **Visionary skills** \u2014 Recognizing AI\u2019s strengths and weaknesses, for example, its creativity and hallucinations, that is, unexpected output.\\n * **Expert skills** \u2014 Managing data and training models and integrating AI technology into apps, such as incorporating the OpenAI application programming interface (API) into an Intelligent App.\\n\\nBased on these levels, you can gauge your team\'s skill readiness to pinpoint the most significant gaps, whether in general AI knowledge across your organization or within technical development teams.\\n\\nAs intelligent apps become increasingly prevalent, upskilling your workforce is crucial. This transition opens a new realm of skills and capabilities for your team to master. You can offer machine learning, data analysis, and cloud computing training programs\u2014such as Microsoft\u2019s [Azure AI Fundamentals certification](https://learn.microsoft.com/en-us/certifications/azure-ai-fundamentals/?WT.mc_id=javascript-99907-ninarasi)\u2014to empower your team to confidently navigate the intelligent apps landscape.\\n\\nNurturing these skills within your organization may require hiring new talent, reskilling current employees, and creating an environment that promotes learning and experimentation. Specifically, if your team understands tools like the [Microsoft Azure AI platform](https://azure.microsoft.com/en-us/solutions/ai/?WT.mc_id=javascript-99907-ninarasi), they could contribute significantly to developing and maintaining intelligent apps.\\n\\nAn upskilled team is more than a functional unit. It\u2019s a think tank to drive innovation and improve your organization\u2019s competitive standing in the digital marketplace.\\n\\n## Building a Culture of Change to Embrace AI\\n\\nEven with the right people and skills in place, change remains an uphill battle for organizations committed to traditional processes.\u202f \\n\\nTransitioning to intelligent apps is as much about mindset as technology and skills. It requires fostering a culture open to change and innovation that understands AI\u2019s transformative potential and embraces it to enhance rather than replace human capabilities.\u202f \\n\\nLeaders play a pivotal role in cultivating this mindset. As they model a positive attitude toward AI, emphasize its benefits, and address any concerns transparently, leaders significantly contribute to the organization\u2019s readiness to adopt intelligent apps.\u202f \\n\\nThe most critical factor in this transition is enabling an AI-friendly culture. It\u2019s about encouraging curiosity, rewarding innovative thinking, and creating an environment where people thrive on learning and experimentation.\u202f \\n\\nEmpowering teams to leverage practical AI tools and AI-driven data insights to aid decision-making helps motivate employees to use and explore AI\u2019s potential, improve their skills, and contribute to the successful integration of intelligent apps. Such an environment is fertile ground to grow breakthrough ideas and innovative solutions.\u202f \\n\\n## Overcoming Resistance to Change and Adopting Intelligent Apps\\n\\nDespite AI\u2019s potential, change isn\u2019t easy. It takes effort and is especially challenging for organizations used to existing processes. This resistance to change is a significant roadblock when transitioning to intelligent apps.\u202f \\n\\nOvercoming this resistance involves:\u202f\u202f \\n\\n * Understanding employees\u2019 concerns.\u202f \\n * Challenging underlying assumptions about traditional systems and outdated processes that hinder change.\u202f \\n * Providing comprehensive training for workers to use AI effectively.\\n * Demonstrating the benefits and opportunities of intelligent apps.\u202f \\n * Clear and continuous communication to dispel fears and misconceptions about AI.\\n\\nThese steps will foster greater acceptance and enthusiasm for intelligent apps in your teams.\u202f \\n\\n## Preparing for the Future of Intelligent Apps and Continuous Evolution\\n\\nFinally, it\u2019s essential to understand that generative AI is just beginning to gain momentum. Adjusting to a world of intelligent apps isn\u2019t a one-time change\u2014it\u2019s a continuous evolution.\u202f \\n\\nAs AI technologies mature and evolve and more businesses realize their benefits, we can expect the landscape of intelligent apps to expand further. Organizations can cultivate a mindset of continuous learning, flexibility, and adaptation to prepare for this future. Staying ahead in this dynamic field requires organizations to keep up with the latest AI advancements, continually reassess their strategies, and pivot when necessary.\u202f \\n\\nThe domain of intelligent apps is rapidly advancing. Organizations must prepare to navigate this dynamic landscape to stay ahead of the curve.\\n\\n## Summary\\n\\nPreparing for AI and intelligent apps is a strategic decision. It requires thoughtful preparation and a culture that embraces change.\\n\\nWith the proper preparation and mindset, adopting AI can become a smoother, collaborative path to innovation for the whole organization. Integrating AI into your organization does not discard what has worked in the past. It simply enhances and augments those processes for a better future.\\n\\nConsider the points above to assess your organization\u2019s readiness and prepare to welcome intelligent apps.\\n\\nIn the first week of this series, you learned about [intelligent apps](https://azure.github.io/Cloud-Native/30daysofIA/demystifying-intelligent-applications), how organizations today find [success through intelligent apps](https://azure.github.io/Cloud-Native/30daysofIA/harnessing-the-power-of-intelligent-apps), what [AI-assisted application development](https://azure.github.io/Cloud-Native/30daysofIA/reimagine-app-development-with-ai) looks like, ways to [leverage the power of generative AI](https://azure.github.io/Cloud-Native/30daysofIA/how-digital-natives-leverage-generative-ai), the technical changes needed to [prepare for intelligent apps](https://azure.github.io/Cloud-Native/30daysofIA/preparing-the-path-for-intelligent-apps/), and, in this article, the organizational changes required to harness the power of AI.\\n\\nThe future is intelligent. Use this series as a guideline to navigate challenges while preparing for and embracing AI\'s opportunities for an intelligent tomorrow.\\n\\n## Exercise\\n\\n * Complete the **[Apps Cloud Skills Challenge](https://aka.ms/fallforIA/apps-csc)** to build on your app dev and AI skills.\\n * Complete the **[AI Cloud Skills Challenge](https://aka.ms/fallforIA/ai-csc)** to build on your AI skills.\\n * Register for **[Episode 03](https://aka.ms/learnlive-contoso-app-deconstructed-Ep3)** of the serverless edition Learn Live session to learn how to build, test and deploy an end-to-end intelligent app solution.\\n * Register for **[Ask the Expert: Azure Functions](https://reactor.microsoft.com/en-us/reactor/series/S-1037/)** session for live Q&A with the Product Engineering team on building intelligent serverless apps."},{"id":"preparing-the-path-for-intelligent-apps","metadata":{"permalink":"/Cloud-Native/30daysofIA/preparing-the-path-for-intelligent-apps","source":"@site/blog-30daysofIA/2023-09-22/preparing-the-path-for-intelligent-apps.md","title":"1-5. Preparing the Path for Intelligent Apps","description":"Learn best practices for modernizing your on-premises or IaaS solutions to intelligent apps in the cloud.","date":"2023-09-22T09:00:00.000Z","formattedDate":"September 22, 2023","tags":[{"label":"Fall-For-IA","permalink":"/Cloud-Native/30daysofIA/tags/fall-for-ia"},{"label":"30-days-of-IA","permalink":"/Cloud-Native/30daysofIA/tags/30-days-of-ia"},{"label":"learn-live","permalink":"/Cloud-Native/30daysofIA/tags/learn-live"},{"label":"hack-together","permalink":"/Cloud-Native/30daysofIA/tags/hack-together"},{"label":"community-buzz","permalink":"/Cloud-Native/30daysofIA/tags/community-buzz"},{"label":"ask-the-expert","permalink":"/Cloud-Native/30daysofIA/tags/ask-the-expert"},{"label":"azure-kubernetes-service","permalink":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service"},{"label":"azure-functions","permalink":"/Cloud-Native/30daysofIA/tags/azure-functions"},{"label":"azure-openai","permalink":"/Cloud-Native/30daysofIA/tags/azure-openai"},{"label":"azure-container-apps","permalink":"/Cloud-Native/30daysofIA/tags/azure-container-apps"},{"label":"azure-cosmos-db","permalink":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db"},{"label":"github-copilot","permalink":"/Cloud-Native/30daysofIA/tags/github-copilot"},{"label":"github-codespaces","permalink":"/Cloud-Native/30daysofIA/tags/github-codespaces"},{"label":"github-actions","permalink":"/Cloud-Native/30daysofIA/tags/github-actions"}],"readingTime":8.59,"hasTruncateMarker":false,"authors":[{"name":"It\'s 30DaysOfIA","title":"FallForIA Content Team","url":"https://azure.github.io/Cloud-Native/Fall-For-IA/","imageURL":"https://azure.github.io/Cloud-Native/img/logo-ms-cloud-native.png","key":"cnteam"}],"frontMatter":{"date":"2023-09-22T09:00","slug":"preparing-the-path-for-intelligent-apps","title":"1-5. Preparing the Path for Intelligent Apps","authors":["cnteam"],"draft":false,"hide_table_of_contents":false,"toc_min_heading_level":2,"toc_max_heading_level":3,"keywords":["Cloud","Data","AI","AI/ML","intelligent apps","cloud-native","30-days","enterprise apps","digital experiences","app modernization","serverless","ai apps","data"],"image":"https://azure.github.io/Cloud-Native/img/ogImage.png","description":"Learn best practices for modernizing your on-premises or IaaS solutions to intelligent apps in the cloud.","tags":["Fall-For-IA","30-days-of-IA","learn-live","hack-together","community-buzz","ask-the-expert","azure-kubernetes-service","azure-functions","azure-openai","azure-container-apps","azure-cosmos-db","github-copilot","github-codespaces","github-actions"]},"prevItem":{"title":"1-6. Cultivating a Culture for Intelligent Apps","permalink":"/Cloud-Native/30daysofIA/cultivating-a-culture-for-intelligent-apps"},"nextItem":{"title":"2-1. Build Your First Intelligent App with Azure AI and AKS-1","permalink":"/Cloud-Native/30daysofIA/build-your-first-intelligent-app-with-azure-ai-and-aks-1"}},"content":"\\n\\n\\n\\n\\n\\n \\n \\n \\n \\n \\n \\n \\n \\n\\n\\n\x3c!-- End METADATA --\x3e\\nLearn best practices for modernizing your on-premises or IaaS solutions to intelligent apps in the cloud.\\n\\n## What We\'ll Cover:\\n\\n * Traditional vs Intelligent Apps\\n * On-Premises/IaaS vs. Cloud-Native Platforms\\n * Modernization strategy for building Intelligent Apps\\n\\n![image of modernizing AI solutions for intelligent apps](../../static/img/fallforia/blogs/2023-09-22/blog-image-1-5.png)\\n\\n## Preparing the Path for Intelligent Apps: Transitioning from On-Premises/IaaS to Cloud-Native Applications\\n\\nThe proliferation of generative AI is paving the way for intelligent applications, software that leverages AI capabilities to deliver unparalleled functionality and user experience. As we covered in a previous article, \u201c[Demystifying Intelligent Applications: Leveraging AI in App Development](https://azure.github.io/Cloud-Native/30daysofIA/demystifying-intelligent-applications),\u201d intelligent Apps aren\u2019t just products of advanced coding. They interact, learn, and evolve thanks to modern AI and machine learning (ML) breakthroughs.\u202f \\n\\nThe paradigm shift from traditional to intelligent apps demands that we change our approach to the technical challenges of software and architecture design. It also necessitates adopting new ways of thinking and operating within an organization, which we explore in greater depth in \u201c[Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management](https://azure.github.io/Cloud-Native/30daysofIA/cultivating-a-culture-for-intelligent-apps).\u201d\u202f \\n\\nThis article explores the technical infrastructure powering intelligent apps, providing a roadmap to transition traditional on-premises or Infrastructure as a Service (IaaS) solutions to intelligent apps deployed to cloud native platforms and services. We\u2019ll discover the differences between conventional and intelligent apps, options for re-architecting existing applications, and critical strategic considerations integral to modernizing the application-building approach.\u202f \\n\\n## Understanding the Shift: Traditional vs. Intelligent Applications\\n\\nIntelligent apps are more than just an incremental evolution from traditional apps. They involve a fundamental shift in software solution design, development, and deployment. A clear understanding of how each paradigm differs is crucial for comprehending the impact of this transition.\u202f \\n\\n### Functionality and User Experience\\n\\nTraditional applications, often rule-based and rigid, rely on preprogrammed operations. A traditional weather app, for example, typically presents structured, location-based forecasts using a predetermined data source. Meanwhile, an intelligent app leverages AI for data-driven decision-making and personalization. Rather than merely displaying the forecast, it integrates ML with additional data sources\u2014like a user\u2019s calendar and fitness data\u2014to determine their favorite outdoor activities, provide relevant clothing suggestions, and even automatically design ideal vacations and times based on the user\u2019s optimal weather preferences.\u202f \\n\\nSimilarly, while traditional applications tend to offer a one-size-fits-all UI, intelligent apps use AI to create tailored interactions. For instance, an intelligent banking app might use generative AI to provide a voice-enabled UI that lets customers use natural language to ask about transactions and receive personalized financial advice.\\n\\n### Infrastructure\\n\\nIntelligent apps integrating these sophisticated features require highly scalable, flexible infrastructure optimized for AI functionality and able to manage large volumes of real-time data. Unlike traditional applications deployed on-premises or in infrastructure as a service (IaaS) environments, intelligent apps demand the flexibility and scalability of cloud native architectures.\\n\\nAn on-premises or IaaS-based e-commerce application may struggle with connecting its data to AI services or scaling to accommodate voluminous real-time data and requests. But an intelligent app built with a cloud native approach and scalability at its core can handle variable demand much more efficiently. It also maintains its infrastructure components on demand, whether for collecting data for AI/ML consumption or user-facing features like AI-enabled customer support and personalization.\\n\\nConsider an intelligent app built on Azure that creates ChatGPT-like experiences with custom data. It might connect [Azure Open AI](https://azure.microsoft.com/en-us/products/ai-services/openai-service?WT.mc_id=javascript-99907-ninarasi) and [Azure Cognitive Search](https://azure.microsoft.com/en-us/products/ai-services/cognitive-search?WT.mc_id=javascript-99907-ninarasi) to an app running on [Azure Container Apps](https://azure.microsoft.com/en-us/products/container-apps/?WT.mc_id=javascript-99907-ninarasi) or [Azure Kubernetes Service](https://azure.microsoft.com/en-us/products/kubernetes-service/?WT.mc_id=javascript-99907-ninarasi).\u202f \\n\\nThe architecture might look like the following diagram based on this [demo app](https://github.com/Azure-Samples/azure-search-openai-demo-csharp):\\n\\n![diagram of a demo app architecture](../../static/img/fallforia/blogs/2023-09-22/diagram-of-a-demo-app-architecture.png)\\n\\nWhile the cloud is well-equipped to handle the challenges associated with shifting to AI-centric infrastructure\u2014handling real-time data processing and storage, integrating and managing AI services, configuring event-driven and microservices architectures, and ensuring security across the distributed environment\u2014re-architecting a traditional app to an intelligent app requires careful planning and execution to ensure it benefits fully from the capabilities of AI and cloud computing.\\n\\n### On-Premises/IaaS vs. Cloud-Native Platforms\\n\\nWhile on-premises and IaaS solutions provide substantial control over data and processes, this fine-grained control comes with setup and maintenance costs and may lack the scalability necessary for AI-driven Intelligent Apps. On-premises hosting involves purchasing and maintaining hardware, including servers and storage systems. IaaS involves leasing virtual hardware and then hiring and training IT personnel for the infrastructure\u2019s operation, maintenance, and security.\\n\\nConversely, infrastructure built on cloud native app services with [Microsoft Azure](https://azure.microsoft.com/?WT.mc_id=javascript-99907-ninarasi) offers virtually unlimited scalability without ongoing hardware and maintenance costs. This feature is ideal for intelligent apps that need to adapt and scale to accommodate fluctuating workloads and data streams. And because these cloud infrastructures typically operate on a pay-as-you-go model, traditionally substantial upfront costs turn into predictable operating expenses, lowering both the cost and risk of launching applications.\\n\\nMoreover, cloud service providers continually update their offerings, so building on sophisticated AI tools and services through the cloud, like the [Azure AI Platform](https://azure.microsoft.com/en-us/solutions/ai/?WT.mc_id=javascript-99907-ninarasi) and orchestration capabilities with [Azure Kubernetes Service](https://azure.microsoft.com/en-us/products/kubernetes-service/?WT.mc_id=javascript-99907-ninarasi), ensures that you have access to the latest AI and most advanced AI models and technologies.\\n\\n### Strategic Considerations for Transitioning to Intelligent Apps\\n\\nWhen developing a strategy for transitioning to intelligent apps, you must consider costs, personnel skill development, and data management.\\n\\nMonolithic architectures are unlikely to support the scale and ease of iteration required by intelligent apps, so you\u2019ll likely need to implement code changes to support the transition. Your chosen hosting solution and the amount of data your app will handle affect cost and performance, in addition to determining privacy and compliance requirements.\\n\\nAn app that manages or stores terabytes or petabytes of data will require storage-optimized virtual machines or a managed database service hosting. Meanwhile, an app that processes video content or performs resource-intensive calculations should prioritize high-performance computing. Maximizing storage and performance can become extraordinarily costly.\\n\\nFinally, existing applications shifting towards intelligent apps may encounter compatibility conflicts, depending on database type or the existing system\u2019s underlying programming language. As you might imagine, a traditional app running on-premises and using a shared local database may need significant restructuring to support a cloud native architecture. You need plans for data migration, potential downtime, and training.\\n\\nNext, let\u2019s look at this transformation process.\\n\\n### Re-Architecting Monolithic Apps into Microservices\\n\\nShifting from [monolithic applications](https://learn.microsoft.com/en-us/dotnet/architecture/containerized-lifecycle/design-develop-containerized-apps/monolithic-applications?WT.mc_id=javascript-99907-ninarasi) to microservices is pivotal for transitioning to Intelligent Apps, and you can use this shift as an opportunity to transform your legacy software into intelligent apps that leverage the flexibility and power of microservices-enabled AI solutions.\\n\\nTraditionally, a monolithic application (let\u2019s use an online marketplace as an example) might handle all activities\u2014from serving webpages to processing payments\u2014within an on-premises server or a cloud server. However, this approach is challenging to scale and remains vulnerable to network failures or cyberattacks.\\n\\nBreaking these applications into multiple services with specific responsibilities, such as authentication or notifications, can overcome these limitations. These services are typically deployed as microservices within container environments like [Azure Kubernetes Service](https://azure.microsoft.com/en-us/products/kubernetes-service/?WT.mc_id=javascript-99907-ninarasi) or serverless platforms like [Azure Container Apps](https://azure.microsoft.com/en-us/products/container-apps?WT.mc_id=javascript-99907-ninarasi) or [Azure Functions](https://azure.microsoft.com/en-us/products/functions/?WT.mc_id=javascript-99907-ninarasi) using automated processes. This approach offers scalability, robustness, and improved performance, as each microservice can run independently and use its own database.\\n\\nShifting towards smaller, independent microservices in app architectures allows us to reap the benefits of AI-powered intelligent apps, for example, by using microservices to handle AI tasks such as processing natural language input, analyzing behavioral data, or personalizing content.\\n\\n:::info\\nWatch [Episode 1](https://aka.ms/learnlive-contoso-app-deconstructed-Ep1) and [Episode 2](https://aka.ms/learnlive-contoso-app-deconstructed-Ep2) of the [Learn Live series](https://aka.ms/contoso-real-estate/learn-live) on how to build, test and deploy an end-to-end intelligent app solution using the [Contoso Real Estate Sample](https://github.com/Azure-Samples/contoso-real-estate).\\n:::\\n\\n### Event-Driven Architectures: Knowing When to Use Them\\n\\n[Event-Driven Architectures](https://learn.microsoft.com/en-us/azure/architecture/guide/architecture-styles/event-driven?WT.mc_id=javascript-99907-ninarasi) (EDAs) can significantly enhance the performance and cost-effectiveness of intelligent apps. EDAs, typically implemented with microservices, respond in real time to events or state changes ranging from user interactions to real-time analytics.\\n\\nConsider an intelligent app designed for personalized customer engagement. Instead of designing the application to check continuously for changes in user data, an EDA can react to such changes immediately, triggering appropriate microservices. For example, if users modify their preferences, an event could trigger an AI-driven recommendation microservice to update its suggestions immediately. This real-time responsiveness, enabled by services like [Azure Event Grid](https://azure.microsoft.com/en-us/products/event-grid/?WT.mc_id=javascript-99907-ninarasi), can improve user experiences and make intelligent apps more adaptive and proactive.\\n\\nHowever, it\u2019s worth noting that while EDAs provide significant benefits, they can introduce unique complexities, such as maintaining event consistency and debugging issues in event chains across multiple microservices. Therefore, it\u2019s important to assess your application\u2019s needs so you can understand where it can benefit from an EDA.\\n\\n## Conclusion\\n\\nTransitioning to intelligent apps represents a shift in strategy, architecture, and infrastructure. By designing and re-architecting for cloud-based infrastructure, your apps gain scalability, cost-effectiveness, robust data management, access to sophisticated AI tools, enhanced security, and reduced operational overhead.\u202f\u202f \\n\\nBut this transition extends well beyond its technical components. Organizations and the people working within them need equal attention. You can explore these facets in greater depth in \u201cCultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management.\u201d\u202f \\n\\nThe power of generative AI is here, and preparing for the move toward cloud-based intelligent applications will ensure that your apps remain performative and scalable and\u2014most importantly\u2014that your business is ready for the newest era of app innovation.\\n\\n## Exercise\\n\\n * Complete the **[Apps Cloud Skills Challenge](https://aka.ms/fallforIA/apps-csc)** to build on your app dev and AI skills.\\n * Complete the **[AI Cloud Skills Challenge](https://aka.ms/fallforIA/ai-csc)** to build on your AI skills.\\n * Register for **[Episode 03](https://aka.ms/learnlive-contoso-app-deconstructed-Ep3)** of the serverless edition Learn Live session to learn how to build, test and deploy an end-to-end intelligent app solution.\\n * Register for **[Ask the Expert: Azure Functions](https://reactor.microsoft.com/en-us/reactor/series/S-1037/)** session for live Q&A with the Product Engineering team on building intelligent serverless apps."},{"id":"build-your-first-intelligent-app-with-azure-ai-and-aks-1","metadata":{"permalink":"/Cloud-Native/30daysofIA/build-your-first-intelligent-app-with-azure-ai-and-aks-1","source":"@site/blog-30daysofIA/2023-09-27/build-your-first-intelligent-app-with-azure-ai-and-aks-1.md","title":"2-1. Build Your First Intelligent App with Azure AI and AKS-1","description":"Delve into creating an Intelligent App that leverages Azure AI Vision to analyze images and extract data. We guide readers through crafting an API to perform optical character recognition (OCR) on uploaded images, and subsequently deploying this API via Azure Kubernetes Service, helping them discover the power of these tools to create innovative, AI-driven solutions.","date":"2023-09-22T09:00:00.000Z","formattedDate":"September 22, 2023","tags":[{"label":"Fall-For-IA","permalink":"/Cloud-Native/30daysofIA/tags/fall-for-ia"},{"label":"30-days-of-IA","permalink":"/Cloud-Native/30daysofIA/tags/30-days-of-ia"},{"label":"learn-live","permalink":"/Cloud-Native/30daysofIA/tags/learn-live"},{"label":"hack-together","permalink":"/Cloud-Native/30daysofIA/tags/hack-together"},{"label":"community-buzz","permalink":"/Cloud-Native/30daysofIA/tags/community-buzz"},{"label":"ask-the-expert","permalink":"/Cloud-Native/30daysofIA/tags/ask-the-expert"},{"label":"azure-kubernetes-service","permalink":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service"},{"label":"azure-functions","permalink":"/Cloud-Native/30daysofIA/tags/azure-functions"},{"label":"azure-openai","permalink":"/Cloud-Native/30daysofIA/tags/azure-openai"},{"label":"azure-container-apps","permalink":"/Cloud-Native/30daysofIA/tags/azure-container-apps"},{"label":"azure-cosmos-db","permalink":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db"},{"label":"github-copilot","permalink":"/Cloud-Native/30daysofIA/tags/github-copilot"},{"label":"github-codespaces","permalink":"/Cloud-Native/30daysofIA/tags/github-codespaces"},{"label":"github-actions","permalink":"/Cloud-Native/30daysofIA/tags/github-actions"}],"readingTime":11.785,"hasTruncateMarker":false,"authors":[{"name":"It\'s 30DaysOfIA","title":"FallForIA Content Team","url":"https://azure.github.io/Cloud-Native/Fall-For-IA/","imageURL":"https://azure.github.io/Cloud-Native/img/logo-ms-cloud-native.png","key":"cnteam"}],"frontMatter":{"date":"2023-09-22T09:00","slug":"build-your-first-intelligent-app-with-azure-ai-and-aks-1","title":"2-1. Build Your First Intelligent App with Azure AI and AKS-1","authors":["cnteam"],"draft":false,"hide_table_of_contents":false,"toc_min_heading_level":2,"toc_max_heading_level":3,"keywords":["Cloud","Data","AI","AI/ML","intelligent apps","cloud-native","30-days","enterprise apps","digital experiences","app modernization","serverless","ai apps","data"],"image":"https://azure.github.io/Cloud-Native/img/ogImage.png","description":"Delve into creating an Intelligent App that leverages Azure AI Vision to analyze images and extract data. We guide readers through crafting an API to perform optical character recognition (OCR) on uploaded images, and subsequently deploying this API via Azure Kubernetes Service, helping them discover the power of these tools to create innovative, AI-driven solutions.","tags":["Fall-For-IA","30-days-of-IA","learn-live","hack-together","community-buzz","ask-the-expert","azure-kubernetes-service","azure-functions","azure-openai","azure-container-apps","azure-cosmos-db","github-copilot","github-codespaces","github-actions"]},"prevItem":{"title":"1-5. Preparing the Path for Intelligent Apps","permalink":"/Cloud-Native/30daysofIA/preparing-the-path-for-intelligent-apps"},"nextItem":{"title":"2-2. Build Your First Intelligent App with Azure AI and AKS-2","permalink":"/Cloud-Native/30daysofIA/build-your-first-intelligent-app-with-azure-ai-and-aks-2"}},"content":"\\n\\n\\n\\n\\n\\n \\n \\n \\n \\n \\n \\n \\n \\n\\n\\n\x3c!-- End METADATA --\x3e\\nDelve into creating an Intelligent App that leverages Azure AI Vision to analyze images and extract data. Develop an API to perform optical character recognition (OCR) on uploaded images and testing this API locally.\\n\\n## What We\'ll Cover:\\n\\n * Understanding Azure AI Vision and Azure Kubernetes Service\\n * Build a Python Web API to perform OCR\\n * Test the API locally\\n\\n![image depicting an Intelligent App using AI](../../static/img/fallforia/blogs/2023-09-27/blog-image-1-1.png)\\n\\n## Jumpstart Your AI Journey: Building Your First Intelligent App with Azure AI and AKS (1)\\n\\nAn Intelligent App is a software application that uses artificial intelligence (AI), such as machine learning (ML), to extract meaningful insights, enabling users to make informed decisions based on the extracted text, data, sound, images, and more.\\n\\nIntelligent Apps can employ advanced algorithms and models to recognize objects, patterns, and features within images, allowing them to perform tasks like object identification, image classification, facial recognition, and image segmentation.\\n\\nTo explore the power of Intelligent Apps, let\u2019s build a Python app that performs [optical character recognition](https://learn.microsoft.com/en-us/azure/ai-services/computer-vision/overview-ocr?WT.mc_id=javascript-99907-ninarasi) (OCR) on images containing numbers and performs aggregate functions on the extracted numbers, like sum and average. We\u2019ll leverage [Azure AI Services](https://azure.microsoft.com/en-ca/products/cognitive-services?WT.mc_id=javascript-99907-ninarasi) for the OCR functionality and [Azure Kubernetes Service](https://azure.microsoft.com/en-ca/products/kubernetes-service?WT.mc_id=javascript-99907-ninarasi) (AKS) to develop, publish, and maintain a cloud-based, scalable app in Azure.\\n\\nLet\u2019s get started!\\n\\n## Understanding Azure AI Vision and Azure Kubernetes Service\\n\\nAzure AI Vision is a cloud-based service that offers a range of advanced computer vision capabilities enabling applications to interpret content, such as images and videos, using ML and other AI algorithms. Azure computer vision lets users integrate powerful vision features into their projects without ML experience.\\n\\nOCR is one of the critical functionalities within Azure vision service, allowing applications to extract text from images. Within Azure AI Vision service, OCR offers several features, including text extraction, multilingual support, layout analysis of documents, and integration with Azure services, such as [Azure Functions](https://azure.microsoft.com/en-ca/products/functions) and [Azure Machine Learning](https://azure.microsoft.com/en-ca/products/machine-learning).\\n\\n:::info\\nWatch the intelligent apps webinar on **[Driving Business Value by Modernizing with Cloud-Native & AI](https://info.microsoft.com/ww-landing-driving-business-value-by-modernizing-with-cloud-native-and-ai.html?WT.mc_id=javascript-99907-ninarasi)**\u202fwith *Microsoft* and *Forrester* on **September 26**. \\n:::\\n\\nAKS is Microsoft Azure\u2019s container orchestration platform that enables us to quickly and efficiently deploy, manage, and scale containerized applications, such as our Intelligent App.\\n\\nLet\u2019s explore how to leverage these tools to build an Intelligent App that uses Azure AI Vision to analyze images and extract data.\\n\\n### Prerequisites\\n\\nTo follow this tutorial, ensure you have the following:\\n\\n- [Python 3.7](https://www.python.org/downloads/) or later installed\\n- [VS Code](https://code.visualstudio.com/download) or another integrated development environment (IDE) for writing Python code\\n- The [sample Python application](https://github.com/contentlab-io/Microsoft-Using-Azure-Kubernetes-Service-to-Deploy-an-Intelligent-App-for-Analyzing-Images-1/tree/main/Microsoft_Series17-18_Code/intelligent-app-before) downloaded\\n- [pip](https://pip.pypa.io/en/stable/installing/), the package manager for Python, installed\\n- [Docker Desktop](https://www.docker.com/products/docker-desktop/) installed. Ensure Docker is running on Linux containers.\\n- [Postman](https://www.postman.com/downloads/) installed. We\u2019ll use this to test our API.\\n- A [free Azure account](https://azure.microsoft.com/free/?WT.mc_id=javascript-99907-ninarasi). Sign up if you don\u2019t have one yet.\\n- The [Azure command-line interface](https://learn.microsoft.com/cli/azure/install-azure-cli?WT.mc_id=javascript-99907-ninarasi) (CLI)\\n\\nFor a preview of this final project, take a look at the [complete project code](https://github.com/contentlab-io/Microsoft-Using-Azure-Kubernetes-Service-to-Deploy-an-Intelligent-App-for-Analyzing-Images-1/tree/main/Microsoft_Series17-18_Code/intelligent-app-after).\\n\\n### Building the API with Azure AI Vision Service\\n\\nFirst, log in to your Azure account and navigate to the [Azure Portal](https://portal.azure.com).\\n\\nClick **Create a resource** and search for \u201cresource group.\u201d Create a new resource group named `computer-vision`.\\n\\n![image in Azure Portal of creating a new resource group](../../static/img/fallforia/blogs/2023-09-27/blog-image-1-2.png)\\n\\nReturn to the Azure Portal home page and click **Create a resource**.\\n\\n![image in Azure Portal of creating a new resource](../../static/img/fallforia/blogs/2023-09-27/blog-image-1-3.png)\\n\\nSearch for \u201ccomputer vision\u201d and select it from the results. Click **Create**.\\n\\n![image of searching for resource group in Azure Portal](../../static/img/fallforia/blogs/2023-09-27/blog-image-1-4.png)\\n\\nClicking **Create** displays the Create Computer Vision wizard. Select the resource group you just created as the Resource group from the dropdown. Enter a resource name similar to what is seen in the screenshot below. Note that this must be a unique name so choose one that is not currently in use. Select Standard S1 as the pricing tier and check the Responsible use of AI box in the field at the bottom.\\n\\n![Image of selecting the Responsible use of AI box](../../static/img/fallforia/blogs/2023-09-27/blog-image-1-5.png)\\n\\n![Image of project details](../../static/img/fallforia/blogs/2023-09-27/blog-image-1-6.png)\\n\\nClick **Review + Create**, then click **Create**.\\n\\nNext, get the API key and endpoint from the Azure AI services multi-service account you created. Once the resource is created, navigate to the resource details page. Under **Resource Management**, select **Keys and Endpoint**. Once on the **Keys and Endpoint** page, you\u2019ll find the **Key 1** and **Endpoint** values. These credentials are necessary to access the Azure AI APIs.\\n\\n![image of selecting Keys and Endpoint](../../static/img/fallforia/blogs/2023-09-27/blog-image-1-7.png)\\n\\n![second image of selecting Keys and Endpoint](../../static/img/fallforia/blogs/2023-09-27/blog-image-1-8.png)\\n\\n#### Configuring the Local Environment Variables\\n\\nNext, we need to define a local environment variable file. This convenient way to store our API keys in a simple text file enables us to manage sensitive information and separate configuration from our API code.\\n\\nIf you haven\u2019t done so already, clone the [starter project template](https://github.com/contentlab-io/Microsoft-Using-Azure-Kubernetes-Service-to-Deploy-an-Intelligent-App-for-Analyzing-Images) from GitHub into your local computer. Open the [starter project template](https://github.com/contentlab-io/Microsoft-Using-Azure-Kubernetes-Service-to-Deploy-an-Intelligent-App-for-Analyzing-Images/tree/main/Microsoft_Series17-18_Code/intelligent-app-before) in a code editor and create a new `.env` file in the root folder.\\n\\n**Note**: Root folder in this case is the \u201c/Microsoft_Series17-18_Code/intelligent-app-before\\" folder.\\n\\nAdd the following key-value pairs to the file, remembering to replace the placeholder values with those you obtained from the previous step.\\n\\n```\\nVISION_KEY=\\nVISION_ENDPOINT=\\n```\\n\\n#### Reviewing the Quickstart Code\\n\\nLet\u2019s review the `app.py` file in the starter project template. \\n\\nThe `app.py` file works as the entry point for the Flask application. It is responsible for initializing the web server, listening for incoming requests, dispatching them to the appropriate route handlers, and generating responses. \\n\\nBelow is the code contained in the `app.py` file:\\n\\n```\\nimport os\\nimport json\\n \\nfrom flask import Flask, request \\nfrom flask_restful import Resource, Api\\nfrom werkzeug.utils import secure_filename\\n\\napp = Flask(__name__,\\n static_url_path=\'\',\\n static_folder=\'static/files\')\\n\\napi = Api(app)\\n\\napp = Flask(__name__)\\n\\napp.config[\'UPLOAD_FOLDER\'] = \'files\'\\n\\napi = Api(app)\\n\\nclass UploadHandler(Resource):\\n\\n def allowed_file(self, filename):\\n return \'.\' in filename and \\\\\\n filename.rsplit(\'.\', 1)[1].lower() in {\'png\'}\\n\\n def post(self):\\n form = request.form.to_dict()\\n\\n if \'file\' not in request.files:\\n return json.dumps({ \\"success\\": False, \\"error\\": \\"No file part\\"})\\n\\n file = request.files.get(\\"file\\")\\n if file and self.allowed_file(file.filename):\\n filename = secure_filename(file.filename)\\n upload_folder = \\"static/files\\"\\n if not os.path.exists(upload_folder):\\n os.makedirs(upload_folder)\\n local_file_path = os.path.join(upload_folder, filename)\\n file.save(local_file_path)\\n\\n return f\\"File {filename} uploaded successfully to folder: {upload_folder}\\"\\n\\napi.add_resource(UploadHandler, \\"/\\")\\n\\nif __name__ == \'__main__\':\\n app.run(debug=True)\\n\\n```\\n\\nThe `UploadHandler` class above is defined as a Flask RESTful resource to handle file uploads. The class contains two methods:\\n\\n- The `allowed_file` method checks whether the file extension is allowed for upload. In this case, only `.png` files are allowed.\\n\\n- The `post` method handles HTTP POST requests for file uploads. It saves the uploaded `.png` file to the `static/files` folder.\\n\\nFinally, the application returns a text response informing us that the file has been uploaded successfully.\\n\\n#### Implementing the REST API in Python\\n\\nTo implement image analysis in your REST API, open the starter project template in your terminal, [create a virtual environment](https://docs.python.org/3/library/venv.html), and [activate it](https://docs.python.org/3/library/venv.html#how-venvs-work).\\n\\nNext, install the Azure AI Vision SDK:\\n\\n```\\npip install azure-ai-vision\\n```\\n\\nThen, add the following line to the `requirements.txt` file to include the `azure-ai-vision` package:\\n\\n```\\nazure-ai-vision==0.13.0b1\\n```\\n\\nNow, create an `ocr_helper.py` file in the project\u2019s root folder. This file is a Python module that provides functions for processing images using OCR. Add the following code to the file:\\n\\n```\\nimport os\\nfrom statistics import median\\nfrom decimal import Decimal\\nimport azure.ai.vision as sdk\\n\\ndef process_ocr(source_image):\\n service_options = sdk.VisionServiceOptions(os.environ[\\"VISION_ENDPOINT\\"],\\n os.environ[\\"VISION_KEY\\"])\\n\\n vision_source = sdk.VisionSource(filename=source_image)\\n\\n analysis_options = sdk.ImageAnalysisOptions()\\n\\n analysis_options.features = (\\n sdk.ImageAnalysisFeature.CAPTION |\\n sdk.ImageAnalysisFeature.TEXT\\n )\\n\\n analysis_options.language = \\"en\\"\\n\\n analysis_options.gender_neutral_caption = True\\n\\n image_analyzer = sdk.ImageAnalyzer(service_options, vision_source, analysis_options)\\n\\n result = image_analyzer.analyze()\\n\\n ocr_result = get_ocr_result(result)\\n\\n return ocr_result\\n\\ndef get_ocr_result(result):\\n string_list = []\\n\\n if result.reason != sdk.ImageAnalysisResultReason.ANALYZED:\\n return sdk.ImageAnalysisErrorDetails.from_result(result)\\n else:\\n if result.text is not None:\\n for line in result.text.lines:\\n for word in line.words:\\n string_list.append(word.content)\\n\\n number_list = convert_to_decimal_list(string_list)\\n\\n aggregate_result = aggregate_operations(number_list)\\n\\n return {\\n \\"aggregate_result\\": aggregate_result,\\n \\"numbers_read\\": string_list\\n } \\n\\ndef convert_to_decimal_list(string_list):\\n return list(map(Decimal, string_list))\\n\\ndef aggregate_operations(numbers):\\n result = {\\n \'sum\': sum(numbers),\\n \'average\': sum(numbers) / len(numbers),\\n \'median\': median(numbers),\\n \'min\': min(numbers),\\n \'max\': max(numbers)\\n `}`\\n `return result`\\n```\\n\\nThis module uses the [azure-ai-vision](https://learn.microsoft.com/python/api/azure-ai-vision/?view=azure-python-preview?WT.mc_id=javascript-99907-ninarasi) package to analyze images, including capturing captions and extracting text from the image. The `process_ocr` function inputs an image file and performs the OCR analysis. The module is a convenient tool for analyzing text-based data in pictures and performing numerical computations based on that data.\\n\\nLet\u2019s review the different components of the `ocr_helper.py` module listed above and explore how its functions help our Intelligent App process images and perform aggregate operations:\\n\\n- The `process_ocr` function takes the parameter `source_image`, which is the path of the image to be processed. The function then initializes the `VisionServiceOptions` using environment variables `VISION_ENDPOINT` and `VISION_KEY` to connect to the Azure AI Vision API.\\n- The `process_ocr` function creates a `VisionSource` object with the specified `source_image` file name. `ImageAnalysisOptions` specify the features to be analyzed, including `CAPTION` and `TEXT`. The language is set to English (`\\"en\\"`), and gender-neutral captions are enabled.\\n- Finally, an `ImageAnalyzer` object is created with the service options, vision source, and analysis options. The image is then analyzed using the `image_analyzer.analyze` method to retrieve OCR results. The recognized text from the OCR result is extracted, with numerical values stored in the `string_list` variable.\\n- The `convert_to_decimal_list` function converts the list of strings to a list of decimal numbers, which helps process numeric values extracted from the text.\\n- The `aggregate_operations` function calculates various aggregate statistics (sum, average, median, minimum, maximum) from a list of numbers and returns the results in a dictionary.\\n\\nNote that you must have the appropriate credentials (`VISION_KEY`) and endpoint (`VISION_ENDPOINT`) configured for the Azure AI Vision API to use this module.\\n\\nFinally, we must modify the `app.py` file so our code can use the `process_ocr` function of the `ocr_helper.py` file.\\n\\nAdd the following import statement to the `app.py` file: \\n\\n```\\nfrom ocr_helper import process_ocr\\n```\\n\\nThen, replace this line:\\n\\n`return f\\"File {filename} uploaded successfully to folder: {upload_folder}\\"`\\n\\nWith these two lines:\\n\\n```\\naggregates = process_ocr(local_file_path)\\nreturn json.dumps(aggregates, default=str)\\n```\\n\\nDoing so allows our application to perform aggregate operations (sum, average, median, minimum, maximum) of any numeric values found in the text and return the operation\u2019s result as a JSON response.\\n\\n### Running the Intelligent App Locally\\n\\nOur final goal is to deploy the sample app to AKS, which will run the app in a container. However, as a good practice, let\u2019s run the app in a container locally before moving it to AKS. Doing so ensures that our application and its dependencies are encapsulated and can be run consistently across different environments.\\n\\n#### Deploying to Docker Desktop\\n\\nFor our application to run on Docker, it needs two additional files: a Dockerfile and `docker-compose.yml`. A Dockerfile creates a single container image by specifying the steps to build it, and a `docker-compose.yml` file manages multiple containers as a cohesive application, defining their configurations and relationships. Dockerfile builds the image, while `docker-compose.yml` orchestrates multi-container applications.\\n\\nIn the project root folder, add a file named `Dockerfile` with the content below:\\n\\n```\\n# syntax=docker/dockerfile:1\\n\\nFROM python:3.8-slim-buster\\n\\nWORKDIR /intelligentapp\\n\\nCOPY requirements.txt requirements.txt\\nRUN pip3 install -r requirements.txt\\n\\nRUN pip install debugpy\\n\\nCOPY . .\\n\\nCMD [ \\"python3\\", \\"-m\\" , \\"flask\\", \\"run\\", \\"--host=0.0.0.0\\"]\\n```\\n\\nNow create a file named `docker-compose.yml` file in the project\u2019s root folder with the following code, replacing the `VISION_KEY` and the `VISION_ENDPOINT` according to the environment variables you configured earlier:\\n\\n```\\nversion: \'3.8\'\\nservices:\\n intelligentapp:\\n build:\\n context: .\\n dockerfile: Dockerfile\\n image: intelligent-app\\n ports:\\n - 5000:5000\\n container_name: intelligent-app\\n environment:\\n - VISION_KEY=\\n - VISION_ENDPOINT=\\n```\\n\\nThen, run the following command in the terminal to build the image and start a container for the Intelligent App services defined in the `docker-compose.yml` file: \\n\\n```\\ndocker-compose up --build --force-recreate\\n```\\n\\nNext, open a new terminal and run the command below to list the image deployed to your local Docker:\\n\\n```\\ndocker images\\n```\\n\\n![docker images](../../static/img/fallforia/blogs/2023-09-27/blog-image-1-9.png)\\n\\n#### Testing the Intelligent App Locally\\n\\nNow, we\u2019ll test the Intelligent App\u2019s functionality using the following test image:\\n\\n![test image to test functionality](../../static/img/fallforia/blogs/2023-09-27/blog-image-1-10.png)\\n\\nThis image is provided as `sample1.png` in the sample app source files, so you can easily use it in the next step.\\n\\nThe Intelligent App will read the image above using OCR to extract numbers in the image and perform five aggregate functions:\\n\\n- Sum\\n- Average\\n- Median\\n- Min\\n- Max\\n\\nTo test the API, open Postman and fill out the fields as follows:\\n\\n- **URL:** http://localhost:5000/\\n- **Method:** POST\\n- **Body:**\\n - **Form-data**\\n - **Key**: File \u2014 Click the right end of the **Key** field and select **File** from the dropdown list.\\n - **Value**: Click **Select Files**, then select the **sample1.png** file provided in the sample code.\\n\\n![image of selecting the sample file](../../static/img/fallforia/blogs/2023-09-27/blog-image-1-11.png)\\n\\nNow click the **Send** button and review the result body:\\n\\n`\\"{\\\\\\"aggregate_result\\\\\\": {\\\\\\"sum\\\\\\": \\\\\\"25821\\\\\\", \\\\\\"average\\\\\\": \\\\\\"5164.2\\\\\\", \\\\\\"median\\\\\\": \\\\\\"5622\\\\\\", \\\\\\"min\\\\\\": \\\\\\"1447\\\\\\", \\\\\\"max\\\\\\": \\\\\\"9802\\\\\\"}, \\\\\\"numbers_read\\\\\\": [\\\\\\"3049\\\\\\", \\\\\\"5901\\\\\\", \\\\\\"5622\\\\\\", \\\\\\"1447\\\\\\", \\\\\\"9802\\\\\\"]}\\"`\\n\\nAs we can see, the app running on a local container returned the correct results based on the sample image. So, we\u2019re ready to prepare our Azure resources to run the Intelligent App on AKS.\\n\\n## Exercise\\n\\n * Complete this **hands-on sample** [project code](https://github.com/contentlab-io/Microsoft-Using-Azure-Kubernetes-Service-to-Deploy-an-Intelligent-App-for-Analyzing-Images-1/tree/main/Microsoft_Series17-18_Code/intelligent-app-after) to build your first intelligent app.\\n * Complete the **[Apps Cloud Skills Challenge](https://aka.ms/fallforIA/apps-csc)** to build on your app dev and AI skills.\\n * Watch the **[Ask the Expert: Azure Functions](https://reactor.microsoft.com/en-us/reactor/series/S-1037/)** session for live Q&A with the Product Engineering team on building intelligent apps.\\n\\n ## Next Steps\\n\\nIn this article, we explored the creation of an Intelligent App that leverages Azure AI Vision to analyze images and extract data. We learned how to build a Python Web API to perform OCR on uploaded images and subsequently test this API locally.\\n\\nHead to the [next part](https://azure.github.io/Cloud-Native/30daysofIA/build-your-first-intelligent-app-with-azure-ai-and-aks-2), to deploy this API via Azure Kubernetes Service."},{"id":"build-your-first-intelligent-app-with-azure-ai-and-aks-2","metadata":{"permalink":"/Cloud-Native/30daysofIA/build-your-first-intelligent-app-with-azure-ai-and-aks-2","source":"@site/blog-30daysofIA/2023-09-27/build-your-first-intelligent-app-with-azure-ai-and-aks-2.md","title":"2-2. Build Your First Intelligent App with Azure AI and AKS-2","description":"Delve into creating an Intelligent App that leverages Azure AI Vision to analyze images and extract data. We guide readers through crafting an API to perform optical character recognition (OCR) on uploaded images, and subsequently deploying this API via Azure Kubernetes Service, helping them discover the power of these tools to create innovative, AI-driven solutions.","date":"2023-09-22T09:00:00.000Z","formattedDate":"September 22, 2023","tags":[{"label":"Fall-For-IA","permalink":"/Cloud-Native/30daysofIA/tags/fall-for-ia"},{"label":"30-days-of-IA","permalink":"/Cloud-Native/30daysofIA/tags/30-days-of-ia"},{"label":"learn-live","permalink":"/Cloud-Native/30daysofIA/tags/learn-live"},{"label":"hack-together","permalink":"/Cloud-Native/30daysofIA/tags/hack-together"},{"label":"community-buzz","permalink":"/Cloud-Native/30daysofIA/tags/community-buzz"},{"label":"ask-the-expert","permalink":"/Cloud-Native/30daysofIA/tags/ask-the-expert"},{"label":"azure-kubernetes-service","permalink":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service"},{"label":"azure-functions","permalink":"/Cloud-Native/30daysofIA/tags/azure-functions"},{"label":"azure-openai","permalink":"/Cloud-Native/30daysofIA/tags/azure-openai"},{"label":"azure-container-apps","permalink":"/Cloud-Native/30daysofIA/tags/azure-container-apps"},{"label":"azure-cosmos-db","permalink":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db"},{"label":"github-copilot","permalink":"/Cloud-Native/30daysofIA/tags/github-copilot"},{"label":"github-codespaces","permalink":"/Cloud-Native/30daysofIA/tags/github-codespaces"},{"label":"github-actions","permalink":"/Cloud-Native/30daysofIA/tags/github-actions"}],"readingTime":8.71,"hasTruncateMarker":false,"authors":[{"name":"It\'s 30DaysOfIA","title":"FallForIA Content Team","url":"https://azure.github.io/Cloud-Native/Fall-For-IA/","imageURL":"https://azure.github.io/Cloud-Native/img/logo-ms-cloud-native.png","key":"cnteam"}],"frontMatter":{"date":"2023-09-22T09:00","slug":"build-your-first-intelligent-app-with-azure-ai-and-aks-2","title":"2-2. Build Your First Intelligent App with Azure AI and AKS-2","authors":["cnteam"],"draft":false,"hide_table_of_contents":false,"toc_min_heading_level":2,"toc_max_heading_level":3,"keywords":["Cloud","Data","AI","AI/ML","intelligent apps","cloud-native","30-days","enterprise apps","digital experiences","app modernization","serverless","ai apps","data"],"image":"https://azure.github.io/Cloud-Native/img/ogImage.png","description":"Delve into creating an Intelligent App that leverages Azure AI Vision to analyze images and extract data. We guide readers through crafting an API to perform optical character recognition (OCR) on uploaded images, and subsequently deploying this API via Azure Kubernetes Service, helping them discover the power of these tools to create innovative, AI-driven solutions.","tags":["Fall-For-IA","30-days-of-IA","learn-live","hack-together","community-buzz","ask-the-expert","azure-kubernetes-service","azure-functions","azure-openai","azure-container-apps","azure-cosmos-db","github-copilot","github-codespaces","github-actions"]},"prevItem":{"title":"2-1. Build Your First Intelligent App with Azure AI and AKS-1","permalink":"/Cloud-Native/30daysofIA/build-your-first-intelligent-app-with-azure-ai-and-aks-1"},"nextItem":{"title":"1-4. How Digital Natives leverage Generative AI","permalink":"/Cloud-Native/30daysofIA/how-digital-natives-leverage-generative-ai"}},"content":"\\n\\n\\n\\n\\n\\n \\n \\n \\n \\n \\n \\n \\n \\n\\n\\n\x3c!-- End METADATA --\x3e\\nIn the last article, we created an Intelligent App that leverages Azure AI Vision to analyze images and extract data. Develop an API to perform optical character recognition (OCR) on uploaded images and testing this API locally. \\n\\nIn this article, we will deploy the web API to the cloud platform using Azure Kubernetes Service. \\n\\n## What We\'ll Cover:\\n\\n * Understanding Azure AI Vision and Azure Kubernetes Service\\n * Build a Python Web API to perform OCR\\n * Test the API locally\\n\\n![image depicting an Intelligent App using AI](../../static/img/fallforia/blogs/2023-09-27/blog-image-2-1.jpeg)\\n\\n## Jumpstart Your AI Journey: Building Your First Intelligent App with Azure AI and AKS (2)\\n\\nIn the previous article we explored the creation of an Intelligent App that leverages Azure AI Vision to analyze images and extract data. We learned how to build a Python Web API to perform OCR on uploaded images and subsequently test this API locally. \\n\\nIn this article we will use [Azure Kubernetes Service](https://azure.microsoft.com/products/kubernetes-service?WT.mc_id=javascript-99907-ninarasi) (AKS) to develop, publish, and maintain our app in the cloud on Azure. \\n\\nLet\u2019s get started!\\n\\n### Prerequisites\\n\\nTo follow this tutorial, ensure you have completed [Jumpstart Your AI Journey: Building Your First Intelligent App with Azure AI and AKS (1)](https://azure.github.io/Cloud-Native/30daysofIA/build-your-first-intelligent-app-with-azure-ai-and-aks-1).\\n\\n## Pushing a Container Image to Azure Container Registry (ACR)\\n\\nTo start, open your CLI or terminal and type the following command:\\n\\n```\\naz login\\n```\\n\\nFollow the instructions displayed on your browser to enter your Azure credentials.\\n\\nOnce authenticated, you\u2019ll initiate a secure connection between your local environment and Azure. This process grants you access to cloud services and resources.\\n\\nNext, type the command below in your terminal to set up a new Azure Container Registry (ACR) to store your container images:\\n\\n```\\naz acr create --resource-group computer-vision --name --sku Basic\\n```\\n\\nRemember to replace `` with your container registry name. The name must be unique within Azure and comply with [these rules](https://learn.microsoft.com/azure/azure-resource-manager/management/resource-name-rules#microsoftcontainerregistry).\\n\\nThe command above creates an [Azure Container Registry](https://azure.microsoft.com/products/container-registry?WT.mc_id=javascript-99907-ninarasi) (ACR) in the `computer-vision` resource group under the [Basic SKU](https://learn.microsoft.com/azure/container-registry/container-registry-skus?WT.mc_id=javascript-99907-ninarasi). This ACR is your secure and private repository for storing container images within Azure.\\n\\nNext, log in to the registry with the following command:\\n\\n```\\naz acr login -n \\n```\\n\\nThe `az acr login` command above lets you securely authenticate and access the specified ACR without providing their Azure credentials each time.\\n\\nNow, run the following command in your terminal. It will display the endpoint URL to log in to and interact with the ACR for pushing and pulling container images.\\n\\n```\\naz acr show --name --query loginServer --output table\\n```\\n\\nThis returns an endpoint URL as follows: \\n\\n Result\\n ----------------------------------\\n .azurecr.io\\n\\nNow, run the following command to show all container images, their repository, tags, and size:\\n\\n```\\ndocker images\\n```\\n\\n| REPOSITORY | TAG | IMAGE ID | CREATED | SIZE |\\n|:--------------|:--------------|:--------------|:--------------|:--------------|\\n| intelligent-app | latest | a7bf9f753617 | 16 hours ago | 197MB |\\n\\nTags are necessary to push Docker images to a remote registry like Azure Container Registry. They also let you differentiate between different versions of the same image and upload or download the one you want.\\n\\nRun the following command to tag your Docker image:\\n\\n```\\ndocker tag intelligent-app .azurecr.io/intelligent-app:v1\\n```\\nThen, run the `docker images` command again to check your tagged image:\\n\\n```\\ndocker images\\n```\\n\\n| REPOSITORY | TAG | IMAGE ID | CREATED | SIZE |\\n|:--------------|:--------------|:--------------|:--------------|:--------------|\\n| intelligent-app | latest | c52168039265 | About a minute ago | 197MB |\\n| <name-of-azure-container-registry>.azurecr.io/intelligent-app | v1 | c52168039265 | About a minute ago | 197MB |\\n\\nNow run the following command so Docker can securely upload the image to your Azure Container Registry:\\n\\n```\\ndocker push .azurecr.io/intelligent-app:v1\\n```\\n\\nOnce we\'ve deployed the image to the container registry, AKS can access it during deployment.\\n\\n:::info\\nWatch **[Episode 03](https://aka.ms/learnlive-contoso-app-deconstructed-Ep3)** for the Learn Live session to experience a guided session on how to build, test and deploy an end-to-end intelligent app solution.\\n:::\\n\\n## Deploying the Intelligent App on Azure Kubernetes Service (AKS)\\n\\nBefore we deploy our Intelligent App to AKS, we need to provision an AKS cluster and define Kubernetes manifests.\\n\\nTo provision an AKS cluster to host our application, we specify the desired configurations for the cluster, such as the number of nodes, node size, and networking options. But first, download and install the Kubernetes command-line tools (kubectl), a client credential plugin implementing Azure authentication:\\n\\n```\\naz aks install-cli\\n```\\n\\nIf you\u2019re using Linux, [review this tutorial](https://learn.microsoft.com/en-us/cli/azure/install-azure-cli-linux?pivots=apt?WT.mc_id=javascript-99907-ninarasi). Then run the following: \\n\\n```\\nsudo az aks install-cli\\n```\\n\\nNext, run the following command in your terminal to enable access to the networking-related resources and services provided by the `Microsoft.Network`` namespace in Azure: \\n\\n```\\naz provider register --namespace Microsoft.Network\\n```\\n\\nNow, we must create an AKS cluster. Run the following command to create an AKS cluster named `aks-intelligent-app` in the `computer-vision` resource group.\\n\\n```\\naz aks create --resource-group computer-vision --name aks-intelligent-app --node-count 1 --generate-ssh-keys\\n```\\n\\nThe command above specifies the target resource group: `computer-vision`. The [node pool](https://learn.microsoft.com/en-us/azure/aks/use-multiple-node-pools?WT.mc_id=javascript-99907-ninarasi) is configured with one virtual machine (VM), and the secure shell (SSH) keys for secure node access are generated automatically.\\n\\nNext, run the following command to update the AKS cluster you created by attaching it to your ACR. Doing so allows the AKS cluster to pull container images from the specified ACR when deploying workloads to the cluster.\\n\\n```\\naz aks update -n aks-intelligent-app -g computer-vision --attach-acr \\n```\\n\\nThen, run the command below to configure kubectl to work with our AKS cluster in the computer-vision resource group. \\n\\n```\\naz aks get-credentials --resource-group computer-vision --name aks-intelligent-app\\n```\\n\\nThe command above retrieves the necessary credentials and context information required for kubectl to communicate with the AKS cluster.\\n\\nWe still have to define the Kubernetes manifests written in YAML, describing the desired state of our application deployment, including containers, networking, and scaling rules. We\u2019ll prepare these manifests, including [Deployment](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/) and [Service](https://kubernetes.io/docs/concepts/services-networking/service/) configurations, to define how our application should be deployed and exposed.\\n\\nCreate a folder named `Deployment` in the project root directory. Next, create two files in the deployment folder: `deployment.yml` and `service.yml`.\\n\\nAdd the following configuration to the `deployment.yml` file, replacing the `` placeholder with your registry\u2019s name:\\n\\n```\\napiVersion: apps/v1\\nkind: Deployment\\nmetadata:\\n name: intelligent-app\\nspec:\\n replicas: 1\\n selector:\\n matchLabels:\\n app: intelligent-app\\n template:\\n metadata:\\n labels:\\n app: intelligent-app\\n spec:\\n nodeSelector:\\n kubernetes.io/os: linux\\n containers:\\n - name: intelligent-app\\n image: .azurecr.io/intelligent-app:v1\\n resources:\\n limits:\\n memory: 512Mi\\n cpu: \\"1\\"\\n requests:\\n memory: 256Mi\\n cpu: \\"0.2\\"\\n ports:\\n - containerPort: 5000\\n env:\\n - name: FLASK_DEBUG\\n value: \\"1\\"\\n - name: VISION_KEY\\n value: \\n - name: VISION_ENDPOINT\\n value: \\n```\\n\\nAdditionally, edit the `VISION_KEY` and `VISION_ENDPOINT` environment variables above according to the API key and endpoint of your Azure AI Services instance.\\n\\nThen, add the following configuration to the `service.yml` file:\\n\\n```\\napiVersion: v1\\nkind: Service \\nmetadata:\\n name: intelligent-app-service\\nspec:\\n type: LoadBalancer\\n ports:\\n - protocol: TCP\\n port: 80\\n targetPort: 5000\\n name: port5000\\n selector:\\n app: intelligent-app\\n```\\n\\nNow, we\u2019ll deploy our application using kubectl. Applying the Kubernetes manifests creates the necessary resources and deploys our containerized application to the AKS cluster.\\n\\nFirst, change the terminal to the `deployment` folder: \\n\\n```\\ncd Deployment \\n```\\n\\nThen, run the following command to create or update Kubernetes resources defined in the `deployment.yml` file:\\n\\n```\\nkubectl apply -f deployment.yml\\n```\\n\\nCreate a Kubernetes Service resource in a Kubernetes cluster based on the configuration defined in the `service.yml` file using the code below: \\n\\n```\\nkubectl apply -f service.yml\\n```\\n\\nOnce you\u2019ve applied the resource definition and the service configuration contained in the `deployment.yml` and the `service.yml` files, open the **aks-intelligent-app** Kubernetes Service in the Azure Portal, select **Workloads** under **Kubernetes resources** on the sidebar, and find the deployment named **intelligent-app**. It must have the status \u201cReady 1/1\u201d. If you encounter an issue with this status, check out these [troubleshooting resources](https://learn.microsoft.com/en-us/troubleshoot/azure/azure-kubernetes/node-not-ready-basic-troubleshooting?WT.mc_id=javascript-99907-ninarasi).\\n\\n![image of selecting the deployment in Azure Portal](../../static/img/fallforia/blogs/2023-09-27/blog-image-2-2.png)\\n\\n## Testing the Intelligent App on AKS\\n\\nTo test the app on AKS, first, run the command below:\\n\\n```\\nkubectl get services\\n```\\n\\nThis command lists the Services and their corresponding details, including the Service name, cluster IP address, external IP, and ports.\\n\\n| NAME | TYPE | CLUSTER-IP | EXTERNAL-IP | PORT(S) | AGE |\\n|:--------------|:--------------|:--------------|:--------------|:--------------|:--------------|\\n| intelligent-app-service | LoadBalancer | 10.0.77.60 | 20.121.76.153 | 80:30936/TCP | 47s |\\n| kubernetes | ClusterIP | 10.0.0.1 | <none> | 443/TCP | 14m |\\n\\nThe output above shows a Kubernetes Service named `intelligent-app-service` with a type set to `LoadBalancer`. It\u2019s reachable from within the cluster using the cluster IP `10.0.77.60` and accessible externally via the external IP `20.121.76.153` on port 80 (mapped to port 30936).\\n\\n**Note**: Your set of IP addresses will differ. Remember to use your unique external IP address when testing with Postman. \\n\\nTo test the deployed app, go to Postman, replace the URL with the external IP of the Kubernetes Service you just deployed, and click **Send**:\\n\\n![mage of sending the app in Postman](../../static/img/fallforia/blogs/2023-09-27/blog-image-2-3.png)\\n\\nAs we can see, our Intelligent App has successfully deployed to AKS and is functioning on the cloud as expected.\\n\\n## Exercise\\n\\n* Complete this **hands-on** sample [project code](https://github.com/contentlab-io/Microsoft-Using-Azure-Kubernetes-Service-to-Deploy-an-Intelligent-App-for-Analyzing-Images-1/tree/main/Microsoft_Series17-18_Code/intelligent-app-after) to build your first intelligent app.\\n* Complete the [Apps Cloud Skills Challenge](https://aka.ms/fallforIA/apps-csc) to build on your app dev and AI skills.\\n* Register for [Ask the Expert: Azure Kubernetes Service](https://reactor.microsoft.com/en-us/reactor/series/S-1037/) session for live Q&A with the Product Engineering team on building intelligent apps.\\n\\n## Next Steps\\n\\nIn this two-part article, we explored the creation of an Intelligent App that leverages Azure AI Vision to analyze images and extract data. We learned how to build a Python Web API to perform OCR on uploaded images and subsequently deploy this API via Azure Kubernetes Service.\\n\\nBesides OCR and Image Analysis, you can continue exploring Azure\u2019s vast array of services and experiment further with Azure AI and AKS by applying various practical uses to your Intelligent Apps, including natural language processing, speech recognition and synthesis, sentiment analysis for customer feedback, and automated content moderation."},{"id":"how-digital-natives-leverage-generative-ai","metadata":{"permalink":"/Cloud-Native/30daysofIA/how-digital-natives-leverage-generative-ai","source":"@site/blog-30daysofIA/2023-09-21/how-digital-natives-leverage-generative-ai.md","title":"1-4. How Digital Natives leverage Generative AI","description":"Article Description: This article explores how digital natives and Independent Software Vendors (ISVs) can transform their business operations and generate higher value for customers with generative AI and intelligent apps.","date":"2023-09-21T09:00:00.000Z","formattedDate":"September 21, 2023","tags":[{"label":"Fall-For-IA","permalink":"/Cloud-Native/30daysofIA/tags/fall-for-ia"},{"label":"30-days-of-IA","permalink":"/Cloud-Native/30daysofIA/tags/30-days-of-ia"},{"label":"learn-live","permalink":"/Cloud-Native/30daysofIA/tags/learn-live"},{"label":"hack-together","permalink":"/Cloud-Native/30daysofIA/tags/hack-together"},{"label":"community-buzz","permalink":"/Cloud-Native/30daysofIA/tags/community-buzz"},{"label":"ask-the-expert","permalink":"/Cloud-Native/30daysofIA/tags/ask-the-expert"},{"label":"azure-kubernetes-service","permalink":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service"},{"label":"azure-functions","permalink":"/Cloud-Native/30daysofIA/tags/azure-functions"},{"label":"azure-openai","permalink":"/Cloud-Native/30daysofIA/tags/azure-openai"},{"label":"azure-container-apps","permalink":"/Cloud-Native/30daysofIA/tags/azure-container-apps"},{"label":"azure-cosmos-db","permalink":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db"},{"label":"github-copilot","permalink":"/Cloud-Native/30daysofIA/tags/github-copilot"},{"label":"github-codespaces","permalink":"/Cloud-Native/30daysofIA/tags/github-codespaces"},{"label":"github-actions","permalink":"/Cloud-Native/30daysofIA/tags/github-actions"}],"readingTime":9.29,"hasTruncateMarker":false,"authors":[{"name":"It\'s 30DaysOfIA","title":"FallForIA Content Team","url":"https://azure.github.io/Cloud-Native/Fall-For-IA/","imageURL":"https://azure.github.io/Cloud-Native/img/logo-ms-cloud-native.png","key":"cnteam"}],"frontMatter":{"date":"2023-09-21T09:00","slug":"how-digital-natives-leverage-generative-ai","title":"1-4. How Digital Natives leverage Generative AI","authors":["cnteam"],"draft":false,"hide_table_of_contents":false,"toc_min_heading_level":2,"toc_max_heading_level":3,"keywords":["Cloud","Data","AI","AI/ML","intelligent apps","cloud-native","30-days","enterprise apps","digital experiences","app modernization","serverless","ai apps","data"],"image":"https://azure.github.io/Cloud-Native/img/ogImage.png","description":"Article Description: This article explores how digital natives and Independent Software Vendors (ISVs) can transform their business operations and generate higher value for customers with generative AI and intelligent apps.","tags":["Fall-For-IA","30-days-of-IA","learn-live","hack-together","community-buzz","ask-the-expert","azure-kubernetes-service","azure-functions","azure-openai","azure-container-apps","azure-cosmos-db","github-copilot","github-codespaces","github-actions"]},"prevItem":{"title":"2-2. Build Your First Intelligent App with Azure AI and AKS-2","permalink":"/Cloud-Native/30daysofIA/build-your-first-intelligent-app-with-azure-ai-and-aks-2"},"nextItem":{"title":"1-3. Reimagine App Development with AI","permalink":"/Cloud-Native/30daysofIA/reimagine-app-development-with-ai"}},"content":"\\n\\n\\n\\n\\n\\n \\n \\n \\n \\n \\n \\n \\n \\n\\n\\n\x3c!-- End METADATA --\x3e\\nExplore how digital natives and Independent Software Vendors (ISVs) can transform their business operations and generate higher value for customers with generative AI and intelligent apps.\\n\\n## What We\'ll Cover:\\n\\n * Potential of Generative AI in application development\\n * Challenges with Generative AI\\n * Integrating Generative AI with business operations\\n\\n![image of generative AI and intelligent apps used in business](../../static/img/fallforia/blogs/2023-09-21/blog-image-1.png)\\n\\n## Unleashing Business Transformation: How Digital Natives Can Leverage Generative AI\\n\\nGenerative AI is no longer a technological flight of fancy. It\u2019s here, and it\u2019s rewriting the rules of business innovation.\u202f \\n\\nThis type of AI leverages machine learning algorithms to create new data with similar characteristics to a given dataset. It goes beyond text generation using generative pretrained transformer (GPT) models, covering an array of applications that span from image synthesis to translations and more. It also powers a new class of intelligent applications, which combine AI capabilities with traditional software functions for enhanced user experiences and advanced business solutions.\u202f \\n\\nGenerative AI is transforming how digital natives and independent software vendors (ISVs) operate. Generative AI offers new possibilities for creating unique business value, enhancing customer experiences, and driving growth.\u202f \\n\\nIt plays a critical role in applications, improving functionality, aiding decision-making, and automating complex tasks to deliver dynamic, uniquely personalized user experiences. Generative AI also enables and automates data-driven outcomes, functions, and features.\\n\\nIntegrating AI capabilities into customer-facing apps and turning them into intelligent apps benefits both the customer and the business, as we discussed in [Demystifying Intelligent Applications: Leveraging AI in App Development](https://azure.github.io/Cloud-Native/30daysofIA/demystifying-intelligent-applications). You get faster product development workflows and better business intelligence for sales and marketing, while your users get more natural and context-aware features and interactions.\u202f \\n\\nThat said, to realize generative AI\u2019s potential, you need some strategy and knowledge of how and where it fits into your current business and operations. We\u2019ll examine this technology\u2019s potential to transform business operations, its risks, and rewards, and how to integrate it into your existing operations.\u202f \\n\\n## Unpacking the Potential of Generative AI\\n\\nGenerative AI is creating new opportunities in the digital playing field. It empowers businesses to generate insights, design products, and automate processes in ways and at speeds that were previously unimaginable. You can now predict customer behavior more accurately, offer highly personalized user experiences, and prototype innovative ideas rapidly.\u202f \\n\\nFor example, Microsoft used generative AI to reinvent web search in the new [AI-powered Bing and Edge](https://blogs.microsoft.com/blog/2023/02/07/reinventing-search-with-a-new-ai-powered-microsoft-bing-and-edge-your-copilot-for-the-web/). The search experience has evolved from a solitary user flow of typing in keywords and scrolling through results. It\u2019s now a conversational experience where Bing uses context to offer better quality and more personalized search queries and results.\u202f \\n\\nSimilarly, Microsoft harnessed generative AI to transform its productivity suite into intelligent apps. Through [Copilot for Microsoft 365](https://blogs.microsoft.com/blog/2023/03/16/introducing-microsoft-365-copilot-your-copilot-for-work/), PowerPoint now empowers users to tell their stories by using DALL-E to generate images effortlessly. It also helps rephrase text-heavy slides to be more straightforward. Outlook now assists users to compose more effective emails, enabling better communication.\\n\\nThese are just some examples of how AI powers day-to-day work and operations.\u202f \\n\\nBusinesses that ignore generative AI miss out on untapped market opportunities, letting competitors gain an edge in the digital race. By seeking ways to rethink assumptions and reinvent existing approaches, you can effectively use generative AI to create new opportunities, open products to new use cases and markets, unlock new growth and customer satisfaction levels, and increase your revenue.\\n\\n:::info\\nRegister for the intelligent apps webinar on [Driving Business Value by Modernizing with Cloud-Native & AI](https://info.microsoft.com/ww-landing-driving-business-value-by-modernizing-with-cloud-native-and-ai.html?lcid=en-us?WT.mc_id=javascript-99907-ninarasi) with *Microsoft* and *Forrester* on **September 26**.\\n\\nExplore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries.\u202f \\n:::\\n\\n## The Crucial Role of Generative AI in Business Operations\\n\\nGenerative AI can be a powerful catalyst for business transformation. Let\u2019s quickly tour some ways it can redefine business operations.\u202f\\n\\nProduct development is traditionally a long and painstaking process. Now, tools like [GitHub Copilot](https://github.com/features/copilot) and [TestPilot](https://githubnext.com/projects/testpilot/) use generative AI to make coding and testing software easier, faster, and more robust.\u202f \\n\\nSimilarly, machine learning as a service technology, such as [Azure Machine Learning](https://azure.microsoft.com/en-us/products/machine-learning?WT.mc_id=javascript-99907-ninarasi), democratizes the availability of machine learning. It helps collect, clean, and train models across datasets, making engineering and analyzing data more efficient and accessible.\u202f\u202f \\n\\nGenerative AI also simplifies the process of creating intelligent apps. Previously, simulating thousands of prototypes, predicting their performance, and narrowing down to the most viable options could take weeks, months, or even years of iteration and data analysis. With generative AI, this process can be as quick as days, hours, or perhaps minutes.\u202f \\n\\nCustomer experience is also ripe for transformation. Personalized recommendations drive engagement and sales, and generative AI can scale that personalization to every customer interaction using comprehensive and continually updated data. For instance, Microsoft leverages AI to enable personalization in [Dynamics 365 Customer Insights](https://dynamics.microsoft.com/en-us/ai/customer-insights/). Using this tool, you can ask questions about your customers in natural language and gain valuable and actionable insights into your customer base.\u202f \\n\\nLastly, marketing and sales can benefit from generative AI. For example, identifying current and future key markets and emerging trends helps companies stay ahead of the curve. Tools like [Copilot in Power BI](https://powerbi.microsoft.com/en-us/blog/introducing-microsoft-fabric-and-copilot-in-microsoft-power-bi/) help you quickly understand and uncover insights about your business, enabling your team to craft targeted campaigns, create engaging content, and optimize your business strategy.\u202f \\n\\nLeveraging this full generative AI potential requires the right tools. GPT-4, for instance, can be instrumental in realizing effective business transformation. It excels in understanding context, generating human-like text, and learning from minimal data input, making it a formidable tool in any digital native\'s arsenal. Platforms like [Azure OpenAI Service](https://azure.microsoft.com/en-us/products/ai-services/openai-service?WT.mc_id=javascript-99907-ninarasi) bring GPT-4\u2019s power to your fingertips.\\n\\n## Navigating Challenges in the Generative AI Landscape\\n\\nHowever, as with any powerful technology, it\u2019s unwise to treat generative AI as a perfect solution. You must know the challenges and risks of not leveraging it effectively, including technical and human-factor considerations.\u202f \\n\\nWithout generative AI, your company risks missing the opportunity to please customers via personalization, as discussed previously. You\u2019ll miss sales opportunities via business intelligence insights, limiting your ability to grow revenue and keep up with the competition.\u202f \\n\\nOne challenge of leveraging generative AI\u2019s full potential is finding the right tools. Fortunately, you can implement generative AI in your organization without investing in infrastructure, computational power, and expertise. Platforms like [Azure OpenAI Service](https://azure.microsoft.com/en-us/products/ai-services/openai-service?WT.mc_id=javascript-99907-ninarasi) and [Azure AI Services](https://azure.microsoft.com/en-us/solutions/ai?WT.mc_id=javascript-99907-ninarasi) make AI easily accessible. They also work with Azure compute and data services like\u202f[Azure Functions](https://azure.microsoft.com/en-us/products/functions?WT.mc_id=javascript-99907-ninarasi) and [Azure Kubernetes Service](https://azure.microsoft.com/en-us/products/kubernetes-service/?WT.mc_id=javascript-99907-ninarasi) (AKS), enabling you to build intelligent apps easily and quickly.\u202f \\n\\nHowever, generative AI can\u2019t do everything. It may need human expertise and intervention to use it fully.\u202f \\n\\nFor example, generative AI can create unique data. However, the data may have limited diversity and complexity. The AI may need help recognizing additional considerations or requirements specific to a problem. It may also lack context due to various factors, such as the information\u2019s freshness. Also, since it\u2019s an emerging field that\u2019s [potentially worth $4.4 trillion](https://www.mckinsey.com/capabilities/mckinsey-digital/our-insights/the-economic-potential-of-generative-ai-the-next-productivity-frontier#key-insights), we\u2019re still learning about the nuances and [potential regulatory impact](https://techcrunch.com/2023/01/27/the-current-legal-cases-against-generative-ai-are-just-the-beginning/) of generative AI.\u202f \\n\\nPerhaps the most crucial and challenging aspect of adopting generative AI is an organization\u2019s required cultural shift. Your teams must learn how to use AI as your company fosters an understanding of AI\u2019s potential and limitations.\u202f \\n\\nBegin integrating AI literacy into your company\u2019s DNA. Knowledge of its potential and the readily available tools can inspire your teams to incorporate generative AI into their daily workflows and business operations quickly, improve productivity, and deliver superior customer experiences.\u202f \\n\\nNo company wants to be outpaced by its competitors. You can balance generative AI\u2019s potential with your business operations by staying aware of and adapting to the dynamic AI landscape.\\n\\n## Achieving Harmony: Integrating Generative AI with Existing Business Operations\\n\\nIntegrating generative AI into existing business operations doesn\u2019t have to be disruptive. With the correct strategy, you can do so seamlessly. Your goal should be to augment and enhance your operations, not disrupt and replace.\u202f \\n\\nFor example, Microsoft Cloud technologies leveraged AI to [relieve healthcare organizations\u2019 challenges](https://www.microsoft.com/en-us/industry/blog/healthcare/2023/04/12/microsoft-cloud-for-healthcare-empowering-healthcare-to-deliver-meaningful-outcomes/?WT.mc_id=javascript-99907-ninarasi), such as talent shortage and workforce burnout. Providers automated their workflows, invested in specific applications and connectors, and enhanced support for data and operations.\u202f \\n\\nAzure AI Services, such as [text analytics for health](https://learn.microsoft.com/en-us/azure/ai-services/language-service/text-analytics-for-health/overview?tabs=ner?WT.mc_id=javascript-99907-ninarasi), enable healthcare organizations to understand their largely unstructured and untapped medical data. [Project Health Insights](https://azure.microsoft.com/en-us/blog/announcing-project-health-insights-preview-advancing-ai-for-health-data/?WT.mc_id=javascript-99907-ninarasi) leans on AI to help healthcare professionals gain actionable insights and inferences from patient data.\u202f \\n\\n[Azure Health Bot](https://techcommunity.microsoft.com/t5/healthcare-and-life-sciences/extending-azure-health-bot-with-azure-openai-service/ba-p/3792560), an Intelligent App, empowers organizations to build and deploy legally compliant, powerful conversational healthcare experiences to patients.\u202f \\n\\nA successful AI integration like this begins with understanding. Knowing what generative AI can do and setting realistic expectations can help you identify where and how AI can best serve your operations.\u202f \\n\\nNext, you can foster a culture of AI literacy across teams, speeding up adoption by helping them understand how it can aid their work. The more familiar your team is with AI and its possibilities, the smoother the transition. Training and education programs play a pivotal role here.\u202f \\n\\nFinally, integration requires a systematic approach. It\u2019s not about overhauling everything at once \u2014 it\'s about identifying areas where AI can add the most value, then gradually experimenting and scaling its use. Successful integration stories often feature a phased approach, starting small and growing steadily.\u202f \\n\\nAs we move toward a future where generative AI is a staple in business operations, remember that every journey is unique. Although there\u2019s no one-size-fits-all solution, a strategic approach and a keen understanding of your business needs will help you navigate this new landscape successfully. \\n\\n## Conclusion\\n\\nGenerative AI offers plenty of opportunities for businesses that embrace it. It\u2019s fueling the next generation of intelligent applications, transforming everything from product development to customer engagement to marketing. This technology is the key to unlocking unprecedented growth and innovation.\u202f \\n\\nWhile the journey may have challenges, the potential rewards make generative AI worth exploring. As generative AI advances, [Azure AI](https://azure.microsoft.com/en-us/products/cognitive-services/#overview?WT.mc_id=javascript-99907-ninarasi)\u2019s broad range of tools and services will help you navigate this fascinating field.\\n\\n## Exercise\\n\\n * Watch **[Episode 02](https://aka.ms/learnlive-contoso-app-deconstructed-Ep2)** of the serverless edition Learn Live session to learn how to build, test and deploy an end-to-end intelligent app solution.\\n * Complete the **[Cloud Skills Challenge](https://aka.ms/fallforIA/ai-csc)** to build on your AI skills.\\n * Register for **[Ask the Expert: Azure Functions](https://aka.ms/FallForIA/ATE)** on September 26 for a live Q&A with the Product Team on how to build AI apps using serverless computing."},{"id":"reimagine-app-development-with-ai","metadata":{"permalink":"/Cloud-Native/30daysofIA/reimagine-app-development-with-ai","source":"@site/blog-30daysofIA/2023-09-20/reimagine-app-development-with-ai.md","title":"1-3. Reimagine App Development with AI","description":"In the part of our series on intelligent apps, we\u2019ll explore how AI is transforming application development, from design and architecture to building.","date":"2023-09-20T09:00:00.000Z","formattedDate":"September 20, 2023","tags":[{"label":"Fall-For-IA","permalink":"/Cloud-Native/30daysofIA/tags/fall-for-ia"},{"label":"30-days-of-IA","permalink":"/Cloud-Native/30daysofIA/tags/30-days-of-ia"},{"label":"learn-live","permalink":"/Cloud-Native/30daysofIA/tags/learn-live"},{"label":"hack-together","permalink":"/Cloud-Native/30daysofIA/tags/hack-together"},{"label":"community-buzz","permalink":"/Cloud-Native/30daysofIA/tags/community-buzz"},{"label":"ask-the-expert","permalink":"/Cloud-Native/30daysofIA/tags/ask-the-expert"},{"label":"azure-kubernetes-service","permalink":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service"},{"label":"azure-functions","permalink":"/Cloud-Native/30daysofIA/tags/azure-functions"},{"label":"azure-openai","permalink":"/Cloud-Native/30daysofIA/tags/azure-openai"},{"label":"azure-container-apps","permalink":"/Cloud-Native/30daysofIA/tags/azure-container-apps"},{"label":"azure-cosmos-db","permalink":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db"},{"label":"github-copilot","permalink":"/Cloud-Native/30daysofIA/tags/github-copilot"},{"label":"github-codespaces","permalink":"/Cloud-Native/30daysofIA/tags/github-codespaces"},{"label":"github-actions","permalink":"/Cloud-Native/30daysofIA/tags/github-actions"}],"readingTime":8.57,"hasTruncateMarker":false,"authors":[{"name":"It\'s 30DaysOfIA","title":"FallForIA Content Team","url":"https://azure.github.io/Cloud-Native/Fall-For-IA/","imageURL":"https://azure.github.io/Cloud-Native/img/logo-ms-cloud-native.png","key":"cnteam"}],"frontMatter":{"date":"2023-09-20T09:00","slug":"reimagine-app-development-with-ai","title":"1-3. Reimagine App Development with AI","authors":["cnteam"],"draft":false,"hide_table_of_contents":false,"toc_min_heading_level":2,"toc_max_heading_level":3,"keywords":["Cloud","Data","AI","AI/ML","intelligent apps","cloud-native","30-days","enterprise apps","digital experiences","app modernization","serverless","ai apps","data"],"image":"https://azure.github.io/Cloud-Native/img/ogImage.png","description":"In the part of our series on intelligent apps, we\u2019ll explore how AI is transforming application development, from design and architecture to building.","tags":["Fall-For-IA","30-days-of-IA","learn-live","hack-together","community-buzz","ask-the-expert","azure-kubernetes-service","azure-functions","azure-openai","azure-container-apps","azure-cosmos-db","github-copilot","github-codespaces","github-actions"]},"prevItem":{"title":"1-4. How Digital Natives leverage Generative AI","permalink":"/Cloud-Native/30daysofIA/how-digital-natives-leverage-generative-ai"},"nextItem":{"title":"1-2. Harnessing the Power of Intelligent Apps","permalink":"/Cloud-Native/30daysofIA/harnessing-the-power-of-intelligent-apps"}},"content":"\\n\\n\\n\\n\\n\\n \\n \\n \\n \\n \\n \\n \\n \\n\\n\\n\x3c!-- End METADATA --\x3e\\nIn the part of our series on intelligent apps, we\u2019ll explore how AI is transforming application development, from design and architecture to building.\\n\\n## What We\'ll Cover:\\n\\n * Revolutionizing application development using AI\\n * Infusing AI in application design architecture\\n * AI Assisted Pair Programming\\n * Future of App Development\\n\\n![A decorative image of application development with AI](../../static/img/fallforia/blogs/2023-09-20/blog-image-1.png)\\n\\n## Reimagining Application Development With AI: A New Paradigm\\n\\nArtificial intelligence (AI) is impacting every industry. AI-powered development tools offer new functionalities and assist software developers throughout the development process, revolutionizing app development. From initial design and architecture to coding and debugging, AI reshapes how we approach application conception, creation, and iteration.\\n\\nIn part one of this series, \u201c[Demystifying Intelligent Applications: Leveraging AI in App Development](https://azure.github.io/Cloud-Native/30daysofIA/demystifying-intelligent-applications),\u201d we explored the new breed of intelligent apps and how they use AI. In this article, we\u2019ll see how AI technologies enhance developer toolkits and shape the future of app development, particularly through AI-enabled coding assistance and debugging.\\n\\n## The Dawn of Intelligent Apps: Revolutionizing Application Development\\n\\nAI is more than just another addition to the development toolbox\u2014it represents a revolution bringing unprecedented efficiency, innovation, and user engagement to our applications.\u202f \\n\\nWhat sets intelligent apps apart is how they can adapt and learn, helping us deliver unique, real-time user experiences.\u202f\u202f \\n\\nOne example is an AI-driven health application that can track biometric data, predict potential health issues, provide personalized health advice, and even guide emergency services during a medical crisis. An AI-enabled language learning app that adapts to our learning speeds, recognizes our problem areas, and customizes its content to help us improve our language proficiencies is another example.\u202f\u202f \\n\\nIntelligent apps like these don\u2019t just use data for recommendations\u2014they can make critical decisions and provide sophisticated services that were previously impossible.\u202f \\n\\nFor developers, intelligent apps mean we\u2019re no longer just coding\u2014we\u2019re teaching applications how to think, adapt, and make decisions. AI is reshaping every step of the development journey, helping us to build more intuitive and user-focused products.\u202f \\n\\nNext, we\u2019ll examine the impact of AI on application design and architecture and how AI-assisted \u201cpair programming\u201d changes software development.\\n\\n:::info\\n**Register** for the *FREE* [webinar on Intelligent Apps](https://info.microsoft.com/ww-landing-driving-business-value-by-modernizing-with-cloud-native-and-ai.html?lcid=en-us&WT.mc_id=javascript-99907-ninarasi) with Microsoft and Forrester on September 26 to explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries. \\n\\nThere will be a showcase of real-world use cases that demonstrate how AI can be seamlessly integrated into cloud-native environments driving tangible business value.\\n:::\\n\\n## AI in Design and Architecture: Fueling Creativity and Efficiency\\n\\nAI technologies help developers automate and streamline their processes and provide new ways to think about design.\u202f\u202f \\n\\nIn traditional application development, the design and architecture phase can take significant time, effort, and expertise. For instance, adopting a monolithic architecture or a microservices-based design can significantly increase coding, testing, and debugging complexity. The app may even behave differently when hosted locally than in a deployed environment. It may take hours to track down an issue manually\u2014time developers could spend elsewhere.\u202f \\n\\nConversely, we can generate numerous design iterations in seconds with AI, each optimized to fulfill specific user needs and business goals. AI tools can quickly model various scenarios\' impacts and software design patterns in the architectural design phase. It provides insights into performance and potential issues and suggests improvements. AI can also generate skeleton code, allowing developers to prototype different solutions or test specific components and technologies rapidly. By reviewing thousands of lines of code in seconds, AI allows us to focus on creating apps that serve users through feature development instead of searching for bugs.\u202f\u202f \\n\\nIntegrating AI into the design and architecture phase can even extend our creative capabilities. By leveraging the power of generative AI\u2014like large language models (LLMs)\u2014and their broad knowledge and context going beyond software, we can better brainstorm apps and feature ideas. We can go beyond code and software architecture and get ideas about user experience, logo and branding, or marketing concepts. And as AI tools deliver insights and ideas we would have never considered; it encourages more innovation.\\n\\n:::info\\nWatch [Episode 01](https://aka.ms/learnlive-contoso-app-deconstructed-Ep1) of the Learn Live session to learn how to build an end-to-end intelligent app solution architecture.\\n:::\\n\\n## The Power of AI-Assisted Pair Programming in Building Applications\\n\\nAI-assisted pair programming is a collaborative coding approach where an AI system\u2014like [GitHub Copilot](https://github.com/features/copilot) or [TestPilot](https://githubnext.com/projects/testpilot/)\u2014assists developers during coding. It\u2019s an increasingly common approach that significantly impacts developer productivity. In fact, [GitHub Copilot](https://github.com/features/copilot) is now behind an [average of 46 percent of developers\u2019 code](https://github.blog/2023-02-14-github-copilot-now-has-a-better-ai-model-and-new-capabilities/) and users are seeing [55 percent faster task completion](https://github.blog/2022-09-07-research-quantifying-github-copilots-impact-on-developer-productivity-and-happiness/) on average.\u202f \\n\\nFor new software developers, or those interested in learning new skills, AI-assisted pair programming are training wheels for coding. With the benefits of code snippet suggestions, developers can avoid struggling with beginner pitfalls like language syntax. Tools like ChatGPT can act as a personal, on-demand tutor\u2014answering questions, generating code samples, and explaining complex code syntax and logic. These tools dramatically speed the learning process and help developers gain confidence in their coding abilities.\u202f \\n\\nBuilding applications with AI tools hastens development and provides more robust code. For example, GitHub Copilot helps us write new code accurately. It also quickly makes sense of existing code, suggests whole new lines or blocks of code, and even detects errors and proposes fixes. It\u2019s like having a second coder working alongside us\u2014one who never tires and is trained on billions of lines of code.\u202f \\n\\nHowever, it\u2019s also important to acknowledge the limitations of AI. While generative AI-assistance technologies like GitHub Copilot are impressive, they\u2019re still imperfect. They can sometimes produce erroneous, non-functional, or vulnerable code. They can also lack nuanced, domain-specific knowledge.\u202f\u202f \\n\\nSo, although these tools are helpful, we can\u2019t rely on them fully\u2014these are programming aids rather than definitive solutions. AI-assisted pair programming is about augmenting the human developer\u2019s skills, not replacing them.\\n\\n## Debugging and Improving Code With AI: Enhancing Application Quality\\n\\nIdentifying and correcting code errors is often time-consuming and challenging for developers. AI streamlines the debugging process by automatically detecting anomalies, suggesting fixes, and learning from previous bugs to prevent them from recurring.\u202f \\n\\nWe can use generative AI to create unit tests from code snippets, from simple parameter checking and input validation to complex mocks of database and network services. Automated AI testing improves code coverage and robustness. It saves significant time compared to manually crafting unit tests.\u202f \\n\\nAI also revolutionizes code reviews by scanning and highlighting potential issues\u2014like code patterns leading to a bug or a coding standards violation\u2014and suggesting ways to simplify or refactor the code for readability and performance. This reduces human error and makes the code review process more efficient. Its benefits extend to small teams, where the availability of developers is crucial to deadlines, and large teams, where effective collaboration depends on readability and code consistency. AI further saves time by triaging issues so developers can prioritize the most mission-critical ones.\u202f \\n\\nAside from improving code quality, AI is critical to understanding system behavior. Intelligent AI-assisted software development tools can monitor and audit system behavior, giving developers a better insight into how their code performs in different scenarios. AI can track patterns, identify inefficiencies, and provide alerts when they detect unusual behavior\u2014enabling developers to enhance their code further.\u202f \\n\\nBy predicting and identifying potential vulnerabilities, intelligent AI-assisted coding apps like [Microsoft Security Copilot](https://www.microsoft.com/en-us/security/business/ai-machine-learning/microsoft-security-copilot?WT.mc_id=javascript-99907-ninarasi) are pivotal in ensuring application functionality, reliability, and security. These AI tools are continually trained and upgraded with large codebases and user feedback that refines their responses. This ability to learn from past mistakes makes AI an incredibly powerful tool that will continue to expand its capabilities in the future.\\n\\n:::info\\nComplete the [AI Cloud Skills Challenge](https://aka.ms/FallForIA/ai-csc) to build on you AI application development skills with paired programming using generative AI and GitHub Copilot.\\n:::\\n\\n## AI and the Future of Application Development\\n\\nThe rapid advancements in AI and machine learning technologies point to an exciting future for application development.\u202f \\n\\nIn \u201c[The Future of Applications](https://mikecann.co.uk/posts/the-future-of-applications),\u201d software developer Mike Cann illustrates how LLMs might revolutionize application development. Cann that suggests fully-autonomous coding\u2014AI-generated applications created entirely from prompts\u2014may supersede traditional hand-crafted applications. Dynamically generated user interfaces could offer personalized and customizable experiences while we can design APIs for machine consumption and efficiency rather than human readability. Furthermore, we can store data in less structured, more adaptable, and flexible forms.\u202f\\n\\nAI is already making application development more accessible. With AI handling the complex, technical aspects of development, less-technical individuals can take a greater role in building applications. Consequently, AI can potentially democratize software engineering.\u202f \\n\\nIntelligent apps will continue to evolve, becoming more efficient, adaptable, and capable of learning from user interactions. Similarly, AI will inevitably become integral in every stage of app development.\\n\\n## Summary\\n\\nAI has already created a new development paradigm\u2014one in which machine intelligence and human creativity come together to produce innovative, efficient, and robust applications. AI already assists us at every stage of app development, from design and architecture to coding and debugging.\u202f \\n\\nDevelopers who embrace AI tools and integrate them into their processes can enhance their productivity, improve the quality of their applications, and expand the possibilities of their application development.\u202f\u202f \\n\\nThe future of development is automated and AI-driven, and it\u2019s here today. Getting started is easy\u2014try an AI-powered development tool like GitHub Copilot to see how AI-assisted pair programming can enhance your productivity and code quality.\\n\\n## Exercise\\n\\n * **Watch** the \u201c[Develop and Deconstruct](https://aka.ms/learnlive-contoso-app-deconstructed-Ep2)\u201d episode of our Learn Live series, a live guided experience on building end-to-end intelligent apps architecture with SMEs from Microsoft.\\n * **Complete** the [Fall For Intelligent Apps Skills Challenges](https://aka.ms/FallForIA/csc) \u2013 ends on October 31!"},{"id":"harnessing-the-power-of-intelligent-apps","metadata":{"permalink":"/Cloud-Native/30daysofIA/harnessing-the-power-of-intelligent-apps","source":"@site/blog-30daysofIA/2023-09-19/harnessing-the-power-of-intelligent-apps.md","title":"1-2. Harnessing the Power of Intelligent Apps","description":"This article explores real-world examples of how Intelligent Apps revolutionize logistics, retail, finance, manufacturing, tech, and medicine.","date":"2023-09-19T09:00:00.000Z","formattedDate":"September 19, 2023","tags":[{"label":"Fall-For-IA","permalink":"/Cloud-Native/30daysofIA/tags/fall-for-ia"},{"label":"30-days-of-IA","permalink":"/Cloud-Native/30daysofIA/tags/30-days-of-ia"},{"label":"learn-live","permalink":"/Cloud-Native/30daysofIA/tags/learn-live"},{"label":"hack-together","permalink":"/Cloud-Native/30daysofIA/tags/hack-together"},{"label":"community-buzz","permalink":"/Cloud-Native/30daysofIA/tags/community-buzz"},{"label":"ask-the-expert","permalink":"/Cloud-Native/30daysofIA/tags/ask-the-expert"},{"label":"azure-kubernetes-service","permalink":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service"},{"label":"azure-functions","permalink":"/Cloud-Native/30daysofIA/tags/azure-functions"},{"label":"azure-openai","permalink":"/Cloud-Native/30daysofIA/tags/azure-openai"},{"label":"azure-container-apps","permalink":"/Cloud-Native/30daysofIA/tags/azure-container-apps"},{"label":"azure-cosmos-db","permalink":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db"},{"label":"github-copilot","permalink":"/Cloud-Native/30daysofIA/tags/github-copilot"},{"label":"github-codespaces","permalink":"/Cloud-Native/30daysofIA/tags/github-codespaces"},{"label":"github-actions","permalink":"/Cloud-Native/30daysofIA/tags/github-actions"}],"readingTime":9.945,"hasTruncateMarker":false,"authors":[{"name":"It\'s 30DaysOfIA","title":"FallForIA Content Team","url":"https://azure.github.io/Cloud-Native/Fall-For-IA/","imageURL":"https://azure.github.io/Cloud-Native/img/logo-ms-cloud-native.png","key":"cnteam"}],"frontMatter":{"date":"2023-09-19T09:00","slug":"harnessing-the-power-of-intelligent-apps","title":"1-2. Harnessing the Power of Intelligent Apps","authors":["cnteam"],"draft":false,"hide_table_of_contents":false,"toc_min_heading_level":2,"toc_max_heading_level":3,"keywords":["Cloud","Data","AI","AI/ML","intelligent apps","cloud-native","30-days","enterprise apps","digital experiences","app modernization","serverless","ai apps","data"],"image":"https://azure.github.io/Cloud-Native/img/ogImage.png","description":"This article explores real-world examples of how Intelligent Apps revolutionize logistics, retail, finance, manufacturing, tech, and medicine.","tags":["Fall-For-IA","30-days-of-IA","learn-live","hack-together","community-buzz","ask-the-expert","azure-kubernetes-service","azure-functions","azure-openai","azure-container-apps","azure-cosmos-db","github-copilot","github-codespaces","github-actions"]},"prevItem":{"title":"1-3. Reimagine App Development with AI","permalink":"/Cloud-Native/30daysofIA/reimagine-app-development-with-ai"},"nextItem":{"title":"1-1. Demystifying Intelligent Applications","permalink":"/Cloud-Native/30daysofIA/demystifying-intelligent-applications"}},"content":"\\n\\n\\n\\n\\n\\n \\n \\n \\n \\n \\n \\n \\n \\n\\n\\n\x3c!-- End METADATA --\x3e\\n\\nExplore real-world examples of how **Intelligent Apps** revolutionize *logistics*, *retail*, *finance*, *manufacturing*, *tech*, and *medicine*.\\n\\n## What We\'ll Cover:\\n\\n * Real-world businesses revolutionizing operations with Intelligent Apps\\n * High level architecture for industry scenarios\\n * Implementation architecture overview across industries\\n\\n![A decorative header image for the blog](../../static/img/fallforia/blogs/2023-09-19/blog-image-1.png)\\n\\n## Real-world Success Stories\\n\\nAccording to IBM\u2019s 2022 [Global AI Adoption Index](https://www.ibm.com/downloads/cas/GVAGA3JP?WT.mc_id=javascript-99907-ninarasi), 35% of surveyed companies already use AI in their business, and 42% are exploring the idea. Organizations lagging behind the AI curve will quickly fall behind early adopters, who are already enjoying its benefits and boosting their investment in this technology.\\n\\nIn the first part of this series, [\u201cDemystifying Intelligent Applications: Leveraging AI in App Development,\u201d](https://azure.github.io/Cloud-Native/30daysofIA/demystifying-intelligent-applications) we explored how organizations can use AI-powered apps, or intelligent apps, to transform their operations, decision-making, and customer experiences, leaping ahead of the competition.\\n\\nIntelligent apps significantly improve operational efficiency and customer engagement across industries like logistics, retail, finance, manufacturing, tech, and healthcare. In this article, we\u2019ll spotlight real-world success stories of businesses revolutionizing operations with intelligent apps using Microsoft Azure\u2019s app, data, and AI services.\\n\\n:::info\\n**Register** for the [Learn Live](https://aka.ms/contoso-real-estate/learn-live?WT.mc_id=javascript-99907-ninarasi) on **September 21** for a live guided experience with SMEs on how to build end-to-end solution architecture for real world implementation of Intelligent Apps.\\n:::\\n\\n## Streamlining Operations: Intelligent Apps in the Airline Industry\\n\\nAerospace pioneer Airbus [leveraged intelligent apps to streamline its operations and innovate its services](https://customers.microsoft.com/en-us/story/858578-airbus-defense-and-intelligence-azure?WT.mc_id=javascript-99907-ninarasi) with two ground-breaking modernizations that use Azure apps, data, and AI services to reimagine pilot training and predict aircraft maintenance issues.\u202f \\n\\nAviation technology\u2019s rapid evolution compelled Airbus to reconceive their approach to training pilots. The aircraft manufacturer created a pilot training chatbot by combining [Azure AI Services](https://azure.microsoft.com/en-us/products/ai-services?WT.mc_id=javascript-99907-ninarasi) with [Speech to Text](https://azure.microsoft.com/en-us/products/ai-services/speech-to-text?WT.mc_id=javascript-99907-ninarasi) and [Text to Speech](https://azure.microsoft.com/en-us/products/ai-services/text-to-speech?WT.mc_id=javascript-99907-ninarasi). The bot helps trainee pilots navigate over 6,000 pages of technical information. Pilots can ask questions verbally for quick answers, aiding their understanding of dense information and speeding up training. And when Airbus needed to accommodate its military consumers, deploying its Kubernetes cluster to a disconnected, \u201cair-gapped\u201d environment met the strict security requirements of military aircraft training.\\n\\nA chatbot could look like the following diagram as Azure services work together to create a conversational experience. Its components could include security and governance, bot logic and user experience (UX), bot cognition and intelligence, data extract, transfer, and load (ETL) operations, quality assurance and enhancements, logging, and monitoring and reporting.\\n\\n![A diagram of a conversational experience enabled by Azure services](../../static/img/fallforia/blogs/2023-09-19/blog-image-2.png)\\n\\nAirbus also used AI to improve its aircraft maintenance and safety. They deployed [AI Anomaly Detector](https://azure.microsoft.com/en-us/products/ai-services/ai-anomaly-detector?WT.mc_id=javascript-99907-ninarasi) to gather and analyze telemetry data from multiple flights, enabling more profound insights into wear and tear on military aircraft operating in harsh conditions. This AI application helps Airbus better understand each airplane\u2019s health. Airbus can predict and fix potential problems before they occur, improving the aircraft\u2019s safety and operational readiness.\\n\\nAI and Azure are changing how organizations like Airbus operate, even in strict, highly regulated markets with stringent security requirements. And by streamlining pilot training and staying ahead of aircraft maintenance, Airbus helps its military and commercial clients reliably deliver their goods across the globe, maintaining their critical supply chains.\\n\\n## Boosting Customer Service: Intelligent Apps in the Retail Industry\\n\\nThe retail industry has been among the first to adopt and benefit from intelligent apps. For example, the leading American used car retailer, CarMax, [uses intelligent apps to transform the car shopping experience](https://customers.microsoft.com/en-us/story/1501304071775762777-carmax-retailer-azure-openai-service?WT.mc_id=javascript-99907-ninarasi).\\n\\nBefore embracing AI, CarMax faced a significant challenge: Manually creating text summaries was time-consuming and labor-intensive. CarMax decided to leverage [Azure OpenAI Service](https://azure.microsoft.com/en-us/products/ai-services/openai-service?WT.mc_id=javascript-99907-ninarasi) to automatically generate car research pages, offering customers valuable insights while enhancing the website\u2019s search engine rankings.\\n\\nThis AI-driven approach has significantly streamlined the process, achieving in a few months what would have taken years manually. Also, the AI system condenses thousands of customer reviews into a few readable sentences, offering potential buyers a quick overview of others\u2019 experiences.\\n\\nCarMax has improved their customer experience while boosting operational efficiency through AI adoption. CarMax\u2019s editorial staff focuses on creating strategic, longer-form content with the time saved, enhancing productivity, and driving more website traffic.\\n\\nCarMax\u2019s success story serves as a testament to the potential of intelligent apps in the retail industry.\\n\\n## Enhancing Cybersecurity: Intelligent Apps in Finance\\n\\nIntelligent apps also impact the highly regulated financial services sector. Swift, a leading infrastructure provider for financial messaging services, [uses intelligent apps to enhance decision-making and combat financial crime](https://customers.microsoft.com/en-us/story/1637929534319366070-swift-banking-capital-markets-azure-machine-learning?WT.mc_id=javascript-99907-ninarasi).\\n\\nThe massive growth of cross-border transactions and instant payment networks led to a surge in financial fraud that can cost the industry hundreds of billions of dollars every year. So, Swift and Microsoft partnered to build an anomaly detection model using [federated learning techniques](https://techcommunity.microsoft.com/t5/ai-machine-learning-blog/federated-learning-with-azure-machine-learning-powering-privacy/ba-p/3824720) with [Azure Machine Learning](https://azure.microsoft.com/en-us/products/machine-learning/) and [Azure confidential computing](https://azure.microsoft.com/en-us/solutions/confidential-compute/).\\n\\n[Microsoft Purview](https://azure.microsoft.com/en-us/products/purview/?WT.mc_id=javascript-99907-ninarasi) helped govern data, as the federated learning method partially trained each model in a silo before aggregating them. This approach enabled Swift and Microsoft to develop a highly accurate model without copying or moving data from Swift members\u2019 secure locations, ensuring the highest level of security and privacy.\\n\\nFinancial organizations can now deploy this trained ML model to run a real-time fraud detection system, lowering the cost of financial fraud.\\n\\nOrganizations can deploy a fraud detection system like the following example on Microsoft Azure. The system ingests data from streams or storage, runs analytics, and uses ML to detect fraud and store results. It then alerts users and other systems to the security issue.\\n\\n![A diagram of an example fraud detection system](../../static/img/fallforia/blogs/2023-09-19/blog-image-3.png)\\n\\nSwift provides the innovative model to its banking partners, who can share data to further train the model. Azure confidential computing securely runs new models on current data to accumulate insights, generating an ongoing learning cycle. This iterative approach strengthens the global detection of fraudulent financial transactions through an increasingly faster and more accurate model.\u202f \\n\\nThe collaboration between Swift and Microsoft demonstrates how combining various Azure services creates Intelligent Apps to enhance decision-making in finance. These apps solve real problems at an unprecedented global scale.\u202f \\n\\n## Enhancing Efficiency: Intelligent Apps in Manufacturing\\n\\n3M, known for Post-it Notes and other innovative products, [wielded the power of Intelligent Apps to standardize, automate, and accelerate its sales forecasting](https://customers.microsoft.com/en-us/story/1504342377134122633-3M-manufacturing-azure-machine-learning?WT.mc_id=javascript-99907-ninarasi). The company needed a unified, automated approach to replace its multiple manual methods.\\n\\nIt adopted Azure Machine Learning and other Azure AI Services to develop, train, and implement 1,500 custom models for each region and division, replacing manual methods. This solution delivers timely finance, sales, and marketing insights while freeing teams from time-consuming manual data manipulation tasks.\\n\\n3M used Azure Machine Learning, [automated machine learning](https://azure.microsoft.com/en-us/products/machine-learning/automatedml/) (AutoML), and the [Many Models Solution Accelerator](https://github.com/microsoft/solution-accelerator-many-models) to train and score numerous machine learning models in parallel. This approach significantly shortened the company\u2019s development cycle time.\\n\\nThen, 3M integrated [Microsoft Power BI](https://powerbi.microsoft.com/en-us/?WT.mc_id=javascript-99907-ninarasi) into their solution to compile and visualize results more effectively. Executives now have an unbiased and unfiltered view of data for better decision-making. With its improved ability to forecast sales, 3M can focus on manufacturing the products it needs efficiently.\\n\\n3M\u2019s success demonstrates how combining various Azure services can enhance efficiency in the manufacturing industry. These apps improve operational processes while enabling employees to focus on higher-level tasks. Strategic decisions are more targeted, and business conversations are more meaningful.\\n\\n## Driving Innovation: Intelligent Apps in the Technology Industry\\n\\nAs expected, the tech industry is at the forefront of leveraging Intelligent Apps to drive innovation. Elastic [saw an opportunity to use intelligent apps to optimize its search services and meet customer needs more effectively](https://customers.microsoft.com/en-us/story/1653495116803202350-elastic-partner-professional-services-azure-openai-service?WT.mc_id=javascript-99907-ninarasi). The company built a managed, secure, and scalable solution for its customers, Elastic Cloud, by leveraging [Microsoft Azure](https://azure.microsoft.com/en-us?WT.mc_id=javascript-99907-ninarasi).\\n\\nElastic Cloud uses Azure Machine Learning and other Azure AI Services to automate many basic management tasks, such as backing up data, upgrading, and scaling deployments. This approach enables Elastic\u2019s customers to focus less on managing the software and more on serving their own clients. Customers can quickly deploy and manage Elastic Cloud on Kubernetes (ECK) using [Azure Kubernetes Service](https://azure.microsoft.com/en-us/products/kubernetes-service/?WT.mc_id=javascript-99907-ninarasi).\\n\\nAs a result, Elastic\u2019s cloud business is growing twice as fast as its on-premises solutions. Enterprise search users have reported significant improvements in system availability and employee satisfaction.\\n\\nAdditionally, Elastic incorporated Azure OpenAI Service into its Elasticsearch Relevance Engine (ESRE), providing more relevant answers to search queries. ESRE enables text, vector, and hybrid search, offering a more effective and accurate response to queries.\\n\\nElastic\u2019s success story demonstrates how combining various Azure services can create intelligent Apps to drive innovation in the tech industry. These apps help improve operational processes and deliver better results quickly.\\n\\n## Revolutionizing Healthcare: Intelligent Apps in Medicine\\n\\nIntelligent apps also drive transformative changes in the healthcare sector. Cambridgeshire and Peterborough NHS Foundation Trust, a healthcare provider in the United Kingdom, gradually shifted their on-premises systems to the cloud using Azure, improving flexibility, and lowering costs. They needed a solution for their electronic patient record (EPR) system that integrates 23 years of data across multiple systems and formats.\\n\\nThe Trust used Microsoft Azure and [Azure Cognitive Search](https://azure.microsoft.com/en-us/products/ai-services/cognitive-search?WT.mc_id=javascript-99907-ninarasi), including semantic search for understanding intent and contexts and cognitive capabilities for extracting insights from data, to [standardize and automate data management](https://customers.microsoft.com/en-us/story/1612121636839251558-cpft-health-provider-azure-en-united-kingdom?WT.mc_id=javascript-99907-ninarasi). This approach made legacy, archive, and live data more discoverable, saving clinicians\u2019 time. The Trust\u2019s solution searches through millions of clinical documents in seconds, even recognizing handwritten notes without needing a separate optical character recognition (OCR) solution to extract text from images (Azure Cognitive Search automatically does it). Clinicians can now uncover previously inaccessible insights, enabling them to offer more personalized care.\\n\\nThe Trust also used [Integration Services](https://azure.microsoft.com/en-us/products/category/integration/?WT.mc_id=javascript-99907-ninarasi) tools such as [Azure Logic Apps](https://azure.microsoft.com/en-us/products/logic-apps/?WT.mc_id=javascript-99907-ninarasi) to create workflows without writing code and [Event Grid](https://azure.microsoft.com/en-us/products/event-grid/?WT.mc_id=javascript-99907-ninarasi) to connect Azure and third-party services through a [publisher-subscriber model](https://learn.microsoft.com/en-us/azure/architecture/patterns/publisher-subscriber?WT.mc_id=javascript-99907-ninarasi). They switched on a live feed from their new EPR system to Azure to feed real-time information into other applications. Researchers can now access anonymized data to fuel medical innovations beyond the NHS.\\n\\nThe healthcare provider also invested heavily in Power BI\u2019s data visualization and analytical functions to sift through massive amounts of information. This level of data accessibility was previously impossible with their on-premises system. The new insights have significantly improved clinicians\u2019 efficiency for better patient care.\\n\\nData accessibility and security are critical in the healthcare industry. Intelligent Apps empower organizations like the Trust to revolutionize medical care. AI capabilities helped the Trust improve its operational efficiency and user experiences, setting a new standard for healthcare in the digital age.\\n\\n## Summary\\n\\nAI is driving transformation and real-world successes across industries. Intelligent apps are reshaping our world by streamlining operations in manufacturing and logistics, enhancing customer experience in the retail sector, improving decision-making in finance, driving innovation in tech, and revolutionizing healthcare.\\n\\nAs organizations adopt AI, they\u2019re innovating in ways that were once too difficult, expensive, or outright impossible. Companies that wait to embrace intelligent apps risk the chance of falling behind. By embracing intelligent apps and leveraging the capabilities of Azure, you can harness the transformative potential of AI to remain on the cutting edge of technology.\\n\\n## Exercise\\n\\n * **Complete** the [Intelligent Apps Cloud Skills Challenge](https://aka.ms/fallforIA/apps-csc) to build on your to build your fundamentals for AI app development.\\n * **Watch** [Episode 01](https://aka.ms/learnlive-contoso-app-deconstructed-Ep1)\u202fof the Serverless Edition Learn Live session to learn how to build an end-to-end intelligent app solution.\\n * **Register** for the [Intelligent Apps webinar](https://info.microsoft.com/ww-landing-driving-business-value-by-modernizing-with-cloud-native-and-ai.html?lcid=en-us?WT.mc_id=javascript-99907-ninarasi) with Microsoft and Forrester."},{"id":"demystifying-intelligent-applications","metadata":{"permalink":"/Cloud-Native/30daysofIA/demystifying-intelligent-applications","source":"@site/blog-30daysofIA/2023-09-18/demystifying-intelligent-applications.md","title":"1-1. Demystifying Intelligent Applications","description":"This article explores the concept of intelligent applications for readers, providing a clear understanding of the role of AI capabilities in modern applications.","date":"2023-09-18T09:00:00.000Z","formattedDate":"September 18, 2023","tags":[{"label":"Fall-For-IA","permalink":"/Cloud-Native/30daysofIA/tags/fall-for-ia"},{"label":"30-days-of-IA","permalink":"/Cloud-Native/30daysofIA/tags/30-days-of-ia"},{"label":"learn-live","permalink":"/Cloud-Native/30daysofIA/tags/learn-live"},{"label":"hack-together","permalink":"/Cloud-Native/30daysofIA/tags/hack-together"},{"label":"community-buzz","permalink":"/Cloud-Native/30daysofIA/tags/community-buzz"},{"label":"ask-the-expert","permalink":"/Cloud-Native/30daysofIA/tags/ask-the-expert"},{"label":"azure-kubernetes-service","permalink":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service"},{"label":"azure-functions","permalink":"/Cloud-Native/30daysofIA/tags/azure-functions"},{"label":"azure-openai","permalink":"/Cloud-Native/30daysofIA/tags/azure-openai"},{"label":"azure-container-apps","permalink":"/Cloud-Native/30daysofIA/tags/azure-container-apps"},{"label":"azure-cosmos-db","permalink":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db"},{"label":"github-copilot","permalink":"/Cloud-Native/30daysofIA/tags/github-copilot"},{"label":"github-codespaces","permalink":"/Cloud-Native/30daysofIA/tags/github-codespaces"},{"label":"github-actions","permalink":"/Cloud-Native/30daysofIA/tags/github-actions"}],"readingTime":7.25,"hasTruncateMarker":false,"authors":[{"name":"It\'s 30DaysOfIA","title":"FallForIA Content Team","url":"https://azure.github.io/Cloud-Native/Fall-For-IA/","imageURL":"https://azure.github.io/Cloud-Native/img/logo-ms-cloud-native.png","key":"cnteam"}],"frontMatter":{"date":"2023-09-18T09:00","slug":"demystifying-intelligent-applications","title":"1-1. Demystifying Intelligent Applications","authors":["cnteam"],"draft":false,"hide_table_of_contents":false,"toc_min_heading_level":2,"toc_max_heading_level":3,"keywords":["Cloud","Data","AI","AI/ML","intelligent apps","cloud-native","30-days","enterprise apps","digital experiences","app modernization","serverless","ai apps"],"image":"https://azure.github.io/Cloud-Native/img/ogImage.png","description":"This article explores the concept of intelligent applications for readers, providing a clear understanding of the role of AI capabilities in modern applications.","tags":["Fall-For-IA","30-days-of-IA","learn-live","hack-together","community-buzz","ask-the-expert","azure-kubernetes-service","azure-functions","azure-openai","azure-container-apps","azure-cosmos-db","github-copilot","github-codespaces","github-actions"]},"prevItem":{"title":"1-2. Harnessing the Power of Intelligent Apps","permalink":"/Cloud-Native/30daysofIA/harnessing-the-power-of-intelligent-apps"},"nextItem":{"title":"Kick-off #30DaysofIA \ud83c\udf42","permalink":"/Cloud-Native/30daysofIA/kick-off"}},"content":"\\n\\n\\n\\n\\n\\n \\n \\n \\n \\n \\n \\n \\n \\n\\n\\n\x3c!-- End METADATA --\x3e\\n\\nLet\u2019s start with understanding the **power of intelligent applications**. \\n\\n## What We\'ll Cover:\\n\\n * What are intelligent applications?\\n * Categories of Intelligent Apps\\n * Breaking down the role of AI in intelligent apps\\n\\n![A decorative header image for the blog](../../static/img/fallforia/blogs/2023-09-18/blog-image1.png)\\n\\n## Demystifying Intelligent Applications: Leveraging AI in App Development\\n\\nUntil recent years, when you heard the term \u201cartificial intelligence\u201d (AI), you might have pictured autonomous robots from sci-fi films. Now, perhaps your mind jumps to big data and machine learning (ML). Maybe you\u2019re even thinking about the latest ChatGPT plugins and transformer architectures. Today, AI is more than these imaginations: it\u2019s a reality increasingly woven into our day-to-day routines through intelligent applications.\\n\\nIntelligent apps combine cloud-scale compute, data, and predictive or generative AI. These applications help you automate tasks and make data-driven decisions, aiding in leveraging business intelligence to gain meaningful, practical outcomes.\u202f \\n\\nFor developers, the AI landscape isn\u2019t an abstract complexity. It\u2019s a real, tangible opportunity for evolving traditional applications, making them \u201cintelligent,\u201d and delivering more value to users. And with tools like Azure Kubernetes Service, Azure Cosmos DB, and [Azure AI Services](https://azure.microsoft.com/en-us/solutions/ai/), stepping into the world of intelligent apps isn\u2019t a steep climb but a steady trek across a manageable learning curve.\\n\\n## The Three Categories of Intelligent Applications\\n\\nBefore we start building, it\u2019s critical to understand what makes an app \u201cintelligent.\u201d It\u2019s not a universal definition so much as an identifier for apps that fall into one or more of the three primary categories:\u202f \\n\\n * **Outcome-based** \u2014 This designation represents the broadest category of intelligent apps, whose principal value for users is the intelligent outcomes they provide. These apps process data to provide insightful results that enhance decision-making or help with tasks like trend prediction, resource management, and customer relationship management.\\n\\n * **Functionality-based** \u2014 This type of intelligent app takes AI integration a degree further, integrating AI as a function of the app to create or process its outputs, enhancing the app\u2019s overall interactivity and usefulness.\\n\\n * **Feature-based** \u2014 This is the narrowest category, in which the app\u2019s AI/ML component is one of its core features and primary selling points\\n\\nLet\u2019s dive more deeply into each category.\\n\\n## Outcome-Based Apps: Intelligent Outcomes Drive Success\\n\\nOutcome-based applications are all about the results provided to the user. They function beyond merely processing data to offer insightful outcomes that drive success.\\n\\nThink of a personal fitness tracker app that uses AI to analyze the user\u2019s activity and provides actionable recommendations for achieving a goal. This type of app not only tracks your steps, sleep patterns, and heart rate but uses that data to provide you with personalized health advice. It helps you make informed lifestyle choices \u2014 the \u201cintelligent\u201d outcome.\\n\\nAnother example is a social media marketing app that uses AI for time-saving data analysis, task automation, and trend prediction. AI-based sentiment analysis can gauge customer perception while natural language processing (NLP) accurately interprets customer inquiries. Task automation can schedule posts and automate responses to common customer queries, and trend forecasting helps model and predict customer behavior to further personalize targeted promotional efforts. Building this type of intelligence into your apps provides cost-effective, actionable insights that guide sales and marketing strategies.\\n\\n## Functionality-Based Apps: AI-Driven Features Enhance User Experiences\\n\\nAs the name suggests, functionality-based applications integrate AI into how the app functions, but the end user drives the action and outcome. In this category of apps, AI takes a more active role in the user\u2019s interaction by allowing them to use AI-created or processed output. These apps leverage AI technologies like [Azure AI Services](https://azure.microsoft.com/en-us/products/cognitive-services/) for NLP, image recognition, and pattern recognition to improve user experience.\\n\\nConsider a music app that generates personalized playlists based on your listening habits or your smartphone\u2019s camera app that uses AI to recognize faces and optimize photo settings. Even more advanced are the language translation apps that translate text from one language to another and can even understand and maintain the context and subtleties of the conversation. They use NLP to accomplish these feats.\\n\\nSimilarly, consider the latest e-commerce apps, whose AI-driven functionalities enable customers to try on clothes virtually, significantly enhancing the online shopping experience.\\n\\n## Feature-Based Apps: Integrating Advanced AI/ML Components\\n\\nFinally, there are feature-based intelligent apps. These apps incorporate advanced AI/ML components like neural networks, internal large language models (LLMs), and sophisticated algorithms as core features for the user. In this type of application, AI is the primary feature for users to engage with and the primary selling point for the app.\\n\\nSome examples of feature-based applications are chatbots and virtual agents built using [Azure Bot Service](https://azure.microsoft.com/en-ca/products/bot-services/). These robo-representatives use AI to provide more natural and human-like conversation, balancing superior efficiency with a friendlier, more personalized user experience. [The new Bing](https://www.microsoft.com/en-us/edge/features/the-new-bing) is a prime example of this implementation, integrating AI to give users a knowledgeable copilot for web searches.\\n\\nSimilarly, OpenAI\u2019s [ChatGPT](https://openai.com/chatgpt) allows users access to its state-of-the-art language models available as Microsoft\u2019s [Azure OpenAI Service](https://azure.microsoft.com/en-us/products/cognitive-services/openai-service/). It leverages the power of the highly sophisticated GPT-3 LLM to perform tasks like drafting emails, writing code, answering general knowledge questions, translating between languages, and tutoring in numerous subjects.\\n\\nThese apps don\u2019t just use AI to provide outputs or improve user experiences: They push the boundaries of what\u2019s possible in app development by integrating AI/ML components.\\n\\n:::info\\n**Register** for the live Q&A [Ask The Expert session](https://aka.ms/fallforIA/ATE) on **September 26** with the Azure Functions product engineering team.\\n:::\\n\\n## Breaking Down the Role of AI in Intelligent Applications\\n\\nIntelligent apps reflect the merging of traditional software design with the dynamic capabilities of AI. Let\u2019s explore how AI integrates and amplifies these applications.\\n\\nAt its core, AI augments applications with the capacity to self-learn and self-improve. It allows you to create applications that predict, react, and adapt to a constantly changing environment. In the power grid management system for a major city, an AI-infused application can forecast power usage trends, react to anomalies, and adapt to sudden demand surges, ensuring optimal efficiency and preventing blackouts.\\n\\nBut AI\u2019s role doesn\u2019t end at prediction or decision-making: It also automates tasks. In enterprise solutions, it has moved beyond simple automation to encompass intelligent process automation (IPA). In this arena, AI algorithms understand, learn, and then automate entire digital workflows. For developers, this is a fertile ground for creating applications that can dramatically improve the operational efficiency of businesses.\\n\\nEven more, AI substantially aids in development tasks. For example, there\u2019s [GitHub Copilot](https://github.com/features/copilot), an AI-powered code assistant that offers suggestions based not only on syntax but on development best practices and the intent of the code. AI has evolved from a tool you use to build apps to a partner that helps in creating them.\u202f \\n\\nUsing AI to build intelligent apps isn\u2019t about throwing out what\u2019s worked before. It\u2019s about enhancing the components that have the most impact and achieve the best results. Incorporating AI in apps elevates their predictive abilities, streamlines complex tasks, and even aids in the development and creation process. When you build intelligent apps, you don\u2019t just address current problems. You anticipate future challenges and craft solutions that adapt over time.\\n\\n## Summary\\n\\nAs developers the power of AI is in our hands, and it\u2019s our responsibility to harness its full potential. Each category tells the story of AI\'s transformative potential, from applications that deliver intelligent outcomes to those with AI capabilities to enrich the user experience and those pushing boundaries with advanced AI/ML technology.\\n\\nAccessible and robust platforms like [Azure AI](https://www.microsoft.com/en-us/ai) can simplify the process of developing Intelligent Applications. Whether you\u2019re refining an existing app or building a new one, just entering the world of AI, or already familiar with the landscape, take a moment to appreciate the potential that AI integration offers. You\u2019re embracing this exciting frontier and shaping the future of app development.\u202f \\n\\n## Exercise\\n\\n * **Complete** the [Cloud Skills Challenge](https://aka.ms/fallforIA/csc) to build on your apps and AI skills. \\n * Watch [Episode 01](https://aka.ms/learnlive-contoso-app-deconstructed-Ep1) of the Serverless Edition Learn Live session to learn how to build an end-to-end intelligent app solution."},{"id":"kick-off","metadata":{"permalink":"/Cloud-Native/30daysofIA/kick-off","source":"@site/blog-30daysofIA/2023-09-15/kick-off.md","title":"Kick-off #30DaysofIA \ud83c\udf42","description":"This Fall focus on building intelligent apps using AI and cloud-native technologies. `#FallForIntelligentApps` brings to you a learning journey to build your skills on creating differentiated experiences while modernizing your applications. It\u2019s time to `learn it all`.","date":"2023-09-17T09:00:00.000Z","formattedDate":"September 17, 2023","tags":[{"label":"Fall-For-IA","permalink":"/Cloud-Native/30daysofIA/tags/fall-for-ia"},{"label":"30-days-of-IA","permalink":"/Cloud-Native/30daysofIA/tags/30-days-of-ia"},{"label":"learn-live","permalink":"/Cloud-Native/30daysofIA/tags/learn-live"},{"label":"hack-together","permalink":"/Cloud-Native/30daysofIA/tags/hack-together"},{"label":"community-buzz","permalink":"/Cloud-Native/30daysofIA/tags/community-buzz"},{"label":"ask-the-expert","permalink":"/Cloud-Native/30daysofIA/tags/ask-the-expert"},{"label":"azure-kubernetes-service","permalink":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service"},{"label":"azure-functions","permalink":"/Cloud-Native/30daysofIA/tags/azure-functions"},{"label":"azure-openai","permalink":"/Cloud-Native/30daysofIA/tags/azure-openai"},{"label":"azure-container-apps","permalink":"/Cloud-Native/30daysofIA/tags/azure-container-apps"},{"label":"azure-cosmos-db","permalink":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db"},{"label":"github-copilot","permalink":"/Cloud-Native/30daysofIA/tags/github-copilot"},{"label":"github-codespaces","permalink":"/Cloud-Native/30daysofIA/tags/github-codespaces"},{"label":"github-actions","permalink":"/Cloud-Native/30daysofIA/tags/github-actions"}],"readingTime":3.685,"hasTruncateMarker":false,"authors":[{"name":"It\'s 30DaysOfIA","title":"FallForIA Content Team","url":"https://azure.github.io/Cloud-Native/Fall-For-IA/","imageURL":"https://azure.github.io/Cloud-Native/img/logo-ms-cloud-native.png","key":"cnteam"}],"frontMatter":{"date":"2023-09-17T09:00","slug":"kick-off","title":"Kick-off #30DaysofIA \ud83c\udf42","authors":["cnteam"],"draft":false,"hide_table_of_contents":false,"toc_min_heading_level":2,"toc_max_heading_level":3,"keywords":["Cloud","Data","AI","AI/ML","intelligent apps","cloud-native","30-days","enterprise apps","digital experiences","app modernization","serverless","ai apps"],"image":"https://azure.github.io/Cloud-Native/img/ogImage.png","description":"This Fall focus on building intelligent apps using AI and cloud-native technologies. `#FallForIntelligentApps` brings to you a learning journey to build your skills on creating differentiated experiences while modernizing your applications. It\u2019s time to `learn it all`.","tags":["Fall-For-IA","30-days-of-IA","learn-live","hack-together","community-buzz","ask-the-expert","azure-kubernetes-service","azure-functions","azure-openai","azure-container-apps","azure-cosmos-db","github-copilot","github-codespaces","github-actions"]},"prevItem":{"title":"1-1. Demystifying Intelligent Applications","permalink":"/Cloud-Native/30daysofIA/demystifying-intelligent-applications"},"nextItem":{"title":"HackTogether Recap \ud83c\udf42","permalink":"/Cloud-Native/30daysofIA/hacktogether-recap"}},"content":"\\n\\n\\n\\n\\n\\n \\n \\n \\n \\n \\n \\n \\n \\n\\n\\n\x3c!-- End METADATA --\x3e\\n\\nThis Fall focus on building intelligent apps using AI and cloud-native technologies. `#FallForIntelligentApps` brings to you a learning journey to build your skills on creating differentiated experiences while modernizing your applications. It\u2019s time to `learn it all`. \\n\\n## What We\'ll Cover\\n * What is Fall For Intelligent Apps? \\n * How Can I participate? \\n * How Can I skill up? (in just 30 Days) \\n * **Exercise:** Take the [Fall For Intelligent Apps Skills Challenge](https://aka.ms/FallForIA/apps-csc)\\n * **Resources:** [#30DaysOfIA Collection](https://aka.ms/fallforIA/collection/?WT.mc_id=javascript-99907-ninarasi)\\n\\n![image](../../static/img/fallforia/blogs/2023-09-17/FallForIA_Key_visual.jpg)\\n\\n## Get Ready To #FallForIntelligentApps starting September 18!\\n\\nToday, we kick off the Fall season with content and activities to skill you up on all things Intelligent Apps or AI Apps on Azure with content, events, and community interactions! Read on to learn about what is coming!\\n\\n## Explore Our Initiatives\\n\\nWe have a number of initiatives planned for the month to help you learn and skill up on relevant technologies. Click on the links to visit the relevant pages for each\\n\\n * [#30DaysOfIA](https://aka.ms/fallforIA/30days) - 6 themed weeks of daily articles in a structured roadmap\\n * [Learn Live Series](https://aka.ms/FallForIA/LearnLive) \u2013 8 weekly live episodes on `Serverless` and `Kubernetes`\\n * [Ask The Expert](https://aka.ms/FallForIA/ATE-series) \u2013 join live Q&A sessions with Product Engineering teams\\n * [Cloud Skills Challenge](https://aka.ms/fallforIA/csc) \u2013 skill up by competing with peers to complete modules\\n\\n![image](../../static/img/fallforia/blogs/2023-09-17/Website-kick-off.jpg)\\n\\n:::info **Register for the events!**\\n\\nWhat are 4 things you can do today, to jumpstart your learning journey?\\n\\n * Register for live Q&A sessions (free, online) \\n * September 26 \u2013 [Ask The Expert: Azure Functions](https://aka.ms/FallForIA/ATE-series)\\n * October 11 \u2013 [Ask The Expert: Azure App Service](https://aka.ms/FallForIA/ATE-series)\\n * October 18 - [Ask The Expert: Azure Container Apps](https://aka.ms/FallForIA/ATE-series)\\n * October 25 \u2013 [Ask The Expert: Azure Kubernetes Service](https://aka.ms/FallForIA/ATE-series)\\n * Register for the [Learn Live Series: Serverless Edition](https://aka.ms/FallForIA/LearnLive) \u2013 weekly live learning \\n * Register for the [Intelligent Apps webinar](https://info.microsoft.com/ww-landing-driving-business-value-by-modernizing-with-cloud-native-and-ai.html?lcid=en-us) with Microsoft and Forrester\\n * Complete the [Cloud Skills Challenge](https://aka.ms/fallforIA/csc) \u2013 ends on October 31! \\n:::\\n\\n## #30Days Of Intelligent Apps\\n\\n[#30DaysOfIA](https://aka.ms/fallforIA/30days) is a series of daily blog posts grouped into themed weeks - taking you from core concepts to end-to-end solution examples in 30 days. Each article will provide conceptual lessons paired with exercises and resources to help you reinforce learnings and take next steps.\\n\\nThis series takes you through learning journey in\u202f**six stages**, each building on the previous week to help you skill up in a beginner-friendly way:\\n\\n * **Week 1**: Power of [Intelligent Applications](https://azure.microsoft.com/en-us/blog/build-next-generation-ai-powered-applications-on-microsoft-azure/?WT.mc_id=javascript-99907-ninarasi)\\n * **Week 2:** How to build intelligent apps with [cloud-native](https://azure.microsoft.com/en-us/solutions/cloud-native-apps/?WT.mc_id=javascript-99907-ninarasi)?\\n * **Week 3:** Powering intelligent applications using [Azure Kubernetes Service](https://learn.microsoft.com/en-us/azure/aks/?WT.mc_id=javascript-99907-ninarasi)\\n * **Week 4:** Building a Serverless Intelligent App with [Azure Functions](https://learn.microsoft.com/en-us/azure/azure-functions/functions-overview?WT.mc_id=javascript-99907-ninarasi&pivots=programming-language-csharp) and [Azure OpenAI](https://learn.microsoft.com/en-us/azure/ai-services/openai/overview/?WT.mc_id=javascript-99907-ninarasi)\\n * **Week 5:** Build end to end AI powered solutions with real world [reference architectures](https://learn.microsoft.com/en-us/azure/architecture/?WT.mc_id=javascript-99907-ninarasi)\\n * **Week 6:** Build your own [Copilot](https://learn.microsoft.com/en-us/training/paths/copilot/?WT.mc_id=javascript-99907-ninarasi)\\n\\nWe will start with defining intelligent apps and then expand on how to build with cloud-native technologies like [Azure Kubernetes Service](https://azure.microsoft.com/en-us/products/kubernetes-service/?WT.mc_id=javascript-99907-ninarasi), [Azure Container Apps](https://azure.microsoft.com/en-us/products/container-apps/?WT.mc_id=javascript-99907-ninarasi) and [Azure Functions](https://azure.microsoft.com/en-us/products/functions?WT.mc_id=javascript-99907-ninarasi), and take you through end-to-end scenarios for real world application development. Before we dive deep on intelligent apps, here is a high-level overview of the **Intelligent Apps** landscape on Azure: \\n\\n![image](../../static/img/fallforia/blogs/2023-09-17/intelligent-apps-image.jpg)\\n\\n**Containers on Azure** services offer you a wide range of capabilities, from simplicity to control to suit your different needs.\\n\\n![image](../../static/img/fallforia/blogs/2023-09-17/Containers-on-Azure.jpg)\\n![image](../../static/img/fallforia/blogs/2023-09-17/Containers-on-Azure-2.jpg)\\n\\nTo start with the basics for developing [Kubernetes](https://azure.microsoft.com/en-us/products/kubernetes-service/?WT.mc_id=javascript-99907-ninarasi) applications, explore [#30DaysOfCloudNative](https://azure.github.io/Cloud-Native/cnny-2023).\\n\\nCloud-native development when paired with **serverless computing** enhances your solution architecture for building cost optimized, resilient applications.\\n\\n![image](../../static/img/fallforia/blogs/2023-09-17/Serverless-New.jpg)\\n\\nTo start with the basics for [serverless computing](https://azure.microsoft.com/solutions/serverless/?WT.mc_id=javascript-99907-ninarasi), explore [#30DaysOfServerless](https://azure.github.io/Cloud-Native/blog).\\n\\n## Let\u2019s Get Started\\n\\nNow you know everything! We hope you are as excited as we are to dive into a full month of active learning and doing! Don\'t forget to\u202f[subscribe](https://aka.ms/fallforIA/30days/subscribe)\u202ffor updates in your favorite feed reader!\u202f**And look out for our first Intelligent Apps post Monday!**"},{"id":"hacktogether-recap","metadata":{"permalink":"/Cloud-Native/30daysofIA/hacktogether-recap","source":"@site/blog-30daysofIA/2023-09-08/hack-together-recap.md","title":"HackTogether Recap \ud83c\udf42","description":"Exciting news! As we approach the close of #JavaScript on #Azure Global Hack today, we are thrilled to announce another exciting opportunity for all JavaScript developers!! Find a recap of Hack together and read all about the upcoming #FallIntoIA on this post!","date":"2023-09-08T00:00:00.000Z","formattedDate":"September 8, 2023","tags":[{"label":"Fall-For-IA","permalink":"/Cloud-Native/30daysofIA/tags/fall-for-ia"},{"label":"30-days-of-IA","permalink":"/Cloud-Native/30daysofIA/tags/30-days-of-ia"},{"label":"learn-live","permalink":"/Cloud-Native/30daysofIA/tags/learn-live"},{"label":"hack-together","permalink":"/Cloud-Native/30daysofIA/tags/hack-together"},{"label":"community-buzz","permalink":"/Cloud-Native/30daysofIA/tags/community-buzz"},{"label":"ask-the-expert","permalink":"/Cloud-Native/30daysofIA/tags/ask-the-expert"},{"label":"azure-kubernetes-service","permalink":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service"},{"label":"azure-functions","permalink":"/Cloud-Native/30daysofIA/tags/azure-functions"},{"label":"azure-openai","permalink":"/Cloud-Native/30daysofIA/tags/azure-openai"},{"label":"azure-container-apps","permalink":"/Cloud-Native/30daysofIA/tags/azure-container-apps"},{"label":"azure-cosmos-db","permalink":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db"},{"label":"github-copilot","permalink":"/Cloud-Native/30daysofIA/tags/github-copilot"},{"label":"github-codespaces","permalink":"/Cloud-Native/30daysofIA/tags/github-codespaces"},{"label":"github-actions","permalink":"/Cloud-Native/30daysofIA/tags/github-actions"}],"readingTime":3.995,"hasTruncateMarker":false,"authors":[{"name":"It\'s 30DaysOfIA","title":"FallForIA Content Team","url":"https://azure.github.io/Cloud-Native/Fall-For-IA/","imageURL":"https://azure.github.io/Cloud-Native/img/logo-ms-cloud-native.png","key":"cnteam"}],"frontMatter":{"slug":"hacktogether-recap","title":"HackTogether Recap \ud83c\udf42","authors":["cnteam"],"draft":false,"hide_table_of_contents":false,"toc_min_heading_level":2,"toc_max_heading_level":3,"keywords":["Cloud-Scale","Data","AI","AI/ML","intelligent apps","cloud-native","30-days","enterprise apps","digital experiences","app modernization","hack-together"],"image":"https://azure.github.io/Cloud-Native/img/ogImage.png","description":"Exciting news! As we approach the close of #JavaScript on #Azure Global Hack today, we are thrilled to announce another exciting opportunity for all JavaScript developers!! Find a recap of Hack together and read all about the upcoming #FallIntoIA on this post!","tags":["Fall-For-IA","30-days-of-IA","learn-live","hack-together","community-buzz","ask-the-expert","azure-kubernetes-service","azure-functions","azure-openai","azure-container-apps","azure-cosmos-db","github-copilot","github-codespaces","github-actions"]},"prevItem":{"title":"Kick-off #30DaysofIA \ud83c\udf42","permalink":"/Cloud-Native/30daysofIA/kick-off"},"nextItem":{"title":"Fall is Coming! \ud83c\udf42","permalink":"/Cloud-Native/30daysofIA/road-to-fallforIA"}},"content":"\\n\\n\\n\\n\\n\\n \\n \\n \\n \\n \\n \\n \\n \\n\\n\\n\x3c!-- End METADATA --\x3e\\n\\nContinue The Learning Journey through **Fall For Intelligent Apps!** \ud83c\udf42\\n\\n## What We\'ll Cover\\n * Thank you! \u2665\ufe0f \\n * Recap of The [JavaScript on Azure Global Hack-Together](https://aka.ms/JavaScripton_Azure)\\n * Continue the journey\\n * Hands-on practice: Make your first contribution to open-source!\\n * Resources: For self-study!\\n\\n\\n\x3c!-- ************************************* --\x3e\\n\x3c!-- AUTHORS: ONLY UPDATE BELOW THIS LINE --\x3e\\n\x3c!-- ************************************* --\x3e\\n\\n## Thank you! \u2665\ufe0f \\n![image](https://user-images.githubusercontent.com/40116776/264592120-1dc08b59-0555-40b2-8866-59248a573b83.png)\\n\\nIt\'s hard to believe that JavaScript on Azure hack-together is ending! It seems like just yesterday that we launched this initiative, and yet here we are, 15 days later, with an incredible amount of learning and growth behind us. So... it\'s time for a wrap!\\n\\nFrom the bottom of our hearts, we want to thank each and every one of you for your participation, engagement, and enthusiasm. It\'s been truly inspiring to see the passion and dedication from this strong community, and we\'re honored to be a part of it. \u2728\\n\\n## Recap of The [JavaScript on Azure Global Hack-Together](https://aka.ms/JavaScripton_Azure)\\n\\nAs we wrap up this exciting event, we wanted to take a moment to reflect on all that we\'ve accomplished together. Over the last 15 days, we\'ve covered a lot of ground, from the basics of contributing to Open source to the exploration of the Contoso Real Estate project from its Frontend to its Backend and future AI implementation. \\n\\nNow that the hack-together is ending, we want to make sure that you have all the resources you need to continue honing your skills in the future. Whether you\'re looking to make your fist contribution to open source, become an open source maintainers, collaborate with others, or simply keep learning, there are plenty of resources out there to help you achieve your goals. So, let\'s dive in and explore all the ways you can continue to grow your JavaScript skills on Azure!\\n\\n### JSonAzure Hack-together Roadmap \ud83d\udccd:\\n![hack-together-roadmap (2)](https://user-images.githubusercontent.com/40116776/264975573-85938fcc-b235-4b5b-b45a-f174d3cf560d.png)\\n\\n\\n### Recap on past Livestreams\ud83c\udf1f:\\n\\nDay 1\ufe0f\u20e3: [Opening Keynote (Hack-together Launch)](https://developer.microsoft.com/reactor/events/20275/?WT.mc_id=academic-98351-juliamuiruri): Introduction to the Contoso Real Estate Open-source project!, managing complex and complex enterprise architecture, new announcements for JavaScript developers on Azure\\n\\nDay 2\ufe0f\u20e3: [GitHub Copilot & Codespaces](https://developer.microsoft.com/reactor/events/20321/?WT.mc_id=academic-98351-juliamuiruri): Introduction to your AI pair programmer (GitHub Copilot) and your virtual developer environment on the cloud (GitHub Codespaces)\\n\\nDay 6\ufe0f\u20e3: [Build your Frontend using Static Web Apps](https://developer.microsoft.com/reactor/events/20276/?WT.mc_id=academic-98351-juliamuiruri) as part of a complex, modern composable frontends (or micro-frontends) and cloud-native applications.\\n\\nDay 9\ufe0f\u20e3: Build a Serverless Backend using [Azure Functions](https://developer.microsoft.com/reactor/events/20277/?WT.mc_id=academic-98351-juliamuiruri)\\n\\nDay 1\ufe0f\u20e33\ufe0f\u20e3: Easily connect to an [Azure Cosmos DB](https://developer.microsoft.com/reactor/events/20278/?WT.mc_id=academic-98351-juliamuiruri), exploring its benefits and how to get started\\n\\nDay 1\ufe0f\u20e35\ufe0f\u20e3: Being in the AI Era, we dive into the [Azure OpenAI Service](https://developer.microsoft.com/reactor/events/20322/?WT.mc_id=academic-98351-juliamuiruri) and how you can start to build intelligent JavaScript applications\\n\\n### \ud83d\udcd6 Self-Learning Resources\\n\\n1. JavaScript on Azure Global Hack Together [Module collection](https://aka.ms/JavaScriptonAzureCSC)\\n2. Lets #HackTogether: Javascript On Azure [Keynote](https://dev.to/azure/lets-hacktogether-javascript-on-azure-keynote-nml)\\n3. [Step by Step Guide: Migrating v3 to v4 programming model for Azure Functions for Node.Js Application](https://techcommunity.microsoft.com/t5/educator-developer-blog/step-by-step-guide-migrating-v3-to-v4-programming-model-for/ba-p/3897691?WT.mc_id=academic-98351-juliamuiruri)\\n\\n## Continue your journey with #FallForIntelligentApps\\nJoin us this Fall on a learning journey to explore building intelligent apps. Combine the power of AI, cloud-native app development, and cloud-scale data to build highly differentiated digital experiences. Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure. Engage in a self-paced learning adventure by following along the #30Days of Intelligent Apps series, completing the Intelligent Apps Skills Challenge, joining the Product Group for a live Ask The Expert series or building an end to end solution architecture with a live guided experience through the Learn Live series. Discover more here.\\n\\n## Hands-on practice: Make your first contribution to open source!\\nJoin our GitHUb Discussion Forum to connect with developers from every part of world, see contributions from other, find collaborators and make your first contribution to a real-world project!\\nDon\'t forget to give the repo a star \u2b50\\n\\n## Resources\\nAll resources are accessible on our [landing page](https://aka.ms/JavaScripton_Azure)"},{"id":"road-to-fallforIA","metadata":{"permalink":"/Cloud-Native/30daysofIA/road-to-fallforIA","source":"@site/blog-30daysofIA/2023-08-28/road-to-fallforia.md","title":"Fall is Coming! \ud83c\udf42","description":"Combine the power of AI, cloud-scale data, and cloud-native app development to create highly differentiated digital experiences. Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure.","date":"2023-08-28T00:00:00.000Z","formattedDate":"August 28, 2023","tags":[{"label":"Fall-For-IA","permalink":"/Cloud-Native/30daysofIA/tags/fall-for-ia"},{"label":"30-days-of-IA","permalink":"/Cloud-Native/30daysofIA/tags/30-days-of-ia"},{"label":"learn-live","permalink":"/Cloud-Native/30daysofIA/tags/learn-live"},{"label":"hack-together","permalink":"/Cloud-Native/30daysofIA/tags/hack-together"},{"label":"community-buzz","permalink":"/Cloud-Native/30daysofIA/tags/community-buzz"},{"label":"ask-the-expert","permalink":"/Cloud-Native/30daysofIA/tags/ask-the-expert"},{"label":"azure-kubernetes-service","permalink":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service"},{"label":"azure-functions","permalink":"/Cloud-Native/30daysofIA/tags/azure-functions"},{"label":"azure-openai","permalink":"/Cloud-Native/30daysofIA/tags/azure-openai"},{"label":"azure-container-apps","permalink":"/Cloud-Native/30daysofIA/tags/azure-container-apps"},{"label":"azure-cosmos-db","permalink":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db"},{"label":"github-copilot","permalink":"/Cloud-Native/30daysofIA/tags/github-copilot"},{"label":"github-codespaces","permalink":"/Cloud-Native/30daysofIA/tags/github-codespaces"},{"label":"github-actions","permalink":"/Cloud-Native/30daysofIA/tags/github-actions"}],"readingTime":1.055,"hasTruncateMarker":false,"authors":[{"name":"It\'s 30DaysOfIA","title":"FallForIA Content Team","url":"https://azure.github.io/Cloud-Native/Fall-For-IA/","imageURL":"https://azure.github.io/Cloud-Native/img/logo-ms-cloud-native.png","key":"cnteam"}],"frontMatter":{"slug":"road-to-fallforIA","title":"Fall is Coming! \ud83c\udf42","authors":["cnteam"],"draft":false,"hide_table_of_contents":false,"toc_min_heading_level":2,"toc_max_heading_level":3,"keywords":["Cloud-Scale","Data","AI","AI/ML","intelligent apps","cloud-native","30-days","enterprise apps","digital experiences","app modernization"],"image":"https://github.com/Azure/Cloud-Native/blob/main/website/static/img/ogImage.png","description":"Combine the power of AI, cloud-scale data, and cloud-native app development to create highly differentiated digital experiences. Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure.","tags":["Fall-For-IA","30-days-of-IA","learn-live","hack-together","community-buzz","ask-the-expert","azure-kubernetes-service","azure-functions","azure-openai","azure-container-apps","azure-cosmos-db","github-copilot","github-codespaces","github-actions"]},"prevItem":{"title":"HackTogether Recap \ud83c\udf42","permalink":"/Cloud-Native/30daysofIA/hacktogether-recap"}},"content":"\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n\\n\\n\x3c!-- End METADATA --\x3e\\n\\nSeptember is almost here - and that can only mean one thing!! It\'s time to **\ud83c\udf42 Fall for something new and exciting** and spend a few weeks skilling up on relevant tools, techologies and solutions!! \\n\\nLast year, we focused on #ServerlessSeptember. This year, we\'re building on that theme with the addition of cloud-scale **Data**, cloud-native **Technologies** and cloud-based **AI** integrations to help you modernize and build intelligent apps for the enterprise!\\n\\nWatch this space - and join us in September to learn more!"}]}')}}]); \ No newline at end of file diff --git a/assets/js/010f538e.db12abf4.js b/assets/js/010f538e.db12abf4.js deleted file mode 100644 index 7ab76ed02a..0000000000 --- a/assets/js/010f538e.db12abf4.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[997],{19881:e=>{e.exports=JSON.parse('{"blogPosts":[{"id":"cultivating-a-culture-for-intelligent-apps","metadata":{"permalink":"/Cloud-Native/30daysofIA/cultivating-a-culture-for-intelligent-apps","source":"@site/blog-30daysofIA/2023-09-22/cultivating-a-culture-for-intelligent-apps.md","title":"1-6. Cultivating a Culture for Intelligent Apps","description":"Prepare your organization to adopt intelligent apps by taking a change management approach. Assess your readiness, upskill teams, create a culture of change and overcome resistance, and prepare for future evolution to pave the way for a smooth transition into an AI-centric company.","date":"2023-09-22T09:00:00.000Z","formattedDate":"September 22, 2023","tags":[{"label":"Fall-For-IA","permalink":"/Cloud-Native/30daysofIA/tags/fall-for-ia"},{"label":"30-days-of-IA","permalink":"/Cloud-Native/30daysofIA/tags/30-days-of-ia"},{"label":"learn-live","permalink":"/Cloud-Native/30daysofIA/tags/learn-live"},{"label":"hack-together","permalink":"/Cloud-Native/30daysofIA/tags/hack-together"},{"label":"community-buzz","permalink":"/Cloud-Native/30daysofIA/tags/community-buzz"},{"label":"ask-the-expert","permalink":"/Cloud-Native/30daysofIA/tags/ask-the-expert"},{"label":"azure-kubernetes-service","permalink":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service"},{"label":"azure-functions","permalink":"/Cloud-Native/30daysofIA/tags/azure-functions"},{"label":"azure-openai","permalink":"/Cloud-Native/30daysofIA/tags/azure-openai"},{"label":"azure-container-apps","permalink":"/Cloud-Native/30daysofIA/tags/azure-container-apps"},{"label":"azure-cosmos-db","permalink":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db"},{"label":"github-copilot","permalink":"/Cloud-Native/30daysofIA/tags/github-copilot"},{"label":"github-codespaces","permalink":"/Cloud-Native/30daysofIA/tags/github-codespaces"},{"label":"github-actions","permalink":"/Cloud-Native/30daysofIA/tags/github-actions"}],"readingTime":9.215,"hasTruncateMarker":false,"authors":[{"name":"It\'s 30DaysOfIA","title":"FallForIA Content Team","url":"https://azure.github.io/Cloud-Native/Fall-For-IA/","imageURL":"https://azure.github.io/Cloud-Native/img/logo-ms-cloud-native.png","key":"cnteam"}],"frontMatter":{"date":"2023-09-22T09:00","slug":"cultivating-a-culture-for-intelligent-apps","title":"1-6. Cultivating a Culture for Intelligent Apps","authors":["cnteam"],"draft":false,"hide_table_of_contents":false,"toc_min_heading_level":2,"toc_max_heading_level":3,"keywords":["Cloud","Data","AI","AI/ML","intelligent apps","cloud-native","30-days","enterprise apps","digital experiences","app modernization","serverless","ai apps","data"],"image":"https://azure.github.io/Cloud-Native/img/ogImage.png","description":"Prepare your organization to adopt intelligent apps by taking a change management approach. Assess your readiness, upskill teams, create a culture of change and overcome resistance, and prepare for future evolution to pave the way for a smooth transition into an AI-centric company.","tags":["Fall-For-IA","30-days-of-IA","learn-live","hack-together","community-buzz","ask-the-expert","azure-kubernetes-service","azure-functions","azure-openai","azure-container-apps","azure-cosmos-db","github-copilot","github-codespaces","github-actions"]},"nextItem":{"title":"1-5. Preparing the Path for Intelligent Apps","permalink":"/Cloud-Native/30daysofIA/preparing-the-path-for-intelligent-apps"}},"content":"\\n\\n\\n\\n\\n\\n \\n \\n \\n \\n \\n \\n \\n \\n\\n\\n\x3c!-- End METADATA --\x3e\\nPrepare your organization to adopt intelligent apps by taking a change management approach. Assess your readiness, upskill teams, create a culture of change and overcome resistance, and prepare for future evolution to pave the way for a smooth transition into an AI-centric company.\\n\\n## What We\'ll Cover:\\n\\n * Role of change management in modernizing to intelligent apps\\n * Skills needed for building intelligent apps in your organization\\n * How to build a culture of change to embrace AI\\n * How to prepare for the future of intelligent apps and continuous evolution\\n\\n![image of modernizing AI solutions for intelligent apps](../../static/img/fallforia/blogs/2023-09-22/blog-image-1-6.png)\\n\\n## Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management\\n\\nAs we\u2019ve discussed throughout this series, intelligent apps and generative AI are rewriting the rules of business and application development. The first article of this series, \u201c[Demystifying Intelligent Applications: Leveraging AI in App Development](https://azure.github.io/Cloud-Native/30daysofIA/demystifying-intelligent-applications),\u201d explored how intelligent apps can enhance user experiences and drive businesses growth. These advancements are opening new opportunities and markets for companies ready to embrace them.\\n\\nHowever, embracing this digital transformation is more complex than flipping a switch. It requires adjustments to the technical side, which we explored in the series\u2019 fifth article, \u201c[Preparing the Path for Intelligent Apps: Transitioning from On-Premises/IaaS to Cloud Native Applications](https://azure.github.io/Cloud-Native/30daysofIA/preparing-the-path-for-intelligent-apps/).\u201d It also demands a shift in how organizations adapt to change and approach app design and development. Let\u2019s explore that mindset transition with tips to help you make it happen.\\n\\n## The Role of Change Management in Transitioning to Intelligent Apps\\n\\nTransitioning to intelligent apps isn\u2019t merely a technological change. It\u2019s a profound shift in how businesses perceive and operate. This move toward artificial intelligence (AI)-centric approach isn\u2019t meant to replace humans with machines or radically overhaul all business operations.\\n\\nInstead, intelligent apps should augment human capabilities, streamline processes, and foster an environment where innovation flourishes. They expand toolkits and techniques to include AI-assisted development tools like [GitHub Copilot](https://github.com/features/copilot) and AI-enabled workflows, helping us design and deliver AI-enhanced experiences.\\n\\nEffective change management strategies are crucial in helping employees adapt to these new ways of working with intelligent apps, ensuring that the transition doesn\u2019t disrupt productivity or morale.\\n\\nTeam and organization strategies should:\u202f \\n\\n * Assess the impact of any proposed changes.\u202f\u202f \\n * Communicate the benefits and reasons behind the change to all affected team members.\u202f\u202f \\n * Provide the necessary training and support.\u202f\u202f \\n * Integrate the changes into the culture and daily operations.\u202f \\n\\nBy incorporating this robust change management approach, you can identify and mitigate potential transition risks early. Moreover, this strategy ensures that employees are engaged throughout the process, fostering a sense of involvement and ownership. This, in turn, cultivates a culture suited for innovation through intelligent apps.\u202f \\n\\nThese organizational changes focus on the following aspects: organizational readiness, skillset, culture of change and overcoming resistance, and adaptability. Let\u2019s explore them below.\\n\\n:::info\\nRegister for the intelligent apps webinar on [Driving Business Value by Modernizing with Cloud-Native & AI](https://info.microsoft.com/ww-landing-driving-business-value-by-modernizing-with-cloud-native-and-ai.html?lcid=en-us?WT.mc_id=javascript-99907-ninarasi) with *Microsoft* and *Forrester* on **September 26**.\u202f \\n\\nExplore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries.\u202f \\n:::\\n\\n## Identifying Organizational Readiness for Intelligent Apps\\n\\nThe first step in preparing your organization for intelligent apps is to assess your current readiness for change. Analyze various elements of your organization, from infrastructure and workforce skills to strategic vision and overall culture.\\n\\nCan your apps and systems integrate with AI platforms like [Microsoft Azure AI](https://azure.microsoft.com/en-us/solutions/ai/?WT.mc_id=javascript-99907-ninarasi)? Is your workforce comfortable working alongside AI, or are they fearful and resistant? Does your strategic vision incorporate AI\u2019s transformative potential, or does it fail to acknowledge AI as a critical driver of future growth?\\n\\nThese questions identify the challenges of integrating AI into an organization and determine the company\u2019s readiness for intelligent apps.\\n\\nEmbracing intelligent apps involves more than being ready with your technological infrastructure or having the right technology or tools. You need flexible organizational structures and processes driving your business to accommodate new ideas and capabilities. Your teams must feel empowered to own and drive them. Much like Microsoft\u2019s shift to AI-powered services\u2014like [AI-powered Bing and Edge](https://www.microsoft.com/en-us/edge/features/the-new-bing?WT.mc_id=javascript-99907-ninarasi) and [Microsoft 365 Copilot](https://adoption.microsoft.com/en-us/copilot/)\u2014embracing AI organization-wide requires a holistic vision and willingness to innovate, with teams capable of delivering on the plan.\\n\\nAfter accessing these critical areas for organizational readiness, you can start planning a strategy for this transition to an AI-ready organization. Identify skill gaps and work to bridge them while nurturing an environment that appreciates and harnesses the potential of AI and intelligent apps.\\n\\n## Nurturing the Indispensable Skills for Intelligent Apps\\n\\nTasking your AI specialists with preparing your organization to use and build intelligent apps isn\u2019t enough. Accomplishing this goal requires a cross-functional effort and a diverse skill set.\\n\\nDeveloping intelligent apps involves data scientists managing and interpreting vast amounts of data, machine learning engineers building and training the models, and traditional developers integrating these models into usable applications. However, the impact of generative AI spans beyond creating apps. AI fluency across the entire organization\u2014from sales and marketing to accounting and legal departments\u2014offers substantial benefits, sparking new ideas and solutions from sometimes unexpected sources.\\n\\nTeams\' AI skills might roughly fall into three levels:\u202f \\n\\n * **Fundamental skills** \u2014 Being familiar and efficient with using AI tools, for example, knowing how to write effective prompts for large language models (LLMs).\\n * **Visionary skills** \u2014 Recognizing AI\u2019s strengths and weaknesses, for example, its creativity and hallucinations, that is, unexpected output.\\n * **Expert skills** \u2014 Managing data and training models and integrating AI technology into apps, such as incorporating the OpenAI application programming interface (API) into an Intelligent App.\\n\\nBased on these levels, you can gauge your team\'s skill readiness to pinpoint the most significant gaps, whether in general AI knowledge across your organization or within technical development teams.\\n\\nAs intelligent apps become increasingly prevalent, upskilling your workforce is crucial. This transition opens a new realm of skills and capabilities for your team to master. You can offer machine learning, data analysis, and cloud computing training programs\u2014such as Microsoft\u2019s [Azure AI Fundamentals certification](https://learn.microsoft.com/en-us/certifications/azure-ai-fundamentals/?WT.mc_id=javascript-99907-ninarasi)\u2014to empower your team to confidently navigate the intelligent apps landscape.\\n\\nNurturing these skills within your organization may require hiring new talent, reskilling current employees, and creating an environment that promotes learning and experimentation. Specifically, if your team understands tools like the [Microsoft Azure AI platform](https://azure.microsoft.com/en-us/solutions/ai/?WT.mc_id=javascript-99907-ninarasi), they could contribute significantly to developing and maintaining intelligent apps.\\n\\nAn upskilled team is more than a functional unit. It\u2019s a think tank to drive innovation and improve your organization\u2019s competitive standing in the digital marketplace.\\n\\n## Building a Culture of Change to Embrace AI\\n\\nEven with the right people and skills in place, change remains an uphill battle for organizations committed to traditional processes.\u202f \\n\\nTransitioning to intelligent apps is as much about mindset as technology and skills. It requires fostering a culture open to change and innovation that understands AI\u2019s transformative potential and embraces it to enhance rather than replace human capabilities.\u202f \\n\\nLeaders play a pivotal role in cultivating this mindset. As they model a positive attitude toward AI, emphasize its benefits, and address any concerns transparently, leaders significantly contribute to the organization\u2019s readiness to adopt intelligent apps.\u202f \\n\\nThe most critical factor in this transition is enabling an AI-friendly culture. It\u2019s about encouraging curiosity, rewarding innovative thinking, and creating an environment where people thrive on learning and experimentation.\u202f \\n\\nEmpowering teams to leverage practical AI tools and AI-driven data insights to aid decision-making helps motivate employees to use and explore AI\u2019s potential, improve their skills, and contribute to the successful integration of intelligent apps. Such an environment is fertile ground to grow breakthrough ideas and innovative solutions.\u202f \\n\\n## Overcoming Resistance to Change and Adopting Intelligent Apps\\n\\nDespite AI\u2019s potential, change isn\u2019t easy. It takes effort and is especially challenging for organizations used to existing processes. This resistance to change is a significant roadblock when transitioning to intelligent apps.\u202f \\n\\nOvercoming this resistance involves:\u202f\u202f \\n\\n * Understanding employees\u2019 concerns.\u202f \\n * Challenging underlying assumptions about traditional systems and outdated processes that hinder change.\u202f \\n * Providing comprehensive training for workers to use AI effectively.\\n * Demonstrating the benefits and opportunities of intelligent apps.\u202f \\n * Clear and continuous communication to dispel fears and misconceptions about AI.\\n\\nThese steps will foster greater acceptance and enthusiasm for intelligent apps in your teams.\u202f \\n\\n## Preparing for the Future of Intelligent Apps and Continuous Evolution\\n\\nFinally, it\u2019s essential to understand that generative AI is just beginning to gain momentum. Adjusting to a world of intelligent apps isn\u2019t a one-time change\u2014it\u2019s a continuous evolution.\u202f \\n\\nAs AI technologies mature and evolve and more businesses realize their benefits, we can expect the landscape of intelligent apps to expand further. Organizations can cultivate a mindset of continuous learning, flexibility, and adaptation to prepare for this future. Staying ahead in this dynamic field requires organizations to keep up with the latest AI advancements, continually reassess their strategies, and pivot when necessary.\u202f \\n\\nThe domain of intelligent apps is rapidly advancing. Organizations must prepare to navigate this dynamic landscape to stay ahead of the curve.\\n\\n## Summary\\n\\nPreparing for AI and intelligent apps is a strategic decision. It requires thoughtful preparation and a culture that embraces change.\\n\\nWith the proper preparation and mindset, adopting AI can become a smoother, collaborative path to innovation for the whole organization. Integrating AI into your organization does not discard what has worked in the past. It simply enhances and augments those processes for a better future.\\n\\nConsider the points above to assess your organization\u2019s readiness and prepare to welcome intelligent apps.\\n\\nIn the first week of this series, you learned about [intelligent apps](https://azure.github.io/Cloud-Native/30daysofIA/demystifying-intelligent-applications), how organizations today find [success through intelligent apps](https://azure.github.io/Cloud-Native/30daysofIA/harnessing-the-power-of-intelligent-apps), what [AI-assisted application development](https://azure.github.io/Cloud-Native/30daysofIA/reimagine-app-development-with-ai) looks like, ways to [leverage the power of generative AI](https://azure.github.io/Cloud-Native/30daysofIA/how-digital-natives-leverage-generative-ai), the technical changes needed to [prepare for intelligent apps](https://azure.github.io/Cloud-Native/30daysofIA/preparing-the-path-for-intelligent-apps/), and, in this article, the organizational changes required to harness the power of AI.\\n\\nThe future is intelligent. Use this series as a guideline to navigate challenges while preparing for and embracing AI\'s opportunities for an intelligent tomorrow.\\n\\n## Exercise\\n\\n * Complete the **[Apps Cloud Skills Challenge](https://aka.ms/fallforIA/apps-csc)** to build on your app dev and AI skills.\\n * Complete the **[AI Cloud Skills Challenge](https://aka.ms/fallforIA/ai-csc)** to build on your AI skills.\\n * Register for **[Episode 03](https://aka.ms/learnlive-contoso-app-deconstructed-Ep3)** of the serverless edition Learn Live session to learn how to build, test and deploy an end-to-end intelligent app solution.\\n * Register for **[Ask the Expert: Azure Functions](https://reactor.microsoft.com/en-us/reactor/series/S-1037/)** session for live Q&A with the Product Engineering team on building intelligent serverless apps."},{"id":"preparing-the-path-for-intelligent-apps","metadata":{"permalink":"/Cloud-Native/30daysofIA/preparing-the-path-for-intelligent-apps","source":"@site/blog-30daysofIA/2023-09-22/preparing-the-path-for-intelligent-apps.md","title":"1-5. Preparing the Path for Intelligent Apps","description":"Learn best practices for modernizing your on-premises or IaaS solutions to intelligent apps in the cloud.","date":"2023-09-22T09:00:00.000Z","formattedDate":"September 22, 2023","tags":[{"label":"Fall-For-IA","permalink":"/Cloud-Native/30daysofIA/tags/fall-for-ia"},{"label":"30-days-of-IA","permalink":"/Cloud-Native/30daysofIA/tags/30-days-of-ia"},{"label":"learn-live","permalink":"/Cloud-Native/30daysofIA/tags/learn-live"},{"label":"hack-together","permalink":"/Cloud-Native/30daysofIA/tags/hack-together"},{"label":"community-buzz","permalink":"/Cloud-Native/30daysofIA/tags/community-buzz"},{"label":"ask-the-expert","permalink":"/Cloud-Native/30daysofIA/tags/ask-the-expert"},{"label":"azure-kubernetes-service","permalink":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service"},{"label":"azure-functions","permalink":"/Cloud-Native/30daysofIA/tags/azure-functions"},{"label":"azure-openai","permalink":"/Cloud-Native/30daysofIA/tags/azure-openai"},{"label":"azure-container-apps","permalink":"/Cloud-Native/30daysofIA/tags/azure-container-apps"},{"label":"azure-cosmos-db","permalink":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db"},{"label":"github-copilot","permalink":"/Cloud-Native/30daysofIA/tags/github-copilot"},{"label":"github-codespaces","permalink":"/Cloud-Native/30daysofIA/tags/github-codespaces"},{"label":"github-actions","permalink":"/Cloud-Native/30daysofIA/tags/github-actions"}],"readingTime":8.59,"hasTruncateMarker":false,"authors":[{"name":"It\'s 30DaysOfIA","title":"FallForIA Content Team","url":"https://azure.github.io/Cloud-Native/Fall-For-IA/","imageURL":"https://azure.github.io/Cloud-Native/img/logo-ms-cloud-native.png","key":"cnteam"}],"frontMatter":{"date":"2023-09-22T09:00","slug":"preparing-the-path-for-intelligent-apps","title":"1-5. Preparing the Path for Intelligent Apps","authors":["cnteam"],"draft":false,"hide_table_of_contents":false,"toc_min_heading_level":2,"toc_max_heading_level":3,"keywords":["Cloud","Data","AI","AI/ML","intelligent apps","cloud-native","30-days","enterprise apps","digital experiences","app modernization","serverless","ai apps","data"],"image":"https://azure.github.io/Cloud-Native/img/ogImage.png","description":"Learn best practices for modernizing your on-premises or IaaS solutions to intelligent apps in the cloud.","tags":["Fall-For-IA","30-days-of-IA","learn-live","hack-together","community-buzz","ask-the-expert","azure-kubernetes-service","azure-functions","azure-openai","azure-container-apps","azure-cosmos-db","github-copilot","github-codespaces","github-actions"]},"prevItem":{"title":"1-6. Cultivating a Culture for Intelligent Apps","permalink":"/Cloud-Native/30daysofIA/cultivating-a-culture-for-intelligent-apps"},"nextItem":{"title":"1-4. How Digital Natives leverage Generative AI","permalink":"/Cloud-Native/30daysofIA/how-digital-natives-leverage-generative-ai"}},"content":"\\n\\n\\n\\n\\n\\n \\n \\n \\n \\n \\n \\n \\n \\n\\n\\n\x3c!-- End METADATA --\x3e\\nLearn best practices for modernizing your on-premises or IaaS solutions to intelligent apps in the cloud.\\n\\n## What We\'ll Cover:\\n\\n * Traditional vs Intelligent Apps\\n * On-Premises/IaaS vs. Cloud-Native Platforms\\n * Modernization strategy for building Intelligent Apps\\n\\n![image of modernizing AI solutions for intelligent apps](../../static/img/fallforia/blogs/2023-09-22/blog-image-1-5.png)\\n\\n## Preparing the Path for Intelligent Apps: Transitioning from On-Premises/IaaS to Cloud-Native Applications\\n\\nThe proliferation of generative AI is paving the way for intelligent applications, software that leverages AI capabilities to deliver unparalleled functionality and user experience. As we covered in a previous article, \u201c[Demystifying Intelligent Applications: Leveraging AI in App Development](https://azure.github.io/Cloud-Native/30daysofIA/demystifying-intelligent-applications),\u201d intelligent Apps aren\u2019t just products of advanced coding. They interact, learn, and evolve thanks to modern AI and machine learning (ML) breakthroughs.\u202f \\n\\nThe paradigm shift from traditional to intelligent apps demands that we change our approach to the technical challenges of software and architecture design. It also necessitates adopting new ways of thinking and operating within an organization, which we explore in greater depth in \u201c[Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management](https://azure.github.io/Cloud-Native/30daysofIA/cultivating-a-culture-for-intelligent-apps).\u201d\u202f \\n\\nThis article explores the technical infrastructure powering intelligent apps, providing a roadmap to transition traditional on-premises or Infrastructure as a Service (IaaS) solutions to intelligent apps deployed to cloud native platforms and services. We\u2019ll discover the differences between conventional and intelligent apps, options for re-architecting existing applications, and critical strategic considerations integral to modernizing the application-building approach.\u202f \\n\\n## Understanding the Shift: Traditional vs. Intelligent Applications\\n\\nIntelligent apps are more than just an incremental evolution from traditional apps. They involve a fundamental shift in software solution design, development, and deployment. A clear understanding of how each paradigm differs is crucial for comprehending the impact of this transition.\u202f \\n\\n### Functionality and User Experience\\n\\nTraditional applications, often rule-based and rigid, rely on preprogrammed operations. A traditional weather app, for example, typically presents structured, location-based forecasts using a predetermined data source. Meanwhile, an intelligent app leverages AI for data-driven decision-making and personalization. Rather than merely displaying the forecast, it integrates ML with additional data sources\u2014like a user\u2019s calendar and fitness data\u2014to determine their favorite outdoor activities, provide relevant clothing suggestions, and even automatically design ideal vacations and times based on the user\u2019s optimal weather preferences.\u202f \\n\\nSimilarly, while traditional applications tend to offer a one-size-fits-all UI, intelligent apps use AI to create tailored interactions. For instance, an intelligent banking app might use generative AI to provide a voice-enabled UI that lets customers use natural language to ask about transactions and receive personalized financial advice.\\n\\n### Infrastructure\\n\\nIntelligent apps integrating these sophisticated features require highly scalable, flexible infrastructure optimized for AI functionality and able to manage large volumes of real-time data. Unlike traditional applications deployed on-premises or in infrastructure as a service (IaaS) environments, intelligent apps demand the flexibility and scalability of cloud native architectures.\\n\\nAn on-premises or IaaS-based e-commerce application may struggle with connecting its data to AI services or scaling to accommodate voluminous real-time data and requests. But an intelligent app built with a cloud native approach and scalability at its core can handle variable demand much more efficiently. It also maintains its infrastructure components on demand, whether for collecting data for AI/ML consumption or user-facing features like AI-enabled customer support and personalization.\\n\\nConsider an intelligent app built on Azure that creates ChatGPT-like experiences with custom data. It might connect [Azure Open AI](https://azure.microsoft.com/en-us/products/ai-services/openai-service?WT.mc_id=javascript-99907-ninarasi) and [Azure Cognitive Search](https://azure.microsoft.com/en-us/products/ai-services/cognitive-search?WT.mc_id=javascript-99907-ninarasi) to an app running on [Azure Container Apps](https://azure.microsoft.com/en-us/products/container-apps/?WT.mc_id=javascript-99907-ninarasi) or [Azure Kubernetes Service](https://azure.microsoft.com/en-us/products/kubernetes-service/?WT.mc_id=javascript-99907-ninarasi).\u202f \\n\\nThe architecture might look like the following diagram based on this [demo app](https://github.com/Azure-Samples/azure-search-openai-demo-csharp):\\n\\n![diagram of a demo app architecture](../../static/img/fallforia/blogs/2023-09-22/diagram-of-a-demo-app-architecture.png)\\n\\nWhile the cloud is well-equipped to handle the challenges associated with shifting to AI-centric infrastructure\u2014handling real-time data processing and storage, integrating and managing AI services, configuring event-driven and microservices architectures, and ensuring security across the distributed environment\u2014re-architecting a traditional app to an intelligent app requires careful planning and execution to ensure it benefits fully from the capabilities of AI and cloud computing.\\n\\n### On-Premises/IaaS vs. Cloud-Native Platforms\\n\\nWhile on-premises and IaaS solutions provide substantial control over data and processes, this fine-grained control comes with setup and maintenance costs and may lack the scalability necessary for AI-driven Intelligent Apps. On-premises hosting involves purchasing and maintaining hardware, including servers and storage systems. IaaS involves leasing virtual hardware and then hiring and training IT personnel for the infrastructure\u2019s operation, maintenance, and security.\\n\\nConversely, infrastructure built on cloud native app services with [Microsoft Azure](https://azure.microsoft.com/?WT.mc_id=javascript-99907-ninarasi) offers virtually unlimited scalability without ongoing hardware and maintenance costs. This feature is ideal for intelligent apps that need to adapt and scale to accommodate fluctuating workloads and data streams. And because these cloud infrastructures typically operate on a pay-as-you-go model, traditionally substantial upfront costs turn into predictable operating expenses, lowering both the cost and risk of launching applications.\\n\\nMoreover, cloud service providers continually update their offerings, so building on sophisticated AI tools and services through the cloud, like the [Azure AI Platform](https://azure.microsoft.com/en-us/solutions/ai/?WT.mc_id=javascript-99907-ninarasi) and orchestration capabilities with [Azure Kubernetes Service](https://azure.microsoft.com/en-us/products/kubernetes-service/?WT.mc_id=javascript-99907-ninarasi), ensures that you have access to the latest AI and most advanced AI models and technologies.\\n\\n### Strategic Considerations for Transitioning to Intelligent Apps\\n\\nWhen developing a strategy for transitioning to intelligent apps, you must consider costs, personnel skill development, and data management.\\n\\nMonolithic architectures are unlikely to support the scale and ease of iteration required by intelligent apps, so you\u2019ll likely need to implement code changes to support the transition. Your chosen hosting solution and the amount of data your app will handle affect cost and performance, in addition to determining privacy and compliance requirements.\\n\\nAn app that manages or stores terabytes or petabytes of data will require storage-optimized virtual machines or a managed database service hosting. Meanwhile, an app that processes video content or performs resource-intensive calculations should prioritize high-performance computing. Maximizing storage and performance can become extraordinarily costly.\\n\\nFinally, existing applications shifting towards intelligent apps may encounter compatibility conflicts, depending on database type or the existing system\u2019s underlying programming language. As you might imagine, a traditional app running on-premises and using a shared local database may need significant restructuring to support a cloud native architecture. You need plans for data migration, potential downtime, and training.\\n\\nNext, let\u2019s look at this transformation process.\\n\\n### Re-Architecting Monolithic Apps into Microservices\\n\\nShifting from [monolithic applications](https://learn.microsoft.com/en-us/dotnet/architecture/containerized-lifecycle/design-develop-containerized-apps/monolithic-applications?WT.mc_id=javascript-99907-ninarasi) to microservices is pivotal for transitioning to Intelligent Apps, and you can use this shift as an opportunity to transform your legacy software into intelligent apps that leverage the flexibility and power of microservices-enabled AI solutions.\\n\\nTraditionally, a monolithic application (let\u2019s use an online marketplace as an example) might handle all activities\u2014from serving webpages to processing payments\u2014within an on-premises server or a cloud server. However, this approach is challenging to scale and remains vulnerable to network failures or cyberattacks.\\n\\nBreaking these applications into multiple services with specific responsibilities, such as authentication or notifications, can overcome these limitations. These services are typically deployed as microservices within container environments like [Azure Kubernetes Service](https://azure.microsoft.com/en-us/products/kubernetes-service/?WT.mc_id=javascript-99907-ninarasi) or serverless platforms like [Azure Container Apps](https://azure.microsoft.com/en-us/products/container-apps?WT.mc_id=javascript-99907-ninarasi) or [Azure Functions](https://azure.microsoft.com/en-us/products/functions/?WT.mc_id=javascript-99907-ninarasi) using automated processes. This approach offers scalability, robustness, and improved performance, as each microservice can run independently and use its own database.\\n\\nShifting towards smaller, independent microservices in app architectures allows us to reap the benefits of AI-powered intelligent apps, for example, by using microservices to handle AI tasks such as processing natural language input, analyzing behavioral data, or personalizing content.\\n\\n:::info\\nWatch [Episode 1](https://aka.ms/learnlive-contoso-app-deconstructed-Ep1) and [Episode 2](https://aka.ms/learnlive-contoso-app-deconstructed-Ep2) of the [Learn Live series](https://aka.ms/contoso-real-estate/learn-live) on how to build, test and deploy an end-to-end intelligent app solution using the [Contoso Real Estate Sample](https://github.com/Azure-Samples/contoso-real-estate).\\n:::\\n\\n### Event-Driven Architectures: Knowing When to Use Them\\n\\n[Event-Driven Architectures](https://learn.microsoft.com/en-us/azure/architecture/guide/architecture-styles/event-driven?WT.mc_id=javascript-99907-ninarasi) (EDAs) can significantly enhance the performance and cost-effectiveness of intelligent apps. EDAs, typically implemented with microservices, respond in real time to events or state changes ranging from user interactions to real-time analytics.\\n\\nConsider an intelligent app designed for personalized customer engagement. Instead of designing the application to check continuously for changes in user data, an EDA can react to such changes immediately, triggering appropriate microservices. For example, if users modify their preferences, an event could trigger an AI-driven recommendation microservice to update its suggestions immediately. This real-time responsiveness, enabled by services like [Azure Event Grid](https://azure.microsoft.com/en-us/products/event-grid/?WT.mc_id=javascript-99907-ninarasi), can improve user experiences and make intelligent apps more adaptive and proactive.\\n\\nHowever, it\u2019s worth noting that while EDAs provide significant benefits, they can introduce unique complexities, such as maintaining event consistency and debugging issues in event chains across multiple microservices. Therefore, it\u2019s important to assess your application\u2019s needs so you can understand where it can benefit from an EDA.\\n\\n## Conclusion\\n\\nTransitioning to intelligent apps represents a shift in strategy, architecture, and infrastructure. By designing and re-architecting for cloud-based infrastructure, your apps gain scalability, cost-effectiveness, robust data management, access to sophisticated AI tools, enhanced security, and reduced operational overhead.\u202f\u202f \\n\\nBut this transition extends well beyond its technical components. Organizations and the people working within them need equal attention. You can explore these facets in greater depth in \u201cCultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management.\u201d\u202f \\n\\nThe power of generative AI is here, and preparing for the move toward cloud-based intelligent applications will ensure that your apps remain performative and scalable and\u2014most importantly\u2014that your business is ready for the newest era of app innovation.\\n\\n## Exercise\\n\\n * Complete the **[Apps Cloud Skills Challenge](https://aka.ms/fallforIA/apps-csc)** to build on your app dev and AI skills.\\n * Complete the **[AI Cloud Skills Challenge](https://aka.ms/fallforIA/ai-csc)** to build on your AI skills.\\n * Register for **[Episode 03](https://aka.ms/learnlive-contoso-app-deconstructed-Ep3)** of the serverless edition Learn Live session to learn how to build, test and deploy an end-to-end intelligent app solution.\\n * Register for **[Ask the Expert: Azure Functions](https://reactor.microsoft.com/en-us/reactor/series/S-1037/)** session for live Q&A with the Product Engineering team on building intelligent serverless apps."},{"id":"how-digital-natives-leverage-generative-ai","metadata":{"permalink":"/Cloud-Native/30daysofIA/how-digital-natives-leverage-generative-ai","source":"@site/blog-30daysofIA/2023-09-21/how-digital-natives-leverage-generative-ai.md","title":"1-4. How Digital Natives leverage Generative AI","description":"Article Description: This article explores how digital natives and Independent Software Vendors (ISVs) can transform their business operations and generate higher value for customers with generative AI and intelligent apps.","date":"2023-09-21T09:00:00.000Z","formattedDate":"September 21, 2023","tags":[{"label":"Fall-For-IA","permalink":"/Cloud-Native/30daysofIA/tags/fall-for-ia"},{"label":"30-days-of-IA","permalink":"/Cloud-Native/30daysofIA/tags/30-days-of-ia"},{"label":"learn-live","permalink":"/Cloud-Native/30daysofIA/tags/learn-live"},{"label":"hack-together","permalink":"/Cloud-Native/30daysofIA/tags/hack-together"},{"label":"community-buzz","permalink":"/Cloud-Native/30daysofIA/tags/community-buzz"},{"label":"ask-the-expert","permalink":"/Cloud-Native/30daysofIA/tags/ask-the-expert"},{"label":"azure-kubernetes-service","permalink":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service"},{"label":"azure-functions","permalink":"/Cloud-Native/30daysofIA/tags/azure-functions"},{"label":"azure-openai","permalink":"/Cloud-Native/30daysofIA/tags/azure-openai"},{"label":"azure-container-apps","permalink":"/Cloud-Native/30daysofIA/tags/azure-container-apps"},{"label":"azure-cosmos-db","permalink":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db"},{"label":"github-copilot","permalink":"/Cloud-Native/30daysofIA/tags/github-copilot"},{"label":"github-codespaces","permalink":"/Cloud-Native/30daysofIA/tags/github-codespaces"},{"label":"github-actions","permalink":"/Cloud-Native/30daysofIA/tags/github-actions"}],"readingTime":9.29,"hasTruncateMarker":false,"authors":[{"name":"It\'s 30DaysOfIA","title":"FallForIA Content Team","url":"https://azure.github.io/Cloud-Native/Fall-For-IA/","imageURL":"https://azure.github.io/Cloud-Native/img/logo-ms-cloud-native.png","key":"cnteam"}],"frontMatter":{"date":"2023-09-21T09:00","slug":"how-digital-natives-leverage-generative-ai","title":"1-4. How Digital Natives leverage Generative AI","authors":["cnteam"],"draft":false,"hide_table_of_contents":false,"toc_min_heading_level":2,"toc_max_heading_level":3,"keywords":["Cloud","Data","AI","AI/ML","intelligent apps","cloud-native","30-days","enterprise apps","digital experiences","app modernization","serverless","ai apps","data"],"image":"https://azure.github.io/Cloud-Native/img/ogImage.png","description":"Article Description: This article explores how digital natives and Independent Software Vendors (ISVs) can transform their business operations and generate higher value for customers with generative AI and intelligent apps.","tags":["Fall-For-IA","30-days-of-IA","learn-live","hack-together","community-buzz","ask-the-expert","azure-kubernetes-service","azure-functions","azure-openai","azure-container-apps","azure-cosmos-db","github-copilot","github-codespaces","github-actions"]},"prevItem":{"title":"1-5. Preparing the Path for Intelligent Apps","permalink":"/Cloud-Native/30daysofIA/preparing-the-path-for-intelligent-apps"},"nextItem":{"title":"1-3. Reimagine App Development with AI","permalink":"/Cloud-Native/30daysofIA/reimagine-app-development-with-ai"}},"content":"\\n\\n\\n\\n\\n\\n \\n \\n \\n \\n \\n \\n \\n \\n\\n\\n\x3c!-- End METADATA --\x3e\\nExplore how digital natives and Independent Software Vendors (ISVs) can transform their business operations and generate higher value for customers with generative AI and intelligent apps.\\n\\n## What We\'ll Cover:\\n\\n * Potential of Generative AI in application development\\n * Challenges with Generative AI\\n * Integrating Generative AI with business operations\\n\\n![image of generative AI and intelligent apps used in business](../../static/img/fallforia/blogs/2023-09-21/blog-image-1.png)\\n\\n## Unleashing Business Transformation: How Digital Natives Can Leverage Generative AI\\n\\nGenerative AI is no longer a technological flight of fancy. It\u2019s here, and it\u2019s rewriting the rules of business innovation.\u202f \\n\\nThis type of AI leverages machine learning algorithms to create new data with similar characteristics to a given dataset. It goes beyond text generation using generative pretrained transformer (GPT) models, covering an array of applications that span from image synthesis to translations and more. It also powers a new class of intelligent applications, which combine AI capabilities with traditional software functions for enhanced user experiences and advanced business solutions.\u202f \\n\\nGenerative AI is transforming how digital natives and independent software vendors (ISVs) operate. Generative AI offers new possibilities for creating unique business value, enhancing customer experiences, and driving growth.\u202f \\n\\nIt plays a critical role in applications, improving functionality, aiding decision-making, and automating complex tasks to deliver dynamic, uniquely personalized user experiences. Generative AI also enables and automates data-driven outcomes, functions, and features.\\n\\nIntegrating AI capabilities into customer-facing apps and turning them into intelligent apps benefits both the customer and the business, as we discussed in [Demystifying Intelligent Applications: Leveraging AI in App Development](https://azure.github.io/Cloud-Native/30daysofIA/demystifying-intelligent-applications). You get faster product development workflows and better business intelligence for sales and marketing, while your users get more natural and context-aware features and interactions.\u202f \\n\\nThat said, to realize generative AI\u2019s potential, you need some strategy and knowledge of how and where it fits into your current business and operations. We\u2019ll examine this technology\u2019s potential to transform business operations, its risks, and rewards, and how to integrate it into your existing operations.\u202f \\n\\n## Unpacking the Potential of Generative AI\\n\\nGenerative AI is creating new opportunities in the digital playing field. It empowers businesses to generate insights, design products, and automate processes in ways and at speeds that were previously unimaginable. You can now predict customer behavior more accurately, offer highly personalized user experiences, and prototype innovative ideas rapidly.\u202f \\n\\nFor example, Microsoft used generative AI to reinvent web search in the new [AI-powered Bing and Edge](https://blogs.microsoft.com/blog/2023/02/07/reinventing-search-with-a-new-ai-powered-microsoft-bing-and-edge-your-copilot-for-the-web/). The search experience has evolved from a solitary user flow of typing in keywords and scrolling through results. It\u2019s now a conversational experience where Bing uses context to offer better quality and more personalized search queries and results.\u202f \\n\\nSimilarly, Microsoft harnessed generative AI to transform its productivity suite into intelligent apps. Through [Copilot for Microsoft 365](https://blogs.microsoft.com/blog/2023/03/16/introducing-microsoft-365-copilot-your-copilot-for-work/), PowerPoint now empowers users to tell their stories by using DALL-E to generate images effortlessly. It also helps rephrase text-heavy slides to be more straightforward. Outlook now assists users to compose more effective emails, enabling better communication.\\n\\nThese are just some examples of how AI powers day-to-day work and operations.\u202f \\n\\nBusinesses that ignore generative AI miss out on untapped market opportunities, letting competitors gain an edge in the digital race. By seeking ways to rethink assumptions and reinvent existing approaches, you can effectively use generative AI to create new opportunities, open products to new use cases and markets, unlock new growth and customer satisfaction levels, and increase your revenue.\\n\\n:::info\\nRegister for the intelligent apps webinar on [Driving Business Value by Modernizing with Cloud-Native & AI](https://info.microsoft.com/ww-landing-driving-business-value-by-modernizing-with-cloud-native-and-ai.html?lcid=en-us?WT.mc_id=javascript-99907-ninarasi) with *Microsoft* and *Forrester* on **September 26**.\\n\\nExplore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries.\u202f \\n:::\\n\\n## The Crucial Role of Generative AI in Business Operations\\n\\nGenerative AI can be a powerful catalyst for business transformation. Let\u2019s quickly tour some ways it can redefine business operations.\u202f\\n\\nProduct development is traditionally a long and painstaking process. Now, tools like [GitHub Copilot](https://github.com/features/copilot) and [TestPilot](https://githubnext.com/projects/testpilot/) use generative AI to make coding and testing software easier, faster, and more robust.\u202f \\n\\nSimilarly, machine learning as a service technology, such as [Azure Machine Learning](https://azure.microsoft.com/en-us/products/machine-learning?WT.mc_id=javascript-99907-ninarasi), democratizes the availability of machine learning. It helps collect, clean, and train models across datasets, making engineering and analyzing data more efficient and accessible.\u202f\u202f \\n\\nGenerative AI also simplifies the process of creating intelligent apps. Previously, simulating thousands of prototypes, predicting their performance, and narrowing down to the most viable options could take weeks, months, or even years of iteration and data analysis. With generative AI, this process can be as quick as days, hours, or perhaps minutes.\u202f \\n\\nCustomer experience is also ripe for transformation. Personalized recommendations drive engagement and sales, and generative AI can scale that personalization to every customer interaction using comprehensive and continually updated data. For instance, Microsoft leverages AI to enable personalization in [Dynamics 365 Customer Insights](https://dynamics.microsoft.com/en-us/ai/customer-insights/). Using this tool, you can ask questions about your customers in natural language and gain valuable and actionable insights into your customer base.\u202f \\n\\nLastly, marketing and sales can benefit from generative AI. For example, identifying current and future key markets and emerging trends helps companies stay ahead of the curve. Tools like [Copilot in Power BI](https://powerbi.microsoft.com/en-us/blog/introducing-microsoft-fabric-and-copilot-in-microsoft-power-bi/) help you quickly understand and uncover insights about your business, enabling your team to craft targeted campaigns, create engaging content, and optimize your business strategy.\u202f \\n\\nLeveraging this full generative AI potential requires the right tools. GPT-4, for instance, can be instrumental in realizing effective business transformation. It excels in understanding context, generating human-like text, and learning from minimal data input, making it a formidable tool in any digital native\'s arsenal. Platforms like [Azure OpenAI Service](https://azure.microsoft.com/en-us/products/ai-services/openai-service?WT.mc_id=javascript-99907-ninarasi) bring GPT-4\u2019s power to your fingertips.\\n\\n## Navigating Challenges in the Generative AI Landscape\\n\\nHowever, as with any powerful technology, it\u2019s unwise to treat generative AI as a perfect solution. You must know the challenges and risks of not leveraging it effectively, including technical and human-factor considerations.\u202f \\n\\nWithout generative AI, your company risks missing the opportunity to please customers via personalization, as discussed previously. You\u2019ll miss sales opportunities via business intelligence insights, limiting your ability to grow revenue and keep up with the competition.\u202f \\n\\nOne challenge of leveraging generative AI\u2019s full potential is finding the right tools. Fortunately, you can implement generative AI in your organization without investing in infrastructure, computational power, and expertise. Platforms like [Azure OpenAI Service](https://azure.microsoft.com/en-us/products/ai-services/openai-service?WT.mc_id=javascript-99907-ninarasi) and [Azure AI Services](https://azure.microsoft.com/en-us/solutions/ai?WT.mc_id=javascript-99907-ninarasi) make AI easily accessible. They also work with Azure compute and data services like\u202f[Azure Functions](https://azure.microsoft.com/en-us/products/functions?WT.mc_id=javascript-99907-ninarasi) and [Azure Kubernetes Service](https://azure.microsoft.com/en-us/products/kubernetes-service/?WT.mc_id=javascript-99907-ninarasi) (AKS), enabling you to build intelligent apps easily and quickly.\u202f \\n\\nHowever, generative AI can\u2019t do everything. It may need human expertise and intervention to use it fully.\u202f \\n\\nFor example, generative AI can create unique data. However, the data may have limited diversity and complexity. The AI may need help recognizing additional considerations or requirements specific to a problem. It may also lack context due to various factors, such as the information\u2019s freshness. Also, since it\u2019s an emerging field that\u2019s [potentially worth $4.4 trillion](https://www.mckinsey.com/capabilities/mckinsey-digital/our-insights/the-economic-potential-of-generative-ai-the-next-productivity-frontier#key-insights), we\u2019re still learning about the nuances and [potential regulatory impact](https://techcrunch.com/2023/01/27/the-current-legal-cases-against-generative-ai-are-just-the-beginning/) of generative AI.\u202f \\n\\nPerhaps the most crucial and challenging aspect of adopting generative AI is an organization\u2019s required cultural shift. Your teams must learn how to use AI as your company fosters an understanding of AI\u2019s potential and limitations.\u202f \\n\\nBegin integrating AI literacy into your company\u2019s DNA. Knowledge of its potential and the readily available tools can inspire your teams to incorporate generative AI into their daily workflows and business operations quickly, improve productivity, and deliver superior customer experiences.\u202f \\n\\nNo company wants to be outpaced by its competitors. You can balance generative AI\u2019s potential with your business operations by staying aware of and adapting to the dynamic AI landscape.\\n\\n## Achieving Harmony: Integrating Generative AI with Existing Business Operations\\n\\nIntegrating generative AI into existing business operations doesn\u2019t have to be disruptive. With the correct strategy, you can do so seamlessly. Your goal should be to augment and enhance your operations, not disrupt and replace.\u202f \\n\\nFor example, Microsoft Cloud technologies leveraged AI to [relieve healthcare organizations\u2019 challenges](https://www.microsoft.com/en-us/industry/blog/healthcare/2023/04/12/microsoft-cloud-for-healthcare-empowering-healthcare-to-deliver-meaningful-outcomes/?WT.mc_id=javascript-99907-ninarasi), such as talent shortage and workforce burnout. Providers automated their workflows, invested in specific applications and connectors, and enhanced support for data and operations.\u202f \\n\\nAzure AI Services, such as [text analytics for health](https://learn.microsoft.com/en-us/azure/ai-services/language-service/text-analytics-for-health/overview?tabs=ner?WT.mc_id=javascript-99907-ninarasi), enable healthcare organizations to understand their largely unstructured and untapped medical data. [Project Health Insights](https://azure.microsoft.com/en-us/blog/announcing-project-health-insights-preview-advancing-ai-for-health-data/?WT.mc_id=javascript-99907-ninarasi) leans on AI to help healthcare professionals gain actionable insights and inferences from patient data.\u202f \\n\\n[Azure Health Bot](https://techcommunity.microsoft.com/t5/healthcare-and-life-sciences/extending-azure-health-bot-with-azure-openai-service/ba-p/3792560), an Intelligent App, empowers organizations to build and deploy legally compliant, powerful conversational healthcare experiences to patients.\u202f \\n\\nA successful AI integration like this begins with understanding. Knowing what generative AI can do and setting realistic expectations can help you identify where and how AI can best serve your operations.\u202f \\n\\nNext, you can foster a culture of AI literacy across teams, speeding up adoption by helping them understand how it can aid their work. The more familiar your team is with AI and its possibilities, the smoother the transition. Training and education programs play a pivotal role here.\u202f \\n\\nFinally, integration requires a systematic approach. It\u2019s not about overhauling everything at once \u2014 it\'s about identifying areas where AI can add the most value, then gradually experimenting and scaling its use. Successful integration stories often feature a phased approach, starting small and growing steadily.\u202f \\n\\nAs we move toward a future where generative AI is a staple in business operations, remember that every journey is unique. Although there\u2019s no one-size-fits-all solution, a strategic approach and a keen understanding of your business needs will help you navigate this new landscape successfully. \\n\\n## Conclusion\\n\\nGenerative AI offers plenty of opportunities for businesses that embrace it. It\u2019s fueling the next generation of intelligent applications, transforming everything from product development to customer engagement to marketing. This technology is the key to unlocking unprecedented growth and innovation.\u202f \\n\\nWhile the journey may have challenges, the potential rewards make generative AI worth exploring. As generative AI advances, [Azure AI](https://azure.microsoft.com/en-us/products/cognitive-services/#overview?WT.mc_id=javascript-99907-ninarasi)\u2019s broad range of tools and services will help you navigate this fascinating field.\\n\\n## Exercise\\n\\n * Watch **[Episode 02](https://aka.ms/learnlive-contoso-app-deconstructed-Ep2)** of the serverless edition Learn Live session to learn how to build, test and deploy an end-to-end intelligent app solution.\\n * Complete the **[Cloud Skills Challenge](https://aka.ms/fallforIA/ai-csc)** to build on your AI skills.\\n * Register for **[Ask the Expert: Azure Functions](https://aka.ms/FallForIA/ATE)** on September 26 for a live Q&A with the Product Team on how to build AI apps using serverless computing."},{"id":"reimagine-app-development-with-ai","metadata":{"permalink":"/Cloud-Native/30daysofIA/reimagine-app-development-with-ai","source":"@site/blog-30daysofIA/2023-09-20/reimagine-app-development-with-ai.md","title":"1-3. Reimagine App Development with AI","description":"In the part of our series on intelligent apps, we\u2019ll explore how AI is transforming application development, from design and architecture to building.","date":"2023-09-20T09:00:00.000Z","formattedDate":"September 20, 2023","tags":[{"label":"Fall-For-IA","permalink":"/Cloud-Native/30daysofIA/tags/fall-for-ia"},{"label":"30-days-of-IA","permalink":"/Cloud-Native/30daysofIA/tags/30-days-of-ia"},{"label":"learn-live","permalink":"/Cloud-Native/30daysofIA/tags/learn-live"},{"label":"hack-together","permalink":"/Cloud-Native/30daysofIA/tags/hack-together"},{"label":"community-buzz","permalink":"/Cloud-Native/30daysofIA/tags/community-buzz"},{"label":"ask-the-expert","permalink":"/Cloud-Native/30daysofIA/tags/ask-the-expert"},{"label":"azure-kubernetes-service","permalink":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service"},{"label":"azure-functions","permalink":"/Cloud-Native/30daysofIA/tags/azure-functions"},{"label":"azure-openai","permalink":"/Cloud-Native/30daysofIA/tags/azure-openai"},{"label":"azure-container-apps","permalink":"/Cloud-Native/30daysofIA/tags/azure-container-apps"},{"label":"azure-cosmos-db","permalink":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db"},{"label":"github-copilot","permalink":"/Cloud-Native/30daysofIA/tags/github-copilot"},{"label":"github-codespaces","permalink":"/Cloud-Native/30daysofIA/tags/github-codespaces"},{"label":"github-actions","permalink":"/Cloud-Native/30daysofIA/tags/github-actions"}],"readingTime":8.57,"hasTruncateMarker":false,"authors":[{"name":"It\'s 30DaysOfIA","title":"FallForIA Content Team","url":"https://azure.github.io/Cloud-Native/Fall-For-IA/","imageURL":"https://azure.github.io/Cloud-Native/img/logo-ms-cloud-native.png","key":"cnteam"}],"frontMatter":{"date":"2023-09-20T09:00","slug":"reimagine-app-development-with-ai","title":"1-3. Reimagine App Development with AI","authors":["cnteam"],"draft":false,"hide_table_of_contents":false,"toc_min_heading_level":2,"toc_max_heading_level":3,"keywords":["Cloud","Data","AI","AI/ML","intelligent apps","cloud-native","30-days","enterprise apps","digital experiences","app modernization","serverless","ai apps","data"],"image":"https://azure.github.io/Cloud-Native/img/ogImage.png","description":"In the part of our series on intelligent apps, we\u2019ll explore how AI is transforming application development, from design and architecture to building.","tags":["Fall-For-IA","30-days-of-IA","learn-live","hack-together","community-buzz","ask-the-expert","azure-kubernetes-service","azure-functions","azure-openai","azure-container-apps","azure-cosmos-db","github-copilot","github-codespaces","github-actions"]},"prevItem":{"title":"1-4. How Digital Natives leverage Generative AI","permalink":"/Cloud-Native/30daysofIA/how-digital-natives-leverage-generative-ai"},"nextItem":{"title":"1-2. Harnessing the Power of Intelligent Apps","permalink":"/Cloud-Native/30daysofIA/harnessing-the-power-of-intelligent-apps"}},"content":"\\n\\n\\n\\n\\n\\n \\n \\n \\n \\n \\n \\n \\n \\n\\n\\n\x3c!-- End METADATA --\x3e\\nIn the part of our series on intelligent apps, we\u2019ll explore how AI is transforming application development, from design and architecture to building.\\n\\n## What We\'ll Cover:\\n\\n * Revolutionizing application development using AI\\n * Infusing AI in application design architecture\\n * AI Assisted Pair Programming\\n * Future of App Development\\n\\n![A decorative image of application development with AI](../../static/img/fallforia/blogs/2023-09-20/blog-image-1.png)\\n\\n## Reimagining Application Development With AI: A New Paradigm\\n\\nArtificial intelligence (AI) is impacting every industry. AI-powered development tools offer new functionalities and assist software developers throughout the development process, revolutionizing app development. From initial design and architecture to coding and debugging, AI reshapes how we approach application conception, creation, and iteration.\\n\\nIn part one of this series, \u201c[Demystifying Intelligent Applications: Leveraging AI in App Development](https://azure.github.io/Cloud-Native/30daysofIA/demystifying-intelligent-applications),\u201d we explored the new breed of intelligent apps and how they use AI. In this article, we\u2019ll see how AI technologies enhance developer toolkits and shape the future of app development, particularly through AI-enabled coding assistance and debugging.\\n\\n## The Dawn of Intelligent Apps: Revolutionizing Application Development\\n\\nAI is more than just another addition to the development toolbox\u2014it represents a revolution bringing unprecedented efficiency, innovation, and user engagement to our applications.\u202f \\n\\nWhat sets intelligent apps apart is how they can adapt and learn, helping us deliver unique, real-time user experiences.\u202f\u202f \\n\\nOne example is an AI-driven health application that can track biometric data, predict potential health issues, provide personalized health advice, and even guide emergency services during a medical crisis. An AI-enabled language learning app that adapts to our learning speeds, recognizes our problem areas, and customizes its content to help us improve our language proficiencies is another example.\u202f\u202f \\n\\nIntelligent apps like these don\u2019t just use data for recommendations\u2014they can make critical decisions and provide sophisticated services that were previously impossible.\u202f \\n\\nFor developers, intelligent apps mean we\u2019re no longer just coding\u2014we\u2019re teaching applications how to think, adapt, and make decisions. AI is reshaping every step of the development journey, helping us to build more intuitive and user-focused products.\u202f \\n\\nNext, we\u2019ll examine the impact of AI on application design and architecture and how AI-assisted \u201cpair programming\u201d changes software development.\\n\\n:::info\\n**Register** for the *FREE* [webinar on Intelligent Apps](https://info.microsoft.com/ww-landing-driving-business-value-by-modernizing-with-cloud-native-and-ai.html?lcid=en-us&WT.mc_id=javascript-99907-ninarasi) with Microsoft and Forrester on September 26 to explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries. \\n\\nThere will be a showcase of real-world use cases that demonstrate how AI can be seamlessly integrated into cloud-native environments driving tangible business value.\\n:::\\n\\n## AI in Design and Architecture: Fueling Creativity and Efficiency\\n\\nAI technologies help developers automate and streamline their processes and provide new ways to think about design.\u202f\u202f \\n\\nIn traditional application development, the design and architecture phase can take significant time, effort, and expertise. For instance, adopting a monolithic architecture or a microservices-based design can significantly increase coding, testing, and debugging complexity. The app may even behave differently when hosted locally than in a deployed environment. It may take hours to track down an issue manually\u2014time developers could spend elsewhere.\u202f \\n\\nConversely, we can generate numerous design iterations in seconds with AI, each optimized to fulfill specific user needs and business goals. AI tools can quickly model various scenarios\' impacts and software design patterns in the architectural design phase. It provides insights into performance and potential issues and suggests improvements. AI can also generate skeleton code, allowing developers to prototype different solutions or test specific components and technologies rapidly. By reviewing thousands of lines of code in seconds, AI allows us to focus on creating apps that serve users through feature development instead of searching for bugs.\u202f\u202f \\n\\nIntegrating AI into the design and architecture phase can even extend our creative capabilities. By leveraging the power of generative AI\u2014like large language models (LLMs)\u2014and their broad knowledge and context going beyond software, we can better brainstorm apps and feature ideas. We can go beyond code and software architecture and get ideas about user experience, logo and branding, or marketing concepts. And as AI tools deliver insights and ideas we would have never considered; it encourages more innovation.\\n\\n:::info\\nWatch [Episode 01](https://aka.ms/learnlive-contoso-app-deconstructed-Ep1) of the Learn Live session to learn how to build an end-to-end intelligent app solution architecture.\\n:::\\n\\n## The Power of AI-Assisted Pair Programming in Building Applications\\n\\nAI-assisted pair programming is a collaborative coding approach where an AI system\u2014like [GitHub Copilot](https://github.com/features/copilot) or [TestPilot](https://githubnext.com/projects/testpilot/)\u2014assists developers during coding. It\u2019s an increasingly common approach that significantly impacts developer productivity. In fact, [GitHub Copilot](https://github.com/features/copilot) is now behind an [average of 46 percent of developers\u2019 code](https://github.blog/2023-02-14-github-copilot-now-has-a-better-ai-model-and-new-capabilities/) and users are seeing [55 percent faster task completion](https://github.blog/2022-09-07-research-quantifying-github-copilots-impact-on-developer-productivity-and-happiness/) on average.\u202f \\n\\nFor new software developers, or those interested in learning new skills, AI-assisted pair programming are training wheels for coding. With the benefits of code snippet suggestions, developers can avoid struggling with beginner pitfalls like language syntax. Tools like ChatGPT can act as a personal, on-demand tutor\u2014answering questions, generating code samples, and explaining complex code syntax and logic. These tools dramatically speed the learning process and help developers gain confidence in their coding abilities.\u202f \\n\\nBuilding applications with AI tools hastens development and provides more robust code. For example, GitHub Copilot helps us write new code accurately. It also quickly makes sense of existing code, suggests whole new lines or blocks of code, and even detects errors and proposes fixes. It\u2019s like having a second coder working alongside us\u2014one who never tires and is trained on billions of lines of code.\u202f \\n\\nHowever, it\u2019s also important to acknowledge the limitations of AI. While generative AI-assistance technologies like GitHub Copilot are impressive, they\u2019re still imperfect. They can sometimes produce erroneous, non-functional, or vulnerable code. They can also lack nuanced, domain-specific knowledge.\u202f\u202f \\n\\nSo, although these tools are helpful, we can\u2019t rely on them fully\u2014these are programming aids rather than definitive solutions. AI-assisted pair programming is about augmenting the human developer\u2019s skills, not replacing them.\\n\\n## Debugging and Improving Code With AI: Enhancing Application Quality\\n\\nIdentifying and correcting code errors is often time-consuming and challenging for developers. AI streamlines the debugging process by automatically detecting anomalies, suggesting fixes, and learning from previous bugs to prevent them from recurring.\u202f \\n\\nWe can use generative AI to create unit tests from code snippets, from simple parameter checking and input validation to complex mocks of database and network services. Automated AI testing improves code coverage and robustness. It saves significant time compared to manually crafting unit tests.\u202f \\n\\nAI also revolutionizes code reviews by scanning and highlighting potential issues\u2014like code patterns leading to a bug or a coding standards violation\u2014and suggesting ways to simplify or refactor the code for readability and performance. This reduces human error and makes the code review process more efficient. Its benefits extend to small teams, where the availability of developers is crucial to deadlines, and large teams, where effective collaboration depends on readability and code consistency. AI further saves time by triaging issues so developers can prioritize the most mission-critical ones.\u202f \\n\\nAside from improving code quality, AI is critical to understanding system behavior. Intelligent AI-assisted software development tools can monitor and audit system behavior, giving developers a better insight into how their code performs in different scenarios. AI can track patterns, identify inefficiencies, and provide alerts when they detect unusual behavior\u2014enabling developers to enhance their code further.\u202f \\n\\nBy predicting and identifying potential vulnerabilities, intelligent AI-assisted coding apps like [Microsoft Security Copilot](https://www.microsoft.com/en-us/security/business/ai-machine-learning/microsoft-security-copilot?WT.mc_id=javascript-99907-ninarasi) are pivotal in ensuring application functionality, reliability, and security. These AI tools are continually trained and upgraded with large codebases and user feedback that refines their responses. This ability to learn from past mistakes makes AI an incredibly powerful tool that will continue to expand its capabilities in the future.\\n\\n:::info\\nComplete the [AI Cloud Skills Challenge](https://aka.ms/FallForIA/ai-csc) to build on you AI application development skills with paired programming using generative AI and GitHub Copilot.\\n:::\\n\\n## AI and the Future of Application Development\\n\\nThe rapid advancements in AI and machine learning technologies point to an exciting future for application development.\u202f \\n\\nIn \u201c[The Future of Applications](https://mikecann.co.uk/posts/the-future-of-applications),\u201d software developer Mike Cann illustrates how LLMs might revolutionize application development. Cann that suggests fully-autonomous coding\u2014AI-generated applications created entirely from prompts\u2014may supersede traditional hand-crafted applications. Dynamically generated user interfaces could offer personalized and customizable experiences while we can design APIs for machine consumption and efficiency rather than human readability. Furthermore, we can store data in less structured, more adaptable, and flexible forms.\u202f\\n\\nAI is already making application development more accessible. With AI handling the complex, technical aspects of development, less-technical individuals can take a greater role in building applications. Consequently, AI can potentially democratize software engineering.\u202f \\n\\nIntelligent apps will continue to evolve, becoming more efficient, adaptable, and capable of learning from user interactions. Similarly, AI will inevitably become integral in every stage of app development.\\n\\n## Summary\\n\\nAI has already created a new development paradigm\u2014one in which machine intelligence and human creativity come together to produce innovative, efficient, and robust applications. AI already assists us at every stage of app development, from design and architecture to coding and debugging.\u202f \\n\\nDevelopers who embrace AI tools and integrate them into their processes can enhance their productivity, improve the quality of their applications, and expand the possibilities of their application development.\u202f\u202f \\n\\nThe future of development is automated and AI-driven, and it\u2019s here today. Getting started is easy\u2014try an AI-powered development tool like GitHub Copilot to see how AI-assisted pair programming can enhance your productivity and code quality.\\n\\n## Exercise\\n\\n * **Watch** the \u201c[Develop and Deconstruct](https://aka.ms/learnlive-contoso-app-deconstructed-Ep2)\u201d episode of our Learn Live series, a live guided experience on building end-to-end intelligent apps architecture with SMEs from Microsoft.\\n * **Complete** the [Fall For Intelligent Apps Skills Challenges](https://aka.ms/FallForIA/csc) \u2013 ends on October 31!"},{"id":"harnessing-the-power-of-intelligent-apps","metadata":{"permalink":"/Cloud-Native/30daysofIA/harnessing-the-power-of-intelligent-apps","source":"@site/blog-30daysofIA/2023-09-19/harnessing-the-power-of-intelligent-apps.md","title":"1-2. Harnessing the Power of Intelligent Apps","description":"This article explores real-world examples of how Intelligent Apps revolutionize logistics, retail, finance, manufacturing, tech, and medicine.","date":"2023-09-19T09:00:00.000Z","formattedDate":"September 19, 2023","tags":[{"label":"Fall-For-IA","permalink":"/Cloud-Native/30daysofIA/tags/fall-for-ia"},{"label":"30-days-of-IA","permalink":"/Cloud-Native/30daysofIA/tags/30-days-of-ia"},{"label":"learn-live","permalink":"/Cloud-Native/30daysofIA/tags/learn-live"},{"label":"hack-together","permalink":"/Cloud-Native/30daysofIA/tags/hack-together"},{"label":"community-buzz","permalink":"/Cloud-Native/30daysofIA/tags/community-buzz"},{"label":"ask-the-expert","permalink":"/Cloud-Native/30daysofIA/tags/ask-the-expert"},{"label":"azure-kubernetes-service","permalink":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service"},{"label":"azure-functions","permalink":"/Cloud-Native/30daysofIA/tags/azure-functions"},{"label":"azure-openai","permalink":"/Cloud-Native/30daysofIA/tags/azure-openai"},{"label":"azure-container-apps","permalink":"/Cloud-Native/30daysofIA/tags/azure-container-apps"},{"label":"azure-cosmos-db","permalink":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db"},{"label":"github-copilot","permalink":"/Cloud-Native/30daysofIA/tags/github-copilot"},{"label":"github-codespaces","permalink":"/Cloud-Native/30daysofIA/tags/github-codespaces"},{"label":"github-actions","permalink":"/Cloud-Native/30daysofIA/tags/github-actions"}],"readingTime":9.945,"hasTruncateMarker":false,"authors":[{"name":"It\'s 30DaysOfIA","title":"FallForIA Content Team","url":"https://azure.github.io/Cloud-Native/Fall-For-IA/","imageURL":"https://azure.github.io/Cloud-Native/img/logo-ms-cloud-native.png","key":"cnteam"}],"frontMatter":{"date":"2023-09-19T09:00","slug":"harnessing-the-power-of-intelligent-apps","title":"1-2. Harnessing the Power of Intelligent Apps","authors":["cnteam"],"draft":false,"hide_table_of_contents":false,"toc_min_heading_level":2,"toc_max_heading_level":3,"keywords":["Cloud","Data","AI","AI/ML","intelligent apps","cloud-native","30-days","enterprise apps","digital experiences","app modernization","serverless","ai apps","data"],"image":"https://azure.github.io/Cloud-Native/img/ogImage.png","description":"This article explores real-world examples of how Intelligent Apps revolutionize logistics, retail, finance, manufacturing, tech, and medicine.","tags":["Fall-For-IA","30-days-of-IA","learn-live","hack-together","community-buzz","ask-the-expert","azure-kubernetes-service","azure-functions","azure-openai","azure-container-apps","azure-cosmos-db","github-copilot","github-codespaces","github-actions"]},"prevItem":{"title":"1-3. Reimagine App Development with AI","permalink":"/Cloud-Native/30daysofIA/reimagine-app-development-with-ai"},"nextItem":{"title":"1-1. Demystifying Intelligent Applications","permalink":"/Cloud-Native/30daysofIA/demystifying-intelligent-applications"}},"content":"\\n\\n\\n\\n\\n\\n \\n \\n \\n \\n \\n \\n \\n \\n\\n\\n\x3c!-- End METADATA --\x3e\\n\\nExplore real-world examples of how **Intelligent Apps** revolutionize *logistics*, *retail*, *finance*, *manufacturing*, *tech*, and *medicine*.\\n\\n## What We\'ll Cover:\\n\\n * Real-world businesses revolutionizing operations with Intelligent Apps\\n * High level architecture for industry scenarios\\n * Implementation architecture overview across industries\\n\\n![A decorative header image for the blog](../../static/img/fallforia/blogs/2023-09-19/blog-image-1.png)\\n\\n## Real-world Success Stories\\n\\nAccording to IBM\u2019s 2022 [Global AI Adoption Index](https://www.ibm.com/downloads/cas/GVAGA3JP?WT.mc_id=javascript-99907-ninarasi), 35% of surveyed companies already use AI in their business, and 42% are exploring the idea. Organizations lagging behind the AI curve will quickly fall behind early adopters, who are already enjoying its benefits and boosting their investment in this technology.\\n\\nIn the first part of this series, [\u201cDemystifying Intelligent Applications: Leveraging AI in App Development,\u201d](https://azure.github.io/Cloud-Native/30daysofIA/demystifying-intelligent-applications) we explored how organizations can use AI-powered apps, or intelligent apps, to transform their operations, decision-making, and customer experiences, leaping ahead of the competition.\\n\\nIntelligent apps significantly improve operational efficiency and customer engagement across industries like logistics, retail, finance, manufacturing, tech, and healthcare. In this article, we\u2019ll spotlight real-world success stories of businesses revolutionizing operations with intelligent apps using Microsoft Azure\u2019s app, data, and AI services.\\n\\n:::info\\n**Register** for the [Learn Live](https://aka.ms/contoso-real-estate/learn-live?WT.mc_id=javascript-99907-ninarasi) on **September 21** for a live guided experience with SMEs on how to build end-to-end solution architecture for real world implementation of Intelligent Apps.\\n:::\\n\\n## Streamlining Operations: Intelligent Apps in the Airline Industry\\n\\nAerospace pioneer Airbus [leveraged intelligent apps to streamline its operations and innovate its services](https://customers.microsoft.com/en-us/story/858578-airbus-defense-and-intelligence-azure?WT.mc_id=javascript-99907-ninarasi) with two ground-breaking modernizations that use Azure apps, data, and AI services to reimagine pilot training and predict aircraft maintenance issues.\u202f \\n\\nAviation technology\u2019s rapid evolution compelled Airbus to reconceive their approach to training pilots. The aircraft manufacturer created a pilot training chatbot by combining [Azure AI Services](https://azure.microsoft.com/en-us/products/ai-services?WT.mc_id=javascript-99907-ninarasi) with [Speech to Text](https://azure.microsoft.com/en-us/products/ai-services/speech-to-text?WT.mc_id=javascript-99907-ninarasi) and [Text to Speech](https://azure.microsoft.com/en-us/products/ai-services/text-to-speech?WT.mc_id=javascript-99907-ninarasi). The bot helps trainee pilots navigate over 6,000 pages of technical information. Pilots can ask questions verbally for quick answers, aiding their understanding of dense information and speeding up training. And when Airbus needed to accommodate its military consumers, deploying its Kubernetes cluster to a disconnected, \u201cair-gapped\u201d environment met the strict security requirements of military aircraft training.\\n\\nA chatbot could look like the following diagram as Azure services work together to create a conversational experience. Its components could include security and governance, bot logic and user experience (UX), bot cognition and intelligence, data extract, transfer, and load (ETL) operations, quality assurance and enhancements, logging, and monitoring and reporting.\\n\\n![A diagram of a conversational experience enabled by Azure services](../../static/img/fallforia/blogs/2023-09-19/blog-image-2.png)\\n\\nAirbus also used AI to improve its aircraft maintenance and safety. They deployed [AI Anomaly Detector](https://azure.microsoft.com/en-us/products/ai-services/ai-anomaly-detector?WT.mc_id=javascript-99907-ninarasi) to gather and analyze telemetry data from multiple flights, enabling more profound insights into wear and tear on military aircraft operating in harsh conditions. This AI application helps Airbus better understand each airplane\u2019s health. Airbus can predict and fix potential problems before they occur, improving the aircraft\u2019s safety and operational readiness.\\n\\nAI and Azure are changing how organizations like Airbus operate, even in strict, highly regulated markets with stringent security requirements. And by streamlining pilot training and staying ahead of aircraft maintenance, Airbus helps its military and commercial clients reliably deliver their goods across the globe, maintaining their critical supply chains.\\n\\n## Boosting Customer Service: Intelligent Apps in the Retail Industry\\n\\nThe retail industry has been among the first to adopt and benefit from intelligent apps. For example, the leading American used car retailer, CarMax, [uses intelligent apps to transform the car shopping experience](https://customers.microsoft.com/en-us/story/1501304071775762777-carmax-retailer-azure-openai-service?WT.mc_id=javascript-99907-ninarasi).\\n\\nBefore embracing AI, CarMax faced a significant challenge: Manually creating text summaries was time-consuming and labor-intensive. CarMax decided to leverage [Azure OpenAI Service](https://azure.microsoft.com/en-us/products/ai-services/openai-service?WT.mc_id=javascript-99907-ninarasi) to automatically generate car research pages, offering customers valuable insights while enhancing the website\u2019s search engine rankings.\\n\\nThis AI-driven approach has significantly streamlined the process, achieving in a few months what would have taken years manually. Also, the AI system condenses thousands of customer reviews into a few readable sentences, offering potential buyers a quick overview of others\u2019 experiences.\\n\\nCarMax has improved their customer experience while boosting operational efficiency through AI adoption. CarMax\u2019s editorial staff focuses on creating strategic, longer-form content with the time saved, enhancing productivity, and driving more website traffic.\\n\\nCarMax\u2019s success story serves as a testament to the potential of intelligent apps in the retail industry.\\n\\n## Enhancing Cybersecurity: Intelligent Apps in Finance\\n\\nIntelligent apps also impact the highly regulated financial services sector. Swift, a leading infrastructure provider for financial messaging services, [uses intelligent apps to enhance decision-making and combat financial crime](https://customers.microsoft.com/en-us/story/1637929534319366070-swift-banking-capital-markets-azure-machine-learning?WT.mc_id=javascript-99907-ninarasi).\\n\\nThe massive growth of cross-border transactions and instant payment networks led to a surge in financial fraud that can cost the industry hundreds of billions of dollars every year. So, Swift and Microsoft partnered to build an anomaly detection model using [federated learning techniques](https://techcommunity.microsoft.com/t5/ai-machine-learning-blog/federated-learning-with-azure-machine-learning-powering-privacy/ba-p/3824720) with [Azure Machine Learning](https://azure.microsoft.com/en-us/products/machine-learning/) and [Azure confidential computing](https://azure.microsoft.com/en-us/solutions/confidential-compute/).\\n\\n[Microsoft Purview](https://azure.microsoft.com/en-us/products/purview/?WT.mc_id=javascript-99907-ninarasi) helped govern data, as the federated learning method partially trained each model in a silo before aggregating them. This approach enabled Swift and Microsoft to develop a highly accurate model without copying or moving data from Swift members\u2019 secure locations, ensuring the highest level of security and privacy.\\n\\nFinancial organizations can now deploy this trained ML model to run a real-time fraud detection system, lowering the cost of financial fraud.\\n\\nOrganizations can deploy a fraud detection system like the following example on Microsoft Azure. The system ingests data from streams or storage, runs analytics, and uses ML to detect fraud and store results. It then alerts users and other systems to the security issue.\\n\\n![A diagram of an example fraud detection system](../../static/img/fallforia/blogs/2023-09-19/blog-image-3.png)\\n\\nSwift provides the innovative model to its banking partners, who can share data to further train the model. Azure confidential computing securely runs new models on current data to accumulate insights, generating an ongoing learning cycle. This iterative approach strengthens the global detection of fraudulent financial transactions through an increasingly faster and more accurate model.\u202f \\n\\nThe collaboration between Swift and Microsoft demonstrates how combining various Azure services creates Intelligent Apps to enhance decision-making in finance. These apps solve real problems at an unprecedented global scale.\u202f \\n\\n## Enhancing Efficiency: Intelligent Apps in Manufacturing\\n\\n3M, known for Post-it Notes and other innovative products, [wielded the power of Intelligent Apps to standardize, automate, and accelerate its sales forecasting](https://customers.microsoft.com/en-us/story/1504342377134122633-3M-manufacturing-azure-machine-learning?WT.mc_id=javascript-99907-ninarasi). The company needed a unified, automated approach to replace its multiple manual methods.\\n\\nIt adopted Azure Machine Learning and other Azure AI Services to develop, train, and implement 1,500 custom models for each region and division, replacing manual methods. This solution delivers timely finance, sales, and marketing insights while freeing teams from time-consuming manual data manipulation tasks.\\n\\n3M used Azure Machine Learning, [automated machine learning](https://azure.microsoft.com/en-us/products/machine-learning/automatedml/) (AutoML), and the [Many Models Solution Accelerator](https://github.com/microsoft/solution-accelerator-many-models) to train and score numerous machine learning models in parallel. This approach significantly shortened the company\u2019s development cycle time.\\n\\nThen, 3M integrated [Microsoft Power BI](https://powerbi.microsoft.com/en-us/?WT.mc_id=javascript-99907-ninarasi) into their solution to compile and visualize results more effectively. Executives now have an unbiased and unfiltered view of data for better decision-making. With its improved ability to forecast sales, 3M can focus on manufacturing the products it needs efficiently.\\n\\n3M\u2019s success demonstrates how combining various Azure services can enhance efficiency in the manufacturing industry. These apps improve operational processes while enabling employees to focus on higher-level tasks. Strategic decisions are more targeted, and business conversations are more meaningful.\\n\\n## Driving Innovation: Intelligent Apps in the Technology Industry\\n\\nAs expected, the tech industry is at the forefront of leveraging Intelligent Apps to drive innovation. Elastic [saw an opportunity to use intelligent apps to optimize its search services and meet customer needs more effectively](https://customers.microsoft.com/en-us/story/1653495116803202350-elastic-partner-professional-services-azure-openai-service?WT.mc_id=javascript-99907-ninarasi). The company built a managed, secure, and scalable solution for its customers, Elastic Cloud, by leveraging [Microsoft Azure](https://azure.microsoft.com/en-us?WT.mc_id=javascript-99907-ninarasi).\\n\\nElastic Cloud uses Azure Machine Learning and other Azure AI Services to automate many basic management tasks, such as backing up data, upgrading, and scaling deployments. This approach enables Elastic\u2019s customers to focus less on managing the software and more on serving their own clients. Customers can quickly deploy and manage Elastic Cloud on Kubernetes (ECK) using [Azure Kubernetes Service](https://azure.microsoft.com/en-us/products/kubernetes-service/?WT.mc_id=javascript-99907-ninarasi).\\n\\nAs a result, Elastic\u2019s cloud business is growing twice as fast as its on-premises solutions. Enterprise search users have reported significant improvements in system availability and employee satisfaction.\\n\\nAdditionally, Elastic incorporated Azure OpenAI Service into its Elasticsearch Relevance Engine (ESRE), providing more relevant answers to search queries. ESRE enables text, vector, and hybrid search, offering a more effective and accurate response to queries.\\n\\nElastic\u2019s success story demonstrates how combining various Azure services can create intelligent Apps to drive innovation in the tech industry. These apps help improve operational processes and deliver better results quickly.\\n\\n## Revolutionizing Healthcare: Intelligent Apps in Medicine\\n\\nIntelligent apps also drive transformative changes in the healthcare sector. Cambridgeshire and Peterborough NHS Foundation Trust, a healthcare provider in the United Kingdom, gradually shifted their on-premises systems to the cloud using Azure, improving flexibility, and lowering costs. They needed a solution for their electronic patient record (EPR) system that integrates 23 years of data across multiple systems and formats.\\n\\nThe Trust used Microsoft Azure and [Azure Cognitive Search](https://azure.microsoft.com/en-us/products/ai-services/cognitive-search?WT.mc_id=javascript-99907-ninarasi), including semantic search for understanding intent and contexts and cognitive capabilities for extracting insights from data, to [standardize and automate data management](https://customers.microsoft.com/en-us/story/1612121636839251558-cpft-health-provider-azure-en-united-kingdom?WT.mc_id=javascript-99907-ninarasi). This approach made legacy, archive, and live data more discoverable, saving clinicians\u2019 time. The Trust\u2019s solution searches through millions of clinical documents in seconds, even recognizing handwritten notes without needing a separate optical character recognition (OCR) solution to extract text from images (Azure Cognitive Search automatically does it). Clinicians can now uncover previously inaccessible insights, enabling them to offer more personalized care.\\n\\nThe Trust also used [Integration Services](https://azure.microsoft.com/en-us/products/category/integration/?WT.mc_id=javascript-99907-ninarasi) tools such as [Azure Logic Apps](https://azure.microsoft.com/en-us/products/logic-apps/?WT.mc_id=javascript-99907-ninarasi) to create workflows without writing code and [Event Grid](https://azure.microsoft.com/en-us/products/event-grid/?WT.mc_id=javascript-99907-ninarasi) to connect Azure and third-party services through a [publisher-subscriber model](https://learn.microsoft.com/en-us/azure/architecture/patterns/publisher-subscriber?WT.mc_id=javascript-99907-ninarasi). They switched on a live feed from their new EPR system to Azure to feed real-time information into other applications. Researchers can now access anonymized data to fuel medical innovations beyond the NHS.\\n\\nThe healthcare provider also invested heavily in Power BI\u2019s data visualization and analytical functions to sift through massive amounts of information. This level of data accessibility was previously impossible with their on-premises system. The new insights have significantly improved clinicians\u2019 efficiency for better patient care.\\n\\nData accessibility and security are critical in the healthcare industry. Intelligent Apps empower organizations like the Trust to revolutionize medical care. AI capabilities helped the Trust improve its operational efficiency and user experiences, setting a new standard for healthcare in the digital age.\\n\\n## Summary\\n\\nAI is driving transformation and real-world successes across industries. Intelligent apps are reshaping our world by streamlining operations in manufacturing and logistics, enhancing customer experience in the retail sector, improving decision-making in finance, driving innovation in tech, and revolutionizing healthcare.\\n\\nAs organizations adopt AI, they\u2019re innovating in ways that were once too difficult, expensive, or outright impossible. Companies that wait to embrace intelligent apps risk the chance of falling behind. By embracing intelligent apps and leveraging the capabilities of Azure, you can harness the transformative potential of AI to remain on the cutting edge of technology.\\n\\n## Exercise\\n\\n * **Complete** the [Intelligent Apps Cloud Skills Challenge](https://aka.ms/fallforIA/apps-csc) to build on your to build your fundamentals for AI app development.\\n * **Watch** [Episode 01](https://aka.ms/learnlive-contoso-app-deconstructed-Ep1)\u202fof the Serverless Edition Learn Live session to learn how to build an end-to-end intelligent app solution.\\n * **Register** for the [Intelligent Apps webinar](https://info.microsoft.com/ww-landing-driving-business-value-by-modernizing-with-cloud-native-and-ai.html?lcid=en-us?WT.mc_id=javascript-99907-ninarasi) with Microsoft and Forrester."},{"id":"demystifying-intelligent-applications","metadata":{"permalink":"/Cloud-Native/30daysofIA/demystifying-intelligent-applications","source":"@site/blog-30daysofIA/2023-09-18/demystifying-intelligent-applications.md","title":"1-1. Demystifying Intelligent Applications","description":"This article explores the concept of intelligent applications for readers, providing a clear understanding of the role of AI capabilities in modern applications.","date":"2023-09-18T09:00:00.000Z","formattedDate":"September 18, 2023","tags":[{"label":"Fall-For-IA","permalink":"/Cloud-Native/30daysofIA/tags/fall-for-ia"},{"label":"30-days-of-IA","permalink":"/Cloud-Native/30daysofIA/tags/30-days-of-ia"},{"label":"learn-live","permalink":"/Cloud-Native/30daysofIA/tags/learn-live"},{"label":"hack-together","permalink":"/Cloud-Native/30daysofIA/tags/hack-together"},{"label":"community-buzz","permalink":"/Cloud-Native/30daysofIA/tags/community-buzz"},{"label":"ask-the-expert","permalink":"/Cloud-Native/30daysofIA/tags/ask-the-expert"},{"label":"azure-kubernetes-service","permalink":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service"},{"label":"azure-functions","permalink":"/Cloud-Native/30daysofIA/tags/azure-functions"},{"label":"azure-openai","permalink":"/Cloud-Native/30daysofIA/tags/azure-openai"},{"label":"azure-container-apps","permalink":"/Cloud-Native/30daysofIA/tags/azure-container-apps"},{"label":"azure-cosmos-db","permalink":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db"},{"label":"github-copilot","permalink":"/Cloud-Native/30daysofIA/tags/github-copilot"},{"label":"github-codespaces","permalink":"/Cloud-Native/30daysofIA/tags/github-codespaces"},{"label":"github-actions","permalink":"/Cloud-Native/30daysofIA/tags/github-actions"}],"readingTime":7.25,"hasTruncateMarker":false,"authors":[{"name":"It\'s 30DaysOfIA","title":"FallForIA Content Team","url":"https://azure.github.io/Cloud-Native/Fall-For-IA/","imageURL":"https://azure.github.io/Cloud-Native/img/logo-ms-cloud-native.png","key":"cnteam"}],"frontMatter":{"date":"2023-09-18T09:00","slug":"demystifying-intelligent-applications","title":"1-1. Demystifying Intelligent Applications","authors":["cnteam"],"draft":false,"hide_table_of_contents":false,"toc_min_heading_level":2,"toc_max_heading_level":3,"keywords":["Cloud","Data","AI","AI/ML","intelligent apps","cloud-native","30-days","enterprise apps","digital experiences","app modernization","serverless","ai apps"],"image":"https://azure.github.io/Cloud-Native/img/ogImage.png","description":"This article explores the concept of intelligent applications for readers, providing a clear understanding of the role of AI capabilities in modern applications.","tags":["Fall-For-IA","30-days-of-IA","learn-live","hack-together","community-buzz","ask-the-expert","azure-kubernetes-service","azure-functions","azure-openai","azure-container-apps","azure-cosmos-db","github-copilot","github-codespaces","github-actions"]},"prevItem":{"title":"1-2. Harnessing the Power of Intelligent Apps","permalink":"/Cloud-Native/30daysofIA/harnessing-the-power-of-intelligent-apps"},"nextItem":{"title":"Kick-off #30DaysofIA \ud83c\udf42","permalink":"/Cloud-Native/30daysofIA/kick-off"}},"content":"\\n\\n\\n\\n\\n\\n \\n \\n \\n \\n \\n \\n \\n \\n\\n\\n\x3c!-- End METADATA --\x3e\\n\\nLet\u2019s start with understanding the **power of intelligent applications**. \\n\\n## What We\'ll Cover:\\n\\n * What are intelligent applications?\\n * Categories of Intelligent Apps\\n * Breaking down the role of AI in intelligent apps\\n\\n![A decorative header image for the blog](../../static/img/fallforia/blogs/2023-09-18/blog-image1.png)\\n\\n## Demystifying Intelligent Applications: Leveraging AI in App Development\\n\\nUntil recent years, when you heard the term \u201cartificial intelligence\u201d (AI), you might have pictured autonomous robots from sci-fi films. Now, perhaps your mind jumps to big data and machine learning (ML). Maybe you\u2019re even thinking about the latest ChatGPT plugins and transformer architectures. Today, AI is more than these imaginations: it\u2019s a reality increasingly woven into our day-to-day routines through intelligent applications.\\n\\nIntelligent apps combine cloud-scale compute, data, and predictive or generative AI. These applications help you automate tasks and make data-driven decisions, aiding in leveraging business intelligence to gain meaningful, practical outcomes.\u202f \\n\\nFor developers, the AI landscape isn\u2019t an abstract complexity. It\u2019s a real, tangible opportunity for evolving traditional applications, making them \u201cintelligent,\u201d and delivering more value to users. And with tools like Azure Kubernetes Service, Azure Cosmos DB, and [Azure AI Services](https://azure.microsoft.com/en-us/solutions/ai/), stepping into the world of intelligent apps isn\u2019t a steep climb but a steady trek across a manageable learning curve.\\n\\n## The Three Categories of Intelligent Applications\\n\\nBefore we start building, it\u2019s critical to understand what makes an app \u201cintelligent.\u201d It\u2019s not a universal definition so much as an identifier for apps that fall into one or more of the three primary categories:\u202f \\n\\n * **Outcome-based** \u2014 This designation represents the broadest category of intelligent apps, whose principal value for users is the intelligent outcomes they provide. These apps process data to provide insightful results that enhance decision-making or help with tasks like trend prediction, resource management, and customer relationship management.\\n\\n * **Functionality-based** \u2014 This type of intelligent app takes AI integration a degree further, integrating AI as a function of the app to create or process its outputs, enhancing the app\u2019s overall interactivity and usefulness.\\n\\n * **Feature-based** \u2014 This is the narrowest category, in which the app\u2019s AI/ML component is one of its core features and primary selling points\\n\\nLet\u2019s dive more deeply into each category.\\n\\n## Outcome-Based Apps: Intelligent Outcomes Drive Success\\n\\nOutcome-based applications are all about the results provided to the user. They function beyond merely processing data to offer insightful outcomes that drive success.\\n\\nThink of a personal fitness tracker app that uses AI to analyze the user\u2019s activity and provides actionable recommendations for achieving a goal. This type of app not only tracks your steps, sleep patterns, and heart rate but uses that data to provide you with personalized health advice. It helps you make informed lifestyle choices \u2014 the \u201cintelligent\u201d outcome.\\n\\nAnother example is a social media marketing app that uses AI for time-saving data analysis, task automation, and trend prediction. AI-based sentiment analysis can gauge customer perception while natural language processing (NLP) accurately interprets customer inquiries. Task automation can schedule posts and automate responses to common customer queries, and trend forecasting helps model and predict customer behavior to further personalize targeted promotional efforts. Building this type of intelligence into your apps provides cost-effective, actionable insights that guide sales and marketing strategies.\\n\\n## Functionality-Based Apps: AI-Driven Features Enhance User Experiences\\n\\nAs the name suggests, functionality-based applications integrate AI into how the app functions, but the end user drives the action and outcome. In this category of apps, AI takes a more active role in the user\u2019s interaction by allowing them to use AI-created or processed output. These apps leverage AI technologies like [Azure AI Services](https://azure.microsoft.com/en-us/products/cognitive-services/) for NLP, image recognition, and pattern recognition to improve user experience.\\n\\nConsider a music app that generates personalized playlists based on your listening habits or your smartphone\u2019s camera app that uses AI to recognize faces and optimize photo settings. Even more advanced are the language translation apps that translate text from one language to another and can even understand and maintain the context and subtleties of the conversation. They use NLP to accomplish these feats.\\n\\nSimilarly, consider the latest e-commerce apps, whose AI-driven functionalities enable customers to try on clothes virtually, significantly enhancing the online shopping experience.\\n\\n## Feature-Based Apps: Integrating Advanced AI/ML Components\\n\\nFinally, there are feature-based intelligent apps. These apps incorporate advanced AI/ML components like neural networks, internal large language models (LLMs), and sophisticated algorithms as core features for the user. In this type of application, AI is the primary feature for users to engage with and the primary selling point for the app.\\n\\nSome examples of feature-based applications are chatbots and virtual agents built using [Azure Bot Service](https://azure.microsoft.com/en-ca/products/bot-services/). These robo-representatives use AI to provide more natural and human-like conversation, balancing superior efficiency with a friendlier, more personalized user experience. [The new Bing](https://www.microsoft.com/en-us/edge/features/the-new-bing) is a prime example of this implementation, integrating AI to give users a knowledgeable copilot for web searches.\\n\\nSimilarly, OpenAI\u2019s [ChatGPT](https://openai.com/chatgpt) allows users access to its state-of-the-art language models available as Microsoft\u2019s [Azure OpenAI Service](https://azure.microsoft.com/en-us/products/cognitive-services/openai-service/). It leverages the power of the highly sophisticated GPT-3 LLM to perform tasks like drafting emails, writing code, answering general knowledge questions, translating between languages, and tutoring in numerous subjects.\\n\\nThese apps don\u2019t just use AI to provide outputs or improve user experiences: They push the boundaries of what\u2019s possible in app development by integrating AI/ML components.\\n\\n:::info\\n**Register** for the live Q&A [Ask The Expert session](https://aka.ms/fallforIA/ATE) on **September 26** with the Azure Functions product engineering team.\\n:::\\n\\n## Breaking Down the Role of AI in Intelligent Applications\\n\\nIntelligent apps reflect the merging of traditional software design with the dynamic capabilities of AI. Let\u2019s explore how AI integrates and amplifies these applications.\\n\\nAt its core, AI augments applications with the capacity to self-learn and self-improve. It allows you to create applications that predict, react, and adapt to a constantly changing environment. In the power grid management system for a major city, an AI-infused application can forecast power usage trends, react to anomalies, and adapt to sudden demand surges, ensuring optimal efficiency and preventing blackouts.\\n\\nBut AI\u2019s role doesn\u2019t end at prediction or decision-making: It also automates tasks. In enterprise solutions, it has moved beyond simple automation to encompass intelligent process automation (IPA). In this arena, AI algorithms understand, learn, and then automate entire digital workflows. For developers, this is a fertile ground for creating applications that can dramatically improve the operational efficiency of businesses.\\n\\nEven more, AI substantially aids in development tasks. For example, there\u2019s [GitHub Copilot](https://github.com/features/copilot), an AI-powered code assistant that offers suggestions based not only on syntax but on development best practices and the intent of the code. AI has evolved from a tool you use to build apps to a partner that helps in creating them.\u202f \\n\\nUsing AI to build intelligent apps isn\u2019t about throwing out what\u2019s worked before. It\u2019s about enhancing the components that have the most impact and achieve the best results. Incorporating AI in apps elevates their predictive abilities, streamlines complex tasks, and even aids in the development and creation process. When you build intelligent apps, you don\u2019t just address current problems. You anticipate future challenges and craft solutions that adapt over time.\\n\\n## Summary\\n\\nAs developers the power of AI is in our hands, and it\u2019s our responsibility to harness its full potential. Each category tells the story of AI\'s transformative potential, from applications that deliver intelligent outcomes to those with AI capabilities to enrich the user experience and those pushing boundaries with advanced AI/ML technology.\\n\\nAccessible and robust platforms like [Azure AI](https://www.microsoft.com/en-us/ai) can simplify the process of developing Intelligent Applications. Whether you\u2019re refining an existing app or building a new one, just entering the world of AI, or already familiar with the landscape, take a moment to appreciate the potential that AI integration offers. You\u2019re embracing this exciting frontier and shaping the future of app development.\u202f \\n\\n## Exercise\\n\\n * **Complete** the [Cloud Skills Challenge](https://aka.ms/fallforIA/csc) to build on your apps and AI skills. \\n * Watch [Episode 01](https://aka.ms/learnlive-contoso-app-deconstructed-Ep1) of the Serverless Edition Learn Live session to learn how to build an end-to-end intelligent app solution."},{"id":"kick-off","metadata":{"permalink":"/Cloud-Native/30daysofIA/kick-off","source":"@site/blog-30daysofIA/2023-09-15/kick-off.md","title":"Kick-off #30DaysofIA \ud83c\udf42","description":"This Fall focus on building intelligent apps using AI and cloud-native technologies. `#FallForIntelligentApps` brings to you a learning journey to build your skills on creating differentiated experiences while modernizing your applications. It\u2019s time to `learn it all`.","date":"2023-09-17T09:00:00.000Z","formattedDate":"September 17, 2023","tags":[{"label":"Fall-For-IA","permalink":"/Cloud-Native/30daysofIA/tags/fall-for-ia"},{"label":"30-days-of-IA","permalink":"/Cloud-Native/30daysofIA/tags/30-days-of-ia"},{"label":"learn-live","permalink":"/Cloud-Native/30daysofIA/tags/learn-live"},{"label":"hack-together","permalink":"/Cloud-Native/30daysofIA/tags/hack-together"},{"label":"community-buzz","permalink":"/Cloud-Native/30daysofIA/tags/community-buzz"},{"label":"ask-the-expert","permalink":"/Cloud-Native/30daysofIA/tags/ask-the-expert"},{"label":"azure-kubernetes-service","permalink":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service"},{"label":"azure-functions","permalink":"/Cloud-Native/30daysofIA/tags/azure-functions"},{"label":"azure-openai","permalink":"/Cloud-Native/30daysofIA/tags/azure-openai"},{"label":"azure-container-apps","permalink":"/Cloud-Native/30daysofIA/tags/azure-container-apps"},{"label":"azure-cosmos-db","permalink":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db"},{"label":"github-copilot","permalink":"/Cloud-Native/30daysofIA/tags/github-copilot"},{"label":"github-codespaces","permalink":"/Cloud-Native/30daysofIA/tags/github-codespaces"},{"label":"github-actions","permalink":"/Cloud-Native/30daysofIA/tags/github-actions"}],"readingTime":3.685,"hasTruncateMarker":false,"authors":[{"name":"It\'s 30DaysOfIA","title":"FallForIA Content Team","url":"https://azure.github.io/Cloud-Native/Fall-For-IA/","imageURL":"https://azure.github.io/Cloud-Native/img/logo-ms-cloud-native.png","key":"cnteam"}],"frontMatter":{"date":"2023-09-17T09:00","slug":"kick-off","title":"Kick-off #30DaysofIA \ud83c\udf42","authors":["cnteam"],"draft":false,"hide_table_of_contents":false,"toc_min_heading_level":2,"toc_max_heading_level":3,"keywords":["Cloud","Data","AI","AI/ML","intelligent apps","cloud-native","30-days","enterprise apps","digital experiences","app modernization","serverless","ai apps"],"image":"https://azure.github.io/Cloud-Native/img/ogImage.png","description":"This Fall focus on building intelligent apps using AI and cloud-native technologies. `#FallForIntelligentApps` brings to you a learning journey to build your skills on creating differentiated experiences while modernizing your applications. It\u2019s time to `learn it all`.","tags":["Fall-For-IA","30-days-of-IA","learn-live","hack-together","community-buzz","ask-the-expert","azure-kubernetes-service","azure-functions","azure-openai","azure-container-apps","azure-cosmos-db","github-copilot","github-codespaces","github-actions"]},"prevItem":{"title":"1-1. Demystifying Intelligent Applications","permalink":"/Cloud-Native/30daysofIA/demystifying-intelligent-applications"},"nextItem":{"title":"HackTogether Recap \ud83c\udf42","permalink":"/Cloud-Native/30daysofIA/hacktogether-recap"}},"content":"\\n\\n\\n\\n\\n\\n \\n \\n \\n \\n \\n \\n \\n \\n\\n\\n\x3c!-- End METADATA --\x3e\\n\\nThis Fall focus on building intelligent apps using AI and cloud-native technologies. `#FallForIntelligentApps` brings to you a learning journey to build your skills on creating differentiated experiences while modernizing your applications. It\u2019s time to `learn it all`. \\n\\n## What We\'ll Cover\\n * What is Fall For Intelligent Apps? \\n * How Can I participate? \\n * How Can I skill up? (in just 30 Days) \\n * **Exercise:** Take the [Fall For Intelligent Apps Skills Challenge](https://aka.ms/FallForIA/apps-csc)\\n * **Resources:** [#30DaysOfIA Collection](https://aka.ms/fallforIA/collection/?WT.mc_id=javascript-99907-ninarasi)\\n\\n![image](../../static/img/fallforia/blogs/2023-09-17/FallForIA_Key_visual.jpg)\\n\\n## Get Ready To #FallForIntelligentApps starting September 18!\\n\\nToday, we kick off the Fall season with content and activities to skill you up on all things Intelligent Apps or AI Apps on Azure with content, events, and community interactions! Read on to learn about what is coming!\\n\\n## Explore Our Initiatives\\n\\nWe have a number of initiatives planned for the month to help you learn and skill up on relevant technologies. Click on the links to visit the relevant pages for each\\n\\n * [#30DaysOfIA](https://aka.ms/fallforIA/30days) - 6 themed weeks of daily articles in a structured roadmap\\n * [Learn Live Series](https://aka.ms/FallForIA/LearnLive) \u2013 8 weekly live episodes on `Serverless` and `Kubernetes`\\n * [Ask The Expert](https://aka.ms/FallForIA/ATE-series) \u2013 join live Q&A sessions with Product Engineering teams\\n * [Cloud Skills Challenge](https://aka.ms/fallforIA/csc) \u2013 skill up by competing with peers to complete modules\\n\\n![image](../../static/img/fallforia/blogs/2023-09-17/Website-kick-off.jpg)\\n\\n:::info **Register for the events!**\\n\\nWhat are 4 things you can do today, to jumpstart your learning journey?\\n\\n * Register for live Q&A sessions (free, online) \\n * September 26 \u2013 [Ask The Expert: Azure Functions](https://aka.ms/FallForIA/ATE-series)\\n * October 11 \u2013 [Ask The Expert: Azure App Service](https://aka.ms/FallForIA/ATE-series)\\n * October 18 - [Ask The Expert: Azure Container Apps](https://aka.ms/FallForIA/ATE-series)\\n * October 25 \u2013 [Ask The Expert: Azure Kubernetes Service](https://aka.ms/FallForIA/ATE-series)\\n * Register for the [Learn Live Series: Serverless Edition](https://aka.ms/FallForIA/LearnLive) \u2013 weekly live learning \\n * Register for the [Intelligent Apps webinar](https://info.microsoft.com/ww-landing-driving-business-value-by-modernizing-with-cloud-native-and-ai.html?lcid=en-us) with Microsoft and Forrester\\n * Complete the [Cloud Skills Challenge](https://aka.ms/fallforIA/csc) \u2013 ends on October 31! \\n:::\\n\\n## #30Days Of Intelligent Apps\\n\\n[#30DaysOfIA](https://aka.ms/fallforIA/30days) is a series of daily blog posts grouped into themed weeks - taking you from core concepts to end-to-end solution examples in 30 days. Each article will provide conceptual lessons paired with exercises and resources to help you reinforce learnings and take next steps.\\n\\nThis series takes you through learning journey in\u202f**six stages**, each building on the previous week to help you skill up in a beginner-friendly way:\\n\\n * **Week 1**: Power of [Intelligent Applications](https://azure.microsoft.com/en-us/blog/build-next-generation-ai-powered-applications-on-microsoft-azure/?WT.mc_id=javascript-99907-ninarasi)\\n * **Week 2:** How to build intelligent apps with [cloud-native](https://azure.microsoft.com/en-us/solutions/cloud-native-apps/?WT.mc_id=javascript-99907-ninarasi)?\\n * **Week 3:** Powering intelligent applications using [Azure Kubernetes Service](https://learn.microsoft.com/en-us/azure/aks/?WT.mc_id=javascript-99907-ninarasi)\\n * **Week 4:** Building a Serverless Intelligent App with [Azure Functions](https://learn.microsoft.com/en-us/azure/azure-functions/functions-overview?WT.mc_id=javascript-99907-ninarasi&pivots=programming-language-csharp) and [Azure OpenAI](https://learn.microsoft.com/en-us/azure/ai-services/openai/overview/?WT.mc_id=javascript-99907-ninarasi)\\n * **Week 5:** Build end to end AI powered solutions with real world [reference architectures](https://learn.microsoft.com/en-us/azure/architecture/?WT.mc_id=javascript-99907-ninarasi)\\n * **Week 6:** Build your own [Copilot](https://learn.microsoft.com/en-us/training/paths/copilot/?WT.mc_id=javascript-99907-ninarasi)\\n\\nWe will start with defining intelligent apps and then expand on how to build with cloud-native technologies like [Azure Kubernetes Service](https://azure.microsoft.com/en-us/products/kubernetes-service/?WT.mc_id=javascript-99907-ninarasi), [Azure Container Apps](https://azure.microsoft.com/en-us/products/container-apps/?WT.mc_id=javascript-99907-ninarasi) and [Azure Functions](https://azure.microsoft.com/en-us/products/functions?WT.mc_id=javascript-99907-ninarasi), and take you through end-to-end scenarios for real world application development. Before we dive deep on intelligent apps, here is a high-level overview of the **Intelligent Apps** landscape on Azure: \\n\\n![image](../../static/img/fallforia/blogs/2023-09-17/intelligent-apps-image.jpg)\\n\\n**Containers on Azure** services offer you a wide range of capabilities, from simplicity to control to suit your different needs.\\n\\n![image](../../static/img/fallforia/blogs/2023-09-17/Containers-on-Azure.jpg)\\n![image](../../static/img/fallforia/blogs/2023-09-17/Containers-on-Azure-2.jpg)\\n\\nTo start with the basics for developing [Kubernetes](https://azure.microsoft.com/en-us/products/kubernetes-service/?WT.mc_id=javascript-99907-ninarasi) applications, explore [#30DaysOfCloudNative](https://azure.github.io/Cloud-Native/cnny-2023).\\n\\nCloud-native development when paired with **serverless computing** enhances your solution architecture for building cost optimized, resilient applications.\\n\\n![image](../../static/img/fallforia/blogs/2023-09-17/Serverless-New.jpg)\\n\\nTo start with the basics for [serverless computing](https://azure.microsoft.com/solutions/serverless/?WT.mc_id=javascript-99907-ninarasi), explore [#30DaysOfServerless](https://azure.github.io/Cloud-Native/blog).\\n\\n## Let\u2019s Get Started\\n\\nNow you know everything! We hope you are as excited as we are to dive into a full month of active learning and doing! Don\'t forget to\u202f[subscribe](https://aka.ms/fallforIA/30days/subscribe)\u202ffor updates in your favorite feed reader!\u202f**And look out for our first Intelligent Apps post Monday!**"},{"id":"hacktogether-recap","metadata":{"permalink":"/Cloud-Native/30daysofIA/hacktogether-recap","source":"@site/blog-30daysofIA/2023-09-08/hack-together-recap.md","title":"HackTogether Recap \ud83c\udf42","description":"Exciting news! As we approach the close of #JavaScript on #Azure Global Hack today, we are thrilled to announce another exciting opportunity for all JavaScript developers!! Find a recap of Hack together and read all about the upcoming #FallIntoIA on this post!","date":"2023-09-08T00:00:00.000Z","formattedDate":"September 8, 2023","tags":[{"label":"Fall-For-IA","permalink":"/Cloud-Native/30daysofIA/tags/fall-for-ia"},{"label":"30-days-of-IA","permalink":"/Cloud-Native/30daysofIA/tags/30-days-of-ia"},{"label":"learn-live","permalink":"/Cloud-Native/30daysofIA/tags/learn-live"},{"label":"hack-together","permalink":"/Cloud-Native/30daysofIA/tags/hack-together"},{"label":"community-buzz","permalink":"/Cloud-Native/30daysofIA/tags/community-buzz"},{"label":"ask-the-expert","permalink":"/Cloud-Native/30daysofIA/tags/ask-the-expert"},{"label":"azure-kubernetes-service","permalink":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service"},{"label":"azure-functions","permalink":"/Cloud-Native/30daysofIA/tags/azure-functions"},{"label":"azure-openai","permalink":"/Cloud-Native/30daysofIA/tags/azure-openai"},{"label":"azure-container-apps","permalink":"/Cloud-Native/30daysofIA/tags/azure-container-apps"},{"label":"azure-cosmos-db","permalink":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db"},{"label":"github-copilot","permalink":"/Cloud-Native/30daysofIA/tags/github-copilot"},{"label":"github-codespaces","permalink":"/Cloud-Native/30daysofIA/tags/github-codespaces"},{"label":"github-actions","permalink":"/Cloud-Native/30daysofIA/tags/github-actions"}],"readingTime":3.995,"hasTruncateMarker":false,"authors":[{"name":"It\'s 30DaysOfIA","title":"FallForIA Content Team","url":"https://azure.github.io/Cloud-Native/Fall-For-IA/","imageURL":"https://azure.github.io/Cloud-Native/img/logo-ms-cloud-native.png","key":"cnteam"}],"frontMatter":{"slug":"hacktogether-recap","title":"HackTogether Recap \ud83c\udf42","authors":["cnteam"],"draft":false,"hide_table_of_contents":false,"toc_min_heading_level":2,"toc_max_heading_level":3,"keywords":["Cloud-Scale","Data","AI","AI/ML","intelligent apps","cloud-native","30-days","enterprise apps","digital experiences","app modernization","hack-together"],"image":"https://azure.github.io/Cloud-Native/img/ogImage.png","description":"Exciting news! As we approach the close of #JavaScript on #Azure Global Hack today, we are thrilled to announce another exciting opportunity for all JavaScript developers!! Find a recap of Hack together and read all about the upcoming #FallIntoIA on this post!","tags":["Fall-For-IA","30-days-of-IA","learn-live","hack-together","community-buzz","ask-the-expert","azure-kubernetes-service","azure-functions","azure-openai","azure-container-apps","azure-cosmos-db","github-copilot","github-codespaces","github-actions"]},"prevItem":{"title":"Kick-off #30DaysofIA \ud83c\udf42","permalink":"/Cloud-Native/30daysofIA/kick-off"},"nextItem":{"title":"Fall is Coming! \ud83c\udf42","permalink":"/Cloud-Native/30daysofIA/road-to-fallforIA"}},"content":"\\n\\n\\n\\n\\n\\n \\n \\n \\n \\n \\n \\n \\n \\n\\n\\n\x3c!-- End METADATA --\x3e\\n\\nContinue The Learning Journey through **Fall For Intelligent Apps!** \ud83c\udf42\\n\\n## What We\'ll Cover\\n * Thank you! \u2665\ufe0f \\n * Recap of The [JavaScript on Azure Global Hack-Together](https://aka.ms/JavaScripton_Azure)\\n * Continue the journey\\n * Hands-on practice: Make your first contribution to open-source!\\n * Resources: For self-study!\\n\\n\\n\x3c!-- ************************************* --\x3e\\n\x3c!-- AUTHORS: ONLY UPDATE BELOW THIS LINE --\x3e\\n\x3c!-- ************************************* --\x3e\\n\\n## Thank you! \u2665\ufe0f \\n![image](https://user-images.githubusercontent.com/40116776/264592120-1dc08b59-0555-40b2-8866-59248a573b83.png)\\n\\nIt\'s hard to believe that JavaScript on Azure hack-together is ending! It seems like just yesterday that we launched this initiative, and yet here we are, 15 days later, with an incredible amount of learning and growth behind us. So... it\'s time for a wrap!\\n\\nFrom the bottom of our hearts, we want to thank each and every one of you for your participation, engagement, and enthusiasm. It\'s been truly inspiring to see the passion and dedication from this strong community, and we\'re honored to be a part of it. \u2728\\n\\n## Recap of The [JavaScript on Azure Global Hack-Together](https://aka.ms/JavaScripton_Azure)\\n\\nAs we wrap up this exciting event, we wanted to take a moment to reflect on all that we\'ve accomplished together. Over the last 15 days, we\'ve covered a lot of ground, from the basics of contributing to Open source to the exploration of the Contoso Real Estate project from its Frontend to its Backend and future AI implementation. \\n\\nNow that the hack-together is ending, we want to make sure that you have all the resources you need to continue honing your skills in the future. Whether you\'re looking to make your fist contribution to open source, become an open source maintainers, collaborate with others, or simply keep learning, there are plenty of resources out there to help you achieve your goals. So, let\'s dive in and explore all the ways you can continue to grow your JavaScript skills on Azure!\\n\\n### JSonAzure Hack-together Roadmap \ud83d\udccd:\\n![hack-together-roadmap (2)](https://user-images.githubusercontent.com/40116776/264975573-85938fcc-b235-4b5b-b45a-f174d3cf560d.png)\\n\\n\\n### Recap on past Livestreams\ud83c\udf1f:\\n\\nDay 1\ufe0f\u20e3: [Opening Keynote (Hack-together Launch)](https://developer.microsoft.com/reactor/events/20275/?WT.mc_id=academic-98351-juliamuiruri): Introduction to the Contoso Real Estate Open-source project!, managing complex and complex enterprise architecture, new announcements for JavaScript developers on Azure\\n\\nDay 2\ufe0f\u20e3: [GitHub Copilot & Codespaces](https://developer.microsoft.com/reactor/events/20321/?WT.mc_id=academic-98351-juliamuiruri): Introduction to your AI pair programmer (GitHub Copilot) and your virtual developer environment on the cloud (GitHub Codespaces)\\n\\nDay 6\ufe0f\u20e3: [Build your Frontend using Static Web Apps](https://developer.microsoft.com/reactor/events/20276/?WT.mc_id=academic-98351-juliamuiruri) as part of a complex, modern composable frontends (or micro-frontends) and cloud-native applications.\\n\\nDay 9\ufe0f\u20e3: Build a Serverless Backend using [Azure Functions](https://developer.microsoft.com/reactor/events/20277/?WT.mc_id=academic-98351-juliamuiruri)\\n\\nDay 1\ufe0f\u20e33\ufe0f\u20e3: Easily connect to an [Azure Cosmos DB](https://developer.microsoft.com/reactor/events/20278/?WT.mc_id=academic-98351-juliamuiruri), exploring its benefits and how to get started\\n\\nDay 1\ufe0f\u20e35\ufe0f\u20e3: Being in the AI Era, we dive into the [Azure OpenAI Service](https://developer.microsoft.com/reactor/events/20322/?WT.mc_id=academic-98351-juliamuiruri) and how you can start to build intelligent JavaScript applications\\n\\n### \ud83d\udcd6 Self-Learning Resources\\n\\n1. JavaScript on Azure Global Hack Together [Module collection](https://aka.ms/JavaScriptonAzureCSC)\\n2. Lets #HackTogether: Javascript On Azure [Keynote](https://dev.to/azure/lets-hacktogether-javascript-on-azure-keynote-nml)\\n3. [Step by Step Guide: Migrating v3 to v4 programming model for Azure Functions for Node.Js Application](https://techcommunity.microsoft.com/t5/educator-developer-blog/step-by-step-guide-migrating-v3-to-v4-programming-model-for/ba-p/3897691?WT.mc_id=academic-98351-juliamuiruri)\\n\\n## Continue your journey with #FallForIntelligentApps\\nJoin us this Fall on a learning journey to explore building intelligent apps. Combine the power of AI, cloud-native app development, and cloud-scale data to build highly differentiated digital experiences. Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure. Engage in a self-paced learning adventure by following along the #30Days of Intelligent Apps series, completing the Intelligent Apps Skills Challenge, joining the Product Group for a live Ask The Expert series or building an end to end solution architecture with a live guided experience through the Learn Live series. Discover more here.\\n\\n## Hands-on practice: Make your first contribution to open source!\\nJoin our GitHUb Discussion Forum to connect with developers from every part of world, see contributions from other, find collaborators and make your first contribution to a real-world project!\\nDon\'t forget to give the repo a star \u2b50\\n\\n## Resources\\nAll resources are accessible on our [landing page](https://aka.ms/JavaScripton_Azure)"},{"id":"road-to-fallforIA","metadata":{"permalink":"/Cloud-Native/30daysofIA/road-to-fallforIA","source":"@site/blog-30daysofIA/2023-08-28/road-to-fallforia.md","title":"Fall is Coming! \ud83c\udf42","description":"Combine the power of AI, cloud-scale data, and cloud-native app development to create highly differentiated digital experiences. Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure.","date":"2023-08-28T00:00:00.000Z","formattedDate":"August 28, 2023","tags":[{"label":"Fall-For-IA","permalink":"/Cloud-Native/30daysofIA/tags/fall-for-ia"},{"label":"30-days-of-IA","permalink":"/Cloud-Native/30daysofIA/tags/30-days-of-ia"},{"label":"learn-live","permalink":"/Cloud-Native/30daysofIA/tags/learn-live"},{"label":"hack-together","permalink":"/Cloud-Native/30daysofIA/tags/hack-together"},{"label":"community-buzz","permalink":"/Cloud-Native/30daysofIA/tags/community-buzz"},{"label":"ask-the-expert","permalink":"/Cloud-Native/30daysofIA/tags/ask-the-expert"},{"label":"azure-kubernetes-service","permalink":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service"},{"label":"azure-functions","permalink":"/Cloud-Native/30daysofIA/tags/azure-functions"},{"label":"azure-openai","permalink":"/Cloud-Native/30daysofIA/tags/azure-openai"},{"label":"azure-container-apps","permalink":"/Cloud-Native/30daysofIA/tags/azure-container-apps"},{"label":"azure-cosmos-db","permalink":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db"},{"label":"github-copilot","permalink":"/Cloud-Native/30daysofIA/tags/github-copilot"},{"label":"github-codespaces","permalink":"/Cloud-Native/30daysofIA/tags/github-codespaces"},{"label":"github-actions","permalink":"/Cloud-Native/30daysofIA/tags/github-actions"}],"readingTime":1.055,"hasTruncateMarker":false,"authors":[{"name":"It\'s 30DaysOfIA","title":"FallForIA Content Team","url":"https://azure.github.io/Cloud-Native/Fall-For-IA/","imageURL":"https://azure.github.io/Cloud-Native/img/logo-ms-cloud-native.png","key":"cnteam"}],"frontMatter":{"slug":"road-to-fallforIA","title":"Fall is Coming! \ud83c\udf42","authors":["cnteam"],"draft":false,"hide_table_of_contents":false,"toc_min_heading_level":2,"toc_max_heading_level":3,"keywords":["Cloud-Scale","Data","AI","AI/ML","intelligent apps","cloud-native","30-days","enterprise apps","digital experiences","app modernization"],"image":"https://github.com/Azure/Cloud-Native/blob/main/website/static/img/ogImage.png","description":"Combine the power of AI, cloud-scale data, and cloud-native app development to create highly differentiated digital experiences. Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure.","tags":["Fall-For-IA","30-days-of-IA","learn-live","hack-together","community-buzz","ask-the-expert","azure-kubernetes-service","azure-functions","azure-openai","azure-container-apps","azure-cosmos-db","github-copilot","github-codespaces","github-actions"]},"prevItem":{"title":"HackTogether Recap \ud83c\udf42","permalink":"/Cloud-Native/30daysofIA/hacktogether-recap"}},"content":"\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n\\n\\n\x3c!-- End METADATA --\x3e\\n\\nSeptember is almost here - and that can only mean one thing!! It\'s time to **\ud83c\udf42 Fall for something new and exciting** and spend a few weeks skilling up on relevant tools, techologies and solutions!! \\n\\nLast year, we focused on #ServerlessSeptember. This year, we\'re building on that theme with the addition of cloud-scale **Data**, cloud-native **Technologies** and cloud-based **AI** integrations to help you modernize and build intelligent apps for the enterprise!\\n\\nWatch this space - and join us in September to learn more!"}]}')}}]); \ No newline at end of file diff --git a/assets/js/016aea77.b7f3c03d.js b/assets/js/016aea77.b7f3c03d.js new file mode 100644 index 0000000000..9b15871101 --- /dev/null +++ b/assets/js/016aea77.b7f3c03d.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[61489],{22688:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/github-copilot/page/7","page":7,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/github-copilot/page/6","nextPage":"/Cloud-Native/30daysofIA/tags/github-copilot/page/8","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/016aea77.de8e6ee9.js b/assets/js/016aea77.de8e6ee9.js deleted file mode 100644 index 2af7ee729d..0000000000 --- a/assets/js/016aea77.de8e6ee9.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[61489],{22688:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/github-copilot/page/7","page":7,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/github-copilot/page/6","nextPage":"/Cloud-Native/30daysofIA/tags/github-copilot/page/8","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/017fab63.0aa7f6f0.js b/assets/js/017fab63.0aa7f6f0.js new file mode 100644 index 0000000000..0f20974a9a --- /dev/null +++ b/assets/js/017fab63.0aa7f6f0.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[74278],{65124:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/github-actions/page/7","page":7,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/github-actions/page/6","nextPage":"/Cloud-Native/30daysofIA/tags/github-actions/page/8","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/017fab63.72123fac.js b/assets/js/017fab63.72123fac.js deleted file mode 100644 index 180a60ad06..0000000000 --- a/assets/js/017fab63.72123fac.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[74278],{65124:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/github-actions/page/7","page":7,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/github-actions/page/6","nextPage":"/Cloud-Native/30daysofIA/tags/github-actions/page/8","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/02eacc81.14f76dbc.js b/assets/js/02eacc81.2a1132c0.js similarity index 89% rename from assets/js/02eacc81.14f76dbc.js rename to assets/js/02eacc81.2a1132c0.js index cf8289b9e2..b701cb3d20 100644 --- a/assets/js/02eacc81.14f76dbc.js +++ b/assets/js/02eacc81.2a1132c0.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[75555],{37807:e=>{e.exports=JSON.parse('{"label":"azure-kubernetes-service","permalink":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[75555],{37807:e=>{e.exports=JSON.parse('{"label":"azure-kubernetes-service","permalink":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/03633680.0840e695.js b/assets/js/03633680.0840e695.js new file mode 100644 index 0000000000..a9f8eefab5 --- /dev/null +++ b/assets/js/03633680.0840e695.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[17317],{24358:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/github-copilot/page/5","page":5,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/github-copilot/page/4","nextPage":"/Cloud-Native/30daysofIA/tags/github-copilot/page/6","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/03633680.715dbb55.js b/assets/js/03633680.715dbb55.js deleted file mode 100644 index c8f271bcaa..0000000000 --- a/assets/js/03633680.715dbb55.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[17317],{24358:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/github-copilot/page/5","page":5,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/github-copilot/page/4","nextPage":"/Cloud-Native/30daysofIA/tags/github-copilot/page/6","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/03d787c9.6ad441d7.js b/assets/js/03d787c9.6ad441d7.js new file mode 100644 index 0000000000..b59e5b43f3 --- /dev/null +++ b/assets/js/03d787c9.6ad441d7.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[95490],{24511:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/30-days-of-ia/page/10","page":10,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/30-days-of-ia/page/9","nextPage":"/Cloud-Native/30daysofIA/tags/30-days-of-ia/page/11","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/046e34f6.40fe7f17.js b/assets/js/046e34f6.40fe7f17.js new file mode 100644 index 0000000000..651a1f7fc3 --- /dev/null +++ b/assets/js/046e34f6.40fe7f17.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[20853],{56046:s=>{s.exports=JSON.parse('{"label":"azure-cosmos-db","permalink":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/04b79bd3.d727ac5e.js b/assets/js/04b79bd3.f545816b.js similarity index 72% rename from assets/js/04b79bd3.d727ac5e.js rename to assets/js/04b79bd3.f545816b.js index b1aabd845c..9ecaac07cc 100644 --- a/assets/js/04b79bd3.d727ac5e.js +++ b/assets/js/04b79bd3.f545816b.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[37573],{19372:e=>{e.exports=JSON.parse('{"label":"github-copilot","permalink":"/Cloud-Native/30daysofIA/tags/github-copilot","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[37573],{19372:e=>{e.exports=JSON.parse('{"label":"github-copilot","permalink":"/Cloud-Native/30daysofIA/tags/github-copilot","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/059e579b.c955f156.js b/assets/js/059e579b.2f8f1cb2.js similarity index 73% rename from assets/js/059e579b.c955f156.js rename to assets/js/059e579b.2f8f1cb2.js index dd1a36fe14..271a737025 100644 --- a/assets/js/059e579b.c955f156.js +++ b/assets/js/059e579b.2f8f1cb2.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[73175],{35705:e=>{e.exports=JSON.parse('{"label":"hack-together","permalink":"/Cloud-Native/30daysofIA/tags/hack-together","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[73175],{35705:e=>{e.exports=JSON.parse('{"label":"hack-together","permalink":"/Cloud-Native/30daysofIA/tags/hack-together","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/05b7df8f.2d4540ea.js b/assets/js/05b7df8f.2d4540ea.js deleted file mode 100644 index d34da2f356..0000000000 --- a/assets/js/05b7df8f.2d4540ea.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[90388],{38797:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/learn-live/page/2","page":2,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/learn-live","nextPage":"/Cloud-Native/30daysofIA/tags/learn-live/page/3","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/05b7df8f.52a14524.js b/assets/js/05b7df8f.52a14524.js new file mode 100644 index 0000000000..cbe68d9ac3 --- /dev/null +++ b/assets/js/05b7df8f.52a14524.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[90388],{38797:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/learn-live/page/2","page":2,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/learn-live","nextPage":"/Cloud-Native/30daysofIA/tags/learn-live/page/3","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/06db7cdd.2c092a64.js b/assets/js/06db7cdd.2c092a64.js deleted file mode 100644 index 199883f132..0000000000 --- a/assets/js/06db7cdd.2c092a64.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[53118],{61420:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service/page/2","page":2,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service","nextPage":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service/page/3","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/06db7cdd.6d416718.js b/assets/js/06db7cdd.6d416718.js new file mode 100644 index 0000000000..64c3fac4e5 --- /dev/null +++ b/assets/js/06db7cdd.6d416718.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[53118],{61420:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service/page/2","page":2,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service","nextPage":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service/page/3","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/07104faf.8a9124c8.js b/assets/js/07104faf.8a9124c8.js new file mode 100644 index 0000000000..badff2f808 --- /dev/null +++ b/assets/js/07104faf.8a9124c8.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[56118],{58816:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/github-actions/page/11","page":11,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/github-actions/page/10","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/07865a4c.b82f3be9.js b/assets/js/07865a4c.66788ab0.js similarity index 72% rename from assets/js/07865a4c.b82f3be9.js rename to assets/js/07865a4c.66788ab0.js index f722d503a4..64a94f59ec 100644 --- a/assets/js/07865a4c.b82f3be9.js +++ b/assets/js/07865a4c.66788ab0.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[19273],{54908:e=>{e.exports=JSON.parse('{"label":"ask-the-expert","permalink":"/Cloud-Native/30daysofIA/tags/ask-the-expert","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[19273],{54908:e=>{e.exports=JSON.parse('{"label":"ask-the-expert","permalink":"/Cloud-Native/30daysofIA/tags/ask-the-expert","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/07a9616f.1d585d6d.js b/assets/js/07a9616f.1d585d6d.js deleted file mode 100644 index 689c1b6e64..0000000000 --- a/assets/js/07a9616f.1d585d6d.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[42392],{3905:(e,t,a)=>{a.d(t,{Zo:()=>c,kt:()=>g});var i=a(67294);function n(e,t,a){return t in e?Object.defineProperty(e,t,{value:a,enumerable:!0,configurable:!0,writable:!0}):e[t]=a,e}function r(e,t){var a=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);t&&(i=i.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),a.push.apply(a,i)}return a}function o(e){for(var t=1;t=0||(n[a]=e[a]);return n}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(i=0;i=0||Object.prototype.propertyIsEnumerable.call(e,a)&&(n[a]=e[a])}return n}var l=i.createContext({}),p=function(e){var t=i.useContext(l),a=t;return e&&(a="function"==typeof e?e(t):o(o({},t),e)),a},c=function(e){var t=p(e.components);return i.createElement(l.Provider,{value:t},e.children)},u={inlineCode:"code",wrapper:function(e){var t=e.children;return i.createElement(i.Fragment,{},t)}},d=i.forwardRef((function(e,t){var a=e.components,n=e.mdxType,r=e.originalType,l=e.parentName,c=s(e,["components","mdxType","originalType","parentName"]),d=p(a),g=n,m=d["".concat(l,".").concat(g)]||d[g]||u[g]||r;return a?i.createElement(m,o(o({ref:t},c),{},{components:a})):i.createElement(m,o({ref:t},c))}));function g(e,t){var a=arguments,n=t&&t.mdxType;if("string"==typeof e||n){var r=a.length,o=new Array(r);o[0]=d;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s.mdxType="string"==typeof e?e:n,o[1]=s;for(var p=2;p{a.r(t),a.d(t,{assets:()=>l,contentTitle:()=>o,default:()=>u,frontMatter:()=>r,metadata:()=>s,toc:()=>p});var i=a(87462),n=(a(67294),a(3905));const r={date:"2023-09-22T09:00",slug:"preparing-the-path-for-intelligent-apps",title:"1-5. Preparing the Path for Intelligent Apps",authors:["cnteam"],draft:!1,hide_table_of_contents:!1,toc_min_heading_level:2,toc_max_heading_level:3,keywords:["Cloud","Data","AI","AI/ML","intelligent apps","cloud-native","30-days","enterprise apps","digital experiences","app modernization","serverless","ai apps","data"],image:"https://azure.github.io/Cloud-Native/img/ogImage.png",description:"Learn best practices for modernizing your on-premises or IaaS solutions to intelligent apps in the cloud.",tags:["Fall-For-IA","30-days-of-IA","learn-live","hack-together","community-buzz","ask-the-expert","azure-kubernetes-service","azure-functions","azure-openai","azure-container-apps","azure-cosmos-db","github-copilot","github-codespaces","github-actions"]},o=void 0,s={permalink:"/Cloud-Native/30daysofIA/preparing-the-path-for-intelligent-apps",source:"@site/blog-30daysofIA/2023-09-22/preparing-the-path-for-intelligent-apps.md",title:"1-5. Preparing the Path for Intelligent Apps",description:"Learn best practices for modernizing your on-premises or IaaS solutions to intelligent apps in the cloud.",date:"2023-09-22T09:00:00.000Z",formattedDate:"September 22, 2023",tags:[{label:"Fall-For-IA",permalink:"/Cloud-Native/30daysofIA/tags/fall-for-ia"},{label:"30-days-of-IA",permalink:"/Cloud-Native/30daysofIA/tags/30-days-of-ia"},{label:"learn-live",permalink:"/Cloud-Native/30daysofIA/tags/learn-live"},{label:"hack-together",permalink:"/Cloud-Native/30daysofIA/tags/hack-together"},{label:"community-buzz",permalink:"/Cloud-Native/30daysofIA/tags/community-buzz"},{label:"ask-the-expert",permalink:"/Cloud-Native/30daysofIA/tags/ask-the-expert"},{label:"azure-kubernetes-service",permalink:"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service"},{label:"azure-functions",permalink:"/Cloud-Native/30daysofIA/tags/azure-functions"},{label:"azure-openai",permalink:"/Cloud-Native/30daysofIA/tags/azure-openai"},{label:"azure-container-apps",permalink:"/Cloud-Native/30daysofIA/tags/azure-container-apps"},{label:"azure-cosmos-db",permalink:"/Cloud-Native/30daysofIA/tags/azure-cosmos-db"},{label:"github-copilot",permalink:"/Cloud-Native/30daysofIA/tags/github-copilot"},{label:"github-codespaces",permalink:"/Cloud-Native/30daysofIA/tags/github-codespaces"},{label:"github-actions",permalink:"/Cloud-Native/30daysofIA/tags/github-actions"}],readingTime:8.59,hasTruncateMarker:!1,authors:[{name:"It's 30DaysOfIA",title:"FallForIA Content Team",url:"https://azure.github.io/Cloud-Native/Fall-For-IA/",imageURL:"https://azure.github.io/Cloud-Native/img/logo-ms-cloud-native.png",key:"cnteam"}],frontMatter:{date:"2023-09-22T09:00",slug:"preparing-the-path-for-intelligent-apps",title:"1-5. Preparing the Path for Intelligent Apps",authors:["cnteam"],draft:!1,hide_table_of_contents:!1,toc_min_heading_level:2,toc_max_heading_level:3,keywords:["Cloud","Data","AI","AI/ML","intelligent apps","cloud-native","30-days","enterprise apps","digital experiences","app modernization","serverless","ai apps","data"],image:"https://azure.github.io/Cloud-Native/img/ogImage.png",description:"Learn best practices for modernizing your on-premises or IaaS solutions to intelligent apps in the cloud.",tags:["Fall-For-IA","30-days-of-IA","learn-live","hack-together","community-buzz","ask-the-expert","azure-kubernetes-service","azure-functions","azure-openai","azure-container-apps","azure-cosmos-db","github-copilot","github-codespaces","github-actions"]},prevItem:{title:"1-6. Cultivating a Culture for Intelligent Apps",permalink:"/Cloud-Native/30daysofIA/cultivating-a-culture-for-intelligent-apps"},nextItem:{title:"1-4. How Digital Natives leverage Generative AI",permalink:"/Cloud-Native/30daysofIA/how-digital-natives-leverage-generative-ai"}},l={authorsImageUrls:[void 0]},p=[{value:"What We'll Cover:",id:"what-well-cover",level:2},{value:"Preparing the Path for Intelligent Apps: Transitioning from On-Premises/IaaS to Cloud-Native Applications",id:"preparing-the-path-for-intelligent-apps-transitioning-from-on-premisesiaas-to-cloud-native-applications",level:2},{value:"Understanding the Shift: Traditional vs. Intelligent Applications",id:"understanding-the-shift-traditional-vs-intelligent-applications",level:2},{value:"Functionality and User Experience",id:"functionality-and-user-experience",level:3},{value:"Infrastructure",id:"infrastructure",level:3},{value:"On-Premises/IaaS vs. Cloud-Native Platforms",id:"on-premisesiaas-vs-cloud-native-platforms",level:3},{value:"Strategic Considerations for Transitioning to Intelligent Apps",id:"strategic-considerations-for-transitioning-to-intelligent-apps",level:3},{value:"Re-Architecting Monolithic Apps into Microservices",id:"re-architecting-monolithic-apps-into-microservices",level:3},{value:"Event-Driven Architectures: Knowing When to Use Them",id:"event-driven-architectures-knowing-when-to-use-them",level:3},{value:"Conclusion",id:"conclusion",level:2},{value:"Exercise",id:"exercise",level:2}],c={toc:p};function u(e){let{components:t,...r}=e;return(0,n.kt)("wrapper",(0,i.Z)({},c,r,{components:t,mdxType:"MDXLayout"}),(0,n.kt)("head",null,(0,n.kt)("meta",{property:"og:url",content:"https://azure.github.io/cloud-native/30daysofia/how-digital-natives-leverage-generative-ai"}),(0,n.kt)("meta",{property:"og:type",content:"website"}),(0,n.kt)("meta",{property:"og:title",content:"**Fall For Intelligent Apps! \ud83c\udf42| Build AI Apps On Azure"}),(0,n.kt)("meta",{property:"og:description",content:"Explore how digital natives and Independent Software Vendors (ISVs) can transform their business operations and generate higher value for customers with generative AI and intelligent apps."}),(0,n.kt)("meta",{property:"og:image",content:"https://azure.github.io/Cloud-Native/img/ogImage.png"}),(0,n.kt)("meta",{name:"twitter:url",content:"https://azure.github.io/Cloud-Native/30daysofIA/preparing-the-path-for-intelligent-apps"}),(0,n.kt)("meta",{name:"twitter:title",content:"**Fall For Intelligent Apps! \ud83c\udf42 | Build AI Apps On Azure"}),(0,n.kt)("meta",{name:"twitter:description",content:"1-5. Learn best practices for modernizing your on-premises or IaaS solutions to intelligent apps in the cloud."}),(0,n.kt)("meta",{name:"twitter:image",content:"https://azure.github.io/Cloud-Native/img/ogImage.png"}),(0,n.kt)("meta",{name:"twitter:card",content:"summary_large_image"}),(0,n.kt)("meta",{name:"twitter:creator",content:"@devanshidiaries"}),(0,n.kt)("meta",{name:"twitter:site",content:"@AzureAdvocates"}),(0,n.kt)("link",{rel:"canonical",href:"https://azure.github.io/Cloud-Native/30daysofIA/preparing-the-path-for-intelligent-apps"})),(0,n.kt)("p",null,"Learn best practices for modernizing your on-premises or IaaS solutions to intelligent apps in the cloud."),(0,n.kt)("h2",{id:"what-well-cover"},"What We'll Cover:"),(0,n.kt)("ul",null,(0,n.kt)("li",{parentName:"ul"},"Traditional vs Intelligent Apps"),(0,n.kt)("li",{parentName:"ul"},"On-Premises/IaaS vs. Cloud-Native Platforms"),(0,n.kt)("li",{parentName:"ul"},"Modernization strategy for building Intelligent Apps")),(0,n.kt)("p",null,(0,n.kt)("img",{alt:"image of modernizing AI solutions for intelligent apps",src:a(22352).Z,width:"624",height:"380"})),(0,n.kt)("h2",{id:"preparing-the-path-for-intelligent-apps-transitioning-from-on-premisesiaas-to-cloud-native-applications"},"Preparing the Path for Intelligent Apps: Transitioning from On-Premises/IaaS to Cloud-Native Applications"),(0,n.kt)("p",null,"The proliferation of generative AI is paving the way for intelligent applications, software that leverages AI capabilities to deliver unparalleled functionality and user experience. As we covered in a previous article, \u201c",(0,n.kt)("a",{parentName:"p",href:"https://azure.github.io/Cloud-Native/30daysofIA/demystifying-intelligent-applications"},"Demystifying Intelligent Applications: Leveraging AI in App Development"),",\u201d intelligent Apps aren\u2019t just products of advanced coding. They interact, learn, and evolve thanks to modern AI and machine learning (ML) breakthroughs.\u202f "),(0,n.kt)("p",null,"The paradigm shift from traditional to intelligent apps demands that we change our approach to the technical challenges of software and architecture design. It also necessitates adopting new ways of thinking and operating within an organization, which we explore in greater depth in \u201c",(0,n.kt)("a",{parentName:"p",href:"https://azure.github.io/Cloud-Native/30daysofIA/cultivating-a-culture-for-intelligent-apps"},"Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management"),".\u201d\u202f "),(0,n.kt)("p",null,"This article explores the technical infrastructure powering intelligent apps, providing a roadmap to transition traditional on-premises or Infrastructure as a Service (IaaS) solutions to intelligent apps deployed to cloud native platforms and services. We\u2019ll discover the differences between conventional and intelligent apps, options for re-architecting existing applications, and critical strategic considerations integral to modernizing the application-building approach.\u202f "),(0,n.kt)("h2",{id:"understanding-the-shift-traditional-vs-intelligent-applications"},"Understanding the Shift: Traditional vs. Intelligent Applications"),(0,n.kt)("p",null,"Intelligent apps are more than just an incremental evolution from traditional apps. They involve a fundamental shift in software solution design, development, and deployment. A clear understanding of how each paradigm differs is crucial for comprehending the impact of this transition.\u202f "),(0,n.kt)("h3",{id:"functionality-and-user-experience"},"Functionality and User Experience"),(0,n.kt)("p",null,"Traditional applications, often rule-based and rigid, rely on preprogrammed operations. A traditional weather app, for example, typically presents structured, location-based forecasts using a predetermined data source. Meanwhile, an intelligent app leverages AI for data-driven decision-making and personalization. Rather than merely displaying the forecast, it integrates ML with additional data sources\u2014like a user\u2019s calendar and fitness data\u2014to determine their favorite outdoor activities, provide relevant clothing suggestions, and even automatically design ideal vacations and times based on the user\u2019s optimal weather preferences.\u202f "),(0,n.kt)("p",null,"Similarly, while traditional applications tend to offer a one-size-fits-all UI, intelligent apps use AI to create tailored interactions. For instance, an intelligent banking app might use generative AI to provide a voice-enabled UI that lets customers use natural language to ask about transactions and receive personalized financial advice."),(0,n.kt)("h3",{id:"infrastructure"},"Infrastructure"),(0,n.kt)("p",null,"Intelligent apps integrating these sophisticated features require highly scalable, flexible infrastructure optimized for AI functionality and able to manage large volumes of real-time data. Unlike traditional applications deployed on-premises or in infrastructure as a service (IaaS) environments, intelligent apps demand the flexibility and scalability of cloud native architectures."),(0,n.kt)("p",null,"An on-premises or IaaS-based e-commerce application may struggle with connecting its data to AI services or scaling to accommodate voluminous real-time data and requests. But an intelligent app built with a cloud native approach and scalability at its core can handle variable demand much more efficiently. It also maintains its infrastructure components on demand, whether for collecting data for AI/ML consumption or user-facing features like AI-enabled customer support and personalization."),(0,n.kt)("p",null,"Consider an intelligent app built on Azure that creates ChatGPT-like experiences with custom data. It might connect ",(0,n.kt)("a",{parentName:"p",href:"https://azure.microsoft.com/en-us/products/ai-services/openai-service?WT.mc_id=javascript-99907-ninarasi"},"Azure Open AI")," and ",(0,n.kt)("a",{parentName:"p",href:"https://azure.microsoft.com/en-us/products/ai-services/cognitive-search?WT.mc_id=javascript-99907-ninarasi"},"Azure Cognitive Search")," to an app running on ",(0,n.kt)("a",{parentName:"p",href:"https://azure.microsoft.com/en-us/products/container-apps/?WT.mc_id=javascript-99907-ninarasi"},"Azure Container Apps")," or ",(0,n.kt)("a",{parentName:"p",href:"https://azure.microsoft.com/en-us/products/kubernetes-service/?WT.mc_id=javascript-99907-ninarasi"},"Azure Kubernetes Service"),".\u202f "),(0,n.kt)("p",null,"The architecture might look like the following diagram based on this ",(0,n.kt)("a",{parentName:"p",href:"https://github.com/Azure-Samples/azure-search-openai-demo-csharp"},"demo app"),":"),(0,n.kt)("p",null,(0,n.kt)("img",{alt:"diagram of a demo app architecture",src:a(48199).Z,width:"624",height:"386"})),(0,n.kt)("p",null,"While the cloud is well-equipped to handle the challenges associated with shifting to AI-centric infrastructure\u2014handling real-time data processing and storage, integrating and managing AI services, configuring event-driven and microservices architectures, and ensuring security across the distributed environment\u2014re-architecting a traditional app to an intelligent app requires careful planning and execution to ensure it benefits fully from the capabilities of AI and cloud computing."),(0,n.kt)("h3",{id:"on-premisesiaas-vs-cloud-native-platforms"},"On-Premises/IaaS vs. Cloud-Native Platforms"),(0,n.kt)("p",null,"While on-premises and IaaS solutions provide substantial control over data and processes, this fine-grained control comes with setup and maintenance costs and may lack the scalability necessary for AI-driven Intelligent Apps. On-premises hosting involves purchasing and maintaining hardware, including servers and storage systems. IaaS involves leasing virtual hardware and then hiring and training IT personnel for the infrastructure\u2019s operation, maintenance, and security."),(0,n.kt)("p",null,"Conversely, infrastructure built on cloud native app services with ",(0,n.kt)("a",{parentName:"p",href:"https://azure.microsoft.com/?WT.mc_id=javascript-99907-ninarasi"},"Microsoft Azure")," offers virtually unlimited scalability without ongoing hardware and maintenance costs. This feature is ideal for intelligent apps that need to adapt and scale to accommodate fluctuating workloads and data streams. And because these cloud infrastructures typically operate on a pay-as-you-go model, traditionally substantial upfront costs turn into predictable operating expenses, lowering both the cost and risk of launching applications."),(0,n.kt)("p",null,"Moreover, cloud service providers continually update their offerings, so building on sophisticated AI tools and services through the cloud, like the ",(0,n.kt)("a",{parentName:"p",href:"https://azure.microsoft.com/en-us/solutions/ai/?WT.mc_id=javascript-99907-ninarasi"},"Azure AI Platform")," and orchestration capabilities with ",(0,n.kt)("a",{parentName:"p",href:"https://azure.microsoft.com/en-us/products/kubernetes-service/?WT.mc_id=javascript-99907-ninarasi"},"Azure Kubernetes Service"),", ensures that you have access to the latest AI and most advanced AI models and technologies."),(0,n.kt)("h3",{id:"strategic-considerations-for-transitioning-to-intelligent-apps"},"Strategic Considerations for Transitioning to Intelligent Apps"),(0,n.kt)("p",null,"When developing a strategy for transitioning to intelligent apps, you must consider costs, personnel skill development, and data management."),(0,n.kt)("p",null,"Monolithic architectures are unlikely to support the scale and ease of iteration required by intelligent apps, so you\u2019ll likely need to implement code changes to support the transition. Your chosen hosting solution and the amount of data your app will handle affect cost and performance, in addition to determining privacy and compliance requirements."),(0,n.kt)("p",null,"An app that manages or stores terabytes or petabytes of data will require storage-optimized virtual machines or a managed database service hosting. Meanwhile, an app that processes video content or performs resource-intensive calculations should prioritize high-performance computing. Maximizing storage and performance can become extraordinarily costly."),(0,n.kt)("p",null,"Finally, existing applications shifting towards intelligent apps may encounter compatibility conflicts, depending on database type or the existing system\u2019s underlying programming language. As you might imagine, a traditional app running on-premises and using a shared local database may need significant restructuring to support a cloud native architecture. You need plans for data migration, potential downtime, and training."),(0,n.kt)("p",null,"Next, let\u2019s look at this transformation process."),(0,n.kt)("h3",{id:"re-architecting-monolithic-apps-into-microservices"},"Re-Architecting Monolithic Apps into Microservices"),(0,n.kt)("p",null,"Shifting from ",(0,n.kt)("a",{parentName:"p",href:"https://learn.microsoft.com/en-us/dotnet/architecture/containerized-lifecycle/design-develop-containerized-apps/monolithic-applications?WT.mc_id=javascript-99907-ninarasi"},"monolithic applications")," to microservices is pivotal for transitioning to Intelligent Apps, and you can use this shift as an opportunity to transform your legacy software into intelligent apps that leverage the flexibility and power of microservices-enabled AI solutions."),(0,n.kt)("p",null,"Traditionally, a monolithic application (let\u2019s use an online marketplace as an example) might handle all activities\u2014from serving webpages to processing payments\u2014within an on-premises server or a cloud server. However, this approach is challenging to scale and remains vulnerable to network failures or cyberattacks."),(0,n.kt)("p",null,"Breaking these applications into multiple services with specific responsibilities, such as authentication or notifications, can overcome these limitations. These services are typically deployed as microservices within container environments like ",(0,n.kt)("a",{parentName:"p",href:"https://azure.microsoft.com/en-us/products/kubernetes-service/?WT.mc_id=javascript-99907-ninarasi"},"Azure Kubernetes Service")," or serverless platforms like ",(0,n.kt)("a",{parentName:"p",href:"https://azure.microsoft.com/en-us/products/container-apps?WT.mc_id=javascript-99907-ninarasi"},"Azure Container Apps")," or ",(0,n.kt)("a",{parentName:"p",href:"https://azure.microsoft.com/en-us/products/functions/?WT.mc_id=javascript-99907-ninarasi"},"Azure Functions")," using automated processes. This approach offers scalability, robustness, and improved performance, as each microservice can run independently and use its own database."),(0,n.kt)("p",null,"Shifting towards smaller, independent microservices in app architectures allows us to reap the benefits of AI-powered intelligent apps, for example, by using microservices to handle AI tasks such as processing natural language input, analyzing behavioral data, or personalizing content."),(0,n.kt)("admonition",{type:"info"},(0,n.kt)("p",{parentName:"admonition"},"Watch ",(0,n.kt)("a",{parentName:"p",href:"https://aka.ms/learnlive-contoso-app-deconstructed-Ep1"},"Episode 1")," and ",(0,n.kt)("a",{parentName:"p",href:"https://aka.ms/learnlive-contoso-app-deconstructed-Ep2"},"Episode 2")," of the ",(0,n.kt)("a",{parentName:"p",href:"https://aka.ms/contoso-real-estate/learn-live"},"Learn Live series")," on how to build, test and deploy an end-to-end intelligent app solution using the ",(0,n.kt)("a",{parentName:"p",href:"https://github.com/Azure-Samples/contoso-real-estate"},"Contoso Real Estate Sample"),".")),(0,n.kt)("h3",{id:"event-driven-architectures-knowing-when-to-use-them"},"Event-Driven Architectures: Knowing When to Use Them"),(0,n.kt)("p",null,(0,n.kt)("a",{parentName:"p",href:"https://learn.microsoft.com/en-us/azure/architecture/guide/architecture-styles/event-driven?WT.mc_id=javascript-99907-ninarasi"},"Event-Driven Architectures")," (EDAs) can significantly enhance the performance and cost-effectiveness of intelligent apps. EDAs, typically implemented with microservices, respond in real time to events or state changes ranging from user interactions to real-time analytics."),(0,n.kt)("p",null,"Consider an intelligent app designed for personalized customer engagement. Instead of designing the application to check continuously for changes in user data, an EDA can react to such changes immediately, triggering appropriate microservices. For example, if users modify their preferences, an event could trigger an AI-driven recommendation microservice to update its suggestions immediately. This real-time responsiveness, enabled by services like ",(0,n.kt)("a",{parentName:"p",href:"https://azure.microsoft.com/en-us/products/event-grid/?WT.mc_id=javascript-99907-ninarasi"},"Azure Event Grid"),", can improve user experiences and make intelligent apps more adaptive and proactive."),(0,n.kt)("p",null,"However, it\u2019s worth noting that while EDAs provide significant benefits, they can introduce unique complexities, such as maintaining event consistency and debugging issues in event chains across multiple microservices. Therefore, it\u2019s important to assess your application\u2019s needs so you can understand where it can benefit from an EDA."),(0,n.kt)("h2",{id:"conclusion"},"Conclusion"),(0,n.kt)("p",null,"Transitioning to intelligent apps represents a shift in strategy, architecture, and infrastructure. By designing and re-architecting for cloud-based infrastructure, your apps gain scalability, cost-effectiveness, robust data management, access to sophisticated AI tools, enhanced security, and reduced operational overhead.\u202f\u202f "),(0,n.kt)("p",null,"But this transition extends well beyond its technical components. Organizations and the people working within them need equal attention. You can explore these facets in greater depth in \u201cCultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management.\u201d\u202f "),(0,n.kt)("p",null,"The power of generative AI is here, and preparing for the move toward cloud-based intelligent applications will ensure that your apps remain performative and scalable and\u2014most importantly\u2014that your business is ready for the newest era of app innovation."),(0,n.kt)("h2",{id:"exercise"},"Exercise"),(0,n.kt)("ul",null,(0,n.kt)("li",{parentName:"ul"},"Complete the ",(0,n.kt)("strong",{parentName:"li"},(0,n.kt)("a",{parentName:"strong",href:"https://aka.ms/fallforIA/apps-csc"},"Apps Cloud Skills Challenge"))," to build on your app dev and AI skills."),(0,n.kt)("li",{parentName:"ul"},"Complete the ",(0,n.kt)("strong",{parentName:"li"},(0,n.kt)("a",{parentName:"strong",href:"https://aka.ms/fallforIA/ai-csc"},"AI Cloud Skills Challenge"))," to build on your AI skills."),(0,n.kt)("li",{parentName:"ul"},"Register for ",(0,n.kt)("strong",{parentName:"li"},(0,n.kt)("a",{parentName:"strong",href:"https://aka.ms/learnlive-contoso-app-deconstructed-Ep3"},"Episode 03"))," of the serverless edition Learn Live session to learn how to build, test and deploy an end-to-end intelligent app solution."),(0,n.kt)("li",{parentName:"ul"},"Register for ",(0,n.kt)("strong",{parentName:"li"},(0,n.kt)("a",{parentName:"strong",href:"https://reactor.microsoft.com/en-us/reactor/series/S-1037/"},"Ask the Expert: Azure Functions"))," session for live Q&A with the Product Engineering team on building intelligent serverless apps.")))}u.isMDXComponent=!0},22352:(e,t,a)=>{a.d(t,{Z:()=>i});const i=a.p+"assets/images/blog-image-1-5-8ad85f98f250a51ea0ccadefbdcf2237.png"},48199:(e,t,a)=>{a.d(t,{Z:()=>i});const i=a.p+"assets/images/diagram-of-a-demo-app-architecture-3451b86065584b95e8896c995070eeec.png"}}]); \ No newline at end of file diff --git a/assets/js/07a9616f.42467a5f.js b/assets/js/07a9616f.42467a5f.js new file mode 100644 index 0000000000..3b458d9818 --- /dev/null +++ b/assets/js/07a9616f.42467a5f.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[42392],{3905:(e,t,a)=>{a.d(t,{Zo:()=>c,kt:()=>g});var i=a(67294);function n(e,t,a){return t in e?Object.defineProperty(e,t,{value:a,enumerable:!0,configurable:!0,writable:!0}):e[t]=a,e}function r(e,t){var a=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);t&&(i=i.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),a.push.apply(a,i)}return a}function o(e){for(var t=1;t=0||(n[a]=e[a]);return n}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(i=0;i=0||Object.prototype.propertyIsEnumerable.call(e,a)&&(n[a]=e[a])}return n}var l=i.createContext({}),p=function(e){var t=i.useContext(l),a=t;return e&&(a="function"==typeof e?e(t):o(o({},t),e)),a},c=function(e){var t=p(e.components);return i.createElement(l.Provider,{value:t},e.children)},u={inlineCode:"code",wrapper:function(e){var t=e.children;return i.createElement(i.Fragment,{},t)}},d=i.forwardRef((function(e,t){var a=e.components,n=e.mdxType,r=e.originalType,l=e.parentName,c=s(e,["components","mdxType","originalType","parentName"]),d=p(a),g=n,m=d["".concat(l,".").concat(g)]||d[g]||u[g]||r;return a?i.createElement(m,o(o({ref:t},c),{},{components:a})):i.createElement(m,o({ref:t},c))}));function g(e,t){var a=arguments,n=t&&t.mdxType;if("string"==typeof e||n){var r=a.length,o=new Array(r);o[0]=d;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s.mdxType="string"==typeof e?e:n,o[1]=s;for(var p=2;p{a.r(t),a.d(t,{assets:()=>l,contentTitle:()=>o,default:()=>u,frontMatter:()=>r,metadata:()=>s,toc:()=>p});var i=a(87462),n=(a(67294),a(3905));const r={date:"2023-09-22T09:00",slug:"preparing-the-path-for-intelligent-apps",title:"1-5. Preparing the Path for Intelligent Apps",authors:["cnteam"],draft:!1,hide_table_of_contents:!1,toc_min_heading_level:2,toc_max_heading_level:3,keywords:["Cloud","Data","AI","AI/ML","intelligent apps","cloud-native","30-days","enterprise apps","digital experiences","app modernization","serverless","ai apps","data"],image:"https://azure.github.io/Cloud-Native/img/ogImage.png",description:"Learn best practices for modernizing your on-premises or IaaS solutions to intelligent apps in the cloud.",tags:["Fall-For-IA","30-days-of-IA","learn-live","hack-together","community-buzz","ask-the-expert","azure-kubernetes-service","azure-functions","azure-openai","azure-container-apps","azure-cosmos-db","github-copilot","github-codespaces","github-actions"]},o=void 0,s={permalink:"/Cloud-Native/30daysofIA/preparing-the-path-for-intelligent-apps",source:"@site/blog-30daysofIA/2023-09-22/preparing-the-path-for-intelligent-apps.md",title:"1-5. Preparing the Path for Intelligent Apps",description:"Learn best practices for modernizing your on-premises or IaaS solutions to intelligent apps in the cloud.",date:"2023-09-22T09:00:00.000Z",formattedDate:"September 22, 2023",tags:[{label:"Fall-For-IA",permalink:"/Cloud-Native/30daysofIA/tags/fall-for-ia"},{label:"30-days-of-IA",permalink:"/Cloud-Native/30daysofIA/tags/30-days-of-ia"},{label:"learn-live",permalink:"/Cloud-Native/30daysofIA/tags/learn-live"},{label:"hack-together",permalink:"/Cloud-Native/30daysofIA/tags/hack-together"},{label:"community-buzz",permalink:"/Cloud-Native/30daysofIA/tags/community-buzz"},{label:"ask-the-expert",permalink:"/Cloud-Native/30daysofIA/tags/ask-the-expert"},{label:"azure-kubernetes-service",permalink:"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service"},{label:"azure-functions",permalink:"/Cloud-Native/30daysofIA/tags/azure-functions"},{label:"azure-openai",permalink:"/Cloud-Native/30daysofIA/tags/azure-openai"},{label:"azure-container-apps",permalink:"/Cloud-Native/30daysofIA/tags/azure-container-apps"},{label:"azure-cosmos-db",permalink:"/Cloud-Native/30daysofIA/tags/azure-cosmos-db"},{label:"github-copilot",permalink:"/Cloud-Native/30daysofIA/tags/github-copilot"},{label:"github-codespaces",permalink:"/Cloud-Native/30daysofIA/tags/github-codespaces"},{label:"github-actions",permalink:"/Cloud-Native/30daysofIA/tags/github-actions"}],readingTime:8.59,hasTruncateMarker:!1,authors:[{name:"It's 30DaysOfIA",title:"FallForIA Content Team",url:"https://azure.github.io/Cloud-Native/Fall-For-IA/",imageURL:"https://azure.github.io/Cloud-Native/img/logo-ms-cloud-native.png",key:"cnteam"}],frontMatter:{date:"2023-09-22T09:00",slug:"preparing-the-path-for-intelligent-apps",title:"1-5. Preparing the Path for Intelligent Apps",authors:["cnteam"],draft:!1,hide_table_of_contents:!1,toc_min_heading_level:2,toc_max_heading_level:3,keywords:["Cloud","Data","AI","AI/ML","intelligent apps","cloud-native","30-days","enterprise apps","digital experiences","app modernization","serverless","ai apps","data"],image:"https://azure.github.io/Cloud-Native/img/ogImage.png",description:"Learn best practices for modernizing your on-premises or IaaS solutions to intelligent apps in the cloud.",tags:["Fall-For-IA","30-days-of-IA","learn-live","hack-together","community-buzz","ask-the-expert","azure-kubernetes-service","azure-functions","azure-openai","azure-container-apps","azure-cosmos-db","github-copilot","github-codespaces","github-actions"]},prevItem:{title:"1-6. Cultivating a Culture for Intelligent Apps",permalink:"/Cloud-Native/30daysofIA/cultivating-a-culture-for-intelligent-apps"},nextItem:{title:"2-1. Build Your First Intelligent App with Azure AI and AKS-1",permalink:"/Cloud-Native/30daysofIA/build-your-first-intelligent-app-with-azure-ai-and-aks-1"}},l={authorsImageUrls:[void 0]},p=[{value:"What We'll Cover:",id:"what-well-cover",level:2},{value:"Preparing the Path for Intelligent Apps: Transitioning from On-Premises/IaaS to Cloud-Native Applications",id:"preparing-the-path-for-intelligent-apps-transitioning-from-on-premisesiaas-to-cloud-native-applications",level:2},{value:"Understanding the Shift: Traditional vs. Intelligent Applications",id:"understanding-the-shift-traditional-vs-intelligent-applications",level:2},{value:"Functionality and User Experience",id:"functionality-and-user-experience",level:3},{value:"Infrastructure",id:"infrastructure",level:3},{value:"On-Premises/IaaS vs. Cloud-Native Platforms",id:"on-premisesiaas-vs-cloud-native-platforms",level:3},{value:"Strategic Considerations for Transitioning to Intelligent Apps",id:"strategic-considerations-for-transitioning-to-intelligent-apps",level:3},{value:"Re-Architecting Monolithic Apps into Microservices",id:"re-architecting-monolithic-apps-into-microservices",level:3},{value:"Event-Driven Architectures: Knowing When to Use Them",id:"event-driven-architectures-knowing-when-to-use-them",level:3},{value:"Conclusion",id:"conclusion",level:2},{value:"Exercise",id:"exercise",level:2}],c={toc:p};function u(e){let{components:t,...r}=e;return(0,n.kt)("wrapper",(0,i.Z)({},c,r,{components:t,mdxType:"MDXLayout"}),(0,n.kt)("head",null,(0,n.kt)("meta",{property:"og:url",content:"https://azure.github.io/cloud-native/30daysofia/how-digital-natives-leverage-generative-ai"}),(0,n.kt)("meta",{property:"og:type",content:"website"}),(0,n.kt)("meta",{property:"og:title",content:"**Fall For Intelligent Apps! \ud83c\udf42| Build AI Apps On Azure"}),(0,n.kt)("meta",{property:"og:description",content:"Explore how digital natives and Independent Software Vendors (ISVs) can transform their business operations and generate higher value for customers with generative AI and intelligent apps."}),(0,n.kt)("meta",{property:"og:image",content:"https://azure.github.io/Cloud-Native/img/ogImage.png"}),(0,n.kt)("meta",{name:"twitter:url",content:"https://azure.github.io/Cloud-Native/30daysofIA/preparing-the-path-for-intelligent-apps"}),(0,n.kt)("meta",{name:"twitter:title",content:"**Fall For Intelligent Apps! \ud83c\udf42 | Build AI Apps On Azure"}),(0,n.kt)("meta",{name:"twitter:description",content:"1-5. Learn best practices for modernizing your on-premises or IaaS solutions to intelligent apps in the cloud."}),(0,n.kt)("meta",{name:"twitter:image",content:"https://azure.github.io/Cloud-Native/img/ogImage.png"}),(0,n.kt)("meta",{name:"twitter:card",content:"summary_large_image"}),(0,n.kt)("meta",{name:"twitter:creator",content:"@devanshidiaries"}),(0,n.kt)("meta",{name:"twitter:site",content:"@AzureAdvocates"}),(0,n.kt)("link",{rel:"canonical",href:"https://azure.github.io/Cloud-Native/30daysofIA/preparing-the-path-for-intelligent-apps"})),(0,n.kt)("p",null,"Learn best practices for modernizing your on-premises or IaaS solutions to intelligent apps in the cloud."),(0,n.kt)("h2",{id:"what-well-cover"},"What We'll Cover:"),(0,n.kt)("ul",null,(0,n.kt)("li",{parentName:"ul"},"Traditional vs Intelligent Apps"),(0,n.kt)("li",{parentName:"ul"},"On-Premises/IaaS vs. Cloud-Native Platforms"),(0,n.kt)("li",{parentName:"ul"},"Modernization strategy for building Intelligent Apps")),(0,n.kt)("p",null,(0,n.kt)("img",{alt:"image of modernizing AI solutions for intelligent apps",src:a(22352).Z,width:"624",height:"380"})),(0,n.kt)("h2",{id:"preparing-the-path-for-intelligent-apps-transitioning-from-on-premisesiaas-to-cloud-native-applications"},"Preparing the Path for Intelligent Apps: Transitioning from On-Premises/IaaS to Cloud-Native Applications"),(0,n.kt)("p",null,"The proliferation of generative AI is paving the way for intelligent applications, software that leverages AI capabilities to deliver unparalleled functionality and user experience. As we covered in a previous article, \u201c",(0,n.kt)("a",{parentName:"p",href:"https://azure.github.io/Cloud-Native/30daysofIA/demystifying-intelligent-applications"},"Demystifying Intelligent Applications: Leveraging AI in App Development"),",\u201d intelligent Apps aren\u2019t just products of advanced coding. They interact, learn, and evolve thanks to modern AI and machine learning (ML) breakthroughs.\u202f "),(0,n.kt)("p",null,"The paradigm shift from traditional to intelligent apps demands that we change our approach to the technical challenges of software and architecture design. It also necessitates adopting new ways of thinking and operating within an organization, which we explore in greater depth in \u201c",(0,n.kt)("a",{parentName:"p",href:"https://azure.github.io/Cloud-Native/30daysofIA/cultivating-a-culture-for-intelligent-apps"},"Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management"),".\u201d\u202f "),(0,n.kt)("p",null,"This article explores the technical infrastructure powering intelligent apps, providing a roadmap to transition traditional on-premises or Infrastructure as a Service (IaaS) solutions to intelligent apps deployed to cloud native platforms and services. We\u2019ll discover the differences between conventional and intelligent apps, options for re-architecting existing applications, and critical strategic considerations integral to modernizing the application-building approach.\u202f "),(0,n.kt)("h2",{id:"understanding-the-shift-traditional-vs-intelligent-applications"},"Understanding the Shift: Traditional vs. Intelligent Applications"),(0,n.kt)("p",null,"Intelligent apps are more than just an incremental evolution from traditional apps. They involve a fundamental shift in software solution design, development, and deployment. A clear understanding of how each paradigm differs is crucial for comprehending the impact of this transition.\u202f "),(0,n.kt)("h3",{id:"functionality-and-user-experience"},"Functionality and User Experience"),(0,n.kt)("p",null,"Traditional applications, often rule-based and rigid, rely on preprogrammed operations. A traditional weather app, for example, typically presents structured, location-based forecasts using a predetermined data source. Meanwhile, an intelligent app leverages AI for data-driven decision-making and personalization. Rather than merely displaying the forecast, it integrates ML with additional data sources\u2014like a user\u2019s calendar and fitness data\u2014to determine their favorite outdoor activities, provide relevant clothing suggestions, and even automatically design ideal vacations and times based on the user\u2019s optimal weather preferences.\u202f "),(0,n.kt)("p",null,"Similarly, while traditional applications tend to offer a one-size-fits-all UI, intelligent apps use AI to create tailored interactions. For instance, an intelligent banking app might use generative AI to provide a voice-enabled UI that lets customers use natural language to ask about transactions and receive personalized financial advice."),(0,n.kt)("h3",{id:"infrastructure"},"Infrastructure"),(0,n.kt)("p",null,"Intelligent apps integrating these sophisticated features require highly scalable, flexible infrastructure optimized for AI functionality and able to manage large volumes of real-time data. Unlike traditional applications deployed on-premises or in infrastructure as a service (IaaS) environments, intelligent apps demand the flexibility and scalability of cloud native architectures."),(0,n.kt)("p",null,"An on-premises or IaaS-based e-commerce application may struggle with connecting its data to AI services or scaling to accommodate voluminous real-time data and requests. But an intelligent app built with a cloud native approach and scalability at its core can handle variable demand much more efficiently. It also maintains its infrastructure components on demand, whether for collecting data for AI/ML consumption or user-facing features like AI-enabled customer support and personalization."),(0,n.kt)("p",null,"Consider an intelligent app built on Azure that creates ChatGPT-like experiences with custom data. It might connect ",(0,n.kt)("a",{parentName:"p",href:"https://azure.microsoft.com/en-us/products/ai-services/openai-service?WT.mc_id=javascript-99907-ninarasi"},"Azure Open AI")," and ",(0,n.kt)("a",{parentName:"p",href:"https://azure.microsoft.com/en-us/products/ai-services/cognitive-search?WT.mc_id=javascript-99907-ninarasi"},"Azure Cognitive Search")," to an app running on ",(0,n.kt)("a",{parentName:"p",href:"https://azure.microsoft.com/en-us/products/container-apps/?WT.mc_id=javascript-99907-ninarasi"},"Azure Container Apps")," or ",(0,n.kt)("a",{parentName:"p",href:"https://azure.microsoft.com/en-us/products/kubernetes-service/?WT.mc_id=javascript-99907-ninarasi"},"Azure Kubernetes Service"),".\u202f "),(0,n.kt)("p",null,"The architecture might look like the following diagram based on this ",(0,n.kt)("a",{parentName:"p",href:"https://github.com/Azure-Samples/azure-search-openai-demo-csharp"},"demo app"),":"),(0,n.kt)("p",null,(0,n.kt)("img",{alt:"diagram of a demo app architecture",src:a(48199).Z,width:"624",height:"386"})),(0,n.kt)("p",null,"While the cloud is well-equipped to handle the challenges associated with shifting to AI-centric infrastructure\u2014handling real-time data processing and storage, integrating and managing AI services, configuring event-driven and microservices architectures, and ensuring security across the distributed environment\u2014re-architecting a traditional app to an intelligent app requires careful planning and execution to ensure it benefits fully from the capabilities of AI and cloud computing."),(0,n.kt)("h3",{id:"on-premisesiaas-vs-cloud-native-platforms"},"On-Premises/IaaS vs. Cloud-Native Platforms"),(0,n.kt)("p",null,"While on-premises and IaaS solutions provide substantial control over data and processes, this fine-grained control comes with setup and maintenance costs and may lack the scalability necessary for AI-driven Intelligent Apps. On-premises hosting involves purchasing and maintaining hardware, including servers and storage systems. IaaS involves leasing virtual hardware and then hiring and training IT personnel for the infrastructure\u2019s operation, maintenance, and security."),(0,n.kt)("p",null,"Conversely, infrastructure built on cloud native app services with ",(0,n.kt)("a",{parentName:"p",href:"https://azure.microsoft.com/?WT.mc_id=javascript-99907-ninarasi"},"Microsoft Azure")," offers virtually unlimited scalability without ongoing hardware and maintenance costs. This feature is ideal for intelligent apps that need to adapt and scale to accommodate fluctuating workloads and data streams. And because these cloud infrastructures typically operate on a pay-as-you-go model, traditionally substantial upfront costs turn into predictable operating expenses, lowering both the cost and risk of launching applications."),(0,n.kt)("p",null,"Moreover, cloud service providers continually update their offerings, so building on sophisticated AI tools and services through the cloud, like the ",(0,n.kt)("a",{parentName:"p",href:"https://azure.microsoft.com/en-us/solutions/ai/?WT.mc_id=javascript-99907-ninarasi"},"Azure AI Platform")," and orchestration capabilities with ",(0,n.kt)("a",{parentName:"p",href:"https://azure.microsoft.com/en-us/products/kubernetes-service/?WT.mc_id=javascript-99907-ninarasi"},"Azure Kubernetes Service"),", ensures that you have access to the latest AI and most advanced AI models and technologies."),(0,n.kt)("h3",{id:"strategic-considerations-for-transitioning-to-intelligent-apps"},"Strategic Considerations for Transitioning to Intelligent Apps"),(0,n.kt)("p",null,"When developing a strategy for transitioning to intelligent apps, you must consider costs, personnel skill development, and data management."),(0,n.kt)("p",null,"Monolithic architectures are unlikely to support the scale and ease of iteration required by intelligent apps, so you\u2019ll likely need to implement code changes to support the transition. Your chosen hosting solution and the amount of data your app will handle affect cost and performance, in addition to determining privacy and compliance requirements."),(0,n.kt)("p",null,"An app that manages or stores terabytes or petabytes of data will require storage-optimized virtual machines or a managed database service hosting. Meanwhile, an app that processes video content or performs resource-intensive calculations should prioritize high-performance computing. Maximizing storage and performance can become extraordinarily costly."),(0,n.kt)("p",null,"Finally, existing applications shifting towards intelligent apps may encounter compatibility conflicts, depending on database type or the existing system\u2019s underlying programming language. As you might imagine, a traditional app running on-premises and using a shared local database may need significant restructuring to support a cloud native architecture. You need plans for data migration, potential downtime, and training."),(0,n.kt)("p",null,"Next, let\u2019s look at this transformation process."),(0,n.kt)("h3",{id:"re-architecting-monolithic-apps-into-microservices"},"Re-Architecting Monolithic Apps into Microservices"),(0,n.kt)("p",null,"Shifting from ",(0,n.kt)("a",{parentName:"p",href:"https://learn.microsoft.com/en-us/dotnet/architecture/containerized-lifecycle/design-develop-containerized-apps/monolithic-applications?WT.mc_id=javascript-99907-ninarasi"},"monolithic applications")," to microservices is pivotal for transitioning to Intelligent Apps, and you can use this shift as an opportunity to transform your legacy software into intelligent apps that leverage the flexibility and power of microservices-enabled AI solutions."),(0,n.kt)("p",null,"Traditionally, a monolithic application (let\u2019s use an online marketplace as an example) might handle all activities\u2014from serving webpages to processing payments\u2014within an on-premises server or a cloud server. However, this approach is challenging to scale and remains vulnerable to network failures or cyberattacks."),(0,n.kt)("p",null,"Breaking these applications into multiple services with specific responsibilities, such as authentication or notifications, can overcome these limitations. These services are typically deployed as microservices within container environments like ",(0,n.kt)("a",{parentName:"p",href:"https://azure.microsoft.com/en-us/products/kubernetes-service/?WT.mc_id=javascript-99907-ninarasi"},"Azure Kubernetes Service")," or serverless platforms like ",(0,n.kt)("a",{parentName:"p",href:"https://azure.microsoft.com/en-us/products/container-apps?WT.mc_id=javascript-99907-ninarasi"},"Azure Container Apps")," or ",(0,n.kt)("a",{parentName:"p",href:"https://azure.microsoft.com/en-us/products/functions/?WT.mc_id=javascript-99907-ninarasi"},"Azure Functions")," using automated processes. This approach offers scalability, robustness, and improved performance, as each microservice can run independently and use its own database."),(0,n.kt)("p",null,"Shifting towards smaller, independent microservices in app architectures allows us to reap the benefits of AI-powered intelligent apps, for example, by using microservices to handle AI tasks such as processing natural language input, analyzing behavioral data, or personalizing content."),(0,n.kt)("admonition",{type:"info"},(0,n.kt)("p",{parentName:"admonition"},"Watch ",(0,n.kt)("a",{parentName:"p",href:"https://aka.ms/learnlive-contoso-app-deconstructed-Ep1"},"Episode 1")," and ",(0,n.kt)("a",{parentName:"p",href:"https://aka.ms/learnlive-contoso-app-deconstructed-Ep2"},"Episode 2")," of the ",(0,n.kt)("a",{parentName:"p",href:"https://aka.ms/contoso-real-estate/learn-live"},"Learn Live series")," on how to build, test and deploy an end-to-end intelligent app solution using the ",(0,n.kt)("a",{parentName:"p",href:"https://github.com/Azure-Samples/contoso-real-estate"},"Contoso Real Estate Sample"),".")),(0,n.kt)("h3",{id:"event-driven-architectures-knowing-when-to-use-them"},"Event-Driven Architectures: Knowing When to Use Them"),(0,n.kt)("p",null,(0,n.kt)("a",{parentName:"p",href:"https://learn.microsoft.com/en-us/azure/architecture/guide/architecture-styles/event-driven?WT.mc_id=javascript-99907-ninarasi"},"Event-Driven Architectures")," (EDAs) can significantly enhance the performance and cost-effectiveness of intelligent apps. EDAs, typically implemented with microservices, respond in real time to events or state changes ranging from user interactions to real-time analytics."),(0,n.kt)("p",null,"Consider an intelligent app designed for personalized customer engagement. Instead of designing the application to check continuously for changes in user data, an EDA can react to such changes immediately, triggering appropriate microservices. For example, if users modify their preferences, an event could trigger an AI-driven recommendation microservice to update its suggestions immediately. This real-time responsiveness, enabled by services like ",(0,n.kt)("a",{parentName:"p",href:"https://azure.microsoft.com/en-us/products/event-grid/?WT.mc_id=javascript-99907-ninarasi"},"Azure Event Grid"),", can improve user experiences and make intelligent apps more adaptive and proactive."),(0,n.kt)("p",null,"However, it\u2019s worth noting that while EDAs provide significant benefits, they can introduce unique complexities, such as maintaining event consistency and debugging issues in event chains across multiple microservices. Therefore, it\u2019s important to assess your application\u2019s needs so you can understand where it can benefit from an EDA."),(0,n.kt)("h2",{id:"conclusion"},"Conclusion"),(0,n.kt)("p",null,"Transitioning to intelligent apps represents a shift in strategy, architecture, and infrastructure. By designing and re-architecting for cloud-based infrastructure, your apps gain scalability, cost-effectiveness, robust data management, access to sophisticated AI tools, enhanced security, and reduced operational overhead.\u202f\u202f "),(0,n.kt)("p",null,"But this transition extends well beyond its technical components. Organizations and the people working within them need equal attention. You can explore these facets in greater depth in \u201cCultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management.\u201d\u202f "),(0,n.kt)("p",null,"The power of generative AI is here, and preparing for the move toward cloud-based intelligent applications will ensure that your apps remain performative and scalable and\u2014most importantly\u2014that your business is ready for the newest era of app innovation."),(0,n.kt)("h2",{id:"exercise"},"Exercise"),(0,n.kt)("ul",null,(0,n.kt)("li",{parentName:"ul"},"Complete the ",(0,n.kt)("strong",{parentName:"li"},(0,n.kt)("a",{parentName:"strong",href:"https://aka.ms/fallforIA/apps-csc"},"Apps Cloud Skills Challenge"))," to build on your app dev and AI skills."),(0,n.kt)("li",{parentName:"ul"},"Complete the ",(0,n.kt)("strong",{parentName:"li"},(0,n.kt)("a",{parentName:"strong",href:"https://aka.ms/fallforIA/ai-csc"},"AI Cloud Skills Challenge"))," to build on your AI skills."),(0,n.kt)("li",{parentName:"ul"},"Register for ",(0,n.kt)("strong",{parentName:"li"},(0,n.kt)("a",{parentName:"strong",href:"https://aka.ms/learnlive-contoso-app-deconstructed-Ep3"},"Episode 03"))," of the serverless edition Learn Live session to learn how to build, test and deploy an end-to-end intelligent app solution."),(0,n.kt)("li",{parentName:"ul"},"Register for ",(0,n.kt)("strong",{parentName:"li"},(0,n.kt)("a",{parentName:"strong",href:"https://reactor.microsoft.com/en-us/reactor/series/S-1037/"},"Ask the Expert: Azure Functions"))," session for live Q&A with the Product Engineering team on building intelligent serverless apps.")))}u.isMDXComponent=!0},22352:(e,t,a)=>{a.d(t,{Z:()=>i});const i=a.p+"assets/images/blog-image-1-5-8ad85f98f250a51ea0ccadefbdcf2237.png"},48199:(e,t,a)=>{a.d(t,{Z:()=>i});const i=a.p+"assets/images/diagram-of-a-demo-app-architecture-3451b86065584b95e8896c995070eeec.png"}}]); \ No newline at end of file diff --git a/assets/js/09bb4c1b.60f2b797.js b/assets/js/09bb4c1b.60f2b797.js new file mode 100644 index 0000000000..a2382ae5fb --- /dev/null +++ b/assets/js/09bb4c1b.60f2b797.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[77877],{98498:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/30-days-of-ia/page/4","page":4,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/30-days-of-ia/page/3","nextPage":"/Cloud-Native/30daysofIA/tags/30-days-of-ia/page/5","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/09bb4c1b.b0d72bcb.js b/assets/js/09bb4c1b.b0d72bcb.js deleted file mode 100644 index 2059680a97..0000000000 --- a/assets/js/09bb4c1b.b0d72bcb.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[77877],{98498:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/30-days-of-ia/page/4","page":4,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/30-days-of-ia/page/3","nextPage":"/Cloud-Native/30daysofIA/tags/30-days-of-ia/page/5","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/0a1ee2df.e815f469.js b/assets/js/0a1ee2df.1f90514b.js similarity index 72% rename from assets/js/0a1ee2df.e815f469.js rename to assets/js/0a1ee2df.1f90514b.js index 9f43b72481..d5775133ca 100644 --- a/assets/js/0a1ee2df.e815f469.js +++ b/assets/js/0a1ee2df.1f90514b.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[94304],{58966:e=>{e.exports=JSON.parse('{"label":"github-copilot","permalink":"/Cloud-Native/30daysofIA/tags/github-copilot","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[94304],{58966:e=>{e.exports=JSON.parse('{"label":"github-copilot","permalink":"/Cloud-Native/30daysofIA/tags/github-copilot","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/0a9d7c60.6d9a0e9d.js b/assets/js/0a9d7c60.6d9a0e9d.js new file mode 100644 index 0000000000..9acfd8f39b --- /dev/null +++ b/assets/js/0a9d7c60.6d9a0e9d.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[54491],{1043:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/page/8","page":8,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/page/7","nextPage":"/Cloud-Native/30daysofIA/page/9","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/0a9d7c60.74f6035d.js b/assets/js/0a9d7c60.74f6035d.js deleted file mode 100644 index 8d3d9022c6..0000000000 --- a/assets/js/0a9d7c60.74f6035d.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[54491],{1043:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/page/8","page":8,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/page/7","nextPage":"/Cloud-Native/30daysofIA/page/9","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/0b3e58d2.0b860b7c.js b/assets/js/0b3e58d2.0b860b7c.js new file mode 100644 index 0000000000..07fd2d4b08 --- /dev/null +++ b/assets/js/0b3e58d2.0b860b7c.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[26099],{82578:e=>{e.exports=JSON.parse('{"label":"azure-functions","permalink":"/Cloud-Native/30daysofIA/tags/azure-functions","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/0ba01ce9.5ea69834.js b/assets/js/0ba01ce9.5ea69834.js new file mode 100644 index 0000000000..fe12432eb7 --- /dev/null +++ b/assets/js/0ba01ce9.5ea69834.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[39402],{73990:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/github-codespaces/page/3","page":3,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/github-codespaces/page/2","nextPage":"/Cloud-Native/30daysofIA/tags/github-codespaces/page/4","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/0ba01ce9.96711b0f.js b/assets/js/0ba01ce9.96711b0f.js deleted file mode 100644 index 8df8272f3e..0000000000 --- a/assets/js/0ba01ce9.96711b0f.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[39402],{73990:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/github-codespaces/page/3","page":3,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/github-codespaces/page/2","nextPage":"/Cloud-Native/30daysofIA/tags/github-codespaces/page/4","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/0c929776.14e0a845.js b/assets/js/0c929776.14e0a845.js new file mode 100644 index 0000000000..5560b34491 --- /dev/null +++ b/assets/js/0c929776.14e0a845.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[15536],{12074:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-functions/page/6","page":6,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/azure-functions/page/5","nextPage":"/Cloud-Native/30daysofIA/tags/azure-functions/page/7","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/0c929776.f3c0971a.js b/assets/js/0c929776.f3c0971a.js deleted file mode 100644 index 3f8ed98d9a..0000000000 --- a/assets/js/0c929776.f3c0971a.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[15536],{12074:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-functions/page/6","page":6,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/azure-functions/page/5","nextPage":"/Cloud-Native/30daysofIA/tags/azure-functions/page/7","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/0cc5e40a.a73c5335.js b/assets/js/0cc5e40a.634e4e94.js similarity index 95% rename from assets/js/0cc5e40a.a73c5335.js rename to assets/js/0cc5e40a.634e4e94.js index 1a8a28fe36..045c9fcc30 100644 --- a/assets/js/0cc5e40a.a73c5335.js +++ b/assets/js/0cc5e40a.634e4e94.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[95047],{19139:s=>{s.exports=JSON.parse('{"label":"azure-cosmos-db","permalink":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[95047],{19139:s=>{s.exports=JSON.parse('{"label":"azure-cosmos-db","permalink":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/0db3c1f5.6f88c760.js b/assets/js/0db3c1f5.881974f2.js similarity index 73% rename from assets/js/0db3c1f5.6f88c760.js rename to assets/js/0db3c1f5.881974f2.js index e4ed5cbd13..e7f40d0ef0 100644 --- a/assets/js/0db3c1f5.6f88c760.js +++ b/assets/js/0db3c1f5.881974f2.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[29741],{14602:e=>{e.exports=JSON.parse('{"label":"azure-openai","permalink":"/Cloud-Native/30daysofIA/tags/azure-openai","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[29741],{14602:e=>{e.exports=JSON.parse('{"label":"azure-openai","permalink":"/Cloud-Native/30daysofIA/tags/azure-openai","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/0dcb0a11.0135dcb7.js b/assets/js/0dcb0a11.0135dcb7.js new file mode 100644 index 0000000000..d13b8c9ea3 --- /dev/null +++ b/assets/js/0dcb0a11.0135dcb7.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[72157],{78721:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/page/6","page":6,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/page/5","nextPage":"/Cloud-Native/30daysofIA/page/7","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/0dcb0a11.d27a53ae.js b/assets/js/0dcb0a11.d27a53ae.js deleted file mode 100644 index f4ec8d2117..0000000000 --- a/assets/js/0dcb0a11.d27a53ae.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[72157],{78721:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/page/6","page":6,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/page/5","nextPage":"/Cloud-Native/30daysofIA/page/7","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/0dfdf1d3.0805e944.js b/assets/js/0dfdf1d3.0805e944.js deleted file mode 100644 index d89387f46c..0000000000 --- a/assets/js/0dfdf1d3.0805e944.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[40122],{36165:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/ask-the-expert/page/4","page":4,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/ask-the-expert/page/3","nextPage":"/Cloud-Native/30daysofIA/tags/ask-the-expert/page/5","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/0dfdf1d3.91931fff.js b/assets/js/0dfdf1d3.91931fff.js new file mode 100644 index 0000000000..bcf4868520 --- /dev/null +++ b/assets/js/0dfdf1d3.91931fff.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[40122],{36165:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/ask-the-expert/page/4","page":4,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/ask-the-expert/page/3","nextPage":"/Cloud-Native/30daysofIA/tags/ask-the-expert/page/5","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/0e655584.9955d0b5.js b/assets/js/0e655584.62e917d4.js similarity index 89% rename from assets/js/0e655584.9955d0b5.js rename to assets/js/0e655584.62e917d4.js index 510641c9d2..e50e9a5aa4 100644 --- a/assets/js/0e655584.9955d0b5.js +++ b/assets/js/0e655584.62e917d4.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[90539],{77666:e=>{e.exports=JSON.parse('{"label":"azure-kubernetes-service","permalink":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[90539],{77666:e=>{e.exports=JSON.parse('{"label":"azure-kubernetes-service","permalink":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/0f41348e.36120cad.js b/assets/js/0f41348e.36120cad.js new file mode 100644 index 0000000000..0c69c4dd70 --- /dev/null +++ b/assets/js/0f41348e.36120cad.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[14971],{43792:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/github-copilot/page/11","page":11,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/github-copilot/page/10","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/11f27dd8.44e6cbc1.js b/assets/js/11f27dd8.44e6cbc1.js deleted file mode 100644 index 298bbb01b6..0000000000 --- a/assets/js/11f27dd8.44e6cbc1.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[87097],{2361:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/hack-together/page/2","page":2,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/hack-together","nextPage":"/Cloud-Native/30daysofIA/tags/hack-together/page/3","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/11f27dd8.9bc2b0f1.js b/assets/js/11f27dd8.9bc2b0f1.js new file mode 100644 index 0000000000..9d7ec423a9 --- /dev/null +++ b/assets/js/11f27dd8.9bc2b0f1.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[87097],{2361:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/hack-together/page/2","page":2,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/hack-together","nextPage":"/Cloud-Native/30daysofIA/tags/hack-together/page/3","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/1280d58f.23a1732b.js b/assets/js/1280d58f.23a1732b.js deleted file mode 100644 index 26e415d01d..0000000000 --- a/assets/js/1280d58f.23a1732b.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[37838],{68417:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/hack-together","page":1,"postsPerPage":1,"totalPages":9,"totalCount":9,"nextPage":"/Cloud-Native/30daysofIA/tags/hack-together/page/2","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/1280d58f.6de38eb5.js b/assets/js/1280d58f.6de38eb5.js new file mode 100644 index 0000000000..4342ffe1ec --- /dev/null +++ b/assets/js/1280d58f.6de38eb5.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[37838],{68417:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/hack-together","page":1,"postsPerPage":1,"totalPages":11,"totalCount":11,"nextPage":"/Cloud-Native/30daysofIA/tags/hack-together/page/2","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/1314fad1.8ed9ee95.js b/assets/js/1314fad1.46b934d3.js similarity index 72% rename from assets/js/1314fad1.8ed9ee95.js rename to assets/js/1314fad1.46b934d3.js index ebe063d60f..1edeea2e65 100644 --- a/assets/js/1314fad1.8ed9ee95.js +++ b/assets/js/1314fad1.46b934d3.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[29357],{61613:e=>{e.exports=JSON.parse('{"label":"ask-the-expert","permalink":"/Cloud-Native/30daysofIA/tags/ask-the-expert","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[29357],{61613:e=>{e.exports=JSON.parse('{"label":"ask-the-expert","permalink":"/Cloud-Native/30daysofIA/tags/ask-the-expert","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/138b7335.082758d9.js b/assets/js/138b7335.082758d9.js deleted file mode 100644 index c2bb45d62d..0000000000 --- a/assets/js/138b7335.082758d9.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[98588],{52254:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db/page/4","page":4,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db/page/3","nextPage":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db/page/5","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/138b7335.eb7fa37d.js b/assets/js/138b7335.eb7fa37d.js new file mode 100644 index 0000000000..35a007237f --- /dev/null +++ b/assets/js/138b7335.eb7fa37d.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[98588],{52254:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db/page/4","page":4,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db/page/3","nextPage":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db/page/5","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/1414d4c0.42afa8a5.js b/assets/js/1414d4c0.42afa8a5.js new file mode 100644 index 0000000000..5338eb810e --- /dev/null +++ b/assets/js/1414d4c0.42afa8a5.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[65413],{97245:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/ask-the-expert/page/10","page":10,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/ask-the-expert/page/9","nextPage":"/Cloud-Native/30daysofIA/tags/ask-the-expert/page/11","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/155d8733.7801785a.js b/assets/js/155d8733.684da0fe.js similarity index 72% rename from assets/js/155d8733.7801785a.js rename to assets/js/155d8733.684da0fe.js index 7aea785d6d..005b70c583 100644 --- a/assets/js/155d8733.7801785a.js +++ b/assets/js/155d8733.684da0fe.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[65487],{23110:e=>{e.exports=JSON.parse('{"label":"ask-the-expert","permalink":"/Cloud-Native/30daysofIA/tags/ask-the-expert","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[65487],{23110:e=>{e.exports=JSON.parse('{"label":"ask-the-expert","permalink":"/Cloud-Native/30daysofIA/tags/ask-the-expert","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/157ee2cb.798aeefb.js b/assets/js/157ee2cb.798aeefb.js new file mode 100644 index 0000000000..670367fae8 --- /dev/null +++ b/assets/js/157ee2cb.798aeefb.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[79864],{31578:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-container-apps/page/9","page":9,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/azure-container-apps/page/8","nextPage":"/Cloud-Native/30daysofIA/tags/azure-container-apps/page/10","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/157ee2cb.ffe6e9fa.js b/assets/js/157ee2cb.ffe6e9fa.js deleted file mode 100644 index f68a37937b..0000000000 --- a/assets/js/157ee2cb.ffe6e9fa.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[79864],{31578:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-container-apps/page/9","page":9,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/azure-container-apps/page/8","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/167f29d7.34a5755a.js b/assets/js/167f29d7.1f535b6c.js similarity index 73% rename from assets/js/167f29d7.34a5755a.js rename to assets/js/167f29d7.1f535b6c.js index 15e9404b7b..105a6cb3a3 100644 --- a/assets/js/167f29d7.34a5755a.js +++ b/assets/js/167f29d7.1f535b6c.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[45123],{20959:e=>{e.exports=JSON.parse('{"label":"azure-openai","permalink":"/Cloud-Native/30daysofIA/tags/azure-openai","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[45123],{20959:e=>{e.exports=JSON.parse('{"label":"azure-openai","permalink":"/Cloud-Native/30daysofIA/tags/azure-openai","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/16a2dce4.f4990f35.js b/assets/js/16a2dce4.f4990f35.js new file mode 100644 index 0000000000..9577129e8c --- /dev/null +++ b/assets/js/16a2dce4.f4990f35.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[39051],{67238:e=>{e.exports=JSON.parse('{"label":"hack-together","permalink":"/Cloud-Native/30daysofIA/tags/hack-together","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/184bf20b.fdbb968a.js b/assets/js/184bf20b.fdbb968a.js new file mode 100644 index 0000000000..3a35322bb0 --- /dev/null +++ b/assets/js/184bf20b.fdbb968a.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[21691],{5519:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/30-days-of-ia/page/11","page":11,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/30-days-of-ia/page/10","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/1864d48a.46bb768d.js b/assets/js/1864d48a.46bb768d.js deleted file mode 100644 index 7b5179a153..0000000000 --- a/assets/js/1864d48a.46bb768d.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[12584],{22869:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/community-buzz/page/3","page":3,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/community-buzz/page/2","nextPage":"/Cloud-Native/30daysofIA/tags/community-buzz/page/4","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/1864d48a.72ef5fc2.js b/assets/js/1864d48a.72ef5fc2.js new file mode 100644 index 0000000000..6457bf2242 --- /dev/null +++ b/assets/js/1864d48a.72ef5fc2.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[12584],{22869:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/community-buzz/page/3","page":3,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/community-buzz/page/2","nextPage":"/Cloud-Native/30daysofIA/tags/community-buzz/page/4","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/1a4e3b56.94112cbb.js b/assets/js/1a4e3b56.94112cbb.js new file mode 100644 index 0000000000..db97230023 --- /dev/null +++ b/assets/js/1a4e3b56.94112cbb.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[89677],{37873:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-openai","page":1,"postsPerPage":1,"totalPages":11,"totalCount":11,"nextPage":"/Cloud-Native/30daysofIA/tags/azure-openai/page/2","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/1a4e3b56.ba710a9e.js b/assets/js/1a4e3b56.ba710a9e.js deleted file mode 100644 index 29696621d8..0000000000 --- a/assets/js/1a4e3b56.ba710a9e.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[89677],{37873:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-openai","page":1,"postsPerPage":1,"totalPages":9,"totalCount":9,"nextPage":"/Cloud-Native/30daysofIA/tags/azure-openai/page/2","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/1b60c5ab.01f23419.js b/assets/js/1b60c5ab.01f23419.js new file mode 100644 index 0000000000..7420d69566 --- /dev/null +++ b/assets/js/1b60c5ab.01f23419.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[43832],{93747:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db/page/7","page":7,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db/page/6","nextPage":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db/page/8","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/1b60c5ab.aa889c53.js b/assets/js/1b60c5ab.aa889c53.js deleted file mode 100644 index 1fb885cd3c..0000000000 --- a/assets/js/1b60c5ab.aa889c53.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[43832],{93747:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db/page/7","page":7,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db/page/6","nextPage":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db/page/8","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/1b66ef97.007c766a.js b/assets/js/1b66ef97.007c766a.js new file mode 100644 index 0000000000..094bce6cc3 --- /dev/null +++ b/assets/js/1b66ef97.007c766a.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[26788],{70740:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-openai/page/8","page":8,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/azure-openai/page/7","nextPage":"/Cloud-Native/30daysofIA/tags/azure-openai/page/9","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/1b66ef97.ceaa36a6.js b/assets/js/1b66ef97.ceaa36a6.js deleted file mode 100644 index 121eee8986..0000000000 --- a/assets/js/1b66ef97.ceaa36a6.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[26788],{70740:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-openai/page/8","page":8,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/azure-openai/page/7","nextPage":"/Cloud-Native/30daysofIA/tags/azure-openai/page/9","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/1b81fd5d.02474c25.js b/assets/js/1b81fd5d.02474c25.js deleted file mode 100644 index ee61b511fb..0000000000 --- a/assets/js/1b81fd5d.02474c25.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[66240],{24858:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-openai/page/7","page":7,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/azure-openai/page/6","nextPage":"/Cloud-Native/30daysofIA/tags/azure-openai/page/8","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/1b81fd5d.8e6bd07c.js b/assets/js/1b81fd5d.8e6bd07c.js new file mode 100644 index 0000000000..b2e893eac2 --- /dev/null +++ b/assets/js/1b81fd5d.8e6bd07c.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[66240],{24858:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-openai/page/7","page":7,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/azure-openai/page/6","nextPage":"/Cloud-Native/30daysofIA/tags/azure-openai/page/8","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/1ba2ee3a.57da460a.js b/assets/js/1ba2ee3a.57da460a.js deleted file mode 100644 index d344ca305f..0000000000 --- a/assets/js/1ba2ee3a.57da460a.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[46145],{5794:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/hack-together/page/5","page":5,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/hack-together/page/4","nextPage":"/Cloud-Native/30daysofIA/tags/hack-together/page/6","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/1ba2ee3a.b9a76ace.js b/assets/js/1ba2ee3a.b9a76ace.js new file mode 100644 index 0000000000..1fd9ab10bb --- /dev/null +++ b/assets/js/1ba2ee3a.b9a76ace.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[46145],{5794:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/hack-together/page/5","page":5,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/hack-together/page/4","nextPage":"/Cloud-Native/30daysofIA/tags/hack-together/page/6","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/1c8f664c.3ffc5caa.js b/assets/js/1c8f664c.3ffc5caa.js deleted file mode 100644 index 73354ca52b..0000000000 --- a/assets/js/1c8f664c.3ffc5caa.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[61086],{3076:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service","page":1,"postsPerPage":1,"totalPages":9,"totalCount":9,"nextPage":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service/page/2","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/1c8f664c.5e59f258.js b/assets/js/1c8f664c.5e59f258.js new file mode 100644 index 0000000000..85215b7e83 --- /dev/null +++ b/assets/js/1c8f664c.5e59f258.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[61086],{3076:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service","page":1,"postsPerPage":1,"totalPages":11,"totalCount":11,"nextPage":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service/page/2","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/1c94b949.b446e3f0.js b/assets/js/1c94b949.504650e2.js similarity index 72% rename from assets/js/1c94b949.b446e3f0.js rename to assets/js/1c94b949.504650e2.js index e4d5f89560..aa2ba5e786 100644 --- a/assets/js/1c94b949.b446e3f0.js +++ b/assets/js/1c94b949.504650e2.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[46986],{95679:e=>{e.exports=JSON.parse('{"label":"community-buzz","permalink":"/Cloud-Native/30daysofIA/tags/community-buzz","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[46986],{95679:e=>{e.exports=JSON.parse('{"label":"community-buzz","permalink":"/Cloud-Native/30daysofIA/tags/community-buzz","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/1e1d1ee9.63f3fb3f.js b/assets/js/1e1d1ee9.0ca4cd05.js similarity index 90% rename from assets/js/1e1d1ee9.63f3fb3f.js rename to assets/js/1e1d1ee9.0ca4cd05.js index e9a99bd06b..7286e83122 100644 --- a/assets/js/1e1d1ee9.63f3fb3f.js +++ b/assets/js/1e1d1ee9.0ca4cd05.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[3639],{58634:e=>{e.exports=JSON.parse('{"label":"azure-kubernetes-service","permalink":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[3639],{58634:e=>{e.exports=JSON.parse('{"label":"azure-kubernetes-service","permalink":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/1e8313b4.1b2c0ac8.js b/assets/js/1e8313b4.0af591e7.js similarity index 73% rename from assets/js/1e8313b4.1b2c0ac8.js rename to assets/js/1e8313b4.0af591e7.js index 96eb85da68..cb9cbafb1c 100644 --- a/assets/js/1e8313b4.1b2c0ac8.js +++ b/assets/js/1e8313b4.0af591e7.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[77934],{70735:a=>{a.exports=JSON.parse('{"label":"30-days-of-IA","permalink":"/Cloud-Native/30daysofIA/tags/30-days-of-ia","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[77934],{70735:a=>{a.exports=JSON.parse('{"label":"30-days-of-IA","permalink":"/Cloud-Native/30daysofIA/tags/30-days-of-ia","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/1fbbba6a.67758059.js b/assets/js/1fbbba6a.67758059.js new file mode 100644 index 0000000000..ff3d7c1f71 --- /dev/null +++ b/assets/js/1fbbba6a.67758059.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[58794],{8654:s=>{s.exports=JSON.parse('{"label":"azure-cosmos-db","permalink":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/1fc5a212.b583e52d.js b/assets/js/1fc5a212.dd7baf7c.js similarity index 94% rename from assets/js/1fc5a212.b583e52d.js rename to assets/js/1fc5a212.dd7baf7c.js index 4fbceb69f4..924507c1be 100644 --- a/assets/js/1fc5a212.b583e52d.js +++ b/assets/js/1fc5a212.dd7baf7c.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[92404],{39519:e=>{e.exports=JSON.parse('{"label":"github-codespaces","permalink":"/Cloud-Native/30daysofIA/tags/github-codespaces","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[92404],{39519:e=>{e.exports=JSON.parse('{"label":"github-codespaces","permalink":"/Cloud-Native/30daysofIA/tags/github-codespaces","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/209bfeb4.1eaf4f44.js b/assets/js/209bfeb4.b791c5ad.js similarity index 95% rename from assets/js/209bfeb4.1eaf4f44.js rename to assets/js/209bfeb4.b791c5ad.js index dd6d24dfcc..45b6e4cc7f 100644 --- a/assets/js/209bfeb4.1eaf4f44.js +++ b/assets/js/209bfeb4.b791c5ad.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[85844],{28449:e=>{e.exports=JSON.parse('{"label":"azure-functions","permalink":"/Cloud-Native/30daysofIA/tags/azure-functions","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[85844],{28449:e=>{e.exports=JSON.parse('{"label":"azure-functions","permalink":"/Cloud-Native/30daysofIA/tags/azure-functions","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/22598362.42a670fd.js b/assets/js/22598362.42a670fd.js new file mode 100644 index 0000000000..e0e5a954f1 --- /dev/null +++ b/assets/js/22598362.42a670fd.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[66809],{84058:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/learn-live/page/4","page":4,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/learn-live/page/3","nextPage":"/Cloud-Native/30daysofIA/tags/learn-live/page/5","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/22598362.827d1ce6.js b/assets/js/22598362.827d1ce6.js deleted file mode 100644 index ae374e7f58..0000000000 --- a/assets/js/22598362.827d1ce6.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[66809],{84058:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/learn-live/page/4","page":4,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/learn-live/page/3","nextPage":"/Cloud-Native/30daysofIA/tags/learn-live/page/5","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/239f6fb3.d9b7909e.js b/assets/js/239f6fb3.d9b7909e.js new file mode 100644 index 0000000000..97d23eeeee --- /dev/null +++ b/assets/js/239f6fb3.d9b7909e.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[61131],{43189:e=>{e.exports=JSON.parse('{"label":"ask-the-expert","permalink":"/Cloud-Native/30daysofIA/tags/ask-the-expert","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/24634ed0.1840b116.js b/assets/js/24634ed0.c21c945a.js similarity index 72% rename from assets/js/24634ed0.1840b116.js rename to assets/js/24634ed0.c21c945a.js index d30e8ab05d..b12415c4df 100644 --- a/assets/js/24634ed0.1840b116.js +++ b/assets/js/24634ed0.c21c945a.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[31348],{17780:e=>{e.exports=JSON.parse('{"label":"community-buzz","permalink":"/Cloud-Native/30daysofIA/tags/community-buzz","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[31348],{17780:e=>{e.exports=JSON.parse('{"label":"community-buzz","permalink":"/Cloud-Native/30daysofIA/tags/community-buzz","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/24c13bf4.483b2910.js b/assets/js/24c13bf4.ab69752a.js similarity index 74% rename from assets/js/24c13bf4.483b2910.js rename to assets/js/24c13bf4.ab69752a.js index 2e9475a7d1..584796aaff 100644 --- a/assets/js/24c13bf4.483b2910.js +++ b/assets/js/24c13bf4.ab69752a.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[68630],{20629:a=>{a.exports=JSON.parse('{"label":"Fall-For-IA","permalink":"/Cloud-Native/30daysofIA/tags/fall-for-ia","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[68630],{20629:a=>{a.exports=JSON.parse('{"label":"Fall-For-IA","permalink":"/Cloud-Native/30daysofIA/tags/fall-for-ia","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/2508adfd.9d6e76bb.js b/assets/js/2508adfd.16666c3c.js similarity index 74% rename from assets/js/2508adfd.9d6e76bb.js rename to assets/js/2508adfd.16666c3c.js index f37ef1af69..837a65c31b 100644 --- a/assets/js/2508adfd.9d6e76bb.js +++ b/assets/js/2508adfd.16666c3c.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[41642],{78728:e=>{e.exports=JSON.parse('{"label":"learn-live","permalink":"/Cloud-Native/30daysofIA/tags/learn-live","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[41642],{78728:e=>{e.exports=JSON.parse('{"label":"learn-live","permalink":"/Cloud-Native/30daysofIA/tags/learn-live","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/259b3259.f50a6bd3.js b/assets/js/259b3259.f38a6caf.js similarity index 74% rename from assets/js/259b3259.f50a6bd3.js rename to assets/js/259b3259.f38a6caf.js index b8cdb0fe84..f9328d4a02 100644 --- a/assets/js/259b3259.f50a6bd3.js +++ b/assets/js/259b3259.f38a6caf.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[78078],{45812:e=>{e.exports=JSON.parse('{"label":"learn-live","permalink":"/Cloud-Native/30daysofIA/tags/learn-live","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[78078],{45812:e=>{e.exports=JSON.parse('{"label":"learn-live","permalink":"/Cloud-Native/30daysofIA/tags/learn-live","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/259ea4c2.d38b1087.js b/assets/js/259ea4c2.d38b1087.js new file mode 100644 index 0000000000..8be60e13df --- /dev/null +++ b/assets/js/259ea4c2.d38b1087.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[71901],{3905:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>d});var a=n(67294);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function r(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function o(e){for(var t=1;t=0||(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}var s=a.createContext({}),p=function(e){var t=a.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},u=function(e){var t=p(e.components);return a.createElement(s.Provider,{value:t},e.children)},c={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},m=a.forwardRef((function(e,t){var n=e.components,i=e.mdxType,r=e.originalType,s=e.parentName,u=l(e,["components","mdxType","originalType","parentName"]),m=p(n),d=i,g=m["".concat(s,".").concat(d)]||m[d]||c[d]||r;return n?a.createElement(g,o(o({ref:t},u),{},{components:n})):a.createElement(g,o({ref:t},u))}));function d(e,t){var n=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var r=n.length,o=new Array(r);o[0]=m;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l.mdxType="string"==typeof e?e:i,o[1]=l;for(var p=2;p{n.r(t),n.d(t,{assets:()=>s,contentTitle:()=>o,default:()=>c,frontMatter:()=>r,metadata:()=>l,toc:()=>p});var a=n(87462),i=(n(67294),n(3905));const r={date:"2023-09-22T09:00",slug:"build-your-first-intelligent-app-with-azure-ai-and-aks-1",title:"2-1. Build Your First Intelligent App with Azure AI and AKS-1",authors:["cnteam"],draft:!1,hide_table_of_contents:!1,toc_min_heading_level:2,toc_max_heading_level:3,keywords:["Cloud","Data","AI","AI/ML","intelligent apps","cloud-native","30-days","enterprise apps","digital experiences","app modernization","serverless","ai apps","data"],image:"https://azure.github.io/Cloud-Native/img/ogImage.png",description:"Delve into creating an Intelligent App that leverages Azure AI Vision to analyze images and extract data. We guide readers through crafting an API to perform optical character recognition (OCR) on uploaded images, and subsequently deploying this API via Azure Kubernetes Service, helping them discover the power of these tools to create innovative, AI-driven solutions.",tags:["Fall-For-IA","30-days-of-IA","learn-live","hack-together","community-buzz","ask-the-expert","azure-kubernetes-service","azure-functions","azure-openai","azure-container-apps","azure-cosmos-db","github-copilot","github-codespaces","github-actions"]},o=void 0,l={permalink:"/Cloud-Native/30daysofIA/build-your-first-intelligent-app-with-azure-ai-and-aks-1",source:"@site/blog-30daysofIA/2023-09-27/build-your-first-intelligent-app-with-azure-ai-and-aks-1.md",title:"2-1. Build Your First Intelligent App with Azure AI and AKS-1",description:"Delve into creating an Intelligent App that leverages Azure AI Vision to analyze images and extract data. We guide readers through crafting an API to perform optical character recognition (OCR) on uploaded images, and subsequently deploying this API via Azure Kubernetes Service, helping them discover the power of these tools to create innovative, AI-driven solutions.",date:"2023-09-22T09:00:00.000Z",formattedDate:"September 22, 2023",tags:[{label:"Fall-For-IA",permalink:"/Cloud-Native/30daysofIA/tags/fall-for-ia"},{label:"30-days-of-IA",permalink:"/Cloud-Native/30daysofIA/tags/30-days-of-ia"},{label:"learn-live",permalink:"/Cloud-Native/30daysofIA/tags/learn-live"},{label:"hack-together",permalink:"/Cloud-Native/30daysofIA/tags/hack-together"},{label:"community-buzz",permalink:"/Cloud-Native/30daysofIA/tags/community-buzz"},{label:"ask-the-expert",permalink:"/Cloud-Native/30daysofIA/tags/ask-the-expert"},{label:"azure-kubernetes-service",permalink:"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service"},{label:"azure-functions",permalink:"/Cloud-Native/30daysofIA/tags/azure-functions"},{label:"azure-openai",permalink:"/Cloud-Native/30daysofIA/tags/azure-openai"},{label:"azure-container-apps",permalink:"/Cloud-Native/30daysofIA/tags/azure-container-apps"},{label:"azure-cosmos-db",permalink:"/Cloud-Native/30daysofIA/tags/azure-cosmos-db"},{label:"github-copilot",permalink:"/Cloud-Native/30daysofIA/tags/github-copilot"},{label:"github-codespaces",permalink:"/Cloud-Native/30daysofIA/tags/github-codespaces"},{label:"github-actions",permalink:"/Cloud-Native/30daysofIA/tags/github-actions"}],readingTime:11.785,hasTruncateMarker:!1,authors:[{name:"It's 30DaysOfIA",title:"FallForIA Content Team",url:"https://azure.github.io/Cloud-Native/Fall-For-IA/",imageURL:"https://azure.github.io/Cloud-Native/img/logo-ms-cloud-native.png",key:"cnteam"}],frontMatter:{date:"2023-09-22T09:00",slug:"build-your-first-intelligent-app-with-azure-ai-and-aks-1",title:"2-1. Build Your First Intelligent App with Azure AI and AKS-1",authors:["cnteam"],draft:!1,hide_table_of_contents:!1,toc_min_heading_level:2,toc_max_heading_level:3,keywords:["Cloud","Data","AI","AI/ML","intelligent apps","cloud-native","30-days","enterprise apps","digital experiences","app modernization","serverless","ai apps","data"],image:"https://azure.github.io/Cloud-Native/img/ogImage.png",description:"Delve into creating an Intelligent App that leverages Azure AI Vision to analyze images and extract data. We guide readers through crafting an API to perform optical character recognition (OCR) on uploaded images, and subsequently deploying this API via Azure Kubernetes Service, helping them discover the power of these tools to create innovative, AI-driven solutions.",tags:["Fall-For-IA","30-days-of-IA","learn-live","hack-together","community-buzz","ask-the-expert","azure-kubernetes-service","azure-functions","azure-openai","azure-container-apps","azure-cosmos-db","github-copilot","github-codespaces","github-actions"]},prevItem:{title:"1-5. Preparing the Path for Intelligent Apps",permalink:"/Cloud-Native/30daysofIA/preparing-the-path-for-intelligent-apps"},nextItem:{title:"2-2. Build Your First Intelligent App with Azure AI and AKS-2",permalink:"/Cloud-Native/30daysofIA/build-your-first-intelligent-app-with-azure-ai-and-aks-2"}},s={authorsImageUrls:[void 0]},p=[{value:"What We'll Cover:",id:"what-well-cover",level:2},{value:"Jumpstart Your AI Journey: Building Your First Intelligent App with Azure AI and AKS (1)",id:"jumpstart-your-ai-journey-building-your-first-intelligent-app-with-azure-ai-and-aks-1",level:2},{value:"Understanding Azure AI Vision and Azure Kubernetes Service",id:"understanding-azure-ai-vision-and-azure-kubernetes-service",level:2},{value:"Prerequisites",id:"prerequisites",level:3},{value:"Building the API with Azure AI Vision Service",id:"building-the-api-with-azure-ai-vision-service",level:3},{value:"Configuring the Local Environment Variables",id:"configuring-the-local-environment-variables",level:4},{value:"Reviewing the Quickstart Code",id:"reviewing-the-quickstart-code",level:4},{value:"Implementing the REST API in Python",id:"implementing-the-rest-api-in-python",level:4},{value:"Running the Intelligent App Locally",id:"running-the-intelligent-app-locally",level:3},{value:"Deploying to Docker Desktop",id:"deploying-to-docker-desktop",level:4},{value:"Testing the Intelligent App Locally",id:"testing-the-intelligent-app-locally",level:4},{value:"Exercise",id:"exercise",level:2}],u={toc:p};function c(e){let{components:t,...r}=e;return(0,i.kt)("wrapper",(0,a.Z)({},u,r,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("head",null,(0,i.kt)("meta",{property:"og:url",content:"https://azure.github.io/cloud-native/30daysofia/build-your-first-intelligent-app-with-azure-ai-and-aks-1"}),(0,i.kt)("meta",{property:"og:type",content:"website"}),(0,i.kt)("meta",{property:"og:title",content:"**Fall For Intelligent Apps! \ud83c\udf42| Build AI Apps On Azure"}),(0,i.kt)("meta",{property:"og:description",content:"Create an Intelligent App that leverages Azure AI Vision to analyze images and extract data."}),(0,i.kt)("meta",{property:"og:image",content:"https://azure.github.io/Cloud-Native/img/ogImage.png"}),(0,i.kt)("meta",{name:"twitter:url",content:"https://azure.github.io/Cloud-Native/30daysofIA/build-your-first-intelligent-app-with-azure-ai-and-aks-1"}),(0,i.kt)("meta",{name:"twitter:title",content:"**Fall For Intelligent Apps! \ud83c\udf42 | Build AI Apps On Azure"}),(0,i.kt)("meta",{name:"twitter:description",content:"2-1. Create an Intelligent App that leverages Azure AI Vision to analyze images and extract data."}),(0,i.kt)("meta",{name:"twitter:image",content:"https://azure.github.io/Cloud-Native/img/ogImage.png"}),(0,i.kt)("meta",{name:"twitter:card",content:"summary_large_image"}),(0,i.kt)("meta",{name:"twitter:creator",content:"@devanshidiaries"}),(0,i.kt)("meta",{name:"twitter:site",content:"@AzureAdvocates"}),(0,i.kt)("link",{rel:"canonical",href:"https://azure.github.io/Cloud-Native/30daysofIA/build-your-first-intelligent-app-with-azure-ai-and-aks-1"})),(0,i.kt)("p",null,"Delve into creating an Intelligent App that leverages Azure AI Vision to analyze images and extract data. Develop an API to perform optical character recognition (OCR) on uploaded images and testing this API locally."),(0,i.kt)("h2",{id:"what-well-cover"},"What We'll Cover:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Understanding Azure AI Vision and Azure Kubernetes Service"),(0,i.kt)("li",{parentName:"ul"},"Build a Python Web API to perform OCR"),(0,i.kt)("li",{parentName:"ul"},"Test the API locally")),(0,i.kt)("p",null,(0,i.kt)("img",{alt:"image depicting an Intelligent App using AI",src:n(12177).Z,width:"936",height:"570"})),(0,i.kt)("h2",{id:"jumpstart-your-ai-journey-building-your-first-intelligent-app-with-azure-ai-and-aks-1"},"Jumpstart Your AI Journey: Building Your First Intelligent App with Azure AI and AKS (1)"),(0,i.kt)("p",null,"An Intelligent App is a software application that uses artificial intelligence (AI), such as machine learning (ML), to extract meaningful insights, enabling users to make informed decisions based on the extracted text, data, sound, images, and more."),(0,i.kt)("p",null,"Intelligent Apps can employ advanced algorithms and models to recognize objects, patterns, and features within images, allowing them to perform tasks like object identification, image classification, facial recognition, and image segmentation."),(0,i.kt)("p",null,"To explore the power of Intelligent Apps, let\u2019s build a Python app that performs ",(0,i.kt)("a",{parentName:"p",href:"https://learn.microsoft.com/en-us/azure/ai-services/computer-vision/overview-ocr?WT.mc_id=javascript-99907-ninarasi"},"optical character recognition")," (OCR) on images containing numbers and performs aggregate functions on the extracted numbers, like sum and average. We\u2019ll leverage ",(0,i.kt)("a",{parentName:"p",href:"https://azure.microsoft.com/en-ca/products/cognitive-services?WT.mc_id=javascript-99907-ninarasi"},"Azure AI Services")," for the OCR functionality and ",(0,i.kt)("a",{parentName:"p",href:"https://azure.microsoft.com/en-ca/products/kubernetes-service?WT.mc_id=javascript-99907-ninarasi"},"Azure Kubernetes Service")," (AKS) to develop, publish, and maintain a cloud-based, scalable app in Azure."),(0,i.kt)("p",null,"Let\u2019s get started!"),(0,i.kt)("h2",{id:"understanding-azure-ai-vision-and-azure-kubernetes-service"},"Understanding Azure AI Vision and Azure Kubernetes Service"),(0,i.kt)("p",null,"Azure AI Vision is a cloud-based service that offers a range of advanced computer vision capabilities enabling applications to interpret content, such as images and videos, using ML and other AI algorithms. Azure computer vision lets users integrate powerful vision features into their projects without ML experience."),(0,i.kt)("p",null,"OCR is one of the critical functionalities within Azure vision service, allowing applications to extract text from images. Within Azure AI Vision service, OCR offers several features, including text extraction, multilingual support, layout analysis of documents, and integration with Azure services, such as ",(0,i.kt)("a",{parentName:"p",href:"https://azure.microsoft.com/en-ca/products/functions"},"Azure Functions")," and ",(0,i.kt)("a",{parentName:"p",href:"https://azure.microsoft.com/en-ca/products/machine-learning"},"Azure Machine Learning"),"."),(0,i.kt)("admonition",{type:"info"},(0,i.kt)("p",{parentName:"admonition"},"Watch the intelligent apps webinar on ",(0,i.kt)("strong",{parentName:"p"},(0,i.kt)("a",{parentName:"strong",href:"https://info.microsoft.com/ww-landing-driving-business-value-by-modernizing-with-cloud-native-and-ai.html?WT.mc_id=javascript-99907-ninarasi"},"Driving Business Value by Modernizing with Cloud-Native & AI")),"\u202fwith ",(0,i.kt)("em",{parentName:"p"},"Microsoft")," and ",(0,i.kt)("em",{parentName:"p"},"Forrester")," on ",(0,i.kt)("strong",{parentName:"p"},"September 26"),". ")),(0,i.kt)("p",null,"AKS is Microsoft Azure\u2019s container orchestration platform that enables us to quickly and efficiently deploy, manage, and scale containerized applications, such as our Intelligent App."),(0,i.kt)("p",null,"Let\u2019s explore how to leverage these tools to build an Intelligent App that uses Azure AI Vision to analyze images and extract data."),(0,i.kt)("h3",{id:"prerequisites"},"Prerequisites"),(0,i.kt)("p",null,"To follow this tutorial, ensure you have the following:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"https://www.python.org/downloads/"},"Python 3.7")," or later installed"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"https://code.visualstudio.com/download"},"VS Code")," or another integrated development environment (IDE) for writing Python code"),(0,i.kt)("li",{parentName:"ul"},"The ",(0,i.kt)("a",{parentName:"li",href:"https://github.com/contentlab-io/Microsoft-Using-Azure-Kubernetes-Service-to-Deploy-an-Intelligent-App-for-Analyzing-Images-1/tree/main/Microsoft_Series17-18_Code/intelligent-app-before"},"sample Python application")," downloaded"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"https://pip.pypa.io/en/stable/installing/"},"pip"),", the package manager for Python, installed"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"https://www.docker.com/products/docker-desktop/"},"Docker Desktop")," installed. Ensure Docker is running on Linux containers."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"https://www.postman.com/downloads/"},"Postman")," installed. We\u2019ll use this to test our API."),(0,i.kt)("li",{parentName:"ul"},"A ",(0,i.kt)("a",{parentName:"li",href:"https://azure.microsoft.com/free/?WT.mc_id=javascript-99907-ninarasi"},"free Azure account"),". Sign up if you don\u2019t have one yet."),(0,i.kt)("li",{parentName:"ul"},"The ",(0,i.kt)("a",{parentName:"li",href:"https://learn.microsoft.com/cli/azure/install-azure-cli?WT.mc_id=javascript-99907-ninarasi"},"Azure command-line interface")," (CLI)")),(0,i.kt)("p",null,"For a preview of this final project, take a look at the ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/contentlab-io/Microsoft-Using-Azure-Kubernetes-Service-to-Deploy-an-Intelligent-App-for-Analyzing-Images-1/tree/main/Microsoft_Series17-18_Code/intelligent-app-after"},"complete project code"),"."),(0,i.kt)("h3",{id:"building-the-api-with-azure-ai-vision-service"},"Building the API with Azure AI Vision Service"),(0,i.kt)("p",null,"First, log in to your Azure account and navigate to the ",(0,i.kt)("a",{parentName:"p",href:"https://portal.azure.com"},"Azure Portal"),"."),(0,i.kt)("p",null,"Click ",(0,i.kt)("strong",{parentName:"p"},"Create a resource")," and search for \u201cresource group.\u201d Create a new resource group named ",(0,i.kt)("inlineCode",{parentName:"p"},"computer-vision"),"."),(0,i.kt)("p",null,(0,i.kt)("img",{alt:"image in Azure Portal of creating a new resource group",src:n(88748).Z,width:"1248",height:"761"})),(0,i.kt)("p",null,"Return to the Azure Portal home page and click ",(0,i.kt)("strong",{parentName:"p"},"Create a resource"),"."),(0,i.kt)("p",null,(0,i.kt)("img",{alt:"image in Azure Portal of creating a new resource",src:n(65440).Z,width:"624",height:"308"})),(0,i.kt)("p",null,"Search for \u201ccomputer vision\u201d and select it from the results. Click ",(0,i.kt)("strong",{parentName:"p"},"Create"),"."),(0,i.kt)("p",null,(0,i.kt)("img",{alt:"image of searching for resource group in Azure Portal",src:n(43039).Z,width:"572",height:"1160"})),(0,i.kt)("p",null,"Clicking ",(0,i.kt)("strong",{parentName:"p"},"Create")," displays the Create Computer Vision wizard. Select the resource group you just created as the Resource group from the dropdown. Enter a resource name similar to what is seen in the screenshot below. Note that this must be a unique name so choose one that is not currently in use. Select Standard S1 as the pricing tier and check the Responsible use of AI box in the field at the bottom."),(0,i.kt)("p",null,(0,i.kt)("img",{alt:"Image of selecting the Responsible use of AI box",src:n(49345).Z,width:"983",height:"1100"})),(0,i.kt)("p",null,(0,i.kt)("img",{alt:"Image of project details",src:n(67748).Z,width:"624",height:"380"})),(0,i.kt)("p",null,"Click ",(0,i.kt)("strong",{parentName:"p"},"Review + Create"),", then click ",(0,i.kt)("strong",{parentName:"p"},"Create"),"."),(0,i.kt)("p",null,"Next, get the API key and endpoint from the Azure AI services multi-service account you created. Once the resource is created, navigate to the resource details page. Under ",(0,i.kt)("strong",{parentName:"p"},"Resource Management"),", select ",(0,i.kt)("strong",{parentName:"p"},"Keys and Endpoint"),". Once on the ",(0,i.kt)("strong",{parentName:"p"},"Keys and Endpoint")," page, you\u2019ll find the ",(0,i.kt)("strong",{parentName:"p"},"Key 1")," and ",(0,i.kt)("strong",{parentName:"p"},"Endpoint")," values. These credentials are necessary to access the Azure AI APIs."),(0,i.kt)("p",null,(0,i.kt)("img",{alt:"image of selecting Keys and Endpoint",src:n(14842).Z,width:"624",height:"352"})),(0,i.kt)("p",null,(0,i.kt)("img",{alt:"second image of selecting Keys and Endpoint",src:n(23418).Z,width:"1100",height:"676"})),(0,i.kt)("h4",{id:"configuring-the-local-environment-variables"},"Configuring the Local Environment Variables"),(0,i.kt)("p",null,"Next, we need to define a local environment variable file. This convenient way to store our API keys in a simple text file enables us to manage sensitive information and separate configuration from our API code."),(0,i.kt)("p",null,"If you haven\u2019t done so already, clone the ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/contentlab-io/Microsoft-Using-Azure-Kubernetes-Service-to-Deploy-an-Intelligent-App-for-Analyzing-Images"},"starter project template")," from GitHub into your local computer. Open the ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/contentlab-io/Microsoft-Using-Azure-Kubernetes-Service-to-Deploy-an-Intelligent-App-for-Analyzing-Images/tree/main/Microsoft_Series17-18_Code/intelligent-app-before"},"starter project template")," in a code editor and create a new ",(0,i.kt)("inlineCode",{parentName:"p"},".env")," file in the root folder."),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Note"),': Root folder in this case is the \u201c/Microsoft_Series17-18_Code/intelligent-app-before" folder.'),(0,i.kt)("p",null,"Add the following key-value pairs to the file, remembering to replace the placeholder values with those you obtained from the previous step."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"VISION_KEY=\nVISION_ENDPOINT=\n")),(0,i.kt)("h4",{id:"reviewing-the-quickstart-code"},"Reviewing the Quickstart Code"),(0,i.kt)("p",null,"Let\u2019s review the ",(0,i.kt)("inlineCode",{parentName:"p"},"app.py")," file in the starter project template. "),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"app.py")," file works as the entry point for the Flask application. It is responsible for initializing the web server, listening for incoming requests, dispatching them to the appropriate route handlers, and generating responses. "),(0,i.kt)("p",null,"Below is the code contained in the ",(0,i.kt)("inlineCode",{parentName:"p"},"app.py")," file:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"import os\nimport json\n \nfrom flask import Flask, request \nfrom flask_restful import Resource, Api\nfrom werkzeug.utils import secure_filename\n\napp = Flask(__name__,\n static_url_path='',\n static_folder='static/files')\n\napi = Api(app)\n\napp = Flask(__name__)\n\napp.config['UPLOAD_FOLDER'] = 'files'\n\napi = Api(app)\n\nclass UploadHandler(Resource):\n\n def allowed_file(self, filename):\n return '.' in filename and \\\n filename.rsplit('.', 1)[1].lower() in {'png'}\n\n def post(self):\n form = request.form.to_dict()\n\n if 'file' not in request.files:\n return json.dumps({ \"success\": False, \"error\": \"No file part\"})\n\n file = request.files.get(\"file\")\n if file and self.allowed_file(file.filename):\n filename = secure_filename(file.filename)\n upload_folder = \"static/files\"\n if not os.path.exists(upload_folder):\n os.makedirs(upload_folder)\n local_file_path = os.path.join(upload_folder, filename)\n file.save(local_file_path)\n\n return f\"File {filename} uploaded successfully to folder: {upload_folder}\"\n\napi.add_resource(UploadHandler, \"/\")\n\nif __name__ == '__main__':\n app.run(debug=True)\n\n")),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"UploadHandler")," class above is defined as a Flask RESTful resource to handle file uploads. The class contains two methods:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("p",{parentName:"li"},"The ",(0,i.kt)("inlineCode",{parentName:"p"},"allowed_file")," method checks whether the file extension is allowed for upload. In this case, only ",(0,i.kt)("inlineCode",{parentName:"p"},".png")," files are allowed.")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("p",{parentName:"li"},"The ",(0,i.kt)("inlineCode",{parentName:"p"},"post")," method handles HTTP POST requests for file uploads. It saves the uploaded ",(0,i.kt)("inlineCode",{parentName:"p"},".png")," file to the ",(0,i.kt)("inlineCode",{parentName:"p"},"static/files")," folder."))),(0,i.kt)("p",null,"Finally, the application returns a text response informing us that the file has been uploaded successfully."),(0,i.kt)("h4",{id:"implementing-the-rest-api-in-python"},"Implementing the REST API in Python"),(0,i.kt)("p",null,"To implement image analysis in your REST API, open the starter project template in your terminal, ",(0,i.kt)("a",{parentName:"p",href:"https://docs.python.org/3/library/venv.html"},"create a virtual environment"),", and ",(0,i.kt)("a",{parentName:"p",href:"https://docs.python.org/3/library/venv.html#how-venvs-work"},"activate it"),"."),(0,i.kt)("p",null,"Next, install the Azure AI Vision SDK:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"pip install azure-ai-vision\n")),(0,i.kt)("p",null,"Then, add the following line to the ",(0,i.kt)("inlineCode",{parentName:"p"},"requirements.txt")," file to include the ",(0,i.kt)("inlineCode",{parentName:"p"},"azure-ai-vision")," package:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"azure-ai-vision==0.13.0b1\n")),(0,i.kt)("p",null,"Now, create an ",(0,i.kt)("inlineCode",{parentName:"p"},"ocr_helper.py")," file in the project\u2019s root folder. This file is a Python module that provides functions for processing images using OCR. Add the following code to the file:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"import os\nfrom statistics import median\nfrom decimal import Decimal\nimport azure.ai.vision as sdk\n\ndef process_ocr(source_image):\n service_options = sdk.VisionServiceOptions(os.environ[\"VISION_ENDPOINT\"],\n os.environ[\"VISION_KEY\"])\n\n vision_source = sdk.VisionSource(filename=source_image)\n\n analysis_options = sdk.ImageAnalysisOptions()\n\n analysis_options.features = (\n sdk.ImageAnalysisFeature.CAPTION |\n sdk.ImageAnalysisFeature.TEXT\n )\n\n analysis_options.language = \"en\"\n\n analysis_options.gender_neutral_caption = True\n\n image_analyzer = sdk.ImageAnalyzer(service_options, vision_source, analysis_options)\n\n result = image_analyzer.analyze()\n\n ocr_result = get_ocr_result(result)\n\n return ocr_result\n\ndef get_ocr_result(result):\n string_list = []\n\n if result.reason != sdk.ImageAnalysisResultReason.ANALYZED:\n return sdk.ImageAnalysisErrorDetails.from_result(result)\n else:\n if result.text is not None:\n for line in result.text.lines:\n for word in line.words:\n string_list.append(word.content)\n\n number_list = convert_to_decimal_list(string_list)\n\n aggregate_result = aggregate_operations(number_list)\n\n return {\n \"aggregate_result\": aggregate_result,\n \"numbers_read\": string_list\n } \n\ndef convert_to_decimal_list(string_list):\n return list(map(Decimal, string_list))\n\ndef aggregate_operations(numbers):\n result = {\n 'sum': sum(numbers),\n 'average': sum(numbers) / len(numbers),\n 'median': median(numbers),\n 'min': min(numbers),\n 'max': max(numbers)\n `}`\n `return result`\n")),(0,i.kt)("p",null,"This module uses the ",(0,i.kt)("a",{parentName:"p",href:"https://learn.microsoft.com/python/api/azure-ai-vision/?view=azure-python-preview?WT.mc_id=javascript-99907-ninarasi"},"azure-ai-vision")," package to analyze images, including capturing captions and extracting text from the image. The ",(0,i.kt)("inlineCode",{parentName:"p"},"process_ocr")," function inputs an image file and performs the OCR analysis. The module is a convenient tool for analyzing text-based data in pictures and performing numerical computations based on that data."),(0,i.kt)("p",null,"Let\u2019s review the different components of the ",(0,i.kt)("inlineCode",{parentName:"p"},"ocr_helper.py")," module listed above and explore how its functions help our Intelligent App process images and perform aggregate operations:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"The ",(0,i.kt)("inlineCode",{parentName:"li"},"process_ocr")," function takes the parameter ",(0,i.kt)("inlineCode",{parentName:"li"},"source_image"),", which is the path of the image to be processed. The function then initializes the ",(0,i.kt)("inlineCode",{parentName:"li"},"VisionServiceOptions")," using environment variables ",(0,i.kt)("inlineCode",{parentName:"li"},"VISION_ENDPOINT")," and ",(0,i.kt)("inlineCode",{parentName:"li"},"VISION_KEY")," to connect to the Azure AI Vision API."),(0,i.kt)("li",{parentName:"ul"},"The ",(0,i.kt)("inlineCode",{parentName:"li"},"process_ocr")," function creates a ",(0,i.kt)("inlineCode",{parentName:"li"},"VisionSource")," object with the specified ",(0,i.kt)("inlineCode",{parentName:"li"},"source_image")," file name. ",(0,i.kt)("inlineCode",{parentName:"li"},"ImageAnalysisOptions")," specify the features to be analyzed, including ",(0,i.kt)("inlineCode",{parentName:"li"},"CAPTION")," and ",(0,i.kt)("inlineCode",{parentName:"li"},"TEXT"),". The language is set to English (",(0,i.kt)("inlineCode",{parentName:"li"},'"en"'),"), and gender-neutral captions are enabled."),(0,i.kt)("li",{parentName:"ul"},"Finally, an ",(0,i.kt)("inlineCode",{parentName:"li"},"ImageAnalyzer")," object is created with the service options, vision source, and analysis options. The image is then analyzed using the ",(0,i.kt)("inlineCode",{parentName:"li"},"image_analyzer.analyze")," method to retrieve OCR results. The recognized text from the OCR result is extracted, with numerical values stored in the ",(0,i.kt)("inlineCode",{parentName:"li"},"string_list")," variable."),(0,i.kt)("li",{parentName:"ul"},"The ",(0,i.kt)("inlineCode",{parentName:"li"},"convert_to_decimal_list")," function converts the list of strings to a list of decimal numbers, which helps process numeric values extracted from the text."),(0,i.kt)("li",{parentName:"ul"},"The ",(0,i.kt)("inlineCode",{parentName:"li"},"aggregate_operations")," function calculates various aggregate statistics (sum, average, median, minimum, maximum) from a list of numbers and returns the results in a dictionary.")),(0,i.kt)("p",null,"Note that you must have the appropriate credentials (",(0,i.kt)("inlineCode",{parentName:"p"},"VISION_KEY"),") and endpoint (",(0,i.kt)("inlineCode",{parentName:"p"},"VISION_ENDPOINT"),") configured for the Azure AI Vision API to use this module."),(0,i.kt)("p",null,"Finally, we must modify the ",(0,i.kt)("inlineCode",{parentName:"p"},"app.py")," file so our code can use the ",(0,i.kt)("inlineCode",{parentName:"p"},"process_ocr")," function of the ",(0,i.kt)("inlineCode",{parentName:"p"},"ocr_helper.py")," file."),(0,i.kt)("p",null,"Add the following import statement to the ",(0,i.kt)("inlineCode",{parentName:"p"},"app.py")," file: "),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"from ocr_helper import process_ocr\n")),(0,i.kt)("p",null,"Then, replace this line:"),(0,i.kt)("p",null,(0,i.kt)("inlineCode",{parentName:"p"},'return f"File {filename} uploaded successfully to folder: {upload_folder}"')),(0,i.kt)("p",null,"With these two lines:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"aggregates = process_ocr(local_file_path)\nreturn json.dumps(aggregates, default=str)\n")),(0,i.kt)("p",null,"Doing so allows our application to perform aggregate operations (sum, average, median, minimum, maximum) of any numeric values found in the text and return the operation\u2019s result as a JSON response."),(0,i.kt)("h3",{id:"running-the-intelligent-app-locally"},"Running the Intelligent App Locally"),(0,i.kt)("p",null,"Our final goal is to deploy the sample app to AKS, which will run the app in a container. However, as a good practice, let\u2019s run the app in a container locally before moving it to AKS. Doing so ensures that our application and its dependencies are encapsulated and can be run consistently across different environments."),(0,i.kt)("h4",{id:"deploying-to-docker-desktop"},"Deploying to Docker Desktop"),(0,i.kt)("p",null,"For our application to run on Docker, it needs two additional files: a Dockerfile and ",(0,i.kt)("inlineCode",{parentName:"p"},"docker-compose.yml"),". A Dockerfile creates a single container image by specifying the steps to build it, and a ",(0,i.kt)("inlineCode",{parentName:"p"},"docker-compose.yml")," file manages multiple containers as a cohesive application, defining their configurations and relationships. Dockerfile builds the image, while ",(0,i.kt)("inlineCode",{parentName:"p"},"docker-compose.yml")," orchestrates multi-container applications."),(0,i.kt)("p",null,"In the project root folder, add a file named ",(0,i.kt)("inlineCode",{parentName:"p"},"Dockerfile")," with the content below:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},'# syntax=docker/dockerfile:1\n\nFROM python:3.8-slim-buster\n\nWORKDIR /intelligentapp\n\nCOPY requirements.txt requirements.txt\nRUN pip3 install -r requirements.txt\n\nRUN pip install debugpy\n\nCOPY . .\n\nCMD [ "python3", "-m" , "flask", "run", "--host=0.0.0.0"]\n')),(0,i.kt)("p",null,"Now create a file named ",(0,i.kt)("inlineCode",{parentName:"p"},"docker-compose.yml")," file in the project\u2019s root folder with the following code, replacing the ",(0,i.kt)("inlineCode",{parentName:"p"},"VISION_KEY")," and the ",(0,i.kt)("inlineCode",{parentName:"p"},"VISION_ENDPOINT")," according to the environment variables you configured earlier:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"version: '3.8'\nservices:\n intelligentapp:\n build:\n context: .\n dockerfile: Dockerfile\n image: intelligent-app\n ports:\n - 5000:5000\n container_name: intelligent-app\n environment:\n - VISION_KEY=\n - VISION_ENDPOINT=\n")),(0,i.kt)("p",null,"Then, run the following command in the terminal to build the image and start a container for the Intelligent App services defined in the ",(0,i.kt)("inlineCode",{parentName:"p"},"docker-compose.yml")," file: "),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"docker-compose up --build --force-recreate\n")),(0,i.kt)("p",null,"Next, open a new terminal and run the command below to list the image deployed to your local Docker:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"docker images\n")),(0,i.kt)("p",null,(0,i.kt)("img",{alt:"docker images",src:n(86594).Z,width:"506",height:"76"})),(0,i.kt)("h4",{id:"testing-the-intelligent-app-locally"},"Testing the Intelligent App Locally"),(0,i.kt)("p",null,"Now, we\u2019ll test the Intelligent App\u2019s functionality using the following test image:"),(0,i.kt)("p",null,(0,i.kt)("img",{alt:"test image to test functionality",src:n(74531).Z,width:"204",height:"223"})),(0,i.kt)("p",null,"This image is provided as ",(0,i.kt)("inlineCode",{parentName:"p"},"sample1.png")," in the sample app source files, so you can easily use it in the next step."),(0,i.kt)("p",null,"The Intelligent App will read the image above using OCR to extract numbers in the image and perform five aggregate functions:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Sum"),(0,i.kt)("li",{parentName:"ul"},"Average"),(0,i.kt)("li",{parentName:"ul"},"Median"),(0,i.kt)("li",{parentName:"ul"},"Min"),(0,i.kt)("li",{parentName:"ul"},"Max")),(0,i.kt)("p",null,"To test the API, open Postman and fill out the fields as follows:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"URL:")," http://localhost:5000/"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"Method:")," POST"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"Body:"),(0,i.kt)("ul",{parentName:"li"},(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"Form-data"),(0,i.kt)("ul",{parentName:"li"},(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"Key"),": File \u2014 Click the right end of the ",(0,i.kt)("strong",{parentName:"li"},"Key")," field and select ",(0,i.kt)("strong",{parentName:"li"},"File")," from the dropdown list."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"Value"),": Click ",(0,i.kt)("strong",{parentName:"li"},"Select Files"),", then select the ",(0,i.kt)("strong",{parentName:"li"},"sample1.png")," file provided in the sample code.")))))),(0,i.kt)("p",null,(0,i.kt)("img",{alt:"image of selecting the sample file",src:n(47994).Z,width:"624",height:"375"})),(0,i.kt)("p",null,"Now click the ",(0,i.kt)("strong",{parentName:"p"},"Send")," button and review the result body:"),(0,i.kt)("p",null,(0,i.kt)("inlineCode",{parentName:"p"},'"{\\"aggregate_result\\": {\\"sum\\": \\"25821\\", \\"average\\": \\"5164.2\\", \\"median\\": \\"5622\\", \\"min\\": \\"1447\\", \\"max\\": \\"9802\\"}, \\"numbers_read\\": [\\"3049\\", \\"5901\\", \\"5622\\", \\"1447\\", \\"9802\\"]}"')),(0,i.kt)("p",null,"As we can see, the app running on a local container returned the correct results based on the sample image. So, we\u2019re ready to prepare our Azure resources to run the Intelligent App on AKS."),(0,i.kt)("h2",{id:"exercise"},"Exercise"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("p",{parentName:"li"},"Complete this ",(0,i.kt)("strong",{parentName:"p"},"hands-on sample")," ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/contentlab-io/Microsoft-Using-Azure-Kubernetes-Service-to-Deploy-an-Intelligent-App-for-Analyzing-Images-1/tree/main/Microsoft_Series17-18_Code/intelligent-app-after"},"project code")," to build your first intelligent app.")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("p",{parentName:"li"},"Complete the ",(0,i.kt)("strong",{parentName:"p"},(0,i.kt)("a",{parentName:"strong",href:"https://aka.ms/fallforIA/apps-csc"},"Apps Cloud Skills Challenge"))," to build on your app dev and AI skills.")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("p",{parentName:"li"},"Watch the ",(0,i.kt)("strong",{parentName:"p"},(0,i.kt)("a",{parentName:"strong",href:"https://reactor.microsoft.com/en-us/reactor/series/S-1037/"},"Ask the Expert: Azure Functions"))," session for live Q&A with the Product Engineering team on building intelligent apps."),(0,i.kt)("h2",{parentName:"li",id:"next-steps"},"Next Steps"))),(0,i.kt)("p",null,"In this article, we explored the creation of an Intelligent App that leverages Azure AI Vision to analyze images and extract data. We learned how to build a Python Web API to perform OCR on uploaded images and subsequently test this API locally."),(0,i.kt)("p",null,"Head to the ",(0,i.kt)("a",{parentName:"p",href:"https://azure.github.io/Cloud-Native/30daysofIA/build-your-first-intelligent-app-with-azure-ai-and-aks-2"},"next part"),", to deploy this API via Azure Kubernetes Service."))}c.isMDXComponent=!0},12177:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/blog-image-1-1-e8cf7713343a8855e8f9b05434148f9d.png"},74531:(e,t,n)=>{n.d(t,{Z:()=>a});const a="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMwAAADfCAMAAACNvsz1AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAEUUExUReLRfNO7a62DQZBZIX09DHMuAXItANS9bJlmK5hlKrqXUHc0BbqWT6+GQ4ZLFrOMSNC3ad/NeXUyA7SOSeHQe7WPSsWnXMaoXZdjKbWQSnIuAIdNGKyCQI5XINK5anw8C5FPTygoKE9Pkdmyg0JCZwwMDAAAAG2KvLyKbYOy2TQ0XGGx+fmxYT8/iFw0NAAAYYqKMwAzitWKM7F4VwAAM2EAAABhsVd4sdGiaTOK1YozAIg/PzMAAGmi0RcXF7FhADOKsfrAgWdCQt3AgMmioi4uLnw9DNG4aY9YIayDQYdMF4hOGauBQLaRTNK6apdkKXUyBMeqXpZjKNO8bHYzBLaRS7mVTohNGLiUTq2EQnY0BQAAAJc5ouMAAABcdFJOU/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////8Ag20X4AAAAAlwSFlzAAAOwwAADsMBx2+oZAAACzlJREFUeF7tnVtz47YVxzmzM/ZMHixRL/u0M3Ycsmo2dS/Oxa3STbOOn9KX7oNn9P2/SM8VBEjIJEFAAlv8JhMBRzT3HP4B8HYEVIXCebk+ftjtrtbE7t3xWpx3uf6C3/JGawHd/eIJ56uVxdGxe5UQDEf5ZpUcJQjhKzLebLa1GFZBvd3ckOOONtfYxnabVUUibMh1u99Q338vlZXxnkYBqQDXUL3aSGV1bND7Thrs/TdrbGMM9pt/G/c/rFkYluZ3KVcVnve3Ul4hWwhmJ+WqgspVvd5mVg+CkeICDnfI11ITyCblDtxUisgzbvRN+NF0/KfKUmXumpa4+4MYgFs2tb1w9riplIE72cj6w3kMg1lK+4LHF0P6o1iqg8QHiIVxLM9UI0KjQf+NFhTMUmWkfcFR/5ZLJMBH+PwO/PwTm4iH9s9/6YKBL/+Kn3+DAhlmg30msjLKo/Gpbb+X0g+2n3sIETyXGmz+I5fA9hOX5oL+x1XGoH4+te3fuYTadH424H0XDMgkpeofRtN5JFTmqW24cDDCgJtdOyPvu2CkkSGdcSbo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"},47994:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/blog-image-1-11-96c8a57ad6beab15a2d06d1a0de0ad97.png"},88748:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/blog-image-1-2-49fb07b13652bc56001521b3312fae9d.png"},65440:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/blog-image-1-3-5b57e295568b12f4b213cb9fc3d39e81.png"},43039:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/blog-image-1-4-77065a4476c7a64b6b66d818a4c94a6c.png"},49345:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/blog-image-1-5-efe95dbf3756c59403a30a3a9a2b0c7a.png"},67748:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/blog-image-1-6-4e63cfb3a15c78ba383a4cdbdc221570.png"},14842:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/blog-image-1-7-1d88b437ef046ee6829ccc174cc61f3a.png"},23418:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/blog-image-1-8-f2e1dae9a99355268c4fc214e858ca32.png"},86594:(e,t,n)=>{n.d(t,{Z:()=>a});const a="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAfoAAABMCAMAAACyL4WnAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAG/UExURRgYGBgYVZnAzMzMzMyqfX2qzMCZVRgYfarMzMzAmVUYGKp9GBh9qszMqn0YGBhVmcDMzFWZwMzMwJlVGMvMzBkYGBh9qZlWGRkYVZi/yxkZGXypy8CZVhkZGBhVmL/LzFYZGRh8qX0ZGX2pyxl9qcvLzBgYfKnLy6p+GRkZVBlWmMyqfhoZGXypysDMwMCZfZnAwJmqmczAwMCqfarAwMDMqsvMqn0ZGL/MzFWYv8vMwJipmRkZVap9GanLzL+ZfcvLqn4ZGRgYVKnKv7+ZVsyqmZnAqqp9farMwFWZvxhUmL/LwFSYv32qqn0Yfb/MwJlVGb/Lqhl8qcvAmX4aGcyqqsCqqqqqfarLwMuqfanLwKp+Gsq/mX2qy32pyqrLzFUZGRpWmRoafanKy5mZwMuqfhp9qsq/wMCqfqm+v5lVfam/mcuqmZl9mZi/v5lWfVaZwBkYfMqqmal9farLvxkaflWZqlUZGJmZqn0ZfVWYqcu/mcqqfVaZv6l9GX2qqarKv5lWGqrMqpm/zBkZfanLqr+pqn19qhlVmb/Mqn5+qhoZVZi+v5i+yxkYfVYaGRlWmZi/zBkZVn2ZwAAAAJYAKZcAAACVdFJOU/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////8ANmQbOAAAAAlwSFlzAAAOwwAADsMBx2+oZAAAB59JREFUeF7tmk+L40YQxSVbmNkxHpPx0RDwxddkycU5LQhvFg/KzinkCyyEJOQ2hMDcckgYWJ/2C6feq+rWH8saWXY2E6gfzLSk7q6qrtfdki0njuM4juM4juM4juM4juM4juM4juM4juM4juM4juM4juM4juM4znmko/F4nCXJUorxZH2lZ7j66jrh6ZRNxpMV/o9nN1qn/dKRNJZmEzV2MsFmMoe7JPlCzhZadQIa4HY+3so4msZ0YNrQWupQpPHp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"}}]); \ No newline at end of file diff --git a/assets/js/59a2dd7b.c29a6888.js b/assets/js/260068d7.43396f0e.js similarity index 53% rename from assets/js/59a2dd7b.c29a6888.js rename to assets/js/260068d7.43396f0e.js index b9205d4b63..b00b84b8ac 100644 --- a/assets/js/59a2dd7b.c29a6888.js +++ b/assets/js/260068d7.43396f0e.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[38582],{75929:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/fall-for-ia/page/9","page":9,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/fall-for-ia/page/8","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[15238],{49523:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/fall-for-ia/page/7","page":7,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/fall-for-ia/page/6","nextPage":"/Cloud-Native/30daysofIA/tags/fall-for-ia/page/8","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/260068d7.535a13e7.js b/assets/js/260068d7.535a13e7.js deleted file mode 100644 index 48579cb1cd..0000000000 --- a/assets/js/260068d7.535a13e7.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[15238],{49523:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/fall-for-ia/page/7","page":7,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/fall-for-ia/page/6","nextPage":"/Cloud-Native/30daysofIA/tags/fall-for-ia/page/8","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/27ad03b1.04914118.js b/assets/js/27ad03b1.04914118.js new file mode 100644 index 0000000000..b54975376e --- /dev/null +++ b/assets/js/27ad03b1.04914118.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[64603],{3905:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>d});var a=n(67294);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function r(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function o(e){for(var t=1;t=0||(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}var s=a.createContext({}),p=function(e){var t=a.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},u=function(e){var t=p(e.components);return a.createElement(s.Provider,{value:t},e.children)},c={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},m=a.forwardRef((function(e,t){var n=e.components,i=e.mdxType,r=e.originalType,s=e.parentName,u=l(e,["components","mdxType","originalType","parentName"]),m=p(n),d=i,g=m["".concat(s,".").concat(d)]||m[d]||c[d]||r;return n?a.createElement(g,o(o({ref:t},u),{},{components:n})):a.createElement(g,o({ref:t},u))}));function d(e,t){var n=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var r=n.length,o=new Array(r);o[0]=m;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l.mdxType="string"==typeof e?e:i,o[1]=l;for(var p=2;p{n.r(t),n.d(t,{assets:()=>s,contentTitle:()=>o,default:()=>c,frontMatter:()=>r,metadata:()=>l,toc:()=>p});var a=n(87462),i=(n(67294),n(3905));const r={date:"2023-09-22T09:00",slug:"build-your-first-intelligent-app-with-azure-ai-and-aks-1",title:"2-1. Build Your First Intelligent App with Azure AI and AKS-1",authors:["cnteam"],draft:!1,hide_table_of_contents:!1,toc_min_heading_level:2,toc_max_heading_level:3,keywords:["Cloud","Data","AI","AI/ML","intelligent apps","cloud-native","30-days","enterprise apps","digital experiences","app modernization","serverless","ai apps","data"],image:"https://azure.github.io/Cloud-Native/img/ogImage.png",description:"Delve into creating an Intelligent App that leverages Azure AI Vision to analyze images and extract data. We guide readers through crafting an API to perform optical character recognition (OCR) on uploaded images, and subsequently deploying this API via Azure Kubernetes Service, helping them discover the power of these tools to create innovative, AI-driven solutions.",tags:["Fall-For-IA","30-days-of-IA","learn-live","hack-together","community-buzz","ask-the-expert","azure-kubernetes-service","azure-functions","azure-openai","azure-container-apps","azure-cosmos-db","github-copilot","github-codespaces","github-actions"]},o=void 0,l={permalink:"/Cloud-Native/30daysofIA/build-your-first-intelligent-app-with-azure-ai-and-aks-1",source:"@site/blog-30daysofIA/2023-09-27/build-your-first-intelligent-app-with-azure-ai-and-aks-1.md",title:"2-1. Build Your First Intelligent App with Azure AI and AKS-1",description:"Delve into creating an Intelligent App that leverages Azure AI Vision to analyze images and extract data. We guide readers through crafting an API to perform optical character recognition (OCR) on uploaded images, and subsequently deploying this API via Azure Kubernetes Service, helping them discover the power of these tools to create innovative, AI-driven solutions.",date:"2023-09-22T09:00:00.000Z",formattedDate:"September 22, 2023",tags:[{label:"Fall-For-IA",permalink:"/Cloud-Native/30daysofIA/tags/fall-for-ia"},{label:"30-days-of-IA",permalink:"/Cloud-Native/30daysofIA/tags/30-days-of-ia"},{label:"learn-live",permalink:"/Cloud-Native/30daysofIA/tags/learn-live"},{label:"hack-together",permalink:"/Cloud-Native/30daysofIA/tags/hack-together"},{label:"community-buzz",permalink:"/Cloud-Native/30daysofIA/tags/community-buzz"},{label:"ask-the-expert",permalink:"/Cloud-Native/30daysofIA/tags/ask-the-expert"},{label:"azure-kubernetes-service",permalink:"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service"},{label:"azure-functions",permalink:"/Cloud-Native/30daysofIA/tags/azure-functions"},{label:"azure-openai",permalink:"/Cloud-Native/30daysofIA/tags/azure-openai"},{label:"azure-container-apps",permalink:"/Cloud-Native/30daysofIA/tags/azure-container-apps"},{label:"azure-cosmos-db",permalink:"/Cloud-Native/30daysofIA/tags/azure-cosmos-db"},{label:"github-copilot",permalink:"/Cloud-Native/30daysofIA/tags/github-copilot"},{label:"github-codespaces",permalink:"/Cloud-Native/30daysofIA/tags/github-codespaces"},{label:"github-actions",permalink:"/Cloud-Native/30daysofIA/tags/github-actions"}],readingTime:11.785,hasTruncateMarker:!1,authors:[{name:"It's 30DaysOfIA",title:"FallForIA Content Team",url:"https://azure.github.io/Cloud-Native/Fall-For-IA/",imageURL:"https://azure.github.io/Cloud-Native/img/logo-ms-cloud-native.png",key:"cnteam"}],frontMatter:{date:"2023-09-22T09:00",slug:"build-your-first-intelligent-app-with-azure-ai-and-aks-1",title:"2-1. Build Your First Intelligent App with Azure AI and AKS-1",authors:["cnteam"],draft:!1,hide_table_of_contents:!1,toc_min_heading_level:2,toc_max_heading_level:3,keywords:["Cloud","Data","AI","AI/ML","intelligent apps","cloud-native","30-days","enterprise apps","digital experiences","app modernization","serverless","ai apps","data"],image:"https://azure.github.io/Cloud-Native/img/ogImage.png",description:"Delve into creating an Intelligent App that leverages Azure AI Vision to analyze images and extract data. We guide readers through crafting an API to perform optical character recognition (OCR) on uploaded images, and subsequently deploying this API via Azure Kubernetes Service, helping them discover the power of these tools to create innovative, AI-driven solutions.",tags:["Fall-For-IA","30-days-of-IA","learn-live","hack-together","community-buzz","ask-the-expert","azure-kubernetes-service","azure-functions","azure-openai","azure-container-apps","azure-cosmos-db","github-copilot","github-codespaces","github-actions"]},prevItem:{title:"1-5. Preparing the Path for Intelligent Apps",permalink:"/Cloud-Native/30daysofIA/preparing-the-path-for-intelligent-apps"},nextItem:{title:"2-2. Build Your First Intelligent App with Azure AI and AKS-2",permalink:"/Cloud-Native/30daysofIA/build-your-first-intelligent-app-with-azure-ai-and-aks-2"}},s={authorsImageUrls:[void 0]},p=[{value:"What We'll Cover:",id:"what-well-cover",level:2},{value:"Jumpstart Your AI Journey: Building Your First Intelligent App with Azure AI and AKS (1)",id:"jumpstart-your-ai-journey-building-your-first-intelligent-app-with-azure-ai-and-aks-1",level:2},{value:"Understanding Azure AI Vision and Azure Kubernetes Service",id:"understanding-azure-ai-vision-and-azure-kubernetes-service",level:2},{value:"Prerequisites",id:"prerequisites",level:3},{value:"Building the API with Azure AI Vision Service",id:"building-the-api-with-azure-ai-vision-service",level:3},{value:"Configuring the Local Environment Variables",id:"configuring-the-local-environment-variables",level:4},{value:"Reviewing the Quickstart Code",id:"reviewing-the-quickstart-code",level:4},{value:"Implementing the REST API in Python",id:"implementing-the-rest-api-in-python",level:4},{value:"Running the Intelligent App Locally",id:"running-the-intelligent-app-locally",level:3},{value:"Deploying to Docker Desktop",id:"deploying-to-docker-desktop",level:4},{value:"Testing the Intelligent App Locally",id:"testing-the-intelligent-app-locally",level:4},{value:"Exercise",id:"exercise",level:2}],u={toc:p};function c(e){let{components:t,...r}=e;return(0,i.kt)("wrapper",(0,a.Z)({},u,r,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("head",null,(0,i.kt)("meta",{property:"og:url",content:"https://azure.github.io/cloud-native/30daysofia/build-your-first-intelligent-app-with-azure-ai-and-aks-1"}),(0,i.kt)("meta",{property:"og:type",content:"website"}),(0,i.kt)("meta",{property:"og:title",content:"**Fall For Intelligent Apps! \ud83c\udf42| Build AI Apps On Azure"}),(0,i.kt)("meta",{property:"og:description",content:"Create an Intelligent App that leverages Azure AI Vision to analyze images and extract data."}),(0,i.kt)("meta",{property:"og:image",content:"https://azure.github.io/Cloud-Native/img/ogImage.png"}),(0,i.kt)("meta",{name:"twitter:url",content:"https://azure.github.io/Cloud-Native/30daysofIA/build-your-first-intelligent-app-with-azure-ai-and-aks-1"}),(0,i.kt)("meta",{name:"twitter:title",content:"**Fall For Intelligent Apps! \ud83c\udf42 | Build AI Apps On Azure"}),(0,i.kt)("meta",{name:"twitter:description",content:"2-1. Create an Intelligent App that leverages Azure AI Vision to analyze images and extract data."}),(0,i.kt)("meta",{name:"twitter:image",content:"https://azure.github.io/Cloud-Native/img/ogImage.png"}),(0,i.kt)("meta",{name:"twitter:card",content:"summary_large_image"}),(0,i.kt)("meta",{name:"twitter:creator",content:"@devanshidiaries"}),(0,i.kt)("meta",{name:"twitter:site",content:"@AzureAdvocates"}),(0,i.kt)("link",{rel:"canonical",href:"https://azure.github.io/Cloud-Native/30daysofIA/build-your-first-intelligent-app-with-azure-ai-and-aks-1"})),(0,i.kt)("p",null,"Delve into creating an Intelligent App that leverages Azure AI Vision to analyze images and extract data. Develop an API to perform optical character recognition (OCR) on uploaded images and testing this API locally."),(0,i.kt)("h2",{id:"what-well-cover"},"What We'll Cover:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Understanding Azure AI Vision and Azure Kubernetes Service"),(0,i.kt)("li",{parentName:"ul"},"Build a Python Web API to perform OCR"),(0,i.kt)("li",{parentName:"ul"},"Test the API locally")),(0,i.kt)("p",null,(0,i.kt)("img",{alt:"image depicting an Intelligent App using AI",src:n(12177).Z,width:"936",height:"570"})),(0,i.kt)("h2",{id:"jumpstart-your-ai-journey-building-your-first-intelligent-app-with-azure-ai-and-aks-1"},"Jumpstart Your AI Journey: Building Your First Intelligent App with Azure AI and AKS (1)"),(0,i.kt)("p",null,"An Intelligent App is a software application that uses artificial intelligence (AI), such as machine learning (ML), to extract meaningful insights, enabling users to make informed decisions based on the extracted text, data, sound, images, and more."),(0,i.kt)("p",null,"Intelligent Apps can employ advanced algorithms and models to recognize objects, patterns, and features within images, allowing them to perform tasks like object identification, image classification, facial recognition, and image segmentation."),(0,i.kt)("p",null,"To explore the power of Intelligent Apps, let\u2019s build a Python app that performs ",(0,i.kt)("a",{parentName:"p",href:"https://learn.microsoft.com/en-us/azure/ai-services/computer-vision/overview-ocr?WT.mc_id=javascript-99907-ninarasi"},"optical character recognition")," (OCR) on images containing numbers and performs aggregate functions on the extracted numbers, like sum and average. We\u2019ll leverage ",(0,i.kt)("a",{parentName:"p",href:"https://azure.microsoft.com/en-ca/products/cognitive-services?WT.mc_id=javascript-99907-ninarasi"},"Azure AI Services")," for the OCR functionality and ",(0,i.kt)("a",{parentName:"p",href:"https://azure.microsoft.com/en-ca/products/kubernetes-service?WT.mc_id=javascript-99907-ninarasi"},"Azure Kubernetes Service")," (AKS) to develop, publish, and maintain a cloud-based, scalable app in Azure."),(0,i.kt)("p",null,"Let\u2019s get started!"),(0,i.kt)("h2",{id:"understanding-azure-ai-vision-and-azure-kubernetes-service"},"Understanding Azure AI Vision and Azure Kubernetes Service"),(0,i.kt)("p",null,"Azure AI Vision is a cloud-based service that offers a range of advanced computer vision capabilities enabling applications to interpret content, such as images and videos, using ML and other AI algorithms. Azure computer vision lets users integrate powerful vision features into their projects without ML experience."),(0,i.kt)("p",null,"OCR is one of the critical functionalities within Azure vision service, allowing applications to extract text from images. Within Azure AI Vision service, OCR offers several features, including text extraction, multilingual support, layout analysis of documents, and integration with Azure services, such as ",(0,i.kt)("a",{parentName:"p",href:"https://azure.microsoft.com/en-ca/products/functions"},"Azure Functions")," and ",(0,i.kt)("a",{parentName:"p",href:"https://azure.microsoft.com/en-ca/products/machine-learning"},"Azure Machine Learning"),"."),(0,i.kt)("admonition",{type:"info"},(0,i.kt)("p",{parentName:"admonition"},"Watch the intelligent apps webinar on ",(0,i.kt)("strong",{parentName:"p"},(0,i.kt)("a",{parentName:"strong",href:"https://info.microsoft.com/ww-landing-driving-business-value-by-modernizing-with-cloud-native-and-ai.html?WT.mc_id=javascript-99907-ninarasi"},"Driving Business Value by Modernizing with Cloud-Native & AI")),"\u202fwith ",(0,i.kt)("em",{parentName:"p"},"Microsoft")," and ",(0,i.kt)("em",{parentName:"p"},"Forrester")," on ",(0,i.kt)("strong",{parentName:"p"},"September 26"),". ")),(0,i.kt)("p",null,"AKS is Microsoft Azure\u2019s container orchestration platform that enables us to quickly and efficiently deploy, manage, and scale containerized applications, such as our Intelligent App."),(0,i.kt)("p",null,"Let\u2019s explore how to leverage these tools to build an Intelligent App that uses Azure AI Vision to analyze images and extract data."),(0,i.kt)("h3",{id:"prerequisites"},"Prerequisites"),(0,i.kt)("p",null,"To follow this tutorial, ensure you have the following:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"https://www.python.org/downloads/"},"Python 3.7")," or later installed"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"https://code.visualstudio.com/download"},"VS Code")," or another integrated development environment (IDE) for writing Python code"),(0,i.kt)("li",{parentName:"ul"},"The ",(0,i.kt)("a",{parentName:"li",href:"https://github.com/contentlab-io/Microsoft-Using-Azure-Kubernetes-Service-to-Deploy-an-Intelligent-App-for-Analyzing-Images-1/tree/main/Microsoft_Series17-18_Code/intelligent-app-before"},"sample Python application")," downloaded"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"https://pip.pypa.io/en/stable/installing/"},"pip"),", the package manager for Python, installed"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"https://www.docker.com/products/docker-desktop/"},"Docker Desktop")," installed. Ensure Docker is running on Linux containers."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"https://www.postman.com/downloads/"},"Postman")," installed. We\u2019ll use this to test our API."),(0,i.kt)("li",{parentName:"ul"},"A ",(0,i.kt)("a",{parentName:"li",href:"https://azure.microsoft.com/free/?WT.mc_id=javascript-99907-ninarasi"},"free Azure account"),". Sign up if you don\u2019t have one yet."),(0,i.kt)("li",{parentName:"ul"},"The ",(0,i.kt)("a",{parentName:"li",href:"https://learn.microsoft.com/cli/azure/install-azure-cli?WT.mc_id=javascript-99907-ninarasi"},"Azure command-line interface")," (CLI)")),(0,i.kt)("p",null,"For a preview of this final project, take a look at the ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/contentlab-io/Microsoft-Using-Azure-Kubernetes-Service-to-Deploy-an-Intelligent-App-for-Analyzing-Images-1/tree/main/Microsoft_Series17-18_Code/intelligent-app-after"},"complete project code"),"."),(0,i.kt)("h3",{id:"building-the-api-with-azure-ai-vision-service"},"Building the API with Azure AI Vision Service"),(0,i.kt)("p",null,"First, log in to your Azure account and navigate to the ",(0,i.kt)("a",{parentName:"p",href:"https://portal.azure.com"},"Azure Portal"),"."),(0,i.kt)("p",null,"Click ",(0,i.kt)("strong",{parentName:"p"},"Create a resource")," and search for \u201cresource group.\u201d Create a new resource group named ",(0,i.kt)("inlineCode",{parentName:"p"},"computer-vision"),"."),(0,i.kt)("p",null,(0,i.kt)("img",{alt:"image in Azure Portal of creating a new resource group",src:n(88748).Z,width:"1248",height:"761"})),(0,i.kt)("p",null,"Return to the Azure Portal home page and click ",(0,i.kt)("strong",{parentName:"p"},"Create a resource"),"."),(0,i.kt)("p",null,(0,i.kt)("img",{alt:"image in Azure Portal of creating a new resource",src:n(65440).Z,width:"624",height:"308"})),(0,i.kt)("p",null,"Search for \u201ccomputer vision\u201d and select it from the results. Click ",(0,i.kt)("strong",{parentName:"p"},"Create"),"."),(0,i.kt)("p",null,(0,i.kt)("img",{alt:"image of searching for resource group in Azure Portal",src:n(43039).Z,width:"572",height:"1160"})),(0,i.kt)("p",null,"Clicking ",(0,i.kt)("strong",{parentName:"p"},"Create")," displays the Create Computer Vision wizard. Select the resource group you just created as the Resource group from the dropdown. Enter a resource name similar to what is seen in the screenshot below. Note that this must be a unique name so choose one that is not currently in use. Select Standard S1 as the pricing tier and check the Responsible use of AI box in the field at the bottom."),(0,i.kt)("p",null,(0,i.kt)("img",{alt:"Image of selecting the Responsible use of AI box",src:n(49345).Z,width:"983",height:"1100"})),(0,i.kt)("p",null,(0,i.kt)("img",{alt:"Image of project details",src:n(67748).Z,width:"624",height:"380"})),(0,i.kt)("p",null,"Click ",(0,i.kt)("strong",{parentName:"p"},"Review + Create"),", then click ",(0,i.kt)("strong",{parentName:"p"},"Create"),"."),(0,i.kt)("p",null,"Next, get the API key and endpoint from the Azure AI services multi-service account you created. Once the resource is created, navigate to the resource details page. Under ",(0,i.kt)("strong",{parentName:"p"},"Resource Management"),", select ",(0,i.kt)("strong",{parentName:"p"},"Keys and Endpoint"),". Once on the ",(0,i.kt)("strong",{parentName:"p"},"Keys and Endpoint")," page, you\u2019ll find the ",(0,i.kt)("strong",{parentName:"p"},"Key 1")," and ",(0,i.kt)("strong",{parentName:"p"},"Endpoint")," values. These credentials are necessary to access the Azure AI APIs."),(0,i.kt)("p",null,(0,i.kt)("img",{alt:"image of selecting Keys and Endpoint",src:n(14842).Z,width:"624",height:"352"})),(0,i.kt)("p",null,(0,i.kt)("img",{alt:"second image of selecting Keys and Endpoint",src:n(23418).Z,width:"1100",height:"676"})),(0,i.kt)("h4",{id:"configuring-the-local-environment-variables"},"Configuring the Local Environment Variables"),(0,i.kt)("p",null,"Next, we need to define a local environment variable file. This convenient way to store our API keys in a simple text file enables us to manage sensitive information and separate configuration from our API code."),(0,i.kt)("p",null,"If you haven\u2019t done so already, clone the ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/contentlab-io/Microsoft-Using-Azure-Kubernetes-Service-to-Deploy-an-Intelligent-App-for-Analyzing-Images"},"starter project template")," from GitHub into your local computer. Open the ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/contentlab-io/Microsoft-Using-Azure-Kubernetes-Service-to-Deploy-an-Intelligent-App-for-Analyzing-Images/tree/main/Microsoft_Series17-18_Code/intelligent-app-before"},"starter project template")," in a code editor and create a new ",(0,i.kt)("inlineCode",{parentName:"p"},".env")," file in the root folder."),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Note"),': Root folder in this case is the \u201c/Microsoft_Series17-18_Code/intelligent-app-before" folder.'),(0,i.kt)("p",null,"Add the following key-value pairs to the file, remembering to replace the placeholder values with those you obtained from the previous step."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"VISION_KEY=\nVISION_ENDPOINT=\n")),(0,i.kt)("h4",{id:"reviewing-the-quickstart-code"},"Reviewing the Quickstart Code"),(0,i.kt)("p",null,"Let\u2019s review the ",(0,i.kt)("inlineCode",{parentName:"p"},"app.py")," file in the starter project template. "),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"app.py")," file works as the entry point for the Flask application. It is responsible for initializing the web server, listening for incoming requests, dispatching them to the appropriate route handlers, and generating responses. "),(0,i.kt)("p",null,"Below is the code contained in the ",(0,i.kt)("inlineCode",{parentName:"p"},"app.py")," file:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"import os\nimport json\n \nfrom flask import Flask, request \nfrom flask_restful import Resource, Api\nfrom werkzeug.utils import secure_filename\n\napp = Flask(__name__,\n static_url_path='',\n static_folder='static/files')\n\napi = Api(app)\n\napp = Flask(__name__)\n\napp.config['UPLOAD_FOLDER'] = 'files'\n\napi = Api(app)\n\nclass UploadHandler(Resource):\n\n def allowed_file(self, filename):\n return '.' in filename and \\\n filename.rsplit('.', 1)[1].lower() in {'png'}\n\n def post(self):\n form = request.form.to_dict()\n\n if 'file' not in request.files:\n return json.dumps({ \"success\": False, \"error\": \"No file part\"})\n\n file = request.files.get(\"file\")\n if file and self.allowed_file(file.filename):\n filename = secure_filename(file.filename)\n upload_folder = \"static/files\"\n if not os.path.exists(upload_folder):\n os.makedirs(upload_folder)\n local_file_path = os.path.join(upload_folder, filename)\n file.save(local_file_path)\n\n return f\"File {filename} uploaded successfully to folder: {upload_folder}\"\n\napi.add_resource(UploadHandler, \"/\")\n\nif __name__ == '__main__':\n app.run(debug=True)\n\n")),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"UploadHandler")," class above is defined as a Flask RESTful resource to handle file uploads. The class contains two methods:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("p",{parentName:"li"},"The ",(0,i.kt)("inlineCode",{parentName:"p"},"allowed_file")," method checks whether the file extension is allowed for upload. In this case, only ",(0,i.kt)("inlineCode",{parentName:"p"},".png")," files are allowed.")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("p",{parentName:"li"},"The ",(0,i.kt)("inlineCode",{parentName:"p"},"post")," method handles HTTP POST requests for file uploads. It saves the uploaded ",(0,i.kt)("inlineCode",{parentName:"p"},".png")," file to the ",(0,i.kt)("inlineCode",{parentName:"p"},"static/files")," folder."))),(0,i.kt)("p",null,"Finally, the application returns a text response informing us that the file has been uploaded successfully."),(0,i.kt)("h4",{id:"implementing-the-rest-api-in-python"},"Implementing the REST API in Python"),(0,i.kt)("p",null,"To implement image analysis in your REST API, open the starter project template in your terminal, ",(0,i.kt)("a",{parentName:"p",href:"https://docs.python.org/3/library/venv.html"},"create a virtual environment"),", and ",(0,i.kt)("a",{parentName:"p",href:"https://docs.python.org/3/library/venv.html#how-venvs-work"},"activate it"),"."),(0,i.kt)("p",null,"Next, install the Azure AI Vision SDK:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"pip install azure-ai-vision\n")),(0,i.kt)("p",null,"Then, add the following line to the ",(0,i.kt)("inlineCode",{parentName:"p"},"requirements.txt")," file to include the ",(0,i.kt)("inlineCode",{parentName:"p"},"azure-ai-vision")," package:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"azure-ai-vision==0.13.0b1\n")),(0,i.kt)("p",null,"Now, create an ",(0,i.kt)("inlineCode",{parentName:"p"},"ocr_helper.py")," file in the project\u2019s root folder. This file is a Python module that provides functions for processing images using OCR. Add the following code to the file:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"import os\nfrom statistics import median\nfrom decimal import Decimal\nimport azure.ai.vision as sdk\n\ndef process_ocr(source_image):\n service_options = sdk.VisionServiceOptions(os.environ[\"VISION_ENDPOINT\"],\n os.environ[\"VISION_KEY\"])\n\n vision_source = sdk.VisionSource(filename=source_image)\n\n analysis_options = sdk.ImageAnalysisOptions()\n\n analysis_options.features = (\n sdk.ImageAnalysisFeature.CAPTION |\n sdk.ImageAnalysisFeature.TEXT\n )\n\n analysis_options.language = \"en\"\n\n analysis_options.gender_neutral_caption = True\n\n image_analyzer = sdk.ImageAnalyzer(service_options, vision_source, analysis_options)\n\n result = image_analyzer.analyze()\n\n ocr_result = get_ocr_result(result)\n\n return ocr_result\n\ndef get_ocr_result(result):\n string_list = []\n\n if result.reason != sdk.ImageAnalysisResultReason.ANALYZED:\n return sdk.ImageAnalysisErrorDetails.from_result(result)\n else:\n if result.text is not None:\n for line in result.text.lines:\n for word in line.words:\n string_list.append(word.content)\n\n number_list = convert_to_decimal_list(string_list)\n\n aggregate_result = aggregate_operations(number_list)\n\n return {\n \"aggregate_result\": aggregate_result,\n \"numbers_read\": string_list\n } \n\ndef convert_to_decimal_list(string_list):\n return list(map(Decimal, string_list))\n\ndef aggregate_operations(numbers):\n result = {\n 'sum': sum(numbers),\n 'average': sum(numbers) / len(numbers),\n 'median': median(numbers),\n 'min': min(numbers),\n 'max': max(numbers)\n `}`\n `return result`\n")),(0,i.kt)("p",null,"This module uses the ",(0,i.kt)("a",{parentName:"p",href:"https://learn.microsoft.com/python/api/azure-ai-vision/?view=azure-python-preview?WT.mc_id=javascript-99907-ninarasi"},"azure-ai-vision")," package to analyze images, including capturing captions and extracting text from the image. The ",(0,i.kt)("inlineCode",{parentName:"p"},"process_ocr")," function inputs an image file and performs the OCR analysis. The module is a convenient tool for analyzing text-based data in pictures and performing numerical computations based on that data."),(0,i.kt)("p",null,"Let\u2019s review the different components of the ",(0,i.kt)("inlineCode",{parentName:"p"},"ocr_helper.py")," module listed above and explore how its functions help our Intelligent App process images and perform aggregate operations:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"The ",(0,i.kt)("inlineCode",{parentName:"li"},"process_ocr")," function takes the parameter ",(0,i.kt)("inlineCode",{parentName:"li"},"source_image"),", which is the path of the image to be processed. The function then initializes the ",(0,i.kt)("inlineCode",{parentName:"li"},"VisionServiceOptions")," using environment variables ",(0,i.kt)("inlineCode",{parentName:"li"},"VISION_ENDPOINT")," and ",(0,i.kt)("inlineCode",{parentName:"li"},"VISION_KEY")," to connect to the Azure AI Vision API."),(0,i.kt)("li",{parentName:"ul"},"The ",(0,i.kt)("inlineCode",{parentName:"li"},"process_ocr")," function creates a ",(0,i.kt)("inlineCode",{parentName:"li"},"VisionSource")," object with the specified ",(0,i.kt)("inlineCode",{parentName:"li"},"source_image")," file name. ",(0,i.kt)("inlineCode",{parentName:"li"},"ImageAnalysisOptions")," specify the features to be analyzed, including ",(0,i.kt)("inlineCode",{parentName:"li"},"CAPTION")," and ",(0,i.kt)("inlineCode",{parentName:"li"},"TEXT"),". The language is set to English (",(0,i.kt)("inlineCode",{parentName:"li"},'"en"'),"), and gender-neutral captions are enabled."),(0,i.kt)("li",{parentName:"ul"},"Finally, an ",(0,i.kt)("inlineCode",{parentName:"li"},"ImageAnalyzer")," object is created with the service options, vision source, and analysis options. The image is then analyzed using the ",(0,i.kt)("inlineCode",{parentName:"li"},"image_analyzer.analyze")," method to retrieve OCR results. The recognized text from the OCR result is extracted, with numerical values stored in the ",(0,i.kt)("inlineCode",{parentName:"li"},"string_list")," variable."),(0,i.kt)("li",{parentName:"ul"},"The ",(0,i.kt)("inlineCode",{parentName:"li"},"convert_to_decimal_list")," function converts the list of strings to a list of decimal numbers, which helps process numeric values extracted from the text."),(0,i.kt)("li",{parentName:"ul"},"The ",(0,i.kt)("inlineCode",{parentName:"li"},"aggregate_operations")," function calculates various aggregate statistics (sum, average, median, minimum, maximum) from a list of numbers and returns the results in a dictionary.")),(0,i.kt)("p",null,"Note that you must have the appropriate credentials (",(0,i.kt)("inlineCode",{parentName:"p"},"VISION_KEY"),") and endpoint (",(0,i.kt)("inlineCode",{parentName:"p"},"VISION_ENDPOINT"),") configured for the Azure AI Vision API to use this module."),(0,i.kt)("p",null,"Finally, we must modify the ",(0,i.kt)("inlineCode",{parentName:"p"},"app.py")," file so our code can use the ",(0,i.kt)("inlineCode",{parentName:"p"},"process_ocr")," function of the ",(0,i.kt)("inlineCode",{parentName:"p"},"ocr_helper.py")," file."),(0,i.kt)("p",null,"Add the following import statement to the ",(0,i.kt)("inlineCode",{parentName:"p"},"app.py")," file: "),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"from ocr_helper import process_ocr\n")),(0,i.kt)("p",null,"Then, replace this line:"),(0,i.kt)("p",null,(0,i.kt)("inlineCode",{parentName:"p"},'return f"File {filename} uploaded successfully to folder: {upload_folder}"')),(0,i.kt)("p",null,"With these two lines:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"aggregates = process_ocr(local_file_path)\nreturn json.dumps(aggregates, default=str)\n")),(0,i.kt)("p",null,"Doing so allows our application to perform aggregate operations (sum, average, median, minimum, maximum) of any numeric values found in the text and return the operation\u2019s result as a JSON response."),(0,i.kt)("h3",{id:"running-the-intelligent-app-locally"},"Running the Intelligent App Locally"),(0,i.kt)("p",null,"Our final goal is to deploy the sample app to AKS, which will run the app in a container. However, as a good practice, let\u2019s run the app in a container locally before moving it to AKS. Doing so ensures that our application and its dependencies are encapsulated and can be run consistently across different environments."),(0,i.kt)("h4",{id:"deploying-to-docker-desktop"},"Deploying to Docker Desktop"),(0,i.kt)("p",null,"For our application to run on Docker, it needs two additional files: a Dockerfile and ",(0,i.kt)("inlineCode",{parentName:"p"},"docker-compose.yml"),". A Dockerfile creates a single container image by specifying the steps to build it, and a ",(0,i.kt)("inlineCode",{parentName:"p"},"docker-compose.yml")," file manages multiple containers as a cohesive application, defining their configurations and relationships. Dockerfile builds the image, while ",(0,i.kt)("inlineCode",{parentName:"p"},"docker-compose.yml")," orchestrates multi-container applications."),(0,i.kt)("p",null,"In the project root folder, add a file named ",(0,i.kt)("inlineCode",{parentName:"p"},"Dockerfile")," with the content below:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},'# syntax=docker/dockerfile:1\n\nFROM python:3.8-slim-buster\n\nWORKDIR /intelligentapp\n\nCOPY requirements.txt requirements.txt\nRUN pip3 install -r requirements.txt\n\nRUN pip install debugpy\n\nCOPY . .\n\nCMD [ "python3", "-m" , "flask", "run", "--host=0.0.0.0"]\n')),(0,i.kt)("p",null,"Now create a file named ",(0,i.kt)("inlineCode",{parentName:"p"},"docker-compose.yml")," file in the project\u2019s root folder with the following code, replacing the ",(0,i.kt)("inlineCode",{parentName:"p"},"VISION_KEY")," and the ",(0,i.kt)("inlineCode",{parentName:"p"},"VISION_ENDPOINT")," according to the environment variables you configured earlier:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"version: '3.8'\nservices:\n intelligentapp:\n build:\n context: .\n dockerfile: Dockerfile\n image: intelligent-app\n ports:\n - 5000:5000\n container_name: intelligent-app\n environment:\n - VISION_KEY=\n - VISION_ENDPOINT=\n")),(0,i.kt)("p",null,"Then, run the following command in the terminal to build the image and start a container for the Intelligent App services defined in the ",(0,i.kt)("inlineCode",{parentName:"p"},"docker-compose.yml")," file: "),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"docker-compose up --build --force-recreate\n")),(0,i.kt)("p",null,"Next, open a new terminal and run the command below to list the image deployed to your local Docker:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"docker images\n")),(0,i.kt)("p",null,(0,i.kt)("img",{alt:"docker images",src:n(86594).Z,width:"506",height:"76"})),(0,i.kt)("h4",{id:"testing-the-intelligent-app-locally"},"Testing the Intelligent App Locally"),(0,i.kt)("p",null,"Now, we\u2019ll test the Intelligent App\u2019s functionality using the following test image:"),(0,i.kt)("p",null,(0,i.kt)("img",{alt:"test image to test functionality",src:n(74531).Z,width:"204",height:"223"})),(0,i.kt)("p",null,"This image is provided as ",(0,i.kt)("inlineCode",{parentName:"p"},"sample1.png")," in the sample app source files, so you can easily use it in the next step."),(0,i.kt)("p",null,"The Intelligent App will read the image above using OCR to extract numbers in the image and perform five aggregate functions:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Sum"),(0,i.kt)("li",{parentName:"ul"},"Average"),(0,i.kt)("li",{parentName:"ul"},"Median"),(0,i.kt)("li",{parentName:"ul"},"Min"),(0,i.kt)("li",{parentName:"ul"},"Max")),(0,i.kt)("p",null,"To test the API, open Postman and fill out the fields as follows:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"URL:")," http://localhost:5000/"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"Method:")," POST"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"Body:"),(0,i.kt)("ul",{parentName:"li"},(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"Form-data"),(0,i.kt)("ul",{parentName:"li"},(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"Key"),": File \u2014 Click the right end of the ",(0,i.kt)("strong",{parentName:"li"},"Key")," field and select ",(0,i.kt)("strong",{parentName:"li"},"File")," from the dropdown list."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"Value"),": Click ",(0,i.kt)("strong",{parentName:"li"},"Select Files"),", then select the ",(0,i.kt)("strong",{parentName:"li"},"sample1.png")," file provided in the sample code.")))))),(0,i.kt)("p",null,(0,i.kt)("img",{alt:"image of selecting the sample file",src:n(47994).Z,width:"624",height:"375"})),(0,i.kt)("p",null,"Now click the ",(0,i.kt)("strong",{parentName:"p"},"Send")," button and review the result body:"),(0,i.kt)("p",null,(0,i.kt)("inlineCode",{parentName:"p"},'"{\\"aggregate_result\\": {\\"sum\\": \\"25821\\", \\"average\\": \\"5164.2\\", \\"median\\": \\"5622\\", \\"min\\": \\"1447\\", \\"max\\": \\"9802\\"}, \\"numbers_read\\": [\\"3049\\", \\"5901\\", \\"5622\\", \\"1447\\", \\"9802\\"]}"')),(0,i.kt)("p",null,"As we can see, the app running on a local container returned the correct results based on the sample image. So, we\u2019re ready to prepare our Azure resources to run the Intelligent App on AKS."),(0,i.kt)("h2",{id:"exercise"},"Exercise"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("p",{parentName:"li"},"Complete this ",(0,i.kt)("strong",{parentName:"p"},"hands-on sample")," ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/contentlab-io/Microsoft-Using-Azure-Kubernetes-Service-to-Deploy-an-Intelligent-App-for-Analyzing-Images-1/tree/main/Microsoft_Series17-18_Code/intelligent-app-after"},"project code")," to build your first intelligent app.")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("p",{parentName:"li"},"Complete the ",(0,i.kt)("strong",{parentName:"p"},(0,i.kt)("a",{parentName:"strong",href:"https://aka.ms/fallforIA/apps-csc"},"Apps Cloud Skills Challenge"))," to build on your app dev and AI skills.")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("p",{parentName:"li"},"Watch the ",(0,i.kt)("strong",{parentName:"p"},(0,i.kt)("a",{parentName:"strong",href:"https://reactor.microsoft.com/en-us/reactor/series/S-1037/"},"Ask the Expert: Azure Functions"))," session for live Q&A with the Product Engineering team on building intelligent apps."),(0,i.kt)("h2",{parentName:"li",id:"next-steps"},"Next Steps"))),(0,i.kt)("p",null,"In this article, we explored the creation of an Intelligent App that leverages Azure AI Vision to analyze images and extract data. We learned how to build a Python Web API to perform OCR on uploaded images and subsequently test this API locally."),(0,i.kt)("p",null,"Head to the ",(0,i.kt)("a",{parentName:"p",href:"https://azure.github.io/Cloud-Native/30daysofIA/build-your-first-intelligent-app-with-azure-ai-and-aks-2"},"next part"),", to deploy this API via Azure Kubernetes Service."))}c.isMDXComponent=!0},12177:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/blog-image-1-1-e8cf7713343a8855e8f9b05434148f9d.png"},74531:(e,t,n)=>{n.d(t,{Z:()=>a});const a="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMwAAADfCAMAAACNvsz1AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAEUUExUReLRfNO7a62DQZBZIX09DHMuAXItANS9bJlmK5hlKrqXUHc0BbqWT6+GQ4ZLFrOMSNC3ad/NeXUyA7SOSeHQe7WPSsWnXMaoXZdjKbWQSnIuAIdNGKyCQI5XINK5anw8C5FPTygoKE9Pkdmyg0JCZwwMDAAAAG2KvLyKbYOy2TQ0XGGx+fmxYT8/iFw0NAAAYYqKMwAzitWKM7F4VwAAM2EAAABhsVd4sdGiaTOK1YozAIg/PzMAAGmi0RcXF7FhADOKsfrAgWdCQt3AgMmioi4uLnw9DNG4aY9YIayDQYdMF4hOGauBQLaRTNK6apdkKXUyBMeqXpZjKNO8bHYzBLaRS7mVTohNGLiUTq2EQnY0BQAAAJc5ouMAAABcdFJOU/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////8Ag20X4AAAAAlwSFlzAAAOwwAADsMBx2+oZAAACzlJREFUeF7tnVtz47YVxzmzM/ZMHixRL/u0M3Ycsmo2dS/Oxa3STbOOn9KX7oNn9P2/SM8VBEjIJEFAAlv8JhMBRzT3HP4B8HYEVIXCebk+ftjtrtbE7t3xWpx3uf6C3/JGawHd/eIJ56uVxdGxe5UQDEf5ZpUcJQjhKzLebLa1GFZBvd3ckOOONtfYxnabVUUibMh1u99Q338vlZXxnkYBqQDXUL3aSGV1bND7Thrs/TdrbGMM9pt/G/c/rFkYluZ3KVcVnve3Ul4hWwhmJ+WqgspVvd5mVg+CkeICDnfI11ITyCblDtxUisgzbvRN+NF0/KfKUmXumpa4+4MYgFs2tb1w9riplIE72cj6w3kMg1lK+4LHF0P6o1iqg8QHiIVxLM9UI0KjQf+NFhTMUmWkfcFR/5ZLJMBH+PwO/PwTm4iH9s9/6YKBL/+Kn3+DAhlmg30msjLKo/Gpbb+X0g+2n3sIETyXGmz+I5fA9hOX5oL+x1XGoH4+te3fuYTadH424H0XDMgkpeofRtN5JFTmqW24cDDCgJtdOyPvu2CkkSGdcSbofxplDq30ngfj5s/Yu6W8pxC9wYB+/5TiLNIpc9d8IyXtDOC4FQy3PTsY08xCg0miDI7MreqibmMTa380zotcXTAPPOIRP4eNAEmUYQVMJ+ZgsIl9NM5zI7ODwfGbx4lfYMuw4Qz9T6EMnDT1TI5OgtPUjNT5RhzvgkFpOjLrM3ik+RKgbf9FTQzL4rwZE6xg9GqGyKfPCHoJwO5xh4AmBP/fm85uByOS3lW/OsbpJDzPVPfiE4aizvP5kKIzoP8ugcGkVEZ9gs4gFyp4PYMnTYmig79UHp0ruOmkVAYuY+gTrrp00IVWhb2BmxOCkcBVNn+r6KXDbND/RMo86Kmma2UwPktJ8bSoJlCYlKPZnTnAB7lsBl0Gw1Q/mGcQK1SY+Mp8Ng2oMTdn2JiY3s20HQzfbcOfDe+uJxJfGT33vdg+6SlkEIsdDG3RhN80p1Om7zYZpWzTGW9PbTKZlKPZ+UH/oypzQTJQJuLBQ/+LMtEoyngpyszlXFoXZeZSlAkB/S/KRKMo46UoM5eiTAjof1EmGlkrQ7fyjFjqquZ0BakL9ibKfvj0YDoJlKGHLIxYzNMZ+8mL5gbY4XzG7QKfACLof1xl6Ek4Q/Xam65w28tzqFE8qoYHk0KZwXtv8FDf+alYeyi6eQ73UHr5CbbNS5l+ML50hWaY5/CJekvuynjSFf6j7y+tPIff6P+5K+NJVziR5wDkrownXcGf54Dkr4xpZhqMP88ByU0ZHpq7M6QnXcGb50DkpozApxnY2xNUeukKbHDzHJjMlCFdKBNQnRymK/jyHJjMlCFq0kN7j75rQiSYQZ6DkJkyBvvlJKtl0hU4LjvPQclQGdoHXLJIqcMKxs1zUHJUBmKoNQ/AQtMVhnkOSo59BjkMlWkkvGGeg5KhMnA7hhf+Xw8amd55ncxzyFMZHMHsXcH9CozWarHyHBrnXXleynBuAoTSvIhFXoq76Qo4BDAqFr1zRwN+ht07o/9RleHcBHDcXM/U3nSFFzK2jXHbObUGyRNfGZbBObQn0hV6Vs1TITJRZkj0HZ4i5RXA+UH/izI5gv5HV8bdxf+xMktCR//TKGP2E2GH0yjKjEK7MPuJsMNpFGVGoV2Y/UTY4TSKMqPQLsx+IuxwGkWZUWgXZj8RdjiNoswotAuznwg7nEZRZhTahdlPhB1OI4EychePiIXZ90xUd/Mc2BQePPpv/pqCWXog5fkKwgbaIeUrGBP6zXXr0YXHNI8EysibM6Q7MPTUjEzMoeF60+U5eExzQf8jK9N7p4n7cx8AAo3U8aepVPCaZpJCGSsYOTAPw5+R6U+4HrvfMXtMM0H/0ypTVc/68N+H5+ll6PPmsyhzeOtIRwzmLMq82Qc8bSq0mZ1DmSd8e4HjVO/Ew7jvmgiPaRrof2RlnKSGuqox+UrpJQPSSwMpGTymaSQ5zzBGBxidOnonxGfrp6mCxzQV9D+uMqSLldRQYzBmXoZuziPCej+jeEwTSaCMgLl+3HsOTiKQM1C1ZgoUg8c0GfQ/rjIGPZM/dsll1Q/OWNcMHfeYJpNOGeor8tkF42QwRNYlqTI67cSTlaLVBVM/N/3O4THNIqUyZoj1pmPcDzu6xzQP9D+2MryHZ9PZu+y5X8xotu+9/wc8pnmkUwbvYKSoScyY+SetDEz9SxaPaS7of1RlPEkN1WeIRhALjHR8s4Zwn/eYZhJfmWFSA/AZT6GAiY+rAqslFcYewKeD/kdVxpPUgJBRygBVFQ5bKkwmylwS9D+qMhckS2WCDyf6H1uZS2lblBmjKGNRlAGKMmMUZSyKMkBRZoyijEVRBijKjFGUschMGe+EC721JhDLJE91hHyeAfgnXMBX4vqkSbBN8lRHyOLpzBsTLpBVyoJtkqc6RGAw0ZU5OeGCu9YE4TExD4HPNtH/qH3mxIQL+9Zea4LwmJg38wbeIH6fOTHhAr4J6HnuMTGhwsRXhukH01trAvGYiDpUmGTnmV4w/bUmAI9JCBbmTMrwMgCO5x4Tcx8qzJmUGaw14TUJZjbx+aD/UZXhP3eCGa414TMJMMaFenAWZaycBvoEPCZBkjiCAkL/EyvjWWvCu/wEoe/bQziDMp61Jk4tPwGwMIEHFP1Pq4y8ARTgystrEpYIcw5lxOWOjzh1lotsqsLkqwxdBxPoNnx4TcwiYc51BSB4XHVNmo+WrTIWY8EsEyaBMm9NuDAWjEkUzEWZtyZcGAlmoTCplFF6ylhjsGKbXsz2uShzQVKNZssIPZ7of1EmIcGHE/2PrsyFxC3KjBK+i0X/eFFmlKJMR1EGKMqMUpTpKMoARZlRijIdRRkgiTK01oT78vueHgm4qQpkkjJDMzWEpQAQ6H9kZTgPwFlrQkytFc6tmqSOoYil9xhkMvGV8a01IXVEHdWlNAC1yI+B4ItsZmoA7/QFn7ppJjTubJhogr/cwmfo8nDtk8zU8ARfUWE20ZV5ND/9A8/lrbG+Juum0geT/nbTLD+R4UwNnrUmPME8Oaa+6/4H1eNEV8az1oRl0/nMTy8/gYQGE10Zz1oTOHCxXBAeDwCnl59AQptZAmVMMzPB0MgMPQm6hwzE9ojgCyaTmRq6X8uatSYA+Wmj+bWvdBnP8hNAPjM14JjLfdusNUFQMKbK2wyWn6DjeJ/RTA3O+xltLrh8A6KnewwGmxg2yb4yWc3UoJcuiASDPwcmCfR32ieWn8Dj2H4KSjYl0P+oygB4UQKXkLrWBI5mPHhhAFTAsLRz9ZafyHSmBnBZP3Ughmio32AoOuqNTd4wA/Q/tjKCrjUBlyfW6Z48N1drgJnvoK4ynqlB5wOAodYarSkYiE9N0GV0mMhypgam0SMOwagM4DkbfctPZDtTwzOMaTpRA97hcKfBUw8PZxAgXZ3hCCfC7MPvYxT0P6oynNOPN1pigNGMz/+IdQ2gqAU3or9FgoaB+MrAMQfctSZu9dxj3RGryVikzoQ9CED/oyrjX2uCre71I5mkDFBVyUSZS4L+R1XmghRlciVLZYIPJ/qfQpn5e1n+7w6ViRNLEPJPB3vgBrODyjZONJc4JltwfyflqnoHtY2UL8BSZTbg/gcpV9URajfrVeYG3D9KuaquoXb1egk/iIXKvKL3W/Pn9Reo7t5LbRHnPyLvMZYvUkGucQjYXUqbJcrUr+T6VqpITVJd3WwWj2lnPR719hX7C3QRMQg4BqyWo3MIa5FrlXi6R73FUWCFvLvuxcLV7fHDyuTZ/X60u74DxVRDmwv/75yQz0NO2QuX5n9Cmar6L0vrS6Y5to31AAAAAElFTkSuQmCC"},47994:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/blog-image-1-11-96c8a57ad6beab15a2d06d1a0de0ad97.png"},88748:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/blog-image-1-2-49fb07b13652bc56001521b3312fae9d.png"},65440:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/blog-image-1-3-5b57e295568b12f4b213cb9fc3d39e81.png"},43039:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/blog-image-1-4-77065a4476c7a64b6b66d818a4c94a6c.png"},49345:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/blog-image-1-5-efe95dbf3756c59403a30a3a9a2b0c7a.png"},67748:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/blog-image-1-6-4e63cfb3a15c78ba383a4cdbdc221570.png"},14842:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/blog-image-1-7-1d88b437ef046ee6829ccc174cc61f3a.png"},23418:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/blog-image-1-8-f2e1dae9a99355268c4fc214e858ca32.png"},86594:(e,t,n)=>{n.d(t,{Z:()=>a});const a="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAfoAAABMCAMAAACyL4WnAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAG/UExURRgYGBgYVZnAzMzMzMyqfX2qzMCZVRgYfarMzMzAmVUYGKp9GBh9qszMqn0YGBhVmcDMzFWZwMzMwJlVGMvMzBkYGBh9qZlWGRkYVZi/yxkZGXypy8CZVhkZGBhVmL/LzFYZGRh8qX0ZGX2pyxl9qcvLzBgYfKnLy6p+GRkZVBlWmMyqfhoZGXypysDMwMCZfZnAwJmqmczAwMCqfarAwMDMqsvMqn0ZGL/MzFWYv8vMwJipmRkZVap9GanLzL+ZfcvLqn4ZGRgYVKnKv7+ZVsyqmZnAqqp9farMwFWZvxhUmL/LwFSYv32qqn0Yfb/MwJlVGb/Lqhl8qcvAmX4aGcyqqsCqqqqqfarLwMuqfanLwKp+Gsq/mX2qy32pyqrLzFUZGRpWmRoafanKy5mZwMuqfhp9qsq/wMCqfqm+v5lVfam/mcuqmZl9mZi/v5lWfVaZwBkYfMqqmal9farLvxkaflWZqlUZGJmZqn0ZfVWYqcu/mcqqfVaZv6l9GX2qqarKv5lWGqrMqpm/zBkZfanLqr+pqn19qhlVmb/Mqn5+qhoZVZi+v5i+yxkYfVYaGRlWmZi/zBkZVn2ZwAAAAJYAKZcAAACVdFJOU/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////8ANmQbOAAAAAlwSFlzAAAOwwAADsMBx2+oZAAAB59JREFUeF7tmk+L40YQxSVbmNkxHpPx0RDwxddkycU5LQhvFg/KzinkCyyEJOQ2hMDcckgYWJ/2C6feq+rWH8saWXY2E6gfzLSk7q6qrtfdki0njuM4juM4juM4juM4juM4juM4juM4juM4juM4juM4juM4juM4znmko/F4nCXJUorxZH2lZ7j66jrh6ZRNxpMV/o9nN1qn/dKRNJZmEzV2MsFmMoe7JPlCzhZadQIa4HY+3so4msZ0YNrQWupQpPHp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"}}]); \ No newline at end of file diff --git a/assets/js/292dd6ab.2ed66b1f.js b/assets/js/292dd6ab.96dd7aff.js similarity index 95% rename from assets/js/292dd6ab.2ed66b1f.js rename to assets/js/292dd6ab.96dd7aff.js index 62d2eda5a0..64e2e972c1 100644 --- a/assets/js/292dd6ab.2ed66b1f.js +++ b/assets/js/292dd6ab.96dd7aff.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[63695],{64058:s=>{s.exports=JSON.parse('{"label":"azure-cosmos-db","permalink":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[63695],{64058:s=>{s.exports=JSON.parse('{"label":"azure-cosmos-db","permalink":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/2ab0497b.158cc56e.js b/assets/js/2ab0497b.158cc56e.js deleted file mode 100644 index 7415d6ba1c..0000000000 --- a/assets/js/2ab0497b.158cc56e.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[8320],{50371:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-container-apps/page/3","page":3,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/azure-container-apps/page/2","nextPage":"/Cloud-Native/30daysofIA/tags/azure-container-apps/page/4","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/2ab0497b.aedede75.js b/assets/js/2ab0497b.aedede75.js new file mode 100644 index 0000000000..f8b781a18e --- /dev/null +++ b/assets/js/2ab0497b.aedede75.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[8320],{50371:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-container-apps/page/3","page":3,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/azure-container-apps/page/2","nextPage":"/Cloud-Native/30daysofIA/tags/azure-container-apps/page/4","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/2bc88290.6bb9e2c5.js b/assets/js/2bc88290.6bb9e2c5.js new file mode 100644 index 0000000000..9b3dce5fb1 --- /dev/null +++ b/assets/js/2bc88290.6bb9e2c5.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[9321],{18267:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service/page/4","page":4,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service/page/3","nextPage":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service/page/5","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/2bc88290.dcadace3.js b/assets/js/2bc88290.dcadace3.js deleted file mode 100644 index a7b7b41db9..0000000000 --- a/assets/js/2bc88290.dcadace3.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[9321],{18267:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service/page/4","page":4,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service/page/3","nextPage":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service/page/5","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/2bcfefbc.79af042e.js b/assets/js/2bcfefbc.888cb0ce.js similarity index 72% rename from assets/js/2bcfefbc.79af042e.js rename to assets/js/2bcfefbc.888cb0ce.js index f5253cecb9..c8ffb2cfb3 100644 --- a/assets/js/2bcfefbc.79af042e.js +++ b/assets/js/2bcfefbc.888cb0ce.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[22189],{52698:e=>{e.exports=JSON.parse('{"label":"ask-the-expert","permalink":"/Cloud-Native/30daysofIA/tags/ask-the-expert","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[22189],{52698:e=>{e.exports=JSON.parse('{"label":"ask-the-expert","permalink":"/Cloud-Native/30daysofIA/tags/ask-the-expert","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/2c72658d.ba4d97e4.js b/assets/js/2c72658d.366b7f1b.js similarity index 73% rename from assets/js/2c72658d.ba4d97e4.js rename to assets/js/2c72658d.366b7f1b.js index 8703622b2a..92f30f84b6 100644 --- a/assets/js/2c72658d.ba4d97e4.js +++ b/assets/js/2c72658d.366b7f1b.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[81615],{82854:a=>{a.exports=JSON.parse('{"label":"30-days-of-IA","permalink":"/Cloud-Native/30daysofIA/tags/30-days-of-ia","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[81615],{82854:a=>{a.exports=JSON.parse('{"label":"30-days-of-IA","permalink":"/Cloud-Native/30daysofIA/tags/30-days-of-ia","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/2d813f9f.0d024571.js b/assets/js/2d813f9f.0d024571.js deleted file mode 100644 index 13487948ff..0000000000 --- a/assets/js/2d813f9f.0d024571.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[22405],{1025:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db/page/8","page":8,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db/page/7","nextPage":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db/page/9","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/2d813f9f.11f4ebb8.js b/assets/js/2d813f9f.11f4ebb8.js new file mode 100644 index 0000000000..f1debd5fc9 --- /dev/null +++ b/assets/js/2d813f9f.11f4ebb8.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[22405],{1025:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db/page/8","page":8,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db/page/7","nextPage":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db/page/9","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/2efdc0bf.9e7e15e4.js b/assets/js/2efdc0bf.9e7e15e4.js new file mode 100644 index 0000000000..24f7e0e28a --- /dev/null +++ b/assets/js/2efdc0bf.9e7e15e4.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[30018],{16129:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/github-actions/page/2","page":2,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/github-actions","nextPage":"/Cloud-Native/30daysofIA/tags/github-actions/page/3","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/2efdc0bf.a2651cf3.js b/assets/js/2efdc0bf.a2651cf3.js deleted file mode 100644 index f1ad3bee68..0000000000 --- a/assets/js/2efdc0bf.a2651cf3.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[30018],{16129:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/github-actions/page/2","page":2,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/github-actions","nextPage":"/Cloud-Native/30daysofIA/tags/github-actions/page/3","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/30b4a191.1a460bb4.js b/assets/js/30b4a191.1a460bb4.js new file mode 100644 index 0000000000..0666dd6cef --- /dev/null +++ b/assets/js/30b4a191.1a460bb4.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[92788],{93570:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service/page/5","page":5,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service/page/4","nextPage":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service/page/6","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/30b4a191.e753ac3a.js b/assets/js/30b4a191.e753ac3a.js deleted file mode 100644 index dd426dfb8a..0000000000 --- a/assets/js/30b4a191.e753ac3a.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[92788],{93570:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service/page/5","page":5,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service/page/4","nextPage":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service/page/6","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/30f26a7a.56f65583.js b/assets/js/30f26a7a.56f65583.js new file mode 100644 index 0000000000..8ab91c23ba --- /dev/null +++ b/assets/js/30f26a7a.56f65583.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[60169],{73278:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/learn-live","page":1,"postsPerPage":1,"totalPages":11,"totalCount":11,"nextPage":"/Cloud-Native/30daysofIA/tags/learn-live/page/2","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/30f26a7a.b86dd9c1.js b/assets/js/30f26a7a.b86dd9c1.js deleted file mode 100644 index 97af82d376..0000000000 --- a/assets/js/30f26a7a.b86dd9c1.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[60169],{73278:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/learn-live","page":1,"postsPerPage":1,"totalPages":9,"totalCount":9,"nextPage":"/Cloud-Native/30daysofIA/tags/learn-live/page/2","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/31098de5.8fc37c83.js b/assets/js/31098de5.8fc37c83.js new file mode 100644 index 0000000000..8c5f43cab9 --- /dev/null +++ b/assets/js/31098de5.8fc37c83.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[64315],{40919:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-container-apps/page/7","page":7,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/azure-container-apps/page/6","nextPage":"/Cloud-Native/30daysofIA/tags/azure-container-apps/page/8","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/31098de5.d19d9574.js b/assets/js/31098de5.d19d9574.js deleted file mode 100644 index d3b8622d35..0000000000 --- a/assets/js/31098de5.d19d9574.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[64315],{40919:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-container-apps/page/7","page":7,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/azure-container-apps/page/6","nextPage":"/Cloud-Native/30daysofIA/tags/azure-container-apps/page/8","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/328b45c7.6b2a492c.js b/assets/js/328b45c7.6a90e9b2.js similarity index 95% rename from assets/js/328b45c7.6b2a492c.js rename to assets/js/328b45c7.6a90e9b2.js index faccf2c3f3..43d75e1861 100644 --- a/assets/js/328b45c7.6b2a492c.js +++ b/assets/js/328b45c7.6a90e9b2.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[418],{87357:e=>{e.exports=JSON.parse('{"label":"github-codespaces","permalink":"/Cloud-Native/30daysofIA/tags/github-codespaces","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[418],{87357:e=>{e.exports=JSON.parse('{"label":"github-codespaces","permalink":"/Cloud-Native/30daysofIA/tags/github-codespaces","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/358af5d5.291d56e2.js b/assets/js/358af5d5.291d56e2.js new file mode 100644 index 0000000000..ee8e796116 --- /dev/null +++ b/assets/js/358af5d5.291d56e2.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[89887],{53655:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/community-buzz/page/7","page":7,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/community-buzz/page/6","nextPage":"/Cloud-Native/30daysofIA/tags/community-buzz/page/8","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/358af5d5.f315e1eb.js b/assets/js/358af5d5.f315e1eb.js deleted file mode 100644 index 5c437d32a8..0000000000 --- a/assets/js/358af5d5.f315e1eb.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[89887],{53655:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/community-buzz/page/7","page":7,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/community-buzz/page/6","nextPage":"/Cloud-Native/30daysofIA/tags/community-buzz/page/8","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/367b1faa.1f016f0b.js b/assets/js/367b1faa.1f016f0b.js new file mode 100644 index 0000000000..2e53979e51 --- /dev/null +++ b/assets/js/367b1faa.1f016f0b.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[15405],{59123:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/community-buzz/page/8","page":8,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/community-buzz/page/7","nextPage":"/Cloud-Native/30daysofIA/tags/community-buzz/page/9","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/367b1faa.5032b946.js b/assets/js/367b1faa.5032b946.js deleted file mode 100644 index 1f49d96751..0000000000 --- a/assets/js/367b1faa.5032b946.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[15405],{59123:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/community-buzz/page/8","page":8,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/community-buzz/page/7","nextPage":"/Cloud-Native/30daysofIA/tags/community-buzz/page/9","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/36e282e0.2f1c327e.js b/assets/js/36e282e0.9737ed73.js similarity index 95% rename from assets/js/36e282e0.2f1c327e.js rename to assets/js/36e282e0.9737ed73.js index 66ac8aa374..ac8fc31ecb 100644 --- a/assets/js/36e282e0.2f1c327e.js +++ b/assets/js/36e282e0.9737ed73.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[98686],{96356:e=>{e.exports=JSON.parse('{"label":"azure-functions","permalink":"/Cloud-Native/30daysofIA/tags/azure-functions","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[98686],{96356:e=>{e.exports=JSON.parse('{"label":"azure-functions","permalink":"/Cloud-Native/30daysofIA/tags/azure-functions","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/36e9edc7.2c82a055.js b/assets/js/36e9edc7.2c82a055.js new file mode 100644 index 0000000000..b86cb89876 --- /dev/null +++ b/assets/js/36e9edc7.2c82a055.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[83200],{94803:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/github-codespaces/page/10","page":10,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/github-codespaces/page/9","nextPage":"/Cloud-Native/30daysofIA/tags/github-codespaces/page/11","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/38554ecb.f08d55bd.js b/assets/js/38554ecb.c19b45fa.js similarity index 72% rename from assets/js/38554ecb.f08d55bd.js rename to assets/js/38554ecb.c19b45fa.js index e9f3b8d138..8abdd3529c 100644 --- a/assets/js/38554ecb.f08d55bd.js +++ b/assets/js/38554ecb.c19b45fa.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[5168],{14248:e=>{e.exports=JSON.parse('{"label":"github-copilot","permalink":"/Cloud-Native/30daysofIA/tags/github-copilot","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[5168],{14248:e=>{e.exports=JSON.parse('{"label":"github-copilot","permalink":"/Cloud-Native/30daysofIA/tags/github-copilot","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/388c8f2b.7cfa4692.js b/assets/js/388c8f2b.7cfa4692.js new file mode 100644 index 0000000000..d3df3c0f07 --- /dev/null +++ b/assets/js/388c8f2b.7cfa4692.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[80538],{50125:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/learn-live/page/5","page":5,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/learn-live/page/4","nextPage":"/Cloud-Native/30daysofIA/tags/learn-live/page/6","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/388c8f2b.a450e800.js b/assets/js/388c8f2b.a450e800.js deleted file mode 100644 index 573e59e0c6..0000000000 --- a/assets/js/388c8f2b.a450e800.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[80538],{50125:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/learn-live/page/5","page":5,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/learn-live/page/4","nextPage":"/Cloud-Native/30daysofIA/tags/learn-live/page/6","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/39156339.e3ef9893.js b/assets/js/39156339.7de5e275.js similarity index 89% rename from assets/js/39156339.e3ef9893.js rename to assets/js/39156339.7de5e275.js index d580f6b10d..4338ef9f95 100644 --- a/assets/js/39156339.e3ef9893.js +++ b/assets/js/39156339.7de5e275.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[12099],{34475:e=>{e.exports=JSON.parse('{"label":"azure-kubernetes-service","permalink":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[12099],{34475:e=>{e.exports=JSON.parse('{"label":"azure-kubernetes-service","permalink":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/39200a92.792e3dd0.js b/assets/js/39200a92.792e3dd0.js deleted file mode 100644 index 2551fc0db2..0000000000 --- a/assets/js/39200a92.792e3dd0.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[53876],{31808:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/community-buzz","page":1,"postsPerPage":1,"totalPages":9,"totalCount":9,"nextPage":"/Cloud-Native/30daysofIA/tags/community-buzz/page/2","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/39200a92.f4fd3199.js b/assets/js/39200a92.f4fd3199.js new file mode 100644 index 0000000000..ed38bed8fb --- /dev/null +++ b/assets/js/39200a92.f4fd3199.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[53876],{31808:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/community-buzz","page":1,"postsPerPage":1,"totalPages":11,"totalCount":11,"nextPage":"/Cloud-Native/30daysofIA/tags/community-buzz/page/2","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/39792805.cbc408ac.js b/assets/js/39792805.4f73ade9.js similarity index 94% rename from assets/js/39792805.cbc408ac.js rename to assets/js/39792805.4f73ade9.js index c74dc97698..73a0fb8eb2 100644 --- a/assets/js/39792805.cbc408ac.js +++ b/assets/js/39792805.4f73ade9.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[94645],{97253:e=>{e.exports=JSON.parse('{"label":"github-codespaces","permalink":"/Cloud-Native/30daysofIA/tags/github-codespaces","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[94645],{97253:e=>{e.exports=JSON.parse('{"label":"github-codespaces","permalink":"/Cloud-Native/30daysofIA/tags/github-codespaces","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/3a1a5f0f.bdfcb3d3.js b/assets/js/3a1a5f0f.bdfcb3d3.js new file mode 100644 index 0000000000..a9ca41d13e --- /dev/null +++ b/assets/js/3a1a5f0f.bdfcb3d3.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[30306],{32675:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/fall-for-ia/page/6","page":6,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/fall-for-ia/page/5","nextPage":"/Cloud-Native/30daysofIA/tags/fall-for-ia/page/7","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/3a1a5f0f.da9bb3b3.js b/assets/js/3a1a5f0f.da9bb3b3.js deleted file mode 100644 index e398e5d728..0000000000 --- a/assets/js/3a1a5f0f.da9bb3b3.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[30306],{32675:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/fall-for-ia/page/6","page":6,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/fall-for-ia/page/5","nextPage":"/Cloud-Native/30daysofIA/tags/fall-for-ia/page/7","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/3a894f2b.54db83b7.js b/assets/js/3a894f2b.54db83b7.js new file mode 100644 index 0000000000..7a68615d43 --- /dev/null +++ b/assets/js/3a894f2b.54db83b7.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[90759],{94398:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/github-actions","page":1,"postsPerPage":1,"totalPages":11,"totalCount":11,"nextPage":"/Cloud-Native/30daysofIA/tags/github-actions/page/2","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/3a894f2b.8ae98511.js b/assets/js/3a894f2b.8ae98511.js deleted file mode 100644 index 1cc0106919..0000000000 --- a/assets/js/3a894f2b.8ae98511.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[90759],{94398:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/github-actions","page":1,"postsPerPage":1,"totalPages":9,"totalCount":9,"nextPage":"/Cloud-Native/30daysofIA/tags/github-actions/page/2","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/3ae3f3bb.1ef37446.js b/assets/js/3ae3f3bb.137796ec.js similarity index 73% rename from assets/js/3ae3f3bb.1ef37446.js rename to assets/js/3ae3f3bb.137796ec.js index 73fe3ba40c..46720f9968 100644 --- a/assets/js/3ae3f3bb.1ef37446.js +++ b/assets/js/3ae3f3bb.137796ec.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[67231],{88589:a=>{a.exports=JSON.parse('{"label":"30-days-of-IA","permalink":"/Cloud-Native/30daysofIA/tags/30-days-of-ia","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[67231],{88589:a=>{a.exports=JSON.parse('{"label":"30-days-of-IA","permalink":"/Cloud-Native/30daysofIA/tags/30-days-of-ia","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/3bbe6001.60143556.js b/assets/js/3bbe6001.60143556.js new file mode 100644 index 0000000000..e233261637 --- /dev/null +++ b/assets/js/3bbe6001.60143556.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[27222],{45549:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/github-copilot/page/4","page":4,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/github-copilot/page/3","nextPage":"/Cloud-Native/30daysofIA/tags/github-copilot/page/5","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/3bbe6001.935c8b71.js b/assets/js/3bbe6001.935c8b71.js deleted file mode 100644 index ebbd73086c..0000000000 --- a/assets/js/3bbe6001.935c8b71.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[27222],{45549:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/github-copilot/page/4","page":4,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/github-copilot/page/3","nextPage":"/Cloud-Native/30daysofIA/tags/github-copilot/page/5","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/3bfb947c.79788038.js b/assets/js/3bfb947c.79788038.js new file mode 100644 index 0000000000..36b46fbf8e --- /dev/null +++ b/assets/js/3bfb947c.79788038.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[4707],{92449:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service/page/3","page":3,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service/page/2","nextPage":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service/page/4","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/3bfb947c.b5293a1c.js b/assets/js/3bfb947c.b5293a1c.js deleted file mode 100644 index 458eff7690..0000000000 --- a/assets/js/3bfb947c.b5293a1c.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[4707],{92449:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service/page/3","page":3,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service/page/2","nextPage":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service/page/4","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/3ccca367.66782563.js b/assets/js/3ccca367.66782563.js new file mode 100644 index 0000000000..c1f8faa966 --- /dev/null +++ b/assets/js/3ccca367.66782563.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[93327],{97412:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/community-buzz/page/5","page":5,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/community-buzz/page/4","nextPage":"/Cloud-Native/30daysofIA/tags/community-buzz/page/6","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/3ccca367.6ff99c10.js b/assets/js/3ccca367.6ff99c10.js deleted file mode 100644 index a12f9aac41..0000000000 --- a/assets/js/3ccca367.6ff99c10.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[93327],{97412:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/community-buzz/page/5","page":5,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/community-buzz/page/4","nextPage":"/Cloud-Native/30daysofIA/tags/community-buzz/page/6","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/3e04afb2.1426ee55.js b/assets/js/3e04afb2.1426ee55.js deleted file mode 100644 index a3b0ce37bf..0000000000 --- a/assets/js/3e04afb2.1426ee55.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[59322],{65242:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/hack-together/page/6","page":6,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/hack-together/page/5","nextPage":"/Cloud-Native/30daysofIA/tags/hack-together/page/7","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/3e04afb2.7216156e.js b/assets/js/3e04afb2.7216156e.js new file mode 100644 index 0000000000..a84f650325 --- /dev/null +++ b/assets/js/3e04afb2.7216156e.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[59322],{65242:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/hack-together/page/6","page":6,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/hack-together/page/5","nextPage":"/Cloud-Native/30daysofIA/tags/hack-together/page/7","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/3e675a42.127a98c0.js b/assets/js/3e675a42.127a98c0.js new file mode 100644 index 0000000000..8cbc9b6847 --- /dev/null +++ b/assets/js/3e675a42.127a98c0.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[96789],{85028:e=>{e.exports=JSON.parse('{"label":"ask-the-expert","permalink":"/Cloud-Native/30daysofIA/tags/ask-the-expert","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/3f09b1d2.945667d7.js b/assets/js/3f09b1d2.5981be58.js similarity index 73% rename from assets/js/3f09b1d2.945667d7.js rename to assets/js/3f09b1d2.5981be58.js index b2b651401b..53433f1db7 100644 --- a/assets/js/3f09b1d2.945667d7.js +++ b/assets/js/3f09b1d2.5981be58.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[52911],{79217:e=>{e.exports=JSON.parse('{"label":"azure-openai","permalink":"/Cloud-Native/30daysofIA/tags/azure-openai","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[52911],{79217:e=>{e.exports=JSON.parse('{"label":"azure-openai","permalink":"/Cloud-Native/30daysofIA/tags/azure-openai","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/3f5f9a5a.054d6ab3.js b/assets/js/3f5f9a5a.59c1cdeb.js similarity index 92% rename from assets/js/3f5f9a5a.054d6ab3.js rename to assets/js/3f5f9a5a.59c1cdeb.js index b567be1e57..975570f166 100644 --- a/assets/js/3f5f9a5a.054d6ab3.js +++ b/assets/js/3f5f9a5a.59c1cdeb.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[51128],{18574:a=>{a.exports=JSON.parse('{"label":"azure-container-apps","permalink":"/Cloud-Native/30daysofIA/tags/azure-container-apps","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[51128],{18574:a=>{a.exports=JSON.parse('{"label":"azure-container-apps","permalink":"/Cloud-Native/30daysofIA/tags/azure-container-apps","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/411bca4b.58d1962b.js b/assets/js/411bca4b.58d1962b.js new file mode 100644 index 0000000000..6acab49e4c --- /dev/null +++ b/assets/js/411bca4b.58d1962b.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[47778],{47345:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service/page/11","page":11,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service/page/10","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/42421040.47ac63e8.js b/assets/js/42421040.47ac63e8.js new file mode 100644 index 0000000000..7dfd5936c8 --- /dev/null +++ b/assets/js/42421040.47ac63e8.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[15540],{42223:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service/page/6","page":6,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service/page/5","nextPage":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service/page/7","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/42421040.72f2a6f9.js b/assets/js/42421040.72f2a6f9.js deleted file mode 100644 index b5b0d22d1c..0000000000 --- a/assets/js/42421040.72f2a6f9.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[15540],{42223:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service/page/6","page":6,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service/page/5","nextPage":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service/page/7","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/43949d84.c34a8a35.js b/assets/js/43949d84.c34a8a35.js new file mode 100644 index 0000000000..115457ba27 --- /dev/null +++ b/assets/js/43949d84.c34a8a35.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[76771],{91693:e=>{e.exports=JSON.parse('{"label":"hack-together","permalink":"/Cloud-Native/30daysofIA/tags/hack-together","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/460574f3.e2fea3ea.js b/assets/js/460574f3.e2fea3ea.js new file mode 100644 index 0000000000..3045e6ccbc --- /dev/null +++ b/assets/js/460574f3.e2fea3ea.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[97873],{45108:a=>{a.exports=JSON.parse('{"label":"Fall-For-IA","permalink":"/Cloud-Native/30daysofIA/tags/fall-for-ia","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/48a6e2e6.09239c67.js b/assets/js/48a6e2e6.09239c67.js new file mode 100644 index 0000000000..9c4cc02914 --- /dev/null +++ b/assets/js/48a6e2e6.09239c67.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[37903],{3905:(e,t,a)=>{a.d(t,{Zo:()=>c,kt:()=>g});var i=a(67294);function n(e,t,a){return t in e?Object.defineProperty(e,t,{value:a,enumerable:!0,configurable:!0,writable:!0}):e[t]=a,e}function r(e,t){var a=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);t&&(i=i.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),a.push.apply(a,i)}return a}function o(e){for(var t=1;t=0||(n[a]=e[a]);return n}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(i=0;i=0||Object.prototype.propertyIsEnumerable.call(e,a)&&(n[a]=e[a])}return n}var l=i.createContext({}),p=function(e){var t=i.useContext(l),a=t;return e&&(a="function"==typeof e?e(t):o(o({},t),e)),a},c=function(e){var t=p(e.components);return i.createElement(l.Provider,{value:t},e.children)},u={inlineCode:"code",wrapper:function(e){var t=e.children;return i.createElement(i.Fragment,{},t)}},d=i.forwardRef((function(e,t){var a=e.components,n=e.mdxType,r=e.originalType,l=e.parentName,c=s(e,["components","mdxType","originalType","parentName"]),d=p(a),g=n,m=d["".concat(l,".").concat(g)]||d[g]||u[g]||r;return a?i.createElement(m,o(o({ref:t},c),{},{components:a})):i.createElement(m,o({ref:t},c))}));function g(e,t){var a=arguments,n=t&&t.mdxType;if("string"==typeof e||n){var r=a.length,o=new Array(r);o[0]=d;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s.mdxType="string"==typeof e?e:n,o[1]=s;for(var p=2;p{a.r(t),a.d(t,{assets:()=>l,contentTitle:()=>o,default:()=>u,frontMatter:()=>r,metadata:()=>s,toc:()=>p});var i=a(87462),n=(a(67294),a(3905));const r={date:"2023-09-22T09:00",slug:"preparing-the-path-for-intelligent-apps",title:"1-5. Preparing the Path for Intelligent Apps",authors:["cnteam"],draft:!1,hide_table_of_contents:!1,toc_min_heading_level:2,toc_max_heading_level:3,keywords:["Cloud","Data","AI","AI/ML","intelligent apps","cloud-native","30-days","enterprise apps","digital experiences","app modernization","serverless","ai apps","data"],image:"https://azure.github.io/Cloud-Native/img/ogImage.png",description:"Learn best practices for modernizing your on-premises or IaaS solutions to intelligent apps in the cloud.",tags:["Fall-For-IA","30-days-of-IA","learn-live","hack-together","community-buzz","ask-the-expert","azure-kubernetes-service","azure-functions","azure-openai","azure-container-apps","azure-cosmos-db","github-copilot","github-codespaces","github-actions"]},o=void 0,s={permalink:"/Cloud-Native/30daysofIA/preparing-the-path-for-intelligent-apps",source:"@site/blog-30daysofIA/2023-09-22/preparing-the-path-for-intelligent-apps.md",title:"1-5. Preparing the Path for Intelligent Apps",description:"Learn best practices for modernizing your on-premises or IaaS solutions to intelligent apps in the cloud.",date:"2023-09-22T09:00:00.000Z",formattedDate:"September 22, 2023",tags:[{label:"Fall-For-IA",permalink:"/Cloud-Native/30daysofIA/tags/fall-for-ia"},{label:"30-days-of-IA",permalink:"/Cloud-Native/30daysofIA/tags/30-days-of-ia"},{label:"learn-live",permalink:"/Cloud-Native/30daysofIA/tags/learn-live"},{label:"hack-together",permalink:"/Cloud-Native/30daysofIA/tags/hack-together"},{label:"community-buzz",permalink:"/Cloud-Native/30daysofIA/tags/community-buzz"},{label:"ask-the-expert",permalink:"/Cloud-Native/30daysofIA/tags/ask-the-expert"},{label:"azure-kubernetes-service",permalink:"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service"},{label:"azure-functions",permalink:"/Cloud-Native/30daysofIA/tags/azure-functions"},{label:"azure-openai",permalink:"/Cloud-Native/30daysofIA/tags/azure-openai"},{label:"azure-container-apps",permalink:"/Cloud-Native/30daysofIA/tags/azure-container-apps"},{label:"azure-cosmos-db",permalink:"/Cloud-Native/30daysofIA/tags/azure-cosmos-db"},{label:"github-copilot",permalink:"/Cloud-Native/30daysofIA/tags/github-copilot"},{label:"github-codespaces",permalink:"/Cloud-Native/30daysofIA/tags/github-codespaces"},{label:"github-actions",permalink:"/Cloud-Native/30daysofIA/tags/github-actions"}],readingTime:8.59,hasTruncateMarker:!1,authors:[{name:"It's 30DaysOfIA",title:"FallForIA Content Team",url:"https://azure.github.io/Cloud-Native/Fall-For-IA/",imageURL:"https://azure.github.io/Cloud-Native/img/logo-ms-cloud-native.png",key:"cnteam"}],frontMatter:{date:"2023-09-22T09:00",slug:"preparing-the-path-for-intelligent-apps",title:"1-5. Preparing the Path for Intelligent Apps",authors:["cnteam"],draft:!1,hide_table_of_contents:!1,toc_min_heading_level:2,toc_max_heading_level:3,keywords:["Cloud","Data","AI","AI/ML","intelligent apps","cloud-native","30-days","enterprise apps","digital experiences","app modernization","serverless","ai apps","data"],image:"https://azure.github.io/Cloud-Native/img/ogImage.png",description:"Learn best practices for modernizing your on-premises or IaaS solutions to intelligent apps in the cloud.",tags:["Fall-For-IA","30-days-of-IA","learn-live","hack-together","community-buzz","ask-the-expert","azure-kubernetes-service","azure-functions","azure-openai","azure-container-apps","azure-cosmos-db","github-copilot","github-codespaces","github-actions"]},prevItem:{title:"1-6. Cultivating a Culture for Intelligent Apps",permalink:"/Cloud-Native/30daysofIA/cultivating-a-culture-for-intelligent-apps"},nextItem:{title:"2-1. Build Your First Intelligent App with Azure AI and AKS-1",permalink:"/Cloud-Native/30daysofIA/build-your-first-intelligent-app-with-azure-ai-and-aks-1"}},l={authorsImageUrls:[void 0]},p=[{value:"What We'll Cover:",id:"what-well-cover",level:2},{value:"Preparing the Path for Intelligent Apps: Transitioning from On-Premises/IaaS to Cloud-Native Applications",id:"preparing-the-path-for-intelligent-apps-transitioning-from-on-premisesiaas-to-cloud-native-applications",level:2},{value:"Understanding the Shift: Traditional vs. Intelligent Applications",id:"understanding-the-shift-traditional-vs-intelligent-applications",level:2},{value:"Functionality and User Experience",id:"functionality-and-user-experience",level:3},{value:"Infrastructure",id:"infrastructure",level:3},{value:"On-Premises/IaaS vs. Cloud-Native Platforms",id:"on-premisesiaas-vs-cloud-native-platforms",level:3},{value:"Strategic Considerations for Transitioning to Intelligent Apps",id:"strategic-considerations-for-transitioning-to-intelligent-apps",level:3},{value:"Re-Architecting Monolithic Apps into Microservices",id:"re-architecting-monolithic-apps-into-microservices",level:3},{value:"Event-Driven Architectures: Knowing When to Use Them",id:"event-driven-architectures-knowing-when-to-use-them",level:3},{value:"Conclusion",id:"conclusion",level:2},{value:"Exercise",id:"exercise",level:2}],c={toc:p};function u(e){let{components:t,...r}=e;return(0,n.kt)("wrapper",(0,i.Z)({},c,r,{components:t,mdxType:"MDXLayout"}),(0,n.kt)("head",null,(0,n.kt)("meta",{property:"og:url",content:"https://azure.github.io/cloud-native/30daysofia/how-digital-natives-leverage-generative-ai"}),(0,n.kt)("meta",{property:"og:type",content:"website"}),(0,n.kt)("meta",{property:"og:title",content:"**Fall For Intelligent Apps! \ud83c\udf42| Build AI Apps On Azure"}),(0,n.kt)("meta",{property:"og:description",content:"Explore how digital natives and Independent Software Vendors (ISVs) can transform their business operations and generate higher value for customers with generative AI and intelligent apps."}),(0,n.kt)("meta",{property:"og:image",content:"https://azure.github.io/Cloud-Native/img/ogImage.png"}),(0,n.kt)("meta",{name:"twitter:url",content:"https://azure.github.io/Cloud-Native/30daysofIA/preparing-the-path-for-intelligent-apps"}),(0,n.kt)("meta",{name:"twitter:title",content:"**Fall For Intelligent Apps! \ud83c\udf42 | Build AI Apps On Azure"}),(0,n.kt)("meta",{name:"twitter:description",content:"1-5. Learn best practices for modernizing your on-premises or IaaS solutions to intelligent apps in the cloud."}),(0,n.kt)("meta",{name:"twitter:image",content:"https://azure.github.io/Cloud-Native/img/ogImage.png"}),(0,n.kt)("meta",{name:"twitter:card",content:"summary_large_image"}),(0,n.kt)("meta",{name:"twitter:creator",content:"@devanshidiaries"}),(0,n.kt)("meta",{name:"twitter:site",content:"@AzureAdvocates"}),(0,n.kt)("link",{rel:"canonical",href:"https://azure.github.io/Cloud-Native/30daysofIA/preparing-the-path-for-intelligent-apps"})),(0,n.kt)("p",null,"Learn best practices for modernizing your on-premises or IaaS solutions to intelligent apps in the cloud."),(0,n.kt)("h2",{id:"what-well-cover"},"What We'll Cover:"),(0,n.kt)("ul",null,(0,n.kt)("li",{parentName:"ul"},"Traditional vs Intelligent Apps"),(0,n.kt)("li",{parentName:"ul"},"On-Premises/IaaS vs. Cloud-Native Platforms"),(0,n.kt)("li",{parentName:"ul"},"Modernization strategy for building Intelligent Apps")),(0,n.kt)("p",null,(0,n.kt)("img",{alt:"image of modernizing AI solutions for intelligent apps",src:a(22352).Z,width:"624",height:"380"})),(0,n.kt)("h2",{id:"preparing-the-path-for-intelligent-apps-transitioning-from-on-premisesiaas-to-cloud-native-applications"},"Preparing the Path for Intelligent Apps: Transitioning from On-Premises/IaaS to Cloud-Native Applications"),(0,n.kt)("p",null,"The proliferation of generative AI is paving the way for intelligent applications, software that leverages AI capabilities to deliver unparalleled functionality and user experience. As we covered in a previous article, \u201c",(0,n.kt)("a",{parentName:"p",href:"https://azure.github.io/Cloud-Native/30daysofIA/demystifying-intelligent-applications"},"Demystifying Intelligent Applications: Leveraging AI in App Development"),",\u201d intelligent Apps aren\u2019t just products of advanced coding. They interact, learn, and evolve thanks to modern AI and machine learning (ML) breakthroughs.\u202f "),(0,n.kt)("p",null,"The paradigm shift from traditional to intelligent apps demands that we change our approach to the technical challenges of software and architecture design. It also necessitates adopting new ways of thinking and operating within an organization, which we explore in greater depth in \u201c",(0,n.kt)("a",{parentName:"p",href:"https://azure.github.io/Cloud-Native/30daysofIA/cultivating-a-culture-for-intelligent-apps"},"Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management"),".\u201d\u202f "),(0,n.kt)("p",null,"This article explores the technical infrastructure powering intelligent apps, providing a roadmap to transition traditional on-premises or Infrastructure as a Service (IaaS) solutions to intelligent apps deployed to cloud native platforms and services. We\u2019ll discover the differences between conventional and intelligent apps, options for re-architecting existing applications, and critical strategic considerations integral to modernizing the application-building approach.\u202f "),(0,n.kt)("h2",{id:"understanding-the-shift-traditional-vs-intelligent-applications"},"Understanding the Shift: Traditional vs. Intelligent Applications"),(0,n.kt)("p",null,"Intelligent apps are more than just an incremental evolution from traditional apps. They involve a fundamental shift in software solution design, development, and deployment. A clear understanding of how each paradigm differs is crucial for comprehending the impact of this transition.\u202f "),(0,n.kt)("h3",{id:"functionality-and-user-experience"},"Functionality and User Experience"),(0,n.kt)("p",null,"Traditional applications, often rule-based and rigid, rely on preprogrammed operations. A traditional weather app, for example, typically presents structured, location-based forecasts using a predetermined data source. Meanwhile, an intelligent app leverages AI for data-driven decision-making and personalization. Rather than merely displaying the forecast, it integrates ML with additional data sources\u2014like a user\u2019s calendar and fitness data\u2014to determine their favorite outdoor activities, provide relevant clothing suggestions, and even automatically design ideal vacations and times based on the user\u2019s optimal weather preferences.\u202f "),(0,n.kt)("p",null,"Similarly, while traditional applications tend to offer a one-size-fits-all UI, intelligent apps use AI to create tailored interactions. For instance, an intelligent banking app might use generative AI to provide a voice-enabled UI that lets customers use natural language to ask about transactions and receive personalized financial advice."),(0,n.kt)("h3",{id:"infrastructure"},"Infrastructure"),(0,n.kt)("p",null,"Intelligent apps integrating these sophisticated features require highly scalable, flexible infrastructure optimized for AI functionality and able to manage large volumes of real-time data. Unlike traditional applications deployed on-premises or in infrastructure as a service (IaaS) environments, intelligent apps demand the flexibility and scalability of cloud native architectures."),(0,n.kt)("p",null,"An on-premises or IaaS-based e-commerce application may struggle with connecting its data to AI services or scaling to accommodate voluminous real-time data and requests. But an intelligent app built with a cloud native approach and scalability at its core can handle variable demand much more efficiently. It also maintains its infrastructure components on demand, whether for collecting data for AI/ML consumption or user-facing features like AI-enabled customer support and personalization."),(0,n.kt)("p",null,"Consider an intelligent app built on Azure that creates ChatGPT-like experiences with custom data. It might connect ",(0,n.kt)("a",{parentName:"p",href:"https://azure.microsoft.com/en-us/products/ai-services/openai-service?WT.mc_id=javascript-99907-ninarasi"},"Azure Open AI")," and ",(0,n.kt)("a",{parentName:"p",href:"https://azure.microsoft.com/en-us/products/ai-services/cognitive-search?WT.mc_id=javascript-99907-ninarasi"},"Azure Cognitive Search")," to an app running on ",(0,n.kt)("a",{parentName:"p",href:"https://azure.microsoft.com/en-us/products/container-apps/?WT.mc_id=javascript-99907-ninarasi"},"Azure Container Apps")," or ",(0,n.kt)("a",{parentName:"p",href:"https://azure.microsoft.com/en-us/products/kubernetes-service/?WT.mc_id=javascript-99907-ninarasi"},"Azure Kubernetes Service"),".\u202f "),(0,n.kt)("p",null,"The architecture might look like the following diagram based on this ",(0,n.kt)("a",{parentName:"p",href:"https://github.com/Azure-Samples/azure-search-openai-demo-csharp"},"demo app"),":"),(0,n.kt)("p",null,(0,n.kt)("img",{alt:"diagram of a demo app architecture",src:a(48199).Z,width:"624",height:"386"})),(0,n.kt)("p",null,"While the cloud is well-equipped to handle the challenges associated with shifting to AI-centric infrastructure\u2014handling real-time data processing and storage, integrating and managing AI services, configuring event-driven and microservices architectures, and ensuring security across the distributed environment\u2014re-architecting a traditional app to an intelligent app requires careful planning and execution to ensure it benefits fully from the capabilities of AI and cloud computing."),(0,n.kt)("h3",{id:"on-premisesiaas-vs-cloud-native-platforms"},"On-Premises/IaaS vs. Cloud-Native Platforms"),(0,n.kt)("p",null,"While on-premises and IaaS solutions provide substantial control over data and processes, this fine-grained control comes with setup and maintenance costs and may lack the scalability necessary for AI-driven Intelligent Apps. On-premises hosting involves purchasing and maintaining hardware, including servers and storage systems. IaaS involves leasing virtual hardware and then hiring and training IT personnel for the infrastructure\u2019s operation, maintenance, and security."),(0,n.kt)("p",null,"Conversely, infrastructure built on cloud native app services with ",(0,n.kt)("a",{parentName:"p",href:"https://azure.microsoft.com/?WT.mc_id=javascript-99907-ninarasi"},"Microsoft Azure")," offers virtually unlimited scalability without ongoing hardware and maintenance costs. This feature is ideal for intelligent apps that need to adapt and scale to accommodate fluctuating workloads and data streams. And because these cloud infrastructures typically operate on a pay-as-you-go model, traditionally substantial upfront costs turn into predictable operating expenses, lowering both the cost and risk of launching applications."),(0,n.kt)("p",null,"Moreover, cloud service providers continually update their offerings, so building on sophisticated AI tools and services through the cloud, like the ",(0,n.kt)("a",{parentName:"p",href:"https://azure.microsoft.com/en-us/solutions/ai/?WT.mc_id=javascript-99907-ninarasi"},"Azure AI Platform")," and orchestration capabilities with ",(0,n.kt)("a",{parentName:"p",href:"https://azure.microsoft.com/en-us/products/kubernetes-service/?WT.mc_id=javascript-99907-ninarasi"},"Azure Kubernetes Service"),", ensures that you have access to the latest AI and most advanced AI models and technologies."),(0,n.kt)("h3",{id:"strategic-considerations-for-transitioning-to-intelligent-apps"},"Strategic Considerations for Transitioning to Intelligent Apps"),(0,n.kt)("p",null,"When developing a strategy for transitioning to intelligent apps, you must consider costs, personnel skill development, and data management."),(0,n.kt)("p",null,"Monolithic architectures are unlikely to support the scale and ease of iteration required by intelligent apps, so you\u2019ll likely need to implement code changes to support the transition. Your chosen hosting solution and the amount of data your app will handle affect cost and performance, in addition to determining privacy and compliance requirements."),(0,n.kt)("p",null,"An app that manages or stores terabytes or petabytes of data will require storage-optimized virtual machines or a managed database service hosting. Meanwhile, an app that processes video content or performs resource-intensive calculations should prioritize high-performance computing. Maximizing storage and performance can become extraordinarily costly."),(0,n.kt)("p",null,"Finally, existing applications shifting towards intelligent apps may encounter compatibility conflicts, depending on database type or the existing system\u2019s underlying programming language. As you might imagine, a traditional app running on-premises and using a shared local database may need significant restructuring to support a cloud native architecture. You need plans for data migration, potential downtime, and training."),(0,n.kt)("p",null,"Next, let\u2019s look at this transformation process."),(0,n.kt)("h3",{id:"re-architecting-monolithic-apps-into-microservices"},"Re-Architecting Monolithic Apps into Microservices"),(0,n.kt)("p",null,"Shifting from ",(0,n.kt)("a",{parentName:"p",href:"https://learn.microsoft.com/en-us/dotnet/architecture/containerized-lifecycle/design-develop-containerized-apps/monolithic-applications?WT.mc_id=javascript-99907-ninarasi"},"monolithic applications")," to microservices is pivotal for transitioning to Intelligent Apps, and you can use this shift as an opportunity to transform your legacy software into intelligent apps that leverage the flexibility and power of microservices-enabled AI solutions."),(0,n.kt)("p",null,"Traditionally, a monolithic application (let\u2019s use an online marketplace as an example) might handle all activities\u2014from serving webpages to processing payments\u2014within an on-premises server or a cloud server. However, this approach is challenging to scale and remains vulnerable to network failures or cyberattacks."),(0,n.kt)("p",null,"Breaking these applications into multiple services with specific responsibilities, such as authentication or notifications, can overcome these limitations. These services are typically deployed as microservices within container environments like ",(0,n.kt)("a",{parentName:"p",href:"https://azure.microsoft.com/en-us/products/kubernetes-service/?WT.mc_id=javascript-99907-ninarasi"},"Azure Kubernetes Service")," or serverless platforms like ",(0,n.kt)("a",{parentName:"p",href:"https://azure.microsoft.com/en-us/products/container-apps?WT.mc_id=javascript-99907-ninarasi"},"Azure Container Apps")," or ",(0,n.kt)("a",{parentName:"p",href:"https://azure.microsoft.com/en-us/products/functions/?WT.mc_id=javascript-99907-ninarasi"},"Azure Functions")," using automated processes. This approach offers scalability, robustness, and improved performance, as each microservice can run independently and use its own database."),(0,n.kt)("p",null,"Shifting towards smaller, independent microservices in app architectures allows us to reap the benefits of AI-powered intelligent apps, for example, by using microservices to handle AI tasks such as processing natural language input, analyzing behavioral data, or personalizing content."),(0,n.kt)("admonition",{type:"info"},(0,n.kt)("p",{parentName:"admonition"},"Watch ",(0,n.kt)("a",{parentName:"p",href:"https://aka.ms/learnlive-contoso-app-deconstructed-Ep1"},"Episode 1")," and ",(0,n.kt)("a",{parentName:"p",href:"https://aka.ms/learnlive-contoso-app-deconstructed-Ep2"},"Episode 2")," of the ",(0,n.kt)("a",{parentName:"p",href:"https://aka.ms/contoso-real-estate/learn-live"},"Learn Live series")," on how to build, test and deploy an end-to-end intelligent app solution using the ",(0,n.kt)("a",{parentName:"p",href:"https://github.com/Azure-Samples/contoso-real-estate"},"Contoso Real Estate Sample"),".")),(0,n.kt)("h3",{id:"event-driven-architectures-knowing-when-to-use-them"},"Event-Driven Architectures: Knowing When to Use Them"),(0,n.kt)("p",null,(0,n.kt)("a",{parentName:"p",href:"https://learn.microsoft.com/en-us/azure/architecture/guide/architecture-styles/event-driven?WT.mc_id=javascript-99907-ninarasi"},"Event-Driven Architectures")," (EDAs) can significantly enhance the performance and cost-effectiveness of intelligent apps. EDAs, typically implemented with microservices, respond in real time to events or state changes ranging from user interactions to real-time analytics."),(0,n.kt)("p",null,"Consider an intelligent app designed for personalized customer engagement. Instead of designing the application to check continuously for changes in user data, an EDA can react to such changes immediately, triggering appropriate microservices. For example, if users modify their preferences, an event could trigger an AI-driven recommendation microservice to update its suggestions immediately. This real-time responsiveness, enabled by services like ",(0,n.kt)("a",{parentName:"p",href:"https://azure.microsoft.com/en-us/products/event-grid/?WT.mc_id=javascript-99907-ninarasi"},"Azure Event Grid"),", can improve user experiences and make intelligent apps more adaptive and proactive."),(0,n.kt)("p",null,"However, it\u2019s worth noting that while EDAs provide significant benefits, they can introduce unique complexities, such as maintaining event consistency and debugging issues in event chains across multiple microservices. Therefore, it\u2019s important to assess your application\u2019s needs so you can understand where it can benefit from an EDA."),(0,n.kt)("h2",{id:"conclusion"},"Conclusion"),(0,n.kt)("p",null,"Transitioning to intelligent apps represents a shift in strategy, architecture, and infrastructure. By designing and re-architecting for cloud-based infrastructure, your apps gain scalability, cost-effectiveness, robust data management, access to sophisticated AI tools, enhanced security, and reduced operational overhead.\u202f\u202f "),(0,n.kt)("p",null,"But this transition extends well beyond its technical components. Organizations and the people working within them need equal attention. You can explore these facets in greater depth in \u201cCultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management.\u201d\u202f "),(0,n.kt)("p",null,"The power of generative AI is here, and preparing for the move toward cloud-based intelligent applications will ensure that your apps remain performative and scalable and\u2014most importantly\u2014that your business is ready for the newest era of app innovation."),(0,n.kt)("h2",{id:"exercise"},"Exercise"),(0,n.kt)("ul",null,(0,n.kt)("li",{parentName:"ul"},"Complete the ",(0,n.kt)("strong",{parentName:"li"},(0,n.kt)("a",{parentName:"strong",href:"https://aka.ms/fallforIA/apps-csc"},"Apps Cloud Skills Challenge"))," to build on your app dev and AI skills."),(0,n.kt)("li",{parentName:"ul"},"Complete the ",(0,n.kt)("strong",{parentName:"li"},(0,n.kt)("a",{parentName:"strong",href:"https://aka.ms/fallforIA/ai-csc"},"AI Cloud Skills Challenge"))," to build on your AI skills."),(0,n.kt)("li",{parentName:"ul"},"Register for ",(0,n.kt)("strong",{parentName:"li"},(0,n.kt)("a",{parentName:"strong",href:"https://aka.ms/learnlive-contoso-app-deconstructed-Ep3"},"Episode 03"))," of the serverless edition Learn Live session to learn how to build, test and deploy an end-to-end intelligent app solution."),(0,n.kt)("li",{parentName:"ul"},"Register for ",(0,n.kt)("strong",{parentName:"li"},(0,n.kt)("a",{parentName:"strong",href:"https://reactor.microsoft.com/en-us/reactor/series/S-1037/"},"Ask the Expert: Azure Functions"))," session for live Q&A with the Product Engineering team on building intelligent serverless apps.")))}u.isMDXComponent=!0},22352:(e,t,a)=>{a.d(t,{Z:()=>i});const i=a.p+"assets/images/blog-image-1-5-8ad85f98f250a51ea0ccadefbdcf2237.png"},48199:(e,t,a)=>{a.d(t,{Z:()=>i});const i=a.p+"assets/images/diagram-of-a-demo-app-architecture-3451b86065584b95e8896c995070eeec.png"}}]); \ No newline at end of file diff --git a/assets/js/48a6e2e6.0a891306.js b/assets/js/48a6e2e6.0a891306.js deleted file mode 100644 index b5f981d56d..0000000000 --- a/assets/js/48a6e2e6.0a891306.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[37903],{3905:(e,t,a)=>{a.d(t,{Zo:()=>c,kt:()=>g});var i=a(67294);function n(e,t,a){return t in e?Object.defineProperty(e,t,{value:a,enumerable:!0,configurable:!0,writable:!0}):e[t]=a,e}function r(e,t){var a=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);t&&(i=i.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),a.push.apply(a,i)}return a}function o(e){for(var t=1;t=0||(n[a]=e[a]);return n}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(i=0;i=0||Object.prototype.propertyIsEnumerable.call(e,a)&&(n[a]=e[a])}return n}var l=i.createContext({}),p=function(e){var t=i.useContext(l),a=t;return e&&(a="function"==typeof e?e(t):o(o({},t),e)),a},c=function(e){var t=p(e.components);return i.createElement(l.Provider,{value:t},e.children)},u={inlineCode:"code",wrapper:function(e){var t=e.children;return i.createElement(i.Fragment,{},t)}},d=i.forwardRef((function(e,t){var a=e.components,n=e.mdxType,r=e.originalType,l=e.parentName,c=s(e,["components","mdxType","originalType","parentName"]),d=p(a),g=n,m=d["".concat(l,".").concat(g)]||d[g]||u[g]||r;return a?i.createElement(m,o(o({ref:t},c),{},{components:a})):i.createElement(m,o({ref:t},c))}));function g(e,t){var a=arguments,n=t&&t.mdxType;if("string"==typeof e||n){var r=a.length,o=new Array(r);o[0]=d;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s.mdxType="string"==typeof e?e:n,o[1]=s;for(var p=2;p{a.r(t),a.d(t,{assets:()=>l,contentTitle:()=>o,default:()=>u,frontMatter:()=>r,metadata:()=>s,toc:()=>p});var i=a(87462),n=(a(67294),a(3905));const r={date:"2023-09-22T09:00",slug:"preparing-the-path-for-intelligent-apps",title:"1-5. Preparing the Path for Intelligent Apps",authors:["cnteam"],draft:!1,hide_table_of_contents:!1,toc_min_heading_level:2,toc_max_heading_level:3,keywords:["Cloud","Data","AI","AI/ML","intelligent apps","cloud-native","30-days","enterprise apps","digital experiences","app modernization","serverless","ai apps","data"],image:"https://azure.github.io/Cloud-Native/img/ogImage.png",description:"Learn best practices for modernizing your on-premises or IaaS solutions to intelligent apps in the cloud.",tags:["Fall-For-IA","30-days-of-IA","learn-live","hack-together","community-buzz","ask-the-expert","azure-kubernetes-service","azure-functions","azure-openai","azure-container-apps","azure-cosmos-db","github-copilot","github-codespaces","github-actions"]},o=void 0,s={permalink:"/Cloud-Native/30daysofIA/preparing-the-path-for-intelligent-apps",source:"@site/blog-30daysofIA/2023-09-22/preparing-the-path-for-intelligent-apps.md",title:"1-5. Preparing the Path for Intelligent Apps",description:"Learn best practices for modernizing your on-premises or IaaS solutions to intelligent apps in the cloud.",date:"2023-09-22T09:00:00.000Z",formattedDate:"September 22, 2023",tags:[{label:"Fall-For-IA",permalink:"/Cloud-Native/30daysofIA/tags/fall-for-ia"},{label:"30-days-of-IA",permalink:"/Cloud-Native/30daysofIA/tags/30-days-of-ia"},{label:"learn-live",permalink:"/Cloud-Native/30daysofIA/tags/learn-live"},{label:"hack-together",permalink:"/Cloud-Native/30daysofIA/tags/hack-together"},{label:"community-buzz",permalink:"/Cloud-Native/30daysofIA/tags/community-buzz"},{label:"ask-the-expert",permalink:"/Cloud-Native/30daysofIA/tags/ask-the-expert"},{label:"azure-kubernetes-service",permalink:"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service"},{label:"azure-functions",permalink:"/Cloud-Native/30daysofIA/tags/azure-functions"},{label:"azure-openai",permalink:"/Cloud-Native/30daysofIA/tags/azure-openai"},{label:"azure-container-apps",permalink:"/Cloud-Native/30daysofIA/tags/azure-container-apps"},{label:"azure-cosmos-db",permalink:"/Cloud-Native/30daysofIA/tags/azure-cosmos-db"},{label:"github-copilot",permalink:"/Cloud-Native/30daysofIA/tags/github-copilot"},{label:"github-codespaces",permalink:"/Cloud-Native/30daysofIA/tags/github-codespaces"},{label:"github-actions",permalink:"/Cloud-Native/30daysofIA/tags/github-actions"}],readingTime:8.59,hasTruncateMarker:!1,authors:[{name:"It's 30DaysOfIA",title:"FallForIA Content Team",url:"https://azure.github.io/Cloud-Native/Fall-For-IA/",imageURL:"https://azure.github.io/Cloud-Native/img/logo-ms-cloud-native.png",key:"cnteam"}],frontMatter:{date:"2023-09-22T09:00",slug:"preparing-the-path-for-intelligent-apps",title:"1-5. Preparing the Path for Intelligent Apps",authors:["cnteam"],draft:!1,hide_table_of_contents:!1,toc_min_heading_level:2,toc_max_heading_level:3,keywords:["Cloud","Data","AI","AI/ML","intelligent apps","cloud-native","30-days","enterprise apps","digital experiences","app modernization","serverless","ai apps","data"],image:"https://azure.github.io/Cloud-Native/img/ogImage.png",description:"Learn best practices for modernizing your on-premises or IaaS solutions to intelligent apps in the cloud.",tags:["Fall-For-IA","30-days-of-IA","learn-live","hack-together","community-buzz","ask-the-expert","azure-kubernetes-service","azure-functions","azure-openai","azure-container-apps","azure-cosmos-db","github-copilot","github-codespaces","github-actions"]},prevItem:{title:"1-6. Cultivating a Culture for Intelligent Apps",permalink:"/Cloud-Native/30daysofIA/cultivating-a-culture-for-intelligent-apps"},nextItem:{title:"1-4. How Digital Natives leverage Generative AI",permalink:"/Cloud-Native/30daysofIA/how-digital-natives-leverage-generative-ai"}},l={authorsImageUrls:[void 0]},p=[{value:"What We'll Cover:",id:"what-well-cover",level:2},{value:"Preparing the Path for Intelligent Apps: Transitioning from On-Premises/IaaS to Cloud-Native Applications",id:"preparing-the-path-for-intelligent-apps-transitioning-from-on-premisesiaas-to-cloud-native-applications",level:2},{value:"Understanding the Shift: Traditional vs. Intelligent Applications",id:"understanding-the-shift-traditional-vs-intelligent-applications",level:2},{value:"Functionality and User Experience",id:"functionality-and-user-experience",level:3},{value:"Infrastructure",id:"infrastructure",level:3},{value:"On-Premises/IaaS vs. Cloud-Native Platforms",id:"on-premisesiaas-vs-cloud-native-platforms",level:3},{value:"Strategic Considerations for Transitioning to Intelligent Apps",id:"strategic-considerations-for-transitioning-to-intelligent-apps",level:3},{value:"Re-Architecting Monolithic Apps into Microservices",id:"re-architecting-monolithic-apps-into-microservices",level:3},{value:"Event-Driven Architectures: Knowing When to Use Them",id:"event-driven-architectures-knowing-when-to-use-them",level:3},{value:"Conclusion",id:"conclusion",level:2},{value:"Exercise",id:"exercise",level:2}],c={toc:p};function u(e){let{components:t,...r}=e;return(0,n.kt)("wrapper",(0,i.Z)({},c,r,{components:t,mdxType:"MDXLayout"}),(0,n.kt)("head",null,(0,n.kt)("meta",{property:"og:url",content:"https://azure.github.io/cloud-native/30daysofia/how-digital-natives-leverage-generative-ai"}),(0,n.kt)("meta",{property:"og:type",content:"website"}),(0,n.kt)("meta",{property:"og:title",content:"**Fall For Intelligent Apps! \ud83c\udf42| Build AI Apps On Azure"}),(0,n.kt)("meta",{property:"og:description",content:"Explore how digital natives and Independent Software Vendors (ISVs) can transform their business operations and generate higher value for customers with generative AI and intelligent apps."}),(0,n.kt)("meta",{property:"og:image",content:"https://azure.github.io/Cloud-Native/img/ogImage.png"}),(0,n.kt)("meta",{name:"twitter:url",content:"https://azure.github.io/Cloud-Native/30daysofIA/preparing-the-path-for-intelligent-apps"}),(0,n.kt)("meta",{name:"twitter:title",content:"**Fall For Intelligent Apps! \ud83c\udf42 | Build AI Apps On Azure"}),(0,n.kt)("meta",{name:"twitter:description",content:"1-5. Learn best practices for modernizing your on-premises or IaaS solutions to intelligent apps in the cloud."}),(0,n.kt)("meta",{name:"twitter:image",content:"https://azure.github.io/Cloud-Native/img/ogImage.png"}),(0,n.kt)("meta",{name:"twitter:card",content:"summary_large_image"}),(0,n.kt)("meta",{name:"twitter:creator",content:"@devanshidiaries"}),(0,n.kt)("meta",{name:"twitter:site",content:"@AzureAdvocates"}),(0,n.kt)("link",{rel:"canonical",href:"https://azure.github.io/Cloud-Native/30daysofIA/preparing-the-path-for-intelligent-apps"})),(0,n.kt)("p",null,"Learn best practices for modernizing your on-premises or IaaS solutions to intelligent apps in the cloud."),(0,n.kt)("h2",{id:"what-well-cover"},"What We'll Cover:"),(0,n.kt)("ul",null,(0,n.kt)("li",{parentName:"ul"},"Traditional vs Intelligent Apps"),(0,n.kt)("li",{parentName:"ul"},"On-Premises/IaaS vs. Cloud-Native Platforms"),(0,n.kt)("li",{parentName:"ul"},"Modernization strategy for building Intelligent Apps")),(0,n.kt)("p",null,(0,n.kt)("img",{alt:"image of modernizing AI solutions for intelligent apps",src:a(22352).Z,width:"624",height:"380"})),(0,n.kt)("h2",{id:"preparing-the-path-for-intelligent-apps-transitioning-from-on-premisesiaas-to-cloud-native-applications"},"Preparing the Path for Intelligent Apps: Transitioning from On-Premises/IaaS to Cloud-Native Applications"),(0,n.kt)("p",null,"The proliferation of generative AI is paving the way for intelligent applications, software that leverages AI capabilities to deliver unparalleled functionality and user experience. As we covered in a previous article, \u201c",(0,n.kt)("a",{parentName:"p",href:"https://azure.github.io/Cloud-Native/30daysofIA/demystifying-intelligent-applications"},"Demystifying Intelligent Applications: Leveraging AI in App Development"),",\u201d intelligent Apps aren\u2019t just products of advanced coding. They interact, learn, and evolve thanks to modern AI and machine learning (ML) breakthroughs.\u202f "),(0,n.kt)("p",null,"The paradigm shift from traditional to intelligent apps demands that we change our approach to the technical challenges of software and architecture design. It also necessitates adopting new ways of thinking and operating within an organization, which we explore in greater depth in \u201c",(0,n.kt)("a",{parentName:"p",href:"https://azure.github.io/Cloud-Native/30daysofIA/cultivating-a-culture-for-intelligent-apps"},"Cultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management"),".\u201d\u202f "),(0,n.kt)("p",null,"This article explores the technical infrastructure powering intelligent apps, providing a roadmap to transition traditional on-premises or Infrastructure as a Service (IaaS) solutions to intelligent apps deployed to cloud native platforms and services. We\u2019ll discover the differences between conventional and intelligent apps, options for re-architecting existing applications, and critical strategic considerations integral to modernizing the application-building approach.\u202f "),(0,n.kt)("h2",{id:"understanding-the-shift-traditional-vs-intelligent-applications"},"Understanding the Shift: Traditional vs. Intelligent Applications"),(0,n.kt)("p",null,"Intelligent apps are more than just an incremental evolution from traditional apps. They involve a fundamental shift in software solution design, development, and deployment. A clear understanding of how each paradigm differs is crucial for comprehending the impact of this transition.\u202f "),(0,n.kt)("h3",{id:"functionality-and-user-experience"},"Functionality and User Experience"),(0,n.kt)("p",null,"Traditional applications, often rule-based and rigid, rely on preprogrammed operations. A traditional weather app, for example, typically presents structured, location-based forecasts using a predetermined data source. Meanwhile, an intelligent app leverages AI for data-driven decision-making and personalization. Rather than merely displaying the forecast, it integrates ML with additional data sources\u2014like a user\u2019s calendar and fitness data\u2014to determine their favorite outdoor activities, provide relevant clothing suggestions, and even automatically design ideal vacations and times based on the user\u2019s optimal weather preferences.\u202f "),(0,n.kt)("p",null,"Similarly, while traditional applications tend to offer a one-size-fits-all UI, intelligent apps use AI to create tailored interactions. For instance, an intelligent banking app might use generative AI to provide a voice-enabled UI that lets customers use natural language to ask about transactions and receive personalized financial advice."),(0,n.kt)("h3",{id:"infrastructure"},"Infrastructure"),(0,n.kt)("p",null,"Intelligent apps integrating these sophisticated features require highly scalable, flexible infrastructure optimized for AI functionality and able to manage large volumes of real-time data. Unlike traditional applications deployed on-premises or in infrastructure as a service (IaaS) environments, intelligent apps demand the flexibility and scalability of cloud native architectures."),(0,n.kt)("p",null,"An on-premises or IaaS-based e-commerce application may struggle with connecting its data to AI services or scaling to accommodate voluminous real-time data and requests. But an intelligent app built with a cloud native approach and scalability at its core can handle variable demand much more efficiently. It also maintains its infrastructure components on demand, whether for collecting data for AI/ML consumption or user-facing features like AI-enabled customer support and personalization."),(0,n.kt)("p",null,"Consider an intelligent app built on Azure that creates ChatGPT-like experiences with custom data. It might connect ",(0,n.kt)("a",{parentName:"p",href:"https://azure.microsoft.com/en-us/products/ai-services/openai-service?WT.mc_id=javascript-99907-ninarasi"},"Azure Open AI")," and ",(0,n.kt)("a",{parentName:"p",href:"https://azure.microsoft.com/en-us/products/ai-services/cognitive-search?WT.mc_id=javascript-99907-ninarasi"},"Azure Cognitive Search")," to an app running on ",(0,n.kt)("a",{parentName:"p",href:"https://azure.microsoft.com/en-us/products/container-apps/?WT.mc_id=javascript-99907-ninarasi"},"Azure Container Apps")," or ",(0,n.kt)("a",{parentName:"p",href:"https://azure.microsoft.com/en-us/products/kubernetes-service/?WT.mc_id=javascript-99907-ninarasi"},"Azure Kubernetes Service"),".\u202f "),(0,n.kt)("p",null,"The architecture might look like the following diagram based on this ",(0,n.kt)("a",{parentName:"p",href:"https://github.com/Azure-Samples/azure-search-openai-demo-csharp"},"demo app"),":"),(0,n.kt)("p",null,(0,n.kt)("img",{alt:"diagram of a demo app architecture",src:a(48199).Z,width:"624",height:"386"})),(0,n.kt)("p",null,"While the cloud is well-equipped to handle the challenges associated with shifting to AI-centric infrastructure\u2014handling real-time data processing and storage, integrating and managing AI services, configuring event-driven and microservices architectures, and ensuring security across the distributed environment\u2014re-architecting a traditional app to an intelligent app requires careful planning and execution to ensure it benefits fully from the capabilities of AI and cloud computing."),(0,n.kt)("h3",{id:"on-premisesiaas-vs-cloud-native-platforms"},"On-Premises/IaaS vs. Cloud-Native Platforms"),(0,n.kt)("p",null,"While on-premises and IaaS solutions provide substantial control over data and processes, this fine-grained control comes with setup and maintenance costs and may lack the scalability necessary for AI-driven Intelligent Apps. On-premises hosting involves purchasing and maintaining hardware, including servers and storage systems. IaaS involves leasing virtual hardware and then hiring and training IT personnel for the infrastructure\u2019s operation, maintenance, and security."),(0,n.kt)("p",null,"Conversely, infrastructure built on cloud native app services with ",(0,n.kt)("a",{parentName:"p",href:"https://azure.microsoft.com/?WT.mc_id=javascript-99907-ninarasi"},"Microsoft Azure")," offers virtually unlimited scalability without ongoing hardware and maintenance costs. This feature is ideal for intelligent apps that need to adapt and scale to accommodate fluctuating workloads and data streams. And because these cloud infrastructures typically operate on a pay-as-you-go model, traditionally substantial upfront costs turn into predictable operating expenses, lowering both the cost and risk of launching applications."),(0,n.kt)("p",null,"Moreover, cloud service providers continually update their offerings, so building on sophisticated AI tools and services through the cloud, like the ",(0,n.kt)("a",{parentName:"p",href:"https://azure.microsoft.com/en-us/solutions/ai/?WT.mc_id=javascript-99907-ninarasi"},"Azure AI Platform")," and orchestration capabilities with ",(0,n.kt)("a",{parentName:"p",href:"https://azure.microsoft.com/en-us/products/kubernetes-service/?WT.mc_id=javascript-99907-ninarasi"},"Azure Kubernetes Service"),", ensures that you have access to the latest AI and most advanced AI models and technologies."),(0,n.kt)("h3",{id:"strategic-considerations-for-transitioning-to-intelligent-apps"},"Strategic Considerations for Transitioning to Intelligent Apps"),(0,n.kt)("p",null,"When developing a strategy for transitioning to intelligent apps, you must consider costs, personnel skill development, and data management."),(0,n.kt)("p",null,"Monolithic architectures are unlikely to support the scale and ease of iteration required by intelligent apps, so you\u2019ll likely need to implement code changes to support the transition. Your chosen hosting solution and the amount of data your app will handle affect cost and performance, in addition to determining privacy and compliance requirements."),(0,n.kt)("p",null,"An app that manages or stores terabytes or petabytes of data will require storage-optimized virtual machines or a managed database service hosting. Meanwhile, an app that processes video content or performs resource-intensive calculations should prioritize high-performance computing. Maximizing storage and performance can become extraordinarily costly."),(0,n.kt)("p",null,"Finally, existing applications shifting towards intelligent apps may encounter compatibility conflicts, depending on database type or the existing system\u2019s underlying programming language. As you might imagine, a traditional app running on-premises and using a shared local database may need significant restructuring to support a cloud native architecture. You need plans for data migration, potential downtime, and training."),(0,n.kt)("p",null,"Next, let\u2019s look at this transformation process."),(0,n.kt)("h3",{id:"re-architecting-monolithic-apps-into-microservices"},"Re-Architecting Monolithic Apps into Microservices"),(0,n.kt)("p",null,"Shifting from ",(0,n.kt)("a",{parentName:"p",href:"https://learn.microsoft.com/en-us/dotnet/architecture/containerized-lifecycle/design-develop-containerized-apps/monolithic-applications?WT.mc_id=javascript-99907-ninarasi"},"monolithic applications")," to microservices is pivotal for transitioning to Intelligent Apps, and you can use this shift as an opportunity to transform your legacy software into intelligent apps that leverage the flexibility and power of microservices-enabled AI solutions."),(0,n.kt)("p",null,"Traditionally, a monolithic application (let\u2019s use an online marketplace as an example) might handle all activities\u2014from serving webpages to processing payments\u2014within an on-premises server or a cloud server. However, this approach is challenging to scale and remains vulnerable to network failures or cyberattacks."),(0,n.kt)("p",null,"Breaking these applications into multiple services with specific responsibilities, such as authentication or notifications, can overcome these limitations. These services are typically deployed as microservices within container environments like ",(0,n.kt)("a",{parentName:"p",href:"https://azure.microsoft.com/en-us/products/kubernetes-service/?WT.mc_id=javascript-99907-ninarasi"},"Azure Kubernetes Service")," or serverless platforms like ",(0,n.kt)("a",{parentName:"p",href:"https://azure.microsoft.com/en-us/products/container-apps?WT.mc_id=javascript-99907-ninarasi"},"Azure Container Apps")," or ",(0,n.kt)("a",{parentName:"p",href:"https://azure.microsoft.com/en-us/products/functions/?WT.mc_id=javascript-99907-ninarasi"},"Azure Functions")," using automated processes. This approach offers scalability, robustness, and improved performance, as each microservice can run independently and use its own database."),(0,n.kt)("p",null,"Shifting towards smaller, independent microservices in app architectures allows us to reap the benefits of AI-powered intelligent apps, for example, by using microservices to handle AI tasks such as processing natural language input, analyzing behavioral data, or personalizing content."),(0,n.kt)("admonition",{type:"info"},(0,n.kt)("p",{parentName:"admonition"},"Watch ",(0,n.kt)("a",{parentName:"p",href:"https://aka.ms/learnlive-contoso-app-deconstructed-Ep1"},"Episode 1")," and ",(0,n.kt)("a",{parentName:"p",href:"https://aka.ms/learnlive-contoso-app-deconstructed-Ep2"},"Episode 2")," of the ",(0,n.kt)("a",{parentName:"p",href:"https://aka.ms/contoso-real-estate/learn-live"},"Learn Live series")," on how to build, test and deploy an end-to-end intelligent app solution using the ",(0,n.kt)("a",{parentName:"p",href:"https://github.com/Azure-Samples/contoso-real-estate"},"Contoso Real Estate Sample"),".")),(0,n.kt)("h3",{id:"event-driven-architectures-knowing-when-to-use-them"},"Event-Driven Architectures: Knowing When to Use Them"),(0,n.kt)("p",null,(0,n.kt)("a",{parentName:"p",href:"https://learn.microsoft.com/en-us/azure/architecture/guide/architecture-styles/event-driven?WT.mc_id=javascript-99907-ninarasi"},"Event-Driven Architectures")," (EDAs) can significantly enhance the performance and cost-effectiveness of intelligent apps. EDAs, typically implemented with microservices, respond in real time to events or state changes ranging from user interactions to real-time analytics."),(0,n.kt)("p",null,"Consider an intelligent app designed for personalized customer engagement. Instead of designing the application to check continuously for changes in user data, an EDA can react to such changes immediately, triggering appropriate microservices. For example, if users modify their preferences, an event could trigger an AI-driven recommendation microservice to update its suggestions immediately. This real-time responsiveness, enabled by services like ",(0,n.kt)("a",{parentName:"p",href:"https://azure.microsoft.com/en-us/products/event-grid/?WT.mc_id=javascript-99907-ninarasi"},"Azure Event Grid"),", can improve user experiences and make intelligent apps more adaptive and proactive."),(0,n.kt)("p",null,"However, it\u2019s worth noting that while EDAs provide significant benefits, they can introduce unique complexities, such as maintaining event consistency and debugging issues in event chains across multiple microservices. Therefore, it\u2019s important to assess your application\u2019s needs so you can understand where it can benefit from an EDA."),(0,n.kt)("h2",{id:"conclusion"},"Conclusion"),(0,n.kt)("p",null,"Transitioning to intelligent apps represents a shift in strategy, architecture, and infrastructure. By designing and re-architecting for cloud-based infrastructure, your apps gain scalability, cost-effectiveness, robust data management, access to sophisticated AI tools, enhanced security, and reduced operational overhead.\u202f\u202f "),(0,n.kt)("p",null,"But this transition extends well beyond its technical components. Organizations and the people working within them need equal attention. You can explore these facets in greater depth in \u201cCultivating a Culture for Intelligent Apps: Organizational Readiness and Change Management.\u201d\u202f "),(0,n.kt)("p",null,"The power of generative AI is here, and preparing for the move toward cloud-based intelligent applications will ensure that your apps remain performative and scalable and\u2014most importantly\u2014that your business is ready for the newest era of app innovation."),(0,n.kt)("h2",{id:"exercise"},"Exercise"),(0,n.kt)("ul",null,(0,n.kt)("li",{parentName:"ul"},"Complete the ",(0,n.kt)("strong",{parentName:"li"},(0,n.kt)("a",{parentName:"strong",href:"https://aka.ms/fallforIA/apps-csc"},"Apps Cloud Skills Challenge"))," to build on your app dev and AI skills."),(0,n.kt)("li",{parentName:"ul"},"Complete the ",(0,n.kt)("strong",{parentName:"li"},(0,n.kt)("a",{parentName:"strong",href:"https://aka.ms/fallforIA/ai-csc"},"AI Cloud Skills Challenge"))," to build on your AI skills."),(0,n.kt)("li",{parentName:"ul"},"Register for ",(0,n.kt)("strong",{parentName:"li"},(0,n.kt)("a",{parentName:"strong",href:"https://aka.ms/learnlive-contoso-app-deconstructed-Ep3"},"Episode 03"))," of the serverless edition Learn Live session to learn how to build, test and deploy an end-to-end intelligent app solution."),(0,n.kt)("li",{parentName:"ul"},"Register for ",(0,n.kt)("strong",{parentName:"li"},(0,n.kt)("a",{parentName:"strong",href:"https://reactor.microsoft.com/en-us/reactor/series/S-1037/"},"Ask the Expert: Azure Functions"))," session for live Q&A with the Product Engineering team on building intelligent serverless apps.")))}u.isMDXComponent=!0},22352:(e,t,a)=>{a.d(t,{Z:()=>i});const i=a.p+"assets/images/blog-image-1-5-8ad85f98f250a51ea0ccadefbdcf2237.png"},48199:(e,t,a)=>{a.d(t,{Z:()=>i});const i=a.p+"assets/images/diagram-of-a-demo-app-architecture-3451b86065584b95e8896c995070eeec.png"}}]); \ No newline at end of file diff --git a/assets/js/4982e737.9b94071c.js b/assets/js/4982e737.9b94071c.js new file mode 100644 index 0000000000..4681afffeb --- /dev/null +++ b/assets/js/4982e737.9b94071c.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[71593],{89806:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db/page/10","page":10,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db/page/9","nextPage":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db/page/11","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/4988404d.4fdc47ca.js b/assets/js/4988404d.4fdc47ca.js deleted file mode 100644 index 142b0a5739..0000000000 --- a/assets/js/4988404d.4fdc47ca.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[24913],{57723:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-openai/page/2","page":2,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/azure-openai","nextPage":"/Cloud-Native/30daysofIA/tags/azure-openai/page/3","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/4988404d.d1c1bdfa.js b/assets/js/4988404d.d1c1bdfa.js new file mode 100644 index 0000000000..1f1a5e881e --- /dev/null +++ b/assets/js/4988404d.d1c1bdfa.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[24913],{57723:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-openai/page/2","page":2,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/azure-openai","nextPage":"/Cloud-Native/30daysofIA/tags/azure-openai/page/3","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/4b3f15eb.bb36c1c5.js b/assets/js/4b3f15eb.bb36c1c5.js new file mode 100644 index 0000000000..735fc0cb94 --- /dev/null +++ b/assets/js/4b3f15eb.bb36c1c5.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[87930],{75472:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/30-days-of-ia/page/5","page":5,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/30-days-of-ia/page/4","nextPage":"/Cloud-Native/30daysofIA/tags/30-days-of-ia/page/6","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/4b3f15eb.f1bc5b6e.js b/assets/js/4b3f15eb.f1bc5b6e.js deleted file mode 100644 index 2091fd31ef..0000000000 --- a/assets/js/4b3f15eb.f1bc5b6e.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[87930],{75472:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/30-days-of-ia/page/5","page":5,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/30-days-of-ia/page/4","nextPage":"/Cloud-Native/30daysofIA/tags/30-days-of-ia/page/6","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/4c23d5c0.228a8dca.js b/assets/js/4c23d5c0.228a8dca.js new file mode 100644 index 0000000000..f0fe79727a --- /dev/null +++ b/assets/js/4c23d5c0.228a8dca.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[24919],{24542:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/community-buzz/page/10","page":10,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/community-buzz/page/9","nextPage":"/Cloud-Native/30daysofIA/tags/community-buzz/page/11","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/4c3a8a2f.c17067ba.js b/assets/js/4c3a8a2f.c17067ba.js new file mode 100644 index 0000000000..a43aa11575 --- /dev/null +++ b/assets/js/4c3a8a2f.c17067ba.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[36713],{21904:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/community-buzz/page/11","page":11,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/community-buzz/page/10","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/4c3b9235.80f5d621.js b/assets/js/4c3b9235.46d1b0b8.js similarity index 72% rename from assets/js/4c3b9235.80f5d621.js rename to assets/js/4c3b9235.46d1b0b8.js index 30cb3341ca..edf9d48e1d 100644 --- a/assets/js/4c3b9235.80f5d621.js +++ b/assets/js/4c3b9235.46d1b0b8.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[19581],{27060:e=>{e.exports=JSON.parse('{"label":"ask-the-expert","permalink":"/Cloud-Native/30daysofIA/tags/ask-the-expert","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[19581],{27060:e=>{e.exports=JSON.parse('{"label":"ask-the-expert","permalink":"/Cloud-Native/30daysofIA/tags/ask-the-expert","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/4c3daab0.5a2aacb5.js b/assets/js/4c3daab0.5a2aacb5.js deleted file mode 100644 index 01d2e7acc3..0000000000 --- a/assets/js/4c3daab0.5a2aacb5.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[89443],{1005:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/github-codespaces/page/7","page":7,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/github-codespaces/page/6","nextPage":"/Cloud-Native/30daysofIA/tags/github-codespaces/page/8","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/4c3daab0.75136e9f.js b/assets/js/4c3daab0.75136e9f.js new file mode 100644 index 0000000000..a380595297 --- /dev/null +++ b/assets/js/4c3daab0.75136e9f.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[89443],{1005:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/github-codespaces/page/7","page":7,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/github-codespaces/page/6","nextPage":"/Cloud-Native/30daysofIA/tags/github-codespaces/page/8","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/4d20e64b.a465a014.js b/assets/js/4d20e64b.a465a014.js deleted file mode 100644 index e6443e875e..0000000000 --- a/assets/js/4d20e64b.a465a014.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[57159],{4062:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-openai/page/9","page":9,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/azure-openai/page/8","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/4d20e64b.e0d96e14.js b/assets/js/4d20e64b.e0d96e14.js new file mode 100644 index 0000000000..fb7410a4f0 --- /dev/null +++ b/assets/js/4d20e64b.e0d96e14.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[57159],{4062:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-openai/page/9","page":9,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/azure-openai/page/8","nextPage":"/Cloud-Native/30daysofIA/tags/azure-openai/page/10","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/4d46cbe9.5f7cc02a.js b/assets/js/4d46cbe9.d054f671.js similarity index 92% rename from assets/js/4d46cbe9.5f7cc02a.js rename to assets/js/4d46cbe9.d054f671.js index 6286b7998c..2141ce4a9c 100644 --- a/assets/js/4d46cbe9.5f7cc02a.js +++ b/assets/js/4d46cbe9.d054f671.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[43464],{28763:a=>{a.exports=JSON.parse('{"label":"azure-container-apps","permalink":"/Cloud-Native/30daysofIA/tags/azure-container-apps","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[43464],{28763:a=>{a.exports=JSON.parse('{"label":"azure-container-apps","permalink":"/Cloud-Native/30daysofIA/tags/azure-container-apps","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/4f61748e.873488fb.js b/assets/js/4f61748e.873488fb.js new file mode 100644 index 0000000000..eba63bb0e7 --- /dev/null +++ b/assets/js/4f61748e.873488fb.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[4414],{41400:a=>{a.exports=JSON.parse('{"label":"Fall-For-IA","permalink":"/Cloud-Native/30daysofIA/tags/fall-for-ia","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/4f789287.9959b695.js b/assets/js/4f789287.8ea82e3b.js similarity index 74% rename from assets/js/4f789287.9959b695.js rename to assets/js/4f789287.8ea82e3b.js index dd5860633a..a1aed1d3c1 100644 --- a/assets/js/4f789287.9959b695.js +++ b/assets/js/4f789287.8ea82e3b.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[63186],{3980:a=>{a.exports=JSON.parse('{"label":"Fall-For-IA","permalink":"/Cloud-Native/30daysofIA/tags/fall-for-ia","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[63186],{3980:a=>{a.exports=JSON.parse('{"label":"Fall-For-IA","permalink":"/Cloud-Native/30daysofIA/tags/fall-for-ia","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/50b61013.901e79cd.js b/assets/js/50b61013.901e79cd.js deleted file mode 100644 index 22524f9666..0000000000 --- a/assets/js/50b61013.901e79cd.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[97018],{73537:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-functions/page/5","page":5,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/azure-functions/page/4","nextPage":"/Cloud-Native/30daysofIA/tags/azure-functions/page/6","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/50b61013.91303c96.js b/assets/js/50b61013.91303c96.js new file mode 100644 index 0000000000..bd10791187 --- /dev/null +++ b/assets/js/50b61013.91303c96.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[97018],{73537:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-functions/page/5","page":5,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/azure-functions/page/4","nextPage":"/Cloud-Native/30daysofIA/tags/azure-functions/page/6","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/51f200c1.29807d7a.js b/assets/js/51f200c1.29807d7a.js new file mode 100644 index 0000000000..2e72ee38ce --- /dev/null +++ b/assets/js/51f200c1.29807d7a.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[7485],{22644:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/github-copilot/page/9","page":9,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/github-copilot/page/8","nextPage":"/Cloud-Native/30daysofIA/tags/github-copilot/page/10","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/51f200c1.df545e82.js b/assets/js/51f200c1.df545e82.js deleted file mode 100644 index 741ba66e80..0000000000 --- a/assets/js/51f200c1.df545e82.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[7485],{22644:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/github-copilot/page/9","page":9,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/github-copilot/page/8","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/51fb1599.7c1dedad.js b/assets/js/51fb1599.7c1dedad.js new file mode 100644 index 0000000000..8ced2065a3 --- /dev/null +++ b/assets/js/51fb1599.7c1dedad.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[22362],{13472:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-functions/page/3","page":3,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/azure-functions/page/2","nextPage":"/Cloud-Native/30daysofIA/tags/azure-functions/page/4","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/51fb1599.7d31782f.js b/assets/js/51fb1599.7d31782f.js deleted file mode 100644 index 45e804e448..0000000000 --- a/assets/js/51fb1599.7d31782f.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[22362],{13472:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-functions/page/3","page":3,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/azure-functions/page/2","nextPage":"/Cloud-Native/30daysofIA/tags/azure-functions/page/4","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/52094cec.588fb7e8.js b/assets/js/52094cec.588fb7e8.js new file mode 100644 index 0000000000..a114058c76 --- /dev/null +++ b/assets/js/52094cec.588fb7e8.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[41003],{45118:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db/page/6","page":6,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db/page/5","nextPage":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db/page/7","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/52094cec.85fb4be8.js b/assets/js/52094cec.85fb4be8.js deleted file mode 100644 index 2ba4c102e9..0000000000 --- a/assets/js/52094cec.85fb4be8.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[41003],{45118:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db/page/6","page":6,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db/page/5","nextPage":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db/page/7","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/52d53352.99ac5e15.js b/assets/js/52d53352.99ac5e15.js new file mode 100644 index 0000000000..74a8070f70 --- /dev/null +++ b/assets/js/52d53352.99ac5e15.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[73101],{26008:e=>{e.exports=JSON.parse('{"label":"github-codespaces","permalink":"/Cloud-Native/30daysofIA/tags/github-codespaces","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/535cf760.097fca42.js b/assets/js/535cf760.4e49de03.js similarity index 92% rename from assets/js/535cf760.097fca42.js rename to assets/js/535cf760.4e49de03.js index 0046fa7a1c..f178b68c52 100644 --- a/assets/js/535cf760.097fca42.js +++ b/assets/js/535cf760.4e49de03.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[23089],{64694:a=>{a.exports=JSON.parse('{"label":"azure-container-apps","permalink":"/Cloud-Native/30daysofIA/tags/azure-container-apps","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[23089],{64694:a=>{a.exports=JSON.parse('{"label":"azure-container-apps","permalink":"/Cloud-Native/30daysofIA/tags/azure-container-apps","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/5361a0e4.2555ab44.js b/assets/js/5361a0e4.67242618.js similarity index 73% rename from assets/js/5361a0e4.2555ab44.js rename to assets/js/5361a0e4.67242618.js index 4f98705cd5..c1e77de668 100644 --- a/assets/js/5361a0e4.2555ab44.js +++ b/assets/js/5361a0e4.67242618.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[65269],{52720:e=>{e.exports=JSON.parse('{"label":"hack-together","permalink":"/Cloud-Native/30daysofIA/tags/hack-together","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[65269],{52720:e=>{e.exports=JSON.parse('{"label":"hack-together","permalink":"/Cloud-Native/30daysofIA/tags/hack-together","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/53e9efec.60123bce.js b/assets/js/53e9efec.60123bce.js new file mode 100644 index 0000000000..1fe31beff7 --- /dev/null +++ b/assets/js/53e9efec.60123bce.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[1842],{87883:e=>{e.exports=JSON.parse('{"label":"github-copilot","permalink":"/Cloud-Native/30daysofIA/tags/github-copilot","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/54625fb2.b6386356.js b/assets/js/54625fb2.408714cf.js similarity index 72% rename from assets/js/54625fb2.b6386356.js rename to assets/js/54625fb2.408714cf.js index 29ef4cae67..6db593d019 100644 --- a/assets/js/54625fb2.b6386356.js +++ b/assets/js/54625fb2.408714cf.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[50958],{75239:e=>{e.exports=JSON.parse('{"label":"community-buzz","permalink":"/Cloud-Native/30daysofIA/tags/community-buzz","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[50958],{75239:e=>{e.exports=JSON.parse('{"label":"community-buzz","permalink":"/Cloud-Native/30daysofIA/tags/community-buzz","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/55183266.d6c4c584.js b/assets/js/55183266.d6c4c584.js new file mode 100644 index 0000000000..fd024e3a8e --- /dev/null +++ b/assets/js/55183266.d6c4c584.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[83605],{23928:e=>{e.exports=JSON.parse('{"label":"azure-functions","permalink":"/Cloud-Native/30daysofIA/tags/azure-functions","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/55404de1.f85c3d88.js b/assets/js/55404de1.4e969025.js similarity index 72% rename from assets/js/55404de1.f85c3d88.js rename to assets/js/55404de1.4e969025.js index 2962199f51..79aff67035 100644 --- a/assets/js/55404de1.f85c3d88.js +++ b/assets/js/55404de1.4e969025.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[72644],{49195:a=>{a.exports=JSON.parse('{"label":"github-actions","permalink":"/Cloud-Native/30daysofIA/tags/github-actions","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[72644],{49195:a=>{a.exports=JSON.parse('{"label":"github-actions","permalink":"/Cloud-Native/30daysofIA/tags/github-actions","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/55c9118e.5b5c147b.js b/assets/js/55c9118e.a4a0b9ed.js similarity index 73% rename from assets/js/55c9118e.5b5c147b.js rename to assets/js/55c9118e.a4a0b9ed.js index 02deeae47b..e27abb3752 100644 --- a/assets/js/55c9118e.5b5c147b.js +++ b/assets/js/55c9118e.a4a0b9ed.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[2830],{40787:e=>{e.exports=JSON.parse('{"label":"hack-together","permalink":"/Cloud-Native/30daysofIA/tags/hack-together","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[2830],{40787:e=>{e.exports=JSON.parse('{"label":"hack-together","permalink":"/Cloud-Native/30daysofIA/tags/hack-together","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/565dbc36.8e0e3747.js b/assets/js/565dbc36.8e0e3747.js new file mode 100644 index 0000000000..213afc86a6 --- /dev/null +++ b/assets/js/565dbc36.8e0e3747.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[42443],{85406:e=>{e.exports=JSON.parse('{"label":"github-codespaces","permalink":"/Cloud-Native/30daysofIA/tags/github-codespaces","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/57904ffd.08e3b8d5.js b/assets/js/57904ffd.08e3b8d5.js deleted file mode 100644 index 76856455f6..0000000000 --- a/assets/js/57904ffd.08e3b8d5.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[9118],{86324:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/github-codespaces/page/4","page":4,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/github-codespaces/page/3","nextPage":"/Cloud-Native/30daysofIA/tags/github-codespaces/page/5","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/57904ffd.9957ff58.js b/assets/js/57904ffd.9957ff58.js new file mode 100644 index 0000000000..3c9611bca8 --- /dev/null +++ b/assets/js/57904ffd.9957ff58.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[9118],{86324:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/github-codespaces/page/4","page":4,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/github-codespaces/page/3","nextPage":"/Cloud-Native/30daysofIA/tags/github-codespaces/page/5","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/587f2372.97f2dd26.js b/assets/js/587f2372.f4c6dce4.js similarity index 73% rename from assets/js/587f2372.97f2dd26.js rename to assets/js/587f2372.f4c6dce4.js index 8bc3fd7c2c..dfd00f3d3a 100644 --- a/assets/js/587f2372.97f2dd26.js +++ b/assets/js/587f2372.f4c6dce4.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[55522],{26435:e=>{e.exports=JSON.parse('{"label":"hack-together","permalink":"/Cloud-Native/30daysofIA/tags/hack-together","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[55522],{26435:e=>{e.exports=JSON.parse('{"label":"hack-together","permalink":"/Cloud-Native/30daysofIA/tags/hack-together","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/599dc25b.eb24a7e7.js b/assets/js/599dc25b.21b5c5c4.js similarity index 95% rename from assets/js/599dc25b.eb24a7e7.js rename to assets/js/599dc25b.21b5c5c4.js index deebea0c0e..1c7e376f56 100644 --- a/assets/js/599dc25b.eb24a7e7.js +++ b/assets/js/599dc25b.21b5c5c4.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[59548],{80204:e=>{e.exports=JSON.parse('{"label":"azure-functions","permalink":"/Cloud-Native/30daysofIA/tags/azure-functions","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[59548],{80204:e=>{e.exports=JSON.parse('{"label":"azure-functions","permalink":"/Cloud-Native/30daysofIA/tags/azure-functions","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/59a2dd7b.281bc225.js b/assets/js/59a2dd7b.281bc225.js new file mode 100644 index 0000000000..22cea75461 --- /dev/null +++ b/assets/js/59a2dd7b.281bc225.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[38582],{75929:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/fall-for-ia/page/9","page":9,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/fall-for-ia/page/8","nextPage":"/Cloud-Native/30daysofIA/tags/fall-for-ia/page/10","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/5ae2fd00.9d39a649.js b/assets/js/5ae2fd00.c1ff0dc1.js similarity index 74% rename from assets/js/5ae2fd00.9d39a649.js rename to assets/js/5ae2fd00.c1ff0dc1.js index 8aaf14a9d5..7a7e836c5d 100644 --- a/assets/js/5ae2fd00.9d39a649.js +++ b/assets/js/5ae2fd00.c1ff0dc1.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[14219],{72010:a=>{a.exports=JSON.parse('{"label":"Fall-For-IA","permalink":"/Cloud-Native/30daysofIA/tags/fall-for-ia","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[14219],{72010:a=>{a.exports=JSON.parse('{"label":"Fall-For-IA","permalink":"/Cloud-Native/30daysofIA/tags/fall-for-ia","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/5b107cfc.b7ee69c7.js b/assets/js/5b107cfc.b7ee69c7.js deleted file mode 100644 index dc74190d56..0000000000 --- a/assets/js/5b107cfc.b7ee69c7.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[70176],{8369:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/github-actions/page/4","page":4,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/github-actions/page/3","nextPage":"/Cloud-Native/30daysofIA/tags/github-actions/page/5","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/5b107cfc.e11439d4.js b/assets/js/5b107cfc.e11439d4.js new file mode 100644 index 0000000000..063b92829e --- /dev/null +++ b/assets/js/5b107cfc.e11439d4.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[70176],{8369:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/github-actions/page/4","page":4,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/github-actions/page/3","nextPage":"/Cloud-Native/30daysofIA/tags/github-actions/page/5","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/5c3ab9e3.fefeff21.js b/assets/js/5c3ab9e3.52677e48.js similarity index 73% rename from assets/js/5c3ab9e3.fefeff21.js rename to assets/js/5c3ab9e3.52677e48.js index 83bd0b5ef5..7c7cfda9cd 100644 --- a/assets/js/5c3ab9e3.fefeff21.js +++ b/assets/js/5c3ab9e3.52677e48.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[76310],{73541:e=>{e.exports=JSON.parse('{"label":"azure-openai","permalink":"/Cloud-Native/30daysofIA/tags/azure-openai","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[76310],{73541:e=>{e.exports=JSON.parse('{"label":"azure-openai","permalink":"/Cloud-Native/30daysofIA/tags/azure-openai","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/5cab2412.1a05c5b6.js b/assets/js/5cab2412.16e6edc0.js similarity index 73% rename from assets/js/5cab2412.1a05c5b6.js rename to assets/js/5cab2412.16e6edc0.js index add975a4f3..514ba5c13c 100644 --- a/assets/js/5cab2412.1a05c5b6.js +++ b/assets/js/5cab2412.16e6edc0.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[90494],{32798:e=>{e.exports=JSON.parse('{"label":"azure-openai","permalink":"/Cloud-Native/30daysofIA/tags/azure-openai","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[90494],{32798:e=>{e.exports=JSON.parse('{"label":"azure-openai","permalink":"/Cloud-Native/30daysofIA/tags/azure-openai","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/5cd45a8d.5a5793d7.js b/assets/js/5cd45a8d.5a5793d7.js deleted file mode 100644 index b03f32d765..0000000000 --- a/assets/js/5cd45a8d.5a5793d7.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[83512],{29516:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/30-days-of-ia/page/2","page":2,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/30-days-of-ia","nextPage":"/Cloud-Native/30daysofIA/tags/30-days-of-ia/page/3","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/5cd45a8d.d9bc1b69.js b/assets/js/5cd45a8d.d9bc1b69.js new file mode 100644 index 0000000000..5cd080fb9f --- /dev/null +++ b/assets/js/5cd45a8d.d9bc1b69.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[83512],{29516:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/30-days-of-ia/page/2","page":2,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/30-days-of-ia","nextPage":"/Cloud-Native/30daysofIA/tags/30-days-of-ia/page/3","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/5e601653.5b8c60f6.js b/assets/js/5e601653.5b8c60f6.js deleted file mode 100644 index 0c4ef52c68..0000000000 --- a/assets/js/5e601653.5b8c60f6.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[35059],{55270:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db/page/5","page":5,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db/page/4","nextPage":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db/page/6","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/5e601653.c6820bf7.js b/assets/js/5e601653.c6820bf7.js new file mode 100644 index 0000000000..07fed04819 --- /dev/null +++ b/assets/js/5e601653.c6820bf7.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[35059],{55270:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db/page/5","page":5,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db/page/4","nextPage":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db/page/6","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/5f63ac35.4fce26d7.js b/assets/js/5f63ac35.4fce26d7.js new file mode 100644 index 0000000000..d159534f2d --- /dev/null +++ b/assets/js/5f63ac35.4fce26d7.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[24359],{76757:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/github-copilot/page/2","page":2,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/github-copilot","nextPage":"/Cloud-Native/30daysofIA/tags/github-copilot/page/3","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/5f63ac35.c9dce46d.js b/assets/js/5f63ac35.c9dce46d.js deleted file mode 100644 index db24fd22d9..0000000000 --- a/assets/js/5f63ac35.c9dce46d.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[24359],{76757:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/github-copilot/page/2","page":2,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/github-copilot","nextPage":"/Cloud-Native/30daysofIA/tags/github-copilot/page/3","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/604b448d.a2da63bd.js b/assets/js/604b448d.0e585a19.js similarity index 72% rename from assets/js/604b448d.a2da63bd.js rename to assets/js/604b448d.0e585a19.js index acb3c61458..b3b1737ecc 100644 --- a/assets/js/604b448d.a2da63bd.js +++ b/assets/js/604b448d.0e585a19.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[6312],{43366:a=>{a.exports=JSON.parse('{"label":"github-actions","permalink":"/Cloud-Native/30daysofIA/tags/github-actions","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[6312],{43366:a=>{a.exports=JSON.parse('{"label":"github-actions","permalink":"/Cloud-Native/30daysofIA/tags/github-actions","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/605b2ff8.eb682cc4.js b/assets/js/605b2ff8.82b69a31.js similarity index 72% rename from assets/js/605b2ff8.eb682cc4.js rename to assets/js/605b2ff8.82b69a31.js index 768a07513c..1967ad2b7a 100644 --- a/assets/js/605b2ff8.eb682cc4.js +++ b/assets/js/605b2ff8.82b69a31.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[19863],{96950:e=>{e.exports=JSON.parse('{"label":"github-copilot","permalink":"/Cloud-Native/30daysofIA/tags/github-copilot","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[19863],{96950:e=>{e.exports=JSON.parse('{"label":"github-copilot","permalink":"/Cloud-Native/30daysofIA/tags/github-copilot","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/647961e4.74c55fdf.js b/assets/js/647961e4.74c55fdf.js deleted file mode 100644 index ea1a9fe7ff..0000000000 --- a/assets/js/647961e4.74c55fdf.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[63439],{78064:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-container-apps/page/2","page":2,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/azure-container-apps","nextPage":"/Cloud-Native/30daysofIA/tags/azure-container-apps/page/3","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/647961e4.bd66eaa3.js b/assets/js/647961e4.bd66eaa3.js new file mode 100644 index 0000000000..7467c57bec --- /dev/null +++ b/assets/js/647961e4.bd66eaa3.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[63439],{78064:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-container-apps/page/2","page":2,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/azure-container-apps","nextPage":"/Cloud-Native/30daysofIA/tags/azure-container-apps/page/3","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/654f4d54.802219e0.js b/assets/js/654f4d54.802219e0.js new file mode 100644 index 0000000000..6f77ecb9a1 --- /dev/null +++ b/assets/js/654f4d54.802219e0.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[77618],{45220:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/hack-together/page/10","page":10,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/hack-together/page/9","nextPage":"/Cloud-Native/30daysofIA/tags/hack-together/page/11","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/675273ae.6da76d91.js b/assets/js/675273ae.6da76d91.js new file mode 100644 index 0000000000..2be67b0fa4 --- /dev/null +++ b/assets/js/675273ae.6da76d91.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[91055],{16092:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/github-codespaces/page/11","page":11,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/github-codespaces/page/10","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/677498e0.35cb10c2.js b/assets/js/677498e0.35cb10c2.js new file mode 100644 index 0000000000..1e8ab919f2 --- /dev/null +++ b/assets/js/677498e0.35cb10c2.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[63746],{5464:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/community-buzz/page/9","page":9,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/community-buzz/page/8","nextPage":"/Cloud-Native/30daysofIA/tags/community-buzz/page/10","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/677498e0.5d24dafb.js b/assets/js/677498e0.5d24dafb.js deleted file mode 100644 index fc9d591990..0000000000 --- a/assets/js/677498e0.5d24dafb.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[63746],{5464:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/community-buzz/page/9","page":9,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/community-buzz/page/8","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/67e85bba.ced7a540.js b/assets/js/67e85bba.9649a011.js similarity index 94% rename from assets/js/67e85bba.ced7a540.js rename to assets/js/67e85bba.9649a011.js index 432ad53fba..c95bc0aa7b 100644 --- a/assets/js/67e85bba.ced7a540.js +++ b/assets/js/67e85bba.9649a011.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[66857],{19360:e=>{e.exports=JSON.parse('{"label":"github-codespaces","permalink":"/Cloud-Native/30daysofIA/tags/github-codespaces","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[66857],{19360:e=>{e.exports=JSON.parse('{"label":"github-codespaces","permalink":"/Cloud-Native/30daysofIA/tags/github-codespaces","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/6a20c0a4.d1c41a56.js b/assets/js/6a20c0a4.722023e3.js similarity index 96% rename from assets/js/6a20c0a4.d1c41a56.js rename to assets/js/6a20c0a4.722023e3.js index 239ec7b9d8..cd26af66f3 100644 --- a/assets/js/6a20c0a4.d1c41a56.js +++ b/assets/js/6a20c0a4.722023e3.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[5201],{88271:s=>{s.exports=JSON.parse('{"label":"azure-cosmos-db","permalink":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[5201],{88271:s=>{s.exports=JSON.parse('{"label":"azure-cosmos-db","permalink":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/6ac3c29a.ad250b7a.js b/assets/js/6ac3c29a.3193dde7.js similarity index 96% rename from assets/js/6ac3c29a.ad250b7a.js rename to assets/js/6ac3c29a.3193dde7.js index 77fec5af66..215a8f168d 100644 --- a/assets/js/6ac3c29a.ad250b7a.js +++ b/assets/js/6ac3c29a.3193dde7.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[8939],{51939:s=>{s.exports=JSON.parse('{"label":"azure-cosmos-db","permalink":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[8939],{51939:s=>{s.exports=JSON.parse('{"label":"azure-cosmos-db","permalink":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/6b35c7b1.85494c4c.js b/assets/js/6b35c7b1.c8498244.js similarity index 92% rename from assets/js/6b35c7b1.85494c4c.js rename to assets/js/6b35c7b1.c8498244.js index 2573800719..f53809852a 100644 --- a/assets/js/6b35c7b1.85494c4c.js +++ b/assets/js/6b35c7b1.c8498244.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[1481],{98234:a=>{a.exports=JSON.parse('{"label":"azure-container-apps","permalink":"/Cloud-Native/30daysofIA/tags/azure-container-apps","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[1481],{98234:a=>{a.exports=JSON.parse('{"label":"azure-container-apps","permalink":"/Cloud-Native/30daysofIA/tags/azure-container-apps","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/6b9868e6.16b7a1fe.js b/assets/js/6b9868e6.16b7a1fe.js new file mode 100644 index 0000000000..12c72125b3 --- /dev/null +++ b/assets/js/6b9868e6.16b7a1fe.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[15518],{27382:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/30-days-of-ia","page":1,"postsPerPage":1,"totalPages":11,"totalCount":11,"nextPage":"/Cloud-Native/30daysofIA/tags/30-days-of-ia/page/2","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/6b9868e6.213a574f.js b/assets/js/6b9868e6.213a574f.js deleted file mode 100644 index 1b0032d3ff..0000000000 --- a/assets/js/6b9868e6.213a574f.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[15518],{27382:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/30-days-of-ia","page":1,"postsPerPage":1,"totalPages":9,"totalCount":9,"nextPage":"/Cloud-Native/30daysofIA/tags/30-days-of-ia/page/2","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/6c2bf19a.02d06e0c.js b/assets/js/6c2bf19a.02d06e0c.js deleted file mode 100644 index 3b491b00dd..0000000000 --- a/assets/js/6c2bf19a.02d06e0c.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[48558],{53701:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/fall-for-ia/page/4","page":4,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/fall-for-ia/page/3","nextPage":"/Cloud-Native/30daysofIA/tags/fall-for-ia/page/5","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/6c2bf19a.d8a998d3.js b/assets/js/6c2bf19a.d8a998d3.js new file mode 100644 index 0000000000..144243e89b --- /dev/null +++ b/assets/js/6c2bf19a.d8a998d3.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[48558],{53701:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/fall-for-ia/page/4","page":4,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/fall-for-ia/page/3","nextPage":"/Cloud-Native/30daysofIA/tags/fall-for-ia/page/5","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/6c32d8c3.f1b34cea.js b/assets/js/6c32d8c3.21a56d1b.js similarity index 72% rename from assets/js/6c32d8c3.f1b34cea.js rename to assets/js/6c32d8c3.21a56d1b.js index 29df4d76b9..e7dddb7930 100644 --- a/assets/js/6c32d8c3.f1b34cea.js +++ b/assets/js/6c32d8c3.21a56d1b.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[60806],{82427:e=>{e.exports=JSON.parse('{"label":"ask-the-expert","permalink":"/Cloud-Native/30daysofIA/tags/ask-the-expert","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[60806],{82427:e=>{e.exports=JSON.parse('{"label":"ask-the-expert","permalink":"/Cloud-Native/30daysofIA/tags/ask-the-expert","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/6daa3851.858c1add.js b/assets/js/6daa3851.10a139a9.js similarity index 72% rename from assets/js/6daa3851.858c1add.js rename to assets/js/6daa3851.10a139a9.js index fa94c49bf7..54f4898ce7 100644 --- a/assets/js/6daa3851.858c1add.js +++ b/assets/js/6daa3851.10a139a9.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[60874],{82105:a=>{a.exports=JSON.parse('{"label":"github-actions","permalink":"/Cloud-Native/30daysofIA/tags/github-actions","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[60874],{82105:a=>{a.exports=JSON.parse('{"label":"github-actions","permalink":"/Cloud-Native/30daysofIA/tags/github-actions","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/6e52823d.2341626e.js b/assets/js/6e52823d.2341626e.js deleted file mode 100644 index 755716256d..0000000000 --- a/assets/js/6e52823d.2341626e.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[89828],{27853:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-openai/page/3","page":3,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/azure-openai/page/2","nextPage":"/Cloud-Native/30daysofIA/tags/azure-openai/page/4","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/6e52823d.e1847255.js b/assets/js/6e52823d.e1847255.js new file mode 100644 index 0000000000..9279c1a839 --- /dev/null +++ b/assets/js/6e52823d.e1847255.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[89828],{27853:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-openai/page/3","page":3,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/azure-openai/page/2","nextPage":"/Cloud-Native/30daysofIA/tags/azure-openai/page/4","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/6ed72a5d.5da27e48.js b/assets/js/6ed72a5d.5da27e48.js new file mode 100644 index 0000000000..dda0788b5a --- /dev/null +++ b/assets/js/6ed72a5d.5da27e48.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[28503],{3905:(e,t,a)=>{a.d(t,{Zo:()=>u,kt:()=>d});var n=a(67294);function i(e,t,a){return t in e?Object.defineProperty(e,t,{value:a,enumerable:!0,configurable:!0,writable:!0}):e[t]=a,e}function r(e,t){var a=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),a.push.apply(a,n)}return a}function o(e){for(var t=1;t=0||(i[a]=e[a]);return i}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,a)&&(i[a]=e[a])}return i}var l=n.createContext({}),p=function(e){var t=n.useContext(l),a=t;return e&&(a="function"==typeof e?e(t):o(o({},t),e)),a},u=function(e){var t=p(e.components);return n.createElement(l.Provider,{value:t},e.children)},c={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},g=n.forwardRef((function(e,t){var a=e.components,i=e.mdxType,r=e.originalType,l=e.parentName,u=s(e,["components","mdxType","originalType","parentName"]),g=p(a),d=i,h=g["".concat(l,".").concat(d)]||g[d]||c[d]||r;return a?n.createElement(h,o(o({ref:t},u),{},{components:a})):n.createElement(h,o({ref:t},u))}));function d(e,t){var a=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var r=a.length,o=new Array(r);o[0]=g;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s.mdxType="string"==typeof e?e:i,o[1]=s;for(var p=2;p{a.r(t),a.d(t,{assets:()=>l,contentTitle:()=>o,default:()=>c,frontMatter:()=>r,metadata:()=>s,toc:()=>p});var n=a(87462),i=(a(67294),a(3905));const r={date:"2023-09-21T09:00",slug:"how-digital-natives-leverage-generative-ai",title:"1-4. How Digital Natives leverage Generative AI",authors:["cnteam"],draft:!1,hide_table_of_contents:!1,toc_min_heading_level:2,toc_max_heading_level:3,keywords:["Cloud","Data","AI","AI/ML","intelligent apps","cloud-native","30-days","enterprise apps","digital experiences","app modernization","serverless","ai apps","data"],image:"https://azure.github.io/Cloud-Native/img/ogImage.png",description:"Article Description: This article explores how digital natives and Independent Software Vendors (ISVs) can transform their business operations and generate higher value for customers with generative AI and intelligent apps.",tags:["Fall-For-IA","30-days-of-IA","learn-live","hack-together","community-buzz","ask-the-expert","azure-kubernetes-service","azure-functions","azure-openai","azure-container-apps","azure-cosmos-db","github-copilot","github-codespaces","github-actions"]},o=void 0,s={permalink:"/Cloud-Native/30daysofIA/how-digital-natives-leverage-generative-ai",source:"@site/blog-30daysofIA/2023-09-21/how-digital-natives-leverage-generative-ai.md",title:"1-4. How Digital Natives leverage Generative AI",description:"Article Description: This article explores how digital natives and Independent Software Vendors (ISVs) can transform their business operations and generate higher value for customers with generative AI and intelligent apps.",date:"2023-09-21T09:00:00.000Z",formattedDate:"September 21, 2023",tags:[{label:"Fall-For-IA",permalink:"/Cloud-Native/30daysofIA/tags/fall-for-ia"},{label:"30-days-of-IA",permalink:"/Cloud-Native/30daysofIA/tags/30-days-of-ia"},{label:"learn-live",permalink:"/Cloud-Native/30daysofIA/tags/learn-live"},{label:"hack-together",permalink:"/Cloud-Native/30daysofIA/tags/hack-together"},{label:"community-buzz",permalink:"/Cloud-Native/30daysofIA/tags/community-buzz"},{label:"ask-the-expert",permalink:"/Cloud-Native/30daysofIA/tags/ask-the-expert"},{label:"azure-kubernetes-service",permalink:"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service"},{label:"azure-functions",permalink:"/Cloud-Native/30daysofIA/tags/azure-functions"},{label:"azure-openai",permalink:"/Cloud-Native/30daysofIA/tags/azure-openai"},{label:"azure-container-apps",permalink:"/Cloud-Native/30daysofIA/tags/azure-container-apps"},{label:"azure-cosmos-db",permalink:"/Cloud-Native/30daysofIA/tags/azure-cosmos-db"},{label:"github-copilot",permalink:"/Cloud-Native/30daysofIA/tags/github-copilot"},{label:"github-codespaces",permalink:"/Cloud-Native/30daysofIA/tags/github-codespaces"},{label:"github-actions",permalink:"/Cloud-Native/30daysofIA/tags/github-actions"}],readingTime:9.29,hasTruncateMarker:!1,authors:[{name:"It's 30DaysOfIA",title:"FallForIA Content Team",url:"https://azure.github.io/Cloud-Native/Fall-For-IA/",imageURL:"https://azure.github.io/Cloud-Native/img/logo-ms-cloud-native.png",key:"cnteam"}],frontMatter:{date:"2023-09-21T09:00",slug:"how-digital-natives-leverage-generative-ai",title:"1-4. How Digital Natives leverage Generative AI",authors:["cnteam"],draft:!1,hide_table_of_contents:!1,toc_min_heading_level:2,toc_max_heading_level:3,keywords:["Cloud","Data","AI","AI/ML","intelligent apps","cloud-native","30-days","enterprise apps","digital experiences","app modernization","serverless","ai apps","data"],image:"https://azure.github.io/Cloud-Native/img/ogImage.png",description:"Article Description: This article explores how digital natives and Independent Software Vendors (ISVs) can transform their business operations and generate higher value for customers with generative AI and intelligent apps.",tags:["Fall-For-IA","30-days-of-IA","learn-live","hack-together","community-buzz","ask-the-expert","azure-kubernetes-service","azure-functions","azure-openai","azure-container-apps","azure-cosmos-db","github-copilot","github-codespaces","github-actions"]},prevItem:{title:"2-2. Build Your First Intelligent App with Azure AI and AKS-2",permalink:"/Cloud-Native/30daysofIA/build-your-first-intelligent-app-with-azure-ai-and-aks-2"},nextItem:{title:"1-3. Reimagine App Development with AI",permalink:"/Cloud-Native/30daysofIA/reimagine-app-development-with-ai"}},l={authorsImageUrls:[void 0]},p=[{value:"What We'll Cover:",id:"what-well-cover",level:2},{value:"Unleashing Business Transformation: How Digital Natives Can Leverage Generative AI",id:"unleashing-business-transformation-how-digital-natives-can-leverage-generative-ai",level:2},{value:"Unpacking the Potential of Generative AI",id:"unpacking-the-potential-of-generative-ai",level:2},{value:"The Crucial Role of Generative AI in Business Operations",id:"the-crucial-role-of-generative-ai-in-business-operations",level:2},{value:"Navigating Challenges in the Generative AI Landscape",id:"navigating-challenges-in-the-generative-ai-landscape",level:2},{value:"Achieving Harmony: Integrating Generative AI with Existing Business Operations",id:"achieving-harmony-integrating-generative-ai-with-existing-business-operations",level:2},{value:"Conclusion",id:"conclusion",level:2},{value:"Exercise",id:"exercise",level:2}],u={toc:p};function c(e){let{components:t,...r}=e;return(0,i.kt)("wrapper",(0,n.Z)({},u,r,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("head",null,(0,i.kt)("meta",{property:"og:url",content:"https://azure.github.io/cloud-native/30daysofia/how-digital-natives-leverage-generative-ai"}),(0,i.kt)("meta",{property:"og:type",content:"website"}),(0,i.kt)("meta",{property:"og:title",content:"**Fall For Intelligent Apps! \ud83c\udf42| Build AI Apps On Azure"}),(0,i.kt)("meta",{property:"og:description",content:"Explore how digital natives and Independent Software Vendors (ISVs) can transform their business operations and generate higher value for customers with generative AI and intelligent apps."}),(0,i.kt)("meta",{property:"og:image",content:"https://azure.github.io/Cloud-Native/img/ogImage.png"}),(0,i.kt)("meta",{name:"twitter:url",content:"https://azure.github.io/Cloud-Native/30daysofIA/how-digital-natives-leverage-generative-ai"}),(0,i.kt)("meta",{name:"twitter:title",content:"**Fall For Intelligent Apps! \ud83c\udf42 | Build AI Apps On Azure"}),(0,i.kt)("meta",{name:"twitter:description",content:"1-4. Explore how digital natives and Independent Software Vendors (ISVs) can transform their business operations and generate higher value for customers with generative AI and intelligent apps."}),(0,i.kt)("meta",{name:"twitter:image",content:"https://azure.github.io/Cloud-Native/img/ogImage.png"}),(0,i.kt)("meta",{name:"twitter:card",content:"summary_large_image"}),(0,i.kt)("meta",{name:"twitter:creator",content:"@devanshidiaries"}),(0,i.kt)("meta",{name:"twitter:site",content:"@AzureAdvocates"}),(0,i.kt)("link",{rel:"canonical",href:"https://azure.github.io/Cloud-Native/30daysofIA/how-digital-natives-leverage-generative-ai"})),(0,i.kt)("p",null,"Explore how digital natives and Independent Software Vendors (ISVs) can transform their business operations and generate higher value for customers with generative AI and intelligent apps."),(0,i.kt)("h2",{id:"what-well-cover"},"What We'll Cover:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Potential of Generative AI in application development"),(0,i.kt)("li",{parentName:"ul"},"Challenges with Generative AI"),(0,i.kt)("li",{parentName:"ul"},"Integrating Generative AI with business operations")),(0,i.kt)("p",null,(0,i.kt)("img",{alt:"image of generative AI and intelligent apps used in business",src:a(65299).Z,width:"624",height:"380"})),(0,i.kt)("h2",{id:"unleashing-business-transformation-how-digital-natives-can-leverage-generative-ai"},"Unleashing Business Transformation: How Digital Natives Can Leverage Generative AI"),(0,i.kt)("p",null,"Generative AI is no longer a technological flight of fancy. It\u2019s here, and it\u2019s rewriting the rules of business innovation.\u202f "),(0,i.kt)("p",null,"This type of AI leverages machine learning algorithms to create new data with similar characteristics to a given dataset. It goes beyond text generation using generative pretrained transformer (GPT) models, covering an array of applications that span from image synthesis to translations and more. It also powers a new class of intelligent applications, which combine AI capabilities with traditional software functions for enhanced user experiences and advanced business solutions.\u202f "),(0,i.kt)("p",null,"Generative AI is transforming how digital natives and independent software vendors (ISVs) operate. Generative AI offers new possibilities for creating unique business value, enhancing customer experiences, and driving growth.\u202f "),(0,i.kt)("p",null,"It plays a critical role in applications, improving functionality, aiding decision-making, and automating complex tasks to deliver dynamic, uniquely personalized user experiences. Generative AI also enables and automates data-driven outcomes, functions, and features."),(0,i.kt)("p",null,"Integrating AI capabilities into customer-facing apps and turning them into intelligent apps benefits both the customer and the business, as we discussed in ",(0,i.kt)("a",{parentName:"p",href:"https://azure.github.io/Cloud-Native/30daysofIA/demystifying-intelligent-applications"},"Demystifying Intelligent Applications: Leveraging AI in App Development"),". You get faster product development workflows and better business intelligence for sales and marketing, while your users get more natural and context-aware features and interactions.\u202f "),(0,i.kt)("p",null,"That said, to realize generative AI\u2019s potential, you need some strategy and knowledge of how and where it fits into your current business and operations. We\u2019ll examine this technology\u2019s potential to transform business operations, its risks, and rewards, and how to integrate it into your existing operations.\u202f "),(0,i.kt)("h2",{id:"unpacking-the-potential-of-generative-ai"},"Unpacking the Potential of Generative AI"),(0,i.kt)("p",null,"Generative AI is creating new opportunities in the digital playing field. It empowers businesses to generate insights, design products, and automate processes in ways and at speeds that were previously unimaginable. You can now predict customer behavior more accurately, offer highly personalized user experiences, and prototype innovative ideas rapidly.\u202f "),(0,i.kt)("p",null,"For example, Microsoft used generative AI to reinvent web search in the new ",(0,i.kt)("a",{parentName:"p",href:"https://blogs.microsoft.com/blog/2023/02/07/reinventing-search-with-a-new-ai-powered-microsoft-bing-and-edge-your-copilot-for-the-web/"},"AI-powered Bing and Edge"),". The search experience has evolved from a solitary user flow of typing in keywords and scrolling through results. It\u2019s now a conversational experience where Bing uses context to offer better quality and more personalized search queries and results.\u202f "),(0,i.kt)("p",null,"Similarly, Microsoft harnessed generative AI to transform its productivity suite into intelligent apps. Through ",(0,i.kt)("a",{parentName:"p",href:"https://blogs.microsoft.com/blog/2023/03/16/introducing-microsoft-365-copilot-your-copilot-for-work/"},"Copilot for Microsoft 365"),", PowerPoint now empowers users to tell their stories by using DALL-E to generate images effortlessly. It also helps rephrase text-heavy slides to be more straightforward. Outlook now assists users to compose more effective emails, enabling better communication."),(0,i.kt)("p",null,"These are just some examples of how AI powers day-to-day work and operations.\u202f "),(0,i.kt)("p",null,"Businesses that ignore generative AI miss out on untapped market opportunities, letting competitors gain an edge in the digital race. By seeking ways to rethink assumptions and reinvent existing approaches, you can effectively use generative AI to create new opportunities, open products to new use cases and markets, unlock new growth and customer satisfaction levels, and increase your revenue."),(0,i.kt)("admonition",{type:"info"},(0,i.kt)("p",{parentName:"admonition"},"Register for the intelligent apps webinar on ",(0,i.kt)("a",{parentName:"p",href:"https://info.microsoft.com/ww-landing-driving-business-value-by-modernizing-with-cloud-native-and-ai.html?lcid=en-us?WT.mc_id=javascript-99907-ninarasi"},"Driving Business Value by Modernizing with Cloud-Native & AI")," with ",(0,i.kt)("em",{parentName:"p"},"Microsoft")," and ",(0,i.kt)("em",{parentName:"p"},"Forrester")," on ",(0,i.kt)("strong",{parentName:"p"},"September 26"),"."),(0,i.kt)("p",{parentName:"admonition"},"Explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries.\u202f ")),(0,i.kt)("h2",{id:"the-crucial-role-of-generative-ai-in-business-operations"},"The Crucial Role of Generative AI in Business Operations"),(0,i.kt)("p",null,"Generative AI can be a powerful catalyst for business transformation. Let\u2019s quickly tour some ways it can redefine business operations.\u202f"),(0,i.kt)("p",null,"Product development is traditionally a long and painstaking process. Now, tools like ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/features/copilot"},"GitHub Copilot")," and ",(0,i.kt)("a",{parentName:"p",href:"https://githubnext.com/projects/testpilot/"},"TestPilot")," use generative AI to make coding and testing software easier, faster, and more robust.\u202f "),(0,i.kt)("p",null,"Similarly, machine learning as a service technology, such as ",(0,i.kt)("a",{parentName:"p",href:"https://azure.microsoft.com/en-us/products/machine-learning?WT.mc_id=javascript-99907-ninarasi"},"Azure Machine Learning"),", democratizes the availability of machine learning. It helps collect, clean, and train models across datasets, making engineering and analyzing data more efficient and accessible.\u202f\u202f "),(0,i.kt)("p",null,"Generative AI also simplifies the process of creating intelligent apps. Previously, simulating thousands of prototypes, predicting their performance, and narrowing down to the most viable options could take weeks, months, or even years of iteration and data analysis. With generative AI, this process can be as quick as days, hours, or perhaps minutes.\u202f "),(0,i.kt)("p",null,"Customer experience is also ripe for transformation. Personalized recommendations drive engagement and sales, and generative AI can scale that personalization to every customer interaction using comprehensive and continually updated data. For instance, Microsoft leverages AI to enable personalization in ",(0,i.kt)("a",{parentName:"p",href:"https://dynamics.microsoft.com/en-us/ai/customer-insights/"},"Dynamics 365 Customer Insights"),". Using this tool, you can ask questions about your customers in natural language and gain valuable and actionable insights into your customer base.\u202f "),(0,i.kt)("p",null,"Lastly, marketing and sales can benefit from generative AI. For example, identifying current and future key markets and emerging trends helps companies stay ahead of the curve. Tools like ",(0,i.kt)("a",{parentName:"p",href:"https://powerbi.microsoft.com/en-us/blog/introducing-microsoft-fabric-and-copilot-in-microsoft-power-bi/"},"Copilot in Power BI")," help you quickly understand and uncover insights about your business, enabling your team to craft targeted campaigns, create engaging content, and optimize your business strategy.\u202f "),(0,i.kt)("p",null,"Leveraging this full generative AI potential requires the right tools. GPT-4, for instance, can be instrumental in realizing effective business transformation. It excels in understanding context, generating human-like text, and learning from minimal data input, making it a formidable tool in any digital native's arsenal. Platforms like ",(0,i.kt)("a",{parentName:"p",href:"https://azure.microsoft.com/en-us/products/ai-services/openai-service?WT.mc_id=javascript-99907-ninarasi"},"Azure OpenAI Service")," bring GPT-4\u2019s power to your fingertips."),(0,i.kt)("h2",{id:"navigating-challenges-in-the-generative-ai-landscape"},"Navigating Challenges in the Generative AI Landscape"),(0,i.kt)("p",null,"However, as with any powerful technology, it\u2019s unwise to treat generative AI as a perfect solution. You must know the challenges and risks of not leveraging it effectively, including technical and human-factor considerations.\u202f "),(0,i.kt)("p",null,"Without generative AI, your company risks missing the opportunity to please customers via personalization, as discussed previously. You\u2019ll miss sales opportunities via business intelligence insights, limiting your ability to grow revenue and keep up with the competition.\u202f "),(0,i.kt)("p",null,"One challenge of leveraging generative AI\u2019s full potential is finding the right tools. Fortunately, you can implement generative AI in your organization without investing in infrastructure, computational power, and expertise. Platforms like ",(0,i.kt)("a",{parentName:"p",href:"https://azure.microsoft.com/en-us/products/ai-services/openai-service?WT.mc_id=javascript-99907-ninarasi"},"Azure OpenAI Service")," and ",(0,i.kt)("a",{parentName:"p",href:"https://azure.microsoft.com/en-us/solutions/ai?WT.mc_id=javascript-99907-ninarasi"},"Azure AI Services")," make AI easily accessible. They also work with Azure compute and data services like\u202f",(0,i.kt)("a",{parentName:"p",href:"https://azure.microsoft.com/en-us/products/functions?WT.mc_id=javascript-99907-ninarasi"},"Azure Functions")," and ",(0,i.kt)("a",{parentName:"p",href:"https://azure.microsoft.com/en-us/products/kubernetes-service/?WT.mc_id=javascript-99907-ninarasi"},"Azure Kubernetes Service")," (AKS), enabling you to build intelligent apps easily and quickly.\u202f "),(0,i.kt)("p",null,"However, generative AI can\u2019t do everything. It may need human expertise and intervention to use it fully.\u202f "),(0,i.kt)("p",null,"For example, generative AI can create unique data. However, the data may have limited diversity and complexity. The AI may need help recognizing additional considerations or requirements specific to a problem. It may also lack context due to various factors, such as the information\u2019s freshness. Also, since it\u2019s an emerging field that\u2019s ",(0,i.kt)("a",{parentName:"p",href:"https://www.mckinsey.com/capabilities/mckinsey-digital/our-insights/the-economic-potential-of-generative-ai-the-next-productivity-frontier#key-insights"},"potentially worth $4.4 trillion"),", we\u2019re still learning about the nuances and ",(0,i.kt)("a",{parentName:"p",href:"https://techcrunch.com/2023/01/27/the-current-legal-cases-against-generative-ai-are-just-the-beginning/"},"potential regulatory impact")," of generative AI.\u202f "),(0,i.kt)("p",null,"Perhaps the most crucial and challenging aspect of adopting generative AI is an organization\u2019s required cultural shift. Your teams must learn how to use AI as your company fosters an understanding of AI\u2019s potential and limitations.\u202f "),(0,i.kt)("p",null,"Begin integrating AI literacy into your company\u2019s DNA. Knowledge of its potential and the readily available tools can inspire your teams to incorporate generative AI into their daily workflows and business operations quickly, improve productivity, and deliver superior customer experiences.\u202f "),(0,i.kt)("p",null,"No company wants to be outpaced by its competitors. You can balance generative AI\u2019s potential with your business operations by staying aware of and adapting to the dynamic AI landscape."),(0,i.kt)("h2",{id:"achieving-harmony-integrating-generative-ai-with-existing-business-operations"},"Achieving Harmony: Integrating Generative AI with Existing Business Operations"),(0,i.kt)("p",null,"Integrating generative AI into existing business operations doesn\u2019t have to be disruptive. With the correct strategy, you can do so seamlessly. Your goal should be to augment and enhance your operations, not disrupt and replace.\u202f "),(0,i.kt)("p",null,"For example, Microsoft Cloud technologies leveraged AI to ",(0,i.kt)("a",{parentName:"p",href:"https://www.microsoft.com/en-us/industry/blog/healthcare/2023/04/12/microsoft-cloud-for-healthcare-empowering-healthcare-to-deliver-meaningful-outcomes/?WT.mc_id=javascript-99907-ninarasi"},"relieve healthcare organizations\u2019 challenges"),", such as talent shortage and workforce burnout. Providers automated their workflows, invested in specific applications and connectors, and enhanced support for data and operations.\u202f "),(0,i.kt)("p",null,"Azure AI Services, such as ",(0,i.kt)("a",{parentName:"p",href:"https://learn.microsoft.com/en-us/azure/ai-services/language-service/text-analytics-for-health/overview?tabs=ner?WT.mc_id=javascript-99907-ninarasi"},"text analytics for health"),", enable healthcare organizations to understand their largely unstructured and untapped medical data. ",(0,i.kt)("a",{parentName:"p",href:"https://azure.microsoft.com/en-us/blog/announcing-project-health-insights-preview-advancing-ai-for-health-data/?WT.mc_id=javascript-99907-ninarasi"},"Project Health Insights")," leans on AI to help healthcare professionals gain actionable insights and inferences from patient data.\u202f "),(0,i.kt)("p",null,(0,i.kt)("a",{parentName:"p",href:"https://techcommunity.microsoft.com/t5/healthcare-and-life-sciences/extending-azure-health-bot-with-azure-openai-service/ba-p/3792560"},"Azure Health Bot"),", an Intelligent App, empowers organizations to build and deploy legally compliant, powerful conversational healthcare experiences to patients.\u202f "),(0,i.kt)("p",null,"A successful AI integration like this begins with understanding. Knowing what generative AI can do and setting realistic expectations can help you identify where and how AI can best serve your operations.\u202f "),(0,i.kt)("p",null,"Next, you can foster a culture of AI literacy across teams, speeding up adoption by helping them understand how it can aid their work. The more familiar your team is with AI and its possibilities, the smoother the transition. Training and education programs play a pivotal role here.\u202f "),(0,i.kt)("p",null,"Finally, integration requires a systematic approach. It\u2019s not about overhauling everything at once \u2014 it's about identifying areas where AI can add the most value, then gradually experimenting and scaling its use. Successful integration stories often feature a phased approach, starting small and growing steadily.\u202f "),(0,i.kt)("p",null,"As we move toward a future where generative AI is a staple in business operations, remember that every journey is unique. Although there\u2019s no one-size-fits-all solution, a strategic approach and a keen understanding of your business needs will help you navigate this new landscape successfully. "),(0,i.kt)("h2",{id:"conclusion"},"Conclusion"),(0,i.kt)("p",null,"Generative AI offers plenty of opportunities for businesses that embrace it. It\u2019s fueling the next generation of intelligent applications, transforming everything from product development to customer engagement to marketing. This technology is the key to unlocking unprecedented growth and innovation.\u202f "),(0,i.kt)("p",null,"While the journey may have challenges, the potential rewards make generative AI worth exploring. As generative AI advances, ",(0,i.kt)("a",{parentName:"p",href:"https://azure.microsoft.com/en-us/products/cognitive-services/#overview?WT.mc_id=javascript-99907-ninarasi"},"Azure AI"),"\u2019s broad range of tools and services will help you navigate this fascinating field."),(0,i.kt)("h2",{id:"exercise"},"Exercise"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Watch ",(0,i.kt)("strong",{parentName:"li"},(0,i.kt)("a",{parentName:"strong",href:"https://aka.ms/learnlive-contoso-app-deconstructed-Ep2"},"Episode 02"))," of the serverless edition Learn Live session to learn how to build, test and deploy an end-to-end intelligent app solution."),(0,i.kt)("li",{parentName:"ul"},"Complete the ",(0,i.kt)("strong",{parentName:"li"},(0,i.kt)("a",{parentName:"strong",href:"https://aka.ms/fallforIA/ai-csc"},"Cloud Skills Challenge"))," to build on your AI skills."),(0,i.kt)("li",{parentName:"ul"},"Register for ",(0,i.kt)("strong",{parentName:"li"},(0,i.kt)("a",{parentName:"strong",href:"https://aka.ms/FallForIA/ATE"},"Ask the Expert: Azure Functions"))," on September 26 for a live Q&A with the Product Team on how to build AI apps using serverless computing.")))}c.isMDXComponent=!0},65299:(e,t,a)=>{a.d(t,{Z:()=>n});const n=a.p+"assets/images/blog-image-1-f308cbcc3b29415a5135374b8370a7ca.png"}}]); \ No newline at end of file diff --git a/assets/js/6ed72a5d.875d7450.js b/assets/js/6ed72a5d.875d7450.js deleted file mode 100644 index a900bdc9c7..0000000000 --- a/assets/js/6ed72a5d.875d7450.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[28503],{3905:(e,t,a)=>{a.d(t,{Zo:()=>c,kt:()=>d});var n=a(67294);function i(e,t,a){return t in e?Object.defineProperty(e,t,{value:a,enumerable:!0,configurable:!0,writable:!0}):e[t]=a,e}function r(e,t){var a=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),a.push.apply(a,n)}return a}function o(e){for(var t=1;t=0||(i[a]=e[a]);return i}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,a)&&(i[a]=e[a])}return i}var l=n.createContext({}),p=function(e){var t=n.useContext(l),a=t;return e&&(a="function"==typeof e?e(t):o(o({},t),e)),a},c=function(e){var t=p(e.components);return n.createElement(l.Provider,{value:t},e.children)},u={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},g=n.forwardRef((function(e,t){var a=e.components,i=e.mdxType,r=e.originalType,l=e.parentName,c=s(e,["components","mdxType","originalType","parentName"]),g=p(a),d=i,h=g["".concat(l,".").concat(d)]||g[d]||u[d]||r;return a?n.createElement(h,o(o({ref:t},c),{},{components:a})):n.createElement(h,o({ref:t},c))}));function d(e,t){var a=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var r=a.length,o=new Array(r);o[0]=g;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s.mdxType="string"==typeof e?e:i,o[1]=s;for(var p=2;p{a.r(t),a.d(t,{assets:()=>l,contentTitle:()=>o,default:()=>u,frontMatter:()=>r,metadata:()=>s,toc:()=>p});var n=a(87462),i=(a(67294),a(3905));const r={date:"2023-09-21T09:00",slug:"how-digital-natives-leverage-generative-ai",title:"1-4. How Digital Natives leverage Generative AI",authors:["cnteam"],draft:!1,hide_table_of_contents:!1,toc_min_heading_level:2,toc_max_heading_level:3,keywords:["Cloud","Data","AI","AI/ML","intelligent apps","cloud-native","30-days","enterprise apps","digital experiences","app modernization","serverless","ai apps","data"],image:"https://azure.github.io/Cloud-Native/img/ogImage.png",description:"Article Description: This article explores how digital natives and Independent Software Vendors (ISVs) can transform their business operations and generate higher value for customers with generative AI and intelligent apps.",tags:["Fall-For-IA","30-days-of-IA","learn-live","hack-together","community-buzz","ask-the-expert","azure-kubernetes-service","azure-functions","azure-openai","azure-container-apps","azure-cosmos-db","github-copilot","github-codespaces","github-actions"]},o=void 0,s={permalink:"/Cloud-Native/30daysofIA/how-digital-natives-leverage-generative-ai",source:"@site/blog-30daysofIA/2023-09-21/how-digital-natives-leverage-generative-ai.md",title:"1-4. How Digital Natives leverage Generative AI",description:"Article Description: This article explores how digital natives and Independent Software Vendors (ISVs) can transform their business operations and generate higher value for customers with generative AI and intelligent apps.",date:"2023-09-21T09:00:00.000Z",formattedDate:"September 21, 2023",tags:[{label:"Fall-For-IA",permalink:"/Cloud-Native/30daysofIA/tags/fall-for-ia"},{label:"30-days-of-IA",permalink:"/Cloud-Native/30daysofIA/tags/30-days-of-ia"},{label:"learn-live",permalink:"/Cloud-Native/30daysofIA/tags/learn-live"},{label:"hack-together",permalink:"/Cloud-Native/30daysofIA/tags/hack-together"},{label:"community-buzz",permalink:"/Cloud-Native/30daysofIA/tags/community-buzz"},{label:"ask-the-expert",permalink:"/Cloud-Native/30daysofIA/tags/ask-the-expert"},{label:"azure-kubernetes-service",permalink:"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service"},{label:"azure-functions",permalink:"/Cloud-Native/30daysofIA/tags/azure-functions"},{label:"azure-openai",permalink:"/Cloud-Native/30daysofIA/tags/azure-openai"},{label:"azure-container-apps",permalink:"/Cloud-Native/30daysofIA/tags/azure-container-apps"},{label:"azure-cosmos-db",permalink:"/Cloud-Native/30daysofIA/tags/azure-cosmos-db"},{label:"github-copilot",permalink:"/Cloud-Native/30daysofIA/tags/github-copilot"},{label:"github-codespaces",permalink:"/Cloud-Native/30daysofIA/tags/github-codespaces"},{label:"github-actions",permalink:"/Cloud-Native/30daysofIA/tags/github-actions"}],readingTime:9.29,hasTruncateMarker:!1,authors:[{name:"It's 30DaysOfIA",title:"FallForIA Content Team",url:"https://azure.github.io/Cloud-Native/Fall-For-IA/",imageURL:"https://azure.github.io/Cloud-Native/img/logo-ms-cloud-native.png",key:"cnteam"}],frontMatter:{date:"2023-09-21T09:00",slug:"how-digital-natives-leverage-generative-ai",title:"1-4. How Digital Natives leverage Generative AI",authors:["cnteam"],draft:!1,hide_table_of_contents:!1,toc_min_heading_level:2,toc_max_heading_level:3,keywords:["Cloud","Data","AI","AI/ML","intelligent apps","cloud-native","30-days","enterprise apps","digital experiences","app modernization","serverless","ai apps","data"],image:"https://azure.github.io/Cloud-Native/img/ogImage.png",description:"Article Description: This article explores how digital natives and Independent Software Vendors (ISVs) can transform their business operations and generate higher value for customers with generative AI and intelligent apps.",tags:["Fall-For-IA","30-days-of-IA","learn-live","hack-together","community-buzz","ask-the-expert","azure-kubernetes-service","azure-functions","azure-openai","azure-container-apps","azure-cosmos-db","github-copilot","github-codespaces","github-actions"]},prevItem:{title:"1-5. Preparing the Path for Intelligent Apps",permalink:"/Cloud-Native/30daysofIA/preparing-the-path-for-intelligent-apps"},nextItem:{title:"1-3. Reimagine App Development with AI",permalink:"/Cloud-Native/30daysofIA/reimagine-app-development-with-ai"}},l={authorsImageUrls:[void 0]},p=[{value:"What We'll Cover:",id:"what-well-cover",level:2},{value:"Unleashing Business Transformation: How Digital Natives Can Leverage Generative AI",id:"unleashing-business-transformation-how-digital-natives-can-leverage-generative-ai",level:2},{value:"Unpacking the Potential of Generative AI",id:"unpacking-the-potential-of-generative-ai",level:2},{value:"The Crucial Role of Generative AI in Business Operations",id:"the-crucial-role-of-generative-ai-in-business-operations",level:2},{value:"Navigating Challenges in the Generative AI Landscape",id:"navigating-challenges-in-the-generative-ai-landscape",level:2},{value:"Achieving Harmony: Integrating Generative AI with Existing Business Operations",id:"achieving-harmony-integrating-generative-ai-with-existing-business-operations",level:2},{value:"Conclusion",id:"conclusion",level:2},{value:"Exercise",id:"exercise",level:2}],c={toc:p};function u(e){let{components:t,...r}=e;return(0,i.kt)("wrapper",(0,n.Z)({},c,r,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("head",null,(0,i.kt)("meta",{property:"og:url",content:"https://azure.github.io/cloud-native/30daysofia/how-digital-natives-leverage-generative-ai"}),(0,i.kt)("meta",{property:"og:type",content:"website"}),(0,i.kt)("meta",{property:"og:title",content:"**Fall For Intelligent Apps! \ud83c\udf42| Build AI Apps On Azure"}),(0,i.kt)("meta",{property:"og:description",content:"Explore how digital natives and Independent Software Vendors (ISVs) can transform their business operations and generate higher value for customers with generative AI and intelligent apps."}),(0,i.kt)("meta",{property:"og:image",content:"https://azure.github.io/Cloud-Native/img/ogImage.png"}),(0,i.kt)("meta",{name:"twitter:url",content:"https://azure.github.io/Cloud-Native/30daysofIA/how-digital-natives-leverage-generative-ai"}),(0,i.kt)("meta",{name:"twitter:title",content:"**Fall For Intelligent Apps! \ud83c\udf42 | Build AI Apps On Azure"}),(0,i.kt)("meta",{name:"twitter:description",content:"1-4. Explore how digital natives and Independent Software Vendors (ISVs) can transform their business operations and generate higher value for customers with generative AI and intelligent apps."}),(0,i.kt)("meta",{name:"twitter:image",content:"https://azure.github.io/Cloud-Native/img/ogImage.png"}),(0,i.kt)("meta",{name:"twitter:card",content:"summary_large_image"}),(0,i.kt)("meta",{name:"twitter:creator",content:"@devanshidiaries"}),(0,i.kt)("meta",{name:"twitter:site",content:"@AzureAdvocates"}),(0,i.kt)("link",{rel:"canonical",href:"https://azure.github.io/Cloud-Native/30daysofIA/how-digital-natives-leverage-generative-ai"})),(0,i.kt)("p",null,"Explore how digital natives and Independent Software Vendors (ISVs) can transform their business operations and generate higher value for customers with generative AI and intelligent apps."),(0,i.kt)("h2",{id:"what-well-cover"},"What We'll Cover:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Potential of Generative AI in application development"),(0,i.kt)("li",{parentName:"ul"},"Challenges with Generative AI"),(0,i.kt)("li",{parentName:"ul"},"Integrating Generative AI with business operations")),(0,i.kt)("p",null,(0,i.kt)("img",{alt:"image of generative AI and intelligent apps used in business",src:a(65299).Z,width:"624",height:"380"})),(0,i.kt)("h2",{id:"unleashing-business-transformation-how-digital-natives-can-leverage-generative-ai"},"Unleashing Business Transformation: How Digital Natives Can Leverage Generative AI"),(0,i.kt)("p",null,"Generative AI is no longer a technological flight of fancy. It\u2019s here, and it\u2019s rewriting the rules of business innovation.\u202f "),(0,i.kt)("p",null,"This type of AI leverages machine learning algorithms to create new data with similar characteristics to a given dataset. It goes beyond text generation using generative pretrained transformer (GPT) models, covering an array of applications that span from image synthesis to translations and more. It also powers a new class of intelligent applications, which combine AI capabilities with traditional software functions for enhanced user experiences and advanced business solutions.\u202f "),(0,i.kt)("p",null,"Generative AI is transforming how digital natives and independent software vendors (ISVs) operate. Generative AI offers new possibilities for creating unique business value, enhancing customer experiences, and driving growth.\u202f "),(0,i.kt)("p",null,"It plays a critical role in applications, improving functionality, aiding decision-making, and automating complex tasks to deliver dynamic, uniquely personalized user experiences. Generative AI also enables and automates data-driven outcomes, functions, and features."),(0,i.kt)("p",null,"Integrating AI capabilities into customer-facing apps and turning them into intelligent apps benefits both the customer and the business, as we discussed in ",(0,i.kt)("a",{parentName:"p",href:"https://azure.github.io/Cloud-Native/30daysofIA/demystifying-intelligent-applications"},"Demystifying Intelligent Applications: Leveraging AI in App Development"),". You get faster product development workflows and better business intelligence for sales and marketing, while your users get more natural and context-aware features and interactions.\u202f "),(0,i.kt)("p",null,"That said, to realize generative AI\u2019s potential, you need some strategy and knowledge of how and where it fits into your current business and operations. We\u2019ll examine this technology\u2019s potential to transform business operations, its risks, and rewards, and how to integrate it into your existing operations.\u202f "),(0,i.kt)("h2",{id:"unpacking-the-potential-of-generative-ai"},"Unpacking the Potential of Generative AI"),(0,i.kt)("p",null,"Generative AI is creating new opportunities in the digital playing field. It empowers businesses to generate insights, design products, and automate processes in ways and at speeds that were previously unimaginable. You can now predict customer behavior more accurately, offer highly personalized user experiences, and prototype innovative ideas rapidly.\u202f "),(0,i.kt)("p",null,"For example, Microsoft used generative AI to reinvent web search in the new ",(0,i.kt)("a",{parentName:"p",href:"https://blogs.microsoft.com/blog/2023/02/07/reinventing-search-with-a-new-ai-powered-microsoft-bing-and-edge-your-copilot-for-the-web/"},"AI-powered Bing and Edge"),". The search experience has evolved from a solitary user flow of typing in keywords and scrolling through results. It\u2019s now a conversational experience where Bing uses context to offer better quality and more personalized search queries and results.\u202f "),(0,i.kt)("p",null,"Similarly, Microsoft harnessed generative AI to transform its productivity suite into intelligent apps. Through ",(0,i.kt)("a",{parentName:"p",href:"https://blogs.microsoft.com/blog/2023/03/16/introducing-microsoft-365-copilot-your-copilot-for-work/"},"Copilot for Microsoft 365"),", PowerPoint now empowers users to tell their stories by using DALL-E to generate images effortlessly. It also helps rephrase text-heavy slides to be more straightforward. Outlook now assists users to compose more effective emails, enabling better communication."),(0,i.kt)("p",null,"These are just some examples of how AI powers day-to-day work and operations.\u202f "),(0,i.kt)("p",null,"Businesses that ignore generative AI miss out on untapped market opportunities, letting competitors gain an edge in the digital race. By seeking ways to rethink assumptions and reinvent existing approaches, you can effectively use generative AI to create new opportunities, open products to new use cases and markets, unlock new growth and customer satisfaction levels, and increase your revenue."),(0,i.kt)("admonition",{type:"info"},(0,i.kt)("p",{parentName:"admonition"},"Register for the intelligent apps webinar on ",(0,i.kt)("a",{parentName:"p",href:"https://info.microsoft.com/ww-landing-driving-business-value-by-modernizing-with-cloud-native-and-ai.html?lcid=en-us?WT.mc_id=javascript-99907-ninarasi"},"Driving Business Value by Modernizing with Cloud-Native & AI")," with ",(0,i.kt)("em",{parentName:"p"},"Microsoft")," and ",(0,i.kt)("em",{parentName:"p"},"Forrester")," on ",(0,i.kt)("strong",{parentName:"p"},"September 26"),"."),(0,i.kt)("p",{parentName:"admonition"},"Explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries.\u202f ")),(0,i.kt)("h2",{id:"the-crucial-role-of-generative-ai-in-business-operations"},"The Crucial Role of Generative AI in Business Operations"),(0,i.kt)("p",null,"Generative AI can be a powerful catalyst for business transformation. Let\u2019s quickly tour some ways it can redefine business operations.\u202f"),(0,i.kt)("p",null,"Product development is traditionally a long and painstaking process. Now, tools like ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/features/copilot"},"GitHub Copilot")," and ",(0,i.kt)("a",{parentName:"p",href:"https://githubnext.com/projects/testpilot/"},"TestPilot")," use generative AI to make coding and testing software easier, faster, and more robust.\u202f "),(0,i.kt)("p",null,"Similarly, machine learning as a service technology, such as ",(0,i.kt)("a",{parentName:"p",href:"https://azure.microsoft.com/en-us/products/machine-learning?WT.mc_id=javascript-99907-ninarasi"},"Azure Machine Learning"),", democratizes the availability of machine learning. It helps collect, clean, and train models across datasets, making engineering and analyzing data more efficient and accessible.\u202f\u202f "),(0,i.kt)("p",null,"Generative AI also simplifies the process of creating intelligent apps. Previously, simulating thousands of prototypes, predicting their performance, and narrowing down to the most viable options could take weeks, months, or even years of iteration and data analysis. With generative AI, this process can be as quick as days, hours, or perhaps minutes.\u202f "),(0,i.kt)("p",null,"Customer experience is also ripe for transformation. Personalized recommendations drive engagement and sales, and generative AI can scale that personalization to every customer interaction using comprehensive and continually updated data. For instance, Microsoft leverages AI to enable personalization in ",(0,i.kt)("a",{parentName:"p",href:"https://dynamics.microsoft.com/en-us/ai/customer-insights/"},"Dynamics 365 Customer Insights"),". Using this tool, you can ask questions about your customers in natural language and gain valuable and actionable insights into your customer base.\u202f "),(0,i.kt)("p",null,"Lastly, marketing and sales can benefit from generative AI. For example, identifying current and future key markets and emerging trends helps companies stay ahead of the curve. Tools like ",(0,i.kt)("a",{parentName:"p",href:"https://powerbi.microsoft.com/en-us/blog/introducing-microsoft-fabric-and-copilot-in-microsoft-power-bi/"},"Copilot in Power BI")," help you quickly understand and uncover insights about your business, enabling your team to craft targeted campaigns, create engaging content, and optimize your business strategy.\u202f "),(0,i.kt)("p",null,"Leveraging this full generative AI potential requires the right tools. GPT-4, for instance, can be instrumental in realizing effective business transformation. It excels in understanding context, generating human-like text, and learning from minimal data input, making it a formidable tool in any digital native's arsenal. Platforms like ",(0,i.kt)("a",{parentName:"p",href:"https://azure.microsoft.com/en-us/products/ai-services/openai-service?WT.mc_id=javascript-99907-ninarasi"},"Azure OpenAI Service")," bring GPT-4\u2019s power to your fingertips."),(0,i.kt)("h2",{id:"navigating-challenges-in-the-generative-ai-landscape"},"Navigating Challenges in the Generative AI Landscape"),(0,i.kt)("p",null,"However, as with any powerful technology, it\u2019s unwise to treat generative AI as a perfect solution. You must know the challenges and risks of not leveraging it effectively, including technical and human-factor considerations.\u202f "),(0,i.kt)("p",null,"Without generative AI, your company risks missing the opportunity to please customers via personalization, as discussed previously. You\u2019ll miss sales opportunities via business intelligence insights, limiting your ability to grow revenue and keep up with the competition.\u202f "),(0,i.kt)("p",null,"One challenge of leveraging generative AI\u2019s full potential is finding the right tools. Fortunately, you can implement generative AI in your organization without investing in infrastructure, computational power, and expertise. Platforms like ",(0,i.kt)("a",{parentName:"p",href:"https://azure.microsoft.com/en-us/products/ai-services/openai-service?WT.mc_id=javascript-99907-ninarasi"},"Azure OpenAI Service")," and ",(0,i.kt)("a",{parentName:"p",href:"https://azure.microsoft.com/en-us/solutions/ai?WT.mc_id=javascript-99907-ninarasi"},"Azure AI Services")," make AI easily accessible. They also work with Azure compute and data services like\u202f",(0,i.kt)("a",{parentName:"p",href:"https://azure.microsoft.com/en-us/products/functions?WT.mc_id=javascript-99907-ninarasi"},"Azure Functions")," and ",(0,i.kt)("a",{parentName:"p",href:"https://azure.microsoft.com/en-us/products/kubernetes-service/?WT.mc_id=javascript-99907-ninarasi"},"Azure Kubernetes Service")," (AKS), enabling you to build intelligent apps easily and quickly.\u202f "),(0,i.kt)("p",null,"However, generative AI can\u2019t do everything. It may need human expertise and intervention to use it fully.\u202f "),(0,i.kt)("p",null,"For example, generative AI can create unique data. However, the data may have limited diversity and complexity. The AI may need help recognizing additional considerations or requirements specific to a problem. It may also lack context due to various factors, such as the information\u2019s freshness. Also, since it\u2019s an emerging field that\u2019s ",(0,i.kt)("a",{parentName:"p",href:"https://www.mckinsey.com/capabilities/mckinsey-digital/our-insights/the-economic-potential-of-generative-ai-the-next-productivity-frontier#key-insights"},"potentially worth $4.4 trillion"),", we\u2019re still learning about the nuances and ",(0,i.kt)("a",{parentName:"p",href:"https://techcrunch.com/2023/01/27/the-current-legal-cases-against-generative-ai-are-just-the-beginning/"},"potential regulatory impact")," of generative AI.\u202f "),(0,i.kt)("p",null,"Perhaps the most crucial and challenging aspect of adopting generative AI is an organization\u2019s required cultural shift. Your teams must learn how to use AI as your company fosters an understanding of AI\u2019s potential and limitations.\u202f "),(0,i.kt)("p",null,"Begin integrating AI literacy into your company\u2019s DNA. Knowledge of its potential and the readily available tools can inspire your teams to incorporate generative AI into their daily workflows and business operations quickly, improve productivity, and deliver superior customer experiences.\u202f "),(0,i.kt)("p",null,"No company wants to be outpaced by its competitors. You can balance generative AI\u2019s potential with your business operations by staying aware of and adapting to the dynamic AI landscape."),(0,i.kt)("h2",{id:"achieving-harmony-integrating-generative-ai-with-existing-business-operations"},"Achieving Harmony: Integrating Generative AI with Existing Business Operations"),(0,i.kt)("p",null,"Integrating generative AI into existing business operations doesn\u2019t have to be disruptive. With the correct strategy, you can do so seamlessly. Your goal should be to augment and enhance your operations, not disrupt and replace.\u202f "),(0,i.kt)("p",null,"For example, Microsoft Cloud technologies leveraged AI to ",(0,i.kt)("a",{parentName:"p",href:"https://www.microsoft.com/en-us/industry/blog/healthcare/2023/04/12/microsoft-cloud-for-healthcare-empowering-healthcare-to-deliver-meaningful-outcomes/?WT.mc_id=javascript-99907-ninarasi"},"relieve healthcare organizations\u2019 challenges"),", such as talent shortage and workforce burnout. Providers automated their workflows, invested in specific applications and connectors, and enhanced support for data and operations.\u202f "),(0,i.kt)("p",null,"Azure AI Services, such as ",(0,i.kt)("a",{parentName:"p",href:"https://learn.microsoft.com/en-us/azure/ai-services/language-service/text-analytics-for-health/overview?tabs=ner?WT.mc_id=javascript-99907-ninarasi"},"text analytics for health"),", enable healthcare organizations to understand their largely unstructured and untapped medical data. ",(0,i.kt)("a",{parentName:"p",href:"https://azure.microsoft.com/en-us/blog/announcing-project-health-insights-preview-advancing-ai-for-health-data/?WT.mc_id=javascript-99907-ninarasi"},"Project Health Insights")," leans on AI to help healthcare professionals gain actionable insights and inferences from patient data.\u202f "),(0,i.kt)("p",null,(0,i.kt)("a",{parentName:"p",href:"https://techcommunity.microsoft.com/t5/healthcare-and-life-sciences/extending-azure-health-bot-with-azure-openai-service/ba-p/3792560"},"Azure Health Bot"),", an Intelligent App, empowers organizations to build and deploy legally compliant, powerful conversational healthcare experiences to patients.\u202f "),(0,i.kt)("p",null,"A successful AI integration like this begins with understanding. Knowing what generative AI can do and setting realistic expectations can help you identify where and how AI can best serve your operations.\u202f "),(0,i.kt)("p",null,"Next, you can foster a culture of AI literacy across teams, speeding up adoption by helping them understand how it can aid their work. The more familiar your team is with AI and its possibilities, the smoother the transition. Training and education programs play a pivotal role here.\u202f "),(0,i.kt)("p",null,"Finally, integration requires a systematic approach. It\u2019s not about overhauling everything at once \u2014 it's about identifying areas where AI can add the most value, then gradually experimenting and scaling its use. Successful integration stories often feature a phased approach, starting small and growing steadily.\u202f "),(0,i.kt)("p",null,"As we move toward a future where generative AI is a staple in business operations, remember that every journey is unique. Although there\u2019s no one-size-fits-all solution, a strategic approach and a keen understanding of your business needs will help you navigate this new landscape successfully. "),(0,i.kt)("h2",{id:"conclusion"},"Conclusion"),(0,i.kt)("p",null,"Generative AI offers plenty of opportunities for businesses that embrace it. It\u2019s fueling the next generation of intelligent applications, transforming everything from product development to customer engagement to marketing. This technology is the key to unlocking unprecedented growth and innovation.\u202f "),(0,i.kt)("p",null,"While the journey may have challenges, the potential rewards make generative AI worth exploring. As generative AI advances, ",(0,i.kt)("a",{parentName:"p",href:"https://azure.microsoft.com/en-us/products/cognitive-services/#overview?WT.mc_id=javascript-99907-ninarasi"},"Azure AI"),"\u2019s broad range of tools and services will help you navigate this fascinating field."),(0,i.kt)("h2",{id:"exercise"},"Exercise"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Watch ",(0,i.kt)("strong",{parentName:"li"},(0,i.kt)("a",{parentName:"strong",href:"https://aka.ms/learnlive-contoso-app-deconstructed-Ep2"},"Episode 02"))," of the serverless edition Learn Live session to learn how to build, test and deploy an end-to-end intelligent app solution."),(0,i.kt)("li",{parentName:"ul"},"Complete the ",(0,i.kt)("strong",{parentName:"li"},(0,i.kt)("a",{parentName:"strong",href:"https://aka.ms/fallforIA/ai-csc"},"Cloud Skills Challenge"))," to build on your AI skills."),(0,i.kt)("li",{parentName:"ul"},"Register for ",(0,i.kt)("strong",{parentName:"li"},(0,i.kt)("a",{parentName:"strong",href:"https://aka.ms/FallForIA/ATE"},"Ask the Expert: Azure Functions"))," on September 26 for a live Q&A with the Product Team on how to build AI apps using serverless computing.")))}u.isMDXComponent=!0},65299:(e,t,a)=>{a.d(t,{Z:()=>n});const n=a.p+"assets/images/blog-image-1-f308cbcc3b29415a5135374b8370a7ca.png"}}]); \ No newline at end of file diff --git a/assets/js/6f46b4a6.62e0f8b1.js b/assets/js/6f46b4a6.62e0f8b1.js new file mode 100644 index 0000000000..0b4e70cca5 --- /dev/null +++ b/assets/js/6f46b4a6.62e0f8b1.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[34872],{52636:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/community-buzz/page/4","page":4,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/community-buzz/page/3","nextPage":"/Cloud-Native/30daysofIA/tags/community-buzz/page/5","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/6f46b4a6.c8bfd2ea.js b/assets/js/6f46b4a6.c8bfd2ea.js deleted file mode 100644 index 04b2be5e8c..0000000000 --- a/assets/js/6f46b4a6.c8bfd2ea.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[34872],{52636:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/community-buzz/page/4","page":4,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/community-buzz/page/3","nextPage":"/Cloud-Native/30daysofIA/tags/community-buzz/page/5","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/6f976579.e157b073.js b/assets/js/6f976579.e157b073.js deleted file mode 100644 index 4e76700656..0000000000 --- a/assets/js/6f976579.e157b073.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[17824],{93935:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/30-days-of-ia/page/8","page":8,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/30-days-of-ia/page/7","nextPage":"/Cloud-Native/30daysofIA/tags/30-days-of-ia/page/9","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/6f976579.fb99fe0b.js b/assets/js/6f976579.fb99fe0b.js new file mode 100644 index 0000000000..0cd86a4013 --- /dev/null +++ b/assets/js/6f976579.fb99fe0b.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[17824],{93935:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/30-days-of-ia/page/8","page":8,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/30-days-of-ia/page/7","nextPage":"/Cloud-Native/30daysofIA/tags/30-days-of-ia/page/9","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/6fa36db2.c30e6149.js b/assets/js/6fa36db2.f9495fe1.js similarity index 92% rename from assets/js/6fa36db2.c30e6149.js rename to assets/js/6fa36db2.f9495fe1.js index 5b096d3b9a..cc005cecf1 100644 --- a/assets/js/6fa36db2.c30e6149.js +++ b/assets/js/6fa36db2.f9495fe1.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[9885],{85746:a=>{a.exports=JSON.parse('{"label":"azure-container-apps","permalink":"/Cloud-Native/30daysofIA/tags/azure-container-apps","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[9885],{85746:a=>{a.exports=JSON.parse('{"label":"azure-container-apps","permalink":"/Cloud-Native/30daysofIA/tags/azure-container-apps","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/6fc59d81.5dc3c211.js b/assets/js/6fc59d81.5dc3c211.js new file mode 100644 index 0000000000..3fef005c06 --- /dev/null +++ b/assets/js/6fc59d81.5dc3c211.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[4243],{59842:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-functions/page/11","page":11,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/azure-functions/page/10","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/7166f236.0de6d3c9.js b/assets/js/7166f236.99c6f063.js similarity index 92% rename from assets/js/7166f236.0de6d3c9.js rename to assets/js/7166f236.99c6f063.js index 04dba56c2b..ea7d9ea320 100644 --- a/assets/js/7166f236.0de6d3c9.js +++ b/assets/js/7166f236.99c6f063.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[98161],{54595:a=>{a.exports=JSON.parse('{"label":"azure-container-apps","permalink":"/Cloud-Native/30daysofIA/tags/azure-container-apps","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[98161],{54595:a=>{a.exports=JSON.parse('{"label":"azure-container-apps","permalink":"/Cloud-Native/30daysofIA/tags/azure-container-apps","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/737c62d3.27e16507.js b/assets/js/737c62d3.27e16507.js deleted file mode 100644 index c43bae107d..0000000000 --- a/assets/js/737c62d3.27e16507.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[33472],{78337:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/learn-live/page/7","page":7,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/learn-live/page/6","nextPage":"/Cloud-Native/30daysofIA/tags/learn-live/page/8","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/737c62d3.b70b1402.js b/assets/js/737c62d3.b70b1402.js new file mode 100644 index 0000000000..2a6ddbc267 --- /dev/null +++ b/assets/js/737c62d3.b70b1402.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[33472],{78337:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/learn-live/page/7","page":7,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/learn-live/page/6","nextPage":"/Cloud-Native/30daysofIA/tags/learn-live/page/8","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/74c62dc2.662efd6c.js b/assets/js/74c62dc2.662efd6c.js new file mode 100644 index 0000000000..663a638967 --- /dev/null +++ b/assets/js/74c62dc2.662efd6c.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[61969],{29411:e=>{e.exports=JSON.parse('{"label":"github-copilot","permalink":"/Cloud-Native/30daysofIA/tags/github-copilot","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/75acfd48.4c718337.js b/assets/js/75acfd48.4c718337.js deleted file mode 100644 index 75f3a19088..0000000000 --- a/assets/js/75acfd48.4c718337.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[64891],{94871:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/learn-live/page/8","page":8,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/learn-live/page/7","nextPage":"/Cloud-Native/30daysofIA/tags/learn-live/page/9","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/75acfd48.d5b75f35.js b/assets/js/75acfd48.d5b75f35.js new file mode 100644 index 0000000000..a642f51d93 --- /dev/null +++ b/assets/js/75acfd48.d5b75f35.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[64891],{94871:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/learn-live/page/8","page":8,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/learn-live/page/7","nextPage":"/Cloud-Native/30daysofIA/tags/learn-live/page/9","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/782e7d27.9d79171a.js b/assets/js/782e7d27.8e79bd2a.js similarity index 73% rename from assets/js/782e7d27.9d79171a.js rename to assets/js/782e7d27.8e79bd2a.js index 09f9778c01..f80c817854 100644 --- a/assets/js/782e7d27.9d79171a.js +++ b/assets/js/782e7d27.8e79bd2a.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[94409],{97711:e=>{e.exports=JSON.parse('{"label":"azure-openai","permalink":"/Cloud-Native/30daysofIA/tags/azure-openai","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[94409],{97711:e=>{e.exports=JSON.parse('{"label":"azure-openai","permalink":"/Cloud-Native/30daysofIA/tags/azure-openai","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/79170c02.e6a1b62c.js b/assets/js/79170c02.e6a1b62c.js deleted file mode 100644 index 95cd9c66c9..0000000000 --- a/assets/js/79170c02.e6a1b62c.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[84316],{54200:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/learn-live/page/3","page":3,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/learn-live/page/2","nextPage":"/Cloud-Native/30daysofIA/tags/learn-live/page/4","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/79170c02.edfc67cb.js b/assets/js/79170c02.edfc67cb.js new file mode 100644 index 0000000000..25a6e1057d --- /dev/null +++ b/assets/js/79170c02.edfc67cb.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[84316],{54200:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/learn-live/page/3","page":3,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/learn-live/page/2","nextPage":"/Cloud-Native/30daysofIA/tags/learn-live/page/4","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/7b7d445f.3c029ac8.js b/assets/js/7b7d445f.7de464c0.js similarity index 73% rename from assets/js/7b7d445f.3c029ac8.js rename to assets/js/7b7d445f.7de464c0.js index 8017ef0ef5..ee072f7c0c 100644 --- a/assets/js/7b7d445f.3c029ac8.js +++ b/assets/js/7b7d445f.7de464c0.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[17658],{7479:e=>{e.exports=JSON.parse('{"label":"azure-openai","permalink":"/Cloud-Native/30daysofIA/tags/azure-openai","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[17658],{7479:e=>{e.exports=JSON.parse('{"label":"azure-openai","permalink":"/Cloud-Native/30daysofIA/tags/azure-openai","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/7bd0b9bf.c472bb0f.js b/assets/js/7bd0b9bf.d41919bb.js similarity index 74% rename from assets/js/7bd0b9bf.c472bb0f.js rename to assets/js/7bd0b9bf.d41919bb.js index fd20476080..fada33f74b 100644 --- a/assets/js/7bd0b9bf.c472bb0f.js +++ b/assets/js/7bd0b9bf.d41919bb.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[48157],{84234:a=>{a.exports=JSON.parse('{"label":"Fall-For-IA","permalink":"/Cloud-Native/30daysofIA/tags/fall-for-ia","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[48157],{84234:a=>{a.exports=JSON.parse('{"label":"Fall-For-IA","permalink":"/Cloud-Native/30daysofIA/tags/fall-for-ia","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/7c838aa5.1d0ac9fa.js b/assets/js/7c838aa5.1d0ac9fa.js new file mode 100644 index 0000000000..62003c9521 --- /dev/null +++ b/assets/js/7c838aa5.1d0ac9fa.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[207],{38382:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-container-apps/page/8","page":8,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/azure-container-apps/page/7","nextPage":"/Cloud-Native/30daysofIA/tags/azure-container-apps/page/9","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/7c838aa5.a104c113.js b/assets/js/7c838aa5.a104c113.js deleted file mode 100644 index 6ddad7eacb..0000000000 --- a/assets/js/7c838aa5.a104c113.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[207],{38382:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-container-apps/page/8","page":8,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/azure-container-apps/page/7","nextPage":"/Cloud-Native/30daysofIA/tags/azure-container-apps/page/9","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/7d20b517.bda2cf93.js b/assets/js/7d20b517.bda2cf93.js new file mode 100644 index 0000000000..32fd7da79a --- /dev/null +++ b/assets/js/7d20b517.bda2cf93.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[50928],{13564:a=>{a.exports=JSON.parse('{"label":"azure-container-apps","permalink":"/Cloud-Native/30daysofIA/tags/azure-container-apps","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/7d4a7cb7.d43df8ec.js b/assets/js/7d4a7cb7.7672322c.js similarity index 72% rename from assets/js/7d4a7cb7.d43df8ec.js rename to assets/js/7d4a7cb7.7672322c.js index 64997951d2..dd072e9ccf 100644 --- a/assets/js/7d4a7cb7.d43df8ec.js +++ b/assets/js/7d4a7cb7.7672322c.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[53346],{69812:a=>{a.exports=JSON.parse('{"label":"github-actions","permalink":"/Cloud-Native/30daysofIA/tags/github-actions","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[53346],{69812:a=>{a.exports=JSON.parse('{"label":"github-actions","permalink":"/Cloud-Native/30daysofIA/tags/github-actions","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/7dba6303.c2fef4a8.js b/assets/js/7dba6303.c2fef4a8.js new file mode 100644 index 0000000000..aa924e7168 --- /dev/null +++ b/assets/js/7dba6303.c2fef4a8.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[50321],{27069:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/30-days-of-ia/page/7","page":7,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/30-days-of-ia/page/6","nextPage":"/Cloud-Native/30daysofIA/tags/30-days-of-ia/page/8","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/7dba6303.dd59dd94.js b/assets/js/7dba6303.dd59dd94.js deleted file mode 100644 index bd8bfea1da..0000000000 --- a/assets/js/7dba6303.dd59dd94.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[50321],{27069:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/30-days-of-ia/page/7","page":7,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/30-days-of-ia/page/6","nextPage":"/Cloud-Native/30daysofIA/tags/30-days-of-ia/page/8","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/7e3c7a3f.95768422.js b/assets/js/7e3c7a3f.5f562377.js similarity index 89% rename from assets/js/7e3c7a3f.95768422.js rename to assets/js/7e3c7a3f.5f562377.js index 37c00c91fe..2fab9e8347 100644 --- a/assets/js/7e3c7a3f.95768422.js +++ b/assets/js/7e3c7a3f.5f562377.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[19253],{63273:e=>{e.exports=JSON.parse('{"label":"azure-kubernetes-service","permalink":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[19253],{63273:e=>{e.exports=JSON.parse('{"label":"azure-kubernetes-service","permalink":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/807349ce.1a6a0103.js b/assets/js/807349ce.2f17bbe8.js similarity index 72% rename from assets/js/807349ce.1a6a0103.js rename to assets/js/807349ce.2f17bbe8.js index 97d073e3bd..c1affcb8be 100644 --- a/assets/js/807349ce.1a6a0103.js +++ b/assets/js/807349ce.2f17bbe8.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[38232],{36484:e=>{e.exports=JSON.parse('{"label":"github-copilot","permalink":"/Cloud-Native/30daysofIA/tags/github-copilot","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[38232],{36484:e=>{e.exports=JSON.parse('{"label":"github-copilot","permalink":"/Cloud-Native/30daysofIA/tags/github-copilot","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/80aaee8e.3988266a.js b/assets/js/80aaee8e.3988266a.js deleted file mode 100644 index 91521f075e..0000000000 --- a/assets/js/80aaee8e.3988266a.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[71363],{96843:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service/page/7","page":7,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service/page/6","nextPage":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service/page/8","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/80aaee8e.58d3b646.js b/assets/js/80aaee8e.58d3b646.js new file mode 100644 index 0000000000..f366ed17a5 --- /dev/null +++ b/assets/js/80aaee8e.58d3b646.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[71363],{96843:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service/page/7","page":7,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service/page/6","nextPage":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service/page/8","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/824b618b.adaffeb7.js b/assets/js/824b618b.adaffeb7.js new file mode 100644 index 0000000000..5e58134e82 --- /dev/null +++ b/assets/js/824b618b.adaffeb7.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[91],{3905:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>m});var a=n(67294);function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function o(e){for(var t=1;t=0||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}var s=a.createContext({}),p=function(e){var t=a.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},u=function(e){var t=p(e.components);return a.createElement(s.Provider,{value:t},e.children)},c={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},d=a.forwardRef((function(e,t){var n=e.components,r=e.mdxType,i=e.originalType,s=e.parentName,u=l(e,["components","mdxType","originalType","parentName"]),d=p(n),m=r,g=d["".concat(s,".").concat(m)]||d[m]||c[m]||i;return n?a.createElement(g,o(o({ref:t},u),{},{components:n})):a.createElement(g,o({ref:t},u))}));function m(e,t){var n=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var i=n.length,o=new Array(i);o[0]=d;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l.mdxType="string"==typeof e?e:r,o[1]=l;for(var p=2;p{n.r(t),n.d(t,{assets:()=>s,contentTitle:()=>o,default:()=>c,frontMatter:()=>i,metadata:()=>l,toc:()=>p});var a=n(87462),r=(n(67294),n(3905));const i={date:"2023-09-22T09:00",slug:"build-your-first-intelligent-app-with-azure-ai-and-aks-2",title:"2-2. Build Your First Intelligent App with Azure AI and AKS-2",authors:["cnteam"],draft:!1,hide_table_of_contents:!1,toc_min_heading_level:2,toc_max_heading_level:3,keywords:["Cloud","Data","AI","AI/ML","intelligent apps","cloud-native","30-days","enterprise apps","digital experiences","app modernization","serverless","ai apps","data"],image:"https://azure.github.io/Cloud-Native/img/ogImage.png",description:"Delve into creating an Intelligent App that leverages Azure AI Vision to analyze images and extract data. We guide readers through crafting an API to perform optical character recognition (OCR) on uploaded images, and subsequently deploying this API via Azure Kubernetes Service, helping them discover the power of these tools to create innovative, AI-driven solutions.",tags:["Fall-For-IA","30-days-of-IA","learn-live","hack-together","community-buzz","ask-the-expert","azure-kubernetes-service","azure-functions","azure-openai","azure-container-apps","azure-cosmos-db","github-copilot","github-codespaces","github-actions"]},o=void 0,l={permalink:"/Cloud-Native/30daysofIA/build-your-first-intelligent-app-with-azure-ai-and-aks-2",source:"@site/blog-30daysofIA/2023-09-27/build-your-first-intelligent-app-with-azure-ai-and-aks-2.md",title:"2-2. Build Your First Intelligent App with Azure AI and AKS-2",description:"Delve into creating an Intelligent App that leverages Azure AI Vision to analyze images and extract data. We guide readers through crafting an API to perform optical character recognition (OCR) on uploaded images, and subsequently deploying this API via Azure Kubernetes Service, helping them discover the power of these tools to create innovative, AI-driven solutions.",date:"2023-09-22T09:00:00.000Z",formattedDate:"September 22, 2023",tags:[{label:"Fall-For-IA",permalink:"/Cloud-Native/30daysofIA/tags/fall-for-ia"},{label:"30-days-of-IA",permalink:"/Cloud-Native/30daysofIA/tags/30-days-of-ia"},{label:"learn-live",permalink:"/Cloud-Native/30daysofIA/tags/learn-live"},{label:"hack-together",permalink:"/Cloud-Native/30daysofIA/tags/hack-together"},{label:"community-buzz",permalink:"/Cloud-Native/30daysofIA/tags/community-buzz"},{label:"ask-the-expert",permalink:"/Cloud-Native/30daysofIA/tags/ask-the-expert"},{label:"azure-kubernetes-service",permalink:"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service"},{label:"azure-functions",permalink:"/Cloud-Native/30daysofIA/tags/azure-functions"},{label:"azure-openai",permalink:"/Cloud-Native/30daysofIA/tags/azure-openai"},{label:"azure-container-apps",permalink:"/Cloud-Native/30daysofIA/tags/azure-container-apps"},{label:"azure-cosmos-db",permalink:"/Cloud-Native/30daysofIA/tags/azure-cosmos-db"},{label:"github-copilot",permalink:"/Cloud-Native/30daysofIA/tags/github-copilot"},{label:"github-codespaces",permalink:"/Cloud-Native/30daysofIA/tags/github-codespaces"},{label:"github-actions",permalink:"/Cloud-Native/30daysofIA/tags/github-actions"}],readingTime:8.71,hasTruncateMarker:!1,authors:[{name:"It's 30DaysOfIA",title:"FallForIA Content Team",url:"https://azure.github.io/Cloud-Native/Fall-For-IA/",imageURL:"https://azure.github.io/Cloud-Native/img/logo-ms-cloud-native.png",key:"cnteam"}],frontMatter:{date:"2023-09-22T09:00",slug:"build-your-first-intelligent-app-with-azure-ai-and-aks-2",title:"2-2. Build Your First Intelligent App with Azure AI and AKS-2",authors:["cnteam"],draft:!1,hide_table_of_contents:!1,toc_min_heading_level:2,toc_max_heading_level:3,keywords:["Cloud","Data","AI","AI/ML","intelligent apps","cloud-native","30-days","enterprise apps","digital experiences","app modernization","serverless","ai apps","data"],image:"https://azure.github.io/Cloud-Native/img/ogImage.png",description:"Delve into creating an Intelligent App that leverages Azure AI Vision to analyze images and extract data. We guide readers through crafting an API to perform optical character recognition (OCR) on uploaded images, and subsequently deploying this API via Azure Kubernetes Service, helping them discover the power of these tools to create innovative, AI-driven solutions.",tags:["Fall-For-IA","30-days-of-IA","learn-live","hack-together","community-buzz","ask-the-expert","azure-kubernetes-service","azure-functions","azure-openai","azure-container-apps","azure-cosmos-db","github-copilot","github-codespaces","github-actions"]},prevItem:{title:"2-1. Build Your First Intelligent App with Azure AI and AKS-1",permalink:"/Cloud-Native/30daysofIA/build-your-first-intelligent-app-with-azure-ai-and-aks-1"},nextItem:{title:"1-4. How Digital Natives leverage Generative AI",permalink:"/Cloud-Native/30daysofIA/how-digital-natives-leverage-generative-ai"}},s={authorsImageUrls:[void 0]},p=[{value:"What We'll Cover:",id:"what-well-cover",level:2},{value:"Jumpstart Your AI Journey: Building Your First Intelligent App with Azure AI and AKS (2)",id:"jumpstart-your-ai-journey-building-your-first-intelligent-app-with-azure-ai-and-aks-2",level:2},{value:"Prerequisites",id:"prerequisites",level:3},{value:"Pushing a Container Image to Azure Container Registry (ACR)",id:"pushing-a-container-image-to-azure-container-registry-acr",level:2},{value:"Deploying the Intelligent App on Azure Kubernetes Service (AKS)",id:"deploying-the-intelligent-app-on-azure-kubernetes-service-aks",level:2},{value:"Testing the Intelligent App on AKS",id:"testing-the-intelligent-app-on-aks",level:2},{value:"Exercise",id:"exercise",level:2},{value:"Next Steps",id:"next-steps",level:2}],u={toc:p};function c(e){let{components:t,...i}=e;return(0,r.kt)("wrapper",(0,a.Z)({},u,i,{components:t,mdxType:"MDXLayout"}),(0,r.kt)("head",null,(0,r.kt)("meta",{property:"og:url",content:"https://azure.github.io/cloud-native/30daysofia/build-your-first-intelligent-app-with-azure-ai-and-aks-2"}),(0,r.kt)("meta",{property:"og:type",content:"website"}),(0,r.kt)("meta",{property:"og:title",content:"**Fall For Intelligent Apps! \ud83c\udf42| Build AI Apps On Azure"}),(0,r.kt)("meta",{property:"og:description",content:"Create an Intelligent App that leverages Azure AI Vision to analyze images and extract data."}),(0,r.kt)("meta",{property:"og:image",content:"https://azure.github.io/Cloud-Native/img/ogImage.png"}),(0,r.kt)("meta",{name:"twitter:url",content:"https://azure.github.io/Cloud-Native/30daysofIA/build-your-first-intelligent-app-with-azure-ai-and-aks-2"}),(0,r.kt)("meta",{name:"twitter:title",content:"**Fall For Intelligent Apps! \ud83c\udf42 | Build AI Apps On Azure"}),(0,r.kt)("meta",{name:"twitter:description",content:"2-2. Create an Intelligent App that leverages Azure AI Vision to analyze images and extract data."}),(0,r.kt)("meta",{name:"twitter:image",content:"https://azure.github.io/Cloud-Native/img/ogImage.png"}),(0,r.kt)("meta",{name:"twitter:card",content:"summary_large_image"}),(0,r.kt)("meta",{name:"twitter:creator",content:"@devanshidiaries"}),(0,r.kt)("meta",{name:"twitter:site",content:"@AzureAdvocates"}),(0,r.kt)("link",{rel:"canonical",href:"https://azure.github.io/Cloud-Native/30daysofIA/build-your-first-intelligent-app-with-azure-ai-and-aks-2"})),(0,r.kt)("p",null,"In the last article, we created an Intelligent App that leverages Azure AI Vision to analyze images and extract data. Develop an API to perform optical character recognition (OCR) on uploaded images and testing this API locally. "),(0,r.kt)("p",null,"In this article, we will deploy the web API to the cloud platform using Azure Kubernetes Service. "),(0,r.kt)("h2",{id:"what-well-cover"},"What We'll Cover:"),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},"Understanding Azure AI Vision and Azure Kubernetes Service"),(0,r.kt)("li",{parentName:"ul"},"Build a Python Web API to perform OCR"),(0,r.kt)("li",{parentName:"ul"},"Test the API locally")),(0,r.kt)("p",null,(0,r.kt)("img",{alt:"image depicting an Intelligent App using AI",src:n(87438).Z,width:"624",height:"380"})),(0,r.kt)("h2",{id:"jumpstart-your-ai-journey-building-your-first-intelligent-app-with-azure-ai-and-aks-2"},"Jumpstart Your AI Journey: Building Your First Intelligent App with Azure AI and AKS (2)"),(0,r.kt)("p",null,"In the previous article we explored the creation of an Intelligent App that leverages Azure AI Vision to analyze images and extract data. We learned how to build a Python Web API to perform OCR on uploaded images and subsequently test this API locally. "),(0,r.kt)("p",null,"In this article we will use ",(0,r.kt)("a",{parentName:"p",href:"https://azure.microsoft.com/products/kubernetes-service?WT.mc_id=javascript-99907-ninarasi"},"Azure Kubernetes Service")," (AKS) to develop, publish, and maintain our app in the cloud on Azure. "),(0,r.kt)("p",null,"Let\u2019s get started!"),(0,r.kt)("h3",{id:"prerequisites"},"Prerequisites"),(0,r.kt)("p",null,"To follow this tutorial, ensure you have completed ",(0,r.kt)("a",{parentName:"p",href:"https://azure.github.io/Cloud-Native/30daysofIA/build-your-first-intelligent-app-with-azure-ai-and-aks-1"},"Jumpstart Your AI Journey: Building Your First Intelligent App with Azure AI and AKS (1)"),"."),(0,r.kt)("h2",{id:"pushing-a-container-image-to-azure-container-registry-acr"},"Pushing a Container Image to Azure Container Registry (ACR)"),(0,r.kt)("p",null,"To start, open your CLI or terminal and type the following command:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"az login\n")),(0,r.kt)("p",null,"Follow the instructions displayed on your browser to enter your Azure credentials."),(0,r.kt)("p",null,"Once authenticated, you\u2019ll initiate a secure connection between your local environment and Azure. This process grants you access to cloud services and resources."),(0,r.kt)("p",null,"Next, type the command below in your terminal to set up a new Azure Container Registry (ACR) to store your container images:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"az acr create --resource-group computer-vision --name --sku Basic\n")),(0,r.kt)("p",null,"Remember to replace ",(0,r.kt)("inlineCode",{parentName:"p"},"")," with your container registry name. The name must be unique within Azure and comply with ",(0,r.kt)("a",{parentName:"p",href:"https://learn.microsoft.com/azure/azure-resource-manager/management/resource-name-rules#microsoftcontainerregistry"},"these rules"),"."),(0,r.kt)("p",null,"The command above creates an ",(0,r.kt)("a",{parentName:"p",href:"https://azure.microsoft.com/products/container-registry?WT.mc_id=javascript-99907-ninarasi"},"Azure Container Registry")," (ACR) in the ",(0,r.kt)("inlineCode",{parentName:"p"},"computer-vision")," resource group under the ",(0,r.kt)("a",{parentName:"p",href:"https://learn.microsoft.com/azure/container-registry/container-registry-skus?WT.mc_id=javascript-99907-ninarasi"},"Basic SKU"),". This ACR is your secure and private repository for storing container images within Azure."),(0,r.kt)("p",null,"Next, log in to the registry with the following command:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"az acr login -n \n")),(0,r.kt)("p",null,"The ",(0,r.kt)("inlineCode",{parentName:"p"},"az acr login")," command above lets you securely authenticate and access the specified ACR without providing their Azure credentials each time."),(0,r.kt)("p",null,"Now, run the following command in your terminal. It will display the endpoint URL to log in to and interact with the ACR for pushing and pulling container images."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"az acr show --name --query loginServer --output table\n")),(0,r.kt)("p",null,"This returns an endpoint URL as follows: "),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"Result\n----------------------------------\n.azurecr.io\n")),(0,r.kt)("p",null,"Now, run the following command to show all container images, their repository, tags, and size:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"docker images\n")),(0,r.kt)("table",null,(0,r.kt)("thead",{parentName:"table"},(0,r.kt)("tr",{parentName:"thead"},(0,r.kt)("th",{parentName:"tr",align:"left"},"REPOSITORY"),(0,r.kt)("th",{parentName:"tr",align:"left"},"TAG"),(0,r.kt)("th",{parentName:"tr",align:"left"},"IMAGE ID"),(0,r.kt)("th",{parentName:"tr",align:"left"},"CREATED"),(0,r.kt)("th",{parentName:"tr",align:"left"},"SIZE"))),(0,r.kt)("tbody",{parentName:"table"},(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:"left"},"intelligent-app"),(0,r.kt)("td",{parentName:"tr",align:"left"},"latest"),(0,r.kt)("td",{parentName:"tr",align:"left"},"a7bf9f753617"),(0,r.kt)("td",{parentName:"tr",align:"left"},"16 hours ago"),(0,r.kt)("td",{parentName:"tr",align:"left"},"197MB")))),(0,r.kt)("p",null,"Tags are necessary to push Docker images to a remote registry like Azure Container Registry. They also let you differentiate between different versions of the same image and upload or download the one you want."),(0,r.kt)("p",null,"Run the following command to tag your Docker image:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"docker tag intelligent-app .azurecr.io/intelligent-app:v1\n")),(0,r.kt)("p",null,"Then, run the ",(0,r.kt)("inlineCode",{parentName:"p"},"docker images")," command again to check your tagged image:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"docker images\n")),(0,r.kt)("table",null,(0,r.kt)("thead",{parentName:"table"},(0,r.kt)("tr",{parentName:"thead"},(0,r.kt)("th",{parentName:"tr",align:"left"},"REPOSITORY"),(0,r.kt)("th",{parentName:"tr",align:"left"},"TAG"),(0,r.kt)("th",{parentName:"tr",align:"left"},"IMAGE ID"),(0,r.kt)("th",{parentName:"tr",align:"left"},"CREATED"),(0,r.kt)("th",{parentName:"tr",align:"left"},"SIZE"))),(0,r.kt)("tbody",{parentName:"table"},(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:"left"},"intelligent-app"),(0,r.kt)("td",{parentName:"tr",align:"left"},"latest"),(0,r.kt)("td",{parentName:"tr",align:"left"},"c52168039265"),(0,r.kt)("td",{parentName:"tr",align:"left"},"About a minute ago"),(0,r.kt)("td",{parentName:"tr",align:"left"},"197MB")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:"left"},"<","name-of-azure-container-registry",">",".azurecr.io/intelligent-app"),(0,r.kt)("td",{parentName:"tr",align:"left"},"v1"),(0,r.kt)("td",{parentName:"tr",align:"left"},"c52168039265"),(0,r.kt)("td",{parentName:"tr",align:"left"},"About a minute ago"),(0,r.kt)("td",{parentName:"tr",align:"left"},"197MB")))),(0,r.kt)("p",null,"Now run the following command so Docker can securely upload the image to your Azure Container Registry:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"docker push .azurecr.io/intelligent-app:v1\n")),(0,r.kt)("p",null,"Once we've deployed the image to the container registry, AKS can access it during deployment."),(0,r.kt)("admonition",{type:"info"},(0,r.kt)("p",{parentName:"admonition"},"Watch ",(0,r.kt)("strong",{parentName:"p"},(0,r.kt)("a",{parentName:"strong",href:"https://aka.ms/learnlive-contoso-app-deconstructed-Ep3"},"Episode 03"))," for the Learn Live session to experience a guided session on how to build, test and deploy an end-to-end intelligent app solution.")),(0,r.kt)("h2",{id:"deploying-the-intelligent-app-on-azure-kubernetes-service-aks"},"Deploying the Intelligent App on Azure Kubernetes Service (AKS)"),(0,r.kt)("p",null,"Before we deploy our Intelligent App to AKS, we need to provision an AKS cluster and define Kubernetes manifests."),(0,r.kt)("p",null,"To provision an AKS cluster to host our application, we specify the desired configurations for the cluster, such as the number of nodes, node size, and networking options. But first, download and install the Kubernetes command-line tools (kubectl), a client credential plugin implementing Azure authentication:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"az aks install-cli\n")),(0,r.kt)("p",null,"If you\u2019re using Linux, ",(0,r.kt)("a",{parentName:"p",href:"https://learn.microsoft.com/en-us/cli/azure/install-azure-cli-linux?pivots=apt?WT.mc_id=javascript-99907-ninarasi"},"review this tutorial"),". Then run the following: "),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"sudo az aks install-cli\n")),(0,r.kt)("p",null,"Next, run the following command in your terminal to enable access to the networking-related resources and services provided by the `Microsoft.Network`` namespace in Azure: "),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"az provider register --namespace Microsoft.Network\n")),(0,r.kt)("p",null,"Now, we must create an AKS cluster. Run the following command to create an AKS cluster named ",(0,r.kt)("inlineCode",{parentName:"p"},"aks-intelligent-app")," in the ",(0,r.kt)("inlineCode",{parentName:"p"},"computer-vision")," resource group."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"az aks create --resource-group computer-vision --name aks-intelligent-app --node-count 1 --generate-ssh-keys\n")),(0,r.kt)("p",null,"The command above specifies the target resource group: ",(0,r.kt)("inlineCode",{parentName:"p"},"computer-vision"),". The ",(0,r.kt)("a",{parentName:"p",href:"https://learn.microsoft.com/en-us/azure/aks/use-multiple-node-pools?WT.mc_id=javascript-99907-ninarasi"},"node pool")," is configured with one virtual machine (VM), and the secure shell (SSH) keys for secure node access are generated automatically."),(0,r.kt)("p",null,"Next, run the following command to update the AKS cluster you created by attaching it to your ACR. Doing so allows the AKS cluster to pull container images from the specified ACR when deploying workloads to the cluster."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"az aks update -n aks-intelligent-app -g computer-vision --attach-acr \n")),(0,r.kt)("p",null,"Then, run the command below to configure kubectl to work with our AKS cluster in the computer-vision resource group. "),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"az aks get-credentials --resource-group computer-vision --name aks-intelligent-app\n")),(0,r.kt)("p",null,"The command above retrieves the necessary credentials and context information required for kubectl to communicate with the AKS cluster."),(0,r.kt)("p",null,"We still have to define the Kubernetes manifests written in YAML, describing the desired state of our application deployment, including containers, networking, and scaling rules. We\u2019ll prepare these manifests, including ",(0,r.kt)("a",{parentName:"p",href:"https://kubernetes.io/docs/concepts/workloads/controllers/deployment/"},"Deployment")," and ",(0,r.kt)("a",{parentName:"p",href:"https://kubernetes.io/docs/concepts/services-networking/service/"},"Service")," configurations, to define how our application should be deployed and exposed."),(0,r.kt)("p",null,"Create a folder named ",(0,r.kt)("inlineCode",{parentName:"p"},"Deployment")," in the project root directory. Next, create two files in the deployment folder: ",(0,r.kt)("inlineCode",{parentName:"p"},"deployment.yml")," and ",(0,r.kt)("inlineCode",{parentName:"p"},"service.yml"),"."),(0,r.kt)("p",null,"Add the following configuration to the ",(0,r.kt)("inlineCode",{parentName:"p"},"deployment.yml")," file, replacing the ",(0,r.kt)("inlineCode",{parentName:"p"},"")," placeholder with your registry\u2019s name:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},'apiVersion: apps/v1\nkind: Deployment\nmetadata:\n name: intelligent-app\nspec:\n replicas: 1\n selector:\n matchLabels:\n app: intelligent-app\n template:\n metadata:\n labels:\n app: intelligent-app\n spec:\n nodeSelector:\n kubernetes.io/os: linux\n containers:\n - name: intelligent-app\n image: .azurecr.io/intelligent-app:v1\n resources:\n limits:\n memory: 512Mi\n cpu: "1"\n requests:\n memory: 256Mi\n cpu: "0.2"\n ports:\n - containerPort: 5000\n env:\n - name: FLASK_DEBUG\n value: "1"\n - name: VISION_KEY\n value: \n - name: VISION_ENDPOINT\n value: \n')),(0,r.kt)("p",null,"Additionally, edit the ",(0,r.kt)("inlineCode",{parentName:"p"},"VISION_KEY")," and ",(0,r.kt)("inlineCode",{parentName:"p"},"VISION_ENDPOINT")," environment variables above according to the API key and endpoint of your Azure AI Services instance."),(0,r.kt)("p",null,"Then, add the following configuration to the ",(0,r.kt)("inlineCode",{parentName:"p"},"service.yml")," file:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"apiVersion: v1\nkind: Service \nmetadata:\n name: intelligent-app-service\nspec:\n type: LoadBalancer\n ports:\n - protocol: TCP\n port: 80\n targetPort: 5000\n name: port5000\n selector:\n app: intelligent-app\n")),(0,r.kt)("p",null,"Now, we\u2019ll deploy our application using kubectl. Applying the Kubernetes manifests creates the necessary resources and deploys our containerized application to the AKS cluster."),(0,r.kt)("p",null,"First, change the terminal to the ",(0,r.kt)("inlineCode",{parentName:"p"},"deployment")," folder: "),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"cd Deployment \n")),(0,r.kt)("p",null,"Then, run the following command to create or update Kubernetes resources defined in the ",(0,r.kt)("inlineCode",{parentName:"p"},"deployment.yml")," file:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"kubectl apply -f deployment.yml\n")),(0,r.kt)("p",null,"Create a Kubernetes Service resource in a Kubernetes cluster based on the configuration defined in the ",(0,r.kt)("inlineCode",{parentName:"p"},"service.yml")," file using the code below: "),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"kubectl apply -f service.yml\n")),(0,r.kt)("p",null,"Once you\u2019ve applied the resource definition and the service configuration contained in the ",(0,r.kt)("inlineCode",{parentName:"p"},"deployment.yml")," and the ",(0,r.kt)("inlineCode",{parentName:"p"},"service.yml")," files, open the ",(0,r.kt)("strong",{parentName:"p"},"aks-intelligent-app")," Kubernetes Service in the Azure Portal, select ",(0,r.kt)("strong",{parentName:"p"},"Workloads")," under ",(0,r.kt)("strong",{parentName:"p"},"Kubernetes resources")," on the sidebar, and find the deployment named ",(0,r.kt)("strong",{parentName:"p"},"intelligent-app"),". It must have the status \u201cReady 1/1\u201d. If you encounter an issue with this status, check out these ",(0,r.kt)("a",{parentName:"p",href:"https://learn.microsoft.com/en-us/troubleshoot/azure/azure-kubernetes/node-not-ready-basic-troubleshooting?WT.mc_id=javascript-99907-ninarasi"},"troubleshooting resources"),"."),(0,r.kt)("p",null,(0,r.kt)("img",{alt:"image of selecting the deployment in Azure Portal",src:n(5669).Z,width:"624",height:"318"})),(0,r.kt)("h2",{id:"testing-the-intelligent-app-on-aks"},"Testing the Intelligent App on AKS"),(0,r.kt)("p",null,"To test the app on AKS, first, run the command below:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"kubectl get services\n")),(0,r.kt)("p",null,"This command lists the Services and their corresponding details, including the Service name, cluster IP address, external IP, and ports."),(0,r.kt)("table",null,(0,r.kt)("thead",{parentName:"table"},(0,r.kt)("tr",{parentName:"thead"},(0,r.kt)("th",{parentName:"tr",align:"left"},"NAME"),(0,r.kt)("th",{parentName:"tr",align:"left"},"TYPE"),(0,r.kt)("th",{parentName:"tr",align:"left"},"CLUSTER-IP"),(0,r.kt)("th",{parentName:"tr",align:"left"},"EXTERNAL-IP"),(0,r.kt)("th",{parentName:"tr",align:"left"},"PORT(S)"),(0,r.kt)("th",{parentName:"tr",align:"left"},"AGE"))),(0,r.kt)("tbody",{parentName:"table"},(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:"left"},"intelligent-app-service"),(0,r.kt)("td",{parentName:"tr",align:"left"},"LoadBalancer"),(0,r.kt)("td",{parentName:"tr",align:"left"},"10.0.77.60"),(0,r.kt)("td",{parentName:"tr",align:"left"},"20.121.76.153"),(0,r.kt)("td",{parentName:"tr",align:"left"},"80:30936/TCP"),(0,r.kt)("td",{parentName:"tr",align:"left"},"47s")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:"left"},"kubernetes"),(0,r.kt)("td",{parentName:"tr",align:"left"},"ClusterIP"),(0,r.kt)("td",{parentName:"tr",align:"left"},"10.0.0.1"),(0,r.kt)("td",{parentName:"tr",align:"left"},"<","none",">"),(0,r.kt)("td",{parentName:"tr",align:"left"},"443/TCP"),(0,r.kt)("td",{parentName:"tr",align:"left"},"14m")))),(0,r.kt)("p",null,"The output above shows a Kubernetes Service named ",(0,r.kt)("inlineCode",{parentName:"p"},"intelligent-app-service")," with a type set to ",(0,r.kt)("inlineCode",{parentName:"p"},"LoadBalancer"),". It\u2019s reachable from within the cluster using the cluster IP ",(0,r.kt)("inlineCode",{parentName:"p"},"10.0.77.60")," and accessible externally via the external IP ",(0,r.kt)("inlineCode",{parentName:"p"},"20.121.76.153")," on port 80 (mapped to port 30936)."),(0,r.kt)("p",null,(0,r.kt)("strong",{parentName:"p"},"Note"),": Your set of IP addresses will differ. Remember to use your unique external IP address when testing with Postman. "),(0,r.kt)("p",null,"To test the deployed app, go to Postman, replace the URL with the external IP of the Kubernetes Service you just deployed, and click ",(0,r.kt)("strong",{parentName:"p"},"Send"),":"),(0,r.kt)("p",null,(0,r.kt)("img",{alt:"mage of sending the app in Postman",src:n(8422).Z,width:"624",height:"365"})),(0,r.kt)("p",null,"As we can see, our Intelligent App has successfully deployed to AKS and is functioning on the cloud as expected."),(0,r.kt)("h2",{id:"exercise"},"Exercise"),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},"Complete this ",(0,r.kt)("strong",{parentName:"li"},"hands-on")," sample ",(0,r.kt)("a",{parentName:"li",href:"https://github.com/contentlab-io/Microsoft-Using-Azure-Kubernetes-Service-to-Deploy-an-Intelligent-App-for-Analyzing-Images-1/tree/main/Microsoft_Series17-18_Code/intelligent-app-after"},"project code")," to build your first intelligent app."),(0,r.kt)("li",{parentName:"ul"},"Complete the ",(0,r.kt)("a",{parentName:"li",href:"https://aka.ms/fallforIA/apps-csc"},"Apps Cloud Skills Challenge")," to build on your app dev and AI skills."),(0,r.kt)("li",{parentName:"ul"},"Register for ",(0,r.kt)("a",{parentName:"li",href:"https://reactor.microsoft.com/en-us/reactor/series/S-1037/"},"Ask the Expert: Azure Kubernetes Service")," session for live Q&A with the Product Engineering team on building intelligent apps.")),(0,r.kt)("h2",{id:"next-steps"},"Next Steps"),(0,r.kt)("p",null,"In this two-part article, we explored the creation of an Intelligent App that leverages Azure AI Vision to analyze images and extract data. We learned how to build a Python Web API to perform OCR on uploaded images and subsequently deploy this API via Azure Kubernetes Service."),(0,r.kt)("p",null,"Besides OCR and Image Analysis, you can continue exploring Azure\u2019s vast array of services and experiment further with Azure AI and AKS by applying various practical uses to your Intelligent Apps, including natural language processing, speech recognition and synthesis, sentiment analysis for customer feedback, and automated content moderation."))}c.isMDXComponent=!0},87438:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/blog-image-2-1-974d3a64c66cbcd6e014779113bc1a4b.jpeg"},5669:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/blog-image-2-2-fb13fe692dc47f7d90de361ec6d5b771.png"},8422:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/blog-image-2-3-d8cd3a3026fb47d4589f3ee5baa3de5d.png"}}]); \ No newline at end of file diff --git a/assets/js/8362f252.4d6de7d4.js b/assets/js/8362f252.4d6de7d4.js deleted file mode 100644 index 9c62484fac..0000000000 --- a/assets/js/8362f252.4d6de7d4.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[32767],{68479:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/hack-together/page/7","page":7,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/hack-together/page/6","nextPage":"/Cloud-Native/30daysofIA/tags/hack-together/page/8","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/8362f252.ca0cce64.js b/assets/js/8362f252.ca0cce64.js new file mode 100644 index 0000000000..363696f3bd --- /dev/null +++ b/assets/js/8362f252.ca0cce64.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[32767],{68479:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/hack-together/page/7","page":7,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/hack-together/page/6","nextPage":"/Cloud-Native/30daysofIA/tags/hack-together/page/8","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/8412e158.5679da5f.js b/assets/js/8412e158.2fe1ac2d.js similarity index 95% rename from assets/js/8412e158.5679da5f.js rename to assets/js/8412e158.2fe1ac2d.js index 7d1df4878c..d9f3525eca 100644 --- a/assets/js/8412e158.5679da5f.js +++ b/assets/js/8412e158.2fe1ac2d.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[74801],{25848:e=>{e.exports=JSON.parse('{"label":"azure-functions","permalink":"/Cloud-Native/30daysofIA/tags/azure-functions","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[74801],{25848:e=>{e.exports=JSON.parse('{"label":"azure-functions","permalink":"/Cloud-Native/30daysofIA/tags/azure-functions","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/8445e33e.fae4094b.js b/assets/js/8445e33e.c3f178d1.js similarity index 96% rename from assets/js/8445e33e.fae4094b.js rename to assets/js/8445e33e.c3f178d1.js index 00b669a3e2..1e6280dcb9 100644 --- a/assets/js/8445e33e.fae4094b.js +++ b/assets/js/8445e33e.c3f178d1.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[5482],{27203:e=>{e.exports=JSON.parse('{"label":"azure-functions","permalink":"/Cloud-Native/30daysofIA/tags/azure-functions","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[5482],{27203:e=>{e.exports=JSON.parse('{"label":"azure-functions","permalink":"/Cloud-Native/30daysofIA/tags/azure-functions","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/84806527.0bdc75b2.js b/assets/js/84806527.0bdc75b2.js new file mode 100644 index 0000000000..fef7c638b9 --- /dev/null +++ b/assets/js/84806527.0bdc75b2.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[99125],{95397:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-openai/page/5","page":5,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/azure-openai/page/4","nextPage":"/Cloud-Native/30daysofIA/tags/azure-openai/page/6","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/84806527.25945221.js b/assets/js/84806527.25945221.js deleted file mode 100644 index 107775cb05..0000000000 --- a/assets/js/84806527.25945221.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[99125],{95397:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-openai/page/5","page":5,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/azure-openai/page/4","nextPage":"/Cloud-Native/30daysofIA/tags/azure-openai/page/6","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/84d9913c.d075cf3f.js b/assets/js/84d9913c.0edfd53c.js similarity index 73% rename from assets/js/84d9913c.d075cf3f.js rename to assets/js/84d9913c.0edfd53c.js index fc8bd4ac37..ffec801bce 100644 --- a/assets/js/84d9913c.d075cf3f.js +++ b/assets/js/84d9913c.0edfd53c.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[94608],{44641:e=>{e.exports=JSON.parse('{"label":"azure-openai","permalink":"/Cloud-Native/30daysofIA/tags/azure-openai","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[94608],{44641:e=>{e.exports=JSON.parse('{"label":"azure-openai","permalink":"/Cloud-Native/30daysofIA/tags/azure-openai","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/84e7f16f.95745ba9.js b/assets/js/84e7f16f.95745ba9.js deleted file mode 100644 index 4bca821812..0000000000 --- a/assets/js/84e7f16f.95745ba9.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[31248],{14109:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-openai/page/6","page":6,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/azure-openai/page/5","nextPage":"/Cloud-Native/30daysofIA/tags/azure-openai/page/7","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/84e7f16f.cf9c5f42.js b/assets/js/84e7f16f.cf9c5f42.js new file mode 100644 index 0000000000..26a70bf39b --- /dev/null +++ b/assets/js/84e7f16f.cf9c5f42.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[31248],{14109:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-openai/page/6","page":6,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/azure-openai/page/5","nextPage":"/Cloud-Native/30daysofIA/tags/azure-openai/page/7","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/854ddf44.35dc9a6f.js b/assets/js/854ddf44.35dc9a6f.js new file mode 100644 index 0000000000..15d7f2281b --- /dev/null +++ b/assets/js/854ddf44.35dc9a6f.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[11967],{41467:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/page/4","page":4,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/page/3","nextPage":"/Cloud-Native/30daysofIA/page/5","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/854ddf44.d30be8b3.js b/assets/js/854ddf44.d30be8b3.js deleted file mode 100644 index 1797391996..0000000000 --- a/assets/js/854ddf44.d30be8b3.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[11967],{41467:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/page/4","page":4,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/page/3","nextPage":"/Cloud-Native/30daysofIA/page/5","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/85cba9e9.fc1d5689.js b/assets/js/85cba9e9.47d358fd.js similarity index 73% rename from assets/js/85cba9e9.fc1d5689.js rename to assets/js/85cba9e9.47d358fd.js index c8a339a8b8..5d218bd308 100644 --- a/assets/js/85cba9e9.fc1d5689.js +++ b/assets/js/85cba9e9.47d358fd.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[17745],{46685:e=>{e.exports=JSON.parse('{"label":"hack-together","permalink":"/Cloud-Native/30daysofIA/tags/hack-together","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[17745],{46685:e=>{e.exports=JSON.parse('{"label":"hack-together","permalink":"/Cloud-Native/30daysofIA/tags/hack-together","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/86e280de.6daaa018.js b/assets/js/86e280de.6daaa018.js new file mode 100644 index 0000000000..767d7a6d83 --- /dev/null +++ b/assets/js/86e280de.6daaa018.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[38563],{87303:e=>{e.exports=JSON.parse('{"label":"community-buzz","permalink":"/Cloud-Native/30daysofIA/tags/community-buzz","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/87bbf9f8.32218a34.js b/assets/js/87bbf9f8.7a2199ef.js similarity index 72% rename from assets/js/87bbf9f8.32218a34.js rename to assets/js/87bbf9f8.7a2199ef.js index 03137fd393..f3957d88a6 100644 --- a/assets/js/87bbf9f8.32218a34.js +++ b/assets/js/87bbf9f8.7a2199ef.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[7615],{85472:e=>{e.exports=JSON.parse('{"label":"community-buzz","permalink":"/Cloud-Native/30daysofIA/tags/community-buzz","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[7615],{85472:e=>{e.exports=JSON.parse('{"label":"community-buzz","permalink":"/Cloud-Native/30daysofIA/tags/community-buzz","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/88357ceb.f272dfc8.js b/assets/js/88357ceb.3eb258f5.js similarity index 65% rename from assets/js/88357ceb.f272dfc8.js rename to assets/js/88357ceb.3eb258f5.js index 8e930f25f6..dcbc876b8e 100644 --- a/assets/js/88357ceb.f272dfc8.js +++ b/assets/js/88357ceb.3eb258f5.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[58467],{36756:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/learn-live/page/9","page":9,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/learn-live/page/8","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[58467],{36756:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/learn-live/page/9","page":9,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/learn-live/page/8","nextPage":"/Cloud-Native/30daysofIA/tags/learn-live/page/10","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/88665212.74095adf.js b/assets/js/88665212.74095adf.js deleted file mode 100644 index 045dff5082..0000000000 --- a/assets/js/88665212.74095adf.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[47241],{45338:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/github-codespaces/page/5","page":5,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/github-codespaces/page/4","nextPage":"/Cloud-Native/30daysofIA/tags/github-codespaces/page/6","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/88665212.76f8067e.js b/assets/js/88665212.76f8067e.js new file mode 100644 index 0000000000..0caa5462ca --- /dev/null +++ b/assets/js/88665212.76f8067e.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[47241],{45338:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/github-codespaces/page/5","page":5,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/github-codespaces/page/4","nextPage":"/Cloud-Native/30daysofIA/tags/github-codespaces/page/6","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/8b115eee.453a46b1.js b/assets/js/8b115eee.1be3c929.js similarity index 94% rename from assets/js/8b115eee.453a46b1.js rename to assets/js/8b115eee.1be3c929.js index b3215c2e05..f901facc9c 100644 --- a/assets/js/8b115eee.453a46b1.js +++ b/assets/js/8b115eee.1be3c929.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[41990],{92918:e=>{e.exports=JSON.parse('{"label":"github-codespaces","permalink":"/Cloud-Native/30daysofIA/tags/github-codespaces","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[41990],{92918:e=>{e.exports=JSON.parse('{"label":"github-codespaces","permalink":"/Cloud-Native/30daysofIA/tags/github-codespaces","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/8b5ab4ba.0875bda3.js b/assets/js/8b5ab4ba.0875bda3.js new file mode 100644 index 0000000000..821a378d58 --- /dev/null +++ b/assets/js/8b5ab4ba.0875bda3.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[25767],{73206:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/community-buzz/page/6","page":6,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/community-buzz/page/5","nextPage":"/Cloud-Native/30daysofIA/tags/community-buzz/page/7","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/8b5ab4ba.44ab3abd.js b/assets/js/8b5ab4ba.44ab3abd.js deleted file mode 100644 index 1c062f02da..0000000000 --- a/assets/js/8b5ab4ba.44ab3abd.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[25767],{73206:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/community-buzz/page/6","page":6,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/community-buzz/page/5","nextPage":"/Cloud-Native/30daysofIA/tags/community-buzz/page/7","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/8baf15a9.17bcf8b4.js b/assets/js/8baf15a9.17bcf8b4.js deleted file mode 100644 index 98b8e8c657..0000000000 --- a/assets/js/8baf15a9.17bcf8b4.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[51828],{20441:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/fall-for-ia","page":1,"postsPerPage":1,"totalPages":9,"totalCount":9,"nextPage":"/Cloud-Native/30daysofIA/tags/fall-for-ia/page/2","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/8baf15a9.28a9b535.js b/assets/js/8baf15a9.28a9b535.js new file mode 100644 index 0000000000..912727e8cf --- /dev/null +++ b/assets/js/8baf15a9.28a9b535.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[51828],{20441:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/fall-for-ia","page":1,"postsPerPage":1,"totalPages":11,"totalCount":11,"nextPage":"/Cloud-Native/30daysofIA/tags/fall-for-ia/page/2","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/8c3d2b1d.340adc88.js b/assets/js/8c3d2b1d.6b291ca2.js similarity index 72% rename from assets/js/8c3d2b1d.340adc88.js rename to assets/js/8c3d2b1d.6b291ca2.js index 18be77ac2f..01037f9e74 100644 --- a/assets/js/8c3d2b1d.340adc88.js +++ b/assets/js/8c3d2b1d.6b291ca2.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[36277],{85532:e=>{e.exports=JSON.parse('{"label":"github-copilot","permalink":"/Cloud-Native/30daysofIA/tags/github-copilot","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[36277],{85532:e=>{e.exports=JSON.parse('{"label":"github-copilot","permalink":"/Cloud-Native/30daysofIA/tags/github-copilot","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/8ca49f56.4a007c7d.js b/assets/js/8ca49f56.4a007c7d.js new file mode 100644 index 0000000000..ef41ed5cda --- /dev/null +++ b/assets/js/8ca49f56.4a007c7d.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[71861],{60295:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/github-actions/page/6","page":6,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/github-actions/page/5","nextPage":"/Cloud-Native/30daysofIA/tags/github-actions/page/7","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/8ca49f56.69bc234a.js b/assets/js/8ca49f56.69bc234a.js deleted file mode 100644 index 68605fbd7d..0000000000 --- a/assets/js/8ca49f56.69bc234a.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[71861],{60295:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/github-actions/page/6","page":6,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/github-actions/page/5","nextPage":"/Cloud-Native/30daysofIA/tags/github-actions/page/7","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/8cacefc1.1b7a94a3.js b/assets/js/8cacefc1.23c0a970.js similarity index 73% rename from assets/js/8cacefc1.1b7a94a3.js rename to assets/js/8cacefc1.23c0a970.js index ecb78763f1..a20e7e57c7 100644 --- a/assets/js/8cacefc1.1b7a94a3.js +++ b/assets/js/8cacefc1.23c0a970.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[37460],{91998:e=>{e.exports=JSON.parse('{"label":"azure-openai","permalink":"/Cloud-Native/30daysofIA/tags/azure-openai","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[37460],{91998:e=>{e.exports=JSON.parse('{"label":"azure-openai","permalink":"/Cloud-Native/30daysofIA/tags/azure-openai","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/8e80c8e1.abec1ca4.js b/assets/js/8e80c8e1.5479c0dd.js similarity index 72% rename from assets/js/8e80c8e1.abec1ca4.js rename to assets/js/8e80c8e1.5479c0dd.js index 5dc8fdcafd..85098425a9 100644 --- a/assets/js/8e80c8e1.abec1ca4.js +++ b/assets/js/8e80c8e1.5479c0dd.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[2848],{95391:a=>{a.exports=JSON.parse('{"label":"github-actions","permalink":"/Cloud-Native/30daysofIA/tags/github-actions","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[2848],{95391:a=>{a.exports=JSON.parse('{"label":"github-actions","permalink":"/Cloud-Native/30daysofIA/tags/github-actions","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/8f4add25.26ce8afd.js b/assets/js/8f4add25.52cd3e38.js similarity index 94% rename from assets/js/8f4add25.26ce8afd.js rename to assets/js/8f4add25.52cd3e38.js index e4237f886b..47ebce11cf 100644 --- a/assets/js/8f4add25.26ce8afd.js +++ b/assets/js/8f4add25.52cd3e38.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[65251],{10165:e=>{e.exports=JSON.parse('{"label":"github-codespaces","permalink":"/Cloud-Native/30daysofIA/tags/github-codespaces","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[65251],{10165:e=>{e.exports=JSON.parse('{"label":"github-codespaces","permalink":"/Cloud-Native/30daysofIA/tags/github-codespaces","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/8f65450d.b87b892f.js b/assets/js/8f65450d.b87b892f.js new file mode 100644 index 0000000000..da554de14a --- /dev/null +++ b/assets/js/8f65450d.b87b892f.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[10254],{82134:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-container-apps/page/11","page":11,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/azure-container-apps/page/10","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/9089e36c.638f873c.js b/assets/js/9089e36c.638f873c.js new file mode 100644 index 0000000000..29d6a1ab48 --- /dev/null +++ b/assets/js/9089e36c.638f873c.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[830],{28093:a=>{a.exports=JSON.parse('{"label":"github-actions","permalink":"/Cloud-Native/30daysofIA/tags/github-actions","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/91e58248.d8e381ee.js b/assets/js/91e58248.d8e381ee.js new file mode 100644 index 0000000000..5470374683 --- /dev/null +++ b/assets/js/91e58248.d8e381ee.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[2556],{97721:e=>{e.exports=JSON.parse('{"label":"azure-kubernetes-service","permalink":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/92851fbb.33fa462d.js b/assets/js/92851fbb.33fa462d.js new file mode 100644 index 0000000000..ef4cfe8fac --- /dev/null +++ b/assets/js/92851fbb.33fa462d.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[40869],{928:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db/page/2","page":2,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db","nextPage":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db/page/3","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/92851fbb.bb33843c.js b/assets/js/92851fbb.bb33843c.js deleted file mode 100644 index 980638f75b..0000000000 --- a/assets/js/92851fbb.bb33843c.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[40869],{928:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db/page/2","page":2,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db","nextPage":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db/page/3","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/93465d98.11300f8e.js b/assets/js/93465d98.73c1ce2f.js similarity index 95% rename from assets/js/93465d98.11300f8e.js rename to assets/js/93465d98.73c1ce2f.js index 9856b98e28..da65704670 100644 --- a/assets/js/93465d98.11300f8e.js +++ b/assets/js/93465d98.73c1ce2f.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[70199],{28779:s=>{s.exports=JSON.parse('{"label":"azure-cosmos-db","permalink":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[70199],{28779:s=>{s.exports=JSON.parse('{"label":"azure-cosmos-db","permalink":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/937ccad8.5a0babb2.js b/assets/js/937ccad8.5a0babb2.js deleted file mode 100644 index 61143b5096..0000000000 --- a/assets/js/937ccad8.5a0babb2.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[48758],{73363:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-functions","page":1,"postsPerPage":1,"totalPages":9,"totalCount":9,"nextPage":"/Cloud-Native/30daysofIA/tags/azure-functions/page/2","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/937ccad8.e7f7c42a.js b/assets/js/937ccad8.e7f7c42a.js new file mode 100644 index 0000000000..ca99ff706a --- /dev/null +++ b/assets/js/937ccad8.e7f7c42a.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[48758],{73363:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-functions","page":1,"postsPerPage":1,"totalPages":11,"totalCount":11,"nextPage":"/Cloud-Native/30daysofIA/tags/azure-functions/page/2","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/9448e033.517999af.js b/assets/js/9448e033.517999af.js new file mode 100644 index 0000000000..42009ef714 --- /dev/null +++ b/assets/js/9448e033.517999af.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[59410],{92015:a=>{a.exports=JSON.parse('{"label":"30-days-of-IA","permalink":"/Cloud-Native/30daysofIA/tags/30-days-of-ia","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/9575c44b.2c0af6f4.js b/assets/js/9575c44b.2c0af6f4.js new file mode 100644 index 0000000000..f02e0a9a4a --- /dev/null +++ b/assets/js/9575c44b.2c0af6f4.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[93431],{94576:e=>{e.exports=JSON.parse('{"label":"azure-kubernetes-service","permalink":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/9648b421.11e4bdea.js b/assets/js/9648b421.11e4bdea.js new file mode 100644 index 0000000000..d10ba7fd5a --- /dev/null +++ b/assets/js/9648b421.11e4bdea.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[18601],{12795:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service/page/10","page":10,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service/page/9","nextPage":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service/page/11","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/96d6a951.d9386981.js b/assets/js/96d6a951.77a40a44.js similarity index 74% rename from assets/js/96d6a951.d9386981.js rename to assets/js/96d6a951.77a40a44.js index 6622febf3e..cdd40c61b0 100644 --- a/assets/js/96d6a951.d9386981.js +++ b/assets/js/96d6a951.77a40a44.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[11097],{20978:e=>{e.exports=JSON.parse('{"label":"learn-live","permalink":"/Cloud-Native/30daysofIA/tags/learn-live","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[11097],{20978:e=>{e.exports=JSON.parse('{"label":"learn-live","permalink":"/Cloud-Native/30daysofIA/tags/learn-live","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/9762ef78.4d49e9aa.js b/assets/js/9762ef78.4d49e9aa.js deleted file mode 100644 index ba6673dbb7..0000000000 --- a/assets/js/9762ef78.4d49e9aa.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[4895],{73115:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/30-days-of-ia/page/6","page":6,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/30-days-of-ia/page/5","nextPage":"/Cloud-Native/30daysofIA/tags/30-days-of-ia/page/7","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/9762ef78.7739d6af.js b/assets/js/9762ef78.7739d6af.js new file mode 100644 index 0000000000..e68878d104 --- /dev/null +++ b/assets/js/9762ef78.7739d6af.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[4895],{73115:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/30-days-of-ia/page/6","page":6,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/30-days-of-ia/page/5","nextPage":"/Cloud-Native/30daysofIA/tags/30-days-of-ia/page/7","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/97a96520.5c957134.js b/assets/js/97a96520.1dc3826e.js similarity index 72% rename from assets/js/97a96520.5c957134.js rename to assets/js/97a96520.1dc3826e.js index b3d11dfb8f..0d1ecb38d9 100644 --- a/assets/js/97a96520.5c957134.js +++ b/assets/js/97a96520.1dc3826e.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[7643],{87568:e=>{e.exports=JSON.parse('{"label":"ask-the-expert","permalink":"/Cloud-Native/30daysofIA/tags/ask-the-expert","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[7643],{87568:e=>{e.exports=JSON.parse('{"label":"ask-the-expert","permalink":"/Cloud-Native/30daysofIA/tags/ask-the-expert","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/97c179be.208177bf.js b/assets/js/97c179be.5049607e.js similarity index 72% rename from assets/js/97c179be.208177bf.js rename to assets/js/97c179be.5049607e.js index 5478ba6bf9..8045ab8c28 100644 --- a/assets/js/97c179be.208177bf.js +++ b/assets/js/97c179be.5049607e.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[76299],{31071:e=>{e.exports=JSON.parse('{"label":"github-copilot","permalink":"/Cloud-Native/30daysofIA/tags/github-copilot","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[76299],{31071:e=>{e.exports=JSON.parse('{"label":"github-copilot","permalink":"/Cloud-Native/30daysofIA/tags/github-copilot","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/97e02956.3fa27551.js b/assets/js/97e02956.3fa27551.js deleted file mode 100644 index 390b80d8c7..0000000000 --- a/assets/js/97e02956.3fa27551.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[98746],{47082:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/github-codespaces/page/9","page":9,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/github-codespaces/page/8","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/97e02956.58298c71.js b/assets/js/97e02956.58298c71.js new file mode 100644 index 0000000000..295354444e --- /dev/null +++ b/assets/js/97e02956.58298c71.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[98746],{47082:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/github-codespaces/page/9","page":9,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/github-codespaces/page/8","nextPage":"/Cloud-Native/30daysofIA/tags/github-codespaces/page/10","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/9a9cfa79.62f8a80d.js b/assets/js/9a9cfa79.62f8a80d.js new file mode 100644 index 0000000000..a31d63adbb --- /dev/null +++ b/assets/js/9a9cfa79.62f8a80d.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[2596],{98012:e=>{e.exports=JSON.parse('{"label":"azure-openai","permalink":"/Cloud-Native/30daysofIA/tags/azure-openai","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/9aee8dd5.0ea02461.js b/assets/js/9aee8dd5.0ea02461.js new file mode 100644 index 0000000000..64c4729e2d --- /dev/null +++ b/assets/js/9aee8dd5.0ea02461.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[43974],{81911:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/hack-together/page/4","page":4,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/hack-together/page/3","nextPage":"/Cloud-Native/30daysofIA/tags/hack-together/page/5","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/9aee8dd5.b79fba90.js b/assets/js/9aee8dd5.b79fba90.js deleted file mode 100644 index 2ea096c5dc..0000000000 --- a/assets/js/9aee8dd5.b79fba90.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[43974],{81911:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/hack-together/page/4","page":4,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/hack-together/page/3","nextPage":"/Cloud-Native/30daysofIA/tags/hack-together/page/5","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/9b3a3088.ae8c8777.js b/assets/js/9b3a3088.ae8c8777.js new file mode 100644 index 0000000000..08cb0a7fbe --- /dev/null +++ b/assets/js/9b3a3088.ae8c8777.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[89719],{24303:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-openai/page/10","page":10,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/azure-openai/page/9","nextPage":"/Cloud-Native/30daysofIA/tags/azure-openai/page/11","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/9b60d2ea.a5ae680f.js b/assets/js/9b60d2ea.a5ae680f.js deleted file mode 100644 index fbe5d02892..0000000000 --- a/assets/js/9b60d2ea.a5ae680f.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[95615],{44551:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-functions/page/7","page":7,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/azure-functions/page/6","nextPage":"/Cloud-Native/30daysofIA/tags/azure-functions/page/8","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/9b60d2ea.e73b3058.js b/assets/js/9b60d2ea.e73b3058.js new file mode 100644 index 0000000000..fa31082533 --- /dev/null +++ b/assets/js/9b60d2ea.e73b3058.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[95615],{44551:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-functions/page/7","page":7,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/azure-functions/page/6","nextPage":"/Cloud-Native/30daysofIA/tags/azure-functions/page/8","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/9bc306e8.b47a21be.js b/assets/js/9bc306e8.917cb835.js similarity index 72% rename from assets/js/9bc306e8.b47a21be.js rename to assets/js/9bc306e8.917cb835.js index 4fc7732a8c..d6841775f9 100644 --- a/assets/js/9bc306e8.b47a21be.js +++ b/assets/js/9bc306e8.917cb835.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[90031],{16105:e=>{e.exports=JSON.parse('{"label":"github-copilot","permalink":"/Cloud-Native/30daysofIA/tags/github-copilot","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[90031],{16105:e=>{e.exports=JSON.parse('{"label":"github-copilot","permalink":"/Cloud-Native/30daysofIA/tags/github-copilot","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/9c17a54a.a27405f9.js b/assets/js/9c17a54a.c7677255.js similarity index 95% rename from assets/js/9c17a54a.a27405f9.js rename to assets/js/9c17a54a.c7677255.js index 0304b59654..ca85499277 100644 --- a/assets/js/9c17a54a.a27405f9.js +++ b/assets/js/9c17a54a.c7677255.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[44348],{72557:e=>{e.exports=JSON.parse('{"label":"azure-functions","permalink":"/Cloud-Native/30daysofIA/tags/azure-functions","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[44348],{72557:e=>{e.exports=JSON.parse('{"label":"azure-functions","permalink":"/Cloud-Native/30daysofIA/tags/azure-functions","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/9cdc6aec.0e31f359.js b/assets/js/9cdc6aec.0e31f359.js deleted file mode 100644 index 8bf8fb1589..0000000000 --- a/assets/js/9cdc6aec.0e31f359.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[95252],{76681:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/page/5","page":5,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/page/4","nextPage":"/Cloud-Native/30daysofIA/page/6","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/9cdc6aec.3387524a.js b/assets/js/9cdc6aec.3387524a.js new file mode 100644 index 0000000000..fb19f3611c --- /dev/null +++ b/assets/js/9cdc6aec.3387524a.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[95252],{76681:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/page/5","page":5,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/page/4","nextPage":"/Cloud-Native/30daysofIA/page/6","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/9d6ed2de.b2a88d52.js b/assets/js/9d6ed2de.b2a88d52.js deleted file mode 100644 index 48b985f306..0000000000 --- a/assets/js/9d6ed2de.b2a88d52.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[29052],{56218:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service/page/9","page":9,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service/page/8","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/9d6ed2de.b39d2901.js b/assets/js/9d6ed2de.b39d2901.js new file mode 100644 index 0000000000..f201d06930 --- /dev/null +++ b/assets/js/9d6ed2de.b39d2901.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[29052],{56218:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service/page/9","page":9,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service/page/8","nextPage":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service/page/10","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/9e2f083a.e3f28d8b.js b/assets/js/9e2f083a.eaf3c8af.js similarity index 73% rename from assets/js/9e2f083a.e3f28d8b.js rename to assets/js/9e2f083a.eaf3c8af.js index 67d06f851b..25e8990340 100644 --- a/assets/js/9e2f083a.e3f28d8b.js +++ b/assets/js/9e2f083a.eaf3c8af.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[18438],{57966:e=>{e.exports=JSON.parse('{"label":"hack-together","permalink":"/Cloud-Native/30daysofIA/tags/hack-together","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[18438],{57966:e=>{e.exports=JSON.parse('{"label":"hack-together","permalink":"/Cloud-Native/30daysofIA/tags/hack-together","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/9f14d4e5.1b829b26.js b/assets/js/9f14d4e5.215dbdae.js similarity index 73% rename from assets/js/9f14d4e5.1b829b26.js rename to assets/js/9f14d4e5.215dbdae.js index 4e71642588..9cfa1da779 100644 --- a/assets/js/9f14d4e5.1b829b26.js +++ b/assets/js/9f14d4e5.215dbdae.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[84654],{94633:a=>{a.exports=JSON.parse('{"label":"30-days-of-IA","permalink":"/Cloud-Native/30daysofIA/tags/30-days-of-ia","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[84654],{94633:a=>{a.exports=JSON.parse('{"label":"30-days-of-IA","permalink":"/Cloud-Native/30daysofIA/tags/30-days-of-ia","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/9f3b21c4.335fca1f.js b/assets/js/9f3b21c4.335fca1f.js new file mode 100644 index 0000000000..9954575a28 --- /dev/null +++ b/assets/js/9f3b21c4.335fca1f.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[99888],{18094:a=>{a.exports=JSON.parse('{"label":"github-actions","permalink":"/Cloud-Native/30daysofIA/tags/github-actions","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/9fec92ba.311f5814.js b/assets/js/9fec92ba.311f5814.js new file mode 100644 index 0000000000..9d225e6f94 --- /dev/null +++ b/assets/js/9fec92ba.311f5814.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[36703],{33257:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-container-apps/page/5","page":5,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/azure-container-apps/page/4","nextPage":"/Cloud-Native/30daysofIA/tags/azure-container-apps/page/6","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/9fec92ba.b4a0c45f.js b/assets/js/9fec92ba.b4a0c45f.js deleted file mode 100644 index 6f92ee344e..0000000000 --- a/assets/js/9fec92ba.b4a0c45f.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[36703],{33257:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-container-apps/page/5","page":5,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/azure-container-apps/page/4","nextPage":"/Cloud-Native/30daysofIA/tags/azure-container-apps/page/6","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/a0a724b8.735f7453.js b/assets/js/a0a724b8.0640c6c7.js similarity index 95% rename from assets/js/a0a724b8.735f7453.js rename to assets/js/a0a724b8.0640c6c7.js index 3ece45c159..d9a8f9c1f7 100644 --- a/assets/js/a0a724b8.735f7453.js +++ b/assets/js/a0a724b8.0640c6c7.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[37729],{99326:e=>{e.exports=JSON.parse('{"label":"azure-functions","permalink":"/Cloud-Native/30daysofIA/tags/azure-functions","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[37729],{99326:e=>{e.exports=JSON.parse('{"label":"azure-functions","permalink":"/Cloud-Native/30daysofIA/tags/azure-functions","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/a11db7eb.6715de61.js b/assets/js/a11db7eb.1fc7a15a.js similarity index 94% rename from assets/js/a11db7eb.6715de61.js rename to assets/js/a11db7eb.1fc7a15a.js index 6add92d505..e275135bc9 100644 --- a/assets/js/a11db7eb.6715de61.js +++ b/assets/js/a11db7eb.1fc7a15a.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[91367],{31226:e=>{e.exports=JSON.parse('{"label":"github-codespaces","permalink":"/Cloud-Native/30daysofIA/tags/github-codespaces","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[91367],{31226:e=>{e.exports=JSON.parse('{"label":"github-codespaces","permalink":"/Cloud-Native/30daysofIA/tags/github-codespaces","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/a344ef93.95f38638.js b/assets/js/a344ef93.057c2fb3.js similarity index 74% rename from assets/js/a344ef93.95f38638.js rename to assets/js/a344ef93.057c2fb3.js index 041fe211c6..a221a0729e 100644 --- a/assets/js/a344ef93.95f38638.js +++ b/assets/js/a344ef93.057c2fb3.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[63070],{56147:e=>{e.exports=JSON.parse('{"label":"learn-live","permalink":"/Cloud-Native/30daysofIA/tags/learn-live","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[63070],{56147:e=>{e.exports=JSON.parse('{"label":"learn-live","permalink":"/Cloud-Native/30daysofIA/tags/learn-live","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/a356d3a5.9b9b7f7e.js b/assets/js/a356d3a5.9b9b7f7e.js new file mode 100644 index 0000000000..11fc602fb9 --- /dev/null +++ b/assets/js/a356d3a5.9b9b7f7e.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[1041],{46243:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/page/11","page":11,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/page/10","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/a40c8e08.c73a9ffd.js b/assets/js/a40c8e08.c73a9ffd.js new file mode 100644 index 0000000000..65a7e444e1 --- /dev/null +++ b/assets/js/a40c8e08.c73a9ffd.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[33451],{74701:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-functions/page/9","page":9,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/azure-functions/page/8","nextPage":"/Cloud-Native/30daysofIA/tags/azure-functions/page/10","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/a40c8e08.e7b66816.js b/assets/js/a40c8e08.e7b66816.js deleted file mode 100644 index 8bf5624b85..0000000000 --- a/assets/js/a40c8e08.e7b66816.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[33451],{74701:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-functions/page/9","page":9,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/azure-functions/page/8","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/a4bab584.56403a36.js b/assets/js/a4bab584.56403a36.js deleted file mode 100644 index ed506c605f..0000000000 --- a/assets/js/a4bab584.56403a36.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[77856],{41121:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/hack-together/page/9","page":9,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/hack-together/page/8","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/a4bab584.fa105b35.js b/assets/js/a4bab584.fa105b35.js new file mode 100644 index 0000000000..8dbf177ab5 --- /dev/null +++ b/assets/js/a4bab584.fa105b35.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[77856],{41121:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/hack-together/page/9","page":9,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/hack-together/page/8","nextPage":"/Cloud-Native/30daysofIA/tags/hack-together/page/10","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/a506a500.9e951485.js b/assets/js/a506a500.519b9970.js similarity index 72% rename from assets/js/a506a500.9e951485.js rename to assets/js/a506a500.519b9970.js index 23a401d1a1..89ff68824f 100644 --- a/assets/js/a506a500.9e951485.js +++ b/assets/js/a506a500.519b9970.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[21987],{1645:e=>{e.exports=JSON.parse('{"label":"community-buzz","permalink":"/Cloud-Native/30daysofIA/tags/community-buzz","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[21987],{1645:e=>{e.exports=JSON.parse('{"label":"community-buzz","permalink":"/Cloud-Native/30daysofIA/tags/community-buzz","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/a5551a6a.2ccaa12a.js b/assets/js/a5551a6a.2ccaa12a.js new file mode 100644 index 0000000000..cd8822e981 --- /dev/null +++ b/assets/js/a5551a6a.2ccaa12a.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[87084],{3905:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>m});var a=n(67294);function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function o(e){for(var t=1;t=0||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}var s=a.createContext({}),p=function(e){var t=a.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},u=function(e){var t=p(e.components);return a.createElement(s.Provider,{value:t},e.children)},c={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},d=a.forwardRef((function(e,t){var n=e.components,r=e.mdxType,i=e.originalType,s=e.parentName,u=l(e,["components","mdxType","originalType","parentName"]),d=p(n),m=r,g=d["".concat(s,".").concat(m)]||d[m]||c[m]||i;return n?a.createElement(g,o(o({ref:t},u),{},{components:n})):a.createElement(g,o({ref:t},u))}));function m(e,t){var n=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var i=n.length,o=new Array(i);o[0]=d;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l.mdxType="string"==typeof e?e:r,o[1]=l;for(var p=2;p{n.r(t),n.d(t,{assets:()=>s,contentTitle:()=>o,default:()=>c,frontMatter:()=>i,metadata:()=>l,toc:()=>p});var a=n(87462),r=(n(67294),n(3905));const i={date:"2023-09-22T09:00",slug:"build-your-first-intelligent-app-with-azure-ai-and-aks-2",title:"2-2. Build Your First Intelligent App with Azure AI and AKS-2",authors:["cnteam"],draft:!1,hide_table_of_contents:!1,toc_min_heading_level:2,toc_max_heading_level:3,keywords:["Cloud","Data","AI","AI/ML","intelligent apps","cloud-native","30-days","enterprise apps","digital experiences","app modernization","serverless","ai apps","data"],image:"https://azure.github.io/Cloud-Native/img/ogImage.png",description:"Delve into creating an Intelligent App that leverages Azure AI Vision to analyze images and extract data. We guide readers through crafting an API to perform optical character recognition (OCR) on uploaded images, and subsequently deploying this API via Azure Kubernetes Service, helping them discover the power of these tools to create innovative, AI-driven solutions.",tags:["Fall-For-IA","30-days-of-IA","learn-live","hack-together","community-buzz","ask-the-expert","azure-kubernetes-service","azure-functions","azure-openai","azure-container-apps","azure-cosmos-db","github-copilot","github-codespaces","github-actions"]},o=void 0,l={permalink:"/Cloud-Native/30daysofIA/build-your-first-intelligent-app-with-azure-ai-and-aks-2",source:"@site/blog-30daysofIA/2023-09-27/build-your-first-intelligent-app-with-azure-ai-and-aks-2.md",title:"2-2. Build Your First Intelligent App with Azure AI and AKS-2",description:"Delve into creating an Intelligent App that leverages Azure AI Vision to analyze images and extract data. We guide readers through crafting an API to perform optical character recognition (OCR) on uploaded images, and subsequently deploying this API via Azure Kubernetes Service, helping them discover the power of these tools to create innovative, AI-driven solutions.",date:"2023-09-22T09:00:00.000Z",formattedDate:"September 22, 2023",tags:[{label:"Fall-For-IA",permalink:"/Cloud-Native/30daysofIA/tags/fall-for-ia"},{label:"30-days-of-IA",permalink:"/Cloud-Native/30daysofIA/tags/30-days-of-ia"},{label:"learn-live",permalink:"/Cloud-Native/30daysofIA/tags/learn-live"},{label:"hack-together",permalink:"/Cloud-Native/30daysofIA/tags/hack-together"},{label:"community-buzz",permalink:"/Cloud-Native/30daysofIA/tags/community-buzz"},{label:"ask-the-expert",permalink:"/Cloud-Native/30daysofIA/tags/ask-the-expert"},{label:"azure-kubernetes-service",permalink:"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service"},{label:"azure-functions",permalink:"/Cloud-Native/30daysofIA/tags/azure-functions"},{label:"azure-openai",permalink:"/Cloud-Native/30daysofIA/tags/azure-openai"},{label:"azure-container-apps",permalink:"/Cloud-Native/30daysofIA/tags/azure-container-apps"},{label:"azure-cosmos-db",permalink:"/Cloud-Native/30daysofIA/tags/azure-cosmos-db"},{label:"github-copilot",permalink:"/Cloud-Native/30daysofIA/tags/github-copilot"},{label:"github-codespaces",permalink:"/Cloud-Native/30daysofIA/tags/github-codespaces"},{label:"github-actions",permalink:"/Cloud-Native/30daysofIA/tags/github-actions"}],readingTime:8.71,hasTruncateMarker:!1,authors:[{name:"It's 30DaysOfIA",title:"FallForIA Content Team",url:"https://azure.github.io/Cloud-Native/Fall-For-IA/",imageURL:"https://azure.github.io/Cloud-Native/img/logo-ms-cloud-native.png",key:"cnteam"}],frontMatter:{date:"2023-09-22T09:00",slug:"build-your-first-intelligent-app-with-azure-ai-and-aks-2",title:"2-2. Build Your First Intelligent App with Azure AI and AKS-2",authors:["cnteam"],draft:!1,hide_table_of_contents:!1,toc_min_heading_level:2,toc_max_heading_level:3,keywords:["Cloud","Data","AI","AI/ML","intelligent apps","cloud-native","30-days","enterprise apps","digital experiences","app modernization","serverless","ai apps","data"],image:"https://azure.github.io/Cloud-Native/img/ogImage.png",description:"Delve into creating an Intelligent App that leverages Azure AI Vision to analyze images and extract data. We guide readers through crafting an API to perform optical character recognition (OCR) on uploaded images, and subsequently deploying this API via Azure Kubernetes Service, helping them discover the power of these tools to create innovative, AI-driven solutions.",tags:["Fall-For-IA","30-days-of-IA","learn-live","hack-together","community-buzz","ask-the-expert","azure-kubernetes-service","azure-functions","azure-openai","azure-container-apps","azure-cosmos-db","github-copilot","github-codespaces","github-actions"]},prevItem:{title:"2-1. Build Your First Intelligent App with Azure AI and AKS-1",permalink:"/Cloud-Native/30daysofIA/build-your-first-intelligent-app-with-azure-ai-and-aks-1"},nextItem:{title:"1-4. How Digital Natives leverage Generative AI",permalink:"/Cloud-Native/30daysofIA/how-digital-natives-leverage-generative-ai"}},s={authorsImageUrls:[void 0]},p=[{value:"What We'll Cover:",id:"what-well-cover",level:2},{value:"Jumpstart Your AI Journey: Building Your First Intelligent App with Azure AI and AKS (2)",id:"jumpstart-your-ai-journey-building-your-first-intelligent-app-with-azure-ai-and-aks-2",level:2},{value:"Prerequisites",id:"prerequisites",level:3},{value:"Pushing a Container Image to Azure Container Registry (ACR)",id:"pushing-a-container-image-to-azure-container-registry-acr",level:2},{value:"Deploying the Intelligent App on Azure Kubernetes Service (AKS)",id:"deploying-the-intelligent-app-on-azure-kubernetes-service-aks",level:2},{value:"Testing the Intelligent App on AKS",id:"testing-the-intelligent-app-on-aks",level:2},{value:"Exercise",id:"exercise",level:2},{value:"Next Steps",id:"next-steps",level:2}],u={toc:p};function c(e){let{components:t,...i}=e;return(0,r.kt)("wrapper",(0,a.Z)({},u,i,{components:t,mdxType:"MDXLayout"}),(0,r.kt)("head",null,(0,r.kt)("meta",{property:"og:url",content:"https://azure.github.io/cloud-native/30daysofia/build-your-first-intelligent-app-with-azure-ai-and-aks-2"}),(0,r.kt)("meta",{property:"og:type",content:"website"}),(0,r.kt)("meta",{property:"og:title",content:"**Fall For Intelligent Apps! \ud83c\udf42| Build AI Apps On Azure"}),(0,r.kt)("meta",{property:"og:description",content:"Create an Intelligent App that leverages Azure AI Vision to analyze images and extract data."}),(0,r.kt)("meta",{property:"og:image",content:"https://azure.github.io/Cloud-Native/img/ogImage.png"}),(0,r.kt)("meta",{name:"twitter:url",content:"https://azure.github.io/Cloud-Native/30daysofIA/build-your-first-intelligent-app-with-azure-ai-and-aks-2"}),(0,r.kt)("meta",{name:"twitter:title",content:"**Fall For Intelligent Apps! \ud83c\udf42 | Build AI Apps On Azure"}),(0,r.kt)("meta",{name:"twitter:description",content:"2-2. Create an Intelligent App that leverages Azure AI Vision to analyze images and extract data."}),(0,r.kt)("meta",{name:"twitter:image",content:"https://azure.github.io/Cloud-Native/img/ogImage.png"}),(0,r.kt)("meta",{name:"twitter:card",content:"summary_large_image"}),(0,r.kt)("meta",{name:"twitter:creator",content:"@devanshidiaries"}),(0,r.kt)("meta",{name:"twitter:site",content:"@AzureAdvocates"}),(0,r.kt)("link",{rel:"canonical",href:"https://azure.github.io/Cloud-Native/30daysofIA/build-your-first-intelligent-app-with-azure-ai-and-aks-2"})),(0,r.kt)("p",null,"In the last article, we created an Intelligent App that leverages Azure AI Vision to analyze images and extract data. Develop an API to perform optical character recognition (OCR) on uploaded images and testing this API locally. "),(0,r.kt)("p",null,"In this article, we will deploy the web API to the cloud platform using Azure Kubernetes Service. "),(0,r.kt)("h2",{id:"what-well-cover"},"What We'll Cover:"),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},"Understanding Azure AI Vision and Azure Kubernetes Service"),(0,r.kt)("li",{parentName:"ul"},"Build a Python Web API to perform OCR"),(0,r.kt)("li",{parentName:"ul"},"Test the API locally")),(0,r.kt)("p",null,(0,r.kt)("img",{alt:"image depicting an Intelligent App using AI",src:n(87438).Z,width:"624",height:"380"})),(0,r.kt)("h2",{id:"jumpstart-your-ai-journey-building-your-first-intelligent-app-with-azure-ai-and-aks-2"},"Jumpstart Your AI Journey: Building Your First Intelligent App with Azure AI and AKS (2)"),(0,r.kt)("p",null,"In the previous article we explored the creation of an Intelligent App that leverages Azure AI Vision to analyze images and extract data. We learned how to build a Python Web API to perform OCR on uploaded images and subsequently test this API locally. "),(0,r.kt)("p",null,"In this article we will use ",(0,r.kt)("a",{parentName:"p",href:"https://azure.microsoft.com/products/kubernetes-service?WT.mc_id=javascript-99907-ninarasi"},"Azure Kubernetes Service")," (AKS) to develop, publish, and maintain our app in the cloud on Azure. "),(0,r.kt)("p",null,"Let\u2019s get started!"),(0,r.kt)("h3",{id:"prerequisites"},"Prerequisites"),(0,r.kt)("p",null,"To follow this tutorial, ensure you have completed ",(0,r.kt)("a",{parentName:"p",href:"https://azure.github.io/Cloud-Native/30daysofIA/build-your-first-intelligent-app-with-azure-ai-and-aks-1"},"Jumpstart Your AI Journey: Building Your First Intelligent App with Azure AI and AKS (1)"),"."),(0,r.kt)("h2",{id:"pushing-a-container-image-to-azure-container-registry-acr"},"Pushing a Container Image to Azure Container Registry (ACR)"),(0,r.kt)("p",null,"To start, open your CLI or terminal and type the following command:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"az login\n")),(0,r.kt)("p",null,"Follow the instructions displayed on your browser to enter your Azure credentials."),(0,r.kt)("p",null,"Once authenticated, you\u2019ll initiate a secure connection between your local environment and Azure. This process grants you access to cloud services and resources."),(0,r.kt)("p",null,"Next, type the command below in your terminal to set up a new Azure Container Registry (ACR) to store your container images:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"az acr create --resource-group computer-vision --name --sku Basic\n")),(0,r.kt)("p",null,"Remember to replace ",(0,r.kt)("inlineCode",{parentName:"p"},"")," with your container registry name. The name must be unique within Azure and comply with ",(0,r.kt)("a",{parentName:"p",href:"https://learn.microsoft.com/azure/azure-resource-manager/management/resource-name-rules#microsoftcontainerregistry"},"these rules"),"."),(0,r.kt)("p",null,"The command above creates an ",(0,r.kt)("a",{parentName:"p",href:"https://azure.microsoft.com/products/container-registry?WT.mc_id=javascript-99907-ninarasi"},"Azure Container Registry")," (ACR) in the ",(0,r.kt)("inlineCode",{parentName:"p"},"computer-vision")," resource group under the ",(0,r.kt)("a",{parentName:"p",href:"https://learn.microsoft.com/azure/container-registry/container-registry-skus?WT.mc_id=javascript-99907-ninarasi"},"Basic SKU"),". This ACR is your secure and private repository for storing container images within Azure."),(0,r.kt)("p",null,"Next, log in to the registry with the following command:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"az acr login -n \n")),(0,r.kt)("p",null,"The ",(0,r.kt)("inlineCode",{parentName:"p"},"az acr login")," command above lets you securely authenticate and access the specified ACR without providing their Azure credentials each time."),(0,r.kt)("p",null,"Now, run the following command in your terminal. It will display the endpoint URL to log in to and interact with the ACR for pushing and pulling container images."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"az acr show --name --query loginServer --output table\n")),(0,r.kt)("p",null,"This returns an endpoint URL as follows: "),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"Result\n----------------------------------\n.azurecr.io\n")),(0,r.kt)("p",null,"Now, run the following command to show all container images, their repository, tags, and size:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"docker images\n")),(0,r.kt)("table",null,(0,r.kt)("thead",{parentName:"table"},(0,r.kt)("tr",{parentName:"thead"},(0,r.kt)("th",{parentName:"tr",align:"left"},"REPOSITORY"),(0,r.kt)("th",{parentName:"tr",align:"left"},"TAG"),(0,r.kt)("th",{parentName:"tr",align:"left"},"IMAGE ID"),(0,r.kt)("th",{parentName:"tr",align:"left"},"CREATED"),(0,r.kt)("th",{parentName:"tr",align:"left"},"SIZE"))),(0,r.kt)("tbody",{parentName:"table"},(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:"left"},"intelligent-app"),(0,r.kt)("td",{parentName:"tr",align:"left"},"latest"),(0,r.kt)("td",{parentName:"tr",align:"left"},"a7bf9f753617"),(0,r.kt)("td",{parentName:"tr",align:"left"},"16 hours ago"),(0,r.kt)("td",{parentName:"tr",align:"left"},"197MB")))),(0,r.kt)("p",null,"Tags are necessary to push Docker images to a remote registry like Azure Container Registry. They also let you differentiate between different versions of the same image and upload or download the one you want."),(0,r.kt)("p",null,"Run the following command to tag your Docker image:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"docker tag intelligent-app .azurecr.io/intelligent-app:v1\n")),(0,r.kt)("p",null,"Then, run the ",(0,r.kt)("inlineCode",{parentName:"p"},"docker images")," command again to check your tagged image:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"docker images\n")),(0,r.kt)("table",null,(0,r.kt)("thead",{parentName:"table"},(0,r.kt)("tr",{parentName:"thead"},(0,r.kt)("th",{parentName:"tr",align:"left"},"REPOSITORY"),(0,r.kt)("th",{parentName:"tr",align:"left"},"TAG"),(0,r.kt)("th",{parentName:"tr",align:"left"},"IMAGE ID"),(0,r.kt)("th",{parentName:"tr",align:"left"},"CREATED"),(0,r.kt)("th",{parentName:"tr",align:"left"},"SIZE"))),(0,r.kt)("tbody",{parentName:"table"},(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:"left"},"intelligent-app"),(0,r.kt)("td",{parentName:"tr",align:"left"},"latest"),(0,r.kt)("td",{parentName:"tr",align:"left"},"c52168039265"),(0,r.kt)("td",{parentName:"tr",align:"left"},"About a minute ago"),(0,r.kt)("td",{parentName:"tr",align:"left"},"197MB")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:"left"},"<","name-of-azure-container-registry",">",".azurecr.io/intelligent-app"),(0,r.kt)("td",{parentName:"tr",align:"left"},"v1"),(0,r.kt)("td",{parentName:"tr",align:"left"},"c52168039265"),(0,r.kt)("td",{parentName:"tr",align:"left"},"About a minute ago"),(0,r.kt)("td",{parentName:"tr",align:"left"},"197MB")))),(0,r.kt)("p",null,"Now run the following command so Docker can securely upload the image to your Azure Container Registry:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"docker push .azurecr.io/intelligent-app:v1\n")),(0,r.kt)("p",null,"Once we've deployed the image to the container registry, AKS can access it during deployment."),(0,r.kt)("admonition",{type:"info"},(0,r.kt)("p",{parentName:"admonition"},"Watch ",(0,r.kt)("strong",{parentName:"p"},(0,r.kt)("a",{parentName:"strong",href:"https://aka.ms/learnlive-contoso-app-deconstructed-Ep3"},"Episode 03"))," for the Learn Live session to experience a guided session on how to build, test and deploy an end-to-end intelligent app solution.")),(0,r.kt)("h2",{id:"deploying-the-intelligent-app-on-azure-kubernetes-service-aks"},"Deploying the Intelligent App on Azure Kubernetes Service (AKS)"),(0,r.kt)("p",null,"Before we deploy our Intelligent App to AKS, we need to provision an AKS cluster and define Kubernetes manifests."),(0,r.kt)("p",null,"To provision an AKS cluster to host our application, we specify the desired configurations for the cluster, such as the number of nodes, node size, and networking options. But first, download and install the Kubernetes command-line tools (kubectl), a client credential plugin implementing Azure authentication:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"az aks install-cli\n")),(0,r.kt)("p",null,"If you\u2019re using Linux, ",(0,r.kt)("a",{parentName:"p",href:"https://learn.microsoft.com/en-us/cli/azure/install-azure-cli-linux?pivots=apt?WT.mc_id=javascript-99907-ninarasi"},"review this tutorial"),". Then run the following: "),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"sudo az aks install-cli\n")),(0,r.kt)("p",null,"Next, run the following command in your terminal to enable access to the networking-related resources and services provided by the `Microsoft.Network`` namespace in Azure: "),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"az provider register --namespace Microsoft.Network\n")),(0,r.kt)("p",null,"Now, we must create an AKS cluster. Run the following command to create an AKS cluster named ",(0,r.kt)("inlineCode",{parentName:"p"},"aks-intelligent-app")," in the ",(0,r.kt)("inlineCode",{parentName:"p"},"computer-vision")," resource group."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"az aks create --resource-group computer-vision --name aks-intelligent-app --node-count 1 --generate-ssh-keys\n")),(0,r.kt)("p",null,"The command above specifies the target resource group: ",(0,r.kt)("inlineCode",{parentName:"p"},"computer-vision"),". The ",(0,r.kt)("a",{parentName:"p",href:"https://learn.microsoft.com/en-us/azure/aks/use-multiple-node-pools?WT.mc_id=javascript-99907-ninarasi"},"node pool")," is configured with one virtual machine (VM), and the secure shell (SSH) keys for secure node access are generated automatically."),(0,r.kt)("p",null,"Next, run the following command to update the AKS cluster you created by attaching it to your ACR. Doing so allows the AKS cluster to pull container images from the specified ACR when deploying workloads to the cluster."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"az aks update -n aks-intelligent-app -g computer-vision --attach-acr \n")),(0,r.kt)("p",null,"Then, run the command below to configure kubectl to work with our AKS cluster in the computer-vision resource group. "),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"az aks get-credentials --resource-group computer-vision --name aks-intelligent-app\n")),(0,r.kt)("p",null,"The command above retrieves the necessary credentials and context information required for kubectl to communicate with the AKS cluster."),(0,r.kt)("p",null,"We still have to define the Kubernetes manifests written in YAML, describing the desired state of our application deployment, including containers, networking, and scaling rules. We\u2019ll prepare these manifests, including ",(0,r.kt)("a",{parentName:"p",href:"https://kubernetes.io/docs/concepts/workloads/controllers/deployment/"},"Deployment")," and ",(0,r.kt)("a",{parentName:"p",href:"https://kubernetes.io/docs/concepts/services-networking/service/"},"Service")," configurations, to define how our application should be deployed and exposed."),(0,r.kt)("p",null,"Create a folder named ",(0,r.kt)("inlineCode",{parentName:"p"},"Deployment")," in the project root directory. Next, create two files in the deployment folder: ",(0,r.kt)("inlineCode",{parentName:"p"},"deployment.yml")," and ",(0,r.kt)("inlineCode",{parentName:"p"},"service.yml"),"."),(0,r.kt)("p",null,"Add the following configuration to the ",(0,r.kt)("inlineCode",{parentName:"p"},"deployment.yml")," file, replacing the ",(0,r.kt)("inlineCode",{parentName:"p"},"")," placeholder with your registry\u2019s name:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},'apiVersion: apps/v1\nkind: Deployment\nmetadata:\n name: intelligent-app\nspec:\n replicas: 1\n selector:\n matchLabels:\n app: intelligent-app\n template:\n metadata:\n labels:\n app: intelligent-app\n spec:\n nodeSelector:\n kubernetes.io/os: linux\n containers:\n - name: intelligent-app\n image: .azurecr.io/intelligent-app:v1\n resources:\n limits:\n memory: 512Mi\n cpu: "1"\n requests:\n memory: 256Mi\n cpu: "0.2"\n ports:\n - containerPort: 5000\n env:\n - name: FLASK_DEBUG\n value: "1"\n - name: VISION_KEY\n value: \n - name: VISION_ENDPOINT\n value: \n')),(0,r.kt)("p",null,"Additionally, edit the ",(0,r.kt)("inlineCode",{parentName:"p"},"VISION_KEY")," and ",(0,r.kt)("inlineCode",{parentName:"p"},"VISION_ENDPOINT")," environment variables above according to the API key and endpoint of your Azure AI Services instance."),(0,r.kt)("p",null,"Then, add the following configuration to the ",(0,r.kt)("inlineCode",{parentName:"p"},"service.yml")," file:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"apiVersion: v1\nkind: Service \nmetadata:\n name: intelligent-app-service\nspec:\n type: LoadBalancer\n ports:\n - protocol: TCP\n port: 80\n targetPort: 5000\n name: port5000\n selector:\n app: intelligent-app\n")),(0,r.kt)("p",null,"Now, we\u2019ll deploy our application using kubectl. Applying the Kubernetes manifests creates the necessary resources and deploys our containerized application to the AKS cluster."),(0,r.kt)("p",null,"First, change the terminal to the ",(0,r.kt)("inlineCode",{parentName:"p"},"deployment")," folder: "),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"cd Deployment \n")),(0,r.kt)("p",null,"Then, run the following command to create or update Kubernetes resources defined in the ",(0,r.kt)("inlineCode",{parentName:"p"},"deployment.yml")," file:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"kubectl apply -f deployment.yml\n")),(0,r.kt)("p",null,"Create a Kubernetes Service resource in a Kubernetes cluster based on the configuration defined in the ",(0,r.kt)("inlineCode",{parentName:"p"},"service.yml")," file using the code below: "),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"kubectl apply -f service.yml\n")),(0,r.kt)("p",null,"Once you\u2019ve applied the resource definition and the service configuration contained in the ",(0,r.kt)("inlineCode",{parentName:"p"},"deployment.yml")," and the ",(0,r.kt)("inlineCode",{parentName:"p"},"service.yml")," files, open the ",(0,r.kt)("strong",{parentName:"p"},"aks-intelligent-app")," Kubernetes Service in the Azure Portal, select ",(0,r.kt)("strong",{parentName:"p"},"Workloads")," under ",(0,r.kt)("strong",{parentName:"p"},"Kubernetes resources")," on the sidebar, and find the deployment named ",(0,r.kt)("strong",{parentName:"p"},"intelligent-app"),". It must have the status \u201cReady 1/1\u201d. If you encounter an issue with this status, check out these ",(0,r.kt)("a",{parentName:"p",href:"https://learn.microsoft.com/en-us/troubleshoot/azure/azure-kubernetes/node-not-ready-basic-troubleshooting?WT.mc_id=javascript-99907-ninarasi"},"troubleshooting resources"),"."),(0,r.kt)("p",null,(0,r.kt)("img",{alt:"image of selecting the deployment in Azure Portal",src:n(5669).Z,width:"624",height:"318"})),(0,r.kt)("h2",{id:"testing-the-intelligent-app-on-aks"},"Testing the Intelligent App on AKS"),(0,r.kt)("p",null,"To test the app on AKS, first, run the command below:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"kubectl get services\n")),(0,r.kt)("p",null,"This command lists the Services and their corresponding details, including the Service name, cluster IP address, external IP, and ports."),(0,r.kt)("table",null,(0,r.kt)("thead",{parentName:"table"},(0,r.kt)("tr",{parentName:"thead"},(0,r.kt)("th",{parentName:"tr",align:"left"},"NAME"),(0,r.kt)("th",{parentName:"tr",align:"left"},"TYPE"),(0,r.kt)("th",{parentName:"tr",align:"left"},"CLUSTER-IP"),(0,r.kt)("th",{parentName:"tr",align:"left"},"EXTERNAL-IP"),(0,r.kt)("th",{parentName:"tr",align:"left"},"PORT(S)"),(0,r.kt)("th",{parentName:"tr",align:"left"},"AGE"))),(0,r.kt)("tbody",{parentName:"table"},(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:"left"},"intelligent-app-service"),(0,r.kt)("td",{parentName:"tr",align:"left"},"LoadBalancer"),(0,r.kt)("td",{parentName:"tr",align:"left"},"10.0.77.60"),(0,r.kt)("td",{parentName:"tr",align:"left"},"20.121.76.153"),(0,r.kt)("td",{parentName:"tr",align:"left"},"80:30936/TCP"),(0,r.kt)("td",{parentName:"tr",align:"left"},"47s")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:"left"},"kubernetes"),(0,r.kt)("td",{parentName:"tr",align:"left"},"ClusterIP"),(0,r.kt)("td",{parentName:"tr",align:"left"},"10.0.0.1"),(0,r.kt)("td",{parentName:"tr",align:"left"},"<","none",">"),(0,r.kt)("td",{parentName:"tr",align:"left"},"443/TCP"),(0,r.kt)("td",{parentName:"tr",align:"left"},"14m")))),(0,r.kt)("p",null,"The output above shows a Kubernetes Service named ",(0,r.kt)("inlineCode",{parentName:"p"},"intelligent-app-service")," with a type set to ",(0,r.kt)("inlineCode",{parentName:"p"},"LoadBalancer"),". It\u2019s reachable from within the cluster using the cluster IP ",(0,r.kt)("inlineCode",{parentName:"p"},"10.0.77.60")," and accessible externally via the external IP ",(0,r.kt)("inlineCode",{parentName:"p"},"20.121.76.153")," on port 80 (mapped to port 30936)."),(0,r.kt)("p",null,(0,r.kt)("strong",{parentName:"p"},"Note"),": Your set of IP addresses will differ. Remember to use your unique external IP address when testing with Postman. "),(0,r.kt)("p",null,"To test the deployed app, go to Postman, replace the URL with the external IP of the Kubernetes Service you just deployed, and click ",(0,r.kt)("strong",{parentName:"p"},"Send"),":"),(0,r.kt)("p",null,(0,r.kt)("img",{alt:"mage of sending the app in Postman",src:n(8422).Z,width:"624",height:"365"})),(0,r.kt)("p",null,"As we can see, our Intelligent App has successfully deployed to AKS and is functioning on the cloud as expected."),(0,r.kt)("h2",{id:"exercise"},"Exercise"),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},"Complete this ",(0,r.kt)("strong",{parentName:"li"},"hands-on")," sample ",(0,r.kt)("a",{parentName:"li",href:"https://github.com/contentlab-io/Microsoft-Using-Azure-Kubernetes-Service-to-Deploy-an-Intelligent-App-for-Analyzing-Images-1/tree/main/Microsoft_Series17-18_Code/intelligent-app-after"},"project code")," to build your first intelligent app."),(0,r.kt)("li",{parentName:"ul"},"Complete the ",(0,r.kt)("a",{parentName:"li",href:"https://aka.ms/fallforIA/apps-csc"},"Apps Cloud Skills Challenge")," to build on your app dev and AI skills."),(0,r.kt)("li",{parentName:"ul"},"Register for ",(0,r.kt)("a",{parentName:"li",href:"https://reactor.microsoft.com/en-us/reactor/series/S-1037/"},"Ask the Expert: Azure Kubernetes Service")," session for live Q&A with the Product Engineering team on building intelligent apps.")),(0,r.kt)("h2",{id:"next-steps"},"Next Steps"),(0,r.kt)("p",null,"In this two-part article, we explored the creation of an Intelligent App that leverages Azure AI Vision to analyze images and extract data. We learned how to build a Python Web API to perform OCR on uploaded images and subsequently deploy this API via Azure Kubernetes Service."),(0,r.kt)("p",null,"Besides OCR and Image Analysis, you can continue exploring Azure\u2019s vast array of services and experiment further with Azure AI and AKS by applying various practical uses to your Intelligent Apps, including natural language processing, speech recognition and synthesis, sentiment analysis for customer feedback, and automated content moderation."))}c.isMDXComponent=!0},87438:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/blog-image-2-1-974d3a64c66cbcd6e014779113bc1a4b.jpeg"},5669:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/blog-image-2-2-fb13fe692dc47f7d90de361ec6d5b771.png"},8422:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/blog-image-2-3-d8cd3a3026fb47d4589f3ee5baa3de5d.png"}}]); \ No newline at end of file diff --git a/assets/js/a60bbbfe.b43c9ef3.js b/assets/js/a60bbbfe.b43c9ef3.js new file mode 100644 index 0000000000..55e23e1615 --- /dev/null +++ b/assets/js/a60bbbfe.b43c9ef3.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[79367],{82160:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/github-copilot","page":1,"postsPerPage":1,"totalPages":11,"totalCount":11,"nextPage":"/Cloud-Native/30daysofIA/tags/github-copilot/page/2","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/a60bbbfe.fb7caf18.js b/assets/js/a60bbbfe.fb7caf18.js deleted file mode 100644 index 366c811317..0000000000 --- a/assets/js/a60bbbfe.fb7caf18.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[79367],{82160:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/github-copilot","page":1,"postsPerPage":1,"totalPages":9,"totalCount":9,"nextPage":"/Cloud-Native/30daysofIA/tags/github-copilot/page/2","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/a682bc17.920f04db.js b/assets/js/a682bc17.920f04db.js new file mode 100644 index 0000000000..83c6712128 --- /dev/null +++ b/assets/js/a682bc17.920f04db.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[77971],{46667:a=>{a.exports=JSON.parse('{"label":"30-days-of-IA","permalink":"/Cloud-Native/30daysofIA/tags/30-days-of-ia","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/a6ad4213.5ec66a35.js b/assets/js/a6ad4213.f48cbaaf.js similarity index 73% rename from assets/js/a6ad4213.5ec66a35.js rename to assets/js/a6ad4213.f48cbaaf.js index 05e776dce6..14486f4eec 100644 --- a/assets/js/a6ad4213.5ec66a35.js +++ b/assets/js/a6ad4213.f48cbaaf.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[43622],{26296:a=>{a.exports=JSON.parse('{"label":"30-days-of-IA","permalink":"/Cloud-Native/30daysofIA/tags/30-days-of-ia","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[43622],{26296:a=>{a.exports=JSON.parse('{"label":"30-days-of-IA","permalink":"/Cloud-Native/30daysofIA/tags/30-days-of-ia","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/a787e11b.3cc48baa.js b/assets/js/a787e11b.3cc48baa.js deleted file mode 100644 index cf15842b47..0000000000 --- a/assets/js/a787e11b.3cc48baa.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[79258],{27711:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/github-copilot/page/8","page":8,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/github-copilot/page/7","nextPage":"/Cloud-Native/30daysofIA/tags/github-copilot/page/9","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/a787e11b.db1f02b3.js b/assets/js/a787e11b.db1f02b3.js new file mode 100644 index 0000000000..4b0e5fb704 --- /dev/null +++ b/assets/js/a787e11b.db1f02b3.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[79258],{27711:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/github-copilot/page/8","page":8,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/github-copilot/page/7","nextPage":"/Cloud-Native/30daysofIA/tags/github-copilot/page/9","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/a843e963.5e189999.js b/assets/js/a843e963.5e189999.js deleted file mode 100644 index c1483ebe09..0000000000 --- a/assets/js/a843e963.5e189999.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[33588],{1103:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/github-codespaces/page/6","page":6,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/github-codespaces/page/5","nextPage":"/Cloud-Native/30daysofIA/tags/github-codespaces/page/7","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/a843e963.e9dafc2d.js b/assets/js/a843e963.e9dafc2d.js new file mode 100644 index 0000000000..7875c38770 --- /dev/null +++ b/assets/js/a843e963.e9dafc2d.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[33588],{1103:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/github-codespaces/page/6","page":6,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/github-codespaces/page/5","nextPage":"/Cloud-Native/30daysofIA/tags/github-codespaces/page/7","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/a89c2360.fcbd9632.js b/assets/js/a89c2360.e2b43333.js similarity index 90% rename from assets/js/a89c2360.fcbd9632.js rename to assets/js/a89c2360.e2b43333.js index 2b34f6d47b..92e0a329f9 100644 --- a/assets/js/a89c2360.fcbd9632.js +++ b/assets/js/a89c2360.e2b43333.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[56407],{9822:e=>{e.exports=JSON.parse('{"label":"azure-kubernetes-service","permalink":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[56407],{9822:e=>{e.exports=JSON.parse('{"label":"azure-kubernetes-service","permalink":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/a8dbe756.cc6b938c.js b/assets/js/a8dbe756.cc6b938c.js deleted file mode 100644 index b666fbc210..0000000000 --- a/assets/js/a8dbe756.cc6b938c.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[88350],{45051:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-container-apps/page/4","page":4,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/azure-container-apps/page/3","nextPage":"/Cloud-Native/30daysofIA/tags/azure-container-apps/page/5","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/a8dbe756.d6c565de.js b/assets/js/a8dbe756.d6c565de.js new file mode 100644 index 0000000000..f3eebcf663 --- /dev/null +++ b/assets/js/a8dbe756.d6c565de.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[88350],{45051:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-container-apps/page/4","page":4,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/azure-container-apps/page/3","nextPage":"/Cloud-Native/30daysofIA/tags/azure-container-apps/page/5","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/aa8ea87d.0dae27aa.js b/assets/js/aa8ea87d.9b19d907.js similarity index 74% rename from assets/js/aa8ea87d.0dae27aa.js rename to assets/js/aa8ea87d.9b19d907.js index c51d04e75a..d545e48a1f 100644 --- a/assets/js/aa8ea87d.0dae27aa.js +++ b/assets/js/aa8ea87d.9b19d907.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[39326],{64943:e=>{e.exports=JSON.parse('{"label":"learn-live","permalink":"/Cloud-Native/30daysofIA/tags/learn-live","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[39326],{64943:e=>{e.exports=JSON.parse('{"label":"learn-live","permalink":"/Cloud-Native/30daysofIA/tags/learn-live","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/abf26052.e78788a5.js b/assets/js/abf26052.16b0ff59.js similarity index 72% rename from assets/js/abf26052.e78788a5.js rename to assets/js/abf26052.16b0ff59.js index 8b222372e6..ab62a3924c 100644 --- a/assets/js/abf26052.e78788a5.js +++ b/assets/js/abf26052.16b0ff59.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[80570],{72527:e=>{e.exports=JSON.parse('{"label":"community-buzz","permalink":"/Cloud-Native/30daysofIA/tags/community-buzz","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[80570],{72527:e=>{e.exports=JSON.parse('{"label":"community-buzz","permalink":"/Cloud-Native/30daysofIA/tags/community-buzz","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/ac0e80dd.bec66106.js b/assets/js/ac0e80dd.f0e06a6e.js similarity index 75% rename from assets/js/ac0e80dd.bec66106.js rename to assets/js/ac0e80dd.f0e06a6e.js index 89e86aa3ac..72ede46cdc 100644 --- a/assets/js/ac0e80dd.bec66106.js +++ b/assets/js/ac0e80dd.f0e06a6e.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[90479],{8471:e=>{e.exports=JSON.parse('{"label":"learn-live","permalink":"/Cloud-Native/30daysofIA/tags/learn-live","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[90479],{8471:e=>{e.exports=JSON.parse('{"label":"learn-live","permalink":"/Cloud-Native/30daysofIA/tags/learn-live","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/acfc78c4.3778ff8f.js b/assets/js/acfc78c4.f8739257.js similarity index 95% rename from assets/js/acfc78c4.3778ff8f.js rename to assets/js/acfc78c4.f8739257.js index 6e68e9f155..46ba96332b 100644 --- a/assets/js/acfc78c4.3778ff8f.js +++ b/assets/js/acfc78c4.f8739257.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[33090],{90034:e=>{e.exports=JSON.parse('{"label":"azure-functions","permalink":"/Cloud-Native/30daysofIA/tags/azure-functions","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[33090],{90034:e=>{e.exports=JSON.parse('{"label":"azure-functions","permalink":"/Cloud-Native/30daysofIA/tags/azure-functions","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/adc56754.88e46af7.js b/assets/js/adc56754.88e46af7.js new file mode 100644 index 0000000000..4a735289a1 --- /dev/null +++ b/assets/js/adc56754.88e46af7.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[74792],{12469:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/github-actions/page/8","page":8,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/github-actions/page/7","nextPage":"/Cloud-Native/30daysofIA/tags/github-actions/page/9","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/adc56754.c85fd86a.js b/assets/js/adc56754.c85fd86a.js deleted file mode 100644 index a7d52afdb2..0000000000 --- a/assets/js/adc56754.c85fd86a.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[74792],{12469:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/github-actions/page/8","page":8,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/github-actions/page/7","nextPage":"/Cloud-Native/30daysofIA/tags/github-actions/page/9","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/aeb62146.43d79204.js b/assets/js/aeb62146.59789cea.js similarity index 89% rename from assets/js/aeb62146.43d79204.js rename to assets/js/aeb62146.59789cea.js index e6deeb9d7c..458138a035 100644 --- a/assets/js/aeb62146.43d79204.js +++ b/assets/js/aeb62146.59789cea.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[50730],{98298:e=>{e.exports=JSON.parse('{"label":"azure-kubernetes-service","permalink":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[50730],{98298:e=>{e.exports=JSON.parse('{"label":"azure-kubernetes-service","permalink":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/afc3e988.b4f9b01c.js b/assets/js/afc3e988.e856ccd5.js similarity index 95% rename from assets/js/afc3e988.b4f9b01c.js rename to assets/js/afc3e988.e856ccd5.js index e1c6c42127..17f3d36fef 100644 --- a/assets/js/afc3e988.b4f9b01c.js +++ b/assets/js/afc3e988.e856ccd5.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[66975],{80842:s=>{s.exports=JSON.parse('{"label":"azure-cosmos-db","permalink":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[66975],{80842:s=>{s.exports=JSON.parse('{"label":"azure-cosmos-db","permalink":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/b34c50e5.ef8d6e62.js b/assets/js/b34c50e5.3b84da93.js similarity index 95% rename from assets/js/b34c50e5.ef8d6e62.js rename to assets/js/b34c50e5.3b84da93.js index aaf91a5f8d..b5c16633b0 100644 --- a/assets/js/b34c50e5.ef8d6e62.js +++ b/assets/js/b34c50e5.3b84da93.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[25794],{95852:e=>{e.exports=JSON.parse('{"label":"azure-functions","permalink":"/Cloud-Native/30daysofIA/tags/azure-functions","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[25794],{95852:e=>{e.exports=JSON.parse('{"label":"azure-functions","permalink":"/Cloud-Native/30daysofIA/tags/azure-functions","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/b34f0f06.022baf69.js b/assets/js/b34f0f06.55109f43.js similarity index 75% rename from assets/js/b34f0f06.022baf69.js rename to assets/js/b34f0f06.55109f43.js index d5d5ee8680..e5c6d5d938 100644 --- a/assets/js/b34f0f06.022baf69.js +++ b/assets/js/b34f0f06.55109f43.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[24815],{476:e=>{e.exports=JSON.parse('{"label":"learn-live","permalink":"/Cloud-Native/30daysofIA/tags/learn-live","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[24815],{476:e=>{e.exports=JSON.parse('{"label":"learn-live","permalink":"/Cloud-Native/30daysofIA/tags/learn-live","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/b62d91fe.140a584e.js b/assets/js/b62d91fe.140a584e.js new file mode 100644 index 0000000000..c9a3f28fe2 --- /dev/null +++ b/assets/js/b62d91fe.140a584e.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[71471],{55453:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db/page/11","page":11,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db/page/10","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/b6fb65e1.b5e71854.js b/assets/js/b6fb65e1.5e333588.js similarity index 72% rename from assets/js/b6fb65e1.b5e71854.js rename to assets/js/b6fb65e1.5e333588.js index 0d67e9a9dc..819f49feb7 100644 --- a/assets/js/b6fb65e1.b5e71854.js +++ b/assets/js/b6fb65e1.5e333588.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[57924],{67301:a=>{a.exports=JSON.parse('{"label":"github-actions","permalink":"/Cloud-Native/30daysofIA/tags/github-actions","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[57924],{67301:a=>{a.exports=JSON.parse('{"label":"github-actions","permalink":"/Cloud-Native/30daysofIA/tags/github-actions","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/b70dd8a8.35052561.js b/assets/js/b70dd8a8.35052561.js deleted file mode 100644 index 31d876e02f..0000000000 --- a/assets/js/b70dd8a8.35052561.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[15854],{8600:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/30-days-of-ia/page/9","page":9,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/30-days-of-ia/page/8","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/b70dd8a8.c323d911.js b/assets/js/b70dd8a8.c323d911.js new file mode 100644 index 0000000000..6c9a41b11d --- /dev/null +++ b/assets/js/b70dd8a8.c323d911.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[15854],{8600:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/30-days-of-ia/page/9","page":9,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/30-days-of-ia/page/8","nextPage":"/Cloud-Native/30daysofIA/tags/30-days-of-ia/page/10","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/b760ee9a.9cdd7bcd.js b/assets/js/b760ee9a.5414a064.js similarity index 73% rename from assets/js/b760ee9a.9cdd7bcd.js rename to assets/js/b760ee9a.5414a064.js index 96cac9c5a8..e84a5fdee1 100644 --- a/assets/js/b760ee9a.9cdd7bcd.js +++ b/assets/js/b760ee9a.5414a064.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[9335],{80456:e=>{e.exports=JSON.parse('{"label":"hack-together","permalink":"/Cloud-Native/30daysofIA/tags/hack-together","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[9335],{80456:e=>{e.exports=JSON.parse('{"label":"hack-together","permalink":"/Cloud-Native/30daysofIA/tags/hack-together","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/b826954e.813adb62.js b/assets/js/b826954e.813adb62.js new file mode 100644 index 0000000000..baab6ed740 --- /dev/null +++ b/assets/js/b826954e.813adb62.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[33042],{91933:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/page/9","page":9,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/page/8","nextPage":"/Cloud-Native/30daysofIA/page/10","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/b826954e.e7d16a2c.js b/assets/js/b826954e.e7d16a2c.js deleted file mode 100644 index 1762f72e3d..0000000000 --- a/assets/js/b826954e.e7d16a2c.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[33042],{91933:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/page/9","page":9,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/page/8","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/b860d9a8.94caac31.js b/assets/js/b860d9a8.c31c5887.js similarity index 94% rename from assets/js/b860d9a8.94caac31.js rename to assets/js/b860d9a8.c31c5887.js index 06ca2ee85f..fea0046a50 100644 --- a/assets/js/b860d9a8.94caac31.js +++ b/assets/js/b860d9a8.c31c5887.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[3248],{17626:e=>{e.exports=JSON.parse('{"label":"github-codespaces","permalink":"/Cloud-Native/30daysofIA/tags/github-codespaces","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[3248],{17626:e=>{e.exports=JSON.parse('{"label":"github-codespaces","permalink":"/Cloud-Native/30daysofIA/tags/github-codespaces","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/b95e1cc2.e4128be0.js b/assets/js/b95e1cc2.e4128be0.js new file mode 100644 index 0000000000..8e554a1f2b --- /dev/null +++ b/assets/js/b95e1cc2.e4128be0.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[78541],{86919:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/learn-live/page/10","page":10,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/learn-live/page/9","nextPage":"/Cloud-Native/30daysofIA/tags/learn-live/page/11","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/bbae7a42.9919242e.js b/assets/js/bbae7a42.9919242e.js new file mode 100644 index 0000000000..a9086f2b3a --- /dev/null +++ b/assets/js/bbae7a42.9919242e.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[43283],{72990:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-container-apps/page/10","page":10,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/azure-container-apps/page/9","nextPage":"/Cloud-Native/30daysofIA/tags/azure-container-apps/page/11","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/bd2cd00b.5a65b5d3.js b/assets/js/bd2cd00b.5a65b5d3.js deleted file mode 100644 index 285e6cd1f8..0000000000 --- a/assets/js/bd2cd00b.5a65b5d3.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[38277],{12456:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-functions/page/4","page":4,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/azure-functions/page/3","nextPage":"/Cloud-Native/30daysofIA/tags/azure-functions/page/5","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/bd2cd00b.9171a2a1.js b/assets/js/bd2cd00b.9171a2a1.js new file mode 100644 index 0000000000..886130a327 --- /dev/null +++ b/assets/js/bd2cd00b.9171a2a1.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[38277],{12456:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-functions/page/4","page":4,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/azure-functions/page/3","nextPage":"/Cloud-Native/30daysofIA/tags/azure-functions/page/5","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/bd546f50.e1a8eedc.js b/assets/js/bd546f50.1939fe14.js similarity index 74% rename from assets/js/bd546f50.e1a8eedc.js rename to assets/js/bd546f50.1939fe14.js index 190a5ec9ba..d88bb240d3 100644 --- a/assets/js/bd546f50.e1a8eedc.js +++ b/assets/js/bd546f50.1939fe14.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[37345],{52663:a=>{a.exports=JSON.parse('{"label":"Fall-For-IA","permalink":"/Cloud-Native/30daysofIA/tags/fall-for-ia","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[37345],{52663:a=>{a.exports=JSON.parse('{"label":"Fall-For-IA","permalink":"/Cloud-Native/30daysofIA/tags/fall-for-ia","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/c0e56ffe.bf51f60d.js b/assets/js/c0e56ffe.2b3d51b5.js similarity index 73% rename from assets/js/c0e56ffe.bf51f60d.js rename to assets/js/c0e56ffe.2b3d51b5.js index 8881fbbc5b..6dd7560688 100644 --- a/assets/js/c0e56ffe.bf51f60d.js +++ b/assets/js/c0e56ffe.2b3d51b5.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[4270],{86570:a=>{a.exports=JSON.parse('{"label":"30-days-of-IA","permalink":"/Cloud-Native/30daysofIA/tags/30-days-of-ia","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[4270],{86570:a=>{a.exports=JSON.parse('{"label":"30-days-of-IA","permalink":"/Cloud-Native/30daysofIA/tags/30-days-of-ia","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/c0facb88.ca2fd49c.js b/assets/js/c0facb88.0c3864d1.js similarity index 94% rename from assets/js/c0facb88.ca2fd49c.js rename to assets/js/c0facb88.0c3864d1.js index f591a664f4..944a054d87 100644 --- a/assets/js/c0facb88.ca2fd49c.js +++ b/assets/js/c0facb88.0c3864d1.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[17958],{44423:e=>{e.exports=JSON.parse('{"label":"github-codespaces","permalink":"/Cloud-Native/30daysofIA/tags/github-codespaces","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[17958],{44423:e=>{e.exports=JSON.parse('{"label":"github-codespaces","permalink":"/Cloud-Native/30daysofIA/tags/github-codespaces","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/c141a364.3cb72f76.js b/assets/js/c141a364.3cb72f76.js new file mode 100644 index 0000000000..f0b2e8ea1a --- /dev/null +++ b/assets/js/c141a364.3cb72f76.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[74597],{3199:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-openai/page/11","page":11,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/azure-openai/page/10","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/c1fb8ad8.42c7a1ab.js b/assets/js/c1fb8ad8.42c7a1ab.js new file mode 100644 index 0000000000..c6d4b33445 --- /dev/null +++ b/assets/js/c1fb8ad8.42c7a1ab.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[82880],{19939:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/github-actions/page/5","page":5,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/github-actions/page/4","nextPage":"/Cloud-Native/30daysofIA/tags/github-actions/page/6","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/c1fb8ad8.71d94677.js b/assets/js/c1fb8ad8.71d94677.js deleted file mode 100644 index a7753f9186..0000000000 --- a/assets/js/c1fb8ad8.71d94677.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[82880],{19939:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/github-actions/page/5","page":5,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/github-actions/page/4","nextPage":"/Cloud-Native/30daysofIA/tags/github-actions/page/6","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/c2319041.d383eb5c.js b/assets/js/c2319041.56343153.js similarity index 72% rename from assets/js/c2319041.d383eb5c.js rename to assets/js/c2319041.56343153.js index d13d3d2a54..44da0d2fb4 100644 --- a/assets/js/c2319041.d383eb5c.js +++ b/assets/js/c2319041.56343153.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[3857],{98804:a=>{a.exports=JSON.parse('{"label":"github-actions","permalink":"/Cloud-Native/30daysofIA/tags/github-actions","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[3857],{98804:a=>{a.exports=JSON.parse('{"label":"github-actions","permalink":"/Cloud-Native/30daysofIA/tags/github-actions","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/c2e0ced8.43ee5c30.js b/assets/js/c2e0ced8.43ee5c30.js deleted file mode 100644 index 2c3d7d5f52..0000000000 --- a/assets/js/c2e0ced8.43ee5c30.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[69526],{14361:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/ask-the-expert/page/3","page":3,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/ask-the-expert/page/2","nextPage":"/Cloud-Native/30daysofIA/tags/ask-the-expert/page/4","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/c2e0ced8.5c7389e6.js b/assets/js/c2e0ced8.5c7389e6.js new file mode 100644 index 0000000000..d899d22cd7 --- /dev/null +++ b/assets/js/c2e0ced8.5c7389e6.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[69526],{14361:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/ask-the-expert/page/3","page":3,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/ask-the-expert/page/2","nextPage":"/Cloud-Native/30daysofIA/tags/ask-the-expert/page/4","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/c35448b1.2081a12b.js b/assets/js/c35448b1.2081a12b.js new file mode 100644 index 0000000000..43fe70ad97 --- /dev/null +++ b/assets/js/c35448b1.2081a12b.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[6175],{11222:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-openai/page/4","page":4,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/azure-openai/page/3","nextPage":"/Cloud-Native/30daysofIA/tags/azure-openai/page/5","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/c35448b1.b9fc8a3d.js b/assets/js/c35448b1.b9fc8a3d.js deleted file mode 100644 index e91e867c7b..0000000000 --- a/assets/js/c35448b1.b9fc8a3d.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[6175],{11222:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-openai/page/4","page":4,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/azure-openai/page/3","nextPage":"/Cloud-Native/30daysofIA/tags/azure-openai/page/5","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/c3591ce3.f7b7a154.js b/assets/js/c3591ce3.f7b7a154.js new file mode 100644 index 0000000000..823114fcb4 --- /dev/null +++ b/assets/js/c3591ce3.f7b7a154.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[44250],{8288:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/fall-for-ia/page/11","page":11,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/fall-for-ia/page/10","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/c5298e55.748e52f9.js b/assets/js/c5298e55.748e52f9.js new file mode 100644 index 0000000000..8c4f985292 --- /dev/null +++ b/assets/js/c5298e55.748e52f9.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[95930],{31249:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-functions/page/2","page":2,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/azure-functions","nextPage":"/Cloud-Native/30daysofIA/tags/azure-functions/page/3","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/c5298e55.d8b0f9ee.js b/assets/js/c5298e55.d8b0f9ee.js deleted file mode 100644 index a2163a78d5..0000000000 --- a/assets/js/c5298e55.d8b0f9ee.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[95930],{31249:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-functions/page/2","page":2,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/azure-functions","nextPage":"/Cloud-Native/30daysofIA/tags/azure-functions/page/3","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/c580cfa2.9427a4b5.js b/assets/js/c580cfa2.9427a4b5.js new file mode 100644 index 0000000000..41ce1d6092 --- /dev/null +++ b/assets/js/c580cfa2.9427a4b5.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[59477],{2148:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA","page":1,"postsPerPage":1,"totalPages":11,"totalCount":11,"nextPage":"/Cloud-Native/30daysofIA/page/2","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/c580cfa2.d12e48c2.js b/assets/js/c580cfa2.d12e48c2.js deleted file mode 100644 index c0cb049f2b..0000000000 --- a/assets/js/c580cfa2.d12e48c2.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[59477],{2148:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA","page":1,"postsPerPage":1,"totalPages":9,"totalCount":9,"nextPage":"/Cloud-Native/30daysofIA/page/2","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/c58e39ac.86584c6e.js b/assets/js/c58e39ac.86584c6e.js new file mode 100644 index 0000000000..5e842838de --- /dev/null +++ b/assets/js/c58e39ac.86584c6e.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[51294],{73689:e=>{e.exports=JSON.parse('{"label":"learn-live","permalink":"/Cloud-Native/30daysofIA/tags/learn-live","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/c751643e.06c56971.js b/assets/js/c751643e.e3ae0270.js similarity index 74% rename from assets/js/c751643e.06c56971.js rename to assets/js/c751643e.e3ae0270.js index b8a630b166..500719ac26 100644 --- a/assets/js/c751643e.06c56971.js +++ b/assets/js/c751643e.e3ae0270.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[43259],{21622:e=>{e.exports=JSON.parse('{"label":"learn-live","permalink":"/Cloud-Native/30daysofIA/tags/learn-live","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[43259],{21622:e=>{e.exports=JSON.parse('{"label":"learn-live","permalink":"/Cloud-Native/30daysofIA/tags/learn-live","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/c7f0b8e3.8bcb8248.js b/assets/js/c7f0b8e3.92f020e7.js similarity index 74% rename from assets/js/c7f0b8e3.8bcb8248.js rename to assets/js/c7f0b8e3.92f020e7.js index 8b1587abbe..61a320dd95 100644 --- a/assets/js/c7f0b8e3.8bcb8248.js +++ b/assets/js/c7f0b8e3.92f020e7.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[93734],{76432:a=>{a.exports=JSON.parse('{"label":"Fall-For-IA","permalink":"/Cloud-Native/30daysofIA/tags/fall-for-ia","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[93734],{76432:a=>{a.exports=JSON.parse('{"label":"Fall-For-IA","permalink":"/Cloud-Native/30daysofIA/tags/fall-for-ia","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/c8c60460.636e88ff.js b/assets/js/c8c60460.07a85934.js similarity index 74% rename from assets/js/c8c60460.636e88ff.js rename to assets/js/c8c60460.07a85934.js index 3b73fa011c..99772c21b3 100644 --- a/assets/js/c8c60460.636e88ff.js +++ b/assets/js/c8c60460.07a85934.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[81823],{27059:a=>{a.exports=JSON.parse('{"label":"Fall-For-IA","permalink":"/Cloud-Native/30daysofIA/tags/fall-for-ia","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[81823],{27059:a=>{a.exports=JSON.parse('{"label":"Fall-For-IA","permalink":"/Cloud-Native/30daysofIA/tags/fall-for-ia","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/caa33e0e.73a02ad0.js b/assets/js/caa33e0e.73a02ad0.js new file mode 100644 index 0000000000..9004e1cb12 --- /dev/null +++ b/assets/js/caa33e0e.73a02ad0.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[61285],{40258:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-functions/page/10","page":10,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/azure-functions/page/9","nextPage":"/Cloud-Native/30daysofIA/tags/azure-functions/page/11","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/cafc3c94.07cfb138.js b/assets/js/cafc3c94.1e4690ca.js similarity index 74% rename from assets/js/cafc3c94.07cfb138.js rename to assets/js/cafc3c94.1e4690ca.js index 56275eb58f..3347d8f34e 100644 --- a/assets/js/cafc3c94.07cfb138.js +++ b/assets/js/cafc3c94.1e4690ca.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[94963],{17935:a=>{a.exports=JSON.parse('{"label":"Fall-For-IA","permalink":"/Cloud-Native/30daysofIA/tags/fall-for-ia","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[94963],{17935:a=>{a.exports=JSON.parse('{"label":"Fall-For-IA","permalink":"/Cloud-Native/30daysofIA/tags/fall-for-ia","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/cb997589.614d1f64.js b/assets/js/cb997589.614d1f64.js new file mode 100644 index 0000000000..3c07e3b9ea --- /dev/null +++ b/assets/js/cb997589.614d1f64.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[14611],{69667:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/page/2","page":2,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA","nextPage":"/Cloud-Native/30daysofIA/page/3","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/cb997589.92b1d6e0.js b/assets/js/cb997589.92b1d6e0.js deleted file mode 100644 index 4e5ff79b8b..0000000000 --- a/assets/js/cb997589.92b1d6e0.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[14611],{69667:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/page/2","page":2,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA","nextPage":"/Cloud-Native/30daysofIA/page/3","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/cd923978.66869174.js b/assets/js/cd923978.66869174.js new file mode 100644 index 0000000000..a4a2701b40 --- /dev/null +++ b/assets/js/cd923978.66869174.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[80837],{46155:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-container-apps/page/6","page":6,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/azure-container-apps/page/5","nextPage":"/Cloud-Native/30daysofIA/tags/azure-container-apps/page/7","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/cd923978.81ea0285.js b/assets/js/cd923978.81ea0285.js deleted file mode 100644 index a59e033b7b..0000000000 --- a/assets/js/cd923978.81ea0285.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[80837],{46155:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-container-apps/page/6","page":6,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/azure-container-apps/page/5","nextPage":"/Cloud-Native/30daysofIA/tags/azure-container-apps/page/7","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/ce5487d5.b32f6406.js b/assets/js/ce5487d5.b32f6406.js new file mode 100644 index 0000000000..79082ee4dd --- /dev/null +++ b/assets/js/ce5487d5.b32f6406.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[86033],{32469:a=>{a.exports=JSON.parse('{"label":"azure-container-apps","permalink":"/Cloud-Native/30daysofIA/tags/azure-container-apps","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/cefbb4e5.5d3f7af4.js b/assets/js/cefbb4e5.5d3f7af4.js new file mode 100644 index 0000000000..8ed60130b3 --- /dev/null +++ b/assets/js/cefbb4e5.5d3f7af4.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[14183],{48287:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/hack-together/page/8","page":8,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/hack-together/page/7","nextPage":"/Cloud-Native/30daysofIA/tags/hack-together/page/9","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/cefbb4e5.dd998262.js b/assets/js/cefbb4e5.dd998262.js deleted file mode 100644 index 2834a6739b..0000000000 --- a/assets/js/cefbb4e5.dd998262.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[14183],{48287:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/hack-together/page/8","page":8,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/hack-together/page/7","nextPage":"/Cloud-Native/30daysofIA/tags/hack-together/page/9","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/cf50db93.7a6a170b.js b/assets/js/cf50db93.7a6a170b.js deleted file mode 100644 index aa544bc0c9..0000000000 --- a/assets/js/cf50db93.7a6a170b.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[22272],{26774:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-functions/page/8","page":8,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/azure-functions/page/7","nextPage":"/Cloud-Native/30daysofIA/tags/azure-functions/page/9","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/cf50db93.ece85fa1.js b/assets/js/cf50db93.ece85fa1.js new file mode 100644 index 0000000000..5e3e733208 --- /dev/null +++ b/assets/js/cf50db93.ece85fa1.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[22272],{26774:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-functions/page/8","page":8,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/azure-functions/page/7","nextPage":"/Cloud-Native/30daysofIA/tags/azure-functions/page/9","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/cf9f52c6.2fe7da63.js b/assets/js/cf9f52c6.2fe7da63.js deleted file mode 100644 index 09004e96de..0000000000 --- a/assets/js/cf9f52c6.2fe7da63.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[14939],{44848:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/fall-for-ia/page/5","page":5,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/fall-for-ia/page/4","nextPage":"/Cloud-Native/30daysofIA/tags/fall-for-ia/page/6","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/cf9f52c6.ca1e4d5d.js b/assets/js/cf9f52c6.ca1e4d5d.js new file mode 100644 index 0000000000..3791e0c25a --- /dev/null +++ b/assets/js/cf9f52c6.ca1e4d5d.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[14939],{44848:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/fall-for-ia/page/5","page":5,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/fall-for-ia/page/4","nextPage":"/Cloud-Native/30daysofIA/tags/fall-for-ia/page/6","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/d0273d46.45fddbb3.js b/assets/js/d0273d46.45fddbb3.js deleted file mode 100644 index daae501a90..0000000000 --- a/assets/js/d0273d46.45fddbb3.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[44608],{12996:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/community-buzz/page/2","page":2,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/community-buzz","nextPage":"/Cloud-Native/30daysofIA/tags/community-buzz/page/3","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/d0273d46.8ebabf4d.js b/assets/js/d0273d46.8ebabf4d.js new file mode 100644 index 0000000000..db9cb11169 --- /dev/null +++ b/assets/js/d0273d46.8ebabf4d.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[44608],{12996:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/community-buzz/page/2","page":2,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/community-buzz","nextPage":"/Cloud-Native/30daysofIA/tags/community-buzz/page/3","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/d17a530e.735c308e.js b/assets/js/d17a530e.735c308e.js new file mode 100644 index 0000000000..25d2ec8e8f --- /dev/null +++ b/assets/js/d17a530e.735c308e.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[27618],{77957:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/ask-the-expert/page/6","page":6,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/ask-the-expert/page/5","nextPage":"/Cloud-Native/30daysofIA/tags/ask-the-expert/page/7","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/d17a530e.85d36f48.js b/assets/js/d17a530e.85d36f48.js deleted file mode 100644 index fb08a9275c..0000000000 --- a/assets/js/d17a530e.85d36f48.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[27618],{77957:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/ask-the-expert/page/6","page":6,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/ask-the-expert/page/5","nextPage":"/Cloud-Native/30daysofIA/tags/ask-the-expert/page/7","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/d22a7198.cb837c7d.js b/assets/js/d22a7198.6455ea1b.js similarity index 73% rename from assets/js/d22a7198.cb837c7d.js rename to assets/js/d22a7198.6455ea1b.js index babb08d50d..b364e6e41a 100644 --- a/assets/js/d22a7198.cb837c7d.js +++ b/assets/js/d22a7198.6455ea1b.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[78558],{83341:e=>{e.exports=JSON.parse('{"label":"hack-together","permalink":"/Cloud-Native/30daysofIA/tags/hack-together","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[78558],{83341:e=>{e.exports=JSON.parse('{"label":"hack-together","permalink":"/Cloud-Native/30daysofIA/tags/hack-together","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/d32fff51.fdaaaaa6.js b/assets/js/d32fff51.fdaaaaa6.js new file mode 100644 index 0000000000..0022f2989b --- /dev/null +++ b/assets/js/d32fff51.fdaaaaa6.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[82912],{24214:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/fall-for-ia/page/10","page":10,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/fall-for-ia/page/9","nextPage":"/Cloud-Native/30daysofIA/tags/fall-for-ia/page/11","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/d36d48ac.15749bf4.js b/assets/js/d36d48ac.15749bf4.js new file mode 100644 index 0000000000..7fc18a2f2e --- /dev/null +++ b/assets/js/d36d48ac.15749bf4.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[11656],{62053:e=>{e.exports=JSON.parse('{"label":"community-buzz","permalink":"/Cloud-Native/30daysofIA/tags/community-buzz","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/d37b1df8.3c0c8bee.js b/assets/js/d37b1df8.3c0c8bee.js deleted file mode 100644 index d94f046714..0000000000 --- a/assets/js/d37b1df8.3c0c8bee.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[45448],{23325:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/fall-for-ia/page/3","page":3,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/fall-for-ia/page/2","nextPage":"/Cloud-Native/30daysofIA/tags/fall-for-ia/page/4","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/d37b1df8.fffa0321.js b/assets/js/d37b1df8.fffa0321.js new file mode 100644 index 0000000000..743aac3f3b --- /dev/null +++ b/assets/js/d37b1df8.fffa0321.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[45448],{23325:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/fall-for-ia/page/3","page":3,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/fall-for-ia/page/2","nextPage":"/Cloud-Native/30daysofIA/tags/fall-for-ia/page/4","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/d436e3ab.1513077e.js b/assets/js/d436e3ab.1513077e.js deleted file mode 100644 index 1d6904125b..0000000000 --- a/assets/js/d436e3ab.1513077e.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[90276],{81811:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/ask-the-expert/page/5","page":5,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/ask-the-expert/page/4","nextPage":"/Cloud-Native/30daysofIA/tags/ask-the-expert/page/6","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/d436e3ab.16bd3b08.js b/assets/js/d436e3ab.16bd3b08.js new file mode 100644 index 0000000000..c5b47b0c90 --- /dev/null +++ b/assets/js/d436e3ab.16bd3b08.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[90276],{81811:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/ask-the-expert/page/5","page":5,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/ask-the-expert/page/4","nextPage":"/Cloud-Native/30daysofIA/tags/ask-the-expert/page/6","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/d60c28fa.f5526b88.js b/assets/js/d60c28fa.9b2aab5c.js similarity index 89% rename from assets/js/d60c28fa.f5526b88.js rename to assets/js/d60c28fa.9b2aab5c.js index e60af97149..d1975c49f2 100644 --- a/assets/js/d60c28fa.f5526b88.js +++ b/assets/js/d60c28fa.9b2aab5c.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[19981],{91152:e=>{e.exports=JSON.parse('{"label":"azure-kubernetes-service","permalink":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[19981],{91152:e=>{e.exports=JSON.parse('{"label":"azure-kubernetes-service","permalink":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/d6e399c5.be2c326f.js b/assets/js/d6e399c5.fd1984b6.js similarity index 72% rename from assets/js/d6e399c5.be2c326f.js rename to assets/js/d6e399c5.fd1984b6.js index 285baddd42..949ad1a40a 100644 --- a/assets/js/d6e399c5.be2c326f.js +++ b/assets/js/d6e399c5.fd1984b6.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[30001],{7229:e=>{e.exports=JSON.parse('{"label":"community-buzz","permalink":"/Cloud-Native/30daysofIA/tags/community-buzz","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[30001],{7229:e=>{e.exports=JSON.parse('{"label":"community-buzz","permalink":"/Cloud-Native/30daysofIA/tags/community-buzz","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/d70fd83e.e5c7a76e.js b/assets/js/d70fd83e.5d0a7f03.js similarity index 72% rename from assets/js/d70fd83e.e5c7a76e.js rename to assets/js/d70fd83e.5d0a7f03.js index 469aa2fc75..e1691b1eff 100644 --- a/assets/js/d70fd83e.e5c7a76e.js +++ b/assets/js/d70fd83e.5d0a7f03.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[55369],{10292:e=>{e.exports=JSON.parse('{"label":"community-buzz","permalink":"/Cloud-Native/30daysofIA/tags/community-buzz","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[55369],{10292:e=>{e.exports=JSON.parse('{"label":"community-buzz","permalink":"/Cloud-Native/30daysofIA/tags/community-buzz","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/d721da33.133cf8a6.js b/assets/js/d721da33.7cdd1c40.js similarity index 72% rename from assets/js/d721da33.133cf8a6.js rename to assets/js/d721da33.7cdd1c40.js index c6dc43536f..f98a3d5ee6 100644 --- a/assets/js/d721da33.133cf8a6.js +++ b/assets/js/d721da33.7cdd1c40.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[54018],{69330:e=>{e.exports=JSON.parse('{"label":"ask-the-expert","permalink":"/Cloud-Native/30daysofIA/tags/ask-the-expert","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[54018],{69330:e=>{e.exports=JSON.parse('{"label":"ask-the-expert","permalink":"/Cloud-Native/30daysofIA/tags/ask-the-expert","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/d72f598b.1c9d82dc.js b/assets/js/d72f598b.b20b3e35.js similarity index 72% rename from assets/js/d72f598b.1c9d82dc.js rename to assets/js/d72f598b.b20b3e35.js index 3850369601..2dc461ee6f 100644 --- a/assets/js/d72f598b.1c9d82dc.js +++ b/assets/js/d72f598b.b20b3e35.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[63078],{79752:e=>{e.exports=JSON.parse('{"label":"github-copilot","permalink":"/Cloud-Native/30daysofIA/tags/github-copilot","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[63078],{79752:e=>{e.exports=JSON.parse('{"label":"github-copilot","permalink":"/Cloud-Native/30daysofIA/tags/github-copilot","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/d763cd59.e3dfac6d.js b/assets/js/d763cd59.5be11f16.js similarity index 73% rename from assets/js/d763cd59.e3dfac6d.js rename to assets/js/d763cd59.5be11f16.js index a576c7c4b2..2ad4a12fba 100644 --- a/assets/js/d763cd59.e3dfac6d.js +++ b/assets/js/d763cd59.5be11f16.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[48314],{72138:a=>{a.exports=JSON.parse('{"label":"30-days-of-IA","permalink":"/Cloud-Native/30daysofIA/tags/30-days-of-ia","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[48314],{72138:a=>{a.exports=JSON.parse('{"label":"30-days-of-IA","permalink":"/Cloud-Native/30daysofIA/tags/30-days-of-ia","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/db6130e9.4e9c66a5.js b/assets/js/db6130e9.d3ee41a7.js similarity index 92% rename from assets/js/db6130e9.4e9c66a5.js rename to assets/js/db6130e9.d3ee41a7.js index 65c609f4c0..f7121a2e41 100644 --- a/assets/js/db6130e9.4e9c66a5.js +++ b/assets/js/db6130e9.d3ee41a7.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[18360],{24696:a=>{a.exports=JSON.parse('{"label":"azure-container-apps","permalink":"/Cloud-Native/30daysofIA/tags/azure-container-apps","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[18360],{24696:a=>{a.exports=JSON.parse('{"label":"azure-container-apps","permalink":"/Cloud-Native/30daysofIA/tags/azure-container-apps","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/dd8667a0.6dae5430.js b/assets/js/dd8667a0.6dae5430.js new file mode 100644 index 0000000000..caf150f615 --- /dev/null +++ b/assets/js/dd8667a0.6dae5430.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[87005],{40635:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/page/7","page":7,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/page/6","nextPage":"/Cloud-Native/30daysofIA/page/8","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/dd8667a0.f1b53af5.js b/assets/js/dd8667a0.f1b53af5.js deleted file mode 100644 index b655a7fbdc..0000000000 --- a/assets/js/dd8667a0.f1b53af5.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[87005],{40635:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/page/7","page":7,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/page/6","nextPage":"/Cloud-Native/30daysofIA/page/8","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/dfd81e36.1479e607.js b/assets/js/dfd81e36.1479e607.js new file mode 100644 index 0000000000..b303e85f90 --- /dev/null +++ b/assets/js/dfd81e36.1479e607.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[79509],{98486:a=>{a.exports=JSON.parse('[{"label":"Fall-For-IA","permalink":"/Cloud-Native/30daysofIA/tags/fall-for-ia","count":11},{"label":"30-days-of-IA","permalink":"/Cloud-Native/30daysofIA/tags/30-days-of-ia","count":11},{"label":"learn-live","permalink":"/Cloud-Native/30daysofIA/tags/learn-live","count":11},{"label":"hack-together","permalink":"/Cloud-Native/30daysofIA/tags/hack-together","count":11},{"label":"community-buzz","permalink":"/Cloud-Native/30daysofIA/tags/community-buzz","count":11},{"label":"ask-the-expert","permalink":"/Cloud-Native/30daysofIA/tags/ask-the-expert","count":11},{"label":"azure-kubernetes-service","permalink":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service","count":11},{"label":"azure-functions","permalink":"/Cloud-Native/30daysofIA/tags/azure-functions","count":11},{"label":"azure-openai","permalink":"/Cloud-Native/30daysofIA/tags/azure-openai","count":11},{"label":"azure-container-apps","permalink":"/Cloud-Native/30daysofIA/tags/azure-container-apps","count":11},{"label":"azure-cosmos-db","permalink":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db","count":11},{"label":"github-copilot","permalink":"/Cloud-Native/30daysofIA/tags/github-copilot","count":11},{"label":"github-codespaces","permalink":"/Cloud-Native/30daysofIA/tags/github-codespaces","count":11},{"label":"github-actions","permalink":"/Cloud-Native/30daysofIA/tags/github-actions","count":11}]')}}]); \ No newline at end of file diff --git a/assets/js/dfd81e36.ac9c856b.js b/assets/js/dfd81e36.ac9c856b.js deleted file mode 100644 index ec89b71bc9..0000000000 --- a/assets/js/dfd81e36.ac9c856b.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[79509],{98486:a=>{a.exports=JSON.parse('[{"label":"Fall-For-IA","permalink":"/Cloud-Native/30daysofIA/tags/fall-for-ia","count":9},{"label":"30-days-of-IA","permalink":"/Cloud-Native/30daysofIA/tags/30-days-of-ia","count":9},{"label":"learn-live","permalink":"/Cloud-Native/30daysofIA/tags/learn-live","count":9},{"label":"hack-together","permalink":"/Cloud-Native/30daysofIA/tags/hack-together","count":9},{"label":"community-buzz","permalink":"/Cloud-Native/30daysofIA/tags/community-buzz","count":9},{"label":"ask-the-expert","permalink":"/Cloud-Native/30daysofIA/tags/ask-the-expert","count":9},{"label":"azure-kubernetes-service","permalink":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service","count":9},{"label":"azure-functions","permalink":"/Cloud-Native/30daysofIA/tags/azure-functions","count":9},{"label":"azure-openai","permalink":"/Cloud-Native/30daysofIA/tags/azure-openai","count":9},{"label":"azure-container-apps","permalink":"/Cloud-Native/30daysofIA/tags/azure-container-apps","count":9},{"label":"azure-cosmos-db","permalink":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db","count":9},{"label":"github-copilot","permalink":"/Cloud-Native/30daysofIA/tags/github-copilot","count":9},{"label":"github-codespaces","permalink":"/Cloud-Native/30daysofIA/tags/github-codespaces","count":9},{"label":"github-actions","permalink":"/Cloud-Native/30daysofIA/tags/github-actions","count":9}]')}}]); \ No newline at end of file diff --git a/assets/js/e127deb5.9e5dc6c8.js b/assets/js/e127deb5.1a39730c.js similarity index 92% rename from assets/js/e127deb5.9e5dc6c8.js rename to assets/js/e127deb5.1a39730c.js index 25147b7565..0908eb28be 100644 --- a/assets/js/e127deb5.9e5dc6c8.js +++ b/assets/js/e127deb5.1a39730c.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[71854],{27515:a=>{a.exports=JSON.parse('{"label":"azure-container-apps","permalink":"/Cloud-Native/30daysofIA/tags/azure-container-apps","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[71854],{27515:a=>{a.exports=JSON.parse('{"label":"azure-container-apps","permalink":"/Cloud-Native/30daysofIA/tags/azure-container-apps","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/e137ac4d.61f2c30e.js b/assets/js/e137ac4d.61f2c30e.js new file mode 100644 index 0000000000..1bf67982c9 --- /dev/null +++ b/assets/js/e137ac4d.61f2c30e.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[15967],{91824:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db/page/9","page":9,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db/page/8","nextPage":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db/page/10","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/e137ac4d.cfeddd2a.js b/assets/js/e137ac4d.cfeddd2a.js deleted file mode 100644 index 2fd85fd01a..0000000000 --- a/assets/js/e137ac4d.cfeddd2a.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[15967],{91824:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db/page/9","page":9,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db/page/8","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/e1405df5.5058cd75.js b/assets/js/e1405df5.5058cd75.js deleted file mode 100644 index 11027aec22..0000000000 --- a/assets/js/e1405df5.5058cd75.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[35558],{47864:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service/page/8","page":8,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service/page/7","nextPage":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service/page/9","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/e1405df5.ce5146c7.js b/assets/js/e1405df5.ce5146c7.js new file mode 100644 index 0000000000..2dc4d82a2d --- /dev/null +++ b/assets/js/e1405df5.ce5146c7.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[35558],{47864:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service/page/8","page":8,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service/page/7","nextPage":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service/page/9","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/e1daa54d.cae255b5.js b/assets/js/e1daa54d.cae255b5.js deleted file mode 100644 index b370adc3e4..0000000000 --- a/assets/js/e1daa54d.cae255b5.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[80670],{74571:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/ask-the-expert","page":1,"postsPerPage":1,"totalPages":9,"totalCount":9,"nextPage":"/Cloud-Native/30daysofIA/tags/ask-the-expert/page/2","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/e1daa54d.ee16c7ce.js b/assets/js/e1daa54d.ee16c7ce.js new file mode 100644 index 0000000000..1849ac5ae4 --- /dev/null +++ b/assets/js/e1daa54d.ee16c7ce.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[80670],{74571:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/ask-the-expert","page":1,"postsPerPage":1,"totalPages":11,"totalCount":11,"nextPage":"/Cloud-Native/30daysofIA/tags/ask-the-expert/page/2","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/e37c4032.16459f6f.js b/assets/js/e37c4032.16459f6f.js new file mode 100644 index 0000000000..369adccc52 --- /dev/null +++ b/assets/js/e37c4032.16459f6f.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[55759],{58579:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-container-apps","page":1,"postsPerPage":1,"totalPages":11,"totalCount":11,"nextPage":"/Cloud-Native/30daysofIA/tags/azure-container-apps/page/2","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/e37c4032.1bb77cf7.js b/assets/js/e37c4032.1bb77cf7.js deleted file mode 100644 index b8aec62084..0000000000 --- a/assets/js/e37c4032.1bb77cf7.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[55759],{58579:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-container-apps","page":1,"postsPerPage":1,"totalPages":9,"totalCount":9,"nextPage":"/Cloud-Native/30daysofIA/tags/azure-container-apps/page/2","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/e49fdeb5.81940f11.js b/assets/js/e49fdeb5.2ef6a200.js similarity index 92% rename from assets/js/e49fdeb5.81940f11.js rename to assets/js/e49fdeb5.2ef6a200.js index 9261567798..65bd956eb1 100644 --- a/assets/js/e49fdeb5.81940f11.js +++ b/assets/js/e49fdeb5.2ef6a200.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[23602],{75692:a=>{a.exports=JSON.parse('{"label":"azure-container-apps","permalink":"/Cloud-Native/30daysofIA/tags/azure-container-apps","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[23602],{75692:a=>{a.exports=JSON.parse('{"label":"azure-container-apps","permalink":"/Cloud-Native/30daysofIA/tags/azure-container-apps","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/e5530f58.d3d059d5.js b/assets/js/e5530f58.5759edd6.js similarity index 90% rename from assets/js/e5530f58.d3d059d5.js rename to assets/js/e5530f58.5759edd6.js index 9ab3b68d7d..06b30f345c 100644 --- a/assets/js/e5530f58.d3d059d5.js +++ b/assets/js/e5530f58.5759edd6.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[97159],{2386:e=>{e.exports=JSON.parse('{"label":"azure-kubernetes-service","permalink":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[97159],{2386:e=>{e.exports=JSON.parse('{"label":"azure-kubernetes-service","permalink":"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/e57b34d0.e511a36d.js b/assets/js/e57b34d0.e511a36d.js deleted file mode 100644 index f63c68f31a..0000000000 --- a/assets/js/e57b34d0.e511a36d.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[10805],{88142:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/ask-the-expert/page/7","page":7,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/ask-the-expert/page/6","nextPage":"/Cloud-Native/30daysofIA/tags/ask-the-expert/page/8","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/e57b34d0.fdf7b02e.js b/assets/js/e57b34d0.fdf7b02e.js new file mode 100644 index 0000000000..ad83defbe3 --- /dev/null +++ b/assets/js/e57b34d0.fdf7b02e.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[10805],{88142:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/ask-the-expert/page/7","page":7,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/ask-the-expert/page/6","nextPage":"/Cloud-Native/30daysofIA/tags/ask-the-expert/page/8","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/e645b9de.b8fdff07.js b/assets/js/e645b9de.b8fdff07.js new file mode 100644 index 0000000000..7f9b362581 --- /dev/null +++ b/assets/js/e645b9de.b8fdff07.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[37086],{24436:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/github-actions/page/10","page":10,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/github-actions/page/9","nextPage":"/Cloud-Native/30daysofIA/tags/github-actions/page/11","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/e705147d.cec3bc96.js b/assets/js/e705147d.cec3bc96.js new file mode 100644 index 0000000000..608b43aee6 --- /dev/null +++ b/assets/js/e705147d.cec3bc96.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[31331],{3905:(e,t,a)=>{a.d(t,{Zo:()=>u,kt:()=>d});var n=a(67294);function i(e,t,a){return t in e?Object.defineProperty(e,t,{value:a,enumerable:!0,configurable:!0,writable:!0}):e[t]=a,e}function r(e,t){var a=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),a.push.apply(a,n)}return a}function o(e){for(var t=1;t=0||(i[a]=e[a]);return i}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,a)&&(i[a]=e[a])}return i}var l=n.createContext({}),p=function(e){var t=n.useContext(l),a=t;return e&&(a="function"==typeof e?e(t):o(o({},t),e)),a},u=function(e){var t=p(e.components);return n.createElement(l.Provider,{value:t},e.children)},c={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},g=n.forwardRef((function(e,t){var a=e.components,i=e.mdxType,r=e.originalType,l=e.parentName,u=s(e,["components","mdxType","originalType","parentName"]),g=p(a),d=i,h=g["".concat(l,".").concat(d)]||g[d]||c[d]||r;return a?n.createElement(h,o(o({ref:t},u),{},{components:a})):n.createElement(h,o({ref:t},u))}));function d(e,t){var a=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var r=a.length,o=new Array(r);o[0]=g;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s.mdxType="string"==typeof e?e:i,o[1]=s;for(var p=2;p{a.r(t),a.d(t,{assets:()=>l,contentTitle:()=>o,default:()=>c,frontMatter:()=>r,metadata:()=>s,toc:()=>p});var n=a(87462),i=(a(67294),a(3905));const r={date:"2023-09-21T09:00",slug:"how-digital-natives-leverage-generative-ai",title:"1-4. How Digital Natives leverage Generative AI",authors:["cnteam"],draft:!1,hide_table_of_contents:!1,toc_min_heading_level:2,toc_max_heading_level:3,keywords:["Cloud","Data","AI","AI/ML","intelligent apps","cloud-native","30-days","enterprise apps","digital experiences","app modernization","serverless","ai apps","data"],image:"https://azure.github.io/Cloud-Native/img/ogImage.png",description:"Article Description: This article explores how digital natives and Independent Software Vendors (ISVs) can transform their business operations and generate higher value for customers with generative AI and intelligent apps.",tags:["Fall-For-IA","30-days-of-IA","learn-live","hack-together","community-buzz","ask-the-expert","azure-kubernetes-service","azure-functions","azure-openai","azure-container-apps","azure-cosmos-db","github-copilot","github-codespaces","github-actions"]},o=void 0,s={permalink:"/Cloud-Native/30daysofIA/how-digital-natives-leverage-generative-ai",source:"@site/blog-30daysofIA/2023-09-21/how-digital-natives-leverage-generative-ai.md",title:"1-4. How Digital Natives leverage Generative AI",description:"Article Description: This article explores how digital natives and Independent Software Vendors (ISVs) can transform their business operations and generate higher value for customers with generative AI and intelligent apps.",date:"2023-09-21T09:00:00.000Z",formattedDate:"September 21, 2023",tags:[{label:"Fall-For-IA",permalink:"/Cloud-Native/30daysofIA/tags/fall-for-ia"},{label:"30-days-of-IA",permalink:"/Cloud-Native/30daysofIA/tags/30-days-of-ia"},{label:"learn-live",permalink:"/Cloud-Native/30daysofIA/tags/learn-live"},{label:"hack-together",permalink:"/Cloud-Native/30daysofIA/tags/hack-together"},{label:"community-buzz",permalink:"/Cloud-Native/30daysofIA/tags/community-buzz"},{label:"ask-the-expert",permalink:"/Cloud-Native/30daysofIA/tags/ask-the-expert"},{label:"azure-kubernetes-service",permalink:"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service"},{label:"azure-functions",permalink:"/Cloud-Native/30daysofIA/tags/azure-functions"},{label:"azure-openai",permalink:"/Cloud-Native/30daysofIA/tags/azure-openai"},{label:"azure-container-apps",permalink:"/Cloud-Native/30daysofIA/tags/azure-container-apps"},{label:"azure-cosmos-db",permalink:"/Cloud-Native/30daysofIA/tags/azure-cosmos-db"},{label:"github-copilot",permalink:"/Cloud-Native/30daysofIA/tags/github-copilot"},{label:"github-codespaces",permalink:"/Cloud-Native/30daysofIA/tags/github-codespaces"},{label:"github-actions",permalink:"/Cloud-Native/30daysofIA/tags/github-actions"}],readingTime:9.29,hasTruncateMarker:!1,authors:[{name:"It's 30DaysOfIA",title:"FallForIA Content Team",url:"https://azure.github.io/Cloud-Native/Fall-For-IA/",imageURL:"https://azure.github.io/Cloud-Native/img/logo-ms-cloud-native.png",key:"cnteam"}],frontMatter:{date:"2023-09-21T09:00",slug:"how-digital-natives-leverage-generative-ai",title:"1-4. How Digital Natives leverage Generative AI",authors:["cnteam"],draft:!1,hide_table_of_contents:!1,toc_min_heading_level:2,toc_max_heading_level:3,keywords:["Cloud","Data","AI","AI/ML","intelligent apps","cloud-native","30-days","enterprise apps","digital experiences","app modernization","serverless","ai apps","data"],image:"https://azure.github.io/Cloud-Native/img/ogImage.png",description:"Article Description: This article explores how digital natives and Independent Software Vendors (ISVs) can transform their business operations and generate higher value for customers with generative AI and intelligent apps.",tags:["Fall-For-IA","30-days-of-IA","learn-live","hack-together","community-buzz","ask-the-expert","azure-kubernetes-service","azure-functions","azure-openai","azure-container-apps","azure-cosmos-db","github-copilot","github-codespaces","github-actions"]},prevItem:{title:"2-2. Build Your First Intelligent App with Azure AI and AKS-2",permalink:"/Cloud-Native/30daysofIA/build-your-first-intelligent-app-with-azure-ai-and-aks-2"},nextItem:{title:"1-3. Reimagine App Development with AI",permalink:"/Cloud-Native/30daysofIA/reimagine-app-development-with-ai"}},l={authorsImageUrls:[void 0]},p=[{value:"What We'll Cover:",id:"what-well-cover",level:2},{value:"Unleashing Business Transformation: How Digital Natives Can Leverage Generative AI",id:"unleashing-business-transformation-how-digital-natives-can-leverage-generative-ai",level:2},{value:"Unpacking the Potential of Generative AI",id:"unpacking-the-potential-of-generative-ai",level:2},{value:"The Crucial Role of Generative AI in Business Operations",id:"the-crucial-role-of-generative-ai-in-business-operations",level:2},{value:"Navigating Challenges in the Generative AI Landscape",id:"navigating-challenges-in-the-generative-ai-landscape",level:2},{value:"Achieving Harmony: Integrating Generative AI with Existing Business Operations",id:"achieving-harmony-integrating-generative-ai-with-existing-business-operations",level:2},{value:"Conclusion",id:"conclusion",level:2},{value:"Exercise",id:"exercise",level:2}],u={toc:p};function c(e){let{components:t,...r}=e;return(0,i.kt)("wrapper",(0,n.Z)({},u,r,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("head",null,(0,i.kt)("meta",{property:"og:url",content:"https://azure.github.io/cloud-native/30daysofia/how-digital-natives-leverage-generative-ai"}),(0,i.kt)("meta",{property:"og:type",content:"website"}),(0,i.kt)("meta",{property:"og:title",content:"**Fall For Intelligent Apps! \ud83c\udf42| Build AI Apps On Azure"}),(0,i.kt)("meta",{property:"og:description",content:"Explore how digital natives and Independent Software Vendors (ISVs) can transform their business operations and generate higher value for customers with generative AI and intelligent apps."}),(0,i.kt)("meta",{property:"og:image",content:"https://azure.github.io/Cloud-Native/img/ogImage.png"}),(0,i.kt)("meta",{name:"twitter:url",content:"https://azure.github.io/Cloud-Native/30daysofIA/how-digital-natives-leverage-generative-ai"}),(0,i.kt)("meta",{name:"twitter:title",content:"**Fall For Intelligent Apps! \ud83c\udf42 | Build AI Apps On Azure"}),(0,i.kt)("meta",{name:"twitter:description",content:"1-4. Explore how digital natives and Independent Software Vendors (ISVs) can transform their business operations and generate higher value for customers with generative AI and intelligent apps."}),(0,i.kt)("meta",{name:"twitter:image",content:"https://azure.github.io/Cloud-Native/img/ogImage.png"}),(0,i.kt)("meta",{name:"twitter:card",content:"summary_large_image"}),(0,i.kt)("meta",{name:"twitter:creator",content:"@devanshidiaries"}),(0,i.kt)("meta",{name:"twitter:site",content:"@AzureAdvocates"}),(0,i.kt)("link",{rel:"canonical",href:"https://azure.github.io/Cloud-Native/30daysofIA/how-digital-natives-leverage-generative-ai"})),(0,i.kt)("p",null,"Explore how digital natives and Independent Software Vendors (ISVs) can transform their business operations and generate higher value for customers with generative AI and intelligent apps."),(0,i.kt)("h2",{id:"what-well-cover"},"What We'll Cover:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Potential of Generative AI in application development"),(0,i.kt)("li",{parentName:"ul"},"Challenges with Generative AI"),(0,i.kt)("li",{parentName:"ul"},"Integrating Generative AI with business operations")),(0,i.kt)("p",null,(0,i.kt)("img",{alt:"image of generative AI and intelligent apps used in business",src:a(65299).Z,width:"624",height:"380"})),(0,i.kt)("h2",{id:"unleashing-business-transformation-how-digital-natives-can-leverage-generative-ai"},"Unleashing Business Transformation: How Digital Natives Can Leverage Generative AI"),(0,i.kt)("p",null,"Generative AI is no longer a technological flight of fancy. It\u2019s here, and it\u2019s rewriting the rules of business innovation.\u202f "),(0,i.kt)("p",null,"This type of AI leverages machine learning algorithms to create new data with similar characteristics to a given dataset. It goes beyond text generation using generative pretrained transformer (GPT) models, covering an array of applications that span from image synthesis to translations and more. It also powers a new class of intelligent applications, which combine AI capabilities with traditional software functions for enhanced user experiences and advanced business solutions.\u202f "),(0,i.kt)("p",null,"Generative AI is transforming how digital natives and independent software vendors (ISVs) operate. Generative AI offers new possibilities for creating unique business value, enhancing customer experiences, and driving growth.\u202f "),(0,i.kt)("p",null,"It plays a critical role in applications, improving functionality, aiding decision-making, and automating complex tasks to deliver dynamic, uniquely personalized user experiences. Generative AI also enables and automates data-driven outcomes, functions, and features."),(0,i.kt)("p",null,"Integrating AI capabilities into customer-facing apps and turning them into intelligent apps benefits both the customer and the business, as we discussed in ",(0,i.kt)("a",{parentName:"p",href:"https://azure.github.io/Cloud-Native/30daysofIA/demystifying-intelligent-applications"},"Demystifying Intelligent Applications: Leveraging AI in App Development"),". You get faster product development workflows and better business intelligence for sales and marketing, while your users get more natural and context-aware features and interactions.\u202f "),(0,i.kt)("p",null,"That said, to realize generative AI\u2019s potential, you need some strategy and knowledge of how and where it fits into your current business and operations. We\u2019ll examine this technology\u2019s potential to transform business operations, its risks, and rewards, and how to integrate it into your existing operations.\u202f "),(0,i.kt)("h2",{id:"unpacking-the-potential-of-generative-ai"},"Unpacking the Potential of Generative AI"),(0,i.kt)("p",null,"Generative AI is creating new opportunities in the digital playing field. It empowers businesses to generate insights, design products, and automate processes in ways and at speeds that were previously unimaginable. You can now predict customer behavior more accurately, offer highly personalized user experiences, and prototype innovative ideas rapidly.\u202f "),(0,i.kt)("p",null,"For example, Microsoft used generative AI to reinvent web search in the new ",(0,i.kt)("a",{parentName:"p",href:"https://blogs.microsoft.com/blog/2023/02/07/reinventing-search-with-a-new-ai-powered-microsoft-bing-and-edge-your-copilot-for-the-web/"},"AI-powered Bing and Edge"),". The search experience has evolved from a solitary user flow of typing in keywords and scrolling through results. It\u2019s now a conversational experience where Bing uses context to offer better quality and more personalized search queries and results.\u202f "),(0,i.kt)("p",null,"Similarly, Microsoft harnessed generative AI to transform its productivity suite into intelligent apps. Through ",(0,i.kt)("a",{parentName:"p",href:"https://blogs.microsoft.com/blog/2023/03/16/introducing-microsoft-365-copilot-your-copilot-for-work/"},"Copilot for Microsoft 365"),", PowerPoint now empowers users to tell their stories by using DALL-E to generate images effortlessly. It also helps rephrase text-heavy slides to be more straightforward. Outlook now assists users to compose more effective emails, enabling better communication."),(0,i.kt)("p",null,"These are just some examples of how AI powers day-to-day work and operations.\u202f "),(0,i.kt)("p",null,"Businesses that ignore generative AI miss out on untapped market opportunities, letting competitors gain an edge in the digital race. By seeking ways to rethink assumptions and reinvent existing approaches, you can effectively use generative AI to create new opportunities, open products to new use cases and markets, unlock new growth and customer satisfaction levels, and increase your revenue."),(0,i.kt)("admonition",{type:"info"},(0,i.kt)("p",{parentName:"admonition"},"Register for the intelligent apps webinar on ",(0,i.kt)("a",{parentName:"p",href:"https://info.microsoft.com/ww-landing-driving-business-value-by-modernizing-with-cloud-native-and-ai.html?lcid=en-us?WT.mc_id=javascript-99907-ninarasi"},"Driving Business Value by Modernizing with Cloud-Native & AI")," with ",(0,i.kt)("em",{parentName:"p"},"Microsoft")," and ",(0,i.kt)("em",{parentName:"p"},"Forrester")," on ",(0,i.kt)("strong",{parentName:"p"},"September 26"),"."),(0,i.kt)("p",{parentName:"admonition"},"Explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries.\u202f ")),(0,i.kt)("h2",{id:"the-crucial-role-of-generative-ai-in-business-operations"},"The Crucial Role of Generative AI in Business Operations"),(0,i.kt)("p",null,"Generative AI can be a powerful catalyst for business transformation. Let\u2019s quickly tour some ways it can redefine business operations.\u202f"),(0,i.kt)("p",null,"Product development is traditionally a long and painstaking process. Now, tools like ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/features/copilot"},"GitHub Copilot")," and ",(0,i.kt)("a",{parentName:"p",href:"https://githubnext.com/projects/testpilot/"},"TestPilot")," use generative AI to make coding and testing software easier, faster, and more robust.\u202f "),(0,i.kt)("p",null,"Similarly, machine learning as a service technology, such as ",(0,i.kt)("a",{parentName:"p",href:"https://azure.microsoft.com/en-us/products/machine-learning?WT.mc_id=javascript-99907-ninarasi"},"Azure Machine Learning"),", democratizes the availability of machine learning. It helps collect, clean, and train models across datasets, making engineering and analyzing data more efficient and accessible.\u202f\u202f "),(0,i.kt)("p",null,"Generative AI also simplifies the process of creating intelligent apps. Previously, simulating thousands of prototypes, predicting their performance, and narrowing down to the most viable options could take weeks, months, or even years of iteration and data analysis. With generative AI, this process can be as quick as days, hours, or perhaps minutes.\u202f "),(0,i.kt)("p",null,"Customer experience is also ripe for transformation. Personalized recommendations drive engagement and sales, and generative AI can scale that personalization to every customer interaction using comprehensive and continually updated data. For instance, Microsoft leverages AI to enable personalization in ",(0,i.kt)("a",{parentName:"p",href:"https://dynamics.microsoft.com/en-us/ai/customer-insights/"},"Dynamics 365 Customer Insights"),". Using this tool, you can ask questions about your customers in natural language and gain valuable and actionable insights into your customer base.\u202f "),(0,i.kt)("p",null,"Lastly, marketing and sales can benefit from generative AI. For example, identifying current and future key markets and emerging trends helps companies stay ahead of the curve. Tools like ",(0,i.kt)("a",{parentName:"p",href:"https://powerbi.microsoft.com/en-us/blog/introducing-microsoft-fabric-and-copilot-in-microsoft-power-bi/"},"Copilot in Power BI")," help you quickly understand and uncover insights about your business, enabling your team to craft targeted campaigns, create engaging content, and optimize your business strategy.\u202f "),(0,i.kt)("p",null,"Leveraging this full generative AI potential requires the right tools. GPT-4, for instance, can be instrumental in realizing effective business transformation. It excels in understanding context, generating human-like text, and learning from minimal data input, making it a formidable tool in any digital native's arsenal. Platforms like ",(0,i.kt)("a",{parentName:"p",href:"https://azure.microsoft.com/en-us/products/ai-services/openai-service?WT.mc_id=javascript-99907-ninarasi"},"Azure OpenAI Service")," bring GPT-4\u2019s power to your fingertips."),(0,i.kt)("h2",{id:"navigating-challenges-in-the-generative-ai-landscape"},"Navigating Challenges in the Generative AI Landscape"),(0,i.kt)("p",null,"However, as with any powerful technology, it\u2019s unwise to treat generative AI as a perfect solution. You must know the challenges and risks of not leveraging it effectively, including technical and human-factor considerations.\u202f "),(0,i.kt)("p",null,"Without generative AI, your company risks missing the opportunity to please customers via personalization, as discussed previously. You\u2019ll miss sales opportunities via business intelligence insights, limiting your ability to grow revenue and keep up with the competition.\u202f "),(0,i.kt)("p",null,"One challenge of leveraging generative AI\u2019s full potential is finding the right tools. Fortunately, you can implement generative AI in your organization without investing in infrastructure, computational power, and expertise. Platforms like ",(0,i.kt)("a",{parentName:"p",href:"https://azure.microsoft.com/en-us/products/ai-services/openai-service?WT.mc_id=javascript-99907-ninarasi"},"Azure OpenAI Service")," and ",(0,i.kt)("a",{parentName:"p",href:"https://azure.microsoft.com/en-us/solutions/ai?WT.mc_id=javascript-99907-ninarasi"},"Azure AI Services")," make AI easily accessible. They also work with Azure compute and data services like\u202f",(0,i.kt)("a",{parentName:"p",href:"https://azure.microsoft.com/en-us/products/functions?WT.mc_id=javascript-99907-ninarasi"},"Azure Functions")," and ",(0,i.kt)("a",{parentName:"p",href:"https://azure.microsoft.com/en-us/products/kubernetes-service/?WT.mc_id=javascript-99907-ninarasi"},"Azure Kubernetes Service")," (AKS), enabling you to build intelligent apps easily and quickly.\u202f "),(0,i.kt)("p",null,"However, generative AI can\u2019t do everything. It may need human expertise and intervention to use it fully.\u202f "),(0,i.kt)("p",null,"For example, generative AI can create unique data. However, the data may have limited diversity and complexity. The AI may need help recognizing additional considerations or requirements specific to a problem. It may also lack context due to various factors, such as the information\u2019s freshness. Also, since it\u2019s an emerging field that\u2019s ",(0,i.kt)("a",{parentName:"p",href:"https://www.mckinsey.com/capabilities/mckinsey-digital/our-insights/the-economic-potential-of-generative-ai-the-next-productivity-frontier#key-insights"},"potentially worth $4.4 trillion"),", we\u2019re still learning about the nuances and ",(0,i.kt)("a",{parentName:"p",href:"https://techcrunch.com/2023/01/27/the-current-legal-cases-against-generative-ai-are-just-the-beginning/"},"potential regulatory impact")," of generative AI.\u202f "),(0,i.kt)("p",null,"Perhaps the most crucial and challenging aspect of adopting generative AI is an organization\u2019s required cultural shift. Your teams must learn how to use AI as your company fosters an understanding of AI\u2019s potential and limitations.\u202f "),(0,i.kt)("p",null,"Begin integrating AI literacy into your company\u2019s DNA. Knowledge of its potential and the readily available tools can inspire your teams to incorporate generative AI into their daily workflows and business operations quickly, improve productivity, and deliver superior customer experiences.\u202f "),(0,i.kt)("p",null,"No company wants to be outpaced by its competitors. You can balance generative AI\u2019s potential with your business operations by staying aware of and adapting to the dynamic AI landscape."),(0,i.kt)("h2",{id:"achieving-harmony-integrating-generative-ai-with-existing-business-operations"},"Achieving Harmony: Integrating Generative AI with Existing Business Operations"),(0,i.kt)("p",null,"Integrating generative AI into existing business operations doesn\u2019t have to be disruptive. With the correct strategy, you can do so seamlessly. Your goal should be to augment and enhance your operations, not disrupt and replace.\u202f "),(0,i.kt)("p",null,"For example, Microsoft Cloud technologies leveraged AI to ",(0,i.kt)("a",{parentName:"p",href:"https://www.microsoft.com/en-us/industry/blog/healthcare/2023/04/12/microsoft-cloud-for-healthcare-empowering-healthcare-to-deliver-meaningful-outcomes/?WT.mc_id=javascript-99907-ninarasi"},"relieve healthcare organizations\u2019 challenges"),", such as talent shortage and workforce burnout. Providers automated their workflows, invested in specific applications and connectors, and enhanced support for data and operations.\u202f "),(0,i.kt)("p",null,"Azure AI Services, such as ",(0,i.kt)("a",{parentName:"p",href:"https://learn.microsoft.com/en-us/azure/ai-services/language-service/text-analytics-for-health/overview?tabs=ner?WT.mc_id=javascript-99907-ninarasi"},"text analytics for health"),", enable healthcare organizations to understand their largely unstructured and untapped medical data. ",(0,i.kt)("a",{parentName:"p",href:"https://azure.microsoft.com/en-us/blog/announcing-project-health-insights-preview-advancing-ai-for-health-data/?WT.mc_id=javascript-99907-ninarasi"},"Project Health Insights")," leans on AI to help healthcare professionals gain actionable insights and inferences from patient data.\u202f "),(0,i.kt)("p",null,(0,i.kt)("a",{parentName:"p",href:"https://techcommunity.microsoft.com/t5/healthcare-and-life-sciences/extending-azure-health-bot-with-azure-openai-service/ba-p/3792560"},"Azure Health Bot"),", an Intelligent App, empowers organizations to build and deploy legally compliant, powerful conversational healthcare experiences to patients.\u202f "),(0,i.kt)("p",null,"A successful AI integration like this begins with understanding. Knowing what generative AI can do and setting realistic expectations can help you identify where and how AI can best serve your operations.\u202f "),(0,i.kt)("p",null,"Next, you can foster a culture of AI literacy across teams, speeding up adoption by helping them understand how it can aid their work. The more familiar your team is with AI and its possibilities, the smoother the transition. Training and education programs play a pivotal role here.\u202f "),(0,i.kt)("p",null,"Finally, integration requires a systematic approach. It\u2019s not about overhauling everything at once \u2014 it's about identifying areas where AI can add the most value, then gradually experimenting and scaling its use. Successful integration stories often feature a phased approach, starting small and growing steadily.\u202f "),(0,i.kt)("p",null,"As we move toward a future where generative AI is a staple in business operations, remember that every journey is unique. Although there\u2019s no one-size-fits-all solution, a strategic approach and a keen understanding of your business needs will help you navigate this new landscape successfully. "),(0,i.kt)("h2",{id:"conclusion"},"Conclusion"),(0,i.kt)("p",null,"Generative AI offers plenty of opportunities for businesses that embrace it. It\u2019s fueling the next generation of intelligent applications, transforming everything from product development to customer engagement to marketing. This technology is the key to unlocking unprecedented growth and innovation.\u202f "),(0,i.kt)("p",null,"While the journey may have challenges, the potential rewards make generative AI worth exploring. As generative AI advances, ",(0,i.kt)("a",{parentName:"p",href:"https://azure.microsoft.com/en-us/products/cognitive-services/#overview?WT.mc_id=javascript-99907-ninarasi"},"Azure AI"),"\u2019s broad range of tools and services will help you navigate this fascinating field."),(0,i.kt)("h2",{id:"exercise"},"Exercise"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Watch ",(0,i.kt)("strong",{parentName:"li"},(0,i.kt)("a",{parentName:"strong",href:"https://aka.ms/learnlive-contoso-app-deconstructed-Ep2"},"Episode 02"))," of the serverless edition Learn Live session to learn how to build, test and deploy an end-to-end intelligent app solution."),(0,i.kt)("li",{parentName:"ul"},"Complete the ",(0,i.kt)("strong",{parentName:"li"},(0,i.kt)("a",{parentName:"strong",href:"https://aka.ms/fallforIA/ai-csc"},"Cloud Skills Challenge"))," to build on your AI skills."),(0,i.kt)("li",{parentName:"ul"},"Register for ",(0,i.kt)("strong",{parentName:"li"},(0,i.kt)("a",{parentName:"strong",href:"https://aka.ms/FallForIA/ATE"},"Ask the Expert: Azure Functions"))," on September 26 for a live Q&A with the Product Team on how to build AI apps using serverless computing.")))}c.isMDXComponent=!0},65299:(e,t,a)=>{a.d(t,{Z:()=>n});const n=a.p+"assets/images/blog-image-1-f308cbcc3b29415a5135374b8370a7ca.png"}}]); \ No newline at end of file diff --git a/assets/js/e705147d.e3965d85.js b/assets/js/e705147d.e3965d85.js deleted file mode 100644 index e2e8c8980d..0000000000 --- a/assets/js/e705147d.e3965d85.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[31331],{3905:(e,t,a)=>{a.d(t,{Zo:()=>c,kt:()=>d});var n=a(67294);function i(e,t,a){return t in e?Object.defineProperty(e,t,{value:a,enumerable:!0,configurable:!0,writable:!0}):e[t]=a,e}function r(e,t){var a=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),a.push.apply(a,n)}return a}function o(e){for(var t=1;t=0||(i[a]=e[a]);return i}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,a)&&(i[a]=e[a])}return i}var l=n.createContext({}),p=function(e){var t=n.useContext(l),a=t;return e&&(a="function"==typeof e?e(t):o(o({},t),e)),a},c=function(e){var t=p(e.components);return n.createElement(l.Provider,{value:t},e.children)},u={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},g=n.forwardRef((function(e,t){var a=e.components,i=e.mdxType,r=e.originalType,l=e.parentName,c=s(e,["components","mdxType","originalType","parentName"]),g=p(a),d=i,h=g["".concat(l,".").concat(d)]||g[d]||u[d]||r;return a?n.createElement(h,o(o({ref:t},c),{},{components:a})):n.createElement(h,o({ref:t},c))}));function d(e,t){var a=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var r=a.length,o=new Array(r);o[0]=g;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s.mdxType="string"==typeof e?e:i,o[1]=s;for(var p=2;p{a.r(t),a.d(t,{assets:()=>l,contentTitle:()=>o,default:()=>u,frontMatter:()=>r,metadata:()=>s,toc:()=>p});var n=a(87462),i=(a(67294),a(3905));const r={date:"2023-09-21T09:00",slug:"how-digital-natives-leverage-generative-ai",title:"1-4. How Digital Natives leverage Generative AI",authors:["cnteam"],draft:!1,hide_table_of_contents:!1,toc_min_heading_level:2,toc_max_heading_level:3,keywords:["Cloud","Data","AI","AI/ML","intelligent apps","cloud-native","30-days","enterprise apps","digital experiences","app modernization","serverless","ai apps","data"],image:"https://azure.github.io/Cloud-Native/img/ogImage.png",description:"Article Description: This article explores how digital natives and Independent Software Vendors (ISVs) can transform their business operations and generate higher value for customers with generative AI and intelligent apps.",tags:["Fall-For-IA","30-days-of-IA","learn-live","hack-together","community-buzz","ask-the-expert","azure-kubernetes-service","azure-functions","azure-openai","azure-container-apps","azure-cosmos-db","github-copilot","github-codespaces","github-actions"]},o=void 0,s={permalink:"/Cloud-Native/30daysofIA/how-digital-natives-leverage-generative-ai",source:"@site/blog-30daysofIA/2023-09-21/how-digital-natives-leverage-generative-ai.md",title:"1-4. How Digital Natives leverage Generative AI",description:"Article Description: This article explores how digital natives and Independent Software Vendors (ISVs) can transform their business operations and generate higher value for customers with generative AI and intelligent apps.",date:"2023-09-21T09:00:00.000Z",formattedDate:"September 21, 2023",tags:[{label:"Fall-For-IA",permalink:"/Cloud-Native/30daysofIA/tags/fall-for-ia"},{label:"30-days-of-IA",permalink:"/Cloud-Native/30daysofIA/tags/30-days-of-ia"},{label:"learn-live",permalink:"/Cloud-Native/30daysofIA/tags/learn-live"},{label:"hack-together",permalink:"/Cloud-Native/30daysofIA/tags/hack-together"},{label:"community-buzz",permalink:"/Cloud-Native/30daysofIA/tags/community-buzz"},{label:"ask-the-expert",permalink:"/Cloud-Native/30daysofIA/tags/ask-the-expert"},{label:"azure-kubernetes-service",permalink:"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service"},{label:"azure-functions",permalink:"/Cloud-Native/30daysofIA/tags/azure-functions"},{label:"azure-openai",permalink:"/Cloud-Native/30daysofIA/tags/azure-openai"},{label:"azure-container-apps",permalink:"/Cloud-Native/30daysofIA/tags/azure-container-apps"},{label:"azure-cosmos-db",permalink:"/Cloud-Native/30daysofIA/tags/azure-cosmos-db"},{label:"github-copilot",permalink:"/Cloud-Native/30daysofIA/tags/github-copilot"},{label:"github-codespaces",permalink:"/Cloud-Native/30daysofIA/tags/github-codespaces"},{label:"github-actions",permalink:"/Cloud-Native/30daysofIA/tags/github-actions"}],readingTime:9.29,hasTruncateMarker:!1,authors:[{name:"It's 30DaysOfIA",title:"FallForIA Content Team",url:"https://azure.github.io/Cloud-Native/Fall-For-IA/",imageURL:"https://azure.github.io/Cloud-Native/img/logo-ms-cloud-native.png",key:"cnteam"}],frontMatter:{date:"2023-09-21T09:00",slug:"how-digital-natives-leverage-generative-ai",title:"1-4. How Digital Natives leverage Generative AI",authors:["cnteam"],draft:!1,hide_table_of_contents:!1,toc_min_heading_level:2,toc_max_heading_level:3,keywords:["Cloud","Data","AI","AI/ML","intelligent apps","cloud-native","30-days","enterprise apps","digital experiences","app modernization","serverless","ai apps","data"],image:"https://azure.github.io/Cloud-Native/img/ogImage.png",description:"Article Description: This article explores how digital natives and Independent Software Vendors (ISVs) can transform their business operations and generate higher value for customers with generative AI and intelligent apps.",tags:["Fall-For-IA","30-days-of-IA","learn-live","hack-together","community-buzz","ask-the-expert","azure-kubernetes-service","azure-functions","azure-openai","azure-container-apps","azure-cosmos-db","github-copilot","github-codespaces","github-actions"]},prevItem:{title:"1-5. Preparing the Path for Intelligent Apps",permalink:"/Cloud-Native/30daysofIA/preparing-the-path-for-intelligent-apps"},nextItem:{title:"1-3. Reimagine App Development with AI",permalink:"/Cloud-Native/30daysofIA/reimagine-app-development-with-ai"}},l={authorsImageUrls:[void 0]},p=[{value:"What We'll Cover:",id:"what-well-cover",level:2},{value:"Unleashing Business Transformation: How Digital Natives Can Leverage Generative AI",id:"unleashing-business-transformation-how-digital-natives-can-leverage-generative-ai",level:2},{value:"Unpacking the Potential of Generative AI",id:"unpacking-the-potential-of-generative-ai",level:2},{value:"The Crucial Role of Generative AI in Business Operations",id:"the-crucial-role-of-generative-ai-in-business-operations",level:2},{value:"Navigating Challenges in the Generative AI Landscape",id:"navigating-challenges-in-the-generative-ai-landscape",level:2},{value:"Achieving Harmony: Integrating Generative AI with Existing Business Operations",id:"achieving-harmony-integrating-generative-ai-with-existing-business-operations",level:2},{value:"Conclusion",id:"conclusion",level:2},{value:"Exercise",id:"exercise",level:2}],c={toc:p};function u(e){let{components:t,...r}=e;return(0,i.kt)("wrapper",(0,n.Z)({},c,r,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("head",null,(0,i.kt)("meta",{property:"og:url",content:"https://azure.github.io/cloud-native/30daysofia/how-digital-natives-leverage-generative-ai"}),(0,i.kt)("meta",{property:"og:type",content:"website"}),(0,i.kt)("meta",{property:"og:title",content:"**Fall For Intelligent Apps! \ud83c\udf42| Build AI Apps On Azure"}),(0,i.kt)("meta",{property:"og:description",content:"Explore how digital natives and Independent Software Vendors (ISVs) can transform their business operations and generate higher value for customers with generative AI and intelligent apps."}),(0,i.kt)("meta",{property:"og:image",content:"https://azure.github.io/Cloud-Native/img/ogImage.png"}),(0,i.kt)("meta",{name:"twitter:url",content:"https://azure.github.io/Cloud-Native/30daysofIA/how-digital-natives-leverage-generative-ai"}),(0,i.kt)("meta",{name:"twitter:title",content:"**Fall For Intelligent Apps! \ud83c\udf42 | Build AI Apps On Azure"}),(0,i.kt)("meta",{name:"twitter:description",content:"1-4. Explore how digital natives and Independent Software Vendors (ISVs) can transform their business operations and generate higher value for customers with generative AI and intelligent apps."}),(0,i.kt)("meta",{name:"twitter:image",content:"https://azure.github.io/Cloud-Native/img/ogImage.png"}),(0,i.kt)("meta",{name:"twitter:card",content:"summary_large_image"}),(0,i.kt)("meta",{name:"twitter:creator",content:"@devanshidiaries"}),(0,i.kt)("meta",{name:"twitter:site",content:"@AzureAdvocates"}),(0,i.kt)("link",{rel:"canonical",href:"https://azure.github.io/Cloud-Native/30daysofIA/how-digital-natives-leverage-generative-ai"})),(0,i.kt)("p",null,"Explore how digital natives and Independent Software Vendors (ISVs) can transform their business operations and generate higher value for customers with generative AI and intelligent apps."),(0,i.kt)("h2",{id:"what-well-cover"},"What We'll Cover:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Potential of Generative AI in application development"),(0,i.kt)("li",{parentName:"ul"},"Challenges with Generative AI"),(0,i.kt)("li",{parentName:"ul"},"Integrating Generative AI with business operations")),(0,i.kt)("p",null,(0,i.kt)("img",{alt:"image of generative AI and intelligent apps used in business",src:a(65299).Z,width:"624",height:"380"})),(0,i.kt)("h2",{id:"unleashing-business-transformation-how-digital-natives-can-leverage-generative-ai"},"Unleashing Business Transformation: How Digital Natives Can Leverage Generative AI"),(0,i.kt)("p",null,"Generative AI is no longer a technological flight of fancy. It\u2019s here, and it\u2019s rewriting the rules of business innovation.\u202f "),(0,i.kt)("p",null,"This type of AI leverages machine learning algorithms to create new data with similar characteristics to a given dataset. It goes beyond text generation using generative pretrained transformer (GPT) models, covering an array of applications that span from image synthesis to translations and more. It also powers a new class of intelligent applications, which combine AI capabilities with traditional software functions for enhanced user experiences and advanced business solutions.\u202f "),(0,i.kt)("p",null,"Generative AI is transforming how digital natives and independent software vendors (ISVs) operate. Generative AI offers new possibilities for creating unique business value, enhancing customer experiences, and driving growth.\u202f "),(0,i.kt)("p",null,"It plays a critical role in applications, improving functionality, aiding decision-making, and automating complex tasks to deliver dynamic, uniquely personalized user experiences. Generative AI also enables and automates data-driven outcomes, functions, and features."),(0,i.kt)("p",null,"Integrating AI capabilities into customer-facing apps and turning them into intelligent apps benefits both the customer and the business, as we discussed in ",(0,i.kt)("a",{parentName:"p",href:"https://azure.github.io/Cloud-Native/30daysofIA/demystifying-intelligent-applications"},"Demystifying Intelligent Applications: Leveraging AI in App Development"),". You get faster product development workflows and better business intelligence for sales and marketing, while your users get more natural and context-aware features and interactions.\u202f "),(0,i.kt)("p",null,"That said, to realize generative AI\u2019s potential, you need some strategy and knowledge of how and where it fits into your current business and operations. We\u2019ll examine this technology\u2019s potential to transform business operations, its risks, and rewards, and how to integrate it into your existing operations.\u202f "),(0,i.kt)("h2",{id:"unpacking-the-potential-of-generative-ai"},"Unpacking the Potential of Generative AI"),(0,i.kt)("p",null,"Generative AI is creating new opportunities in the digital playing field. It empowers businesses to generate insights, design products, and automate processes in ways and at speeds that were previously unimaginable. You can now predict customer behavior more accurately, offer highly personalized user experiences, and prototype innovative ideas rapidly.\u202f "),(0,i.kt)("p",null,"For example, Microsoft used generative AI to reinvent web search in the new ",(0,i.kt)("a",{parentName:"p",href:"https://blogs.microsoft.com/blog/2023/02/07/reinventing-search-with-a-new-ai-powered-microsoft-bing-and-edge-your-copilot-for-the-web/"},"AI-powered Bing and Edge"),". The search experience has evolved from a solitary user flow of typing in keywords and scrolling through results. It\u2019s now a conversational experience where Bing uses context to offer better quality and more personalized search queries and results.\u202f "),(0,i.kt)("p",null,"Similarly, Microsoft harnessed generative AI to transform its productivity suite into intelligent apps. Through ",(0,i.kt)("a",{parentName:"p",href:"https://blogs.microsoft.com/blog/2023/03/16/introducing-microsoft-365-copilot-your-copilot-for-work/"},"Copilot for Microsoft 365"),", PowerPoint now empowers users to tell their stories by using DALL-E to generate images effortlessly. It also helps rephrase text-heavy slides to be more straightforward. Outlook now assists users to compose more effective emails, enabling better communication."),(0,i.kt)("p",null,"These are just some examples of how AI powers day-to-day work and operations.\u202f "),(0,i.kt)("p",null,"Businesses that ignore generative AI miss out on untapped market opportunities, letting competitors gain an edge in the digital race. By seeking ways to rethink assumptions and reinvent existing approaches, you can effectively use generative AI to create new opportunities, open products to new use cases and markets, unlock new growth and customer satisfaction levels, and increase your revenue."),(0,i.kt)("admonition",{type:"info"},(0,i.kt)("p",{parentName:"admonition"},"Register for the intelligent apps webinar on ",(0,i.kt)("a",{parentName:"p",href:"https://info.microsoft.com/ww-landing-driving-business-value-by-modernizing-with-cloud-native-and-ai.html?lcid=en-us?WT.mc_id=javascript-99907-ninarasi"},"Driving Business Value by Modernizing with Cloud-Native & AI")," with ",(0,i.kt)("em",{parentName:"p"},"Microsoft")," and ",(0,i.kt)("em",{parentName:"p"},"Forrester")," on ",(0,i.kt)("strong",{parentName:"p"},"September 26"),"."),(0,i.kt)("p",{parentName:"admonition"},"Explore how modernization sets the stage for incorporating AI/ML into existing applications and how building new, intelligent applications can drive innovation and competitive advantage across a range of industries.\u202f ")),(0,i.kt)("h2",{id:"the-crucial-role-of-generative-ai-in-business-operations"},"The Crucial Role of Generative AI in Business Operations"),(0,i.kt)("p",null,"Generative AI can be a powerful catalyst for business transformation. Let\u2019s quickly tour some ways it can redefine business operations.\u202f"),(0,i.kt)("p",null,"Product development is traditionally a long and painstaking process. Now, tools like ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/features/copilot"},"GitHub Copilot")," and ",(0,i.kt)("a",{parentName:"p",href:"https://githubnext.com/projects/testpilot/"},"TestPilot")," use generative AI to make coding and testing software easier, faster, and more robust.\u202f "),(0,i.kt)("p",null,"Similarly, machine learning as a service technology, such as ",(0,i.kt)("a",{parentName:"p",href:"https://azure.microsoft.com/en-us/products/machine-learning?WT.mc_id=javascript-99907-ninarasi"},"Azure Machine Learning"),", democratizes the availability of machine learning. It helps collect, clean, and train models across datasets, making engineering and analyzing data more efficient and accessible.\u202f\u202f "),(0,i.kt)("p",null,"Generative AI also simplifies the process of creating intelligent apps. Previously, simulating thousands of prototypes, predicting their performance, and narrowing down to the most viable options could take weeks, months, or even years of iteration and data analysis. With generative AI, this process can be as quick as days, hours, or perhaps minutes.\u202f "),(0,i.kt)("p",null,"Customer experience is also ripe for transformation. Personalized recommendations drive engagement and sales, and generative AI can scale that personalization to every customer interaction using comprehensive and continually updated data. For instance, Microsoft leverages AI to enable personalization in ",(0,i.kt)("a",{parentName:"p",href:"https://dynamics.microsoft.com/en-us/ai/customer-insights/"},"Dynamics 365 Customer Insights"),". Using this tool, you can ask questions about your customers in natural language and gain valuable and actionable insights into your customer base.\u202f "),(0,i.kt)("p",null,"Lastly, marketing and sales can benefit from generative AI. For example, identifying current and future key markets and emerging trends helps companies stay ahead of the curve. Tools like ",(0,i.kt)("a",{parentName:"p",href:"https://powerbi.microsoft.com/en-us/blog/introducing-microsoft-fabric-and-copilot-in-microsoft-power-bi/"},"Copilot in Power BI")," help you quickly understand and uncover insights about your business, enabling your team to craft targeted campaigns, create engaging content, and optimize your business strategy.\u202f "),(0,i.kt)("p",null,"Leveraging this full generative AI potential requires the right tools. GPT-4, for instance, can be instrumental in realizing effective business transformation. It excels in understanding context, generating human-like text, and learning from minimal data input, making it a formidable tool in any digital native's arsenal. Platforms like ",(0,i.kt)("a",{parentName:"p",href:"https://azure.microsoft.com/en-us/products/ai-services/openai-service?WT.mc_id=javascript-99907-ninarasi"},"Azure OpenAI Service")," bring GPT-4\u2019s power to your fingertips."),(0,i.kt)("h2",{id:"navigating-challenges-in-the-generative-ai-landscape"},"Navigating Challenges in the Generative AI Landscape"),(0,i.kt)("p",null,"However, as with any powerful technology, it\u2019s unwise to treat generative AI as a perfect solution. You must know the challenges and risks of not leveraging it effectively, including technical and human-factor considerations.\u202f "),(0,i.kt)("p",null,"Without generative AI, your company risks missing the opportunity to please customers via personalization, as discussed previously. You\u2019ll miss sales opportunities via business intelligence insights, limiting your ability to grow revenue and keep up with the competition.\u202f "),(0,i.kt)("p",null,"One challenge of leveraging generative AI\u2019s full potential is finding the right tools. Fortunately, you can implement generative AI in your organization without investing in infrastructure, computational power, and expertise. Platforms like ",(0,i.kt)("a",{parentName:"p",href:"https://azure.microsoft.com/en-us/products/ai-services/openai-service?WT.mc_id=javascript-99907-ninarasi"},"Azure OpenAI Service")," and ",(0,i.kt)("a",{parentName:"p",href:"https://azure.microsoft.com/en-us/solutions/ai?WT.mc_id=javascript-99907-ninarasi"},"Azure AI Services")," make AI easily accessible. They also work with Azure compute and data services like\u202f",(0,i.kt)("a",{parentName:"p",href:"https://azure.microsoft.com/en-us/products/functions?WT.mc_id=javascript-99907-ninarasi"},"Azure Functions")," and ",(0,i.kt)("a",{parentName:"p",href:"https://azure.microsoft.com/en-us/products/kubernetes-service/?WT.mc_id=javascript-99907-ninarasi"},"Azure Kubernetes Service")," (AKS), enabling you to build intelligent apps easily and quickly.\u202f "),(0,i.kt)("p",null,"However, generative AI can\u2019t do everything. It may need human expertise and intervention to use it fully.\u202f "),(0,i.kt)("p",null,"For example, generative AI can create unique data. However, the data may have limited diversity and complexity. The AI may need help recognizing additional considerations or requirements specific to a problem. It may also lack context due to various factors, such as the information\u2019s freshness. Also, since it\u2019s an emerging field that\u2019s ",(0,i.kt)("a",{parentName:"p",href:"https://www.mckinsey.com/capabilities/mckinsey-digital/our-insights/the-economic-potential-of-generative-ai-the-next-productivity-frontier#key-insights"},"potentially worth $4.4 trillion"),", we\u2019re still learning about the nuances and ",(0,i.kt)("a",{parentName:"p",href:"https://techcrunch.com/2023/01/27/the-current-legal-cases-against-generative-ai-are-just-the-beginning/"},"potential regulatory impact")," of generative AI.\u202f "),(0,i.kt)("p",null,"Perhaps the most crucial and challenging aspect of adopting generative AI is an organization\u2019s required cultural shift. Your teams must learn how to use AI as your company fosters an understanding of AI\u2019s potential and limitations.\u202f "),(0,i.kt)("p",null,"Begin integrating AI literacy into your company\u2019s DNA. Knowledge of its potential and the readily available tools can inspire your teams to incorporate generative AI into their daily workflows and business operations quickly, improve productivity, and deliver superior customer experiences.\u202f "),(0,i.kt)("p",null,"No company wants to be outpaced by its competitors. You can balance generative AI\u2019s potential with your business operations by staying aware of and adapting to the dynamic AI landscape."),(0,i.kt)("h2",{id:"achieving-harmony-integrating-generative-ai-with-existing-business-operations"},"Achieving Harmony: Integrating Generative AI with Existing Business Operations"),(0,i.kt)("p",null,"Integrating generative AI into existing business operations doesn\u2019t have to be disruptive. With the correct strategy, you can do so seamlessly. Your goal should be to augment and enhance your operations, not disrupt and replace.\u202f "),(0,i.kt)("p",null,"For example, Microsoft Cloud technologies leveraged AI to ",(0,i.kt)("a",{parentName:"p",href:"https://www.microsoft.com/en-us/industry/blog/healthcare/2023/04/12/microsoft-cloud-for-healthcare-empowering-healthcare-to-deliver-meaningful-outcomes/?WT.mc_id=javascript-99907-ninarasi"},"relieve healthcare organizations\u2019 challenges"),", such as talent shortage and workforce burnout. Providers automated their workflows, invested in specific applications and connectors, and enhanced support for data and operations.\u202f "),(0,i.kt)("p",null,"Azure AI Services, such as ",(0,i.kt)("a",{parentName:"p",href:"https://learn.microsoft.com/en-us/azure/ai-services/language-service/text-analytics-for-health/overview?tabs=ner?WT.mc_id=javascript-99907-ninarasi"},"text analytics for health"),", enable healthcare organizations to understand their largely unstructured and untapped medical data. ",(0,i.kt)("a",{parentName:"p",href:"https://azure.microsoft.com/en-us/blog/announcing-project-health-insights-preview-advancing-ai-for-health-data/?WT.mc_id=javascript-99907-ninarasi"},"Project Health Insights")," leans on AI to help healthcare professionals gain actionable insights and inferences from patient data.\u202f "),(0,i.kt)("p",null,(0,i.kt)("a",{parentName:"p",href:"https://techcommunity.microsoft.com/t5/healthcare-and-life-sciences/extending-azure-health-bot-with-azure-openai-service/ba-p/3792560"},"Azure Health Bot"),", an Intelligent App, empowers organizations to build and deploy legally compliant, powerful conversational healthcare experiences to patients.\u202f "),(0,i.kt)("p",null,"A successful AI integration like this begins with understanding. Knowing what generative AI can do and setting realistic expectations can help you identify where and how AI can best serve your operations.\u202f "),(0,i.kt)("p",null,"Next, you can foster a culture of AI literacy across teams, speeding up adoption by helping them understand how it can aid their work. The more familiar your team is with AI and its possibilities, the smoother the transition. Training and education programs play a pivotal role here.\u202f "),(0,i.kt)("p",null,"Finally, integration requires a systematic approach. It\u2019s not about overhauling everything at once \u2014 it's about identifying areas where AI can add the most value, then gradually experimenting and scaling its use. Successful integration stories often feature a phased approach, starting small and growing steadily.\u202f "),(0,i.kt)("p",null,"As we move toward a future where generative AI is a staple in business operations, remember that every journey is unique. Although there\u2019s no one-size-fits-all solution, a strategic approach and a keen understanding of your business needs will help you navigate this new landscape successfully. "),(0,i.kt)("h2",{id:"conclusion"},"Conclusion"),(0,i.kt)("p",null,"Generative AI offers plenty of opportunities for businesses that embrace it. It\u2019s fueling the next generation of intelligent applications, transforming everything from product development to customer engagement to marketing. This technology is the key to unlocking unprecedented growth and innovation.\u202f "),(0,i.kt)("p",null,"While the journey may have challenges, the potential rewards make generative AI worth exploring. As generative AI advances, ",(0,i.kt)("a",{parentName:"p",href:"https://azure.microsoft.com/en-us/products/cognitive-services/#overview?WT.mc_id=javascript-99907-ninarasi"},"Azure AI"),"\u2019s broad range of tools and services will help you navigate this fascinating field."),(0,i.kt)("h2",{id:"exercise"},"Exercise"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Watch ",(0,i.kt)("strong",{parentName:"li"},(0,i.kt)("a",{parentName:"strong",href:"https://aka.ms/learnlive-contoso-app-deconstructed-Ep2"},"Episode 02"))," of the serverless edition Learn Live session to learn how to build, test and deploy an end-to-end intelligent app solution."),(0,i.kt)("li",{parentName:"ul"},"Complete the ",(0,i.kt)("strong",{parentName:"li"},(0,i.kt)("a",{parentName:"strong",href:"https://aka.ms/fallforIA/ai-csc"},"Cloud Skills Challenge"))," to build on your AI skills."),(0,i.kt)("li",{parentName:"ul"},"Register for ",(0,i.kt)("strong",{parentName:"li"},(0,i.kt)("a",{parentName:"strong",href:"https://aka.ms/FallForIA/ATE"},"Ask the Expert: Azure Functions"))," on September 26 for a live Q&A with the Product Team on how to build AI apps using serverless computing.")))}u.isMDXComponent=!0},65299:(e,t,a)=>{a.d(t,{Z:()=>n});const n=a.p+"assets/images/blog-image-1-f308cbcc3b29415a5135374b8370a7ca.png"}}]); \ No newline at end of file diff --git a/assets/js/e73a1950.3fd7c02b.js b/assets/js/e73a1950.3fd7c02b.js new file mode 100644 index 0000000000..b7fa032a40 --- /dev/null +++ b/assets/js/e73a1950.3fd7c02b.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[29168],{75939:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/github-copilot/page/10","page":10,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/github-copilot/page/9","nextPage":"/Cloud-Native/30daysofIA/tags/github-copilot/page/11","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/e82f66e0.b417c3c4.js b/assets/js/e82f66e0.b417c3c4.js new file mode 100644 index 0000000000..215ad3f10a --- /dev/null +++ b/assets/js/e82f66e0.b417c3c4.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[68468],{84087:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/github-codespaces","page":1,"postsPerPage":1,"totalPages":11,"totalCount":11,"nextPage":"/Cloud-Native/30daysofIA/tags/github-codespaces/page/2","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/e82f66e0.ecfbb01b.js b/assets/js/e82f66e0.ecfbb01b.js deleted file mode 100644 index 57246cb298..0000000000 --- a/assets/js/e82f66e0.ecfbb01b.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[68468],{84087:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/github-codespaces","page":1,"postsPerPage":1,"totalPages":9,"totalCount":9,"nextPage":"/Cloud-Native/30daysofIA/tags/github-codespaces/page/2","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/e85f9578.ac63cec0.js b/assets/js/e85f9578.ac63cec0.js deleted file mode 100644 index 63cfda28bc..0000000000 --- a/assets/js/e85f9578.ac63cec0.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[21127],{91832:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/github-actions/page/3","page":3,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/github-actions/page/2","nextPage":"/Cloud-Native/30daysofIA/tags/github-actions/page/4","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/e85f9578.eec3600f.js b/assets/js/e85f9578.eec3600f.js new file mode 100644 index 0000000000..44487c71fa --- /dev/null +++ b/assets/js/e85f9578.eec3600f.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[21127],{91832:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/github-actions/page/3","page":3,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/github-actions/page/2","nextPage":"/Cloud-Native/30daysofIA/tags/github-actions/page/4","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/e90a85bc.31f0cf99.js b/assets/js/e90a85bc.31f0cf99.js new file mode 100644 index 0000000000..74862e1472 --- /dev/null +++ b/assets/js/e90a85bc.31f0cf99.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[39201],{29718:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/hack-together/page/3","page":3,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/hack-together/page/2","nextPage":"/Cloud-Native/30daysofIA/tags/hack-together/page/4","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/e90a85bc.a37f7587.js b/assets/js/e90a85bc.a37f7587.js deleted file mode 100644 index 6bbbda506b..0000000000 --- a/assets/js/e90a85bc.a37f7587.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[39201],{29718:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/hack-together/page/3","page":3,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/hack-together/page/2","nextPage":"/Cloud-Native/30daysofIA/tags/hack-together/page/4","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/e9f92e0d.606dbcda.js b/assets/js/e9f92e0d.606dbcda.js deleted file mode 100644 index 0017d22271..0000000000 --- a/assets/js/e9f92e0d.606dbcda.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[24766],{70487:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/fall-for-ia/page/2","page":2,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/fall-for-ia","nextPage":"/Cloud-Native/30daysofIA/tags/fall-for-ia/page/3","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/e9f92e0d.811b9008.js b/assets/js/e9f92e0d.811b9008.js new file mode 100644 index 0000000000..2758da5d4a --- /dev/null +++ b/assets/js/e9f92e0d.811b9008.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[24766],{70487:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/fall-for-ia/page/2","page":2,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/fall-for-ia","nextPage":"/Cloud-Native/30daysofIA/tags/fall-for-ia/page/3","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/ea15e570.24c52116.js b/assets/js/ea15e570.24c52116.js new file mode 100644 index 0000000000..3a46911bb6 --- /dev/null +++ b/assets/js/ea15e570.24c52116.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[99246],{80686:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/github-copilot/page/3","page":3,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/github-copilot/page/2","nextPage":"/Cloud-Native/30daysofIA/tags/github-copilot/page/4","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/ea15e570.b26e2842.js b/assets/js/ea15e570.b26e2842.js deleted file mode 100644 index 56e51cf77f..0000000000 --- a/assets/js/ea15e570.b26e2842.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[99246],{80686:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/github-copilot/page/3","page":3,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/github-copilot/page/2","nextPage":"/Cloud-Native/30daysofIA/tags/github-copilot/page/4","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/ea89a27b.76738e76.js b/assets/js/ea89a27b.76738e76.js new file mode 100644 index 0000000000..1bb38e7995 --- /dev/null +++ b/assets/js/ea89a27b.76738e76.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[72215],{89207:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/learn-live/page/11","page":11,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/learn-live/page/10","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/eb689fda.e9a3cd63.js b/assets/js/eb689fda.ee9aef96.js similarity index 95% rename from assets/js/eb689fda.e9a3cd63.js rename to assets/js/eb689fda.ee9aef96.js index 32c6a90e6e..ce1e6b33d1 100644 --- a/assets/js/eb689fda.e9a3cd63.js +++ b/assets/js/eb689fda.ee9aef96.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[81583],{30703:s=>{s.exports=JSON.parse('{"label":"azure-cosmos-db","permalink":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[81583],{30703:s=>{s.exports=JSON.parse('{"label":"azure-cosmos-db","permalink":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/ebb76b0d.6bc7151b.js b/assets/js/ebb76b0d.c0390620.js similarity index 74% rename from assets/js/ebb76b0d.6bc7151b.js rename to assets/js/ebb76b0d.c0390620.js index 513cc12fba..b482abcf2b 100644 --- a/assets/js/ebb76b0d.6bc7151b.js +++ b/assets/js/ebb76b0d.c0390620.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[81570],{44222:e=>{e.exports=JSON.parse('{"label":"learn-live","permalink":"/Cloud-Native/30daysofIA/tags/learn-live","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[81570],{44222:e=>{e.exports=JSON.parse('{"label":"learn-live","permalink":"/Cloud-Native/30daysofIA/tags/learn-live","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/ebba27e3.19f47424.js b/assets/js/ebba27e3.19f47424.js new file mode 100644 index 0000000000..ada7d7701d --- /dev/null +++ b/assets/js/ebba27e3.19f47424.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[53199],{160:e=>{e.exports=JSON.parse('{"label":"azure-openai","permalink":"/Cloud-Native/30daysofIA/tags/azure-openai","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/ef7c2797.1a6aaf54.js b/assets/js/ef7c2797.1b163603.js similarity index 72% rename from assets/js/ef7c2797.1a6aaf54.js rename to assets/js/ef7c2797.1b163603.js index c7d8bd9529..b10108e81d 100644 --- a/assets/js/ef7c2797.1a6aaf54.js +++ b/assets/js/ef7c2797.1b163603.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[49391],{1742:e=>{e.exports=JSON.parse('{"label":"ask-the-expert","permalink":"/Cloud-Native/30daysofIA/tags/ask-the-expert","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[49391],{1742:e=>{e.exports=JSON.parse('{"label":"ask-the-expert","permalink":"/Cloud-Native/30daysofIA/tags/ask-the-expert","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/efe016d3.68e5b7b0.js b/assets/js/efe016d3.13a13de6.js similarity index 72% rename from assets/js/efe016d3.68e5b7b0.js rename to assets/js/efe016d3.13a13de6.js index e136f7615f..04ede49681 100644 --- a/assets/js/efe016d3.68e5b7b0.js +++ b/assets/js/efe016d3.13a13de6.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[6994],{32033:a=>{a.exports=JSON.parse('{"label":"github-actions","permalink":"/Cloud-Native/30daysofIA/tags/github-actions","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[6994],{32033:a=>{a.exports=JSON.parse('{"label":"github-actions","permalink":"/Cloud-Native/30daysofIA/tags/github-actions","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/f0d25600.1df33c41.js b/assets/js/f0d25600.1df33c41.js deleted file mode 100644 index 4c2da2063e..0000000000 --- a/assets/js/f0d25600.1df33c41.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[97470],{54458:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/github-codespaces/page/8","page":8,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/github-codespaces/page/7","nextPage":"/Cloud-Native/30daysofIA/tags/github-codespaces/page/9","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/f0d25600.5e8ec391.js b/assets/js/f0d25600.5e8ec391.js new file mode 100644 index 0000000000..c07b1f38be --- /dev/null +++ b/assets/js/f0d25600.5e8ec391.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[97470],{54458:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/github-codespaces/page/8","page":8,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/github-codespaces/page/7","nextPage":"/Cloud-Native/30daysofIA/tags/github-codespaces/page/9","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/f0e90793.bc58d687.js b/assets/js/f0e90793.0eac9db8.js similarity index 95% rename from assets/js/f0e90793.bc58d687.js rename to assets/js/f0e90793.0eac9db8.js index b7496da8a5..f05f2cae82 100644 --- a/assets/js/f0e90793.bc58d687.js +++ b/assets/js/f0e90793.0eac9db8.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[14119],{35394:s=>{s.exports=JSON.parse('{"label":"azure-cosmos-db","permalink":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[14119],{35394:s=>{s.exports=JSON.parse('{"label":"azure-cosmos-db","permalink":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/f12c2396.ebc79d26.js b/assets/js/f12c2396.20aed50e.js similarity index 74% rename from assets/js/f12c2396.ebc79d26.js rename to assets/js/f12c2396.20aed50e.js index 7454a9681a..35d2db2edb 100644 --- a/assets/js/f12c2396.ebc79d26.js +++ b/assets/js/f12c2396.20aed50e.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[5743],{50398:a=>{a.exports=JSON.parse('{"label":"Fall-For-IA","permalink":"/Cloud-Native/30daysofIA/tags/fall-for-ia","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[5743],{50398:a=>{a.exports=JSON.parse('{"label":"Fall-For-IA","permalink":"/Cloud-Native/30daysofIA/tags/fall-for-ia","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/f273b679.29701661.js b/assets/js/f273b679.29701661.js new file mode 100644 index 0000000000..5152713745 --- /dev/null +++ b/assets/js/f273b679.29701661.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[8298],{46387:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/ask-the-expert/page/11","page":11,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/ask-the-expert/page/10","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/f38d0525.5e24e53a.js b/assets/js/f38d0525.5e24e53a.js new file mode 100644 index 0000000000..2ee2b819da --- /dev/null +++ b/assets/js/f38d0525.5e24e53a.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[35979],{81086:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/page/10","page":10,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/page/9","nextPage":"/Cloud-Native/30daysofIA/page/11","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/f5611d39.2c8c96bd.js b/assets/js/f5611d39.2c8c96bd.js new file mode 100644 index 0000000000..0310114ae8 --- /dev/null +++ b/assets/js/f5611d39.2c8c96bd.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[6337],{71367:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/github-actions/page/9","page":9,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/github-actions/page/8","nextPage":"/Cloud-Native/30daysofIA/tags/github-actions/page/10","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/f5611d39.55a4e068.js b/assets/js/f5611d39.55a4e068.js deleted file mode 100644 index 8cb9220716..0000000000 --- a/assets/js/f5611d39.55a4e068.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[6337],{71367:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/github-actions/page/9","page":9,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/github-actions/page/8","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/f5b7c6a9.716334e9.js b/assets/js/f5b7c6a9.716334e9.js new file mode 100644 index 0000000000..8595b77baf --- /dev/null +++ b/assets/js/f5b7c6a9.716334e9.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[39010],{30226:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db","page":1,"postsPerPage":1,"totalPages":11,"totalCount":11,"nextPage":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db/page/2","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/f5b7c6a9.eb909da9.js b/assets/js/f5b7c6a9.eb909da9.js deleted file mode 100644 index 406be01148..0000000000 --- a/assets/js/f5b7c6a9.eb909da9.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[39010],{30226:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db","page":1,"postsPerPage":1,"totalPages":9,"totalCount":9,"nextPage":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db/page/2","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/f6529765.07f885ca.js b/assets/js/f6529765.07f885ca.js new file mode 100644 index 0000000000..98cc8d57a2 --- /dev/null +++ b/assets/js/f6529765.07f885ca.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[52522],{64831:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/hack-together/page/11","page":11,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/hack-together/page/10","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/f6de2cbe.506318cb.js b/assets/js/f6de2cbe.506318cb.js deleted file mode 100644 index fa6dd89388..0000000000 --- a/assets/js/f6de2cbe.506318cb.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[87633],{34866:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/30-days-of-ia/page/3","page":3,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/30-days-of-ia/page/2","nextPage":"/Cloud-Native/30daysofIA/tags/30-days-of-ia/page/4","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/f6de2cbe.84638608.js b/assets/js/f6de2cbe.84638608.js new file mode 100644 index 0000000000..d335ad25c6 --- /dev/null +++ b/assets/js/f6de2cbe.84638608.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[87633],{34866:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/30-days-of-ia/page/3","page":3,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/30-days-of-ia/page/2","nextPage":"/Cloud-Native/30daysofIA/tags/30-days-of-ia/page/4","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/f7310d14.17d5fea5.js b/assets/js/f7310d14.17d5fea5.js new file mode 100644 index 0000000000..1e21c62128 --- /dev/null +++ b/assets/js/f7310d14.17d5fea5.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[38790],{31834:e=>{e.exports=JSON.parse('{"label":"learn-live","permalink":"/Cloud-Native/30daysofIA/tags/learn-live","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/f74cbec7.647f04b0.js b/assets/js/f74cbec7.528f0994.js similarity index 72% rename from assets/js/f74cbec7.647f04b0.js rename to assets/js/f74cbec7.528f0994.js index 6590420532..b5c0d8bca5 100644 --- a/assets/js/f74cbec7.647f04b0.js +++ b/assets/js/f74cbec7.528f0994.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[50427],{72304:a=>{a.exports=JSON.parse('{"label":"github-actions","permalink":"/Cloud-Native/30daysofIA/tags/github-actions","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[50427],{72304:a=>{a.exports=JSON.parse('{"label":"github-actions","permalink":"/Cloud-Native/30daysofIA/tags/github-actions","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/f7b7ef7e.988c1d34.js b/assets/js/f7b7ef7e.988c1d34.js new file mode 100644 index 0000000000..cd801eed1b --- /dev/null +++ b/assets/js/f7b7ef7e.988c1d34.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[4354],{62782:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/learn-live/page/6","page":6,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/learn-live/page/5","nextPage":"/Cloud-Native/30daysofIA/tags/learn-live/page/7","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/f7b7ef7e.e168f09f.js b/assets/js/f7b7ef7e.e168f09f.js deleted file mode 100644 index 6633276fd1..0000000000 --- a/assets/js/f7b7ef7e.e168f09f.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[4354],{62782:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/learn-live/page/6","page":6,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/learn-live/page/5","nextPage":"/Cloud-Native/30daysofIA/tags/learn-live/page/7","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/f7fb9a31.3cf88f51.js b/assets/js/f7fb9a31.3cf88f51.js deleted file mode 100644 index e8f9c91145..0000000000 --- a/assets/js/f7fb9a31.3cf88f51.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[65014],{55042:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/page/3","page":3,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/page/2","nextPage":"/Cloud-Native/30daysofIA/page/4","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/f7fb9a31.ae335a1f.js b/assets/js/f7fb9a31.ae335a1f.js new file mode 100644 index 0000000000..f268293ec0 --- /dev/null +++ b/assets/js/f7fb9a31.ae335a1f.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[65014],{55042:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/page/3","page":3,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/page/2","nextPage":"/Cloud-Native/30daysofIA/page/4","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/f872f327.23de5059.js b/assets/js/f872f327.23de5059.js new file mode 100644 index 0000000000..f8bc71c568 --- /dev/null +++ b/assets/js/f872f327.23de5059.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[38602],{89763:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/ask-the-expert/page/8","page":8,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/ask-the-expert/page/7","nextPage":"/Cloud-Native/30daysofIA/tags/ask-the-expert/page/9","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/f872f327.9d7eb238.js b/assets/js/f872f327.9d7eb238.js deleted file mode 100644 index 2daa7e4eaf..0000000000 --- a/assets/js/f872f327.9d7eb238.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[38602],{89763:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/ask-the-expert/page/8","page":8,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/ask-the-expert/page/7","nextPage":"/Cloud-Native/30daysofIA/tags/ask-the-expert/page/9","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/fa74e77e.245af8f3.js b/assets/js/fa74e77e.245af8f3.js new file mode 100644 index 0000000000..d6af96e7cd --- /dev/null +++ b/assets/js/fa74e77e.245af8f3.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[94125],{38832:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/github-codespaces/page/2","page":2,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/github-codespaces","nextPage":"/Cloud-Native/30daysofIA/tags/github-codespaces/page/3","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/fa74e77e.723f4085.js b/assets/js/fa74e77e.723f4085.js deleted file mode 100644 index 8c71597cf7..0000000000 --- a/assets/js/fa74e77e.723f4085.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[94125],{38832:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/github-codespaces/page/2","page":2,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/github-codespaces","nextPage":"/Cloud-Native/30daysofIA/tags/github-codespaces/page/3","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/faa3ccc1.2c725132.js b/assets/js/faa3ccc1.2c725132.js deleted file mode 100644 index 6706c82b91..0000000000 --- a/assets/js/faa3ccc1.2c725132.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[25196],{60656:e=>{e.exports=JSON.parse('{"title":"#30DaysOfIA","items":[{"title":"1-6. Cultivating a Culture for Intelligent Apps","permalink":"/Cloud-Native/30daysofIA/cultivating-a-culture-for-intelligent-apps"},{"title":"1-5. Preparing the Path for Intelligent Apps","permalink":"/Cloud-Native/30daysofIA/preparing-the-path-for-intelligent-apps"},{"title":"1-4. How Digital Natives leverage Generative AI","permalink":"/Cloud-Native/30daysofIA/how-digital-natives-leverage-generative-ai"},{"title":"1-3. Reimagine App Development with AI","permalink":"/Cloud-Native/30daysofIA/reimagine-app-development-with-ai"},{"title":"1-2. Harnessing the Power of Intelligent Apps","permalink":"/Cloud-Native/30daysofIA/harnessing-the-power-of-intelligent-apps"},{"title":"1-1. Demystifying Intelligent Applications","permalink":"/Cloud-Native/30daysofIA/demystifying-intelligent-applications"},{"title":"Kick-off #30DaysofIA \ud83c\udf42","permalink":"/Cloud-Native/30daysofIA/kick-off"},{"title":"HackTogether Recap \ud83c\udf42","permalink":"/Cloud-Native/30daysofIA/hacktogether-recap"},{"title":"Fall is Coming! \ud83c\udf42","permalink":"/Cloud-Native/30daysofIA/road-to-fallforIA"}]}')}}]); \ No newline at end of file diff --git a/assets/js/faa3ccc1.8f3a55ed.js b/assets/js/faa3ccc1.8f3a55ed.js new file mode 100644 index 0000000000..f5ee848b79 --- /dev/null +++ b/assets/js/faa3ccc1.8f3a55ed.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[25196],{60656:e=>{e.exports=JSON.parse('{"title":"#30DaysOfIA","items":[{"title":"1-6. Cultivating a Culture for Intelligent Apps","permalink":"/Cloud-Native/30daysofIA/cultivating-a-culture-for-intelligent-apps"},{"title":"1-5. Preparing the Path for Intelligent Apps","permalink":"/Cloud-Native/30daysofIA/preparing-the-path-for-intelligent-apps"},{"title":"2-1. Build Your First Intelligent App with Azure AI and AKS-1","permalink":"/Cloud-Native/30daysofIA/build-your-first-intelligent-app-with-azure-ai-and-aks-1"},{"title":"2-2. Build Your First Intelligent App with Azure AI and AKS-2","permalink":"/Cloud-Native/30daysofIA/build-your-first-intelligent-app-with-azure-ai-and-aks-2"},{"title":"1-4. How Digital Natives leverage Generative AI","permalink":"/Cloud-Native/30daysofIA/how-digital-natives-leverage-generative-ai"},{"title":"1-3. Reimagine App Development with AI","permalink":"/Cloud-Native/30daysofIA/reimagine-app-development-with-ai"},{"title":"1-2. Harnessing the Power of Intelligent Apps","permalink":"/Cloud-Native/30daysofIA/harnessing-the-power-of-intelligent-apps"},{"title":"1-1. Demystifying Intelligent Applications","permalink":"/Cloud-Native/30daysofIA/demystifying-intelligent-applications"},{"title":"Kick-off #30DaysofIA \ud83c\udf42","permalink":"/Cloud-Native/30daysofIA/kick-off"},{"title":"HackTogether Recap \ud83c\udf42","permalink":"/Cloud-Native/30daysofIA/hacktogether-recap"},{"title":"Fall is Coming! \ud83c\udf42","permalink":"/Cloud-Native/30daysofIA/road-to-fallforIA"}]}')}}]); \ No newline at end of file diff --git a/assets/js/fac73890.b25b35b1.js b/assets/js/fac73890.b25b35b1.js deleted file mode 100644 index 5ab1c8e5cb..0000000000 --- a/assets/js/fac73890.b25b35b1.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[50395],{20381:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/ask-the-expert/page/9","page":9,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/ask-the-expert/page/8","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/fac73890.c2d4c256.js b/assets/js/fac73890.c2d4c256.js new file mode 100644 index 0000000000..9dc0d63207 --- /dev/null +++ b/assets/js/fac73890.c2d4c256.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[50395],{20381:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/ask-the-expert/page/9","page":9,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/ask-the-expert/page/8","nextPage":"/Cloud-Native/30daysofIA/tags/ask-the-expert/page/10","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/fb5b9b20.a3b5736b.js b/assets/js/fb5b9b20.a2dd7017.js similarity index 95% rename from assets/js/fb5b9b20.a3b5736b.js rename to assets/js/fb5b9b20.a2dd7017.js index 4e1bae5613..7f7f72e0cb 100644 --- a/assets/js/fb5b9b20.a3b5736b.js +++ b/assets/js/fb5b9b20.a2dd7017.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[78060],{39464:s=>{s.exports=JSON.parse('{"label":"azure-cosmos-db","permalink":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[78060],{39464:s=>{s.exports=JSON.parse('{"label":"azure-cosmos-db","permalink":"/Cloud-Native/30daysofIA/tags/azure-cosmos-db","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/fb71245d.907b7674.js b/assets/js/fb71245d.5d25eb96.js similarity index 72% rename from assets/js/fb71245d.907b7674.js rename to assets/js/fb71245d.5d25eb96.js index be08ae9434..bc4dbe501a 100644 --- a/assets/js/fb71245d.907b7674.js +++ b/assets/js/fb71245d.5d25eb96.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[42835],{55885:e=>{e.exports=JSON.parse('{"label":"community-buzz","permalink":"/Cloud-Native/30daysofIA/tags/community-buzz","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[42835],{55885:e=>{e.exports=JSON.parse('{"label":"community-buzz","permalink":"/Cloud-Native/30daysofIA/tags/community-buzz","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/fbdbf422.b27db388.js b/assets/js/fbdbf422.4cd21b16.js similarity index 73% rename from assets/js/fbdbf422.b27db388.js rename to assets/js/fbdbf422.4cd21b16.js index d853c080e5..95f1d8dff8 100644 --- a/assets/js/fbdbf422.b27db388.js +++ b/assets/js/fbdbf422.4cd21b16.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[96010],{80914:a=>{a.exports=JSON.parse('{"label":"30-days-of-IA","permalink":"/Cloud-Native/30daysofIA/tags/30-days-of-ia","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":9}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[96010],{80914:a=>{a.exports=JSON.parse('{"label":"30-days-of-IA","permalink":"/Cloud-Native/30daysofIA/tags/30-days-of-ia","allTagsPath":"/Cloud-Native/30daysofIA/tags","count":11}')}}]); \ No newline at end of file diff --git a/assets/js/fd422a34.52ab7325.js b/assets/js/fd422a34.52ab7325.js deleted file mode 100644 index 47fe2735c6..0000000000 --- a/assets/js/fd422a34.52ab7325.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[16891],{63733:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/github-copilot/page/6","page":6,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/github-copilot/page/5","nextPage":"/Cloud-Native/30daysofIA/tags/github-copilot/page/7","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/fd422a34.a43285ba.js b/assets/js/fd422a34.a43285ba.js new file mode 100644 index 0000000000..c065e249e6 --- /dev/null +++ b/assets/js/fd422a34.a43285ba.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[16891],{63733:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/github-copilot/page/6","page":6,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/github-copilot/page/5","nextPage":"/Cloud-Native/30daysofIA/tags/github-copilot/page/7","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/fd4ba951.23d19ee6.js b/assets/js/fd4ba951.23d19ee6.js new file mode 100644 index 0000000000..e857014f9d --- /dev/null +++ b/assets/js/fd4ba951.23d19ee6.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[83228],{80690:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/ask-the-expert/page/2","page":2,"postsPerPage":1,"totalPages":11,"totalCount":11,"previousPage":"/Cloud-Native/30daysofIA/tags/ask-the-expert","nextPage":"/Cloud-Native/30daysofIA/tags/ask-the-expert/page/3","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/fd4ba951.a923f798.js b/assets/js/fd4ba951.a923f798.js deleted file mode 100644 index 43ce071d1a..0000000000 --- a/assets/js/fd4ba951.a923f798.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[83228],{80690:e=>{e.exports=JSON.parse('{"permalink":"/Cloud-Native/30daysofIA/tags/ask-the-expert/page/2","page":2,"postsPerPage":1,"totalPages":9,"totalCount":9,"previousPage":"/Cloud-Native/30daysofIA/tags/ask-the-expert","nextPage":"/Cloud-Native/30daysofIA/tags/ask-the-expert/page/3","blogDescription":"Develop adaptive, responsive, and personalized experiences by building and modernizing intelligent applications with Azure!","blogTitle":"Learn in #30DaysOfIA"}')}}]); \ No newline at end of file diff --git a/assets/js/main.0f5b4036.js b/assets/js/main.0f5b4036.js deleted file mode 100644 index 82cad6e57f..0000000000 --- a/assets/js/main.0f5b4036.js +++ /dev/null @@ -1,2 +0,0 @@ -/*! For license information please see main.0f5b4036.js.LICENSE.txt */ -(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[40179],{50997:(e,t,a)=>{"use strict";a.d(t,{Z:()=>p});var n=a(67294),o=a(87462),i=a(68356),l=a.n(i),r=a(16887);const s={"00429eb7":[()=>a.e(16509).then(a.bind(a,82092)),"@site/blog/2022-09-17/index.md?truncated=true",82092],"00984d5d":[()=>a.e(99205).then(a.t.bind(a,40161,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-azure-cosmos-db-page-3-9a8-list.json",40161],"0099b9da":[()=>a.e(27078).then(a.t.bind(a,33006,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-30-days-of-ia-page-4-18e.json",33006],"00ac6f8e":[()=>a.e(39761).then(a.t.bind(a,34087,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-fall-for-ia-page-8-753-list.json",34087],"00e5e0c6":[()=>a.e(45345).then(a.bind(a,37122)),"@site/blog-cnny/2023-02-17/cnny-wrap-up.md",37122],"00ff3ab8":[()=>a.e(85992).then(a.t.bind(a,92649,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-hack-together-page-2-c51.json",92649],"010f538e":[()=>a.e(997).then(a.t.bind(a,19881,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-archive-6d8.json",19881],"0154d667":[()=>a.e(20426).then(a.t.bind(a,32215,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-azure-kubernetes-service-page-21-962.json",32215],"015ef8b2":[()=>a.e(85048).then(a.t.bind(a,21626,19)),"~blog/default/cloud-native-blog-tags-zero-to-hero-page-2-555-list.json",21626],"016aea77":[()=>a.e(61489).then(a.t.bind(a,22688,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-github-copilot-page-7-b78-list.json",22688],"017fab63":[()=>a.e(74278).then(a.t.bind(a,65124,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-github-actions-page-7-b7f-list.json",65124],"0182af35":[()=>a.e(18870).then(a.t.bind(a,41865,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-cloud-native-page-2-21c.json",41865],"01a85c17":[()=>Promise.all([a.e(40532),a.e(64013)]).then(a.bind(a,12864)),"@theme/BlogTagsListPage",12864],"01b32472":[()=>a.e(99434).then(a.t.bind(a,31274,19)),"~blog/default/cloud-native-blog-tags-azure-functions-page-5-907-list.json",31274],"01c7a724":[()=>a.e(32344).then(a.t.bind(a,96429,19)),"~blog/blog-cnny/cloud-native-cnny-2023-page-21-fb1.json",96429],"020a0ff4":[()=>a.e(19661).then(a.bind(a,88109)),"@site/blog/2022-09-05/index.md?truncated=true",88109],"021c8d1d":[()=>a.e(63741).then(a.t.bind(a,93090,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-cloud-native-page-4-0de.json",93090],"0254e92e":[()=>a.e(27244).then(a.bind(a,74718)),"@site/blog-cnny/2023-01-30/PodsAndDeployments.md",74718],"02ad56ee":[()=>a.e(8863).then(a.bind(a,78708)),"@site/blog/zero-to-hero/2022-09-06-containerapps.md?truncated=true",78708],"02c87da2":[()=>a.e(98427).then(a.bind(a,15187)),"@site/blog-30daysofIA/2023-09-22/cultivating-a-culture-for-intelligent-apps.md",15187],"02dc33ee":[()=>a.e(67197).then(a.t.bind(a,84625,19)),"~blog/default/cloud-native-blog-tags-serverless-september-page-33-b14-list.json",84625],"02eacc81":[()=>a.e(75555).then(a.t.bind(a,37807,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-azure-kubernetes-service-page-2-3fc.json",37807],"032f8ca1":[()=>a.e(20703).then(a.bind(a,37647)),"@site/blog/2022-08-31/index.md?truncated=true",37647],"03633680":[()=>a.e(17317).then(a.t.bind(a,24358,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-github-copilot-page-5-534-list.json",24358],"04288e05":[()=>a.e(39073).then(a.t.bind(a,62332,19)),"~blog/default/cloud-native-blog-tags-serverless-september-page-24-059.json",62332],"0439459b":[()=>a.e(86761).then(a.t.bind(a,24403,19)),"~blog/default/cloud-native-blog-tags-zero-to-hero-page-5-ddb.json",24403],"04b79bd3":[()=>a.e(37573).then(a.t.bind(a,19372,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-github-copilot-page-3-c8c.json",19372],"05037b3e":[()=>a.e(55912).then(a.t.bind(a,85025,19)),"~blog/default/cloud-native-blog-tags-microservices-page-6-4fc.json",85025],"051147c5":[()=>a.e(80443).then(a.t.bind(a,3731,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-zero-to-hero-page-14-622-list.json",3731],"05309783":[()=>a.e(42346).then(a.t.bind(a,90359,19)),"~blog/default/cloud-native-blog-tags-serverless-september-page-4-592.json",90359],"059e579b":[()=>a.e(73175).then(a.t.bind(a,35705,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-hack-together-page-7-fec.json",35705],"05a8e5eb":[()=>a.e(90866).then(a.t.bind(a,95780,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-cloud-native-page-10-650-list.json",95780],"05b7df8f":[()=>a.e(90388).then(a.t.bind(a,38797,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-learn-live-page-2-796-list.json",38797],"05cbd5e2":[()=>a.e(55191).then(a.t.bind(a,98861,19)),"~blog/default/cloud-native-blog-tags-azure-container-apps-b0c.json",98861],"069faf48":[()=>a.e(16781).then(a.t.bind(a,92608,19)),"~blog/default/cloud-native-blog-tags-zero-to-hero-page-8-956-list.json",92608],"06b5abd5":[()=>a.e(66814).then(a.t.bind(a,53106,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-azure-kubernetes-service-page-20-076-list.json",53106],"06db7cdd":[()=>a.e(53118).then(a.t.bind(a,61420,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-azure-kubernetes-service-page-2-3fc-list.json",61420],"07182537":[()=>a.e(60738).then(a.t.bind(a,63998,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-ask-the-expert-page-8-6c4-list.json",63998],"07865a4c":[()=>a.e(19273).then(a.t.bind(a,54908,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-ask-the-expert-page-7-af0.json",54908],"079fe0b3":[()=>a.e(13596).then(a.t.bind(a,56755,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-azure-kubernetes-service-page-15-695-list.json",56755],"07a9616f":[()=>a.e(42392).then(a.bind(a,67022)),"@site/blog-30daysofIA/2023-09-22/preparing-the-path-for-intelligent-apps.md?truncated=true",67022],"082a9ce0":[()=>a.e(26453).then(a.t.bind(a,82972,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-secrets-management-dc2.json",82972],"087fccde":[()=>a.e(90244).then(a.t.bind(a,67847,19)),"~blog/default/cloud-native-blog-tags-serverless-september-page-14-258-list.json",67847],"08a845a3":[()=>a.e(82048).then(a.t.bind(a,92701,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-kubernetes-d9a-list.json",92701],"08bdb996":[()=>a.e(24966).then(a.t.bind(a,14299,19)),"~blog/default/cloud-native-blog-tags-serverless-september-page-5-ee6-list.json",14299],"08c13187":[()=>a.e(37050).then(a.t.bind(a,28569,19)),"~blog/default/cloud-native-blog-tags-microservices-page-10-e37-list.json",28569],"08cc3f2a":[()=>a.e(84643).then(a.bind(a,71552)),"@site/blog/2022-09-24/index.md?truncated=true",71552],"0955c03d":[()=>a.e(77170).then(a.t.bind(a,13156,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-30-daysofcloudnative-page-3-0c7.json",13156],"09a8101c":[()=>a.e(63637).then(a.t.bind(a,82504,19)),"~blog/default/cloud-native-blog-page-17-4a4.json",82504],"09bb4c1b":[()=>a.e(77877).then(a.t.bind(a,98498,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-30-days-of-ia-page-4-18e-list.json",98498],"0a09fc38":[()=>a.e(3273).then(a.t.bind(a,29696,19)),"~blog/default/cloud-native-blog-tags-30-days-of-serverless-page-7-b12-list.json",29696],"0a1ee2df":[()=>a.e(94304).then(a.t.bind(a,58966,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-github-copilot-page-2-43b.json",58966],"0a90bd61":[()=>a.e(66019).then(a.bind(a,90939)),"@site/blog-cnny/2023-02-06/index.md?truncated=true",90939],"0a9d7c60":[()=>a.e(54491).then(a.t.bind(a,1043,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-page-8-cca.json",1043],"0abb84f4":[()=>a.e(80819).then(a.bind(a,38848)),"@site/blog/2022-09-07/index.md",38848],"0abf7f02":[()=>a.e(60197).then(a.t.bind(a,23734,19)),"~blog/default/cloud-native-blog-tags-python-956-list.json",23734],"0b5f5bbf":[()=>a.e(33990).then(a.t.bind(a,75606,19)),"~blog/default/cloud-native-blog-tags-azure-functions-page-9-c01.json",75606],"0ba01ce9":[()=>a.e(39402).then(a.t.bind(a,73990,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-github-codespaces-page-3-3fa-list.json",73990],"0bcbab68":[()=>a.e(52017).then(a.t.bind(a,64408,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-cloud-native-page-7-3e0-list.json",64408],"0c5ad103":[()=>a.e(40127).then(a.t.bind(a,72828,19)),"~blog/default/cloud-native-blog-tags-30-days-of-serverless-page-18-183.json",72828],"0c8d610a":[()=>a.e(46142).then(a.t.bind(a,71869,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-aks-page-3-97b.json",71869],"0c929776":[()=>a.e(15536).then(a.t.bind(a,12074,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-azure-functions-page-6-1ed-list.json",12074],"0cc5e40a":[()=>a.e(95047).then(a.t.bind(a,19139,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-azure-cosmos-db-page-9-a1c.json",19139],"0d808a5a":[()=>a.e(91645).then(a.t.bind(a,65667,19)),"~blog/default/cloud-native-blog-tags-azure-container-apps-page-9-052-list.json",65667],"0d936d6e":[()=>a.e(90861).then(a.t.bind(a,44710,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-workload-identity-8c2-list.json",44710],"0db3c1f5":[()=>a.e(29741).then(a.t.bind(a,14602,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-azure-openai-page-4-dae.json",14602],"0dcb0a11":[()=>a.e(72157).then(a.t.bind(a,78721,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-page-6-5ba.json",78721],"0dfdf1d3":[()=>a.e(40122).then(a.t.bind(a,36165,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-ask-the-expert-page-4-21a-list.json",36165],"0e1333d1":[()=>a.e(63991).then(a.bind(a,88529)),"@site/src/pages/calendar.md",88529],"0e5b1676":[()=>a.e(95095).then(a.t.bind(a,62212,19)),"~blog/default/cloud-native-blog-tags-azure-container-apps-page-10-fae-list.json",62212],"0e655584":[()=>a.e(90539).then(a.t.bind(a,77666,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-azure-kubernetes-service-page-3-b53.json",77666],"0e760f5c":[()=>a.e(1738).then(a.t.bind(a,58277,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-zero-to-hero-page-10-f09-list.json",58277],"0e832ef5":[()=>a.e(38130).then(a.t.bind(a,69272,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-extensions-15e.json",69272],"0f00d983":[()=>a.e(79522).then(a.t.bind(a,43269,19)),"~blog/default/cloud-native-blog-tags-serverless-bba.json",43269],"0f2db0e2":[()=>a.e(76508).then(a.bind(a,6421)),"@site/blog-30daysofIA/2023-09-08/hack-together-recap.md?truncated=true",6421],"0f519dc1":[()=>a.e(55269).then(a.bind(a,85394)),"@site/src/pages/serverless-september/30DaysOfServerless.md",85394],"0f8260a7":[()=>a.e(88439).then(a.t.bind(a,1820,19)),"~blog/default/cloud-native-blog-tags-microservices-page-4-fbf.json",1820],"0fa6c6d6":[()=>a.e(93609).then(a.t.bind(a,61515,19)),"~blog/default/cloud-native-blog-page-27-df6.json",61515],"0fc52616":[()=>a.e(4391).then(a.t.bind(a,95009,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-azure-key-vault-4ba-list.json",95009],"0fe97ad9":[()=>a.e(29789).then(a.t.bind(a,88691,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-cloud-native-new-year-page-3-4c5.json",88691],"1007ba84":[()=>a.e(84752).then(a.t.bind(a,26658,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-cloud-native-new-year-page-2-cd2.json",26658],"10362b01":[()=>a.e(27019).then(a.bind(a,6337)),"@site/docs/resources/devtools.md",6337],"114a0ea4":[()=>a.e(6664).then(a.t.bind(a,202,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-containers-page-2-487.json",202],"11f27dd8":[()=>a.e(87097).then(a.t.bind(a,2361,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-hack-together-page-2-c51-list.json",2361],"1224d608":[()=>a.e(51494).then(a.t.bind(a,11522,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-30-daysofcloudnative-page-3-0c7-list.json",11522],"1280d58f":[()=>a.e(37838).then(a.t.bind(a,68417,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-hack-together-d8e-list.json",68417],"129735db":[()=>a.e(47348).then(a.t.bind(a,11003,19)),"~blog/blog-cnny/cloud-native-cnny-2023-page-6-ab9.json",11003],"12a40cbb":[()=>a.e(94419).then(a.t.bind(a,16189,19)),"~blog/default/cloud-native-blog-tags-serverless-september-page-7-741.json",16189],"12d48ddb":[()=>a.e(30214).then(a.t.bind(a,11933,19)),"~blog/default/cloud-native-blog-tags-dapr-page-6-a0a.json",11933],"1314fad1":[()=>a.e(29357).then(a.t.bind(a,61613,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-ask-the-expert-page-8-0db.json",61613],13524175:[()=>a.e(95701).then(a.t.bind(a,8626,19)),"~blog/default/cloud-native-blog-tags-azure-container-apps-page-5-ad2.json",8626],"137765a7":[()=>a.e(94106).then(a.t.bind(a,15560,19)),"~blog/default/cloud-native-blog-tags-30-days-of-serverless-page-19-c26.json",15560],"138b7335":[()=>a.e(98588).then(a.t.bind(a,52254,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-azure-cosmos-db-page-4-3bf-list.json",52254],"13f90819":[()=>a.e(10297).then(a.bind(a,90530)),"@site/blog/2022-09-08/index.md?truncated=true",90530],"1420d1e4":[()=>a.e(97492).then(a.t.bind(a,70676,19)),"~blog/default/cloud-native-blog-tags-serverless-september-page-2-5a3.json",70676],"14e99011":[()=>a.e(55558).then(a.bind(a,78990)),"@site/blog/2022-09-10/index.md?truncated=true",78990],"14eb3368":[()=>Promise.all([a.e(40532),a.e(9817)]).then(a.bind(a,80853)),"@theme/DocCategoryGeneratedIndexPage",80853],"14f6b037":[()=>a.e(15494).then(a.bind(a,57899)),"@site/src/pages/serverless-september/ServerlessHacks.md",57899],"155d8733":[()=>a.e(65487).then(a.t.bind(a,23110,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-ask-the-expert-page-2-afc.json",23110],"1567a249":[()=>a.e(92428).then(a.t.bind(a,59767,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-azure-key-vault-page-2-601.json",59767],"157ee2cb":[()=>a.e(79864).then(a.t.bind(a,31578,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-azure-container-apps-page-9-d42-list.json",31578],"158ed014":[()=>a.e(18031).then(a.bind(a,73429)),"@site/blog/2022-09-10/index.md",73429],"15a2eb39":[()=>a.e(30299).then(a.t.bind(a,52820,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-ask-the-expert-page-13-799-list.json",52820],16399044:[()=>a.e(72971).then(a.bind(a,7026)),"@site/src/pages/New-Year/ate.md",7026],"167f29d7":[()=>a.e(45123).then(a.t.bind(a,20959,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-azure-openai-page-2-1e8.json",20959],"168e5dc9":[()=>a.e(97214).then(a.t.bind(a,9327,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-30-daysofcloudnative-page-7-cf4-list.json",9327],"170f5865":[()=>a.e(42374).then(a.t.bind(a,44171,19)),"~blog/default/cloud-native-blog-tags-azure-functions-page-6-4d2-list.json",44171],"171c08f2":[()=>a.e(19977).then(a.t.bind(a,28734,19)),"~blog/default/cloud-native-blog-tags-30-days-of-serverless-d1d-list.json",28734],"1730ce49":[()=>a.e(8258).then(a.t.bind(a,83250,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-30-daysofcloudnative-page-2-88a.json",83250],17896441:[()=>Promise.all([a.e(40532),a.e(75287),a.e(27918)]).then(a.bind(a,42889)),"@theme/DocItem",42889],"17bd234e":[()=>a.e(78505).then(a.t.bind(a,68639,19)),"~blog/default/cloud-native-blog-tags-hacktoberfest-e38.json",68639],"17e657ee":[()=>a.e(40812).then(a.t.bind(a,6167,19)),"~blog/default/cloud-native-blog-tags-azure-container-apps-page-7-cc0-list.json",6167],18305907:[()=>a.e(77309).then(a.t.bind(a,23040,19)),"~blog/default/cloud-native-blog-tags-java-f5a-list.json",23040],"1864d48a":[()=>a.e(12584).then(a.t.bind(a,22869,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-community-buzz-page-3-601-list.json",22869],"18754cb8":[()=>a.e(88526).then(a.t.bind(a,55285,19)),"~blog/default/cloud-native-blog-tags-custom-connector-6cd-list.json",55285],"18b1ff93":[()=>Promise.all([a.e(40532),a.e(15944),a.e(66033)]).then(a.bind(a,33925)),"@site/src/pages/New-Year/index.js",33925],"1933092b":[()=>a.e(67434).then(a.t.bind(a,25727,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-zero-to-hero-page-13-48d-list.json",25727],"195ec8c1":[()=>a.e(34492).then(a.t.bind(a,98960,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-cloud-native-new-year-page-3-4c5-list.json",98960],"197575b4":[()=>a.e(34214).then(a.t.bind(a,23226,19)),"~blog/default/cloud-native-blog-tags-serverless-september-page-8-4a4-list.json",23226],"19ce5436":[()=>a.e(628).then(a.t.bind(a,42632,19)),"~blog/default/cloud-native-blog-tags-azure-functions-page-2-6e7.json",42632],"19d4af76":[()=>a.e(93602).then(a.t.bind(a,138,19)),"~blog/default/cloud-native-blog-tags-zero-to-hero-page-6-460.json",138],"19dd9a55":[()=>a.e(37748).then(a.t.bind(a,82488,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-cloud-native-new-year-be5.json",82488],"19e5cabb":[()=>a.e(76434).then(a.t.bind(a,22651,19)),"~blog/default/cloud-native-blog-tags-serverless-september-page-23-168-list.json",22651],"1a3abbc3":[()=>a.e(59593).then(a.t.bind(a,74315,19)),"~blog/default/cloud-native-blog-tags-dapr-page-15-dad.json",74315],"1a4e3b56":[()=>a.e(89677).then(a.t.bind(a,37873,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-azure-openai-32e-list.json",37873],"1a86c495":[()=>a.e(81644).then(a.bind(a,6692)),"@site/blog-30daysofIA/2023-09-22/cultivating-a-culture-for-intelligent-apps.md?truncated=true",6692],"1aaa8d08":[()=>a.e(14631).then(a.t.bind(a,82405,19)),"~blog/default/cloud-native-blog-tags-azure-functions-page-14-f80-list.json",82405],"1b60c5ab":[()=>a.e(43832).then(a.t.bind(a,93747,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-azure-cosmos-db-page-7-741-list.json",93747],"1b66ef97":[()=>a.e(26788).then(a.t.bind(a,70740,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-azure-openai-page-8-15b-list.json",70740],"1b81fd5d":[()=>a.e(66240).then(a.t.bind(a,24858,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-azure-openai-page-7-185-list.json",24858],"1ba2ee3a":[()=>a.e(46145).then(a.t.bind(a,5794,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-hack-together-page-5-7fa-list.json",5794],"1bdf368a":[()=>a.e(22184).then(a.t.bind(a,89865,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-notation-581-list.json",89865],"1be78505":[()=>Promise.all([a.e(40532),a.e(29514)]).then(a.bind(a,81299)),"@theme/DocPage",81299],"1bf711d8":[()=>a.e(1178).then(a.bind(a,18981)),"@site/blog-cnny/2023-02-14/building-with-draft.md",18981],"1c6266f1":[()=>a.e(49484).then(a.t.bind(a,95184,19)),"~blog/blog-cnny/cloud-native-cnny-2023-page-13-937.json",95184],"1c7a0340":[()=>a.e(36779).then(a.t.bind(a,5416,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-ingress-page-2-7d4.json",5416],"1c8f664c":[()=>a.e(61086).then(a.t.bind(a,3076,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-azure-kubernetes-service-ce5-list.json",3076],"1c910b4c":[()=>a.e(30248).then(a.bind(a,53893)),"@site/blog-cnny/2023-01-25/30days.md?truncated=true",53893],"1c94b949":[()=>a.e(46986).then(a.t.bind(a,95679,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-community-buzz-page-8-330.json",95679],"1c9ffcde":[()=>a.e(62972).then(a.t.bind(a,65743,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-ask-the-expert-page-11-f9f.json",65743],"1cc51124":[()=>a.e(46643).then(a.t.bind(a,48040,19)),"~blog/default/cloud-native-blog-tags-event-hubs-da7-list.json",48040],"1ddf7480":[()=>a.e(25857).then(a.t.bind(a,19279,19)),"~blog/default/cloud-native-blog-tags-30-days-of-serverless-page-16-fb6.json",19279],"1e1d1ee9":[()=>a.e(3639).then(a.t.bind(a,58634,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-azure-kubernetes-service-page-7-c57.json",58634],"1e8313b4":[()=>a.e(77934).then(a.t.bind(a,70735,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-30-days-of-ia-page-8-e5e.json",70735],"1e942b07":[()=>a.e(87166).then(a.t.bind(a,58006,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-30-daysofcloudnative-page-16-df7.json",58006],"1ef7a213":[()=>a.e(3664).then(a.t.bind(a,29604,19)),"~blog/default/cloud-native-blog-tags-devtools-308-list.json",29604],"1f391b9e":[()=>Promise.all([a.e(40532),a.e(75287),a.e(13085)]).then(a.bind(a,96252)),"@theme/MDXPage",96252],"1fb2655d":[()=>a.e(35090).then(a.t.bind(a,15745,19)),"/home/runner/work/Cloud-Native/Cloud-Native/website/.docusaurus/docusaurus-plugin-content-pages/default/plugin-route-context-module-100.json",15745],"1fbd1224":[()=>a.e(66651).then(a.t.bind(a,48e3,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-persistent-volumes-06c-list.json",48e3],"1fc5a212":[()=>a.e(92404).then(a.t.bind(a,39519,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-github-codespaces-page-3-3fa.json",39519],"2007206c":[()=>a.e(59978).then(a.t.bind(a,2575,19)),"~blog/default/cloud-native-blog-tags-azure-developer-cli-page-2-005-list.json",2575],"209bfeb4":[()=>a.e(85844).then(a.t.bind(a,28449,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-azure-functions-page-4-802.json",28449],"21a4b026":[()=>a.e(30611).then(a.t.bind(a,90984,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-30-daysofcloudnative-page-4-c86.json",90984],"221d88a7":[()=>a.e(15344).then(a.t.bind(a,12103,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-azure-kubernetes-service-page-2-c55.json",12103],"221f3b9a":[()=>a.e(91378).then(a.t.bind(a,83544,19)),"~blog/default/cloud-native-blog-tags-azure-developer-cli-083.json",83544],22598362:[()=>a.e(66809).then(a.t.bind(a,84058,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-learn-live-page-4-863-list.json",84058],"225bf44d":[()=>a.e(96935).then(a.t.bind(a,84814,19)),"~blog/default/cloud-native-blog-tags-javascript-f6c-list.json",84814],"225d85cd":[()=>a.e(22154).then(a.t.bind(a,38760,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-cloud-native-page-9-c09-list.json",38760],22711736:[()=>a.e(28583).then(a.t.bind(a,69964,19)),"~blog/default/cloud-native-blog-tags-serverless-september-page-3-788.json",69964],"2287f69c":[()=>a.e(17696).then(a.bind(a,92673)),"@site/blog/2022-09-29/index.md",92673],"2294c633":[()=>a.e(36770).then(a.t.bind(a,7186,19)),"~blog/default/cloud-native-blog-tags-30-days-of-serverless-page-14-3a6.json",7186],"22a8c514":[()=>a.e(86608).then(a.t.bind(a,51492,19)),"~blog/default/cloud-native-blog-tags-power-platform-fa1-list.json",51492],"232a5b9a":[()=>a.e(1484).then(a.t.bind(a,19490,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-zero-to-hero-page-11-69a-list.json",19490],"23b53440":[()=>a.e(29017).then(a.t.bind(a,43347,19)),"~blog/default/cloud-native-blog-tags-dapr-page-6-a0a-list.json",43347],"243ca1b7":[()=>a.e(22281).then(a.bind(a,68760)),"@site/blog-30daysofIA/2023-09-15/kick-off.md",68760],"244544b0":[()=>a.e(97597).then(a.bind(a,19926)),"@site/blog-cnny/2023-02-17/cnny-wrap-up.md?truncated=true",19926],"24634ed0":[()=>a.e(31348).then(a.t.bind(a,17780,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-community-buzz-page-2-684.json",17780],"2464c061":[()=>a.e(89641).then(a.t.bind(a,52080,19)),"~blog/default/cloud-native-blog-tags-serverless-september-page-27-5c3-list.json",52080],"246d6ed0":[()=>a.e(77099).then(a.t.bind(a,9625,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-ask-the-expert-page-8-6c4.json",9625],"24c13bf4":[()=>a.e(68630).then(a.t.bind(a,20629,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-fall-for-ia-page-4-d65.json",20629],25076710:[()=>a.e(40882).then(a.t.bind(a,94953,19)),"~blog/default/cloud-native-blog-tags-dapr-page-15-dad-list.json",94953],"2508adfd":[()=>a.e(41642).then(a.t.bind(a,78728,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-learn-live-page-2-796.json",78728],25508138:[()=>a.e(70729).then(a.t.bind(a,71077,19)),"~blog/default/cloud-native-blog-tags-30-days-of-serverless-page-11-4c9-list.json",71077],25619125:[()=>a.e(73344).then(a.bind(a,23202)),"@site/blog/2022-09-13/index.md",23202],"259b3259":[()=>a.e(78078).then(a.t.bind(a,45812,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-learn-live-page-6-33d.json",45812],"260068d7":[()=>a.e(15238).then(a.t.bind(a,49523,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-fall-for-ia-page-7-8de-list.json",49523],"2605ac5e":[()=>a.e(73323).then(a.t.bind(a,84404,19)),"~blog/default/cloud-native-blog-tags-azure-container-apps-page-3-fe6-list.json",84404],"262e1fb1":[()=>a.e(84432).then(a.t.bind(a,11926,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-zero-to-hero-page-4-5ad-list.json",11926],"26a80f01":[()=>a.e(12045).then(a.t.bind(a,83634,19)),"~blog/default/cloud-native-blog-tags-30-days-of-serverless-page-6-e41-list.json",83634],"26ca5cfc":[()=>a.e(92922).then(a.t.bind(a,68512,19)),"~blog/default/cloud-native-blog-tags-30-days-of-serverless-page-15-6f2.json",68512],"26fa933c":[()=>a.e(43018).then(a.t.bind(a,39978,19)),"~blog/default/cloud-native-blog-tags-microservices-page-7-410.json",39978],"273187e1":[()=>a.e(85969).then(a.t.bind(a,5308,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-azure-kubernetes-service-d0a.json",5308],"2759b647":[()=>a.e(70936).then(a.t.bind(a,50632,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-zero-to-hero-page-10-f09.json",50632],"276eee65":[()=>a.e(74960).then(a.t.bind(a,21504,19)),"~blog/blog-cnny/cloud-native-cnny-2023-page-8-423.json",21504],"2790a299":[()=>a.e(30724).then(a.t.bind(a,70669,19)),"~blog/default/cloud-native-blog-tags-microservices-page-4-fbf-list.json",70669],"27a255b0":[()=>a.e(69197).then(a.t.bind(a,6274,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-azure-kubernetes-service-page-19-572.json",6274],"27dcd181":[()=>a.e(64419).then(a.t.bind(a,26171,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-azure-kubernetes-service-page-5-93c-list.json",26171],"2828c0bd":[()=>a.e(92489).then(a.t.bind(a,36327,19)),"~blog/default/cloud-native-blog-tags-azure-functions-page-12-e32-list.json",36327],"2891c2a3":[()=>a.e(62516).then(a.t.bind(a,68501,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-containers-page-3-c64.json",68501],"28ea4247":[()=>a.e(37923).then(a.t.bind(a,28157,19)),"~blog/blog-cnny/cloud-native-cnny-2023-page-15-4e5.json",28157],"290bbe6d":[()=>a.e(3015).then(a.t.bind(a,97357,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-ask-the-expert-page-13-799.json",97357],"292dd6ab":[()=>a.e(63695).then(a.t.bind(a,64058,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-azure-cosmos-db-page-7-741.json",64058],"297e3da8":[()=>a.e(71133).then(a.t.bind(a,1863,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-30-daysofcloudnative-page-15-fb4-list.json",1863],"2aaf12ef":[()=>a.e(7010).then(a.bind(a,1243)),"@site/blog/2022-09-25/index.md",1243],"2ab0497b":[()=>a.e(8320).then(a.t.bind(a,50371,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-azure-container-apps-page-3-dea-list.json",50371],"2acb43b2":[()=>a.e(71468).then(a.t.bind(a,16029,19)),"~blog/default/cloud-native-blog-page-4-d61.json",16029],"2b022a0d":[()=>a.e(65926).then(a.t.bind(a,82095,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-cloud-native-new-year-page-4-0f4-list.json",82095],"2b471e02":[()=>a.e(96995).then(a.bind(a,60780)),"@site/blog/2022-09-03/index.md?truncated=true",60780],"2b94f1a7":[()=>a.e(14999).then(a.t.bind(a,72831,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-cloud-native-page-13-fd4-list.json",72831],"2bc88290":[()=>a.e(9321).then(a.t.bind(a,18267,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-azure-kubernetes-service-page-4-3e9-list.json",18267],"2bcfefbc":[()=>a.e(22189).then(a.t.bind(a,52698,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-ask-the-expert-page-3-6d4.json",52698],"2c0c4af3":[()=>a.e(21383).then(a.t.bind(a,29947,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-azure-kubernetes-service-page-16-618.json",29947],"2c72658d":[()=>a.e(81615).then(a.t.bind(a,82854,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-30-days-of-ia-page-9-c99.json",82854],"2c768b07":[()=>a.e(79636).then(a.t.bind(a,40873,19)),"~blog/default/cloud-native-blog-tags-serverless-september-page-25-1b0.json",40873],"2cf5c9f6":[()=>a.e(5558).then(a.t.bind(a,9155,19)),"~blog/default/cloud-native-blog-tags-serverless-september-page-6-3be.json",9155],"2d813f9f":[()=>a.e(22405).then(a.t.bind(a,1025,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-azure-cosmos-db-page-8-c1b-list.json",1025],"2d86cfb6":[()=>a.e(63259).then(a.t.bind(a,52573,19)),"~blog/default/cloud-native-blog-tags-azure-developer-cli-083-list.json",52573],"2e52b9a2":[()=>a.e(77795).then(a.t.bind(a,80660,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-zero-to-hero-page-3-4e3.json",80660],"2e6fe460":[()=>a.e(47652).then(a.t.bind(a,52951,19)),"~blog/default/cloud-native-blog-tags-azure-container-apps-page-14-2ac.json",52951],"2efdc0bf":[()=>a.e(30018).then(a.t.bind(a,16129,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-github-actions-page-2-54e-list.json",16129],"2f117675":[()=>a.e(78059).then(a.t.bind(a,26037,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-kubernetes-page-2-536.json",26037],"2f2b5329":[()=>a.e(33133).then(a.t.bind(a,24211,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-ask-the-expert-page-4-e76-list.json",24211],"2f5655a7":[()=>a.e(74668).then(a.t.bind(a,86213,19)),"~blog/default/cloud-native-blog-tags-30-days-of-serverless-page-6-e41.json",86213],"2fff3a21":[()=>a.e(71328).then(a.t.bind(a,20429,19)),"~blog/default/cloud-native-blog-tags-docker-compose-cb7-list.json",20429],"300f5a7a":[()=>a.e(30700).then(a.t.bind(a,47754,19)),"~blog/default/cloud-native-blog-tags-openapi-877.json",47754],"300fad81":[()=>a.e(65477).then(a.t.bind(a,22868,19)),"~blog/default/cloud-native-blog-tags-azure-container-apps-page-8-77c.json",22868],"304f028d":[()=>a.e(10552).then(a.t.bind(a,42168,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-aks-page-4-89b.json",42168],"3052e807":[()=>a.e(68648).then(a.t.bind(a,79328,19)),"~blog/default/cloud-native-blog-tags-serverless-september-page-7-741-list.json",79328],"30b4a191":[()=>a.e(92788).then(a.t.bind(a,93570,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-azure-kubernetes-service-page-5-514-list.json",93570],"30f26a7a":[()=>a.e(60169).then(a.t.bind(a,73278,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-learn-live-577-list.json",73278],"30f527c7":[()=>a.e(10063).then(a.t.bind(a,59971,19)),"~blog/default/cloud-native-blog-tags-serverless-september-page-28-c65.json",59971],"31098de5":[()=>a.e(64315).then(a.t.bind(a,40919,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-azure-container-apps-page-7-2f1-list.json",40919],"310da260":[()=>a.e(31757).then(a.bind(a,49106)),"@site/blog/2022-09-04/index.md?truncated=true",49106],"311af35b":[()=>a.e(18159).then(a.bind(a,5445)),"@site/blog-cnny/2023-02-14/building-with-draft.md?truncated=true",5445],"3125c86a":[()=>a.e(67490).then(a.t.bind(a,81698,19)),"~blog/default/cloud-native-blog-tags-serverless-september-page-12-d03-list.json",81698],"31c97e84":[()=>a.e(62595).then(a.t.bind(a,70969,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-azure-key-vault-4ba.json",70969],"31f0dae5":[()=>a.e(97665).then(a.t.bind(a,19722,19)),"~blog/default/cloud-native-blog-page-25-1d4.json",19722],"328b45c7":[()=>a.e(418).then(a.t.bind(a,87357,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-github-codespaces-page-6-a9c.json",87357],"329ba483":[()=>a.e(61859).then(a.bind(a,66062)),"@site/blog/zero-to-hero/2022-09-19-containerapps.md?truncated=true",66062],"3402daf1":[()=>a.e(57050).then(a.t.bind(a,30353,19)),"~blog/default/cloud-native-blog-tags-dapr-page-12-12d-list.json",30353],"343a65b7":[()=>a.e(3099).then(a.bind(a,43274)),"@site/src/pages/serverless-september/ZeroToHero.md",43274],"3505d13c":[()=>a.e(6597).then(a.t.bind(a,58766,19)),"~blog/default/cloud-native-blog-tags-serverless-september-page-22-1d7.json",58766],"358af5d5":[()=>a.e(89887).then(a.t.bind(a,53655,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-community-buzz-page-7-3b8-list.json",53655],"358ca55c":[()=>a.e(11579).then(a.t.bind(a,50555,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-extensions-15e-list.json",50555],"35ac9352":[()=>a.e(63013).then(a.t.bind(a,95529,19)),"~blog/default/cloud-native-blog-tags-azure-functions-970-list.json",95529],"36385a98":[()=>a.e(65992).then(a.t.bind(a,54675,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-cloud-native-page-4-0de-list.json",54675],"367b1faa":[()=>a.e(15405).then(a.t.bind(a,59123,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-community-buzz-page-8-330-list.json",59123],"36e282e0":[()=>a.e(98686).then(a.t.bind(a,96356,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-azure-functions-page-7-066.json",96356],"36ea8d35":[()=>a.e(67562).then(a.t.bind(a,52702,19)),"~blog/default/cloud-native-blog-tags-dotnet-page-2-742-list.json",52702],"371b5a64":[()=>a.e(6800).then(a.t.bind(a,49705,19)),"~blog/default/cloud-native-blog-page-28-af5.json",49705],"37734e29":[()=>a.e(37651).then(a.t.bind(a,18557,19)),"~blog/default/cloud-native-blog-tags-dapr-page-12-12d.json",18557],"37e3b2f7":[()=>a.e(97613).then(a.bind(a,28537)),"@site/blog/2022-09-15/index.md?truncated=true",28537],"382b7bd1":[()=>a.e(92097).then(a.bind(a,81473)),"@site/docs/resources/serverless.md",81473],"3852251c":[()=>Promise.all([a.e(40532),a.e(15944),a.e(6050)]).then(a.bind(a,12096)),"@site/src/pages/Fall-For-IA/HackTogether.js",12096],"38554ecb":[()=>a.e(5168).then(a.t.bind(a,14248,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-github-copilot-page-4-f06.json",14248],"388c8f2b":[()=>a.e(80538).then(a.t.bind(a,50125,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-learn-live-page-5-f88-list.json",50125],"3914fc9d":[()=>a.e(47585).then(a.bind(a,22174)),"@site/blog-30daysofIA/2023-09-19/harnessing-the-power-of-intelligent-apps.md",22174],39156339:[()=>a.e(12099).then(a.t.bind(a,34475,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-azure-kubernetes-service-page-4-3e9.json",34475],"391aff16":[()=>a.e(25286).then(a.t.bind(a,80783,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-tags-b26.json",80783],"39200a92":[()=>a.e(53876).then(a.t.bind(a,31808,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-community-buzz-068-list.json",31808],39704467:[()=>a.e(61154).then(a.t.bind(a,34520,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-ask-the-expert-page-2-101-list.json",34520],39792805:[()=>a.e(94645).then(a.t.bind(a,97253,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-github-codespaces-page-4-fa7.json",97253],"3a0a8c2d":[()=>a.e(20343).then(a.t.bind(a,40833,19)),"~blog/blog-cnny/cloud-native-cnny-2023-page-7-a57.json",40833],"3a1a5f0f":[()=>a.e(30306).then(a.t.bind(a,32675,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-fall-for-ia-page-6-bb4-list.json",32675],"3a3cf5dd":[()=>a.e(92514).then(a.t.bind(a,87915,19)),"~blog/default/cloud-native-blog-tags-zero-to-hero-page-5-ddb-list.json",87915],"3a7594cb":[()=>a.e(13464).then(a.t.bind(a,97623,19)),"~blog/default/cloud-native-blog-tags-azure-container-apps-page-20-a60-list.json",97623],"3a894f2b":[()=>a.e(90759).then(a.t.bind(a,94398,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-github-actions-252-list.json",94398],"3ae3f3bb":[()=>a.e(67231).then(a.t.bind(a,88589,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-30-days-of-ia-page-3-b08.json",88589],"3b690a08":[()=>a.e(55084).then(a.t.bind(a,74943,19)),"~blog/default/cloud-native-blog-tags-dapr-page-14-bae.json",74943],"3bbe6001":[()=>a.e(27222).then(a.t.bind(a,45549,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-github-copilot-page-4-f06-list.json",45549],"3bfb947c":[()=>a.e(4707).then(a.t.bind(a,92449,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-azure-kubernetes-service-page-3-b53-list.json",92449],"3c0e6537":[()=>a.e(82651).then(a.t.bind(a,62836,19)),"~blog/default/cloud-native-blog-tags-zero-to-hero-6be-list.json",62836],"3c2b2163":[()=>a.e(53174).then(a.bind(a,35446)),"@site/blog/2022-09-02/index.md?truncated=true",35446],"3ca1fc8b":[()=>a.e(91700).then(a.t.bind(a,73408,19)),"~blog/default/cloud-native-blog-tags-azure-functions-page-13-ae5.json",73408],"3ccca367":[()=>a.e(93327).then(a.t.bind(a,97412,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-community-buzz-page-5-478-list.json",97412],"3d607786":[()=>a.e(42613).then(a.bind(a,44504)),"@site/blog-cnny/2023-02-16/index.md",44504],"3dd66ec8":[()=>a.e(74854).then(a.t.bind(a,2147,19)),"~blog/default/cloud-native-blog-tags-dapr-page-7-dd8.json",2147],"3e04afb2":[()=>a.e(59322).then(a.t.bind(a,65242,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-hack-together-page-6-4f5-list.json",65242],"3e382c14":[()=>a.e(69171).then(a.t.bind(a,28150,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-ask-the-expert-page-12-e20.json",28150],"3e423595":[()=>a.e(29620).then(a.t.bind(a,97901,19)),"~blog/default/cloud-native-blog-tags-30-days-of-serverless-page-8-64e-list.json",97901],"3ee42e3e":[()=>a.e(13497).then(a.t.bind(a,1468,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-cloud-native-new-year-be5-list.json",1468],"3f09b1d2":[()=>a.e(52911).then(a.t.bind(a,79217,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-azure-openai-page-6-6f1.json",79217],"3f49754a":[()=>a.e(14899).then(a.t.bind(a,57748,19)),"~blog/default/cloud-native-blog-tags-azure-logic-apps-233.json",57748],"3f4f5020":[()=>a.e(7477).then(a.t.bind(a,60193,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-azure-kubernetes-service-page-8-5be.json",60193],"3f534172":[()=>a.e(62259).then(a.t.bind(a,54492,19)),"~blog/default/cloud-native-blog-tags-azure-container-apps-page-13-faf-list.json",54492],"3f5ea235":[()=>a.e(16857).then(a.bind(a,86130)),"@site/blog/zero-to-hero/2022-09-12-azurefunctions.md",86130],"3f5f9a5a":[()=>a.e(51128).then(a.t.bind(a,18574,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-azure-container-apps-page-7-2f1.json",18574],"4052d3d5":[()=>a.e(16355).then(a.t.bind(a,11819,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-zero-to-hero-page-14-622.json",11819],"4058c823":[()=>a.e(13398).then(a.t.bind(a,42954,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-ask-the-expert-page-6-a9d.json",42954],"40ec79c5":[()=>a.e(37979).then(a.t.bind(a,24083,19)),"~blog/default/cloud-native-blog-tags-serverless-september-page-12-d03.json",24083],"4104106c":[()=>a.e(98433).then(a.t.bind(a,75315,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-containers-page-2-487-list.json",75315],41786804:[()=>a.e(60899).then(a.t.bind(a,61837,19)),"~blog/default/cloud-native-blog-tags-cloud-native-c26.json",61837],"417f410e":[()=>a.e(75927).then(a.t.bind(a,87491,19)),"~blog/default/cloud-native-blog-tags-azure-event-grid-page-2-c6c-list.json",87491],"41c52eb0":[()=>a.e(57168).then(a.t.bind(a,2450,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-aks-page-3-97b-list.json",2450],"4204125f":[()=>a.e(95259).then(a.bind(a,90119)),"@site/blog-cnny/2023-01-22/30days.md?truncated=true",90119],"4220343e":[()=>a.e(8109).then(a.t.bind(a,8136,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-ingress-fe8-list.json",8136],42421040:[()=>a.e(15540).then(a.t.bind(a,42223,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-azure-kubernetes-service-page-6-b0f-list.json",42223],"425319e1":[()=>a.e(63323).then(a.bind(a,77611)),"@site/blog-cnny/2023-02-02/index.md?truncated=true",77611],"4254c5fd":[()=>a.e(40117).then(a.t.bind(a,8143,19)),"~blog/default/cloud-native-blog-tags-30-days-of-serverless-page-19-c26-list.json",8143],"426f5ee7":[()=>a.e(60606).then(a.t.bind(a,15050,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-kubernetes-page-5-dcb-list.json",15050],42917112:[()=>a.e(14220).then(a.t.bind(a,46649,19)),"~blog/blog-cnny/cloud-native-cnny-2023-page-12-907.json",46649],"42fd365f":[()=>a.e(28275).then(a.t.bind(a,68281,19)),"~blog/blog-cnny/cloud-native-cnny-2023-page-11-93d.json",68281],43386584:[()=>a.e(88654).then(a.t.bind(a,17816,19)),"~blog/default/cloud-native-blog-tags-zero-to-hero-page-3-024-list.json",17816],"434ff406":[()=>a.e(82010).then(a.t.bind(a,73859,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-zero-to-hero-page-6-63e-list.json",73859],"444ef230":[()=>a.e(43086).then(a.t.bind(a,42089,19)),"~blog/default/cloud-native-blog-tags-azure-container-apps-page-4-6ed.json",42089],"44a20d39":[()=>a.e(80901).then(a.t.bind(a,26578,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-zero-to-hero-63d.json",26578],45528793:[()=>a.e(90210).then(a.t.bind(a,43965,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-30-daysofcloudnative-page-13-324-list.json",43965],"45b07980":[()=>a.e(43345).then(a.t.bind(a,58790,19)),"~blog/default/cloud-native-blog-tags-microsoft-graph-dba.json",58790],"45fd4fee":[()=>a.e(81155).then(a.t.bind(a,53956,19)),"~blog/default/cloud-native-blog-tags-serverless-september-page-11-62b-list.json",53956],"46f628a8":[()=>a.e(87836).then(a.t.bind(a,98361,19)),"~blog/default/cloud-native-blog-tags-serverless-september-page-19-8be-list.json",98361],"470ed423":[()=>a.e(43602).then(a.t.bind(a,70293,19)),"~blog/default/cloud-native-blog-tags-zero-to-hero-6be.json",70293],"4741b16e":[()=>a.e(97021).then(a.t.bind(a,90362,19)),"~blog/default/cloud-native-blog-tags-dapr-page-8-f68.json",90362],"485b9e1f":[()=>a.e(33275).then(a.t.bind(a,40539,19)),"~blog/default/cloud-native-blog-tags-azure-container-apps-page-2-f0d-list.json",40539],"488446b3":[()=>a.e(49623).then(a.t.bind(a,40057,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-azure-kubernetes-service-page-6-905-list.json",40057],"488d465e":[()=>a.e(58079).then(a.t.bind(a,20174,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-ask-the-expert-page-14-507.json",20174],"48a6e2e6":[()=>a.e(37903).then(a.bind(a,23355)),"@site/blog-30daysofIA/2023-09-22/preparing-the-path-for-intelligent-apps.md",23355],"48d7f22e":[()=>a.e(60303).then(a.bind(a,6321)),"@site/blog-cnny/2023-02-10/index.md",6321],"48d83bfd":[()=>a.e(57119).then(a.t.bind(a,28928,19)),"~blog/default/cloud-native-blog-tags-serverless-september-page-20-cef.json",28928],"48db209f":[()=>a.e(84038).then(a.t.bind(a,11359,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-30-daysofcloudnative-page-9-377-list.json",11359],"48ef63ed":[()=>a.e(33630).then(a.t.bind(a,32370,19)),"~blog/default/cloud-native-blog-tags-zero-to-hero-page-7-fbf-list.json",32370],"48efa9f4":[()=>a.e(56653).then(a.t.bind(a,73001,19)),"~blog/default/cloud-native-blog-tags-30-days-of-serverless-page-16-fb6-list.json",73001],"497459e9":[()=>a.e(83283).then(a.t.bind(a,31769,19)),"~blog/default/cloud-native-blog-tags-hello-edf.json",31769],"4988404d":[()=>a.e(24913).then(a.t.bind(a,57723,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-azure-openai-page-2-1e8-list.json",57723],"4a100773":[()=>a.e(40339).then(a.bind(a,19394)),"@site/blog/2022-09-17/index.md",19394],"4a4c152b":[()=>a.e(13360).then(a.bind(a,39079)),"@site/blog/2022-09-12/index.md",39079],"4a6890ba":[()=>a.e(41571).then(a.bind(a,35894)),"@site/src/pages/New-Year/calendar.md",35894],"4a8159d5":[()=>a.e(29129).then(a.bind(a,45110)),"@site/blog/2022-09-29/index.md?truncated=true",45110],"4a85be1e":[()=>a.e(35727).then(a.t.bind(a,48564,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-azure-kubernetes-service-page-14-619-list.json",48564],"4a8dbbc6":[()=>a.e(122).then(a.t.bind(a,38170,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-azure-kubernetes-service-d0a-list.json",38170],"4a93df7c":[()=>a.e(15133).then(a.t.bind(a,79287,19)),"~blog/default/cloud-native-blog-tags-microsoft-365-a5d.json",79287],"4a945222":[()=>a.e(35002).then(a.t.bind(a,67773,19)),"~blog/default/cloud-native-blog-tags-serverless-september-page-30-abd-list.json",67773],"4aa36b6e":[()=>a.e(93744).then(a.bind(a,34339)),"@site/blog/2022-10-04/index.md",34339],"4b3f15eb":[()=>a.e(87930).then(a.t.bind(a,75472,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-30-days-of-ia-page-5-831-list.json",75472],"4b7d35aa":[()=>a.e(41203).then(a.bind(a,45106)),"@site/blog/2022-09-11/index.md?truncated=true",45106],"4c3b9235":[()=>a.e(19581).then(a.t.bind(a,27060,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-ask-the-expert-page-9-2d6.json",27060],"4c3daab0":[()=>a.e(89443).then(a.t.bind(a,1005,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-github-codespaces-page-7-3a6-list.json",1005],"4c97e608":[()=>a.e(26315).then(a.t.bind(a,65949,19)),"~blog/default/cloud-native-blog-tags-hacktoberfest-e38-list.json",65949],"4d20e64b":[()=>a.e(57159).then(a.t.bind(a,4062,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-azure-openai-page-9-492-list.json",4062],"4d232fa6":[()=>a.e(44113).then(a.t.bind(a,24122,19)),"~blog/default/cloud-native-blog-tags-dotnet-5cc.json",24122],"4d42bb9b":[()=>a.e(67856).then(a.t.bind(a,90781,19)),"~blog/default/cloud-native-blog-page-3-f81.json",90781],"4d46cbe9":[()=>a.e(43464).then(a.t.bind(a,28763,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-azure-container-apps-page-8-446.json",28763],"4e1c0a1c":[()=>a.e(20015).then(a.t.bind(a,55770,19)),"~blog/default/cloud-native-blog-tags-azure-logic-apps-page-3-764-list.json",55770],"4e4c4edb":[()=>a.e(42310).then(a.t.bind(a,7850,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-ask-the-expert-page-15-b7b.json",7850],"4e65812e":[()=>a.e(85493).then(a.t.bind(a,79036,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-azure-kubernetes-service-page-17-8dd.json",79036],"4e85b922":[()=>a.e(41307).then(a.t.bind(a,96542,19)),"~blog/default/cloud-native-blog-page-5-eda.json",96542],"4edd86bf":[()=>a.e(22422).then(a.t.bind(a,9007,19)),"~blog/default/cloud-native-blog-tags-azure-container-apps-page-6-dd4.json",9007],"4edf6cbb":[()=>a.e(25125).then(a.t.bind(a,86987,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-ask-the-expert-page-5-170-list.json",86987],"4f088abf":[()=>a.e(43488).then(a.t.bind(a,14861,19)),"~blog/default/cloud-native-blog-tags-azd-273-list.json",14861],"4f0dde4f":[()=>a.e(39259).then(a.bind(a,14900)),"@site/blog/2022-08-31/index.md",14900],"4f2455b0":[()=>a.e(15851).then(a.bind(a,25139)),"@site/blog-cnny/2023-02-06/index.md",25139],"4f2db759":[()=>a.e(22691).then(a.t.bind(a,68197,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-cloud-native-page-14-ee3.json",68197],"4f49e52d":[()=>a.e(94287).then(a.t.bind(a,58547,19)),"~blog/default/cloud-native-blog-tags-zero-to-hero-page-7-fbf.json",58547],"4f63e6a8":[()=>a.e(19056).then(a.t.bind(a,45025,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-30-daysofcloudnative-page-8-f87.json",45025],"4f789287":[()=>a.e(63186).then(a.t.bind(a,3980,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-fall-for-ia-page-7-8de.json",3980],"4fbefe4a":[()=>a.e(733).then(a.t.bind(a,16998,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-service-3e1.json",16998],"50b61013":[()=>a.e(97018).then(a.t.bind(a,73537,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-azure-functions-page-5-dd8-list.json",73537],"511592be":[()=>a.e(29566).then(a.bind(a,91073)),"@site/blog-cnny/2023-01-24/30days.md",91073],"515b0e16":[()=>a.e(31003).then(a.t.bind(a,85148,19)),"~blog/default/cloud-native-blog-tags-zero-to-hero-page-6-460-list.json",85148],"51698cc9":[()=>a.e(71791).then(a.t.bind(a,18898,19)),"~blog/default/cloud-native-blog-tags-zero-to-hero-page-8-956.json",18898],"51b7d1eb":[()=>a.e(84275).then(a.t.bind(a,81025,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-ingress-page-2-7d4-list.json",81025],"51f200c1":[()=>a.e(7485).then(a.t.bind(a,22644,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-github-copilot-page-9-47f-list.json",22644],"51fb1599":[()=>a.e(22362).then(a.t.bind(a,13472,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-azure-functions-page-3-e0b-list.json",13472],52052568:[()=>a.e(8106).then(a.t.bind(a,13302,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-zero-to-hero-page-12-28b.json",13302],"52094cec":[()=>a.e(41003).then(a.t.bind(a,45118,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-azure-cosmos-db-page-6-176-list.json",45118],"52285efb":[()=>a.e(757).then(a.t.bind(a,3675,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-azure-kubernetes-service-page-11-7b8.json",3675],"5250d15a":[()=>a.e(77182).then(a.t.bind(a,68437,19)),"~docs/default/category-cloud-nativedocs-tutorialsidebar-category-serverless-resources-506.json",68437],"52a2e7f4":[()=>a.e(93683).then(a.t.bind(a,77062,19)),"~blog/default/cloud-native-blog-tags-dotnet-5cc-list.json",77062],"52c003c1":[()=>a.e(52950).then(a.t.bind(a,62635,19)),"~blog/default/cloud-native-blog-tags-azure-container-apps-page-19-820.json",62635],"52fb3760":[()=>a.e(86440).then(a.t.bind(a,38121,19)),"~blog/default/cloud-native-blog-tags-docker-compose-cb7.json",38121],"52fc18de":[()=>a.e(1053).then(a.t.bind(a,980,19)),"~blog/default/cloud-native-blog-tags-serverless-september-page-18-03a.json",980],"532dad37":[()=>a.e(97655).then(a.bind(a,19838)),"@site/blog-30daysofIA/2023-08-28/road-to-fallforia.md",19838],"535cf760":[()=>a.e(23089).then(a.t.bind(a,64694,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-azure-container-apps-2d8.json",64694],"5361a0e4":[()=>a.e(65269).then(a.t.bind(a,52720,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-hack-together-page-9-f7e.json",52720],"53b3fc79":[()=>a.e(56686).then(a.t.bind(a,82345,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-nginx-ingress-controller-100-list.json",82345],"541e2717":[()=>a.e(27855).then(a.t.bind(a,94665,19)),"~blog/default/cloud-native-blog-tags-30-days-of-serverless-page-8-64e.json",94665],"543df9b7":[()=>a.e(23849).then(a.t.bind(a,20171,19)),"~blog/blog-cnny/blog-post-list-prop-blog-cnny.json",20171],"54625fb2":[()=>a.e(50958).then(a.t.bind(a,75239,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-community-buzz-page-3-601.json",75239],"54a5ea7d":[()=>a.e(53389).then(a.t.bind(a,14126,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-notary-225-list.json",14126],"54e2ce19":[()=>a.e(67e3).then(a.t.bind(a,66519,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-kubernetes-page-3-190-list.json",66519],"54e82dbd":[()=>a.e(37728).then(a.bind(a,33104)),"@site/blog-cnny/2023-01-23/cloud-native-fundamentals.md?truncated=true",33104],"54e84bd7":[()=>a.e(40766).then(a.t.bind(a,97487,19)),"~blog/default/cloud-native-blog-tags-azure-container-apps-page-3-fe6.json",97487],"5517e946":[()=>a.e(15399).then(a.t.bind(a,66815,19)),"~blog/default/cloud-native-blog-tags-vscode-e62.json",66815],"55404de1":[()=>a.e(72644).then(a.t.bind(a,49195,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-github-actions-page-7-b7f.json",49195],"554c686d":[()=>a.e(83510).then(a.bind(a,97451)),"@site/blog-cnny/2023-01-27/explore-options.md",97451],"55c9118e":[()=>a.e(2830).then(a.t.bind(a,40787,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-hack-together-page-8-a09.json",40787],"56141d95":[()=>a.e(74452).then(a.bind(a,68608)),"@site/blog-30daysofIA/2023-09-19/harnessing-the-power-of-intelligent-apps.md?truncated=true",68608],"561eb05e":[()=>a.e(69189).then(a.t.bind(a,98517,19)),"~blog/default/cloud-native-blog-tags-dapr-page-14-bae-list.json",98517],"563c77e7":[()=>a.e(41735).then(a.bind(a,3272)),"@site/src/pages/serverless-september/CloudSkills.md",3272],"56ac2859":[()=>a.e(64232).then(a.t.bind(a,23940,19)),"~blog/blog-cnny/cloud-native-cnny-2023-page-20-6c7.json",23940],"56ed19dd":[()=>a.e(8420).then(a.bind(a,55849)),"@site/blog-cnny/2023-02-10/index.md?truncated=true",55849],"570b38e4":[()=>a.e(95774).then(a.t.bind(a,97673,19)),"~blog/default/cloud-native-blog-tags-azure-container-apps-page-11-2ad.json",97673],"57904ffd":[()=>a.e(9118).then(a.t.bind(a,86324,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-github-codespaces-page-4-fa7-list.json",86324],"57ada458":[()=>a.e(79203).then(a.t.bind(a,43344,19)),"~blog/default/cloud-native-blog-tags-serverless-september-page-14-258.json",43344],"57e8111f":[()=>a.e(28687).then(a.bind(a,81787)),"@site/blog/2022-09-24/index.md",81787],"57fa9de9":[()=>a.e(36650).then(a.t.bind(a,48541,19)),"~blog/default/cloud-native-blog-tags-logic-apps-c49.json",48541],58413115:[()=>a.e(68368).then(a.bind(a,73695)),"@site/blog-cnny/2023-02-08/index.md",73695],"584ccef3":[()=>a.e(33508).then(a.t.bind(a,82611,19)),"~blog/default/cloud-native-blog-tags-azure-functions-970.json",82611],"587f2372":[()=>a.e(55522).then(a.t.bind(a,26435,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-hack-together-page-3-880.json",26435],"5979b063":[()=>a.e(37961).then(a.bind(a,80577)),"@site/blog-30daysofIA/2023-09-08/hack-together-recap.md",80577],"597d409d":[()=>a.e(71223).then(a.t.bind(a,98463,19)),"~blog/default/cloud-native-blog-tags-30-days-of-serverless-page-5-05c.json",98463],"599dc25b":[()=>a.e(59548).then(a.t.bind(a,80204,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-azure-functions-page-6-1ed.json",80204],"59a2dd7b":[()=>a.e(38582).then(a.t.bind(a,75929,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-fall-for-ia-page-9-1f6-list.json",75929],"5a27c07c":[()=>a.e(30227).then(a.t.bind(a,75339,19)),"~blog/default/cloud-native-blog-tags-30-days-of-serverless-page-15-6f2-list.json",75339],"5ac38b2f":[()=>a.e(75080).then(a.t.bind(a,54759,19)),"~blog/default/cloud-native-blog-tags-serverless-september-page-9-ef1-list.json",54759],"5ae2fd00":[()=>a.e(14219).then(a.t.bind(a,72010,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-fall-for-ia-page-2-b6a.json",72010],"5b107cfc":[()=>a.e(70176).then(a.t.bind(a,8369,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-github-actions-page-4-d4b-list.json",8369],"5b188835":[()=>a.e(11142).then(a.t.bind(a,76473,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-30-daysofcloudnative-page-6-efa-list.json",76473],"5b222fc6":[()=>a.e(63642).then(a.t.bind(a,3928,19)),"~blog/default/cloud-native-blog-tags-hello-edf-list.json",3928],"5b38bd06":[()=>a.e(86592).then(a.bind(a,10537)),"@site/blog-cnny/2023-02-15/index.md?truncated=true",10537],"5c062db9":[()=>a.e(27256).then(a.t.bind(a,19391,19)),"~blog/default/cloud-native-blog-tags-microservices-page-11-7c3-list.json",19391],"5c2ccfbc":[()=>a.e(51115).then(a.t.bind(a,44621,19)),"~blog/default/cloud-native-blog-tags-microservices-page-7-410-list.json",44621],"5c3ab9e3":[()=>a.e(76310).then(a.t.bind(a,73541,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-azure-openai-page-7-185.json",73541],"5cab2412":[()=>a.e(90494).then(a.t.bind(a,32798,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-azure-openai-page-9-492.json",32798],"5cd45a8d":[()=>a.e(83512).then(a.t.bind(a,29516,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-30-days-of-ia-page-2-c12-list.json",29516],"5cf46a9a":[()=>a.e(10853).then(a.t.bind(a,42662,19)),"~blog/default/cloud-native-blog-tags-keda-49d.json",42662],"5d153b8f":[()=>a.e(71303).then(a.t.bind(a,43905,19)),"~blog/default/cloud-native-blog-page-9-e1c.json",43905],"5d9699b4":[()=>a.e(57040).then(a.t.bind(a,24256,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-zero-to-hero-page-16-f9c-list.json",24256],"5db8c956":[()=>a.e(60840).then(a.t.bind(a,8669,19)),"~blog/default/cloud-native-blog-tags-microservices-page-3-307.json",8669],"5e204f51":[()=>a.e(30743).then(a.bind(a,85727)),"@site/blog/2022-09-11/index.md",85727],"5e4e61a3":[()=>a.e(9437).then(a.bind(a,97586)),"@site/blog-cnny/2023-02-13/30days.md?truncated=true",97586],"5e601653":[()=>a.e(35059).then(a.t.bind(a,55270,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-azure-cosmos-db-page-5-7a9-list.json",55270],"5e9f5e1a":[()=>Promise.resolve().then(a.bind(a,36809)),"@generated/docusaurus.config",36809],"5ebfacad":[()=>a.e(42589).then(a.t.bind(a,70259,19)),"~blog/default/cloud-native-blog-tags-azure-functions-page-11-c62-list.json",70259],"5f63ac35":[()=>a.e(24359).then(a.t.bind(a,76757,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-github-copilot-page-2-43b-list.json",76757],"5f86de18":[()=>a.e(19093).then(a.t.bind(a,54780,19)),"~blog/default/cloud-native-blog-tags-serverless-september-page-20-cef-list.json",54780],"5fc14c1c":[()=>a.e(32502).then(a.bind(a,98450)),"@site/blog-30daysofIA/2023-09-20/reimagine-app-development-with-ai.md?truncated=true",98450],"603045b5":[()=>a.e(74970).then(a.t.bind(a,62454,19)),"~blog/default/cloud-native-blog-tags-azure-logic-apps-page-2-7a3-list.json",62454],"6042952c":[()=>a.e(19570).then(a.t.bind(a,33103,19)),"~blog/default/cloud-native-blog-tags-azure-container-apps-page-12-82f.json",33103],"604b448d":[()=>a.e(6312).then(a.t.bind(a,43366,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-github-actions-page-5-22f.json",43366],"605b2ff8":[()=>a.e(19863).then(a.t.bind(a,96950,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-github-copilot-page-9-47f.json",96950],"605b97c9":[()=>a.e(46822).then(a.t.bind(a,38006,19)),"~blog/default/cloud-native-blog-tags-azure-container-apps-page-2-f0d.json",38006],"60f25d92":[()=>a.e(19457).then(a.t.bind(a,78669,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-30-daysofcloudnative-page-12-b68-list.json",78669],"60fcf8e3":[()=>a.e(22531).then(a.t.bind(a,17503,19)),"~blog/default/cloud-native-blog-tags-azure-container-apps-page-18-295.json",17503],"6131b196":[()=>a.e(81225).then(a.t.bind(a,71330,19)),"~blog/default/cloud-native-blog-tags-dapr-page-3-52a-list.json",71330],"61c47875":[()=>a.e(78192).then(a.t.bind(a,67680,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-secure-supply-chain-07b-list.json",67680],"61d029d7":[()=>a.e(72050).then(a.t.bind(a,27895,19)),"~blog/default/cloud-native-blog-page-12-154.json",27895],"631988a9":[()=>a.e(91584).then(a.t.bind(a,94749,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-azure-kubernetes-service-page-4-dac.json",94749],"6352e992":[()=>a.e(10426).then(a.t.bind(a,95465,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-cloud-native-60e.json",95465],"6383d72d":[()=>a.e(92311).then(a.t.bind(a,69686,19)),"~blog/default/cloud-native-blog-tags-azure-logic-apps-233-list.json",69686],"63b6a597":[()=>a.e(17573).then(a.t.bind(a,12441,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-containers-ae5-list.json",12441],"63c787c2":[()=>a.e(12697).then(a.t.bind(a,83864,19)),"~blog/default/cloud-native-blog-tags-dapr-page-5-db0.json",83864],"647961e4":[()=>a.e(63439).then(a.t.bind(a,78064,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-azure-container-apps-page-2-da7-list.json",78064],"64930ae0":[()=>a.e(45508).then(a.t.bind(a,8276,19)),"~blog/default/cloud-native-blog-tags-serverless-september-page-13-6e6-list.json",8276],"64f93100":[()=>a.e(82623).then(a.t.bind(a,94924,19)),"~blog/default/cloud-native-blog-tags-dapr-page-2-4b3-list.json",94924],"65bd9c5f":[()=>a.e(62519).then(a.t.bind(a,36869,19)),"~blog/default/cloud-native-blog-tags-serverless-september-page-2-5a3-list.json",36869],"65cafd8e":[()=>a.e(50070).then(a.t.bind(a,91108,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-service-3e1-list.json",91108],"66301b34":[()=>a.e(84567).then(a.t.bind(a,77998,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-ask-the-expert-page-10-716.json",77998],"6633d22a":[()=>a.e(64835).then(a.t.bind(a,62478,19)),"~blog/default/cloud-native-blog-tags-microservices-d05.json",62478],"66c5ad31":[()=>a.e(23198).then(a.t.bind(a,37790,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-ask-the-expert-page-7-154-list.json",37790],"66ce2abc":[()=>a.e(58280).then(a.bind(a,60348)),"@site/blog/2022-09-09/index.md?truncated=true",60348],"66f5903d":[()=>a.e(97491).then(a.t.bind(a,36336,19)),"~blog/default/cloud-native-blog-page-31-850.json",36336],"677498e0":[()=>a.e(63746).then(a.t.bind(a,5464,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-community-buzz-page-9-a70-list.json",5464],"67d300de":[()=>a.e(91034).then(a.t.bind(a,76669,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-aks-page-2-f25.json",76669],"67e85bba":[()=>a.e(66857).then(a.t.bind(a,19360,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-github-codespaces-page-8-588.json",19360],"67f3d899":[()=>a.e(99521).then(a.t.bind(a,30421,19)),"~blog/default/cloud-native-blog-page-16-feb.json",30421],"67f51f7e":[()=>a.e(95903).then(a.bind(a,14370)),"@site/blog/2022-10-06/index.md?truncated=true",14370],"6875c492":[()=>Promise.all([a.e(40532),a.e(75287),a.e(21791),a.e(48610)]).then(a.bind(a,74883)),"@theme/BlogTagsPostsPage",74883],"695a0e95":[()=>a.e(90950).then(a.t.bind(a,9215,19)),"~blog/blog-cnny/cloud-native-cnny-2023-page-19-2cd.json",9215],"695b08bd":[()=>a.e(8410).then(a.t.bind(a,40145,19)),"~blog/default/cloud-native-blog-tags-javascript-f6c.json",40145],"69c441bd":[()=>a.e(23372).then(a.t.bind(a,12443,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-azure-dns-399-list.json",12443],"69e6ed04":[()=>a.e(22723).then(a.t.bind(a,52333,19)),"~blog/default/cloud-native-blog-tags-30-days-of-serverless-page-18-183-list.json",52333],"6a04bf88":[()=>a.e(5621).then(a.t.bind(a,2239,19)),"~blog/default/cloud-native-blog-tags-30-days-of-serverless-page-7-b12.json",2239],"6a20c0a4":[()=>a.e(5201).then(a.t.bind(a,88271,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-azure-cosmos-db-page-4-3bf.json",88271],"6a312c97":[()=>a.e(48919).then(a.t.bind(a,52877,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-persistent-storage-e6a-list.json",52877],"6a4ca75b":[()=>a.e(38780).then(a.bind(a,41328)),"@site/blog/2022-09-04/index.md",41328],"6a5b295a":[()=>a.e(29604).then(a.t.bind(a,28720,19)),"~blog/default/cloud-native-blog-tags-serverless-september-page-31-7d0.json",28720],"6a5e520d":[()=>a.e(75337).then(a.t.bind(a,59519,19)),"~blog/default/cloud-native-blog-tags-azure-container-apps-page-13-faf.json",59519],"6a6147d5":[()=>a.e(9832).then(a.t.bind(a,28421,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-ask-the-expert-page-4-e76.json",28421],"6abbc264":[()=>a.e(40835).then(a.t.bind(a,81701,19)),"~blog/default/cloud-native-blog-tags-serverless-september-page-19-8be.json",81701],"6ac0f798":[()=>a.e(54894).then(a.t.bind(a,66502,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-azure-kubernetes-service-page-13-b35-list.json",66502],"6ac3c29a":[()=>a.e(8939).then(a.t.bind(a,51939,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-azure-cosmos-db-page-6-176.json",51939],"6b04e7ad":[()=>a.e(83260).then(a.t.bind(a,571,19)),"~blog/default/cloud-native-blog-tags-serverless-september-page-16-09e-list.json",571],"6b35c7b1":[()=>a.e(1481).then(a.t.bind(a,98234,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-azure-container-apps-page-5-85c.json",98234],"6b9868e6":[()=>a.e(15518).then(a.t.bind(a,27382,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-30-days-of-ia-da6-list.json",27382],"6c2093fb":[()=>a.e(35377).then(a.t.bind(a,69666,19)),"~blog/default/cloud-native-blog-tags-zero-to-hero-page-4-1c1-list.json",69666],"6c2bf19a":[()=>a.e(48558).then(a.t.bind(a,53701,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-fall-for-ia-page-4-d65-list.json",53701],"6c32d8c3":[()=>a.e(60806).then(a.t.bind(a,82427,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-ask-the-expert-page-4-21a.json",82427],"6cffcc32":[()=>a.e(33280).then(a.t.bind(a,24364,19)),"~blog/default/cloud-native-blog-tags-serverless-september-page-15-f1d.json",24364],"6d43c7c4":[()=>a.e(77916).then(a.t.bind(a,98355,19)),"~blog/default/cloud-native-blog-tags-serverless-september-page-13-6e6.json",98355],"6d71a54c":[()=>a.e(46154).then(a.t.bind(a,9491,19)),"~blog/blog-cnny/cloud-native-cnny-2023-page-2-1a6.json",9491],"6daa3851":[()=>a.e(60874).then(a.t.bind(a,82105,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-github-actions-page-9-74c.json",82105],"6e09f910":[()=>a.e(62614).then(a.t.bind(a,53201,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-kubernetes-page-5-dcb.json",53201],"6e13655f":[()=>a.e(34934).then(a.t.bind(a,66961,19)),"~blog/default/cloud-native-blog-tags-dapr-page-13-ddd.json",66961],"6e3cf958":[()=>a.e(29327).then(a.t.bind(a,40575,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-azure-kubernetes-service-page-20-076.json",40575],"6e52823d":[()=>a.e(89828).then(a.t.bind(a,27853,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-azure-openai-page-3-1f5-list.json",27853],"6e8a7b67":[()=>a.e(64696).then(a.t.bind(a,5340,19)),"~blog/default/cloud-native-blog-tags-microservices-page-9-5cd-list.json",5340],"6ed72a5d":[()=>a.e(28503).then(a.bind(a,43083)),"@site/blog-30daysofIA/2023-09-21/how-digital-natives-leverage-generative-ai.md",43083],"6ef7e3d4":[()=>a.e(49958).then(a.t.bind(a,6082,19)),"~blog/default/cloud-native-blog-tags-dapr-c54-list.json",6082],"6f0c12c9":[()=>a.e(72235).then(a.t.bind(a,19518,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-microservices-c4b.json",19518],"6f14a4c7":[()=>a.e(48236).then(a.t.bind(a,10360,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-cloud-native-page-12-e4a-list.json",10360],"6f46b4a6":[()=>a.e(34872).then(a.t.bind(a,52636,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-community-buzz-page-4-0da-list.json",52636],"6f976579":[()=>a.e(17824).then(a.t.bind(a,93935,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-30-days-of-ia-page-8-e5e-list.json",93935],"6fa36db2":[()=>a.e(9885).then(a.t.bind(a,85746,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-azure-container-apps-page-2-da7.json",85746],70037571:[()=>a.e(8229).then(a.bind(a,40483)),"@site/blog-cnny/2023-02-07/index.md?truncated=true",40483],"700e33eb":[()=>a.e(31135).then(a.bind(a,30547)),"@site/blog/2022-09-28/index.md",30547],"708744f2":[()=>a.e(33733).then(a.bind(a,54420)),"@site/blog-cnny/2023-02-08/index.md?truncated=true",54420],"7097285a":[()=>a.e(12915).then(a.t.bind(a,49566,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-zero-to-hero-page-2-720.json",49566],"70978acc":[()=>a.e(37754).then(a.bind(a,46435)),"@site/blog/2022-09-07/index.md?truncated=true",46435],"70b24ff0":[()=>a.e(91518).then(a.t.bind(a,79944,19)),"~blog/default/cloud-native-blog-page-13-193.json",79944],"70b87c8a":[()=>a.e(40418).then(a.t.bind(a,9618,19)),"~blog/default/cloud-native-blog-tags-dapr-page-2-4b3.json",9618],"70e93a45":[()=>a.e(58497).then(a.t.bind(a,91060,19)),"~blog/default/cloud-native-blog-tags-dapr-page-16-0b8.json",91060],"70ec2d67":[()=>a.e(8366).then(a.t.bind(a,62873,19)),"~blog/default/cloud-native-blog-tags-dapr-page-10-d0d.json",62873],"714230e1":[()=>a.e(37984).then(a.t.bind(a,16731,19)),"~blog/default/cloud-native-blog-page-10-0a0.json",16731],"7166f236":[()=>a.e(98161).then(a.t.bind(a,54595,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-azure-container-apps-page-4-755.json",54595],"717ca7ad":[()=>a.e(56462).then(a.t.bind(a,6986,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-cloud-native-page-5-f54-list.json",6986],"728f6513":[()=>a.e(90442).then(a.t.bind(a,38663,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-aks-page-4-89b-list.json",38663],"72e38fbe":[()=>a.e(53470).then(a.bind(a,8414)),"@site/blog-cnny/2023-01-31/index.md?truncated=true",8414],"735396ab":[()=>a.e(7488).then(a.t.bind(a,31740,19)),"~blog/default/cloud-native-blog-tags-serverless-september-page-22-1d7-list.json",31740],"737c62d3":[()=>a.e(33472).then(a.t.bind(a,78337,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-learn-live-page-7-db8-list.json",78337],"739bc6b2":[()=>a.e(83501).then(a.t.bind(a,42083,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-cloud-native-page-2-21c-list.json",42083],"73f0aa6e":[()=>a.e(65571).then(a.t.bind(a,42474,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-ingress-fe8.json",42474],"742b38dc":[()=>a.e(64804).then(a.t.bind(a,21983,19)),"~blog/default/cloud-native-blog-tags-azure-functions-page-5-907.json",21983],"74a6c4d8":[()=>a.e(71898).then(a.t.bind(a,70032,19)),"~blog/default/cloud-native-blog-tags-serverless-september-page-11-62b.json",70032],"74bd70f4":[()=>a.e(26553).then(a.t.bind(a,53697,19)),"~blog/default/cloud-native-blog-tags-azure-functions-page-8-477-list.json",53697],"7520942f":[()=>a.e(12230).then(a.t.bind(a,14468,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-ask-the-expert-page-7-154.json",14468],"7527a9ef":[()=>a.e(82633).then(a.t.bind(a,33500,19)),"~blog/default/cloud-native-blog-tags-microservices-page-8-61f.json",33500],"75acfd48":[()=>a.e(64891).then(a.t.bind(a,94871,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-learn-live-page-8-06b-list.json",94871],"75d4719a":[()=>a.e(18034).then(a.t.bind(a,21365,19)),"~blog/default/cloud-native-blog-tags-students-21a.json",21365],"7628c73f":[()=>a.e(21683).then(a.t.bind(a,29105,19)),"~blog/default/cloud-native-blog-tags-cloud-native-c26-list.json",29105],"763e49fc":[()=>a.e(76894).then(a.t.bind(a,72338,19)),"~blog/default/cloud-native-blog-tags-autoscaling-377.json",72338],"765bde49":[()=>a.e(99285).then(a.t.bind(a,61264,19)),"~blog/default/cloud-native-blog-tags-dapr-page-4-60f.json",61264],"76723d32":[()=>a.e(30753).then(a.t.bind(a,83685,19)),"~blog/default/cloud-native-blog-tags-azure-functions-page-3-9c6-list.json",83685],"76df9d58":[()=>a.e(68927).then(a.t.bind(a,32137,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-kubernetes-page-2-536-list.json",32137],"77053eb1":[()=>a.e(88564).then(a.t.bind(a,96974,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-aks-page-5-c56-list.json",96974],"7709179f":[()=>a.e(29162).then(a.t.bind(a,27091,19)),"~blog/default/cloud-native-blog-tags-asp-net-ae9-list.json",27091],"782e7d27":[()=>a.e(94409).then(a.t.bind(a,97711,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-azure-openai-page-3-1f5.json",97711],"78f7c451":[()=>a.e(38945).then(a.t.bind(a,11179,19)),"~blog/default/cloud-native-blog-tags-cloudevents-44d.json",11179],"79170c02":[()=>a.e(84316).then(a.t.bind(a,54200,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-learn-live-page-3-ca6-list.json",54200],"79b2265b":[()=>a.e(53486).then(a.t.bind(a,7864,19)),"~blog/default/cloud-native-blog-tags-30-days-of-serverless-d1d.json",7864],"79b64cea":[()=>a.e(54202).then(a.t.bind(a,36905,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-persistent-volume-claims-a2e.json",36905],"79ca3466":[()=>a.e(684).then(a.t.bind(a,27656,19)),"~blog/default/cloud-native-blog-tags-serverless-september-page-15-f1d-list.json",27656],"7a79be67":[()=>a.e(2803).then(a.t.bind(a,82249,19)),"~blog/default/cloud-native-blog-tags-azure-container-apps-page-12-82f-list.json",82249],"7b07dcad":[()=>a.e(89534).then(a.t.bind(a,40619,19)),"~blog/default/cloud-native-blog-tags-serverless-september-page-17-1ef-list.json",40619],"7b7d445f":[()=>a.e(17658).then(a.t.bind(a,7479,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-azure-openai-page-8-15b.json",7479],"7b92706b":[()=>a.e(7698).then(a.t.bind(a,59090,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-30-daysofcloudnative-page-8-f87-list.json",59090],"7bd0b9bf":[()=>a.e(48157).then(a.t.bind(a,84234,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-fall-for-ia-page-6-bb4.json",84234],"7c5cb72e":[()=>a.e(88646).then(a.t.bind(a,42880,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-cloud-native-page-3-73e.json",42880],"7c838aa5":[()=>a.e(207).then(a.t.bind(a,38382,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-azure-container-apps-page-8-446-list.json",38382],"7ca86db6":[()=>a.e(81296).then(a.t.bind(a,57171,19)),"~blog/default/cloud-native-blog-tags-azure-event-grid-page-3-be8-list.json",57171],"7cae6c3b":[()=>a.e(95904).then(a.t.bind(a,76582,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-azure-kubernetes-service-page-7-eb3-list.json",76582],"7ce70624":[()=>a.e(76336).then(a.t.bind(a,18591,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-zero-to-hero-page-15-0b5-list.json",18591],"7d1b9d2c":[()=>a.e(31972).then(a.bind(a,21654)),"@site/blog-cnny/2023-02-09/index.md?truncated=true",21654],"7d4a7cb7":[()=>a.e(53346).then(a.t.bind(a,69812,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-github-actions-page-3-55f.json",69812],"7d93b36b":[()=>a.e(76785).then(a.bind(a,2032)),"@site/blog/zero-to-hero/2022-09-19-containerapps.md",2032],"7dba6303":[()=>a.e(50321).then(a.t.bind(a,27069,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-30-days-of-ia-page-7-d25-list.json",27069],"7e3c7a3f":[()=>a.e(19253).then(a.t.bind(a,63273,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-azure-kubernetes-service-page-8-a29.json",63273],"7e7aedec":[()=>a.e(73945).then(a.t.bind(a,43770,19)),"~blog/default/cloud-native-blog-page-8-dbb.json",43770],"7f177be4":[()=>a.e(26314).then(a.t.bind(a,13194,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-ask-the-expert-page-2-101.json",13194],"7fd555e2":[()=>a.e(91589).then(a.t.bind(a,58518,19)),"~blog/default/cloud-native-blog-tags-serverless-september-page-10-ccc.json",58518],"8025f7fd":[()=>Promise.all([a.e(40532),a.e(86697)]).then(a.bind(a,5373)),"@site/src/pages/Fall-For-IA/index.js",5373],"807349ce":[()=>a.e(38232).then(a.t.bind(a,36484,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-github-copilot-page-7-b78.json",36484],"8085909f":[()=>a.e(4237).then(a.bind(a,58548)),"@site/blog/zero-to-hero/2022-09-26-azurefunctions.md",58548],"808a5912":[()=>a.e(33647).then(a.t.bind(a,49375,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-ask-the-expert-page-12-e20-list.json",49375],"808b9749":[()=>a.e(20116).then(a.t.bind(a,18460,19)),"~blog/default/cloud-native-blog-tags-serverless-hacks-b39.json",18460],"808beaf0":[()=>a.e(61209).then(a.t.bind(a,51352,19)),"~blog/default/cloud-native-blog-tags-azure-container-apps-page-17-f0e.json",51352],"80aaee8e":[()=>a.e(71363).then(a.t.bind(a,96843,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-azure-kubernetes-service-page-7-c57-list.json",96843],"8136a61a":[()=>a.e(76611).then(a.t.bind(a,67024,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-30-daysofcloudnative-5f9.json",67024],"814f3328":[()=>a.e(52535).then(a.t.bind(a,60425,19)),"~blog/default/blog-post-list-prop-default.json",60425],81540514:[()=>a.e(80035).then(a.t.bind(a,29570,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-kubernetes-page-4-bc2-list.json",29570],"8178af10":[()=>a.e(70727).then(a.t.bind(a,95350,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-cloud-native-page-16-ffb.json",95350],"81ce6d13":[()=>a.e(20454).then(a.t.bind(a,97904,19)),"~blog/default/cloud-native-blog-tags-30-days-of-serverless-page-12-299-list.json",97904],"81eaba73":[()=>a.e(77034).then(a.t.bind(a,84905,19)),"~blog/default/cloud-native-blog-tags-30-days-of-serverless-page-9-3da.json",84905],"827b607c":[()=>a.e(83592).then(a.t.bind(a,78493,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-kubernetes-page-3-190.json",78493],"82d2e731":[()=>a.e(47547).then(a.t.bind(a,12197,19)),"~blog/default/cloud-native-blog-tags-dapr-page-13-ddd-list.json",12197],"82d96bf5":[()=>a.e(43333).then(a.bind(a,88995)),"@site/blog/2022-09-05/index.md",88995],"8355b08c":[()=>a.e(331).then(a.bind(a,94769)),"@site/blog/zero-to-hero/2022-09-26-azurefunctions.md?truncated=true",94769],"8362f252":[()=>a.e(32767).then(a.t.bind(a,68479,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-hack-together-page-7-fec-list.json",68479],"83bddd4b":[()=>a.e(18656).then(a.t.bind(a,30874,19)),"~blog/default/cloud-native-blog-tags-microservices-page-9-5cd.json",30874],"83f3468a":[()=>a.e(81814).then(a.t.bind(a,13494,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-azure-kubernetes-service-page-11-7b8-list.json",13494],"83f4d82c":[()=>a.e(71515).then(a.t.bind(a,56267,19)),"~blog/default/cloud-native-blog-tags-serverless-hacks-b39-list.json",56267],"83f9535a":[()=>a.e(15602).then(a.t.bind(a,5700,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-ask-the-expert-page-3-867.json",5700],"840d1cce":[()=>a.e(7617).then(a.t.bind(a,47845,19)),"~blog/default/cloud-native-blog-tags-azure-logic-apps-page-2-7a3.json",47845],"8412e158":[()=>a.e(74801).then(a.t.bind(a,25848,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-azure-functions-page-8-a98.json",25848],"8445e33e":[()=>a.e(5482).then(a.t.bind(a,27203,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-azure-functions-page-2-a61.json",27203],"844f46a5":[()=>a.e(48).then(a.t.bind(a,4692,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-cloud-native-page-11-6bc-list.json",4692],84806527:[()=>a.e(99125).then(a.t.bind(a,95397,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-azure-openai-page-5-145-list.json",95397],"84d9913c":[()=>a.e(94608).then(a.t.bind(a,44641,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-azure-openai-page-5-145.json",44641],"84e7f16f":[()=>a.e(31248).then(a.t.bind(a,14109,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-azure-openai-page-6-6f1-list.json",14109],"85298cc0":[()=>a.e(66434).then(a.bind(a,4698)),"@site/blog-30daysofIA/2023-09-20/reimagine-app-development-with-ai.md",4698],"854ddf44":[()=>a.e(11967).then(a.t.bind(a,41467,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-page-4-220.json",41467],"85550d99":[()=>a.e(33882).then(a.t.bind(a,45629,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-azure-kubernetes-service-page-5-93c.json",45629],"855c4aff":[()=>a.e(82204).then(a.t.bind(a,24469,19)),"/home/runner/work/Cloud-Native/Cloud-Native/website/.docusaurus/docusaurus-plugin-content-blog/default/plugin-route-context-module-100.json",24469],"85cba9e9":[()=>a.e(17745).then(a.t.bind(a,46685,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-hack-together-page-5-7fa.json",46685],"85ebb381":[()=>a.e(25481).then(a.t.bind(a,82929,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-workload-identity-8c2.json",82929],"86a7690c":[()=>a.e(60952).then(a.t.bind(a,87752,19)),"~blog/default/cloud-native-blog-tags-azure-functions-page-4-555-list.json",87752],"86b9f332":[()=>a.e(63981).then(a.t.bind(a,99948,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-cloud-native-new-year-page-2-cd2-list.json",99948],"86d446e2":[()=>a.e(19205).then(a.t.bind(a,93414,19)),"~blog/default/cloud-native-blog-tags-azure-container-apps-page-15-f41-list.json",93414],"87bbf9f8":[()=>a.e(7615).then(a.t.bind(a,85472,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-community-buzz-068.json",85472],"88357ceb":[()=>a.e(58467).then(a.t.bind(a,36756,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-learn-live-page-9-55c-list.json",36756],88665212:[()=>a.e(47241).then(a.t.bind(a,45338,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-github-codespaces-page-5-922-list.json",45338],"8866a401":[()=>a.e(53698).then(a.bind(a,78008)),"@site/blog-cnny/2023-02-15/index.md",78008],"88d99e0f":[()=>a.e(79994).then(a.bind(a,89095)),"@site/blog-cnny/2023-02-01/index.md?truncated=true",89095],"89197f4f":[()=>a.e(67493).then(a.bind(a,50391)),"@site/blog/2022-09-14/index.md?truncated=true",50391],"893f2e93":[()=>a.e(55411).then(a.bind(a,14988)),"@site/blog-cnny/2023-01-24/30days.md?truncated=true",14988],"89439f6f":[()=>a.e(65753).then(a.t.bind(a,26571,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-microservices-c4b-list.json",26571],"8955acc6":[()=>a.e(81512).then(a.t.bind(a,59462,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-azure-kubernetes-service-page-3-220.json",59462],"8974c763":[()=>a.e(37948).then(a.t.bind(a,41447,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-30-daysofcloudnative-page-9-377.json",41447],"898c55cb":[()=>a.e(52989).then(a.bind(a,7902)),"@site/blog/2022-09-20/index.md",7902],"8a1f6266":[()=>a.e(30495).then(a.t.bind(a,21321,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-zero-to-hero-page-13-48d.json",21321],"8a2e5722":[()=>a.e(16189).then(a.t.bind(a,18135,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-ask-the-expert-251.json",18135],"8a766219":[()=>a.e(78345).then(a.bind(a,31202)),"@site/blog-30daysofIA/2023-09-18/demystifying-intelligent-applications.md?truncated=true",31202],"8b115eee":[()=>a.e(41990).then(a.t.bind(a,92918,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-github-codespaces-page-5-922.json",92918],"8b5714b2":[()=>a.e(69432).then(a.t.bind(a,18596,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-cloud-native-page-12-e4a.json",18596],"8b5ab4ba":[()=>a.e(25767).then(a.t.bind(a,73206,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-community-buzz-page-6-d92-list.json",73206],"8ba97af6":[()=>a.e(10387).then(a.t.bind(a,18509,19)),"~blog/blog-cnny/cloud-native-cnny-2023-page-5-04b.json",18509],"8baf15a9":[()=>a.e(51828).then(a.t.bind(a,20441,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-fall-for-ia-3be-list.json",20441],"8bc7054e":[()=>a.e(66672).then(a.bind(a,79284)),"@site/src/pages/serverless-september/AskTheExpert.md",79284],"8bdb3070":[()=>a.e(34206).then(a.t.bind(a,35413,19)),"~blog/blog-cnny/cloud-native-cnny-2023-page-10-6fb.json",35413],"8c3d2b1d":[()=>a.e(36277).then(a.t.bind(a,85532,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-github-copilot-page-5-534.json",85532],"8c56eedf":[()=>a.e(12829).then(a.t.bind(a,41903,19)),"~blog/default/cloud-native-blog-tags-serverless-september-page-26-5d6.json",41903],"8c6b1b70":[()=>a.e(38600).then(a.t.bind(a,87533,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-ask-the-expert-page-16-795-list.json",87533],"8c7d23e7":[()=>a.e(40270).then(a.t.bind(a,28624,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-cloud-native-page-8-39e.json",28624],"8c99d685":[()=>a.e(63776).then(a.bind(a,82878)),"@site/blog-cnny/2023-01-31/index.md",82878],"8ca49f56":[()=>a.e(71861).then(a.t.bind(a,60295,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-github-actions-page-6-be7-list.json",60295],"8cacefc1":[()=>a.e(37460).then(a.t.bind(a,91998,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-azure-openai-32e.json",91998],"8d1ef8e7":[()=>a.e(96655).then(a.bind(a,15559)),"@site/blog-cnny/2023-02-07/index.md",15559],"8dbb57bc":[()=>a.e(47246).then(a.t.bind(a,79e3,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-persistent-volume-claims-a2e-list.json",79e3],"8e5814b3":[()=>a.e(16590).then(a.t.bind(a,23243,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-persistent-storage-e6a.json",23243],"8e80c8e1":[()=>a.e(2848).then(a.t.bind(a,95391,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-github-actions-page-6-be7.json",95391],"8e84163f":[()=>a.e(98663).then(a.t.bind(a,2248,19)),"~blog/default/cloud-native-blog-tags-dapr-page-7-dd8-list.json",2248],"8f4add25":[()=>a.e(65251).then(a.t.bind(a,10165,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-github-codespaces-page-2-0e1.json",10165],"9007293a":[()=>a.e(3453).then(a.t.bind(a,9159,19)),"~blog/default/cloud-native-blog-tags-azd-273.json",9159],90806480:[()=>a.e(94399).then(a.t.bind(a,41554,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-azure-kubernetes-service-page-17-8dd-list.json",41554],"90b498df":[()=>a.e(15107).then(a.bind(a,79984)),"@site/blog/zero-to-hero/2022-09-06-containerapps.md",79984],"9147217a":[()=>a.e(33568).then(a.t.bind(a,87735,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-cloud-native-page-9-c09.json",87735],"915f7087":[()=>a.e(25743).then(a.bind(a,89626)),"@site/blog/zero-to-hero/2022-09-19-azurefunctions.md",89626],"9241169f":[()=>a.e(48494).then(a.t.bind(a,20689,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-azure-kubernetes-service-page-3-220-list.json",20689],"9245a8c6":[()=>a.e(27226).then(a.bind(a,86266)),"@site/blog/2022-09-28/index.md?truncated=true",86266],"92851fbb":[()=>a.e(40869).then(a.t.bind(a,928,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-azure-cosmos-db-page-2-e86-list.json",928],"93465d98":[()=>a.e(70199).then(a.t.bind(a,28779,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-azure-cosmos-db-page-3-9a8.json",28779],"934b3b5c":[()=>a.e(20006).then(a.t.bind(a,20105,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-zero-to-hero-page-5-b70-list.json",20105],"935f2afb":[()=>a.e(80053).then(a.t.bind(a,1109,19)),"~docs/default/version-current-metadata-prop-751.json",1109],"937ccad8":[()=>a.e(48758).then(a.t.bind(a,73363,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-azure-functions-243-list.json",73363],"93b8c5e1":[()=>a.e(50572).then(a.t.bind(a,57438,19)),"~blog/default/cloud-native-blog-tags-30-days-of-serverless-page-10-c1f.json",57438],"940c9439":[()=>a.e(45711).then(a.t.bind(a,44979,19)),"~blog/default/cloud-native-blog-tags-serverless-e-2-e-d39-list.json",44979],"9444fc8d":[()=>a.e(47857).then(a.t.bind(a,29037,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-azure-kubernetes-service-page-9-5d8-list.json",29037],"94607c5f":[()=>a.e(38781).then(a.t.bind(a,8132,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-azure-key-vault-page-2-601-list.json",8132],"94c8d0a4":[()=>a.e(14094).then(a.t.bind(a,92145,19)),"~blog/default/cloud-native-blog-tags-azure-container-apps-page-9-052.json",92145],"94ced535":[()=>a.e(17420).then(a.t.bind(a,44084,19)),"~blog/default/cloud-native-blog-page-22-49c.json",44084],"94d4ac07":[()=>a.e(64336).then(a.t.bind(a,92203,19)),"~blog/default/cloud-native-blog-tags-dapr-page-5-db0-list.json",92203],"94fd4cc3":[()=>a.e(5627).then(a.t.bind(a,65302,19)),"~blog/default/cloud-native-blog-page-20-c97.json",65302],"9580127c":[()=>a.e(7706).then(a.t.bind(a,64265,19)),"~blog/default/cloud-native-blog-tags-azure-event-grid-page-3-be8.json",64265],"962e1587":[()=>a.e(86766).then(a.t.bind(a,97026,19)),"~blog/default/cloud-native-blog-tags-openapi-877-list.json",97026],"96d6a951":[()=>a.e(11097).then(a.t.bind(a,20978,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-learn-live-page-4-863.json",20978],"9750cd01":[()=>a.e(58738).then(a.t.bind(a,96047,19)),"~blog/default/cloud-native-blog-tags-30-days-of-serverless-page-2-71d.json",96047],"9762ef78":[()=>a.e(4895).then(a.t.bind(a,73115,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-30-days-of-ia-page-6-b3c-list.json",73115],"97a5ae26":[()=>a.e(79695).then(a.t.bind(a,74867,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-azure-kubernetes-service-page-15-695.json",74867],"97a96520":[()=>a.e(7643).then(a.t.bind(a,87568,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-ask-the-expert-page-6-b19.json",87568],"97ba9f7a":[()=>a.e(31609).then(a.t.bind(a,61269,19)),"~blog/default/cloud-native-blog-page-26-ffc.json",61269],"97c179be":[()=>a.e(76299).then(a.t.bind(a,31071,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-github-copilot-9d5.json",31071],"97e02956":[()=>a.e(98746).then(a.t.bind(a,47082,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-github-codespaces-page-9-dd9-list.json",47082],"98735e69":[()=>a.e(67622).then(a.t.bind(a,28567,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-windows-ba3-list.json",28567],"98a79a26":[()=>a.e(89799).then(a.t.bind(a,68347,19)),"~blog/default/cloud-native-blog-tags-dapr-page-9-29e.json",68347],"98acceed":[()=>a.e(6038).then(a.t.bind(a,4546,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-30-daysofcloudnative-page-10-bae-list.json",4546],"996a3652":[()=>a.e(68652).then(a.t.bind(a,72150,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-cloud-native-page-8-39e-list.json",72150],"99a61e74":[()=>a.e(77946).then(a.t.bind(a,28210,19)),"~blog/default/cloud-native-blog-tags-devtools-308.json",28210],"99a72a3c":[()=>a.e(81194).then(a.t.bind(a,41096,19)),"~blog/default/cloud-native-blog-tags-azure-developer-cli-page-2-005.json",41096],"99d1ecb8":[()=>a.e(39714).then(a.t.bind(a,12057,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-zero-to-hero-page-9-ebc.json",12057],"9a3e0d8e":[()=>a.e(63749).then(a.t.bind(a,27015,19)),"~blog/default/cloud-native-blog-page-7-604.json",27015],"9a8df0df":[()=>a.e(73012).then(a.t.bind(a,24961,19)),"~blog/default/cloud-native-blog-tags-30-days-of-serverless-page-2-71d-list.json",24961],"9aaaf4b8":[()=>a.e(48625).then(a.t.bind(a,3933,19)),"~blog/default/cloud-native-blog-tags-30-days-of-serverless-page-11-4c9.json",3933],"9ac18e3c":[()=>a.e(4644).then(a.t.bind(a,76660,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-containers-ae5.json",76660],"9aee8dd5":[()=>a.e(43974).then(a.t.bind(a,81911,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-hack-together-page-4-8f5-list.json",81911],"9af977f2":[()=>a.e(34967).then(a.t.bind(a,56467,19)),"~blog/default/cloud-native-blog-tags-microservices-page-8-61f-list.json",56467],"9b60d2ea":[()=>a.e(95615).then(a.t.bind(a,44551,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-azure-functions-page-7-066-list.json",44551],"9bc306e8":[()=>a.e(90031).then(a.t.bind(a,16105,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-github-copilot-page-6-73f.json",16105],"9c17a54a":[()=>a.e(44348).then(a.t.bind(a,72557,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-azure-functions-page-3-e0b.json",72557],"9c3672b5":[()=>a.e(11357).then(a.t.bind(a,53677,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-azure-kubernetes-service-page-13-b35.json",53677],"9cdc6aec":[()=>a.e(95252).then(a.t.bind(a,76681,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-page-5-e10.json",76681],"9d6ed2de":[()=>a.e(29052).then(a.t.bind(a,56218,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-azure-kubernetes-service-page-9-9bd-list.json",56218],"9d765af9":[()=>a.e(21401).then(a.t.bind(a,38324,19)),"~blog/default/cloud-native-blog-tags-asp-net-ae9.json",38324],"9dbc57f1":[()=>a.e(19699).then(a.bind(a,15772)),"@site/blog/2022-09-14/index.md",15772],"9ddf9492":[()=>a.e(78132).then(a.t.bind(a,97550,19)),"~blog/default/cloud-native-blog-page-18-e55.json",97550],"9de9dc34":[()=>a.e(43148).then(a.t.bind(a,85123,19)),"~blog/default/cloud-native-blog-tags-30-days-of-serverless-page-13-e82.json",85123],"9e2f083a":[()=>a.e(18438).then(a.t.bind(a,57966,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-hack-together-d8e.json",57966],"9e4087bc":[()=>a.e(53608).then(a.bind(a,28151)),"@theme/BlogArchivePage",28151],"9f0c8c51":[()=>a.e(91214).then(a.t.bind(a,39442,19)),"~blog/default/cloud-native-blog-tags-autoscaling-377-list.json",39442],"9f14d4e5":[()=>a.e(84654).then(a.t.bind(a,94633,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-30-days-of-ia-page-2-c12.json",94633],"9f789b70":[()=>a.e(41566).then(a.t.bind(a,8153,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-ask-the-expert-page-15-b7b-list.json",8153],"9fbb892a":[()=>a.e(60981).then(a.t.bind(a,35763,19)),"~blog/default/cloud-native-blog-tags-dapr-page-9-29e-list.json",35763],"9fec92ba":[()=>a.e(36703).then(a.t.bind(a,33257,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-azure-container-apps-page-5-85c-list.json",33257],a00df5b4:[()=>a.e(66686).then(a.t.bind(a,91516,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-30-daysofcloudnative-page-12-b68.json",91516],a0a724b8:[()=>a.e(37729).then(a.t.bind(a,99326,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-azure-functions-page-9-007.json",99326],a11db7eb:[()=>a.e(91367).then(a.t.bind(a,31226,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-github-codespaces-b37.json",31226],a123ff76:[()=>a.e(65893).then(a.t.bind(a,20324,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-azure-kubernetes-service-page-10-71f.json",20324],a24481e9:[()=>a.e(52569).then(a.t.bind(a,73909,19)),"~blog/default/cloud-native-blog-tags-azure-functions-page-3-9c6.json",73909],a285ff6f:[()=>a.e(27028).then(a.t.bind(a,67359,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-30-daysofcloudnative-page-10-bae.json",67359],a2e6ced6:[()=>a.e(78897).then(a.t.bind(a,88826,19)),"~blog/default/cloud-native-blog-tags-event-hubs-da7.json",88826],a344ef93:[()=>a.e(63070).then(a.t.bind(a,56147,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-learn-live-page-9-55c.json",56147],a361f0db:[()=>a.e(33209).then(a.t.bind(a,5904,19)),"~blog/default/cloud-native-blog-tags-serverless-september-page-3-788-list.json",5904],a3def401:[()=>a.e(2453).then(a.t.bind(a,45805,19)),"~blog/default/cloud-native-blog-tags-devtools-page-2-bb3.json",45805],a404e9a0:[()=>a.e(61520).then(a.t.bind(a,89210,19)),"~blog/default/cloud-native-blog-tags-microsoft-graph-dba-list.json",89210],a40c8e08:[()=>a.e(33451).then(a.t.bind(a,74701,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-azure-functions-page-9-007-list.json",74701],a42917dd:[()=>a.e(42267).then(a.t.bind(a,41196,19)),"~blog/blog-cnny/cloud-native-cnny-2023-archive-0fc.json",41196],a48a9539:[()=>a.e(9054).then(a.t.bind(a,99928,19)),"~blog/default/cloud-native-blog-tags-serverless-september-page-30-abd.json",99928],a49e650c:[()=>a.e(48850).then(a.t.bind(a,69446,19)),"~blog/default/cloud-native-blog-tags-30-days-of-serverless-page-9-3da-list.json",69446],a4a37188:[()=>a.e(94919).then(a.t.bind(a,96746,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-configmaps-2b7.json",96746],a4a649e5:[()=>a.e(94445).then(a.t.bind(a,42355,19)),"~blog/default/cloud-native-blog-page-11-e13.json",42355],a4bab584:[()=>a.e(77856).then(a.t.bind(a,41121,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-hack-together-page-9-f7e-list.json",41121],a506a500:[()=>a.e(21987).then(a.t.bind(a,1645,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-community-buzz-page-6-d92.json",1645],a534381b:[()=>a.e(91117).then(a.t.bind(a,40954,19)),"~blog/default/cloud-native-blog-tags-30-days-of-serverless-page-17-3f5.json",40954],a5de73d8:[()=>a.e(15762).then(a.bind(a,79710)),"@site/blog/zero-to-hero/2022-09-06-azurefunctions.md",79710],a60bbbfe:[()=>a.e(79367).then(a.t.bind(a,82160,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-github-copilot-9d5-list.json",82160],a6400791:[()=>a.e(38336).then(a.bind(a,63107)),"@site/src/pages/Fall-For-IA/CloudSkills.md",63107],a648e1ec:[()=>a.e(30335).then(a.t.bind(a,23911,19)),"~blog/default/cloud-native-blog-tags-microservices-page-11-7c3.json",23911],a65e9479:[()=>a.e(55800).then(a.bind(a,2620)),"@site/blog-cnny/2023-01-25/30days.md",2620],a6767219:[()=>a.e(28199).then(a.bind(a,96330)),"@site/blog/2022-09-13/index.md?truncated=true",96330],a68ee39b:[()=>a.e(79005).then(a.t.bind(a,29435,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-aks-e9c.json",29435],a6aa9e1f:[()=>Promise.all([a.e(40532),a.e(75287),a.e(21791),a.e(93089)]).then(a.bind(a,9928)),"@theme/BlogListPage",9928],a6ad4213:[()=>a.e(43622).then(a.t.bind(a,26296,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-30-days-of-ia-page-5-831.json",26296],a6dcb37f:[()=>a.e(52238).then(a.t.bind(a,68408,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-30-daysofcloudnative-page-5-93f-list.json",68408],a787e11b:[()=>a.e(79258).then(a.t.bind(a,27711,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-github-copilot-page-8-701-list.json",27711],a843e963:[()=>a.e(33588).then(a.t.bind(a,1103,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-github-codespaces-page-6-a9c-list.json",1103],a875518b:[()=>a.e(90628).then(a.t.bind(a,76546,19)),"~blog/default/cloud-native-blog-tags-serverless-bba-list.json",76546],a89c2360:[()=>a.e(56407).then(a.t.bind(a,9822,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-azure-kubernetes-service-page-6-b0f.json",9822],a8dbe756:[()=>a.e(88350).then(a.t.bind(a,45051,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-azure-container-apps-page-4-755-list.json",45051],a8ee6229:[()=>a.e(67700).then(a.t.bind(a,66903,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-cloud-native-page-6-187-list.json",66903],a940942f:[()=>a.e(32627).then(a.t.bind(a,31306,19)),"~blog/default/cloud-native-blog-tags-logic-apps-c49-list.json",31306],a95a7c55:[()=>a.e(48036).then(a.t.bind(a,6132,19)),"~blog/default/cloud-native-blog-tags-azure-container-apps-page-18-295-list.json",6132],a9bdffda:[()=>a.e(60933).then(a.t.bind(a,6201,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-windows-ba3.json",6201],a9e32c6a:[()=>a.e(79823).then(a.t.bind(a,18639,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-cloud-native-page-14-ee3-list.json",18639],a9e85955:[()=>a.e(32197).then(a.t.bind(a,3861,19)),"~blog/default/cloud-native-blog-tags-azure-event-grid-page-2-c6c.json",3861],a9ff5d75:[()=>a.e(12402).then(a.t.bind(a,3972,19)),"~blog/default/cloud-native-blog-tags-azure-container-apps-page-16-3ed-list.json",3972],aa72c38b:[()=>a.e(66444).then(a.t.bind(a,94407,19)),"~blog/blog-cnny/cloud-native-cnny-2023-page-3-5b4.json",94407],aa826c81:[()=>a.e(72612).then(a.t.bind(a,50799,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-30-daysofcloudnative-page-13-324.json",50799],aa8ea87d:[()=>a.e(39326).then(a.t.bind(a,64943,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-learn-live-page-3-ca6.json",64943],aaf8be7c:[()=>a.e(62497).then(a.t.bind(a,48189,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-zero-to-hero-page-16-f9c.json",48189],ab0051c0:[()=>a.e(70266).then(a.t.bind(a,83202,19)),"~blog/default/cloud-native-blog-tags-azure-container-apps-page-11-2ad-list.json",83202],ab87274c:[()=>a.e(45121).then(a.t.bind(a,28060,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-cloud-native-page-16-ffb-list.json",28060],abf26052:[()=>a.e(80570).then(a.t.bind(a,72527,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-community-buzz-page-9-a70.json",72527],abf597e2:[()=>a.e(81551).then(a.t.bind(a,87391,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-zero-to-hero-63d-list.json",87391],ac07c4a5:[()=>a.e(5452).then(a.bind(a,40628)),"@site/blog-30daysofIA/2023-09-18/demystifying-intelligent-applications.md",40628],ac0e80dd:[()=>a.e(90479).then(a.t.bind(a,8471,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-learn-live-577.json",8471],ac2c6f29:[()=>a.e(32708).then(a.t.bind(a,58596,19)),"~blog/default/cloud-native-blog-tags-microservices-page-5-896.json",58596],ac6b5ff3:[()=>a.e(26952).then(a.t.bind(a,28520,19)),"~blog/default/cloud-native-blog-tags-serverless-september-page-10-ccc-list.json",28520],ac8cc8fe:[()=>a.e(58288).then(a.t.bind(a,18485,19)),"~blog/default/cloud-native-blog-tags-serverless-september-page-16-09e.json",18485],acfc78c4:[()=>a.e(33090).then(a.t.bind(a,90034,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-azure-functions-page-5-dd8.json",90034],ad46602f:[()=>a.e(44116).then(a.bind(a,15583)),"@site/blog-cnny/2023-01-26/30days.md",15583],adc56754:[()=>a.e(74792).then(a.t.bind(a,12469,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-github-actions-page-8-627-list.json",12469],ae14fa1f:[()=>a.e(75678).then(a.bind(a,90605)),"@site/blog/2022-09-01/index.md?truncated=true",90605],ae3f1154:[()=>a.e(18677).then(a.t.bind(a,48034,19)),"~blog/default/cloud-native-blog-tags-30-days-of-serverless-page-20-bc2.json",48034],ae4a8bfb:[()=>a.e(78484).then(a.t.bind(a,85755,19)),"~blog/default/cloud-native-blog-tags-serverless-september-page-32-216.json",85755],ae8acb83:[()=>a.e(34749).then(a.t.bind(a,45343,19)),"~blog/default/cloud-native-blog-tags-dapr-page-3-52a.json",45343],aeb62146:[()=>a.e(50730).then(a.t.bind(a,98298,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-azure-kubernetes-service-page-9-9bd.json",98298],af753b33:[()=>a.e(78564).then(a.t.bind(a,76355,19)),"~blog/default/cloud-native-blog-tags-serverless-september-page-29-4db.json",76355],af82476a:[()=>a.e(93954).then(a.bind(a,14417)),"@site/blog-cnny/2023-01-30/PodsAndDeployments.md?truncated=true",14417],afc3e988:[()=>a.e(66975).then(a.t.bind(a,80842,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-azure-cosmos-db-842.json",80842],b004fb50:[()=>a.e(58428).then(a.t.bind(a,79860,19)),"~blog/default/cloud-native-blog-tags-azure-event-grid-823.json",79860],b04df543:[()=>a.e(81183).then(a.t.bind(a,88927,19)),"~blog/default/cloud-native-blog-tags-serverless-september-page-26-5d6-list.json",88927],b09e51a7:[()=>a.e(25847).then(a.t.bind(a,60539,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-secure-supply-chain-07b.json",60539],b1059194:[()=>a.e(71940).then(a.t.bind(a,42923,19)),"~blog/default/cloud-native-blog-page-29-717.json",42923],b144e829:[()=>a.e(7623).then(a.t.bind(a,26647,19)),"~blog/blog-cnny/cloud-native-cnny-2023-page-4-cba.json",26647],b1509bad:[()=>a.e(79476).then(a.t.bind(a,88644,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-configmaps-2b7-list.json",88644],b1a57682:[()=>a.e(48155).then(a.bind(a,97684)),"@site/docs/resources/intro.md",97684],b1cd5b20:[()=>a.e(82462).then(a.t.bind(a,48279,19)),"~blog/blog-cnny/cloud-native-cnny-2023-page-18-e6e.json",48279],b1db9e78:[()=>a.e(66668).then(a.t.bind(a,28435,19)),"~blog/default/cloud-native-blog-tags-dapr-page-11-511-list.json",28435],b2c16f4e:[()=>a.e(15987).then(a.t.bind(a,27026,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-azure-kubernetes-service-page-9-5d8.json",27026],b31f0c62:[()=>a.e(2856).then(a.t.bind(a,34624,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-30-daysofcloudnative-page-2-88a-list.json",34624],b34c50e5:[()=>a.e(25794).then(a.t.bind(a,95852,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-azure-functions-243.json",95852],b34f0f06:[()=>a.e(24815).then(a.t.bind(a,476,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-learn-live-page-8-06b.json",476],b3d197ad:[()=>a.e(40976).then(a.t.bind(a,63622,19)),"~blog/default/cloud-native-blog-tags-serverless-september-page-21-700-list.json",63622],b40db2ab:[()=>a.e(13015).then(a.t.bind(a,53479,19)),"~blog/default/cloud-native-blog-tags-keda-49d-list.json",53479],b425f106:[()=>a.e(70177).then(a.bind(a,58656)),"@site/blog-cnny/2023-02-03/scaling.md",58656],b430dbd6:[()=>a.e(5044).then(a.t.bind(a,17123,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-azure-kubernetes-service-page-4-dac-list.json",17123],b44a2473:[()=>a.e(57832).then(a.bind(a,24597)),"@site/blog/2022-09-20/index.md?truncated=true",24597],b45174e4:[()=>a.e(9704).then(a.t.bind(a,21481,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-zero-to-hero-page-7-e56-list.json",21481],b46d1039:[()=>a.e(52932).then(a.t.bind(a,20279,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-azure-kubernetes-service-page-16-618-list.json",20279],b46e7759:[()=>a.e(84359).then(a.t.bind(a,80588,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-cloud-native-60e-list.json",80588],b478b21b:[()=>a.e(83351).then(a.t.bind(a,39913,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-cloud-native-page-3-73e-list.json",39913],b4a8043d:[()=>a.e(27837).then(a.bind(a,66247)),"@site/blog/zero-to-hero/2022-09-12-containerapps.md",66247],b4e6e6a7:[()=>a.e(97559).then(a.t.bind(a,17290,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-azure-kubernetes-service-page-18-d4d.json",17290],b4ea6d68:[()=>a.e(14051).then(a.t.bind(a,67549,19)),"~blog/default/cloud-native-blog-tags-azure-container-apps-page-10-fae.json",67549],b53ee4cc:[()=>a.e(33581).then(a.bind(a,28871)),"@site/blog/2022-09-03/index.md",28871],b57dcd1d:[()=>a.e(86519).then(a.t.bind(a,92892,19)),"~blog/default/cloud-native-blog-tags-azure-container-apps-page-17-f0e-list.json",92892],b589b176:[()=>a.e(35782).then(a.t.bind(a,5185,19)),"~blog/default/cloud-native-blog-tags-dapr-page-8-f68-list.json",5185],b5a12906:[()=>a.e(81744).then(a.bind(a,93267)),"@site/blog-cnny/2023-02-13/30days.md",93267],b5dae24c:[()=>a.e(67052).then(a.t.bind(a,63444,19)),"~blog/default/cloud-native-blog-tags-dapr-c54.json",63444],b66d95b7:[()=>a.e(21703).then(a.t.bind(a,882,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-zero-to-hero-page-8-419-list.json",882],b675f7d6:[()=>a.e(47335).then(a.t.bind(a,7939,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-azure-kubernetes-service-page-21-962-list.json",7939],b6c1521e:[()=>a.e(8442).then(a.t.bind(a,48437,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-zero-to-hero-page-5-b70.json",48437],b6fb65e1:[()=>a.e(57924).then(a.t.bind(a,67301,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-github-actions-page-8-627.json",67301],b70dd8a8:[()=>a.e(15854).then(a.t.bind(a,8600,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-30-days-of-ia-page-9-c99-list.json",8600],b75b9dbd:[()=>a.e(52434).then(a.t.bind(a,57554,19)),"~blog/blog-cnny/cloud-native-cnny-2023-2d4.json",57554],b760ee9a:[()=>a.e(9335).then(a.t.bind(a,80456,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-hack-together-page-4-8f5.json",80456],b76458e3:[()=>a.e(87585).then(a.t.bind(a,70761,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-azure-kubernetes-service-page-8-5be-list.json",70761],b826954e:[()=>a.e(33042).then(a.t.bind(a,91933,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-page-9-795.json",91933],b860d9a8:[()=>a.e(3248).then(a.t.bind(a,17626,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-github-codespaces-page-9-dd9.json",17626],b8de4b14:[()=>a.e(91019).then(a.t.bind(a,75825,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-cloud-native-page-7-3e0.json",75825],b9c0af58:[()=>a.e(13838).then(a.t.bind(a,91158,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-30-daysofcloudnative-page-11-601.json",91158],b9d35a0d:[()=>a.e(15717).then(a.t.bind(a,59960,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-zero-to-hero-page-11-69a.json",59960],b9e364fe:[()=>a.e(7881).then(a.bind(a,55394)),"@site/blog/2022-08-17/index.md",55394],b9f7f737:[()=>a.e(83357).then(a.t.bind(a,45695,19)),"~blog/default/cloud-native-blog-page-14-9b9.json",45695],ba136adc:[()=>a.e(11575).then(a.bind(a,9083)),"@site/docs/resources/languages.md",9083],ba7af0ad:[()=>a.e(21484).then(a.t.bind(a,90713,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-ask-the-expert-page-6-a9d-list.json",90713],babe571b:[()=>a.e(28145).then(a.bind(a,31203)),"@site/src/pages/serverless-september/CommunityBuzz.md",31203],baf5811d:[()=>a.e(51445).then(a.t.bind(a,51532,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-30-daysofcloudnative-5f9-list.json",51532],bb1bd555:[()=>a.e(77022).then(a.t.bind(a,87230,19)),"~blog/default/cloud-native-blog-tags-microservices-page-2-002-list.json",87230],bb1d8af3:[()=>a.e(58034).then(a.t.bind(a,51823,19)),"~blog/default/cloud-native-blog-tags-serverless-september-page-27-5c3.json",51823],bb243f37:[()=>a.e(69253).then(a.bind(a,6001)),"@site/blog-cnny/2023-02-03/scaling.md?truncated=true",6001],bb29086a:[()=>a.e(54293).then(a.t.bind(a,54599,19)),"/home/runner/work/Cloud-Native/Cloud-Native/website/.docusaurus/docusaurus-plugin-content-blog/blog-cnny/plugin-route-context-module-100.json",54599],bb9438bd:[()=>a.e(72601).then(a.t.bind(a,23257,19)),"~blog/default/cloud-native-blog-tags-azure-functions-page-9-c01-list.json",23257],bbc2b27f:[()=>a.e(11933).then(a.bind(a,69899)),"@site/blog-cnny/2023-01-26/30days.md?truncated=true",69899],bbf7817a:[()=>a.e(25227).then(a.bind(a,31304)),"@site/blog/2022-10-06/index.md",31304],bc0e8ad0:[()=>a.e(66242).then(a.bind(a,89997)),"@site/blog-cnny/2023-01-23/cloud-native-fundamentals.md",89997],bcbec2d9:[()=>a.e(23873).then(a.bind(a,69768)),"@site/blog-cnny/2023-02-02/index.md",69768],bcf95b3c:[()=>a.e(41905).then(a.t.bind(a,98121,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-ask-the-expert-page-11-f9f-list.json",98121],bd2cd00b:[()=>a.e(38277).then(a.t.bind(a,12456,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-azure-functions-page-4-802-list.json",12456],bd546f50:[()=>a.e(37345).then(a.t.bind(a,52663,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-fall-for-ia-page-9-1f6.json",52663],be284c34:[()=>a.e(47839).then(a.t.bind(a,42414,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-ask-the-expert-page-3-867-list.json",42414],be7dee77:[()=>a.e(34996).then(a.t.bind(a,62749,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-aks-page-5-c56.json",62749],beaf9ddb:[()=>a.e(27242).then(a.t.bind(a,49665,19)),"~blog/default/cloud-native-blog-tags-microservices-page-6-4fc-list.json",49665],bf4ba93b:[()=>a.e(36686).then(a.t.bind(a,62263,19)),"~blog/default/cloud-native-blog-tags-microservices-page-2-002.json",62263],bff09194:[()=>a.e(93804).then(a.t.bind(a,62089,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-ask-the-expert-page-10-716-list.json",62089],c0a2372d:[()=>a.e(66497).then(a.t.bind(a,31800,19)),"~blog/default/cloud-native-blog-tags-azure-functions-page-7-f67.json",31800],c0c2b9da:[()=>a.e(91259).then(a.t.bind(a,33118,19)),"~blog/default/cloud-native-blog-tags-azure-functions-page-7-f67-list.json",33118],c0df61e5:[()=>a.e(63053).then(a.t.bind(a,3743,19)),"~blog/default/cloud-native-blog-tags-azure-container-apps-page-16-3ed.json",3743],c0e56ffe:[()=>a.e(4270).then(a.t.bind(a,86570,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-30-days-of-ia-page-7-d25.json",86570],c0facb88:[()=>a.e(17958).then(a.t.bind(a,44423,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-github-codespaces-page-7-3a6.json",44423],c1d8a90e:[()=>a.e(13545).then(a.t.bind(a,4332,19)),"~blog/default/cloud-native-blog-645.json",4332],c1fb8ad8:[()=>a.e(82880).then(a.t.bind(a,19939,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-github-actions-page-5-22f-list.json",19939],c202d824:[()=>a.e(82369).then(a.t.bind(a,19990,19)),"~blog/default/cloud-native-blog-tags-azure-functions-page-6-4d2.json",19990],c212c0a6:[()=>a.e(71926).then(a.t.bind(a,79771,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-30-daysofcloudnative-page-5-93f.json",79771],c2319041:[()=>a.e(3857).then(a.t.bind(a,98804,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-github-actions-252.json",98804],c2839c2d:[()=>a.e(54012).then(a.t.bind(a,95665,19)),"~blog/default/cloud-native-blog-tags-azure-functions-page-15-2ed.json",95665],c2c35f38:[()=>a.e(5352).then(a.t.bind(a,68154,19)),"~blog/default/cloud-native-blog-page-30-706.json",68154],c2d757e2:[()=>a.e(68551).then(a.t.bind(a,74319,19)),"~blog/default/cloud-native-blog-tags-30-days-of-serverless-page-10-c1f-list.json",74319],c2d8f9fd:[()=>a.e(21899).then(a.t.bind(a,83769,19)),"/home/runner/work/Cloud-Native/Cloud-Native/website/.docusaurus/docusaurus-plugin-content-docs/default/plugin-route-context-module-100.json",83769],c2e0ced8:[()=>a.e(69526).then(a.t.bind(a,14361,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-ask-the-expert-page-3-6d4-list.json",14361],c2fb8e8b:[()=>a.e(55224).then(a.t.bind(a,18434,19)),"~blog/default/cloud-native-blog-tags-hello-page-2-980-list.json",18434],c32220c7:[()=>a.e(46781).then(a.t.bind(a,35223,19)),"~blog/default/cloud-native-blog-page-6-fc8.json",35223],c327d421:[()=>a.e(25623).then(a.t.bind(a,79847,19)),"~blog/default/cloud-native-blog-tags-serverless-september-page-9-ef1.json",79847],c32a5425:[()=>a.e(83508).then(a.t.bind(a,91650,19)),"~blog/default/cloud-native-blog-archive-30c.json",91650],c33a8a7c:[()=>a.e(52579).then(a.t.bind(a,68974,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-30-daysofcloudnative-page-11-601-list.json",68974],c35448b1:[()=>a.e(6175).then(a.t.bind(a,11222,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-azure-openai-page-4-dae-list.json",11222],c39a1b67:[()=>a.e(32403).then(a.t.bind(a,34785,19)),"~blog/default/cloud-native-blog-tags-serverless-september-page-23-168.json",34785],c43f31e5:[()=>a.e(79245).then(a.bind(a,3034)),"@site/blog-cnny/2023-02-09/index.md",3034],c44bb002:[()=>a.e(59543).then(a.t.bind(a,65688,19)),"~blog/default/cloud-native-blog-tags-microservices-page-5-896-list.json",65688],c44d11af:[()=>a.e(57169).then(a.t.bind(a,17445,19)),"~blog/default/cloud-native-blog-tags-serverless-september-page-5-ee6.json",17445],c46736c1:[()=>a.e(39923).then(a.t.bind(a,99653,19)),"~blog/blog-cnny/cloud-native-cnny-2023-page-16-bb5.json",99653],c4b4de0f:[()=>a.e(66537).then(a.t.bind(a,90757,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-zero-to-hero-page-15-0b5.json",90757],c4dc1033:[()=>a.e(56115).then(a.t.bind(a,53883,19)),"~blog/default/cloud-native-blog-tags-serverless-september-page-6-3be-list.json",53883],c4f5d8e4:[()=>Promise.all([a.e(40532),a.e(64195)]).then(a.bind(a,24945)),"@site/src/pages/index.js",24945],c5298e55:[()=>a.e(95930).then(a.t.bind(a,31249,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-azure-functions-page-2-a61-list.json",31249],c52c4229:[()=>a.e(73337).then(a.bind(a,70153)),"@site/blog-cnny/2023-01-27/explore-options.md?truncated=true",70153],c580cfa2:[()=>a.e(59477).then(a.t.bind(a,2148,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-a2f.json",2148],c63bdbb4:[()=>a.e(3377).then(a.t.bind(a,91405,19)),"~blog/default/cloud-native-blog-tags-serverless-september-page-17-1ef.json",91405],c67b3c2e:[()=>a.e(98268).then(a.t.bind(a,67029,19)),"~blog/default/cloud-native-blog-tags-microsoft-365-a5d-list.json",67029],c6c2f8a6:[()=>a.e(35644).then(a.t.bind(a,46075,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-zero-to-hero-page-4-5ad.json",46075],c736ecf7:[()=>a.e(97732).then(a.t.bind(a,39996,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-zero-to-hero-page-9-ebc-list.json",39996],c751643e:[()=>a.e(43259).then(a.t.bind(a,21622,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-learn-live-page-7-db8.json",21622],c7b8e407:[()=>a.e(7840).then(a.bind(a,39627)),"@site/blog-30daysofIA/2023-09-15/kick-off.md?truncated=true",39627],c7f0b8e3:[()=>a.e(93734).then(a.t.bind(a,76432,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-fall-for-ia-page-8-753.json",76432],c80c34af:[()=>a.e(49452).then(a.t.bind(a,63893,19)),"~blog/default/cloud-native-blog-tags-serverless-september-page-33-b14.json",63893],c8594c9f:[()=>a.e(40055).then(a.t.bind(a,79715,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-cloud-native-page-13-fd4.json",79715],c8c60460:[()=>a.e(81823).then(a.t.bind(a,27059,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-fall-for-ia-page-5-2b9.json",27059],c983f72a:[()=>a.e(638).then(a.t.bind(a,82447,19)),"~blog/default/cloud-native-blog-page-21-d0c.json",82447],ca71fe7b:[()=>a.e(97192).then(a.bind(a,60155)),"@site/blog/zero-to-hero/2022-09-12-containerapps.md?truncated=true",60155],cac8e99d:[()=>a.e(21852).then(a.bind(a,84617)),"@site/src/pages/Fall-For-IA/calendar.md",84617],cafc3c94:[()=>a.e(94963).then(a.t.bind(a,17935,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-fall-for-ia-3be.json",17935],cb2d3221:[()=>a.e(47561).then(a.t.bind(a,96243,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-azure-kubernetes-service-page-10-71f-list.json",96243],cb997589:[()=>a.e(14611).then(a.t.bind(a,69667,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-page-2-c61.json",69667],ccc49370:[()=>Promise.all([a.e(40532),a.e(75287),a.e(21791),a.e(46103)]).then(a.bind(a,324)),"@theme/BlogPostPage",324],cd336e02:[()=>a.e(74834).then(a.t.bind(a,20227,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-ask-the-expert-page-14-507-list.json",20227],cd923978:[()=>a.e(80837).then(a.t.bind(a,46155,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-azure-container-apps-page-6-156-list.json",46155],cdc79d9c:[()=>a.e(57550).then(a.bind(a,68686)),"@site/blog/2022-09-21/index.md",68686],ce53ffd6:[()=>a.e(33319).then(a.t.bind(a,88933,19)),"~blog/default/cloud-native-blog-tags-students-21a-list.json",88933],ce9b313c:[()=>a.e(82902).then(a.t.bind(a,11776,19)),"~blog/default/cloud-native-blog-tags-azure-functions-page-13-ae5-list.json",11776],cea706bc:[()=>a.e(57077).then(a.t.bind(a,94759,19)),"~blog/default/cloud-native-blog-tags-30-days-of-serverless-page-4-390-list.json",94759],cefbb4e5:[()=>a.e(14183).then(a.t.bind(a,48287,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-hack-together-page-8-a09-list.json",48287],cf50db93:[()=>a.e(22272).then(a.t.bind(a,26774,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-azure-functions-page-8-a98-list.json",26774],cf57d094:[()=>a.e(28843).then(a.t.bind(a,79858,19)),"~blog/default/cloud-native-blog-tags-serverless-september-page-32-216-list.json",79858],cf9f52c6:[()=>a.e(14939).then(a.t.bind(a,44848,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-fall-for-ia-page-5-2b9-list.json",44848],d00410c7:[()=>a.e(78133).then(a.t.bind(a,99758,19)),"~blog/default/cloud-native-blog-tags-microservices-page-3-307-list.json",99758],d0273d46:[()=>a.e(44608).then(a.t.bind(a,12996,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-community-buzz-page-2-684-list.json",12996],d04223d4:[()=>a.e(69964).then(a.t.bind(a,26217,19)),"~blog/blog-cnny/cloud-native-cnny-2023-page-14-dc9.json",26217],d05368c3:[()=>a.e(64243).then(a.bind(a,81362)),"@site/blog/2022-09-02/index.md",81362],d0db6cd5:[()=>a.e(41200).then(a.bind(a,73096)),"@site/blog/2022-09-06/index.md?truncated=true",73096],d11520dd:[()=>a.e(23064).then(a.t.bind(a,85261,19)),"~blog/default/cloud-native-blog-page-23-dfe.json",85261],d11663f1:[()=>a.e(71012).then(a.t.bind(a,34603,19)),"~blog/default/cloud-native-blog-tags-zero-to-hero-page-3-024.json",34603],d17a530e:[()=>a.e(27618).then(a.t.bind(a,77957,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-ask-the-expert-page-6-b19-list.json",77957],d1be9ff4:[()=>a.e(77046).then(a.t.bind(a,84709,19)),"~blog/default/cloud-native-blog-tags-serverless-september-page-24-059-list.json",84709],d216db91:[()=>a.e(42333).then(a.t.bind(a,56375,19)),"~blog/default/cloud-native-blog-tags-azure-container-apps-page-5-ad2-list.json",56375],d22054a7:[()=>a.e(16651).then(a.t.bind(a,13303,19)),"~blog/default/cloud-native-blog-tags-java-f5a.json",13303],d22a7198:[()=>a.e(78558).then(a.t.bind(a,83341,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-hack-together-page-6-4f5.json",83341],d2487d2c:[()=>a.e(41331).then(a.t.bind(a,18067,19)),"~blog/blog-cnny/cloud-native-cnny-2023-page-17-451.json",18067],d2567b4d:[()=>a.e(82813).then(a.t.bind(a,52643,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-cloud-native-page-15-5b3.json",52643],d272aefc:[()=>a.e(71151).then(a.t.bind(a,63146,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-zero-to-hero-page-8-419.json",63146],d2aa22d4:[()=>a.e(59988).then(a.t.bind(a,18347,19)),"~blog/default/cloud-native-blog-tags-tags-20a.json",18347],d307d5dd:[()=>a.e(28369).then(a.t.bind(a,57337,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-ask-the-expert-page-5-170.json",57337],d37b1df8:[()=>a.e(45448).then(a.t.bind(a,23325,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-fall-for-ia-page-3-156-list.json",23325],d41d8467:[()=>Promise.all([a.e(40532),a.e(94598),a.e(38057)]).then(a.bind(a,2569)),"@site/src/pages/Fall-For-IA/CommunityGallery.tsx",2569],d436e3ab:[()=>a.e(90276).then(a.t.bind(a,81811,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-ask-the-expert-page-5-99b-list.json",81811],d475afe6:[()=>a.e(58714).then(a.t.bind(a,33164,19)),"/home/runner/work/Cloud-Native/Cloud-Native/website/.docusaurus/docusaurus-plugin-content-blog/blog-30daysofIA/plugin-route-context-module-100.json",33164],d49a3a10:[()=>a.e(8325).then(a.t.bind(a,40672,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-cloud-native-new-year-page-4-0f4.json",40672],d4bbb0c6:[()=>a.e(28920).then(a.bind(a,75723)),"@site/blog/2022-08-17/index.md?truncated=true",75723],d50e2b40:[()=>a.e(70982).then(a.t.bind(a,4317,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-30-daysofcloudnative-page-16-df7-list.json",4317],d584ff55:[()=>a.e(46250).then(a.t.bind(a,21011,19)),"~blog/default/cloud-native-blog-page-15-27f.json",21011],d60c28fa:[()=>a.e(19981).then(a.t.bind(a,91152,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-azure-kubernetes-service-ce5.json",91152],d64808fa:[()=>a.e(66627).then(a.t.bind(a,1993,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-30-daysofcloudnative-page-14-262-list.json",1993],d64c3433:[()=>a.e(34594).then(a.t.bind(a,32149,19)),"~blog/default/cloud-native-blog-tags-power-platform-fa1.json",32149],d667446e:[()=>a.e(52338).then(a.t.bind(a,88907,19)),"~blog/default/cloud-native-blog-tags-azure-container-apps-page-15-f41.json",88907],d69f1c18:[()=>a.e(15734).then(a.t.bind(a,12525,19)),"~blog/default/cloud-native-blog-tags-azure-functions-page-14-f80.json",12525],d6e399c5:[()=>a.e(30001).then(a.t.bind(a,7229,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-community-buzz-page-4-0da.json",7229],d70fd83e:[()=>a.e(55369).then(a.t.bind(a,10292,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-community-buzz-page-7-3b8.json",10292],d721da33:[()=>a.e(54018).then(a.t.bind(a,69330,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-ask-the-expert-456.json",69330],d72f598b:[()=>a.e(63078).then(a.t.bind(a,79752,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-github-copilot-page-8-701.json",79752],d763cd59:[()=>a.e(48314).then(a.t.bind(a,72138,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-30-days-of-ia-page-6-b3c.json",72138],d7dbf034:[()=>a.e(92728).then(a.t.bind(a,14015,19)),"~blog/default/cloud-native-blog-tags-30-days-of-serverless-page-5-05c-list.json",14015],d9293a3c:[()=>a.e(10619).then(a.t.bind(a,84722,19)),"~blog/default/cloud-native-blog-tags-hello-page-2-980.json",84722],d94a5b94:[()=>a.e(29257).then(a.t.bind(a,1581,19)),"~blog/default/cloud-native-blog-tags-azure-container-apps-page-7-cc0.json",1581],d999f503:[()=>a.e(78061).then(a.bind(a,38777)),"@site/blog-cnny/2023-02-16/index.md?truncated=true",38777],d9a25476:[()=>a.e(99007).then(a.t.bind(a,26118,19)),"~blog/default/cloud-native-blog-tags-ask-the-expert-e90-list.json",26118],d9a67898:[()=>a.e(58064).then(a.t.bind(a,99136,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-kubernetes-d9a.json",99136],d9d7f0a9:[()=>a.e(92749).then(a.t.bind(a,58269,19)),"~blog/default/cloud-native-blog-tags-azure-container-apps-page-14-2ac-list.json",58269],da0c8116:[()=>a.e(27319).then(a.t.bind(a,42113,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-30-daysofcloudnative-page-4-c86-list.json",42113],da11032a:[()=>a.e(35398).then(a.t.bind(a,17874,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-cloud-native-page-5-f54.json",17874],da6fbf2a:[()=>a.e(35975).then(a.t.bind(a,22072,19)),"~blog/default/cloud-native-blog-tags-azure-logic-apps-page-3-764.json",22072],db1823d5:[()=>a.e(22854).then(a.t.bind(a,3380,19)),"~blog/default/cloud-native-blog-tags-serverless-september-page-25-1b0-list.json",3380],db6130e9:[()=>a.e(18360).then(a.t.bind(a,24696,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-azure-container-apps-page-9-d42.json",24696],db9e00b3:[()=>a.e(63167).then(a.bind(a,25112)),"@site/blog/2022-09-21/index.md?truncated=true",25112],dbe9f459:[()=>a.e(5172).then(a.t.bind(a,97680,19)),"~blog/default/cloud-native-blog-tags-cloudevents-44d-list.json",97680],dc5eefd4:[()=>a.e(93450).then(a.t.bind(a,12540,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-azure-kubernetes-service-page-12-cda.json",12540],dc727da6:[()=>a.e(44193).then(a.bind(a,32823)),"@site/blog-30daysofIA/2023-08-28/road-to-fallforia.md?truncated=true",32823],dccd6689:[()=>a.e(62455).then(a.t.bind(a,38559,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-secrets-management-dc2-list.json",38559],dceeb781:[()=>a.e(20243).then(a.t.bind(a,33594,19)),"~blog/default/cloud-native-blog-tags-azure-functions-page-10-01c.json",33594],dcf58f45:[()=>a.e(99450).then(a.t.bind(a,53066,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-azure-dns-399.json",53066],dd2fa4fa:[()=>a.e(28836).then(a.t.bind(a,18638,19)),"~blog/default/cloud-native-blog-tags-azure-container-apps-page-19-820-list.json",18638],dd73d8cf:[()=>a.e(47145).then(a.t.bind(a,76004,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-kubernetes-page-4-bc2.json",76004],dd8667a0:[()=>a.e(87005).then(a.t.bind(a,40635,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-page-7-d3f.json",40635],ddeb9c3b:[()=>a.e(43049).then(a.t.bind(a,57982,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-ask-the-expert-page-9-6d7-list.json",57982],de48c1c2:[()=>a.e(40208).then(a.bind(a,16393)),"@site/blog/2022-09-15/index.md",16393],de4b4bc0:[()=>a.e(41160).then(a.t.bind(a,2867,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-cloud-native-page-11-6bc.json",2867],de689bb5:[()=>a.e(22671).then(a.bind(a,51861)),"@site/blog/2022-09-18/index.md?truncated=true",51861],de95f9b4:[()=>a.e(5710).then(a.t.bind(a,42312,19)),"~blog/default/cloud-native-blog-tags-30-days-of-serverless-page-13-e82-list.json",42312],decd1b07:[()=>a.e(70440).then(a.t.bind(a,87922,19)),"~docs/default/category-cloud-nativedocs-tutorialsidebar-category-serverless-playlists-1ec.json",87922],df7b95b8:[()=>Promise.all([a.e(40532),a.e(29227)]).then(a.bind(a,28876)),"@site/src/pages/serverless-september/index.js",28876],df8f2207:[()=>a.e(92878).then(a.t.bind(a,22089,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-zero-to-hero-page-12-28b-list.json",22089],dfd81e36:[()=>a.e(79509).then(a.t.bind(a,98486,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-tags-402.json",98486],e097c1da:[()=>a.e(91821).then(a.t.bind(a,29231,19)),"~blog/default/cloud-native-blog-tags-azure-functions-page-12-e32.json",29231],e0b2cabb:[()=>a.e(78459).then(a.t.bind(a,5243,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-cloud-native-page-15-5b3-list.json",5243],e0be8f6f:[()=>a.e(37382).then(a.t.bind(a,21128,19)),"~blog/default/cloud-native-blog-tags-ask-the-expert-e90.json",21128],e0ee4473:[()=>a.e(64388).then(a.t.bind(a,97705,19)),"~blog/default/cloud-native-blog-tags-azure-container-apps-b0c-list.json",97705],e127deb5:[()=>a.e(71854).then(a.t.bind(a,27515,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-azure-container-apps-page-3-dea.json",27515],e137ac4d:[()=>a.e(15967).then(a.t.bind(a,91824,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-azure-cosmos-db-page-9-a1c-list.json",91824],e1399b64:[()=>a.e(30040).then(a.t.bind(a,44310,19)),"~blog/default/cloud-native-blog-tags-serverless-september-page-29-4db-list.json",44310],e1405df5:[()=>a.e(35558).then(a.t.bind(a,47864,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-azure-kubernetes-service-page-8-a29-list.json",47864],e1586f77:[()=>a.e(19608).then(a.t.bind(a,90055,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-cloud-native-page-10-650.json",90055],e159664d:[()=>a.e(74710).then(a.t.bind(a,34518,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-zero-to-hero-page-7-e56.json",34518],e16a4367:[()=>a.e(43871).then(a.t.bind(a,95823,19)),"~blog/default/cloud-native-blog-tags-azure-container-apps-page-4-6ed-list.json",95823],e1bc2a63:[()=>a.e(42036).then(a.t.bind(a,46317,19)),"~blog/default/cloud-native-blog-tags-30-days-of-serverless-page-17-3f5-list.json",46317],e1c2af7b:[()=>a.e(87413).then(a.t.bind(a,3564,19)),"~blog/default/cloud-native-blog-tags-azure-functions-page-2-6e7-list.json",3564],e1d438e9:[()=>a.e(62240).then(a.t.bind(a,96176,19)),"~blog/default/cloud-native-blog-tags-dapr-page-11-511.json",96176],e1daa54d:[()=>a.e(80670).then(a.t.bind(a,74571,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-ask-the-expert-456-list.json",74571],e1fc87d9:[()=>a.e(83061).then(a.t.bind(a,38477,19)),"~blog/blog-cnny/cloud-native-cnny-2023-page-9-6dc.json",38477],e2262ac9:[()=>a.e(23454).then(a.t.bind(a,52935,19)),"~blog/default/cloud-native-blog-tags-serverless-september-page-18-03a-list.json",52935],e37c4032:[()=>a.e(55759).then(a.t.bind(a,58579,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-azure-container-apps-2d8-list.json",58579],e3bf2dfe:[()=>a.e(88156).then(a.t.bind(a,95101,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-cloud-native-new-year-page-5-823-list.json",95101],e41794f0:[()=>a.e(50808).then(a.bind(a,86804)),"@site/blog/2022-09-06/index.md",86804],e49fdeb5:[()=>a.e(23602).then(a.t.bind(a,75692,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-azure-container-apps-page-6-156.json",75692],e4a2f027:[()=>a.e(64675).then(a.t.bind(a,9749,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-zero-to-hero-page-2-720-list.json",9749],e4fc1a09:[()=>a.e(54813).then(a.t.bind(a,98067,19)),"~blog/default/cloud-native-blog-tags-azure-container-apps-page-20-a60.json",98067],e5530f58:[()=>a.e(97159).then(a.t.bind(a,2386,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-azure-kubernetes-service-page-5-514.json",2386],e57b34d0:[()=>a.e(10805).then(a.t.bind(a,88142,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-ask-the-expert-page-7-af0-list.json",88142],e5ae2d3d:[()=>a.e(57849).then(a.t.bind(a,22551,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-azure-kubernetes-service-page-7-eb3.json",22551],e5becd70:[()=>a.e(46828).then(a.t.bind(a,76097,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-aks-e9c-list.json",76097],e65795b9:[()=>Promise.all([a.e(40532),a.e(15944),a.e(25080)]).then(a.bind(a,85052)),"@site/src/pages/Fall-For-IA/LearnLive.js",85052],e6ac9ebe:[()=>a.e(37582).then(a.bind(a,25625)),"@site/blog/2022-09-08/index.md",25625],e703f3a7:[()=>a.e(18390).then(a.t.bind(a,79349,19)),"~blog/default/cloud-native-blog-tags-custom-connector-6cd.json",79349],e705147d:[()=>a.e(31331).then(a.bind(a,87659)),"@site/blog-30daysofIA/2023-09-21/how-digital-natives-leverage-generative-ai.md?truncated=true",87659],e7136c90:[()=>a.e(96907).then(a.t.bind(a,14102,19)),"~blog/default/cloud-native-blog-tags-azure-functions-page-16-412-list.json",14102],e7c29825:[()=>a.e(81257).then(a.t.bind(a,19790,19)),"~blog/default/cloud-native-blog-tags-devtools-page-2-bb3-list.json",19790],e82f66e0:[()=>a.e(68468).then(a.t.bind(a,84087,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-github-codespaces-b37-list.json",84087],e85f9578:[()=>a.e(21127).then(a.t.bind(a,91832,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-github-actions-page-3-55f-list.json",91832],e88e2b57:[()=>a.e(44209).then(a.t.bind(a,97801,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-aks-page-2-f25-list.json",97801],e89bd621:[()=>a.e(46305).then(a.t.bind(a,76348,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-zero-to-hero-page-6-63e.json",76348],e8b803ba:[()=>a.e(38794).then(a.t.bind(a,12627,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-azure-kubernetes-service-page-12-cda-list.json",12627],e8c81125:[()=>a.e(44353).then(a.t.bind(a,66204,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-ask-the-expert-page-9-6d7.json",66204],e8dcc3fe:[()=>a.e(71630).then(a.t.bind(a,7612,19)),"~blog/default/cloud-native-blog-tags-azure-container-apps-page-8-77c-list.json",7612],e90a85bc:[()=>a.e(39201).then(a.t.bind(a,29718,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-hack-together-page-3-880-list.json",29718],e934991f:[()=>a.e(27063).then(a.t.bind(a,37721,19)),"~blog/default/cloud-native-blog-tags-30-days-of-serverless-page-3-331-list.json",37721],e936f9f6:[()=>a.e(2820).then(a.t.bind(a,97521,19)),"~blog/default/cloud-native-blog-tags-serverless-e-2-e-d39.json",97521],e94673f9:[()=>a.e(7945).then(a.t.bind(a,97966,19)),"~blog/default/cloud-native-blog-tags-30-days-of-serverless-page-20-bc2-list.json",97966],e9ebb693:[()=>a.e(15326).then(a.bind(a,27258)),"@site/blog/zero-to-hero/2022-09-19-azurefunctions.md?truncated=true",27258],e9f92e0d:[()=>a.e(24766).then(a.t.bind(a,70487,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-fall-for-ia-page-2-b6a-list.json",70487],e9fc3e68:[()=>a.e(56414).then(a.t.bind(a,77008,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-persistent-volumes-06c.json",77008],ea15e570:[()=>a.e(99246).then(a.t.bind(a,80686,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-github-copilot-page-3-c8c-list.json",80686],ea385099:[()=>a.e(5078).then(a.t.bind(a,75335,19)),"~blog/default/cloud-native-blog-tags-azure-functions-page-4-555.json",75335],eacffe03:[()=>a.e(25231).then(a.t.bind(a,34949,19)),"~blog/default/cloud-native-blog-tags-serverless-september-page-31-7d0-list.json",34949],eb689fda:[()=>a.e(81583).then(a.t.bind(a,30703,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-azure-cosmos-db-page-2-e86.json",30703],eb884ce7:[()=>a.e(60706).then(a.t.bind(a,17711,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-addons-788-list.json",17711],eb8d02f3:[()=>a.e(38889).then(a.t.bind(a,74088,19)),"~blog/default/cloud-native-blog-tags-microservices-d05-list.json",74088],ebabe618:[()=>a.e(82457).then(a.bind(a,2446)),"@site/blog/zero-to-hero/2022-09-12-azurefunctions.md?truncated=true",2446],ebb76b0d:[()=>a.e(81570).then(a.t.bind(a,44222,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-learn-live-page-5-f88.json",44222],ebbe4e7d:[()=>a.e(97515).then(a.t.bind(a,9128,19)),"~blog/default/cloud-native-blog-page-19-d7c.json",9128],ec0ac9db:[()=>a.e(16663).then(a.t.bind(a,12649,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-30-daysofcloudnative-page-6-efa.json",12649],ec244af9:[()=>a.e(66099).then(a.bind(a,48692)),"@site/docs/videos/intro.md",48692],ec65f5d5:[()=>a.e(86943).then(a.t.bind(a,31556,19)),"~blog/default/cloud-native-blog-tags-azure-functions-page-8-477.json",31556],ed2b8e88:[()=>a.e(604).then(a.t.bind(a,98132,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-addons-788.json",98132],edb3edba:[()=>a.e(77364).then(a.t.bind(a,52799,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-azure-kubernetes-service-page-18-d4d-list.json",52799],edef2ad1:[()=>a.e(38097).then(a.t.bind(a,67523,19)),"~blog/default/cloud-native-blog-tags-30-days-of-serverless-page-12-299.json",67523],ee2b3c0a:[()=>a.e(99632).then(a.t.bind(a,89637,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-azure-kubernetes-service-page-19-572-list.json",89637],ee9bc1a2:[()=>a.e(21897).then(a.t.bind(a,9077,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-cloud-native-new-year-page-5-823.json",9077],eef2ff81:[()=>a.e(25304).then(a.t.bind(a,75382,19)),"~blog/default/cloud-native-blog-tags-dapr-page-4-60f-list.json",75382],ef05350a:[()=>a.e(93176).then(a.t.bind(a,72609,19)),"~blog/default/cloud-native-blog-tags-serverless-september-page-4-592-list.json",72609],ef64c709:[()=>a.e(4311).then(a.t.bind(a,99766,19)),"~blog/default/cloud-native-blog-page-24-5ca.json",99766],ef7c2797:[()=>a.e(49391).then(a.t.bind(a,1742,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-ask-the-expert-page-5-99b.json",1742],ef8eddd0:[()=>a.e(68216).then(a.t.bind(a,85859,19)),"~blog/default/cloud-native-blog-tags-microservices-page-10-e37.json",85859],efe016d3:[()=>a.e(6994).then(a.t.bind(a,32033,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-github-actions-page-2-54e.json",32033],f0d25600:[()=>a.e(97470).then(a.t.bind(a,54458,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-github-codespaces-page-8-588-list.json",54458],f0e90793:[()=>a.e(14119).then(a.t.bind(a,35394,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-azure-cosmos-db-page-5-7a9.json",35394],f128a0f0:[()=>a.e(31132).then(a.t.bind(a,31960,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-ask-the-expert-page-16-795.json",31960],f12c2396:[()=>a.e(5743).then(a.t.bind(a,50398,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-fall-for-ia-page-3-156.json",50398],f1fe5cc7:[()=>a.e(17925).then(a.t.bind(a,1181,19)),"~blog/default/cloud-native-blog-tags-dapr-page-10-d0d-list.json",1181],f21c8b70:[()=>a.e(92340).then(a.t.bind(a,52096,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-notary-225.json",52096],f248a380:[()=>a.e(44702).then(a.t.bind(a,68578,19)),"~blog/default/cloud-native-blog-tags-serverless-september-805-list.json",68578],f2d08d34:[()=>a.e(71359).then(a.t.bind(a,14562,19)),"~blog/default/cloud-native-blog-tags-vscode-e62-list.json",14562],f30b4e00:[()=>a.e(42888).then(a.t.bind(a,47804,19)),"~blog/default/cloud-native-blog-tags-dapr-page-16-0b8-list.json",47804],f3333784:[()=>a.e(7861).then(a.t.bind(a,9529,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-azure-kubernetes-service-page-14-619.json",9529],f34f398b:[()=>a.e(28058).then(a.t.bind(a,60800,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-zero-to-hero-page-3-4e3-list.json",60800],f3cb94e5:[()=>a.e(51237).then(a.t.bind(a,55135,19)),"~blog/default/cloud-native-blog-page-2-e4d.json",55135],f4610d17:[()=>a.e(83765).then(a.t.bind(a,18339,19)),"~blog/default/cloud-native-blog-tags-30-days-of-serverless-page-14-3a6-list.json",18339],f470690a:[()=>a.e(95175).then(a.t.bind(a,74905,19)),"~blog/default/cloud-native-blog-tags-azure-functions-page-15-2ed-list.json",74905],f4b1ab07:[()=>a.e(96792).then(a.t.bind(a,61316,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-ask-the-expert-251-list.json",61316],f5611d39:[()=>a.e(6337).then(a.t.bind(a,71367,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-github-actions-page-9-74c-list.json",71367],f5784bce:[()=>a.e(18631).then(a.bind(a,72411)),"@site/blog/zero-to-hero/2022-09-06-azurefunctions.md?truncated=true",72411],f5a85496:[()=>a.e(46989).then(a.bind(a,51248)),"@site/blog/2022-09-18/index.md",51248],f5ac3b90:[()=>a.e(37279).then(a.t.bind(a,99652,19)),"~blog/default/cloud-native-blog-tags-dotnet-page-2-742.json",99652],f5b7c6a9:[()=>a.e(39010).then(a.t.bind(a,30226,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-azure-cosmos-db-842-list.json",30226],f5ef3ca7:[()=>a.e(77411).then(a.t.bind(a,59377,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-nginx-ingress-controller-100.json",59377],f5f8a48c:[()=>a.e(52071).then(a.t.bind(a,70986,19)),"~blog/default/cloud-native-blog-tags-serverless-september-805.json",70986],f689083d:[()=>a.e(97734).then(a.t.bind(a,60499,19)),"~blog/default/cloud-native-blog-tags-azure-functions-page-16-412.json",60499],f6a05f02:[()=>a.e(60841).then(a.t.bind(a,29295,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-30-daysofcloudnative-page-7-cf4.json",29295],f6bd7cc2:[()=>a.e(2312).then(a.t.bind(a,80386,19)),"~blog/default/cloud-native-blog-tags-azure-functions-page-10-01c-list.json",80386],f6c4aca5:[()=>a.e(90300).then(a.bind(a,9735)),"@site/blog-cnny/2023-01-22/30days.md",9735],f6de2cbe:[()=>a.e(87633).then(a.t.bind(a,34866,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-30-days-of-ia-page-3-b08-list.json",34866],f6df8ec8:[()=>a.e(39717).then(a.t.bind(a,77031,19)),"~blog/default/cloud-native-blog-tags-serverless-september-page-8-4a4.json",77031],f6f0ee1b:[()=>a.e(91007).then(a.bind(a,63347)),"@site/blog/2022-09-25/index.md?truncated=true",63347],f74cbec7:[()=>a.e(50427).then(a.t.bind(a,72304,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-github-actions-page-4-d4b.json",72304],f79c2b36:[()=>a.e(40626).then(a.t.bind(a,76892,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-azure-kubernetes-service-page-2-c55-list.json",76892],f7b7ef7e:[()=>a.e(4354).then(a.t.bind(a,62782,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-learn-live-page-6-33d-list.json",62782],f7daf5fe:[()=>a.e(10322).then(a.t.bind(a,98826,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-30-daysofcloudnative-page-14-262.json",98826],f7decf47:[()=>a.e(81944).then(a.t.bind(a,25302,19)),"~blog/default/cloud-native-blog-tags-serverless-september-page-28-c65-list.json",25302],f7fb9a31:[()=>a.e(65014).then(a.t.bind(a,55042,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-page-3-58b.json",55042],f8223cf0:[()=>a.e(8217).then(a.bind(a,33477)),"@site/blog-cnny/2023-02-01/index.md",33477],f830ec9e:[()=>a.e(59070).then(a.bind(a,18337)),"@site/blog/2022-09-12/index.md?truncated=true",18337],f83e3e43:[()=>a.e(5017).then(a.t.bind(a,73451,19)),"~blog/default/cloud-native-blog-tags-serverless-september-page-21-700.json",73451],f848febd:[()=>a.e(73354).then(a.t.bind(a,98897,19)),"~blog/default/cloud-native-blog-page-34-74e.json",98897],f872f327:[()=>a.e(38602).then(a.t.bind(a,89763,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-ask-the-expert-page-8-0db-list.json",89763],f8bd7d44:[()=>a.e(40821).then(a.t.bind(a,19426,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-cloud-native-page-6-187.json",19426],f97394ec:[()=>a.e(79787).then(a.t.bind(a,51022,19)),"~blog/default/cloud-native-blog-tags-30-days-of-serverless-page-4-390.json",51022],f97a64b3:[()=>a.e(87027).then(a.t.bind(a,36254,19)),"~blog/default/cloud-native-blog-tags-zero-to-hero-page-4-1c1.json",36254],f9d7044e:[()=>a.e(93061).then(a.t.bind(a,87894,19)),"~blog/default/cloud-native-blog-tags-azure-container-apps-page-6-dd4-list.json",87894],fa0a96ff:[()=>a.e(1426).then(a.bind(a,50921)),"@site/blog/2022-10-04/index.md?truncated=true",50921],fa26972a:[()=>a.e(12046).then(a.t.bind(a,7582,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-containers-page-3-c64-list.json",7582],fa6b5e6c:[()=>a.e(76058).then(a.t.bind(a,46967,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-azure-kubernetes-service-page-6-905.json",46967],fa74e77e:[()=>a.e(94125).then(a.t.bind(a,38832,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-github-codespaces-page-2-0e1-list.json",38832],faa3ccc1:[()=>a.e(25196).then(a.t.bind(a,60656,19)),"~blog/blog-30daysofIA/blog-post-list-prop-blog-30daysofIA.json",60656],fac73890:[()=>a.e(50395).then(a.t.bind(a,20381,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-ask-the-expert-page-9-2d6-list.json",20381],fb5b9b20:[()=>a.e(78060).then(a.t.bind(a,39464,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-azure-cosmos-db-page-8-c1b.json",39464],fb71245d:[()=>a.e(42835).then(a.t.bind(a,55885,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-community-buzz-page-5-478.json",55885],fb88b8ca:[()=>a.e(54236).then(a.t.bind(a,90256,19)),"~blog/default/cloud-native-blog-page-32-09b.json",90256],fbdbf422:[()=>a.e(96010).then(a.t.bind(a,80914,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-30-days-of-ia-da6.json",80914],fbfa5a90:[()=>a.e(43583).then(a.t.bind(a,24765,19)),"~blog/default/cloud-native-blog-page-33-475.json",24765],fc042285:[()=>a.e(51789).then(a.bind(a,140)),"@site/src/pages/Fall-For-IA/AskTheExpert.md",140],fcb7c80a:[()=>a.e(8726).then(a.t.bind(a,67403,19)),"~blog/default/cloud-native-blog-tags-python-956.json",67403],fd422a34:[()=>a.e(16891).then(a.t.bind(a,63733,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-github-copilot-page-6-73f-list.json",63733],fd4ba951:[()=>a.e(83228).then(a.t.bind(a,80690,19)),"~blog/blog-30daysofIA/cloud-native-30-daysof-ia-tags-ask-the-expert-page-2-afc-list.json",80690],fd7a878f:[()=>a.e(69407).then(a.t.bind(a,81813,19)),"~blog/default/cloud-native-blog-tags-zero-to-hero-page-2-555.json",81813],feb943f9:[()=>a.e(37795).then(a.bind(a,11774)),"@site/blog/2022-09-09/index.md",11774],ff198b7d:[()=>a.e(32574).then(a.t.bind(a,77589,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-30-daysofcloudnative-page-15-fb4.json",77589],ff1fa6c9:[()=>a.e(54647).then(a.bind(a,53163)),"@site/blog/2022-09-01/index.md",53163],ff37d1e9:[()=>a.e(37).then(a.t.bind(a,67487,19)),"~blog/default/cloud-native-blog-tags-azure-event-grid-823-list.json",67487],ff734053:[()=>a.e(40858).then(a.t.bind(a,33337,19)),"~blog/default/cloud-native-blog-tags-30-days-of-serverless-page-3-331.json",33337],ffb3fd1a:[()=>a.e(35680).then(a.t.bind(a,1677,19)),"~blog/blog-cnny/cloud-native-cnny-2023-tags-notation-581.json",1677],ffe586b2:[()=>a.e(67251).then(a.t.bind(a,76400,19)),"~blog/default/cloud-native-blog-tags-azure-functions-page-11-c62.json",76400]};function c(e){let{error:t,retry:a,pastDelay:o}=e;return t?n.createElement("div",{style:{textAlign:"center",color:"#fff",backgroundColor:"#fa383e",borderColor:"#fa383e",borderStyle:"solid",borderRadius:"0.25rem",borderWidth:"1px",boxSizing:"border-box",display:"block",padding:"1rem",flex:"0 0 50%",marginLeft:"25%",marginRight:"25%",marginTop:"5rem",maxWidth:"50%",width:"100%"}},n.createElement("p",null,String(t)),n.createElement("div",null,n.createElement("button",{type:"button",onClick:a},"Retry"))):o?n.createElement("div",{style:{display:"flex",justifyContent:"center",alignItems:"center",height:"100vh"}},n.createElement("svg",{id:"loader",style:{width:128,height:110,position:"absolute",top:"calc(100vh - 64%)"},viewBox:"0 0 45 45",xmlns:"http://www.w3.org/2000/svg",stroke:"#61dafb"},n.createElement("g",{fill:"none",fillRule:"evenodd",transform:"translate(1 1)",strokeWidth:"2"},n.createElement("circle",{cx:"22",cy:"22",r:"6",strokeOpacity:"0"},n.createElement("animate",{attributeName:"r",begin:"1.5s",dur:"3s",values:"6;22",calcMode:"linear",repeatCount:"indefinite"}),n.createElement("animate",{attributeName:"stroke-opacity",begin:"1.5s",dur:"3s",values:"1;0",calcMode:"linear",repeatCount:"indefinite"}),n.createElement("animate",{attributeName:"stroke-width",begin:"1.5s",dur:"3s",values:"2;0",calcMode:"linear",repeatCount:"indefinite"})),n.createElement("circle",{cx:"22",cy:"22",r:"6",strokeOpacity:"0"},n.createElement("animate",{attributeName:"r",begin:"3s",dur:"3s",values:"6;22",calcMode:"linear",repeatCount:"indefinite"}),n.createElement("animate",{attributeName:"stroke-opacity",begin:"3s",dur:"3s",values:"1;0",calcMode:"linear",repeatCount:"indefinite"}),n.createElement("animate",{attributeName:"stroke-width",begin:"3s",dur:"3s",values:"2;0",calcMode:"linear",repeatCount:"indefinite"})),n.createElement("circle",{cx:"22",cy:"22",r:"8"},n.createElement("animate",{attributeName:"r",begin:"0s",dur:"1.5s",values:"6;1;2;3;4;5;6",calcMode:"linear",repeatCount:"indefinite"}))))):null}var d=a(5304),u=a(69656);function g(e,t){if("*"===e)return l()({loading:c,loader:()=>a.e(74248).then(a.bind(a,74248)),modules:["@theme/NotFound"],webpack:()=>[74248],render(e,t){const a=e.default;return n.createElement(u.z,{value:{plugin:{name:"native",id:"default"}}},n.createElement(a,t))}});const i=r[`${e}-${t}`],g={},p=[],f=[],b=(0,d.Z)(i);return Object.entries(b).forEach((e=>{let[t,a]=e;const n=s[a];n&&(g[t]=n[0],p.push(n[1]),f.push(n[2]))})),l().Map({loading:c,loader:g,modules:p,webpack:()=>f,render(t,a){const l=JSON.parse(JSON.stringify(i));Object.entries(t).forEach((t=>{let[a,n]=t;const o=n.default;if(!o)throw new Error(`The page component at ${e} doesn't have a default export. This makes it impossible to render anything. Consider default-exporting a React component.`);"object"!=typeof o&&"function"!=typeof o||Object.keys(n).filter((e=>"default"!==e)).forEach((e=>{o[e]=n[e]}));let i=l;const r=a.split(".");r.slice(0,-1).forEach((e=>{i=i[e]})),i[r[r.length-1]]=o}));const r=l.__comp;delete l.__comp;const s=l.__context;return delete l.__context,n.createElement(u.z,{value:s},n.createElement(r,(0,o.Z)({},l,a)))}})}const p=[{path:"/Cloud-Native/30daysofIA",component:g("/Cloud-Native/30daysofIA","c04"),exact:!0},{path:"/Cloud-Native/30daysofIA/archive",component:g("/Cloud-Native/30daysofIA/archive","df3"),exact:!0},{path:"/Cloud-Native/30daysofIA/cultivating-a-culture-for-intelligent-apps",component:g("/Cloud-Native/30daysofIA/cultivating-a-culture-for-intelligent-apps","0aa"),exact:!0},{path:"/Cloud-Native/30daysofIA/demystifying-intelligent-applications",component:g("/Cloud-Native/30daysofIA/demystifying-intelligent-applications","b74"),exact:!0},{path:"/Cloud-Native/30daysofIA/hacktogether-recap",component:g("/Cloud-Native/30daysofIA/hacktogether-recap","743"),exact:!0},{path:"/Cloud-Native/30daysofIA/harnessing-the-power-of-intelligent-apps",component:g("/Cloud-Native/30daysofIA/harnessing-the-power-of-intelligent-apps","0fe"),exact:!0},{path:"/Cloud-Native/30daysofIA/how-digital-natives-leverage-generative-ai",component:g("/Cloud-Native/30daysofIA/how-digital-natives-leverage-generative-ai","d70"),exact:!0},{path:"/Cloud-Native/30daysofIA/kick-off",component:g("/Cloud-Native/30daysofIA/kick-off","862"),exact:!0},{path:"/Cloud-Native/30daysofIA/page/2",component:g("/Cloud-Native/30daysofIA/page/2","6f4"),exact:!0},{path:"/Cloud-Native/30daysofIA/page/3",component:g("/Cloud-Native/30daysofIA/page/3","2bf"),exact:!0},{path:"/Cloud-Native/30daysofIA/page/4",component:g("/Cloud-Native/30daysofIA/page/4","36e"),exact:!0},{path:"/Cloud-Native/30daysofIA/page/5",component:g("/Cloud-Native/30daysofIA/page/5","dad"),exact:!0},{path:"/Cloud-Native/30daysofIA/page/6",component:g("/Cloud-Native/30daysofIA/page/6","a06"),exact:!0},{path:"/Cloud-Native/30daysofIA/page/7",component:g("/Cloud-Native/30daysofIA/page/7","a43"),exact:!0},{path:"/Cloud-Native/30daysofIA/page/8",component:g("/Cloud-Native/30daysofIA/page/8","ce8"),exact:!0},{path:"/Cloud-Native/30daysofIA/page/9",component:g("/Cloud-Native/30daysofIA/page/9","e74"),exact:!0},{path:"/Cloud-Native/30daysofIA/preparing-the-path-for-intelligent-apps",component:g("/Cloud-Native/30daysofIA/preparing-the-path-for-intelligent-apps","549"),exact:!0},{path:"/Cloud-Native/30daysofIA/reimagine-app-development-with-ai",component:g("/Cloud-Native/30daysofIA/reimagine-app-development-with-ai","fcb"),exact:!0},{path:"/Cloud-Native/30daysofIA/road-to-fallforIA",component:g("/Cloud-Native/30daysofIA/road-to-fallforIA","35d"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags",component:g("/Cloud-Native/30daysofIA/tags","ca3"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/30-days-of-ia",component:g("/Cloud-Native/30daysofIA/tags/30-days-of-ia","b8d"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/30-days-of-ia/page/2",component:g("/Cloud-Native/30daysofIA/tags/30-days-of-ia/page/2","f9f"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/30-days-of-ia/page/3",component:g("/Cloud-Native/30daysofIA/tags/30-days-of-ia/page/3","a51"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/30-days-of-ia/page/4",component:g("/Cloud-Native/30daysofIA/tags/30-days-of-ia/page/4","f6a"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/30-days-of-ia/page/5",component:g("/Cloud-Native/30daysofIA/tags/30-days-of-ia/page/5","3b4"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/30-days-of-ia/page/6",component:g("/Cloud-Native/30daysofIA/tags/30-days-of-ia/page/6","ff8"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/30-days-of-ia/page/7",component:g("/Cloud-Native/30daysofIA/tags/30-days-of-ia/page/7","b54"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/30-days-of-ia/page/8",component:g("/Cloud-Native/30daysofIA/tags/30-days-of-ia/page/8","a8a"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/30-days-of-ia/page/9",component:g("/Cloud-Native/30daysofIA/tags/30-days-of-ia/page/9","757"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/ask-the-expert",component:g("/Cloud-Native/30daysofIA/tags/ask-the-expert","ece"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/ask-the-expert/page/2",component:g("/Cloud-Native/30daysofIA/tags/ask-the-expert/page/2","120"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/ask-the-expert/page/3",component:g("/Cloud-Native/30daysofIA/tags/ask-the-expert/page/3","7ca"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/ask-the-expert/page/4",component:g("/Cloud-Native/30daysofIA/tags/ask-the-expert/page/4","f57"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/ask-the-expert/page/5",component:g("/Cloud-Native/30daysofIA/tags/ask-the-expert/page/5","c36"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/ask-the-expert/page/6",component:g("/Cloud-Native/30daysofIA/tags/ask-the-expert/page/6","533"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/ask-the-expert/page/7",component:g("/Cloud-Native/30daysofIA/tags/ask-the-expert/page/7","315"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/ask-the-expert/page/8",component:g("/Cloud-Native/30daysofIA/tags/ask-the-expert/page/8","a00"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/ask-the-expert/page/9",component:g("/Cloud-Native/30daysofIA/tags/ask-the-expert/page/9","6b2"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/azure-container-apps",component:g("/Cloud-Native/30daysofIA/tags/azure-container-apps","881"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/azure-container-apps/page/2",component:g("/Cloud-Native/30daysofIA/tags/azure-container-apps/page/2","ad0"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/azure-container-apps/page/3",component:g("/Cloud-Native/30daysofIA/tags/azure-container-apps/page/3","7c5"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/azure-container-apps/page/4",component:g("/Cloud-Native/30daysofIA/tags/azure-container-apps/page/4","496"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/azure-container-apps/page/5",component:g("/Cloud-Native/30daysofIA/tags/azure-container-apps/page/5","610"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/azure-container-apps/page/6",component:g("/Cloud-Native/30daysofIA/tags/azure-container-apps/page/6","01a"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/azure-container-apps/page/7",component:g("/Cloud-Native/30daysofIA/tags/azure-container-apps/page/7","610"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/azure-container-apps/page/8",component:g("/Cloud-Native/30daysofIA/tags/azure-container-apps/page/8","bca"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/azure-container-apps/page/9",component:g("/Cloud-Native/30daysofIA/tags/azure-container-apps/page/9","3fb"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/azure-cosmos-db",component:g("/Cloud-Native/30daysofIA/tags/azure-cosmos-db","bc2"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/azure-cosmos-db/page/2",component:g("/Cloud-Native/30daysofIA/tags/azure-cosmos-db/page/2","51b"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/azure-cosmos-db/page/3",component:g("/Cloud-Native/30daysofIA/tags/azure-cosmos-db/page/3","da8"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/azure-cosmos-db/page/4",component:g("/Cloud-Native/30daysofIA/tags/azure-cosmos-db/page/4","e50"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/azure-cosmos-db/page/5",component:g("/Cloud-Native/30daysofIA/tags/azure-cosmos-db/page/5","d81"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/azure-cosmos-db/page/6",component:g("/Cloud-Native/30daysofIA/tags/azure-cosmos-db/page/6","0d8"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/azure-cosmos-db/page/7",component:g("/Cloud-Native/30daysofIA/tags/azure-cosmos-db/page/7","6d1"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/azure-cosmos-db/page/8",component:g("/Cloud-Native/30daysofIA/tags/azure-cosmos-db/page/8","273"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/azure-cosmos-db/page/9",component:g("/Cloud-Native/30daysofIA/tags/azure-cosmos-db/page/9","ad3"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/azure-functions",component:g("/Cloud-Native/30daysofIA/tags/azure-functions","369"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/azure-functions/page/2",component:g("/Cloud-Native/30daysofIA/tags/azure-functions/page/2","0f3"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/azure-functions/page/3",component:g("/Cloud-Native/30daysofIA/tags/azure-functions/page/3","a68"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/azure-functions/page/4",component:g("/Cloud-Native/30daysofIA/tags/azure-functions/page/4","19a"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/azure-functions/page/5",component:g("/Cloud-Native/30daysofIA/tags/azure-functions/page/5","2e8"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/azure-functions/page/6",component:g("/Cloud-Native/30daysofIA/tags/azure-functions/page/6","51e"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/azure-functions/page/7",component:g("/Cloud-Native/30daysofIA/tags/azure-functions/page/7","be7"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/azure-functions/page/8",component:g("/Cloud-Native/30daysofIA/tags/azure-functions/page/8","ce1"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/azure-functions/page/9",component:g("/Cloud-Native/30daysofIA/tags/azure-functions/page/9","d34"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service",component:g("/Cloud-Native/30daysofIA/tags/azure-kubernetes-service","ac2"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service/page/2",component:g("/Cloud-Native/30daysofIA/tags/azure-kubernetes-service/page/2","29e"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service/page/3",component:g("/Cloud-Native/30daysofIA/tags/azure-kubernetes-service/page/3","38f"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service/page/4",component:g("/Cloud-Native/30daysofIA/tags/azure-kubernetes-service/page/4","015"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service/page/5",component:g("/Cloud-Native/30daysofIA/tags/azure-kubernetes-service/page/5","07b"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service/page/6",component:g("/Cloud-Native/30daysofIA/tags/azure-kubernetes-service/page/6","ebe"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service/page/7",component:g("/Cloud-Native/30daysofIA/tags/azure-kubernetes-service/page/7","741"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service/page/8",component:g("/Cloud-Native/30daysofIA/tags/azure-kubernetes-service/page/8","85b"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/azure-kubernetes-service/page/9",component:g("/Cloud-Native/30daysofIA/tags/azure-kubernetes-service/page/9","86a"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/azure-openai",component:g("/Cloud-Native/30daysofIA/tags/azure-openai","ac0"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/azure-openai/page/2",component:g("/Cloud-Native/30daysofIA/tags/azure-openai/page/2","0ea"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/azure-openai/page/3",component:g("/Cloud-Native/30daysofIA/tags/azure-openai/page/3","2b1"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/azure-openai/page/4",component:g("/Cloud-Native/30daysofIA/tags/azure-openai/page/4","96c"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/azure-openai/page/5",component:g("/Cloud-Native/30daysofIA/tags/azure-openai/page/5","551"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/azure-openai/page/6",component:g("/Cloud-Native/30daysofIA/tags/azure-openai/page/6","e71"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/azure-openai/page/7",component:g("/Cloud-Native/30daysofIA/tags/azure-openai/page/7","a9e"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/azure-openai/page/8",component:g("/Cloud-Native/30daysofIA/tags/azure-openai/page/8","934"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/azure-openai/page/9",component:g("/Cloud-Native/30daysofIA/tags/azure-openai/page/9","af6"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/community-buzz",component:g("/Cloud-Native/30daysofIA/tags/community-buzz","5d1"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/community-buzz/page/2",component:g("/Cloud-Native/30daysofIA/tags/community-buzz/page/2","2bf"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/community-buzz/page/3",component:g("/Cloud-Native/30daysofIA/tags/community-buzz/page/3","1a0"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/community-buzz/page/4",component:g("/Cloud-Native/30daysofIA/tags/community-buzz/page/4","cf2"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/community-buzz/page/5",component:g("/Cloud-Native/30daysofIA/tags/community-buzz/page/5","c92"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/community-buzz/page/6",component:g("/Cloud-Native/30daysofIA/tags/community-buzz/page/6","d2c"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/community-buzz/page/7",component:g("/Cloud-Native/30daysofIA/tags/community-buzz/page/7","9d9"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/community-buzz/page/8",component:g("/Cloud-Native/30daysofIA/tags/community-buzz/page/8","811"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/community-buzz/page/9",component:g("/Cloud-Native/30daysofIA/tags/community-buzz/page/9","665"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/fall-for-ia",component:g("/Cloud-Native/30daysofIA/tags/fall-for-ia","93f"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/fall-for-ia/page/2",component:g("/Cloud-Native/30daysofIA/tags/fall-for-ia/page/2","c91"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/fall-for-ia/page/3",component:g("/Cloud-Native/30daysofIA/tags/fall-for-ia/page/3","503"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/fall-for-ia/page/4",component:g("/Cloud-Native/30daysofIA/tags/fall-for-ia/page/4","edc"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/fall-for-ia/page/5",component:g("/Cloud-Native/30daysofIA/tags/fall-for-ia/page/5","64f"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/fall-for-ia/page/6",component:g("/Cloud-Native/30daysofIA/tags/fall-for-ia/page/6","fc6"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/fall-for-ia/page/7",component:g("/Cloud-Native/30daysofIA/tags/fall-for-ia/page/7","e03"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/fall-for-ia/page/8",component:g("/Cloud-Native/30daysofIA/tags/fall-for-ia/page/8","d4c"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/fall-for-ia/page/9",component:g("/Cloud-Native/30daysofIA/tags/fall-for-ia/page/9","b8b"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/github-actions",component:g("/Cloud-Native/30daysofIA/tags/github-actions","d81"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/github-actions/page/2",component:g("/Cloud-Native/30daysofIA/tags/github-actions/page/2","250"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/github-actions/page/3",component:g("/Cloud-Native/30daysofIA/tags/github-actions/page/3","dc0"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/github-actions/page/4",component:g("/Cloud-Native/30daysofIA/tags/github-actions/page/4","22f"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/github-actions/page/5",component:g("/Cloud-Native/30daysofIA/tags/github-actions/page/5","e9c"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/github-actions/page/6",component:g("/Cloud-Native/30daysofIA/tags/github-actions/page/6","27b"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/github-actions/page/7",component:g("/Cloud-Native/30daysofIA/tags/github-actions/page/7","11d"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/github-actions/page/8",component:g("/Cloud-Native/30daysofIA/tags/github-actions/page/8","7f3"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/github-actions/page/9",component:g("/Cloud-Native/30daysofIA/tags/github-actions/page/9","167"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/github-codespaces",component:g("/Cloud-Native/30daysofIA/tags/github-codespaces","3ea"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/github-codespaces/page/2",component:g("/Cloud-Native/30daysofIA/tags/github-codespaces/page/2","0c9"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/github-codespaces/page/3",component:g("/Cloud-Native/30daysofIA/tags/github-codespaces/page/3","2fa"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/github-codespaces/page/4",component:g("/Cloud-Native/30daysofIA/tags/github-codespaces/page/4","691"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/github-codespaces/page/5",component:g("/Cloud-Native/30daysofIA/tags/github-codespaces/page/5","56e"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/github-codespaces/page/6",component:g("/Cloud-Native/30daysofIA/tags/github-codespaces/page/6","365"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/github-codespaces/page/7",component:g("/Cloud-Native/30daysofIA/tags/github-codespaces/page/7","2cc"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/github-codespaces/page/8",component:g("/Cloud-Native/30daysofIA/tags/github-codespaces/page/8","2cf"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/github-codespaces/page/9",component:g("/Cloud-Native/30daysofIA/tags/github-codespaces/page/9","a06"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/github-copilot",component:g("/Cloud-Native/30daysofIA/tags/github-copilot","081"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/github-copilot/page/2",component:g("/Cloud-Native/30daysofIA/tags/github-copilot/page/2","7a1"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/github-copilot/page/3",component:g("/Cloud-Native/30daysofIA/tags/github-copilot/page/3","d40"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/github-copilot/page/4",component:g("/Cloud-Native/30daysofIA/tags/github-copilot/page/4","584"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/github-copilot/page/5",component:g("/Cloud-Native/30daysofIA/tags/github-copilot/page/5","54e"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/github-copilot/page/6",component:g("/Cloud-Native/30daysofIA/tags/github-copilot/page/6","5af"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/github-copilot/page/7",component:g("/Cloud-Native/30daysofIA/tags/github-copilot/page/7","ef9"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/github-copilot/page/8",component:g("/Cloud-Native/30daysofIA/tags/github-copilot/page/8","333"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/github-copilot/page/9",component:g("/Cloud-Native/30daysofIA/tags/github-copilot/page/9","742"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/hack-together",component:g("/Cloud-Native/30daysofIA/tags/hack-together","b3a"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/hack-together/page/2",component:g("/Cloud-Native/30daysofIA/tags/hack-together/page/2","776"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/hack-together/page/3",component:g("/Cloud-Native/30daysofIA/tags/hack-together/page/3","2c0"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/hack-together/page/4",component:g("/Cloud-Native/30daysofIA/tags/hack-together/page/4","16a"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/hack-together/page/5",component:g("/Cloud-Native/30daysofIA/tags/hack-together/page/5","50d"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/hack-together/page/6",component:g("/Cloud-Native/30daysofIA/tags/hack-together/page/6","90e"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/hack-together/page/7",component:g("/Cloud-Native/30daysofIA/tags/hack-together/page/7","4c1"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/hack-together/page/8",component:g("/Cloud-Native/30daysofIA/tags/hack-together/page/8","894"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/hack-together/page/9",component:g("/Cloud-Native/30daysofIA/tags/hack-together/page/9","b66"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/learn-live",component:g("/Cloud-Native/30daysofIA/tags/learn-live","89c"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/learn-live/page/2",component:g("/Cloud-Native/30daysofIA/tags/learn-live/page/2","978"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/learn-live/page/3",component:g("/Cloud-Native/30daysofIA/tags/learn-live/page/3","465"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/learn-live/page/4",component:g("/Cloud-Native/30daysofIA/tags/learn-live/page/4","7d2"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/learn-live/page/5",component:g("/Cloud-Native/30daysofIA/tags/learn-live/page/5","6e9"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/learn-live/page/6",component:g("/Cloud-Native/30daysofIA/tags/learn-live/page/6","e35"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/learn-live/page/7",component:g("/Cloud-Native/30daysofIA/tags/learn-live/page/7","c0b"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/learn-live/page/8",component:g("/Cloud-Native/30daysofIA/tags/learn-live/page/8","c77"),exact:!0},{path:"/Cloud-Native/30daysofIA/tags/learn-live/page/9",component:g("/Cloud-Native/30daysofIA/tags/learn-live/page/9","0a0"),exact:!0},{path:"/Cloud-Native/blog",component:g("/Cloud-Native/blog","c18"),exact:!0},{path:"/Cloud-Native/blog/01-kickoff",component:g("/Cloud-Native/blog/01-kickoff","f5d"),exact:!0},{path:"/Cloud-Native/blog/02-functions-intro",component:g("/Cloud-Native/blog/02-functions-intro","37c"),exact:!0},{path:"/Cloud-Native/blog/03-functions-quickstart",component:g("/Cloud-Native/blog/03-functions-quickstart","3a1"),exact:!0},{path:"/Cloud-Native/blog/04-functions-java",component:g("/Cloud-Native/blog/04-functions-java","a12"),exact:!0},{path:"/Cloud-Native/blog/05-functions-js",component:g("/Cloud-Native/blog/05-functions-js","ad5"),exact:!0},{path:"/Cloud-Native/blog/06-functions-dotnet",component:g("/Cloud-Native/blog/06-functions-dotnet","98d"),exact:!0},{path:"/Cloud-Native/blog/07-functions-python",component:g("/Cloud-Native/blog/07-functions-python","0ee"),exact:!0},{path:"/Cloud-Native/blog/08-functions-azure",component:g("/Cloud-Native/blog/08-functions-azure","337"),exact:!0},{path:"/Cloud-Native/blog/09-aca-fundamentals",component:g("/Cloud-Native/blog/09-aca-fundamentals","963"),exact:!0},{path:"/Cloud-Native/blog/11-scaling-container-apps",component:g("/Cloud-Native/blog/11-scaling-container-apps","ee5"),exact:!0},{path:"/Cloud-Native/blog/12-build-with-dapr",component:g("/Cloud-Native/blog/12-build-with-dapr","5d3"),exact:!0},{path:"/Cloud-Native/blog/13-aca-managed-id",component:g("/Cloud-Native/blog/13-aca-managed-id","2af"),exact:!0},{path:"/Cloud-Native/blog/14-dapr-aca-quickstart",component:g("/Cloud-Native/blog/14-dapr-aca-quickstart","910"),exact:!0},{path:"/Cloud-Native/blog/15-microservices-azure",component:g("/Cloud-Native/blog/15-microservices-azure","258"),exact:!0},{path:"/Cloud-Native/blog/17-integrate-cosmosdb",component:g("/Cloud-Native/blog/17-integrate-cosmosdb","53b"),exact:!0},{path:"/Cloud-Native/blog/18-cloudmail",component:g("/Cloud-Native/blog/18-cloudmail","015"),exact:!0},{path:"/Cloud-Native/blog/20-events-graph",component:g("/Cloud-Native/blog/20-events-graph","8c6"),exact:!0},{path:"/Cloud-Native/blog/21-cloudevents-via-event-grid",component:g("/Cloud-Native/blog/21-cloudevents-via-event-grid","5ba"),exact:!0},{path:"/Cloud-Native/blog/24-aca-dotnet",component:g("/Cloud-Native/blog/24-aca-dotnet","ce6"),exact:!0},{path:"/Cloud-Native/blog/25-aca-java",component:g("/Cloud-Native/blog/25-aca-java","446"),exact:!0},{path:"/Cloud-Native/blog/28-where-am-i",component:g("/Cloud-Native/blog/28-where-am-i","a1b"),exact:!0},{path:"/Cloud-Native/blog/29-awesome-azd",component:g("/Cloud-Native/blog/29-awesome-azd","a32"),exact:!0},{path:"/Cloud-Native/blog/29-azure-developer-cli",component:g("/Cloud-Native/blog/29-azure-developer-cli","f3b"),exact:!0},{path:"/Cloud-Native/blog/archive",component:g("/Cloud-Native/blog/archive","4d7"),exact:!0},{path:"/Cloud-Native/blog/microservices-10",component:g("/Cloud-Native/blog/microservices-10","ae2"),exact:!0},{path:"/Cloud-Native/blog/page/10",component:g("/Cloud-Native/blog/page/10","58e"),exact:!0},{path:"/Cloud-Native/blog/page/11",component:g("/Cloud-Native/blog/page/11","b71"),exact:!0},{path:"/Cloud-Native/blog/page/12",component:g("/Cloud-Native/blog/page/12","e7f"),exact:!0},{path:"/Cloud-Native/blog/page/13",component:g("/Cloud-Native/blog/page/13","b95"),exact:!0},{path:"/Cloud-Native/blog/page/14",component:g("/Cloud-Native/blog/page/14","804"),exact:!0},{path:"/Cloud-Native/blog/page/15",component:g("/Cloud-Native/blog/page/15","071"),exact:!0},{path:"/Cloud-Native/blog/page/16",component:g("/Cloud-Native/blog/page/16","0ed"),exact:!0},{path:"/Cloud-Native/blog/page/17",component:g("/Cloud-Native/blog/page/17","a8b"),exact:!0},{path:"/Cloud-Native/blog/page/18",component:g("/Cloud-Native/blog/page/18","c7d"),exact:!0},{path:"/Cloud-Native/blog/page/19",component:g("/Cloud-Native/blog/page/19","9ee"),exact:!0},{path:"/Cloud-Native/blog/page/2",component:g("/Cloud-Native/blog/page/2","fcb"),exact:!0},{path:"/Cloud-Native/blog/page/20",component:g("/Cloud-Native/blog/page/20","551"),exact:!0},{path:"/Cloud-Native/blog/page/21",component:g("/Cloud-Native/blog/page/21","f0f"),exact:!0},{path:"/Cloud-Native/blog/page/22",component:g("/Cloud-Native/blog/page/22","cd5"),exact:!0},{path:"/Cloud-Native/blog/page/23",component:g("/Cloud-Native/blog/page/23","480"),exact:!0},{path:"/Cloud-Native/blog/page/24",component:g("/Cloud-Native/blog/page/24","968"),exact:!0},{path:"/Cloud-Native/blog/page/25",component:g("/Cloud-Native/blog/page/25","9a2"),exact:!0},{path:"/Cloud-Native/blog/page/26",component:g("/Cloud-Native/blog/page/26","aa7"),exact:!0},{path:"/Cloud-Native/blog/page/27",component:g("/Cloud-Native/blog/page/27","b9a"),exact:!0},{path:"/Cloud-Native/blog/page/28",component:g("/Cloud-Native/blog/page/28","02e"),exact:!0},{path:"/Cloud-Native/blog/page/29",component:g("/Cloud-Native/blog/page/29","72b"),exact:!0},{path:"/Cloud-Native/blog/page/3",component:g("/Cloud-Native/blog/page/3","3fb"),exact:!0},{path:"/Cloud-Native/blog/page/30",component:g("/Cloud-Native/blog/page/30","589"),exact:!0},{path:"/Cloud-Native/blog/page/31",component:g("/Cloud-Native/blog/page/31","812"),exact:!0},{path:"/Cloud-Native/blog/page/32",component:g("/Cloud-Native/blog/page/32","f43"),exact:!0},{path:"/Cloud-Native/blog/page/33",component:g("/Cloud-Native/blog/page/33","577"),exact:!0},{path:"/Cloud-Native/blog/page/34",component:g("/Cloud-Native/blog/page/34","718"),exact:!0},{path:"/Cloud-Native/blog/page/4",component:g("/Cloud-Native/blog/page/4","e5c"),exact:!0},{path:"/Cloud-Native/blog/page/5",component:g("/Cloud-Native/blog/page/5","305"),exact:!0},{path:"/Cloud-Native/blog/page/6",component:g("/Cloud-Native/blog/page/6","8e4"),exact:!0},{path:"/Cloud-Native/blog/page/7",component:g("/Cloud-Native/blog/page/7","640"),exact:!0},{path:"/Cloud-Native/blog/page/8",component:g("/Cloud-Native/blog/page/8","cb0"),exact:!0},{path:"/Cloud-Native/blog/page/9",component:g("/Cloud-Native/blog/page/9","854"),exact:!0},{path:"/Cloud-Native/blog/serverless-status-post",component:g("/Cloud-Native/blog/serverless-status-post","e28"),exact:!0},{path:"/Cloud-Native/blog/students",component:g("/Cloud-Native/blog/students","4e9"),exact:!0},{path:"/Cloud-Native/blog/tags",component:g("/Cloud-Native/blog/tags","f05"),exact:!0},{path:"/Cloud-Native/blog/tags/30-days-of-serverless",component:g("/Cloud-Native/blog/tags/30-days-of-serverless","44e"),exact:!0},{path:"/Cloud-Native/blog/tags/30-days-of-serverless/page/10",component:g("/Cloud-Native/blog/tags/30-days-of-serverless/page/10","8c8"),exact:!0},{path:"/Cloud-Native/blog/tags/30-days-of-serverless/page/11",component:g("/Cloud-Native/blog/tags/30-days-of-serverless/page/11","43f"),exact:!0},{path:"/Cloud-Native/blog/tags/30-days-of-serverless/page/12",component:g("/Cloud-Native/blog/tags/30-days-of-serverless/page/12","873"),exact:!0},{path:"/Cloud-Native/blog/tags/30-days-of-serverless/page/13",component:g("/Cloud-Native/blog/tags/30-days-of-serverless/page/13","013"),exact:!0},{path:"/Cloud-Native/blog/tags/30-days-of-serverless/page/14",component:g("/Cloud-Native/blog/tags/30-days-of-serverless/page/14","d4f"),exact:!0},{path:"/Cloud-Native/blog/tags/30-days-of-serverless/page/15",component:g("/Cloud-Native/blog/tags/30-days-of-serverless/page/15","d95"),exact:!0},{path:"/Cloud-Native/blog/tags/30-days-of-serverless/page/16",component:g("/Cloud-Native/blog/tags/30-days-of-serverless/page/16","b1f"),exact:!0},{path:"/Cloud-Native/blog/tags/30-days-of-serverless/page/17",component:g("/Cloud-Native/blog/tags/30-days-of-serverless/page/17","15b"),exact:!0},{path:"/Cloud-Native/blog/tags/30-days-of-serverless/page/18",component:g("/Cloud-Native/blog/tags/30-days-of-serverless/page/18","571"),exact:!0},{path:"/Cloud-Native/blog/tags/30-days-of-serverless/page/19",component:g("/Cloud-Native/blog/tags/30-days-of-serverless/page/19","8ee"),exact:!0},{path:"/Cloud-Native/blog/tags/30-days-of-serverless/page/2",component:g("/Cloud-Native/blog/tags/30-days-of-serverless/page/2","e8c"),exact:!0},{path:"/Cloud-Native/blog/tags/30-days-of-serverless/page/20",component:g("/Cloud-Native/blog/tags/30-days-of-serverless/page/20","0b3"),exact:!0},{path:"/Cloud-Native/blog/tags/30-days-of-serverless/page/3",component:g("/Cloud-Native/blog/tags/30-days-of-serverless/page/3","813"),exact:!0},{path:"/Cloud-Native/blog/tags/30-days-of-serverless/page/4",component:g("/Cloud-Native/blog/tags/30-days-of-serverless/page/4","ec1"),exact:!0},{path:"/Cloud-Native/blog/tags/30-days-of-serverless/page/5",component:g("/Cloud-Native/blog/tags/30-days-of-serverless/page/5","f19"),exact:!0},{path:"/Cloud-Native/blog/tags/30-days-of-serverless/page/6",component:g("/Cloud-Native/blog/tags/30-days-of-serverless/page/6","65c"),exact:!0},{path:"/Cloud-Native/blog/tags/30-days-of-serverless/page/7",component:g("/Cloud-Native/blog/tags/30-days-of-serverless/page/7","0d6"),exact:!0},{path:"/Cloud-Native/blog/tags/30-days-of-serverless/page/8",component:g("/Cloud-Native/blog/tags/30-days-of-serverless/page/8","105"),exact:!0},{path:"/Cloud-Native/blog/tags/30-days-of-serverless/page/9",component:g("/Cloud-Native/blog/tags/30-days-of-serverless/page/9","fa9"),exact:!0},{path:"/Cloud-Native/blog/tags/ask-the-expert",component:g("/Cloud-Native/blog/tags/ask-the-expert","617"),exact:!0},{path:"/Cloud-Native/blog/tags/asp-net",component:g("/Cloud-Native/blog/tags/asp-net","90f"),exact:!0},{path:"/Cloud-Native/blog/tags/autoscaling",component:g("/Cloud-Native/blog/tags/autoscaling","9e9"),exact:!0},{path:"/Cloud-Native/blog/tags/azd",component:g("/Cloud-Native/blog/tags/azd","95a"),exact:!0},{path:"/Cloud-Native/blog/tags/azure-container-apps",component:g("/Cloud-Native/blog/tags/azure-container-apps","ad2"),exact:!0},{path:"/Cloud-Native/blog/tags/azure-container-apps/page/10",component:g("/Cloud-Native/blog/tags/azure-container-apps/page/10","f80"),exact:!0},{path:"/Cloud-Native/blog/tags/azure-container-apps/page/11",component:g("/Cloud-Native/blog/tags/azure-container-apps/page/11","76d"),exact:!0},{path:"/Cloud-Native/blog/tags/azure-container-apps/page/12",component:g("/Cloud-Native/blog/tags/azure-container-apps/page/12","02e"),exact:!0},{path:"/Cloud-Native/blog/tags/azure-container-apps/page/13",component:g("/Cloud-Native/blog/tags/azure-container-apps/page/13","03f"),exact:!0},{path:"/Cloud-Native/blog/tags/azure-container-apps/page/14",component:g("/Cloud-Native/blog/tags/azure-container-apps/page/14","4cc"),exact:!0},{path:"/Cloud-Native/blog/tags/azure-container-apps/page/15",component:g("/Cloud-Native/blog/tags/azure-container-apps/page/15","9e7"),exact:!0},{path:"/Cloud-Native/blog/tags/azure-container-apps/page/16",component:g("/Cloud-Native/blog/tags/azure-container-apps/page/16","e34"),exact:!0},{path:"/Cloud-Native/blog/tags/azure-container-apps/page/17",component:g("/Cloud-Native/blog/tags/azure-container-apps/page/17","551"),exact:!0},{path:"/Cloud-Native/blog/tags/azure-container-apps/page/18",component:g("/Cloud-Native/blog/tags/azure-container-apps/page/18","472"),exact:!0},{path:"/Cloud-Native/blog/tags/azure-container-apps/page/19",component:g("/Cloud-Native/blog/tags/azure-container-apps/page/19","d00"),exact:!0},{path:"/Cloud-Native/blog/tags/azure-container-apps/page/2",component:g("/Cloud-Native/blog/tags/azure-container-apps/page/2","c2f"),exact:!0},{path:"/Cloud-Native/blog/tags/azure-container-apps/page/20",component:g("/Cloud-Native/blog/tags/azure-container-apps/page/20","344"),exact:!0},{path:"/Cloud-Native/blog/tags/azure-container-apps/page/3",component:g("/Cloud-Native/blog/tags/azure-container-apps/page/3","8b5"),exact:!0},{path:"/Cloud-Native/blog/tags/azure-container-apps/page/4",component:g("/Cloud-Native/blog/tags/azure-container-apps/page/4","dc5"),exact:!0},{path:"/Cloud-Native/blog/tags/azure-container-apps/page/5",component:g("/Cloud-Native/blog/tags/azure-container-apps/page/5","7c4"),exact:!0},{path:"/Cloud-Native/blog/tags/azure-container-apps/page/6",component:g("/Cloud-Native/blog/tags/azure-container-apps/page/6","e4f"),exact:!0},{path:"/Cloud-Native/blog/tags/azure-container-apps/page/7",component:g("/Cloud-Native/blog/tags/azure-container-apps/page/7","a93"),exact:!0},{path:"/Cloud-Native/blog/tags/azure-container-apps/page/8",component:g("/Cloud-Native/blog/tags/azure-container-apps/page/8","49c"),exact:!0},{path:"/Cloud-Native/blog/tags/azure-container-apps/page/9",component:g("/Cloud-Native/blog/tags/azure-container-apps/page/9","603"),exact:!0},{path:"/Cloud-Native/blog/tags/azure-developer-cli",component:g("/Cloud-Native/blog/tags/azure-developer-cli","352"),exact:!0},{path:"/Cloud-Native/blog/tags/azure-developer-cli/page/2",component:g("/Cloud-Native/blog/tags/azure-developer-cli/page/2","71e"),exact:!0},{path:"/Cloud-Native/blog/tags/azure-event-grid",component:g("/Cloud-Native/blog/tags/azure-event-grid","62c"),exact:!0},{path:"/Cloud-Native/blog/tags/azure-event-grid/page/2",component:g("/Cloud-Native/blog/tags/azure-event-grid/page/2","46f"),exact:!0},{path:"/Cloud-Native/blog/tags/azure-event-grid/page/3",component:g("/Cloud-Native/blog/tags/azure-event-grid/page/3","ccb"),exact:!0},{path:"/Cloud-Native/blog/tags/azure-functions",component:g("/Cloud-Native/blog/tags/azure-functions","932"),exact:!0},{path:"/Cloud-Native/blog/tags/azure-functions/page/10",component:g("/Cloud-Native/blog/tags/azure-functions/page/10","463"),exact:!0},{path:"/Cloud-Native/blog/tags/azure-functions/page/11",component:g("/Cloud-Native/blog/tags/azure-functions/page/11","4f8"),exact:!0},{path:"/Cloud-Native/blog/tags/azure-functions/page/12",component:g("/Cloud-Native/blog/tags/azure-functions/page/12","a38"),exact:!0},{path:"/Cloud-Native/blog/tags/azure-functions/page/13",component:g("/Cloud-Native/blog/tags/azure-functions/page/13","37c"),exact:!0},{path:"/Cloud-Native/blog/tags/azure-functions/page/14",component:g("/Cloud-Native/blog/tags/azure-functions/page/14","d2b"),exact:!0},{path:"/Cloud-Native/blog/tags/azure-functions/page/15",component:g("/Cloud-Native/blog/tags/azure-functions/page/15","871"),exact:!0},{path:"/Cloud-Native/blog/tags/azure-functions/page/16",component:g("/Cloud-Native/blog/tags/azure-functions/page/16","9ba"),exact:!0},{path:"/Cloud-Native/blog/tags/azure-functions/page/2",component:g("/Cloud-Native/blog/tags/azure-functions/page/2","22e"),exact:!0},{path:"/Cloud-Native/blog/tags/azure-functions/page/3",component:g("/Cloud-Native/blog/tags/azure-functions/page/3","080"),exact:!0},{path:"/Cloud-Native/blog/tags/azure-functions/page/4",component:g("/Cloud-Native/blog/tags/azure-functions/page/4","e86"),exact:!0},{path:"/Cloud-Native/blog/tags/azure-functions/page/5",component:g("/Cloud-Native/blog/tags/azure-functions/page/5","caf"),exact:!0},{path:"/Cloud-Native/blog/tags/azure-functions/page/6",component:g("/Cloud-Native/blog/tags/azure-functions/page/6","ed7"),exact:!0},{path:"/Cloud-Native/blog/tags/azure-functions/page/7",component:g("/Cloud-Native/blog/tags/azure-functions/page/7","6cb"),exact:!0},{path:"/Cloud-Native/blog/tags/azure-functions/page/8",component:g("/Cloud-Native/blog/tags/azure-functions/page/8","b98"),exact:!0},{path:"/Cloud-Native/blog/tags/azure-functions/page/9",component:g("/Cloud-Native/blog/tags/azure-functions/page/9","5e3"),exact:!0},{path:"/Cloud-Native/blog/tags/azure-logic-apps",component:g("/Cloud-Native/blog/tags/azure-logic-apps","d0f"),exact:!0},{path:"/Cloud-Native/blog/tags/azure-logic-apps/page/2",component:g("/Cloud-Native/blog/tags/azure-logic-apps/page/2","831"),exact:!0},{path:"/Cloud-Native/blog/tags/azure-logic-apps/page/3",component:g("/Cloud-Native/blog/tags/azure-logic-apps/page/3","77e"),exact:!0},{path:"/Cloud-Native/blog/tags/cloud-native",component:g("/Cloud-Native/blog/tags/cloud-native","966"),exact:!0},{path:"/Cloud-Native/blog/tags/cloudevents",component:g("/Cloud-Native/blog/tags/cloudevents","3ca"),exact:!0},{path:"/Cloud-Native/blog/tags/custom-connector",component:g("/Cloud-Native/blog/tags/custom-connector","736"),exact:!0},{path:"/Cloud-Native/blog/tags/dapr",component:g("/Cloud-Native/blog/tags/dapr","af8"),exact:!0},{path:"/Cloud-Native/blog/tags/dapr/page/10",component:g("/Cloud-Native/blog/tags/dapr/page/10","529"),exact:!0},{path:"/Cloud-Native/blog/tags/dapr/page/11",component:g("/Cloud-Native/blog/tags/dapr/page/11","dce"),exact:!0},{path:"/Cloud-Native/blog/tags/dapr/page/12",component:g("/Cloud-Native/blog/tags/dapr/page/12","971"),exact:!0},{path:"/Cloud-Native/blog/tags/dapr/page/13",component:g("/Cloud-Native/blog/tags/dapr/page/13","f33"),exact:!0},{path:"/Cloud-Native/blog/tags/dapr/page/14",component:g("/Cloud-Native/blog/tags/dapr/page/14","a3a"),exact:!0},{path:"/Cloud-Native/blog/tags/dapr/page/15",component:g("/Cloud-Native/blog/tags/dapr/page/15","2da"),exact:!0},{path:"/Cloud-Native/blog/tags/dapr/page/16",component:g("/Cloud-Native/blog/tags/dapr/page/16","72c"),exact:!0},{path:"/Cloud-Native/blog/tags/dapr/page/2",component:g("/Cloud-Native/blog/tags/dapr/page/2","92e"),exact:!0},{path:"/Cloud-Native/blog/tags/dapr/page/3",component:g("/Cloud-Native/blog/tags/dapr/page/3","53b"),exact:!0},{path:"/Cloud-Native/blog/tags/dapr/page/4",component:g("/Cloud-Native/blog/tags/dapr/page/4","e7b"),exact:!0},{path:"/Cloud-Native/blog/tags/dapr/page/5",component:g("/Cloud-Native/blog/tags/dapr/page/5","162"),exact:!0},{path:"/Cloud-Native/blog/tags/dapr/page/6",component:g("/Cloud-Native/blog/tags/dapr/page/6","e7b"),exact:!0},{path:"/Cloud-Native/blog/tags/dapr/page/7",component:g("/Cloud-Native/blog/tags/dapr/page/7","a70"),exact:!0},{path:"/Cloud-Native/blog/tags/dapr/page/8",component:g("/Cloud-Native/blog/tags/dapr/page/8","60c"),exact:!0},{path:"/Cloud-Native/blog/tags/dapr/page/9",component:g("/Cloud-Native/blog/tags/dapr/page/9","6b3"),exact:!0},{path:"/Cloud-Native/blog/tags/devtools",component:g("/Cloud-Native/blog/tags/devtools","6ed"),exact:!0},{path:"/Cloud-Native/blog/tags/devtools/page/2",component:g("/Cloud-Native/blog/tags/devtools/page/2","f56"),exact:!0},{path:"/Cloud-Native/blog/tags/docker-compose",component:g("/Cloud-Native/blog/tags/docker-compose","888"),exact:!0},{path:"/Cloud-Native/blog/tags/dotnet",component:g("/Cloud-Native/blog/tags/dotnet","2eb"),exact:!0},{path:"/Cloud-Native/blog/tags/dotnet/page/2",component:g("/Cloud-Native/blog/tags/dotnet/page/2","bcd"),exact:!0},{path:"/Cloud-Native/blog/tags/event-hubs",component:g("/Cloud-Native/blog/tags/event-hubs","da7"),exact:!0},{path:"/Cloud-Native/blog/tags/hacktoberfest",component:g("/Cloud-Native/blog/tags/hacktoberfest","fe8"),exact:!0},{path:"/Cloud-Native/blog/tags/hello",component:g("/Cloud-Native/blog/tags/hello","d63"),exact:!0},{path:"/Cloud-Native/blog/tags/hello/page/2",component:g("/Cloud-Native/blog/tags/hello/page/2","0c2"),exact:!0},{path:"/Cloud-Native/blog/tags/java",component:g("/Cloud-Native/blog/tags/java","d1b"),exact:!0},{path:"/Cloud-Native/blog/tags/javascript",component:g("/Cloud-Native/blog/tags/javascript","1da"),exact:!0},{path:"/Cloud-Native/blog/tags/keda",component:g("/Cloud-Native/blog/tags/keda","e74"),exact:!0},{path:"/Cloud-Native/blog/tags/logic-apps",component:g("/Cloud-Native/blog/tags/logic-apps","a83"),exact:!0},{path:"/Cloud-Native/blog/tags/microservices",component:g("/Cloud-Native/blog/tags/microservices","e1c"),exact:!0},{path:"/Cloud-Native/blog/tags/microservices/page/10",component:g("/Cloud-Native/blog/tags/microservices/page/10","777"),exact:!0},{path:"/Cloud-Native/blog/tags/microservices/page/11",component:g("/Cloud-Native/blog/tags/microservices/page/11","1ae"),exact:!0},{path:"/Cloud-Native/blog/tags/microservices/page/2",component:g("/Cloud-Native/blog/tags/microservices/page/2","c81"),exact:!0},{path:"/Cloud-Native/blog/tags/microservices/page/3",component:g("/Cloud-Native/blog/tags/microservices/page/3","df6"),exact:!0},{path:"/Cloud-Native/blog/tags/microservices/page/4",component:g("/Cloud-Native/blog/tags/microservices/page/4","902"),exact:!0},{path:"/Cloud-Native/blog/tags/microservices/page/5",component:g("/Cloud-Native/blog/tags/microservices/page/5","b5d"),exact:!0},{path:"/Cloud-Native/blog/tags/microservices/page/6",component:g("/Cloud-Native/blog/tags/microservices/page/6","c3b"),exact:!0},{path:"/Cloud-Native/blog/tags/microservices/page/7",component:g("/Cloud-Native/blog/tags/microservices/page/7","28e"),exact:!0},{path:"/Cloud-Native/blog/tags/microservices/page/8",component:g("/Cloud-Native/blog/tags/microservices/page/8","c23"),exact:!0},{path:"/Cloud-Native/blog/tags/microservices/page/9",component:g("/Cloud-Native/blog/tags/microservices/page/9","d13"),exact:!0},{path:"/Cloud-Native/blog/tags/microsoft-365",component:g("/Cloud-Native/blog/tags/microsoft-365","54b"),exact:!0},{path:"/Cloud-Native/blog/tags/microsoft-graph",component:g("/Cloud-Native/blog/tags/microsoft-graph","6aa"),exact:!0},{path:"/Cloud-Native/blog/tags/openapi",component:g("/Cloud-Native/blog/tags/openapi","6c7"),exact:!0},{path:"/Cloud-Native/blog/tags/power-platform",component:g("/Cloud-Native/blog/tags/power-platform","33c"),exact:!0},{path:"/Cloud-Native/blog/tags/python",component:g("/Cloud-Native/blog/tags/python","c99"),exact:!0},{path:"/Cloud-Native/blog/tags/serverless",component:g("/Cloud-Native/blog/tags/serverless","ca5"),exact:!0},{path:"/Cloud-Native/blog/tags/serverless-e-2-e",component:g("/Cloud-Native/blog/tags/serverless-e-2-e","05c"),exact:!0},{path:"/Cloud-Native/blog/tags/serverless-hacks",component:g("/Cloud-Native/blog/tags/serverless-hacks","509"),exact:!0},{path:"/Cloud-Native/blog/tags/serverless-september",component:g("/Cloud-Native/blog/tags/serverless-september","a1b"),exact:!0},{path:"/Cloud-Native/blog/tags/serverless-september/page/10",component:g("/Cloud-Native/blog/tags/serverless-september/page/10","f4d"),exact:!0},{path:"/Cloud-Native/blog/tags/serverless-september/page/11",component:g("/Cloud-Native/blog/tags/serverless-september/page/11","6cb"),exact:!0},{path:"/Cloud-Native/blog/tags/serverless-september/page/12",component:g("/Cloud-Native/blog/tags/serverless-september/page/12","efb"),exact:!0},{path:"/Cloud-Native/blog/tags/serverless-september/page/13",component:g("/Cloud-Native/blog/tags/serverless-september/page/13","25d"),exact:!0},{path:"/Cloud-Native/blog/tags/serverless-september/page/14",component:g("/Cloud-Native/blog/tags/serverless-september/page/14","b24"),exact:!0},{path:"/Cloud-Native/blog/tags/serverless-september/page/15",component:g("/Cloud-Native/blog/tags/serverless-september/page/15","a61"),exact:!0},{path:"/Cloud-Native/blog/tags/serverless-september/page/16",component:g("/Cloud-Native/blog/tags/serverless-september/page/16","8ee"),exact:!0},{path:"/Cloud-Native/blog/tags/serverless-september/page/17",component:g("/Cloud-Native/blog/tags/serverless-september/page/17","062"),exact:!0},{path:"/Cloud-Native/blog/tags/serverless-september/page/18",component:g("/Cloud-Native/blog/tags/serverless-september/page/18","0e8"),exact:!0},{path:"/Cloud-Native/blog/tags/serverless-september/page/19",component:g("/Cloud-Native/blog/tags/serverless-september/page/19","232"),exact:!0},{path:"/Cloud-Native/blog/tags/serverless-september/page/2",component:g("/Cloud-Native/blog/tags/serverless-september/page/2","e7d"),exact:!0},{path:"/Cloud-Native/blog/tags/serverless-september/page/20",component:g("/Cloud-Native/blog/tags/serverless-september/page/20","9fc"),exact:!0},{path:"/Cloud-Native/blog/tags/serverless-september/page/21",component:g("/Cloud-Native/blog/tags/serverless-september/page/21","df4"),exact:!0},{path:"/Cloud-Native/blog/tags/serverless-september/page/22",component:g("/Cloud-Native/blog/tags/serverless-september/page/22","817"),exact:!0},{path:"/Cloud-Native/blog/tags/serverless-september/page/23",component:g("/Cloud-Native/blog/tags/serverless-september/page/23","c0e"),exact:!0},{path:"/Cloud-Native/blog/tags/serverless-september/page/24",component:g("/Cloud-Native/blog/tags/serverless-september/page/24","9bb"),exact:!0},{path:"/Cloud-Native/blog/tags/serverless-september/page/25",component:g("/Cloud-Native/blog/tags/serverless-september/page/25","dc2"),exact:!0},{path:"/Cloud-Native/blog/tags/serverless-september/page/26",component:g("/Cloud-Native/blog/tags/serverless-september/page/26","52f"),exact:!0},{path:"/Cloud-Native/blog/tags/serverless-september/page/27",component:g("/Cloud-Native/blog/tags/serverless-september/page/27","8e9"),exact:!0},{path:"/Cloud-Native/blog/tags/serverless-september/page/28",component:g("/Cloud-Native/blog/tags/serverless-september/page/28","245"),exact:!0},{path:"/Cloud-Native/blog/tags/serverless-september/page/29",component:g("/Cloud-Native/blog/tags/serverless-september/page/29","5dd"),exact:!0},{path:"/Cloud-Native/blog/tags/serverless-september/page/3",component:g("/Cloud-Native/blog/tags/serverless-september/page/3","e66"),exact:!0},{path:"/Cloud-Native/blog/tags/serverless-september/page/30",component:g("/Cloud-Native/blog/tags/serverless-september/page/30","af5"),exact:!0},{path:"/Cloud-Native/blog/tags/serverless-september/page/31",component:g("/Cloud-Native/blog/tags/serverless-september/page/31","a5a"),exact:!0},{path:"/Cloud-Native/blog/tags/serverless-september/page/32",component:g("/Cloud-Native/blog/tags/serverless-september/page/32","921"),exact:!0},{path:"/Cloud-Native/blog/tags/serverless-september/page/33",component:g("/Cloud-Native/blog/tags/serverless-september/page/33","e0e"),exact:!0},{path:"/Cloud-Native/blog/tags/serverless-september/page/4",component:g("/Cloud-Native/blog/tags/serverless-september/page/4","a5e"),exact:!0},{path:"/Cloud-Native/blog/tags/serverless-september/page/5",component:g("/Cloud-Native/blog/tags/serverless-september/page/5","069"),exact:!0},{path:"/Cloud-Native/blog/tags/serverless-september/page/6",component:g("/Cloud-Native/blog/tags/serverless-september/page/6","ced"),exact:!0},{path:"/Cloud-Native/blog/tags/serverless-september/page/7",component:g("/Cloud-Native/blog/tags/serverless-september/page/7","e96"),exact:!0},{path:"/Cloud-Native/blog/tags/serverless-september/page/8",component:g("/Cloud-Native/blog/tags/serverless-september/page/8","caf"),exact:!0},{path:"/Cloud-Native/blog/tags/serverless-september/page/9",component:g("/Cloud-Native/blog/tags/serverless-september/page/9","bba"),exact:!0},{path:"/Cloud-Native/blog/tags/students",component:g("/Cloud-Native/blog/tags/students","3d2"),exact:!0},{path:"/Cloud-Native/blog/tags/vscode",component:g("/Cloud-Native/blog/tags/vscode","95a"),exact:!0},{path:"/Cloud-Native/blog/tags/zero-to-hero",component:g("/Cloud-Native/blog/tags/zero-to-hero","ac4"),exact:!0},{path:"/Cloud-Native/blog/tags/zero-to-hero/page/2",component:g("/Cloud-Native/blog/tags/zero-to-hero/page/2","f9d"),exact:!0},{path:"/Cloud-Native/blog/tags/zero-to-hero/page/3",component:g("/Cloud-Native/blog/tags/zero-to-hero/page/3","9f9"),exact:!0},{path:"/Cloud-Native/blog/tags/zero-to-hero/page/4",component:g("/Cloud-Native/blog/tags/zero-to-hero/page/4","1a0"),exact:!0},{path:"/Cloud-Native/blog/tags/zero-to-hero/page/5",component:g("/Cloud-Native/blog/tags/zero-to-hero/page/5","bb3"),exact:!0},{path:"/Cloud-Native/blog/tags/zero-to-hero/page/6",component:g("/Cloud-Native/blog/tags/zero-to-hero/page/6","a2b"),exact:!0},{path:"/Cloud-Native/blog/tags/zero-to-hero/page/7",component:g("/Cloud-Native/blog/tags/zero-to-hero/page/7","7b8"),exact:!0},{path:"/Cloud-Native/blog/tags/zero-to-hero/page/8",component:g("/Cloud-Native/blog/tags/zero-to-hero/page/8","1fb"),exact:!0},{path:"/Cloud-Native/blog/welcome",component:g("/Cloud-Native/blog/welcome","cf3"),exact:!0},{path:"/Cloud-Native/blog/zero2hero-aca-01",component:g("/Cloud-Native/blog/zero2hero-aca-01","26f"),exact:!0},{path:"/Cloud-Native/blog/zero2hero-aca-04",component:g("/Cloud-Native/blog/zero2hero-aca-04","5e6"),exact:!0},{path:"/Cloud-Native/blog/zero2hero-aca-06",component:g("/Cloud-Native/blog/zero2hero-aca-06","b2c"),exact:!0},{path:"/Cloud-Native/blog/zero2hero-func-02",component:g("/Cloud-Native/blog/zero2hero-func-02","ca2"),exact:!0},{path:"/Cloud-Native/blog/zero2hero-func-03",component:g("/Cloud-Native/blog/zero2hero-func-03","18d"),exact:!0},{path:"/Cloud-Native/blog/zero2hero-func-05",component:g("/Cloud-Native/blog/zero2hero-func-05","b0d"),exact:!0},{path:"/Cloud-Native/blog/zero2hero-func-07",component:g("/Cloud-Native/blog/zero2hero-func-07","e64"),exact:!0},{path:"/Cloud-Native/calendar",component:g("/Cloud-Native/calendar","270"),exact:!0},{path:"/Cloud-Native/cnny-2023",component:g("/Cloud-Native/cnny-2023","c6a"),exact:!0},{path:"/Cloud-Native/cnny-2023/aks-extensions-addons",component:g("/Cloud-Native/cnny-2023/aks-extensions-addons","4cd"),exact:!0},{path:"/Cloud-Native/cnny-2023/archive",component:g("/Cloud-Native/cnny-2023/archive","cc4"),exact:!0},{path:"/Cloud-Native/cnny-2023/bring-your-app-day-1",component:g("/Cloud-Native/cnny-2023/bring-your-app-day-1","38b"),exact:!0},{path:"/Cloud-Native/cnny-2023/bring-your-app-day-2",component:g("/Cloud-Native/cnny-2023/bring-your-app-day-2","505"),exact:!0},{path:"/Cloud-Native/cnny-2023/bring-your-app-day-3",component:g("/Cloud-Native/cnny-2023/bring-your-app-day-3","95b"),exact:!0},{path:"/Cloud-Native/cnny-2023/bring-your-app-day-4",component:g("/Cloud-Native/cnny-2023/bring-your-app-day-4","460"),exact:!0},{path:"/Cloud-Native/cnny-2023/bring-your-app-day-5",component:g("/Cloud-Native/cnny-2023/bring-your-app-day-5","a7e"),exact:!0},{path:"/Cloud-Native/cnny-2023/building-with-draft",component:g("/Cloud-Native/cnny-2023/building-with-draft","86d"),exact:!0},{path:"/Cloud-Native/cnny-2023/cloud-native-fundamentals",component:g("/Cloud-Native/cnny-2023/cloud-native-fundamentals","560"),exact:!0},{path:"/Cloud-Native/cnny-2023/cnny-kickoff",component:g("/Cloud-Native/cnny-2023/cnny-kickoff","e38"),exact:!0},{path:"/Cloud-Native/cnny-2023/cnny-wrap-up",component:g("/Cloud-Native/cnny-2023/cnny-wrap-up","59e"),exact:!0},{path:"/Cloud-Native/cnny-2023/containers-101",component:g("/Cloud-Native/cnny-2023/containers-101","e13"),exact:!0},{path:"/Cloud-Native/cnny-2023/explore-options",component:g("/Cloud-Native/cnny-2023/explore-options","46d"),exact:!0},{path:"/Cloud-Native/cnny-2023/fundamentals-day-1",component:g("/Cloud-Native/cnny-2023/fundamentals-day-1","dde"),exact:!0},{path:"/Cloud-Native/cnny-2023/fundamentals-day-2",component:g("/Cloud-Native/cnny-2023/fundamentals-day-2","d82"),exact:!0},{path:"/Cloud-Native/cnny-2023/fundamentals-day-3",component:g("/Cloud-Native/cnny-2023/fundamentals-day-3","7e1"),exact:!0},{path:"/Cloud-Native/cnny-2023/fundamentals-day-4",component:g("/Cloud-Native/cnny-2023/fundamentals-day-4","13e"),exact:!0},{path:"/Cloud-Native/cnny-2023/fundamentals-day-5",component:g("/Cloud-Native/cnny-2023/fundamentals-day-5","4d9"),exact:!0},{path:"/Cloud-Native/cnny-2023/Kubernetes-101",component:g("/Cloud-Native/cnny-2023/Kubernetes-101","d28"),exact:!0},{path:"/Cloud-Native/cnny-2023/microservices-101",component:g("/Cloud-Native/cnny-2023/microservices-101","39b"),exact:!0},{path:"/Cloud-Native/cnny-2023/page/10",component:g("/Cloud-Native/cnny-2023/page/10","bc3"),exact:!0},{path:"/Cloud-Native/cnny-2023/page/11",component:g("/Cloud-Native/cnny-2023/page/11","d56"),exact:!0},{path:"/Cloud-Native/cnny-2023/page/12",component:g("/Cloud-Native/cnny-2023/page/12","20e"),exact:!0},{path:"/Cloud-Native/cnny-2023/page/13",component:g("/Cloud-Native/cnny-2023/page/13","d89"),exact:!0},{path:"/Cloud-Native/cnny-2023/page/14",component:g("/Cloud-Native/cnny-2023/page/14","666"),exact:!0},{path:"/Cloud-Native/cnny-2023/page/15",component:g("/Cloud-Native/cnny-2023/page/15","877"),exact:!0},{path:"/Cloud-Native/cnny-2023/page/16",component:g("/Cloud-Native/cnny-2023/page/16","c6e"),exact:!0},{path:"/Cloud-Native/cnny-2023/page/17",component:g("/Cloud-Native/cnny-2023/page/17","c39"),exact:!0},{path:"/Cloud-Native/cnny-2023/page/18",component:g("/Cloud-Native/cnny-2023/page/18","53a"),exact:!0},{path:"/Cloud-Native/cnny-2023/page/19",component:g("/Cloud-Native/cnny-2023/page/19","545"),exact:!0},{path:"/Cloud-Native/cnny-2023/page/2",component:g("/Cloud-Native/cnny-2023/page/2","965"),exact:!0},{path:"/Cloud-Native/cnny-2023/page/20",component:g("/Cloud-Native/cnny-2023/page/20","1ae"),exact:!0},{path:"/Cloud-Native/cnny-2023/page/21",component:g("/Cloud-Native/cnny-2023/page/21","45d"),exact:!0},{path:"/Cloud-Native/cnny-2023/page/3",component:g("/Cloud-Native/cnny-2023/page/3","36b"),exact:!0},{path:"/Cloud-Native/cnny-2023/page/4",component:g("/Cloud-Native/cnny-2023/page/4","337"),exact:!0},{path:"/Cloud-Native/cnny-2023/page/5",component:g("/Cloud-Native/cnny-2023/page/5","058"),exact:!0},{path:"/Cloud-Native/cnny-2023/page/6",component:g("/Cloud-Native/cnny-2023/page/6","fd1"),exact:!0},{path:"/Cloud-Native/cnny-2023/page/7",component:g("/Cloud-Native/cnny-2023/page/7","43e"),exact:!0},{path:"/Cloud-Native/cnny-2023/page/8",component:g("/Cloud-Native/cnny-2023/page/8","4be"),exact:!0},{path:"/Cloud-Native/cnny-2023/page/9",component:g("/Cloud-Native/cnny-2023/page/9","fba"),exact:!0},{path:"/Cloud-Native/cnny-2023/serverless-containers",component:g("/Cloud-Native/cnny-2023/serverless-containers","a74"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags",component:g("/Cloud-Native/cnny-2023/tags","bab"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/30-daysofcloudnative",component:g("/Cloud-Native/cnny-2023/tags/30-daysofcloudnative","e28"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/30-daysofcloudnative/page/10",component:g("/Cloud-Native/cnny-2023/tags/30-daysofcloudnative/page/10","3d8"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/30-daysofcloudnative/page/11",component:g("/Cloud-Native/cnny-2023/tags/30-daysofcloudnative/page/11","548"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/30-daysofcloudnative/page/12",component:g("/Cloud-Native/cnny-2023/tags/30-daysofcloudnative/page/12","e1c"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/30-daysofcloudnative/page/13",component:g("/Cloud-Native/cnny-2023/tags/30-daysofcloudnative/page/13","2bc"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/30-daysofcloudnative/page/14",component:g("/Cloud-Native/cnny-2023/tags/30-daysofcloudnative/page/14","fe0"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/30-daysofcloudnative/page/15",component:g("/Cloud-Native/cnny-2023/tags/30-daysofcloudnative/page/15","c71"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/30-daysofcloudnative/page/16",component:g("/Cloud-Native/cnny-2023/tags/30-daysofcloudnative/page/16","1ee"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/30-daysofcloudnative/page/2",component:g("/Cloud-Native/cnny-2023/tags/30-daysofcloudnative/page/2","99f"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/30-daysofcloudnative/page/3",component:g("/Cloud-Native/cnny-2023/tags/30-daysofcloudnative/page/3","4e5"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/30-daysofcloudnative/page/4",component:g("/Cloud-Native/cnny-2023/tags/30-daysofcloudnative/page/4","795"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/30-daysofcloudnative/page/5",component:g("/Cloud-Native/cnny-2023/tags/30-daysofcloudnative/page/5","96c"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/30-daysofcloudnative/page/6",component:g("/Cloud-Native/cnny-2023/tags/30-daysofcloudnative/page/6","32d"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/30-daysofcloudnative/page/7",component:g("/Cloud-Native/cnny-2023/tags/30-daysofcloudnative/page/7","d86"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/30-daysofcloudnative/page/8",component:g("/Cloud-Native/cnny-2023/tags/30-daysofcloudnative/page/8","3ab"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/30-daysofcloudnative/page/9",component:g("/Cloud-Native/cnny-2023/tags/30-daysofcloudnative/page/9","a54"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/addons",component:g("/Cloud-Native/cnny-2023/tags/addons","4ef"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/aks",component:g("/Cloud-Native/cnny-2023/tags/aks","517"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/aks/page/2",component:g("/Cloud-Native/cnny-2023/tags/aks/page/2","b50"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/aks/page/3",component:g("/Cloud-Native/cnny-2023/tags/aks/page/3","6a0"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/aks/page/4",component:g("/Cloud-Native/cnny-2023/tags/aks/page/4","ff1"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/aks/page/5",component:g("/Cloud-Native/cnny-2023/tags/aks/page/5","c79"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/ask-the-expert",component:g("/Cloud-Native/cnny-2023/tags/ask-the-expert","cc6"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/ask-the-expert/page/10",component:g("/Cloud-Native/cnny-2023/tags/ask-the-expert/page/10","123"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/ask-the-expert/page/11",component:g("/Cloud-Native/cnny-2023/tags/ask-the-expert/page/11","8a5"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/ask-the-expert/page/12",component:g("/Cloud-Native/cnny-2023/tags/ask-the-expert/page/12","dbc"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/ask-the-expert/page/13",component:g("/Cloud-Native/cnny-2023/tags/ask-the-expert/page/13","2ca"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/ask-the-expert/page/14",component:g("/Cloud-Native/cnny-2023/tags/ask-the-expert/page/14","610"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/ask-the-expert/page/15",component:g("/Cloud-Native/cnny-2023/tags/ask-the-expert/page/15","e77"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/ask-the-expert/page/16",component:g("/Cloud-Native/cnny-2023/tags/ask-the-expert/page/16","3ad"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/ask-the-expert/page/2",component:g("/Cloud-Native/cnny-2023/tags/ask-the-expert/page/2","ac2"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/ask-the-expert/page/3",component:g("/Cloud-Native/cnny-2023/tags/ask-the-expert/page/3","16b"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/ask-the-expert/page/4",component:g("/Cloud-Native/cnny-2023/tags/ask-the-expert/page/4","fe0"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/ask-the-expert/page/5",component:g("/Cloud-Native/cnny-2023/tags/ask-the-expert/page/5","4ed"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/ask-the-expert/page/6",component:g("/Cloud-Native/cnny-2023/tags/ask-the-expert/page/6","af6"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/ask-the-expert/page/7",component:g("/Cloud-Native/cnny-2023/tags/ask-the-expert/page/7","551"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/ask-the-expert/page/8",component:g("/Cloud-Native/cnny-2023/tags/ask-the-expert/page/8","fe3"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/ask-the-expert/page/9",component:g("/Cloud-Native/cnny-2023/tags/ask-the-expert/page/9","f6a"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/azure-dns",component:g("/Cloud-Native/cnny-2023/tags/azure-dns","08b"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/azure-key-vault",component:g("/Cloud-Native/cnny-2023/tags/azure-key-vault","d89"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/azure-key-vault/page/2",component:g("/Cloud-Native/cnny-2023/tags/azure-key-vault/page/2","896"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/azure-kubernetes-service",component:g("/Cloud-Native/cnny-2023/tags/azure-kubernetes-service","fed"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/azure-kubernetes-service/page/10",component:g("/Cloud-Native/cnny-2023/tags/azure-kubernetes-service/page/10","ed3"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/azure-kubernetes-service/page/11",component:g("/Cloud-Native/cnny-2023/tags/azure-kubernetes-service/page/11","d2d"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/azure-kubernetes-service/page/12",component:g("/Cloud-Native/cnny-2023/tags/azure-kubernetes-service/page/12","33e"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/azure-kubernetes-service/page/13",component:g("/Cloud-Native/cnny-2023/tags/azure-kubernetes-service/page/13","1e8"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/azure-kubernetes-service/page/14",component:g("/Cloud-Native/cnny-2023/tags/azure-kubernetes-service/page/14","b20"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/azure-kubernetes-service/page/15",component:g("/Cloud-Native/cnny-2023/tags/azure-kubernetes-service/page/15","746"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/azure-kubernetes-service/page/16",component:g("/Cloud-Native/cnny-2023/tags/azure-kubernetes-service/page/16","8ac"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/azure-kubernetes-service/page/17",component:g("/Cloud-Native/cnny-2023/tags/azure-kubernetes-service/page/17","ab4"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/azure-kubernetes-service/page/18",component:g("/Cloud-Native/cnny-2023/tags/azure-kubernetes-service/page/18","4eb"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/azure-kubernetes-service/page/19",component:g("/Cloud-Native/cnny-2023/tags/azure-kubernetes-service/page/19","6ba"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/azure-kubernetes-service/page/2",component:g("/Cloud-Native/cnny-2023/tags/azure-kubernetes-service/page/2","5f1"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/azure-kubernetes-service/page/20",component:g("/Cloud-Native/cnny-2023/tags/azure-kubernetes-service/page/20","795"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/azure-kubernetes-service/page/21",component:g("/Cloud-Native/cnny-2023/tags/azure-kubernetes-service/page/21","684"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/azure-kubernetes-service/page/3",component:g("/Cloud-Native/cnny-2023/tags/azure-kubernetes-service/page/3","1ad"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/azure-kubernetes-service/page/4",component:g("/Cloud-Native/cnny-2023/tags/azure-kubernetes-service/page/4","237"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/azure-kubernetes-service/page/5",component:g("/Cloud-Native/cnny-2023/tags/azure-kubernetes-service/page/5","ee0"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/azure-kubernetes-service/page/6",component:g("/Cloud-Native/cnny-2023/tags/azure-kubernetes-service/page/6","1f2"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/azure-kubernetes-service/page/7",component:g("/Cloud-Native/cnny-2023/tags/azure-kubernetes-service/page/7","fd6"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/azure-kubernetes-service/page/8",component:g("/Cloud-Native/cnny-2023/tags/azure-kubernetes-service/page/8","999"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/azure-kubernetes-service/page/9",component:g("/Cloud-Native/cnny-2023/tags/azure-kubernetes-service/page/9","6f5"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/cloud-native",component:g("/Cloud-Native/cnny-2023/tags/cloud-native","93a"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/cloud-native-new-year",component:g("/Cloud-Native/cnny-2023/tags/cloud-native-new-year","8fa"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/cloud-native-new-year/page/2",component:g("/Cloud-Native/cnny-2023/tags/cloud-native-new-year/page/2","5a6"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/cloud-native-new-year/page/3",component:g("/Cloud-Native/cnny-2023/tags/cloud-native-new-year/page/3","a4f"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/cloud-native-new-year/page/4",component:g("/Cloud-Native/cnny-2023/tags/cloud-native-new-year/page/4","04f"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/cloud-native-new-year/page/5",component:g("/Cloud-Native/cnny-2023/tags/cloud-native-new-year/page/5","220"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/cloud-native/page/10",component:g("/Cloud-Native/cnny-2023/tags/cloud-native/page/10","036"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/cloud-native/page/11",component:g("/Cloud-Native/cnny-2023/tags/cloud-native/page/11","68d"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/cloud-native/page/12",component:g("/Cloud-Native/cnny-2023/tags/cloud-native/page/12","393"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/cloud-native/page/13",component:g("/Cloud-Native/cnny-2023/tags/cloud-native/page/13","8c9"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/cloud-native/page/14",component:g("/Cloud-Native/cnny-2023/tags/cloud-native/page/14","2f4"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/cloud-native/page/15",component:g("/Cloud-Native/cnny-2023/tags/cloud-native/page/15","078"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/cloud-native/page/16",component:g("/Cloud-Native/cnny-2023/tags/cloud-native/page/16","4d0"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/cloud-native/page/2",component:g("/Cloud-Native/cnny-2023/tags/cloud-native/page/2","9a6"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/cloud-native/page/3",component:g("/Cloud-Native/cnny-2023/tags/cloud-native/page/3","828"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/cloud-native/page/4",component:g("/Cloud-Native/cnny-2023/tags/cloud-native/page/4","50b"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/cloud-native/page/5",component:g("/Cloud-Native/cnny-2023/tags/cloud-native/page/5","2b7"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/cloud-native/page/6",component:g("/Cloud-Native/cnny-2023/tags/cloud-native/page/6","d70"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/cloud-native/page/7",component:g("/Cloud-Native/cnny-2023/tags/cloud-native/page/7","f5b"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/cloud-native/page/8",component:g("/Cloud-Native/cnny-2023/tags/cloud-native/page/8","9eb"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/cloud-native/page/9",component:g("/Cloud-Native/cnny-2023/tags/cloud-native/page/9","443"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/configmaps",component:g("/Cloud-Native/cnny-2023/tags/configmaps","4c8"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/containers",component:g("/Cloud-Native/cnny-2023/tags/containers","c8b"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/containers/page/2",component:g("/Cloud-Native/cnny-2023/tags/containers/page/2","504"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/containers/page/3",component:g("/Cloud-Native/cnny-2023/tags/containers/page/3","57e"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/extensions",component:g("/Cloud-Native/cnny-2023/tags/extensions","efc"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/ingress",component:g("/Cloud-Native/cnny-2023/tags/ingress","211"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/ingress/page/2",component:g("/Cloud-Native/cnny-2023/tags/ingress/page/2","e9c"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/kubernetes",component:g("/Cloud-Native/cnny-2023/tags/kubernetes","e96"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/kubernetes/page/2",component:g("/Cloud-Native/cnny-2023/tags/kubernetes/page/2","63e"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/kubernetes/page/3",component:g("/Cloud-Native/cnny-2023/tags/kubernetes/page/3","939"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/kubernetes/page/4",component:g("/Cloud-Native/cnny-2023/tags/kubernetes/page/4","6a6"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/kubernetes/page/5",component:g("/Cloud-Native/cnny-2023/tags/kubernetes/page/5","158"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/microservices",component:g("/Cloud-Native/cnny-2023/tags/microservices","af7"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/nginx-ingress-controller",component:g("/Cloud-Native/cnny-2023/tags/nginx-ingress-controller","9c7"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/notary",component:g("/Cloud-Native/cnny-2023/tags/notary","796"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/notation",component:g("/Cloud-Native/cnny-2023/tags/notation","522"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/persistent-storage",component:g("/Cloud-Native/cnny-2023/tags/persistent-storage","7b9"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/persistent-volume-claims",component:g("/Cloud-Native/cnny-2023/tags/persistent-volume-claims","407"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/persistent-volumes",component:g("/Cloud-Native/cnny-2023/tags/persistent-volumes","e21"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/secrets-management",component:g("/Cloud-Native/cnny-2023/tags/secrets-management","629"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/secure-supply-chain",component:g("/Cloud-Native/cnny-2023/tags/secure-supply-chain","bd4"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/service",component:g("/Cloud-Native/cnny-2023/tags/service","74e"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/windows",component:g("/Cloud-Native/cnny-2023/tags/windows","242"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/workload-identity",component:g("/Cloud-Native/cnny-2023/tags/workload-identity","573"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/zero-to-hero",component:g("/Cloud-Native/cnny-2023/tags/zero-to-hero","e5a"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/zero-to-hero/page/10",component:g("/Cloud-Native/cnny-2023/tags/zero-to-hero/page/10","e09"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/zero-to-hero/page/11",component:g("/Cloud-Native/cnny-2023/tags/zero-to-hero/page/11","45e"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/zero-to-hero/page/12",component:g("/Cloud-Native/cnny-2023/tags/zero-to-hero/page/12","d91"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/zero-to-hero/page/13",component:g("/Cloud-Native/cnny-2023/tags/zero-to-hero/page/13","53f"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/zero-to-hero/page/14",component:g("/Cloud-Native/cnny-2023/tags/zero-to-hero/page/14","280"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/zero-to-hero/page/15",component:g("/Cloud-Native/cnny-2023/tags/zero-to-hero/page/15","0f5"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/zero-to-hero/page/16",component:g("/Cloud-Native/cnny-2023/tags/zero-to-hero/page/16","b19"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/zero-to-hero/page/2",component:g("/Cloud-Native/cnny-2023/tags/zero-to-hero/page/2","faa"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/zero-to-hero/page/3",component:g("/Cloud-Native/cnny-2023/tags/zero-to-hero/page/3","9cb"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/zero-to-hero/page/4",component:g("/Cloud-Native/cnny-2023/tags/zero-to-hero/page/4","db7"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/zero-to-hero/page/5",component:g("/Cloud-Native/cnny-2023/tags/zero-to-hero/page/5","384"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/zero-to-hero/page/6",component:g("/Cloud-Native/cnny-2023/tags/zero-to-hero/page/6","ff7"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/zero-to-hero/page/7",component:g("/Cloud-Native/cnny-2023/tags/zero-to-hero/page/7","9d6"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/zero-to-hero/page/8",component:g("/Cloud-Native/cnny-2023/tags/zero-to-hero/page/8","a84"),exact:!0},{path:"/Cloud-Native/cnny-2023/tags/zero-to-hero/page/9",component:g("/Cloud-Native/cnny-2023/tags/zero-to-hero/page/9","cdd"),exact:!0},{path:"/Cloud-Native/cnny-2023/windows-containers",component:g("/Cloud-Native/cnny-2023/windows-containers","276"),exact:!0},{path:"/Cloud-Native/Fall-For-IA/",component:g("/Cloud-Native/Fall-For-IA/","85b"),exact:!0},{path:"/Cloud-Native/Fall-For-IA/AskTheExpert",component:g("/Cloud-Native/Fall-For-IA/AskTheExpert","e7f"),exact:!0},{path:"/Cloud-Native/Fall-For-IA/calendar",component:g("/Cloud-Native/Fall-For-IA/calendar","d50"),exact:!0},{path:"/Cloud-Native/Fall-For-IA/CloudSkills",component:g("/Cloud-Native/Fall-For-IA/CloudSkills","4bc"),exact:!0},{path:"/Cloud-Native/Fall-For-IA/CommunityGallery",component:g("/Cloud-Native/Fall-For-IA/CommunityGallery","a99"),exact:!0},{path:"/Cloud-Native/Fall-For-IA/HackTogether",component:g("/Cloud-Native/Fall-For-IA/HackTogether","720"),exact:!0},{path:"/Cloud-Native/Fall-For-IA/LearnLive",component:g("/Cloud-Native/Fall-For-IA/LearnLive","c56"),exact:!0},{path:"/Cloud-Native/New-Year/",component:g("/Cloud-Native/New-Year/","790"),exact:!0},{path:"/Cloud-Native/New-Year/ate",component:g("/Cloud-Native/New-Year/ate","2ec"),exact:!0},{path:"/Cloud-Native/New-Year/calendar",component:g("/Cloud-Native/New-Year/calendar","65a"),exact:!0},{path:"/Cloud-Native/serverless-september/",component:g("/Cloud-Native/serverless-september/","e79"),exact:!0},{path:"/Cloud-Native/serverless-september/30DaysOfServerless",component:g("/Cloud-Native/serverless-september/30DaysOfServerless","81c"),exact:!0},{path:"/Cloud-Native/serverless-september/AskTheExpert",component:g("/Cloud-Native/serverless-september/AskTheExpert","cdf"),exact:!0},{path:"/Cloud-Native/serverless-september/CloudSkills",component:g("/Cloud-Native/serverless-september/CloudSkills","8cc"),exact:!0},{path:"/Cloud-Native/serverless-september/CommunityBuzz",component:g("/Cloud-Native/serverless-september/CommunityBuzz","169"),exact:!0},{path:"/Cloud-Native/serverless-september/ServerlessHacks",component:g("/Cloud-Native/serverless-september/ServerlessHacks","e24"),exact:!0},{path:"/Cloud-Native/serverless-september/ZeroToHero",component:g("/Cloud-Native/serverless-september/ZeroToHero","25c"),exact:!0},{path:"/Cloud-Native/docs",component:g("/Cloud-Native/docs","172"),routes:[{path:"/Cloud-Native/docs/category/resources",component:g("/Cloud-Native/docs/category/resources","fdb"),exact:!0,sidebar:"tutorialSidebar"},{path:"/Cloud-Native/docs/category/videos",component:g("/Cloud-Native/docs/category/videos","258"),exact:!0,sidebar:"tutorialSidebar"},{path:"/Cloud-Native/docs/resources/devtools",component:g("/Cloud-Native/docs/resources/devtools","d9d"),exact:!0,sidebar:"tutorialSidebar"},{path:"/Cloud-Native/docs/resources/intro",component:g("/Cloud-Native/docs/resources/intro","5a1"),exact:!0,sidebar:"tutorialSidebar"},{path:"/Cloud-Native/docs/resources/languages",component:g("/Cloud-Native/docs/resources/languages","981"),exact:!0,sidebar:"tutorialSidebar"},{path:"/Cloud-Native/docs/resources/serverless",component:g("/Cloud-Native/docs/resources/serverless","577"),exact:!0,sidebar:"tutorialSidebar"},{path:"/Cloud-Native/docs/videos/intro",component:g("/Cloud-Native/docs/videos/intro","5d9"),exact:!0,sidebar:"tutorialSidebar"}]},{path:"/Cloud-Native/",component:g("/Cloud-Native/","3a8"),exact:!0},{path:"*",component:g("*")}]},88121:(e,t,a)=>{"use strict";a.d(t,{_:()=>o,t:()=>i});var n=a(67294);const o=n.createContext(!1);function i(e){let{children:t}=e;const[a,i]=(0,n.useState)(!1);return(0,n.useEffect)((()=>{i(!0)}),[]),n.createElement(o.Provider,{value:a},t)}},90654:(e,t,a)=>{"use strict";var n=a(67294),o=a(73935),i=a(73727),l=a(70405),r=a(36136);const s=[a(30984),a(82251),a(79957),a(46930)];var c=a(50997),d=a(76775),u=a(18790);function g(e){let{children:t}=e;return n.createElement(n.Fragment,null,t)}var p=a(87462),f=a(31514),b=a(39962),m=a(79524),v=a(20107),h=a(35463),y=a(40626),_=a(78181),C=a(60246),N=a(33647);function x(){const{i18n:{defaultLocale:e,localeConfigs:t}}=(0,b.Z)(),a=(0,y.l)();return n.createElement(f.Z,null,Object.entries(t).map((e=>{let[t,{htmlLang:o}]=e;return n.createElement("link",{key:t,rel:"alternate",href:a.createUrl({locale:t,fullyQualified:!0}),hrefLang:o})})),n.createElement("link",{rel:"alternate",href:a.createUrl({locale:e,fullyQualified:!0}),hrefLang:"x-default"}))}function k(e){let{permalink:t}=e;const{siteConfig:{url:a}}=(0,b.Z)(),o=function(){const{siteConfig:{url:e}}=(0,b.Z)(),{pathname:t}=(0,d.TH)();return e+(0,m.Z)(t)}(),i=t?`${a}${t}`:o;return n.createElement(f.Z,null,n.createElement("meta",{property:"og:url",content:i}),n.createElement("link",{rel:"canonical",href:i}))}function w(){const{i18n:{currentLocale:e}}=(0,b.Z)(),{metadata:t,image:a}=(0,v.L)();return n.createElement(n.Fragment,null,n.createElement(f.Z,null,n.createElement("meta",{name:"twitter:card",content:"summary_large_image"}),n.createElement("body",{className:_.h})),a&&n.createElement(h.d,{image:a}),n.createElement(k,null),n.createElement(x,null),n.createElement(N.Z,{tag:C.HX,locale:e}),n.createElement(f.Z,null,t.map(((e,t)=>n.createElement("meta",(0,p.Z)({key:t},e))))))}const A=new Map;function E(e){if(A.has(e.pathname))return{...e,pathname:A.get(e.pathname)};if((0,u.f)(c.Z,e.pathname).some((e=>{let{route:t}=e;return!0===t.exact})))return A.set(e.pathname,e.pathname),e;const t=e.pathname.trim().replace(/(?:\/index)?\.html$/,"")||"/";return A.set(e.pathname,t),{...e,pathname:t}}var I=a(88121),j=a(80694);function S(e){for(var t=arguments.length,a=new Array(t>1?t-1:0),n=1;n{var n;const o=(null==(n=t.default)?void 0:n[e])??t[e];return null==o?void 0:o(...a)}));return()=>o.forEach((e=>null==e?void 0:e()))}const z=function(e){let{children:t,location:a,previousLocation:o}=e;return(0,n.useLayoutEffect)((()=>{o!==a&&(!function(e){let{location:t,previousLocation:a}=e;if(!a)return;const n=t.pathname===a.pathname,o=t.hash===a.hash,i=t.search===a.search;if(n&&o&&!i)return;const{hash:l}=t;if(l){const e=decodeURIComponent(l.substring(1)),t=document.getElementById(e);null==t||t.scrollIntoView()}else window.scrollTo(0,0)}({location:a,previousLocation:o}),S("onRouteDidUpdate",{previousLocation:o,location:a}))}),[o,a]),t};function M(e){const t=Array.from(new Set([e,decodeURI(e)])).map((e=>(0,u.f)(c.Z,e))).flat();return Promise.all(t.map((e=>null==e.route.component.preload?void 0:e.route.component.preload())))}class T extends n.Component{previousLocation;routeUpdateCleanupCb;constructor(e){super(e),this.previousLocation=null,this.routeUpdateCleanupCb=r.Z.canUseDOM?S("onRouteUpdate",{previousLocation:null,location:this.props.location}):()=>{},this.state={nextRouteHasLoaded:!0}}shouldComponentUpdate(e,t){if(e.location===this.props.location)return t.nextRouteHasLoaded;const a=e.location;return this.previousLocation=this.props.location,this.setState({nextRouteHasLoaded:!1}),this.routeUpdateCleanupCb=S("onRouteUpdate",{previousLocation:this.previousLocation,location:a}),M(a.pathname).then((()=>{this.routeUpdateCleanupCb(),this.setState({nextRouteHasLoaded:!0})})).catch((e=>{console.warn(e),window.location.reload()})),!1}render(){const{children:e,location:t}=this.props;return n.createElement(z,{previousLocation:this.previousLocation,location:t},n.createElement(d.AW,{location:t,render:()=>e}))}}const L=T,P="__docusaurus-base-url-issue-banner-container",R="__docusaurus-base-url-issue-banner-suggestion-container",O="__DOCUSAURUS_INSERT_BASEURL_BANNER";function D(e){return`\nwindow['${O}'] = true;\n\ndocument.addEventListener('DOMContentLoaded', maybeInsertBanner);\n\nfunction maybeInsertBanner() {\n var shouldInsert = window['${O}'];\n shouldInsert && insertBanner();\n}\n\nfunction insertBanner() {\n var bannerContainer = document.getElementById('${P}');\n if (!bannerContainer) {\n return;\n }\n var bannerHtml = ${JSON.stringify(function(e){return`\n
    \n

    Your Docusaurus site did not load properly.

    \n

    A very common reason is a wrong site baseUrl configuration.

    \n

    Current configured baseUrl = ${e} ${"/"===e?" (default value)":""}

    \n

    We suggest trying baseUrl =

    \n
    \n`}(e)).replace(/{window[O]=!1}),[]),n.createElement(n.Fragment,null,!r.Z.canUseDOM&&n.createElement(f.Z,null,n.createElement("script",null,D(e))),n.createElement("div",{id:P}))}function B(){const{siteConfig:{baseUrl:e,baseUrlIssueBanner:t}}=(0,b.Z)(),{pathname:a}=(0,d.TH)();return t&&a===e?n.createElement(F,null):null}function U(){const{siteConfig:{favicon:e,title:t,noIndex:a},i18n:{currentLocale:o,localeConfigs:i}}=(0,b.Z)(),l=(0,m.Z)(e),{htmlLang:r,direction:s}=i[o];return n.createElement(f.Z,null,n.createElement("html",{lang:r,dir:s}),n.createElement("title",null,t),n.createElement("meta",{property:"og:title",content:t}),n.createElement("meta",{name:"viewport",content:"width=device-width, initial-scale=1.0"}),a&&n.createElement("meta",{name:"robots",content:"noindex, nofollow"}),e&&n.createElement("link",{rel:"icon",href:l}))}var $=a(73256);function q(){const e=(0,u.H)(c.Z),t=(0,d.TH)();return n.createElement($.Z,null,n.createElement(j.M,null,n.createElement(I.t,null,n.createElement(g,null,n.createElement(U,null),n.createElement(w,null),n.createElement(B,null),n.createElement(L,{location:E(t)},e)))))}var H=a(16887);const G=function(e){try{return document.createElement("link").relList.supports(e)}catch{return!1}}("prefetch")?function(e){return new Promise(((t,a)=>{var n;if("undefined"==typeof document)return void a();const o=document.createElement("link");o.setAttribute("rel","prefetch"),o.setAttribute("href",e),o.onload=()=>t(),o.onerror=()=>a();const i=document.getElementsByTagName("head")[0]??(null==(n=document.getElementsByName("script")[0])?void 0:n.parentNode);null==i||i.appendChild(o)}))}:function(e){return new Promise(((t,a)=>{const n=new XMLHttpRequest;n.open("GET",e,!0),n.withCredentials=!0,n.onload=()=>{200===n.status?t():a()},n.send(null)}))};var Z=a(5304);const V=new Set,W=new Set,Y=()=>{var e,t;return(null==(e=navigator.connection)?void 0:e.effectiveType.includes("2g"))||(null==(t=navigator.connection)?void 0:t.saveData)},K={prefetch(e){if(!(e=>!Y()&&!W.has(e)&&!V.has(e))(e))return!1;V.add(e);const t=(0,u.f)(c.Z,e).flatMap((e=>{return t=e.route.path,Object.entries(H).filter((e=>{let[a]=e;return a.replace(/-[^-]+$/,"")===t})).flatMap((e=>{let[,t]=e;return Object.values((0,Z.Z)(t))}));var t}));return Promise.all(t.map((e=>{const t=a.gca(e);return t&&!t.includes("undefined")?G(t).catch((()=>{})):Promise.resolve()})))},preload:e=>!!(e=>!Y()&&!W.has(e))(e)&&(W.add(e),M(e))},Q=Object.freeze(K);if(r.Z.canUseDOM){window.docusaurus=Q;const e=o.hydrate;M(window.location.pathname).then((()=>{e(n.createElement(l.B6,null,n.createElement(i.VK,null,n.createElement(q,null))),document.getElementById("__docusaurus"))}))}},80694:(e,t,a)=>{"use strict";a.d(t,{_:()=>d,M:()=>u});var n=a(67294),o=a(36809);const i=JSON.parse('{"docusaurus-plugin-content-docs":{"default":{"path":"/Cloud-Native/docs","versions":[{"name":"current","label":"Next","isLast":true,"path":"/Cloud-Native/docs","mainDocId":"resources/intro","docs":[{"id":"resources/devtools","path":"/Cloud-Native/docs/resources/devtools","sidebar":"tutorialSidebar"},{"id":"resources/intro","path":"/Cloud-Native/docs/resources/intro","sidebar":"tutorialSidebar"},{"id":"resources/languages","path":"/Cloud-Native/docs/resources/languages","sidebar":"tutorialSidebar"},{"id":"resources/serverless","path":"/Cloud-Native/docs/resources/serverless","sidebar":"tutorialSidebar"},{"id":"videos/intro","path":"/Cloud-Native/docs/videos/intro","sidebar":"tutorialSidebar"},{"id":"/category/resources","path":"/Cloud-Native/docs/category/resources","sidebar":"tutorialSidebar"},{"id":"/category/videos","path":"/Cloud-Native/docs/category/videos","sidebar":"tutorialSidebar"}],"draftIds":[],"sidebars":{"tutorialSidebar":{"link":{"path":"/Cloud-Native/docs/category/resources","label":"Resources"}}}}],"breadcrumbs":true}}}'),l=JSON.parse('{"defaultLocale":"en","locales":["en"],"path":"i18n","currentLocale":"en","localeConfigs":{"en":{"label":"English","direction":"ltr","htmlLang":"en","calendar":"gregory","path":"en"}}}');var r=a(57529);const s=JSON.parse('{"docusaurusVersion":"2.4.1","siteVersion":"0.0.0","pluginVersions":{"docusaurus-plugin-content-docs":{"type":"package","name":"@docusaurus/plugin-content-docs","version":"2.4.1"},"docusaurus-plugin-content-blog":{"type":"package","name":"@docusaurus/plugin-content-blog","version":"2.4.1"},"docusaurus-plugin-content-pages":{"type":"package","name":"@docusaurus/plugin-content-pages","version":"2.4.1"},"docusaurus-plugin-sitemap":{"type":"package","name":"@docusaurus/plugin-sitemap","version":"2.4.1"},"docusaurus-theme-classic":{"type":"package","name":"@docusaurus/theme-classic","version":"2.4.1"},"docusaurus-plugin-ideal-image":{"type":"package","name":"@docusaurus/plugin-ideal-image","version":"2.4.1"},"docusaurus-plugin-clarity":{"type":"package","name":"docusaurus-plugin-clarity","version":"2.1.0"}}}'),c={siteConfig:o.default,siteMetadata:s,globalData:i,i18n:l,codeTranslations:r},d=n.createContext(c);function u(e){let{children:t}=e;return n.createElement(d.Provider,{value:c},t)}},73256:(e,t,a)=>{"use strict";a.d(t,{Z:()=>g});var n=a(67294),o=a(36136),i=a(31514),l=a(63905),r=a(91764);function s(e){let{error:t,tryAgain:a}=e;return n.createElement("div",{style:{display:"flex",flexDirection:"column",justifyContent:"center",alignItems:"flex-start",minHeight:"100vh",width:"100%",maxWidth:"80ch",fontSize:"20px",margin:"0 auto",padding:"1rem"}},n.createElement("h1",{style:{fontSize:"3rem"}},"This page crashed"),n.createElement("button",{type:"button",onClick:a,style:{margin:"1rem 0",fontSize:"2rem",cursor:"pointer",borderRadius:20,padding:"1rem"}},"Try again"),n.createElement(c,{error:t}))}function c(e){let{error:t}=e;const a=(0,l.getErrorCausalChain)(t).map((e=>e.message)).join("\n\nCause:\n");return n.createElement("p",{style:{whiteSpace:"pre-wrap"}},a)}function d(e){let{error:t,tryAgain:a}=e;return n.createElement(g,{fallback:()=>n.createElement(s,{error:t,tryAgain:a})},n.createElement(i.Z,null,n.createElement("title",null,"Page Error")),n.createElement(r.Z,null,n.createElement(s,{error:t,tryAgain:a})))}const u=e=>n.createElement(d,e);class g extends n.Component{constructor(e){super(e),this.state={error:null}}componentDidCatch(e){o.Z.canUseDOM&&this.setState({error:e})}render(){const{children:e}=this.props,{error:t}=this.state;if(t){const e={error:t,tryAgain:()=>this.setState({error:null})};return(this.props.fallback??u)(e)}return e??null}}},36136:(e,t,a)=>{"use strict";a.d(t,{Z:()=>o});const n="undefined"!=typeof window&&"document"in window&&"createElement"in window.document,o={canUseDOM:n,canUseEventListeners:n&&("addEventListener"in window||"attachEvent"in window),canUseIntersectionObserver:n&&"IntersectionObserver"in window,canUseViewport:n&&"screen"in window}},31514:(e,t,a)=>{"use strict";a.d(t,{Z:()=>i});var n=a(67294),o=a(70405);function i(e){return n.createElement(o.ql,e)}},83699:(e,t,a)=>{"use strict";a.d(t,{Z:()=>p});var n=a(87462),o=a(67294),i=a(73727),l=a(63905),r=a(39962),s=a(2735),c=a(36136);const d=o.createContext({collectLink:()=>{}});var u=a(79524);function g(e,t){var a;let{isNavLink:g,to:p,href:f,activeClassName:b,isActive:m,"data-noBrokenLinkCheck":v,autoAddBaseUrl:h=!0,...y}=e;const{siteConfig:{trailingSlash:_,baseUrl:C}}=(0,r.Z)(),{withBaseUrl:N}=(0,u.C)(),x=(0,o.useContext)(d),k=(0,o.useRef)(null);(0,o.useImperativeHandle)(t,(()=>k.current));const w=p||f;const A=(0,s.Z)(w),E=null==w?void 0:w.replace("pathname://","");let I=void 0!==E?(j=E,h&&(e=>e.startsWith("/"))(j)?N(j):j):void 0;var j;I&&A&&(I=(0,l.applyTrailingSlash)(I,{trailingSlash:_,baseUrl:C}));const S=(0,o.useRef)(!1),z=g?i.OL:i.rU,M=c.Z.canUseIntersectionObserver,T=(0,o.useRef)(),L=()=>{S.current||null==I||(window.docusaurus.preload(I),S.current=!0)};(0,o.useEffect)((()=>(!M&&A&&null!=I&&window.docusaurus.prefetch(I),()=>{M&&T.current&&T.current.disconnect()})),[T,I,M,A]);const P=(null==(a=I)?void 0:a.startsWith("#"))??!1,R=!I||!A||P;return R||v||x.collectLink(I),R?o.createElement("a",(0,n.Z)({ref:k,href:I},w&&!A&&{target:"_blank",rel:"noopener noreferrer"},y)):o.createElement(z,(0,n.Z)({},y,{onMouseEnter:L,onTouchStart:L,innerRef:e=>{k.current=e,M&&e&&A&&(T.current=new window.IntersectionObserver((t=>{t.forEach((t=>{e===t.target&&(t.isIntersecting||t.intersectionRatio>0)&&(T.current.unobserve(e),T.current.disconnect(),null!=I&&window.docusaurus.prefetch(I))}))})),T.current.observe(e))},to:I},g&&{isActive:m,activeClassName:b}))}const p=o.forwardRef(g)},23855:(e,t,a)=>{"use strict";a.d(t,{Z:()=>n});const n=()=>null},97325:(e,t,a)=>{"use strict";a.d(t,{Z:()=>s,I:()=>r});var n=a(67294);function o(e,t){const a=e.split(/(\{\w+\})/).map(((e,a)=>{if(a%2==1){const a=null==t?void 0:t[e.slice(1,-1)];if(void 0!==a)return a}return e}));return a.some((e=>(0,n.isValidElement)(e)))?a.map(((e,t)=>(0,n.isValidElement)(e)?n.cloneElement(e,{key:t}):e)).filter((e=>""!==e)):a.join("")}var i=a(57529);function l(e){let{id:t,message:a}=e;if(void 0===t&&void 0===a)throw new Error("Docusaurus translation declarations must have at least a translation id or a default translation message");return i[t??a]??a??t}function r(e,t){let{message:a,id:n}=e;return o(l({message:a,id:n}),t)}function s(e){let{children:t,id:a,values:i}=e;if(t&&"string"!=typeof t)throw console.warn("Illegal children",t),new Error("The Docusaurus component only accept simple string values");const r=l({message:t,id:a});return n.createElement(n.Fragment,null,o(r,i))}},6875:(e,t,a)=>{"use strict";a.d(t,{m:()=>n});const n="default"},2735:(e,t,a)=>{"use strict";function n(e){return/^(?:\w*:|\/\/)/.test(e)}function o(e){return void 0!==e&&!n(e)}a.d(t,{Z:()=>o,b:()=>n})},79524:(e,t,a)=>{"use strict";a.d(t,{C:()=>l,Z:()=>r});var n=a(67294),o=a(39962),i=a(2735);function l(){const{siteConfig:{baseUrl:e,url:t}}=(0,o.Z)(),a=(0,n.useCallback)(((a,n)=>function(e,t,a,n){let{forcePrependBaseUrl:o=!1,absolute:l=!1}=void 0===n?{}:n;if(!a||a.startsWith("#")||(0,i.b)(a))return a;if(o)return t+a.replace(/^\//,"");if(a===t.replace(/\/$/,""))return t;const r=a.startsWith(t)?a:t+a.replace(/^\//,"");return l?e+r:r}(t,e,a,n)),[t,e]);return{withBaseUrl:a}}function r(e,t){void 0===t&&(t={});const{withBaseUrl:a}=l();return a(e,t)}},39962:(e,t,a)=>{"use strict";a.d(t,{Z:()=>i});var n=a(67294),o=a(80694);function i(){return(0,n.useContext)(o._)}},51048:(e,t,a)=>{"use strict";a.d(t,{Z:()=>i});var n=a(67294),o=a(88121);function i(){return(0,n.useContext)(o._)}},5304:(e,t,a)=>{"use strict";a.d(t,{Z:()=>n});function n(e){const t={};return function e(a,n){Object.entries(a).forEach((a=>{let[o,i]=a;const l=n?`${n}.${o}`:o;var r;"object"==typeof(r=i)&&r&&Object.keys(r).length>0?e(i,l):t[l]=i}))}(e),t}},69656:(e,t,a)=>{"use strict";a.d(t,{_:()=>o,z:()=>i});var n=a(67294);const o=n.createContext(null);function i(e){let{children:t,value:a}=e;const i=n.useContext(o),l=(0,n.useMemo)((()=>function(e){let{parent:t,value:a}=e;if(!t){if(!a)throw new Error("Unexpected: no Docusaurus route context found");if(!("plugin"in a))throw new Error("Unexpected: Docusaurus topmost route context has no `plugin` attribute");return a}const n={...t.data,...null==a?void 0:a.data};return{plugin:t.plugin,data:n}}({parent:i,value:a})),[i,a]);return n.createElement(o.Provider,{value:l},t)}},89871:(e,t,a)=>{"use strict";a.d(t,{Iw:()=>b,gA:()=>g,_r:()=>d,Jo:()=>m,zh:()=>u,yW:()=>f,gB:()=>p});var n=a(76775),o=a(39962),i=a(6875);function l(e,t){void 0===t&&(t={});const a=function(){const{globalData:e}=(0,o.Z)();return e}()[e];if(!a&&t.failfast)throw new Error(`Docusaurus plugin global data not found for "${e}" plugin.`);return a}const r=e=>e.versions.find((e=>e.isLast));function s(e,t){const a=function(e,t){const a=r(e);return[...e.versions.filter((e=>e!==a)),a].find((e=>!!(0,n.LX)(t,{path:e.path,exact:!1,strict:!1})))}(e,t),o=null==a?void 0:a.docs.find((e=>!!(0,n.LX)(t,{path:e.path,exact:!0,strict:!1})));return{activeVersion:a,activeDoc:o,alternateDocVersions:o?function(t){const a={};return e.versions.forEach((e=>{e.docs.forEach((n=>{n.id===t&&(a[e.name]=n)}))})),a}(o.id):{}}}const c={},d=()=>l("docusaurus-plugin-content-docs")??c,u=e=>function(e,t,a){void 0===t&&(t=i.m),void 0===a&&(a={});const n=l(e),o=null==n?void 0:n[t];if(!o&&a.failfast)throw new Error(`Docusaurus plugin global data not found for "${e}" plugin with id "${t}".`);return o}("docusaurus-plugin-content-docs",e,{failfast:!0});function g(e){void 0===e&&(e={});const t=d(),{pathname:a}=(0,n.TH)();return function(e,t,a){void 0===a&&(a={});const o=Object.entries(e).sort(((e,t)=>t[1].path.localeCompare(e[1].path))).find((e=>{let[,a]=e;return!!(0,n.LX)(t,{path:a.path,exact:!1,strict:!1})})),i=o?{pluginId:o[0],pluginData:o[1]}:void 0;if(!i&&a.failfast)throw new Error(`Can't find active docs plugin for "${t}" pathname, while it was expected to be found. Maybe you tried to use a docs feature that can only be used on a docs-related page? Existing docs plugin paths are: ${Object.values(e).map((e=>e.path)).join(", ")}`);return i}(t,a,e)}function p(e){return u(e).versions}function f(e){const t=u(e);return r(t)}function b(e){const t=u(e),{pathname:a}=(0,n.TH)();return s(t,a)}function m(e){const t=u(e),{pathname:a}=(0,n.TH)();return function(e,t){const a=r(e);return{latestDocSuggestion:s(e,t).alternateDocVersions[a.name],latestVersionSuggestion:a}}(t,a)}},79957:(e,t,a)=>{"use strict";a.r(t),a.d(t,{default:()=>i});var n=a(74865),o=a.n(n);o().configure({showSpinner:!1});const i={onRouteUpdate(e){let{location:t,previousLocation:a}=e;if(a&&t.pathname!==a.pathname){const e=window.setTimeout((()=>{o().start()}),200);return()=>window.clearTimeout(e)}},onRouteDidUpdate(){o().done()}}},82251:(e,t,a)=>{"use strict";a.r(t);var n=a(87410),o=a(36809);!function(e){const{themeConfig:{prism:t}}=o.default,{additionalLanguages:n}=t;globalThis.Prism=e,n.forEach((e=>{a(6726)(`./prism-${e}`)})),delete globalThis.Prism}(n.Z)},14082:(e,t,a)=>{"use strict";a.d(t,{Z:()=>i});var n=a(67294);const o="iconExternalLink_nPIU";function i(e){let{width:t=13.5,height:a=13.5}=e;return n.createElement("svg",{width:t,height:a,"aria-hidden":"true",viewBox:"0 0 24 24",className:o},n.createElement("path",{fill:"currentColor",d:"M21 13v10h-21v-19h12v2h-10v15h17v-8h2zm3-12h-10.988l4.035 4-6.977 7.07 2.828 2.828 6.977-7.07 4.125 4.172v-11z"}))}},91764:(e,t,a)=>{"use strict";a.d(t,{Z:()=>ft});var n=a(67294),o=a(86010),i=a(73256),l=a(35463),r=a(87462),s=a(76775),c=a(97325),d=a(43266);const u="__docusaurus_skipToContent_fallback";function g(e){e.setAttribute("tabindex","-1"),e.focus(),e.removeAttribute("tabindex")}function p(){const e=(0,n.useRef)(null),{action:t}=(0,s.k6)(),a=(0,n.useCallback)((e=>{e.preventDefault();const t=document.querySelector("main:first-of-type")??document.getElementById(u);t&&g(t)}),[]);return(0,d.S)((a=>{let{location:n}=a;e.current&&!n.hash&&"PUSH"===t&&g(e.current)})),{containerRef:e,onClick:a}}const f=(0,c.I)({id:"theme.common.skipToMainContent",description:"The skip to content label used for accessibility, allowing to rapidly navigate to main content with keyboard tab/enter navigation",message:"Skip to main content"});function b(e){const t=e.children??f,{containerRef:a,onClick:o}=p();return n.createElement("div",{ref:a,role:"region","aria-label":f},n.createElement("a",(0,r.Z)({},e,{href:`#${u}`,onClick:o}),t))}var m=a(23702),v=a(78181);const h="skipToContent_fXgn";function y(){return n.createElement(b,{className:h})}var _=a(20107),C=a(65830);function N(e){let{width:t=21,height:a=21,color:o="currentColor",strokeWidth:i=1.2,className:l,...s}=e;return n.createElement("svg",(0,r.Z)({viewBox:"0 0 15 15",width:t,height:a},s),n.createElement("g",{stroke:o,strokeWidth:i},n.createElement("path",{d:"M.75.75l13.5 13.5M14.25.75L.75 14.25"})))}const x="closeButton_CVFx";function k(e){return n.createElement("button",(0,r.Z)({type:"button","aria-label":(0,c.I)({id:"theme.AnnouncementBar.closeButtonAriaLabel",message:"Close",description:"The ARIA label for close button of announcement bar"})},e,{className:(0,o.Z)("clean-btn close",x,e.className)}),n.createElement(N,{width:14,height:14,strokeWidth:3.1}))}const w="content_knG7";function A(e){const{announcementBar:t}=(0,_.L)(),{content:a}=t;return n.createElement("div",(0,r.Z)({},e,{className:(0,o.Z)(w,e.className),dangerouslySetInnerHTML:{__html:a}}))}const E="announcementBar_mb4j",I="announcementBarPlaceholder_vyr4",j="announcementBarClose_gvF7",S="announcementBarContent_xLdY";function z(){const{announcementBar:e}=(0,_.L)(),{isActive:t,close:a}=(0,C.nT)();if(!t)return null;const{backgroundColor:o,textColor:i,isCloseable:l}=e;return n.createElement("div",{className:E,style:{backgroundColor:o,color:i},role:"banner"},l&&n.createElement("div",{className:I}),n.createElement(A,{className:S}),l&&n.createElement(k,{onClick:a,className:j}))}var M=a(52600),T=a(72957);var L=a(43768),P=a(53086);const R=n.createContext(null);function O(e){let{children:t}=e;const a=function(){const e=(0,M.e)(),t=(0,P.HY)(),[a,o]=(0,n.useState)(!1),i=null!==t.component,l=(0,L.D9)(i);return(0,n.useEffect)((()=>{i&&!l&&o(!0)}),[i,l]),(0,n.useEffect)((()=>{i?e.shown||o(!0):o(!1)}),[e.shown,i]),(0,n.useMemo)((()=>[a,o]),[a])}();return n.createElement(R.Provider,{value:a},t)}function D(e){if(e.component){const t=e.component;return n.createElement(t,e.props)}}function F(){const e=(0,n.useContext)(R);if(!e)throw new L.i6("NavbarSecondaryMenuDisplayProvider");const[t,a]=e,o=(0,n.useCallback)((()=>a(!1)),[a]),i=(0,P.HY)();return(0,n.useMemo)((()=>({shown:t,hide:o,content:D(i)})),[o,i,t])}function B(e){let{header:t,primaryMenu:a,secondaryMenu:i}=e;const{shown:l}=F();return n.createElement("div",{className:"navbar-sidebar"},t,n.createElement("div",{className:(0,o.Z)("navbar-sidebar__items",{"navbar-sidebar__items--show-secondary":l})},n.createElement("div",{className:"navbar-sidebar__item menu"},a),n.createElement("div",{className:"navbar-sidebar__item menu"},i)))}var U=a(9200),$=a(51048);function q(e){return n.createElement("svg",(0,r.Z)({viewBox:"0 0 24 24",width:24,height:24},e),n.createElement("path",{fill:"currentColor",d:"M12,9c1.65,0,3,1.35,3,3s-1.35,3-3,3s-3-1.35-3-3S10.35,9,12,9 M12,7c-2.76,0-5,2.24-5,5s2.24,5,5,5s5-2.24,5-5 S14.76,7,12,7L12,7z M2,13l2,0c0.55,0,1-0.45,1-1s-0.45-1-1-1l-2,0c-0.55,0-1,0.45-1,1S1.45,13,2,13z M20,13l2,0c0.55,0,1-0.45,1-1 s-0.45-1-1-1l-2,0c-0.55,0-1,0.45-1,1S19.45,13,20,13z M11,2v2c0,0.55,0.45,1,1,1s1-0.45,1-1V2c0-0.55-0.45-1-1-1S11,1.45,11,2z M11,20v2c0,0.55,0.45,1,1,1s1-0.45,1-1v-2c0-0.55-0.45-1-1-1C11.45,19,11,19.45,11,20z M5.99,4.58c-0.39-0.39-1.03-0.39-1.41,0 c-0.39,0.39-0.39,1.03,0,1.41l1.06,1.06c0.39,0.39,1.03,0.39,1.41,0s0.39-1.03,0-1.41L5.99,4.58z M18.36,16.95 c-0.39-0.39-1.03-0.39-1.41,0c-0.39,0.39-0.39,1.03,0,1.41l1.06,1.06c0.39,0.39,1.03,0.39,1.41,0c0.39-0.39,0.39-1.03,0-1.41 L18.36,16.95z M19.42,5.99c0.39-0.39,0.39-1.03,0-1.41c-0.39-0.39-1.03-0.39-1.41,0l-1.06,1.06c-0.39,0.39-0.39,1.03,0,1.41 s1.03,0.39,1.41,0L19.42,5.99z M7.05,18.36c0.39-0.39,0.39-1.03,0-1.41c-0.39-0.39-1.03-0.39-1.41,0l-1.06,1.06 c-0.39,0.39-0.39,1.03,0,1.41s1.03,0.39,1.41,0L7.05,18.36z"}))}function H(e){return n.createElement("svg",(0,r.Z)({viewBox:"0 0 24 24",width:24,height:24},e),n.createElement("path",{fill:"currentColor",d:"M9.37,5.51C9.19,6.15,9.1,6.82,9.1,7.5c0,4.08,3.32,7.4,7.4,7.4c0.68,0,1.35-0.09,1.99-0.27C17.45,17.19,14.93,19,12,19 c-3.86,0-7-3.14-7-7C5,9.07,6.81,6.55,9.37,5.51z M12,3c-4.97,0-9,4.03-9,9s4.03,9,9,9s9-4.03,9-9c0-0.46-0.04-0.92-0.1-1.36 c-0.98,1.37-2.58,2.26-4.4,2.26c-2.98,0-5.4-2.42-5.4-5.4c0-1.81,0.89-3.42,2.26-4.4C12.92,3.04,12.46,3,12,3L12,3z"}))}const G={toggle:"toggle_vylO",toggleButton:"toggleButton_gllP",darkToggleIcon:"darkToggleIcon_wfgR",lightToggleIcon:"lightToggleIcon_pyhR",toggleButtonDisabled:"toggleButtonDisabled_aARS"};function Z(e){let{className:t,buttonClassName:a,value:i,onChange:l}=e;const r=(0,$.Z)(),s=(0,c.I)({message:"Switch between dark and light mode (currently {mode})",id:"theme.colorToggle.ariaLabel",description:"The ARIA label for the navbar color mode toggle"},{mode:"dark"===i?(0,c.I)({message:"dark mode",id:"theme.colorToggle.ariaLabel.mode.dark",description:"The name for the dark color mode"}):(0,c.I)({message:"light mode",id:"theme.colorToggle.ariaLabel.mode.light",description:"The name for the light color mode"})});return n.createElement("div",{className:(0,o.Z)(G.toggle,t)},n.createElement("button",{className:(0,o.Z)("clean-btn",G.toggleButton,!r&&G.toggleButtonDisabled,a),type:"button",onClick:()=>l("dark"===i?"light":"dark"),disabled:!r,title:s,"aria-label":s,"aria-live":"polite"},n.createElement(q,{className:(0,o.Z)(G.toggleIcon,G.lightToggleIcon)}),n.createElement(H,{className:(0,o.Z)(G.toggleIcon,G.darkToggleIcon)})))}const V=n.memo(Z),W="darkNavbarColorModeToggle_X3D1";function Y(e){let{className:t}=e;const a=(0,_.L)().navbar.style,o=(0,_.L)().colorMode.disableSwitch,{colorMode:i,setColorMode:l}=(0,U.I)();return o?null:n.createElement(V,{className:t,buttonClassName:"dark"===a?W:void 0,value:i,onChange:l})}var K=a(96811);function Q(){return n.createElement(K.Z,{className:"navbar__brand",imageClassName:"navbar__logo",titleClassName:"navbar__title text--truncate"})}function X(){const e=(0,M.e)();return n.createElement("button",{type:"button","aria-label":(0,c.I)({id:"theme.docs.sidebar.closeSidebarButtonAriaLabel",message:"Close navigation bar",description:"The ARIA label for close button of mobile sidebar"}),className:"clean-btn navbar-sidebar__close",onClick:()=>e.toggle()},n.createElement(N,{color:"var(--ifm-color-emphasis-600)"}))}function J(){return n.createElement("div",{className:"navbar-sidebar__brand"},n.createElement(Q,null),n.createElement(Y,{className:"margin-right--md"}),n.createElement(X,null))}var ee=a(83699),te=a(79524),ae=a(2735);function ne(e,t){return void 0!==e&&void 0!==t&&new RegExp(e,"gi").test(t)}var oe=a(14082);function ie(e){let{activeBasePath:t,activeBaseRegex:a,to:o,href:i,label:l,html:s,isDropdownLink:c,prependBaseUrlToHref:d,...u}=e;const g=(0,te.Z)(o),p=(0,te.Z)(t),f=(0,te.Z)(i,{forcePrependBaseUrl:!0}),b=l&&i&&!(0,ae.Z)(i),m=s?{dangerouslySetInnerHTML:{__html:s}}:{children:n.createElement(n.Fragment,null,l,b&&n.createElement(oe.Z,c&&{width:12,height:12}))};return i?n.createElement(ee.Z,(0,r.Z)({href:d?f:i},u,m)):n.createElement(ee.Z,(0,r.Z)({to:g,isNavLink:!0},(t||a)&&{isActive:(e,t)=>a?ne(a,t.pathname):t.pathname.startsWith(p)},u,m))}function le(e){let{className:t,isDropdownItem:a=!1,...i}=e;const l=n.createElement(ie,(0,r.Z)({className:(0,o.Z)(a?"dropdown__link":"navbar__item navbar__link",t),isDropdownLink:a},i));return a?n.createElement("li",null,l):l}function re(e){let{className:t,isDropdownItem:a,...i}=e;return n.createElement("li",{className:"menu__list-item"},n.createElement(ie,(0,r.Z)({className:(0,o.Z)("menu__link",t)},i)))}function se(e){let{mobile:t=!1,position:a,...o}=e;const i=t?re:le;return n.createElement(i,(0,r.Z)({},o,{activeClassName:o.activeClassName??(t?"menu__link--active":"navbar__link--active")}))}var ce=a(54639),de=a(69003),ue=a(39962);function ge(e,t){return e.some((e=>function(e,t){return!!(0,de.Mg)(e.to,t)||!!ne(e.activeBaseRegex,t)||!(!e.activeBasePath||!t.startsWith(e.activeBasePath))}(e,t)))}function pe(e){let{items:t,position:a,className:i,onClick:l,...s}=e;const c=(0,n.useRef)(null),[d,u]=(0,n.useState)(!1);return(0,n.useEffect)((()=>{const e=e=>{c.current&&!c.current.contains(e.target)&&u(!1)};return document.addEventListener("mousedown",e),document.addEventListener("touchstart",e),document.addEventListener("focusin",e),()=>{document.removeEventListener("mousedown",e),document.removeEventListener("touchstart",e),document.removeEventListener("focusin",e)}}),[c]),n.createElement("div",{ref:c,className:(0,o.Z)("navbar__item","dropdown","dropdown--hoverable",{"dropdown--right":"right"===a,"dropdown--show":d})},n.createElement(ie,(0,r.Z)({"aria-haspopup":"true","aria-expanded":d,role:"button",href:s.to?void 0:"#",className:(0,o.Z)("navbar__link",i)},s,{onClick:s.to?void 0:e=>e.preventDefault(),onKeyDown:e=>{"Enter"===e.key&&(e.preventDefault(),u(!d))}}),s.children??s.label),n.createElement("ul",{className:"dropdown__menu"},t.map(((e,t)=>n.createElement(Ee,(0,r.Z)({isDropdownItem:!0,activeClassName:"dropdown__link--active"},e,{key:t}))))))}function fe(e){let{items:t,className:a,position:i,onClick:l,...c}=e;const d=function(){const{siteConfig:{baseUrl:e}}=(0,ue.Z)(),{pathname:t}=(0,s.TH)();return t.replace(e,"/")}(),u=ge(t,d),{collapsed:g,toggleCollapsed:p,setCollapsed:f}=(0,ce.u)({initialState:()=>!u});return(0,n.useEffect)((()=>{u&&f(!u)}),[d,u,f]),n.createElement("li",{className:(0,o.Z)("menu__list-item",{"menu__list-item--collapsed":g})},n.createElement(ie,(0,r.Z)({role:"button",className:(0,o.Z)("menu__link menu__link--sublist menu__link--sublist-caret",a)},c,{onClick:e=>{e.preventDefault(),p()}}),c.children??c.label),n.createElement(ce.z,{lazy:!0,as:"ul",className:"menu__list",collapsed:g},t.map(((e,t)=>n.createElement(Ee,(0,r.Z)({mobile:!0,isDropdownItem:!0,onClick:l,activeClassName:"menu__link--active"},e,{key:t}))))))}function be(e){let{mobile:t=!1,...a}=e;const o=t?fe:pe;return n.createElement(o,a)}var me=a(40626);function ve(e){let{width:t=20,height:a=20,...o}=e;return n.createElement("svg",(0,r.Z)({viewBox:"0 0 24 24",width:t,height:a,"aria-hidden":!0},o),n.createElement("path",{fill:"currentColor",d:"M12.87 15.07l-2.54-2.51.03-.03c1.74-1.94 2.98-4.17 3.71-6.53H17V4h-7V2H8v2H1v1.99h11.17C11.5 7.92 10.44 9.75 9 11.35 8.07 10.32 7.3 9.19 6.69 8h-2c.73 1.63 1.73 3.17 2.98 4.56l-5.09 5.02L4 19l5-5 3.11 3.11.76-2.04zM18.5 10h-2L12 22h2l1.12-3h4.75L21 22h2l-4.5-12zm-2.62 7l1.62-4.33L19.12 17h-3.24z"}))}const he="iconLanguage_nlXk";var ye=a(23855);const _e="searchBox_ZlJk";function Ce(e){let{children:t,className:a}=e;return n.createElement("div",{className:(0,o.Z)(a,_e)},t)}var Ne=a(89871),xe=a(3734);var ke=a(86409);const we=e=>e.docs.find((t=>t.id===e.mainDocId));const Ae={default:se,localeDropdown:function(e){let{mobile:t,dropdownItemsBefore:a,dropdownItemsAfter:o,...i}=e;const{i18n:{currentLocale:l,locales:d,localeConfigs:u}}=(0,ue.Z)(),g=(0,me.l)(),{search:p,hash:f}=(0,s.TH)(),b=[...a,...d.map((e=>{const a=`${`pathname://${g.createUrl({locale:e,fullyQualified:!1})}`}${p}${f}`;return{label:u[e].label,lang:u[e].htmlLang,to:a,target:"_self",autoAddBaseUrl:!1,className:e===l?t?"menu__link--active":"dropdown__link--active":""}})),...o],m=t?(0,c.I)({message:"Languages",id:"theme.navbar.mobileLanguageDropdown.label",description:"The label for the mobile language switcher dropdown"}):u[l].label;return n.createElement(be,(0,r.Z)({},i,{mobile:t,label:n.createElement(n.Fragment,null,n.createElement(ve,{className:he}),m),items:b}))},search:function(e){let{mobile:t,className:a}=e;return t?null:n.createElement(Ce,{className:a},n.createElement(ye.Z,null))},dropdown:be,html:function(e){let{value:t,className:a,mobile:i=!1,isDropdownItem:l=!1}=e;const r=l?"li":"div";return n.createElement(r,{className:(0,o.Z)({navbar__item:!i&&!l,"menu__list-item":i},a),dangerouslySetInnerHTML:{__html:t}})},doc:function(e){let{docId:t,label:a,docsPluginId:o,...i}=e;const{activeDoc:l}=(0,Ne.Iw)(o),s=(0,xe.vY)(t,o);return null===s?null:n.createElement(se,(0,r.Z)({exact:!0},i,{isActive:()=>(null==l?void 0:l.path)===s.path||!(null==l||!l.sidebar)&&l.sidebar===s.sidebar,label:a??s.id,to:s.path}))},docSidebar:function(e){let{sidebarId:t,label:a,docsPluginId:o,...i}=e;const{activeDoc:l}=(0,Ne.Iw)(o),s=(0,xe.oz)(t,o).link;if(!s)throw new Error(`DocSidebarNavbarItem: Sidebar with ID "${t}" doesn't have anything to be linked to.`);return n.createElement(se,(0,r.Z)({exact:!0},i,{isActive:()=>(null==l?void 0:l.sidebar)===t,label:a??s.label,to:s.path}))},docsVersion:function(e){let{label:t,to:a,docsPluginId:o,...i}=e;const l=(0,xe.lO)(o)[0],s=t??l.label,c=a??(e=>e.docs.find((t=>t.id===e.mainDocId)))(l).path;return n.createElement(se,(0,r.Z)({},i,{label:s,to:c}))},docsVersionDropdown:function(e){let{mobile:t,docsPluginId:a,dropdownActiveClassDisabled:o,dropdownItemsBefore:i,dropdownItemsAfter:l,...d}=e;const{search:u,hash:g}=(0,s.TH)(),p=(0,Ne.Iw)(a),f=(0,Ne.gB)(a),{savePreferredVersionName:b}=(0,ke.J)(a),m=[...i,...f.map((e=>{const t=p.alternateDocVersions[e.name]??we(e);return{label:e.label,to:`${t.path}${u}${g}`,isActive:()=>e===p.activeVersion,onClick:()=>b(e.name)}})),...l],v=(0,xe.lO)(a)[0],h=t&&m.length>1?(0,c.I)({id:"theme.navbar.mobileVersionsDropdown.label",message:"Versions",description:"The label for the navbar versions dropdown on mobile view"}):v.label,y=t&&m.length>1?void 0:we(v).path;return m.length<=1?n.createElement(se,(0,r.Z)({},d,{mobile:t,label:h,to:y,isActive:o?()=>!1:void 0})):n.createElement(be,(0,r.Z)({},d,{mobile:t,label:h,to:y,items:m,isActive:o?()=>!1:void 0}))}};function Ee(e){let{type:t,...a}=e;const o=function(e,t){return e&&"default"!==e?e:"items"in t?"dropdown":"default"}(t,a),i=Ae[o];if(!i)throw new Error(`No NavbarItem component found for type "${t}".`);return n.createElement(i,a)}function Ie(){const e=(0,M.e)(),t=(0,_.L)().navbar.items;return n.createElement("ul",{className:"menu__list"},t.map(((t,a)=>n.createElement(Ee,(0,r.Z)({mobile:!0},t,{onClick:()=>e.toggle(),key:a})))))}function je(e){return n.createElement("button",(0,r.Z)({},e,{type:"button",className:"clean-btn navbar-sidebar__back"}),n.createElement(c.Z,{id:"theme.navbar.mobileSidebarSecondaryMenu.backButtonLabel",description:"The label of the back button to return to main menu, inside the mobile navbar sidebar secondary menu (notably used to display the docs sidebar)"},"\u2190 Back to main menu"))}function Se(){const e=0===(0,_.L)().navbar.items.length,t=F();return n.createElement(n.Fragment,null,!e&&n.createElement(je,{onClick:()=>t.hide()}),t.content)}function ze(){const e=(0,M.e)();var t;return void 0===(t=e.shown)&&(t=!0),(0,n.useEffect)((()=>(document.body.style.overflow=t?"hidden":"visible",()=>{document.body.style.overflow="visible"})),[t]),e.shouldRender?n.createElement(B,{header:n.createElement(J,null),primaryMenu:n.createElement(Ie,null),secondaryMenu:n.createElement(Se,null)}):null}const Me="navbarHideable_m1mJ",Te="navbarHidden_jGov";function Le(e){return n.createElement("div",(0,r.Z)({role:"presentation"},e,{className:(0,o.Z)("navbar-sidebar__backdrop",e.className)}))}function Pe(e){let{children:t}=e;const{navbar:{hideOnScroll:a,style:i}}=(0,_.L)(),l=(0,M.e)(),{navbarRef:r,isNavbarVisible:s}=function(e){const[t,a]=(0,n.useState)(e),o=(0,n.useRef)(!1),i=(0,n.useRef)(0),l=(0,n.useCallback)((e=>{null!==e&&(i.current=e.getBoundingClientRect().height)}),[]);return(0,T.RF)(((t,n)=>{let{scrollY:l}=t;if(!e)return;if(l=r?a(!1):l+c{if(!e)return;const n=t.location.hash;if(n?document.getElementById(n.substring(1)):void 0)return o.current=!0,void a(!1);a(!0)})),{navbarRef:l,isNavbarVisible:t}}(a);return n.createElement("nav",{ref:r,"aria-label":(0,c.I)({id:"theme.NavBar.navAriaLabel",message:"Main",description:"The ARIA label for the main navigation"}),className:(0,o.Z)("navbar","navbar--fixed-top",a&&[Me,!s&&Te],{"navbar--dark":"dark"===i,"navbar--primary":"primary"===i,"navbar-sidebar--show":l.shown})},t,n.createElement(Le,{onClick:l.toggle}),n.createElement(ze,null))}var Re=a(63905);const Oe="errorBoundaryError_a6uf";function De(e){return n.createElement("button",(0,r.Z)({type:"button"},e),n.createElement(c.Z,{id:"theme.ErrorPageContent.tryAgain",description:"The label of the button to try again rendering when the React error boundary captures an error"},"Try again"))}function Fe(e){let{error:t}=e;const a=(0,Re.getErrorCausalChain)(t).map((e=>e.message)).join("\n\nCause:\n");return n.createElement("p",{className:Oe},a)}class Be extends n.Component{componentDidCatch(e,t){throw this.props.onError(e,t)}render(){return this.props.children}}function Ue(e){let{width:t=30,height:a=30,className:o,...i}=e;return n.createElement("svg",(0,r.Z)({className:o,width:t,height:a,viewBox:"0 0 30 30","aria-hidden":"true"},i),n.createElement("path",{stroke:"currentColor",strokeLinecap:"round",strokeMiterlimit:"10",strokeWidth:"2",d:"M4 7h22M4 15h22M4 23h22"}))}function $e(){const{toggle:e,shown:t}=(0,M.e)();return n.createElement("button",{onClick:e,"aria-label":(0,c.I)({id:"theme.docs.sidebar.toggleSidebarButtonAriaLabel",message:"Toggle navigation bar",description:"The ARIA label for hamburger menu button of mobile navigation"}),"aria-expanded":t,className:"navbar__toggle clean-btn",type:"button"},n.createElement(Ue,null))}const qe="colorModeToggle_DEke";function He(e){let{items:t}=e;return n.createElement(n.Fragment,null,t.map(((e,t)=>n.createElement(Be,{key:t,onError:t=>new Error(`A theme navbar item failed to render.\nPlease double-check the following navbar item (themeConfig.navbar.items) of your Docusaurus config:\n${JSON.stringify(e,null,2)}`,{cause:t})},n.createElement(Ee,e)))))}function Ge(e){let{left:t,right:a}=e;return n.createElement("div",{className:"navbar__inner"},n.createElement("div",{className:"navbar__items"},t),n.createElement("div",{className:"navbar__items navbar__items--right"},a))}function Ze(){const e=(0,M.e)(),t=(0,_.L)().navbar.items,[a,o]=function(e){function t(e){return"left"===(e.position??"right")}return[e.filter(t),e.filter((e=>!t(e)))]}(t),i=t.find((e=>"search"===e.type));return n.createElement(Ge,{left:n.createElement(n.Fragment,null,!e.disabled&&n.createElement($e,null),n.createElement(Q,null),n.createElement(He,{items:a})),right:n.createElement(n.Fragment,null,n.createElement(He,{items:o}),n.createElement(Y,{className:qe}),!i&&n.createElement(Ce,null,n.createElement(ye.Z,null)))})}function Ve(){return n.createElement(Pe,null,n.createElement(Ze,null))}function We(e){let{item:t}=e;const{to:a,href:o,label:i,prependBaseUrlToHref:l,...s}=t,c=(0,te.Z)(a),d=(0,te.Z)(o,{forcePrependBaseUrl:!0});return n.createElement(ee.Z,(0,r.Z)({className:"footer__link-item"},o?{href:l?d:o}:{to:c},s),i,o&&!(0,ae.Z)(o)&&n.createElement(oe.Z,null))}function Ye(e){let{item:t}=e;return t.html?n.createElement("li",{className:"footer__item",dangerouslySetInnerHTML:{__html:t.html}}):n.createElement("li",{key:t.href??t.to,className:"footer__item"},n.createElement(We,{item:t}))}function Ke(e){let{column:t}=e;return n.createElement("div",{className:"col footer__col"},n.createElement("div",{className:"footer__title"},t.title),n.createElement("ul",{className:"footer__items clean-list"},t.items.map(((e,t)=>n.createElement(Ye,{key:t,item:e})))))}function Qe(e){let{columns:t}=e;return n.createElement("div",{className:"row footer__links"},t.map(((e,t)=>n.createElement(Ke,{key:t,column:e}))))}function Xe(){return n.createElement("span",{className:"footer__link-separator"},"\xb7")}function Je(e){let{item:t}=e;return t.html?n.createElement("span",{className:"footer__link-item",dangerouslySetInnerHTML:{__html:t.html}}):n.createElement(We,{item:t})}function et(e){let{links:t}=e;return n.createElement("div",{className:"footer__links text--center"},n.createElement("div",{className:"footer__links"},t.map(((e,a)=>n.createElement(n.Fragment,{key:a},n.createElement(Je,{item:e}),t.length!==a+1&&n.createElement(Xe,null))))))}function tt(e){let{links:t}=e;return function(e){return"title"in e[0]}(t)?n.createElement(Qe,{columns:t}):n.createElement(et,{links:t})}var at=a(7909);const nt="footerLogoLink_BH7S";function ot(e){let{logo:t}=e;const{withBaseUrl:a}=(0,te.C)(),i={light:a(t.src),dark:a(t.srcDark??t.src)};return n.createElement(at.Z,{className:(0,o.Z)("footer__logo",t.className),alt:t.alt,sources:i,width:t.width,height:t.height,style:t.style})}function it(e){let{logo:t}=e;return t.href?n.createElement(ee.Z,{href:t.href,className:nt,target:t.target},n.createElement(ot,{logo:t})):n.createElement(ot,{logo:t})}function lt(e){let{copyright:t}=e;return n.createElement("div",{className:"footer__copyright",dangerouslySetInnerHTML:{__html:t}})}function rt(e){let{style:t,links:a,logo:i,copyright:l}=e;return n.createElement("footer",{className:(0,o.Z)("footer",{"footer--dark":"dark"===t})},n.createElement("div",{className:"container container-fluid"},a,(i||l)&&n.createElement("div",{className:"footer__bottom text--center"},i&&n.createElement("div",{className:"margin-bottom--sm"},i),l)))}function st(){const{footer:e}=(0,_.L)();if(!e)return null;const{copyright:t,links:a,logo:o,style:i}=e;return n.createElement(rt,{style:i,links:a&&a.length>0&&n.createElement(tt,{links:a}),logo:o&&n.createElement(it,{logo:o}),copyright:t&&n.createElement(lt,{copyright:t})})}const ct=n.memo(st),dt=(0,L.Qc)([U.S,C.pl,T.OC,ke.L5,l.VC,function(e){let{children:t}=e;return n.createElement(P.n2,null,n.createElement(M.M,null,n.createElement(O,null,t)))}]);function ut(e){let{children:t}=e;return n.createElement(dt,null,t)}function gt(e){let{error:t,tryAgain:a}=e;return n.createElement("main",{className:"container margin-vert--xl"},n.createElement("div",{className:"row"},n.createElement("div",{className:"col col--6 col--offset-3"},n.createElement("h1",{className:"hero__title"},n.createElement(c.Z,{id:"theme.ErrorPageContent.title",description:"The title of the fallback page when the page crashed"},"This page crashed.")),n.createElement("div",{className:"margin-vert--lg"},n.createElement(De,{onClick:a,className:"button button--primary shadow--lw"})),n.createElement("hr",null),n.createElement("div",{className:"margin-vert--md"},n.createElement(Fe,{error:t})))))}const pt="mainWrapper_z2l0";function ft(e){const{children:t,noFooter:a,wrapperClassName:r,title:s,description:c}=e;return(0,v.t)(),n.createElement(ut,null,n.createElement(l.d,{title:s,description:c}),n.createElement(y,null),n.createElement(z,null),n.createElement(Ve,null),n.createElement("div",{id:u,className:(0,o.Z)(m.k.wrapper.main,pt,r)},n.createElement(i.Z,{fallback:e=>n.createElement(gt,e)},t)),!a&&n.createElement(ct,null))}},96811:(e,t,a)=>{"use strict";a.d(t,{Z:()=>u});var n=a(87462),o=a(67294),i=a(83699),l=a(79524),r=a(39962),s=a(20107),c=a(7909);function d(e){let{logo:t,alt:a,imageClassName:n}=e;const i={light:(0,l.Z)(t.src),dark:(0,l.Z)(t.srcDark||t.src)},r=o.createElement(c.Z,{className:t.className,sources:i,height:t.height,width:t.width,alt:a,style:t.style});return n?o.createElement("div",{className:n},r):r}function u(e){const{siteConfig:{title:t}}=(0,r.Z)(),{navbar:{title:a,logo:c}}=(0,s.L)(),{imageClassName:u,titleClassName:g,...p}=e,f=(0,l.Z)((null==c?void 0:c.href)||"/"),b=a?"":t,m=(null==c?void 0:c.alt)??b;return o.createElement(i.Z,(0,n.Z)({to:f},p,(null==c?void 0:c.target)&&{target:c.target}),c&&o.createElement(d,{logo:c,alt:m,imageClassName:u}),null!=a&&o.createElement("b",{className:g},a))}},33647:(e,t,a)=>{"use strict";a.d(t,{Z:()=>i});var n=a(67294),o=a(31514);function i(e){let{locale:t,version:a,tag:i}=e;const l=t;return n.createElement(o.Z,null,t&&n.createElement("meta",{name:"docusaurus_locale",content:t}),a&&n.createElement("meta",{name:"docusaurus_version",content:a}),i&&n.createElement("meta",{name:"docusaurus_tag",content:i}),l&&n.createElement("meta",{name:"docsearch:language",content:l}),a&&n.createElement("meta",{name:"docsearch:version",content:a}),i&&n.createElement("meta",{name:"docsearch:docusaurus_tag",content:i}))}},7909:(e,t,a)=>{"use strict";a.d(t,{Z:()=>c});var n=a(87462),o=a(67294),i=a(86010),l=a(51048),r=a(9200);const s={themedImage:"themedImage_ToTc","themedImage--light":"themedImage--light_HNdA","themedImage--dark":"themedImage--dark_i4oU"};function c(e){const t=(0,l.Z)(),{colorMode:a}=(0,r.I)(),{sources:c,className:d,alt:u,...g}=e,p=t?"dark"===a?["dark"]:["light"]:["light","dark"];return o.createElement(o.Fragment,null,p.map((e=>o.createElement("img",(0,n.Z)({key:e,src:c[e],alt:u,className:(0,i.Z)(s.themedImage,s[`themedImage--${e}`],d)},g)))))}},54639:(e,t,a)=>{"use strict";a.d(t,{u:()=>r,z:()=>b});var n=a(87462),o=a(67294),i=a(36136),l=a(58986);function r(e){let{initialState:t}=e;const[a,n]=(0,o.useState)(t??!1),i=(0,o.useCallback)((()=>{n((e=>!e))}),[]);return{collapsed:a,setCollapsed:n,toggleCollapsed:i}}const s={display:"none",overflow:"hidden",height:"0px"},c={display:"block",overflow:"visible",height:"auto"};function d(e,t){const a=t?s:c;e.style.display=a.display,e.style.overflow=a.overflow,e.style.height=a.height}function u(e){let{collapsibleRef:t,collapsed:a,animation:n}=e;const i=(0,o.useRef)(!1);(0,o.useEffect)((()=>{const e=t.current;function o(){const t=e.scrollHeight,a=(null==n?void 0:n.duration)??function(e){if((0,l.n)())return 1;const t=e/36;return Math.round(10*(4+15*t**.25+t/5))}(t);return{transition:`height ${a}ms ${(null==n?void 0:n.easing)??"ease-in-out"}`,height:`${t}px`}}function r(){const t=o();e.style.transition=t.transition,e.style.height=t.height}if(!i.current)return d(e,a),void(i.current=!0);return e.style.willChange="height",function(){const t=requestAnimationFrame((()=>{a?(r(),requestAnimationFrame((()=>{e.style.height=s.height,e.style.overflow=s.overflow}))):(e.style.display="block",requestAnimationFrame((()=>{r()})))}));return()=>cancelAnimationFrame(t)}()}),[t,a,n])}function g(e){if(!i.Z.canUseDOM)return e?s:c}function p(e){let{as:t="div",collapsed:a,children:n,animation:i,onCollapseTransitionEnd:l,className:r,disableSSRStyle:s}=e;const c=(0,o.useRef)(null);return u({collapsibleRef:c,collapsed:a,animation:i}),o.createElement(t,{ref:c,style:s?void 0:g(a),onTransitionEnd:e=>{"height"===e.propertyName&&(d(c.current,a),null==l||l(a))},className:r},n)}function f(e){let{collapsed:t,...a}=e;const[i,l]=(0,o.useState)(!t),[r,s]=(0,o.useState)(t);return(0,o.useLayoutEffect)((()=>{t||l(!0)}),[t]),(0,o.useLayoutEffect)((()=>{i&&s(t)}),[i,t]),i?o.createElement(p,(0,n.Z)({},a,{collapsed:r})):null}function b(e){let{lazy:t,...a}=e;const n=t?f:p;return o.createElement(n,a)}},65830:(e,t,a)=>{"use strict";a.d(t,{nT:()=>f,pl:()=>p});var n=a(67294),o=a(51048),i=a(92560),l=a(43768),r=a(20107);const s=(0,i.WA)("docusaurus.announcement.dismiss"),c=(0,i.WA)("docusaurus.announcement.id"),d=()=>"true"===s.get(),u=e=>s.set(String(e)),g=n.createContext(null);function p(e){let{children:t}=e;const a=function(){const{announcementBar:e}=(0,r.L)(),t=(0,o.Z)(),[a,i]=(0,n.useState)((()=>!!t&&d()));(0,n.useEffect)((()=>{i(d())}),[]);const l=(0,n.useCallback)((()=>{u(!0),i(!0)}),[]);return(0,n.useEffect)((()=>{if(!e)return;const{id:t}=e;let a=c.get();"annoucement-bar"===a&&(a="announcement-bar");const n=t!==a;c.set(t),n&&u(!1),!n&&d()||i(!1)}),[e]),(0,n.useMemo)((()=>({isActive:!!e&&!a,close:l})),[e,a,l])}();return n.createElement(g.Provider,{value:a},t)}function f(){const e=(0,n.useContext)(g);if(!e)throw new l.i6("AnnouncementBarProvider");return e}},9200:(e,t,a)=>{"use strict";a.d(t,{I:()=>m,S:()=>b});var n=a(67294),o=a(36136),i=a(43768),l=a(92560),r=a(20107);const s=n.createContext(void 0),c="theme",d=(0,l.WA)(c),u="light",g="dark",p=e=>e===g?g:u;function f(){const{colorMode:{defaultMode:e,disableSwitch:t,respectPrefersColorScheme:a}}=(0,r.L)(),[i,l]=(0,n.useState)((e=>o.Z.canUseDOM?p(document.documentElement.getAttribute("data-theme")):p(e))(e));(0,n.useEffect)((()=>{t&&d.del()}),[t]);const s=(0,n.useCallback)((function(t,n){void 0===n&&(n={});const{persist:o=!0}=n;t?(l(t),o&&(e=>{d.set(p(e))})(t)):(l(a?window.matchMedia("(prefers-color-scheme: dark)").matches?g:u:e),d.del())}),[a,e]);(0,n.useEffect)((()=>{document.documentElement.setAttribute("data-theme",p(i))}),[i]),(0,n.useEffect)((()=>{if(t)return;const e=e=>{if(e.key!==c)return;const t=d.get();null!==t&&s(p(t))};return window.addEventListener("storage",e),()=>window.removeEventListener("storage",e)}),[t,s]);const f=(0,n.useRef)(!1);return(0,n.useEffect)((()=>{if(t&&!a)return;const e=window.matchMedia("(prefers-color-scheme: dark)"),n=()=>{window.matchMedia("print").matches||f.current?f.current=window.matchMedia("print").matches:s(null)};return e.addListener(n),()=>e.removeListener(n)}),[s,t,a]),(0,n.useMemo)((()=>({colorMode:i,setColorMode:s,get isDarkTheme(){return i===g},setLightTheme(){s(u)},setDarkTheme(){s(g)}})),[i,s])}function b(e){let{children:t}=e;const a=f();return n.createElement(s.Provider,{value:a},t)}function m(){const e=(0,n.useContext)(s);if(null==e)throw new i.i6("ColorModeProvider","Please see https://docusaurus.io/docs/api/themes/configuration#use-color-mode.");return e}},86409:(e,t,a)=>{"use strict";a.d(t,{J:()=>y,L5:()=>v});var n=a(67294),o=a(89871),i=a(6875),l=a(20107),r=a(3734),s=a(43768),c=a(92560);const d=e=>`docs-preferred-version-${e}`,u=(e,t,a)=>{(0,c.WA)(d(e),{persistence:t}).set(a)},g=(e,t)=>(0,c.WA)(d(e),{persistence:t}).get(),p=(e,t)=>{(0,c.WA)(d(e),{persistence:t}).del()};const f=n.createContext(null);function b(){const e=(0,o._r)(),t=(0,l.L)().docs.versionPersistence,a=(0,n.useMemo)((()=>Object.keys(e)),[e]),[i,r]=(0,n.useState)((()=>(e=>Object.fromEntries(e.map((e=>[e,{preferredVersionName:null}]))))(a)));(0,n.useEffect)((()=>{r(function(e){let{pluginIds:t,versionPersistence:a,allDocsData:n}=e;function o(e){const t=g(e,a);return n[e].versions.some((e=>e.name===t))?{preferredVersionName:t}:(p(e,a),{preferredVersionName:null})}return Object.fromEntries(t.map((e=>[e,o(e)])))}({allDocsData:e,versionPersistence:t,pluginIds:a}))}),[e,t,a]);return[i,(0,n.useMemo)((()=>({savePreferredVersion:function(e,a){u(e,t,a),r((t=>({...t,[e]:{preferredVersionName:a}})))}})),[t])]}function m(e){let{children:t}=e;const a=b();return n.createElement(f.Provider,{value:a},t)}function v(e){let{children:t}=e;return r.cE?n.createElement(m,null,t):n.createElement(n.Fragment,null,t)}function h(){const e=(0,n.useContext)(f);if(!e)throw new s.i6("DocsPreferredVersionContextProvider");return e}function y(e){void 0===e&&(e=i.m);const t=(0,o.zh)(e),[a,l]=h(),{preferredVersionName:r}=a[e];return{preferredVersion:t.versions.find((e=>e.name===r))??null,savePreferredVersionName:(0,n.useCallback)((t=>{l.savePreferredVersion(e,t)}),[l,e])}}},84432:(e,t,a)=>{"use strict";a.d(t,{V:()=>s,b:()=>r});var n=a(67294),o=a(43768);const i=Symbol("EmptyContext"),l=n.createContext(i);function r(e){let{children:t,name:a,items:o}=e;const i=(0,n.useMemo)((()=>a&&o?{name:a,items:o}:null),[a,o]);return n.createElement(l.Provider,{value:i},t)}function s(){const e=(0,n.useContext)(l);if(e===i)throw new o.i6("DocsSidebarProvider");return e}},58801:(e,t,a)=>{"use strict";a.d(t,{E:()=>r,q:()=>l});var n=a(67294),o=a(43768);const i=n.createContext(null);function l(e){let{children:t,version:a}=e;return n.createElement(i.Provider,{value:a},t)}function r(){const e=(0,n.useContext)(i);if(null===e)throw new o.i6("DocsVersionProvider");return e}},52600:(e,t,a)=>{"use strict";a.d(t,{M:()=>g,e:()=>p});var n=a(67294),o=a(53086),i=a(13488),l=a(76775),r=(a(61688),a(43768));function s(e){!function(e){const t=(0,l.k6)(),a=(0,r.zX)(e);(0,n.useEffect)((()=>t.block(((e,t)=>a(e,t)))),[t,a])}(((t,a)=>{if("POP"===a)return e(t,a)}))}var c=a(20107);const d=n.createContext(void 0);function u(){const e=function(){const e=(0,o.HY)(),{items:t}=(0,c.L)().navbar;return 0===t.length&&!e.component}(),t=(0,i.i)(),a=!e&&"mobile"===t,[l,r]=(0,n.useState)(!1);s((()=>{if(l)return r(!1),!1}));const d=(0,n.useCallback)((()=>{r((e=>!e))}),[]);return(0,n.useEffect)((()=>{"desktop"===t&&r(!1)}),[t]),(0,n.useMemo)((()=>({disabled:e,shouldRender:a,toggle:d,shown:l})),[e,a,d,l])}function g(e){let{children:t}=e;const a=u();return n.createElement(d.Provider,{value:a},t)}function p(){const e=n.useContext(d);if(void 0===e)throw new r.i6("NavbarMobileSidebarProvider");return e}},53086:(e,t,a)=>{"use strict";a.d(t,{HY:()=>r,Zo:()=>s,n2:()=>l});var n=a(67294),o=a(43768);const i=n.createContext(null);function l(e){let{children:t}=e;const a=(0,n.useState)({component:null,props:null});return n.createElement(i.Provider,{value:a},t)}function r(){const e=(0,n.useContext)(i);if(!e)throw new o.i6("NavbarSecondaryMenuContentProvider");return e[0]}function s(e){let{component:t,props:a}=e;const l=(0,n.useContext)(i);if(!l)throw new o.i6("NavbarSecondaryMenuContentProvider");const[,r]=l,s=(0,o.Ql)(a);return(0,n.useEffect)((()=>{r({component:t,props:s})}),[r,t,s]),(0,n.useEffect)((()=>()=>r({component:null,props:null})),[r]),null}},78181:(e,t,a)=>{"use strict";a.d(t,{h:()=>o,t:()=>i});var n=a(67294);const o="navigation-with-keyboard";function i(){(0,n.useEffect)((()=>{function e(e){"keydown"===e.type&&"Tab"===e.key&&document.body.classList.add(o),"mousedown"===e.type&&document.body.classList.remove(o)}return document.addEventListener("keydown",e),document.addEventListener("mousedown",e),()=>{document.body.classList.remove(o),document.removeEventListener("keydown",e),document.removeEventListener("mousedown",e)}}),[])}},13488:(e,t,a)=>{"use strict";a.d(t,{i:()=>c});var n=a(67294),o=a(36136);const i="desktop",l="mobile",r="ssr";function s(){return o.Z.canUseDOM?window.innerWidth>996?i:l:r}function c(){const[e,t]=(0,n.useState)((()=>s()));return(0,n.useEffect)((()=>{function e(){t(s())}return window.addEventListener("resize",e),()=>{window.removeEventListener("resize",e),clearTimeout(undefined)}}),[]),e}},23702:(e,t,a)=>{"use strict";a.d(t,{k:()=>n});const n={page:{blogListPage:"blog-list-page",blogPostPage:"blog-post-page",blogTagsListPage:"blog-tags-list-page",blogTagPostListPage:"blog-tags-post-list-page",docsDocPage:"docs-doc-page",docsTagsListPage:"docs-tags-list-page",docsTagDocListPage:"docs-tags-doc-list-page",mdxPage:"mdx-page"},wrapper:{main:"main-wrapper",blogPages:"blog-wrapper",docsPages:"docs-wrapper",mdxPages:"mdx-wrapper"},common:{editThisPage:"theme-edit-this-page",lastUpdated:"theme-last-updated",backToTopButton:"theme-back-to-top-button",codeBlock:"theme-code-block",admonition:"theme-admonition",admonitionType:e=>`theme-admonition-${e}`},layout:{},docs:{docVersionBanner:"theme-doc-version-banner",docVersionBadge:"theme-doc-version-badge",docBreadcrumbs:"theme-doc-breadcrumbs",docMarkdown:"theme-doc-markdown",docTocMobile:"theme-doc-toc-mobile",docTocDesktop:"theme-doc-toc-desktop",docFooter:"theme-doc-footer",docFooterTagsRow:"theme-doc-footer-tags-row",docFooterEditMetaRow:"theme-doc-footer-edit-meta-row",docSidebarContainer:"theme-doc-sidebar-container",docSidebarMenu:"theme-doc-sidebar-menu",docSidebarItemCategory:"theme-doc-sidebar-item-category",docSidebarItemLink:"theme-doc-sidebar-item-link",docSidebarItemCategoryLevel:e=>`theme-doc-sidebar-item-category-level-${e}`,docSidebarItemLinkLevel:e=>`theme-doc-sidebar-item-link-level-${e}`},blog:{}}},58986:(e,t,a)=>{"use strict";function n(){return window.matchMedia("(prefers-reduced-motion: reduce)").matches}a.d(t,{n:()=>n})},3734:(e,t,a)=>{"use strict";a.d(t,{MN:()=>k,Wl:()=>f,_F:()=>v,cE:()=>g,jA:()=>b,xz:()=>p,hI:()=>x,lO:()=>_,vY:()=>N,oz:()=>C,s1:()=>y});var n=a(67294),o=a(76775),i=a(18790),l=a(89871),r=a(86409),s=a(58801),c=a(84432);function d(e){return Array.from(new Set(e))}var u=a(69003);const g=!!l._r;function p(e){const t=(0,s.E)();if(!e)return;const a=t.docs[e];if(!a)throw new Error(`no version doc found by id=${e}`);return a}function f(e){if(e.href)return e.href;for(const t of e.items){if("link"===t.type)return t.href;if("category"===t.type){const e=f(t);if(e)return e}}}function b(){const{pathname:e}=(0,o.TH)(),t=(0,c.V)();if(!t)throw new Error("Unexpected: cant find current sidebar in context");const a=h({sidebarItems:t.items,pathname:e,onlyCategories:!0}).slice(-1)[0];if(!a)throw new Error(`${e} is not associated with a category. useCurrentSidebarCategory() should only be used on category index pages.`);return a}const m=(e,t)=>void 0!==e&&(0,u.Mg)(e,t);function v(e,t){return"link"===e.type?m(e.href,t):"category"===e.type&&(m(e.href,t)||((e,t)=>e.some((e=>v(e,t))))(e.items,t))}function h(e){let{sidebarItems:t,pathname:a,onlyCategories:n=!1}=e;const o=[];return function e(t){for(const i of t)if("category"===i.type&&((0,u.Mg)(i.href,a)||e(i.items))||"link"===i.type&&(0,u.Mg)(i.href,a)){return n&&"category"!==i.type||o.unshift(i),!0}return!1}(t),o}function y(){var e;const t=(0,c.V)(),{pathname:a}=(0,o.TH)();return!1!==(null==(e=(0,l.gA)())?void 0:e.pluginData.breadcrumbs)&&t?h({sidebarItems:t.items,pathname:a}):null}function _(e){const{activeVersion:t}=(0,l.Iw)(e),{preferredVersion:a}=(0,r.J)(e),o=(0,l.yW)(e);return(0,n.useMemo)((()=>d([t,a,o].filter(Boolean))),[t,a,o])}function C(e,t){const a=_(t);return(0,n.useMemo)((()=>{const t=a.flatMap((e=>e.sidebars?Object.entries(e.sidebars):[])),n=t.find((t=>t[0]===e));if(!n)throw new Error(`Can't find any sidebar with id "${e}" in version${a.length>1?"s":""} ${a.map((e=>e.name)).join(", ")}".\nAvailable sidebar ids are:\n- ${t.map((e=>e[0])).join("\n- ")}`);return n[1]}),[e,a])}function N(e,t){const a=_(t);return(0,n.useMemo)((()=>{const t=a.flatMap((e=>e.docs)),n=t.find((t=>t.id===e));if(!n){if(a.flatMap((e=>e.draftIds)).includes(e))return null;throw new Error(`Couldn't find any doc with id "${e}" in version${a.length>1?"s":""} "${a.map((e=>e.name)).join(", ")}".\nAvailable doc ids are:\n- ${d(t.map((e=>e.id))).join("\n- ")}`)}return n}),[e,a])}function x(e){let{route:t,versionMetadata:a}=e;const n=(0,o.TH)(),l=t.routes,r=l.find((e=>(0,o.LX)(n.pathname,e)));if(!r)return null;const s=r.sidebar,c=s?a.docsSidebars[s]:void 0;return{docElement:(0,i.H)(l),sidebarName:s,sidebarItems:c}}function k(e){return e.filter((e=>"category"!==e.type||!!f(e)))}},35463:(e,t,a)=>{"use strict";a.d(t,{FG:()=>g,d:()=>d,VC:()=>p});var n=a(67294),o=a(86010),i=a(31514),l=a(69656);function r(){const e=n.useContext(l._);if(!e)throw new Error("Unexpected: no Docusaurus route context found");return e}var s=a(79524),c=a(39962);function d(e){let{title:t,description:a,keywords:o,image:l,children:r}=e;const d=function(e){const{siteConfig:t}=(0,c.Z)(),{title:a,titleDelimiter:n}=t;return null!=e&&e.trim().length?`${e.trim()} ${n} ${a}`:a}(t),{withBaseUrl:u}=(0,s.C)(),g=l?u(l,{absolute:!0}):void 0;return n.createElement(i.Z,null,t&&n.createElement("title",null,d),t&&n.createElement("meta",{property:"og:title",content:d}),a&&n.createElement("meta",{name:"description",content:a}),a&&n.createElement("meta",{property:"og:description",content:a}),o&&n.createElement("meta",{name:"keywords",content:Array.isArray(o)?o.join(","):o}),g&&n.createElement("meta",{property:"og:image",content:g}),g&&n.createElement("meta",{name:"twitter:image",content:g}),r)}const u=n.createContext(void 0);function g(e){let{className:t,children:a}=e;const l=n.useContext(u),r=(0,o.Z)(l,t);return n.createElement(u.Provider,{value:r},n.createElement(i.Z,null,n.createElement("html",{className:r})),a)}function p(e){let{children:t}=e;const a=r(),i=`plugin-${a.plugin.name.replace(/docusaurus-(?:plugin|theme)-(?:content-)?/gi,"")}`;const l=`plugin-id-${a.plugin.id}`;return n.createElement(g,{className:(0,o.Z)(i,l)},t)}},43768:(e,t,a)=>{"use strict";a.d(t,{D9:()=>l,Qc:()=>c,Ql:()=>s,i6:()=>r,zX:()=>i});var n=a(67294);const o=a(36136).Z.canUseDOM?n.useLayoutEffect:n.useEffect;function i(e){const t=(0,n.useRef)(e);return o((()=>{t.current=e}),[e]),(0,n.useCallback)((function(){return t.current(...arguments)}),[])}function l(e){const t=(0,n.useRef)();return o((()=>{t.current=e})),t.current}class r extends Error{constructor(e,t){var a,n,o;super(),this.name="ReactContextError",this.message=`Hook ${(null==(a=this.stack)||null==(n=a.split("\n")[1])||null==(o=n.match(/at (?:\w+\.)?(?\w+)/))?void 0:o.groups.name)??""} is called outside the <${e}>. ${t??""}`}}function s(e){const t=Object.entries(e);return t.sort(((e,t)=>e[0].localeCompare(t[0]))),(0,n.useMemo)((()=>e),t.flat())}function c(e){return t=>{let{children:a}=t;return n.createElement(n.Fragment,null,e.reduceRight(((e,t)=>n.createElement(t,null,e)),a))}}},69003:(e,t,a)=>{"use strict";a.d(t,{Mg:()=>l,Ns:()=>r});var n=a(67294),o=a(50997),i=a(39962);function l(e,t){const a=e=>{var t;return null==(t=!e||e.endsWith("/")?e:`${e}/`)?void 0:t.toLowerCase()};return a(e)===a(t)}function r(){const{baseUrl:e}=(0,i.Z)().siteConfig;return(0,n.useMemo)((()=>function(e){let{baseUrl:t,routes:a}=e;function n(e){return e.path===t&&!0===e.exact}function o(e){return e.path===t&&!e.exact}return function e(t){if(0===t.length)return;return t.find(n)||e(t.filter(o).flatMap((e=>e.routes??[])))}(a)}({routes:o.Z,baseUrl:e})),[e])}},72957:(e,t,a)=>{"use strict";a.d(t,{Ct:()=>g,OC:()=>s,RF:()=>u});var n=a(67294),o=a(36136),i=a(51048),l=a(43768);const r=n.createContext(void 0);function s(e){let{children:t}=e;const a=function(){const e=(0,n.useRef)(!0);return(0,n.useMemo)((()=>({scrollEventsEnabledRef:e,enableScrollEvents:()=>{e.current=!0},disableScrollEvents:()=>{e.current=!1}})),[])}();return n.createElement(r.Provider,{value:a},t)}function c(){const e=(0,n.useContext)(r);if(null==e)throw new l.i6("ScrollControllerProvider");return e}const d=()=>o.Z.canUseDOM?{scrollX:window.pageXOffset,scrollY:window.pageYOffset}:null;function u(e,t){void 0===t&&(t=[]);const{scrollEventsEnabledRef:a}=c(),o=(0,n.useRef)(d()),i=(0,l.zX)(e);(0,n.useEffect)((()=>{const e=()=>{if(!a.current)return;const e=d();i(e,o.current),o.current=e},t={passive:!0};return e(),window.addEventListener("scroll",e,t),()=>window.removeEventListener("scroll",e,t)}),[i,a,...t])}function g(){const e=(0,n.useRef)(null),t=(0,i.Z)()&&"smooth"===getComputedStyle(document.documentElement).scrollBehavior;return{startScroll:a=>{e.current=t?function(e){return window.scrollTo({top:e,behavior:"smooth"}),()=>{}}(a):function(e){let t=null;const a=document.documentElement.scrollTop>e;return function n(){const o=document.documentElement.scrollTop;(a&&o>e||!a&&ot&&cancelAnimationFrame(t)}(a)},cancelScroll:()=>null==e.current?void 0:e.current()}}},60246:(e,t,a)=>{"use strict";a.d(t,{HX:()=>n,os:()=>o});a(39962);const n="default";function o(e,t){return`docs-${e}-${t}`}},92560:(e,t,a)=>{"use strict";a.d(t,{WA:()=>s});a(67294),a(61688);const n="localStorage";function o(e){let{key:t,oldValue:a,newValue:n,storage:o}=e;if(a===n)return;const i=document.createEvent("StorageEvent");i.initStorageEvent("storage",!1,!1,t,a,n,window.location.href,o),window.dispatchEvent(i)}function i(e){if(void 0===e&&(e=n),"undefined"==typeof window)throw new Error("Browser storage is not available on Node.js/Docusaurus SSR process.");if("none"===e)return null;try{return window[e]}catch(a){return t=a,l||(console.warn("Docusaurus browser storage is not available.\nPossible reasons: running Docusaurus in an iframe, in an incognito browser session, or using too strict browser privacy settings.",t),l=!0),null}var t}let l=!1;const r={get:()=>null,set:()=>{},del:()=>{},listen:()=>()=>{}};function s(e,t){if("undefined"==typeof window)return function(e){function t(){throw new Error(`Illegal storage API usage for storage key "${e}".\nDocusaurus storage APIs are not supposed to be called on the server-rendering process.\nPlease only call storage APIs in effects and event handlers.`)}return{get:t,set:t,del:t,listen:t}}(e);const a=i(null==t?void 0:t.persistence);return null===a?r:{get:()=>{try{return a.getItem(e)}catch(t){return console.error(`Docusaurus storage error, can't get key=${e}`,t),null}},set:t=>{try{const n=a.getItem(e);a.setItem(e,t),o({key:e,oldValue:n,newValue:t,storage:a})}catch(n){console.error(`Docusaurus storage error, can't set ${e}=${t}`,n)}},del:()=>{try{const t=a.getItem(e);a.removeItem(e),o({key:e,oldValue:t,newValue:null,storage:a})}catch(t){console.error(`Docusaurus storage error, can't delete key=${e}`,t)}},listen:t=>{try{const n=n=>{n.storageArea===a&&n.key===e&&t(n)};return window.addEventListener("storage",n),()=>window.removeEventListener("storage",n)}catch(n){return console.error(`Docusaurus storage error, can't listen for changes of key=${e}`,n),()=>{}}}}}},40626:(e,t,a)=>{"use strict";a.d(t,{l:()=>i});var n=a(39962),o=a(76775);function i(){const{siteConfig:{baseUrl:e,url:t},i18n:{defaultLocale:a,currentLocale:i}}=(0,n.Z)(),{pathname:l}=(0,o.TH)(),r=i===a?e:e.replace(`/${i}/`,"/"),s=l.replace(e,"");return{createUrl:function(e){let{locale:n,fullyQualified:o}=e;return`${o?t:""}${function(e){return e===a?`${r}`:`${r}${e}/`}(n)}${s}`}}}},43266:(e,t,a)=>{"use strict";a.d(t,{S:()=>l});var n=a(67294),o=a(76775),i=a(43768);function l(e){const t=(0,o.TH)(),a=(0,i.D9)(t),l=(0,i.zX)(e);(0,n.useEffect)((()=>{a&&t!==a&&l({location:t,previousLocation:a})}),[l,t,a])}},20107:(e,t,a)=>{"use strict";a.d(t,{L:()=>o});var n=a(39962);function o(){return(0,n.Z)().siteConfig.themeConfig}},84136:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e,t){const{trailingSlash:a,baseUrl:n}=t;if(e.startsWith("#"))return e;if(void 0===a)return e;const[o]=e.split(/[#?]/),i="/"===o||o===n?o:(l=o,a?function(e){return e.endsWith("/")?e:`${e}/`}(l):function(e){return e.endsWith("/")?e.slice(0,-1):e}(l));var l;return e.replace(o,i)}},15806:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.getErrorCausalChain=void 0,t.getErrorCausalChain=function e(t){return t.cause?[t,...e(t.cause)]:[t]}},63905:function(e,t,a){"use strict";var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.getErrorCausalChain=t.applyTrailingSlash=t.blogPostContainerID=void 0,t.blogPostContainerID="__blog-post-container";var o=a(84136);Object.defineProperty(t,"applyTrailingSlash",{enumerable:!0,get:function(){return n(o).default}});var i=a(15806);Object.defineProperty(t,"getErrorCausalChain",{enumerable:!0,get:function(){return i.getErrorCausalChain}})},86010:(e,t,a)=>{"use strict";function n(e){var t,a,o="";if("string"==typeof e||"number"==typeof e)o+=e;else if("object"==typeof e)if(Array.isArray(e))for(t=0;to});const o=function(){for(var e,t,a=0,o="";a{"use strict";a.d(t,{lX:()=>_,q_:()=>A,ob:()=>p,PP:()=>I,Ep:()=>g});var n=a(87462);function o(e){return"/"===e.charAt(0)}function i(e,t){for(var a=t,n=a+1,o=e.length;n=0;g--){var p=l[g];"."===p?i(l,g):".."===p?(i(l,g),u++):u&&(i(l,g),u--)}if(!c)for(;u--;u)l.unshift("..");!c||""===l[0]||l[0]&&o(l[0])||l.unshift("");var f=l.join("/");return a&&"/"!==f.substr(-1)&&(f+="/"),f};var r=a(2177);function s(e){return"/"===e.charAt(0)?e:"/"+e}function c(e){return"/"===e.charAt(0)?e.substr(1):e}function d(e,t){return function(e,t){return 0===e.toLowerCase().indexOf(t.toLowerCase())&&-1!=="/?#".indexOf(e.charAt(t.length))}(e,t)?e.substr(t.length):e}function u(e){return"/"===e.charAt(e.length-1)?e.slice(0,-1):e}function g(e){var t=e.pathname,a=e.search,n=e.hash,o=t||"/";return a&&"?"!==a&&(o+="?"===a.charAt(0)?a:"?"+a),n&&"#"!==n&&(o+="#"===n.charAt(0)?n:"#"+n),o}function p(e,t,a,o){var i;"string"==typeof e?(i=function(e){var t=e||"/",a="",n="",o=t.indexOf("#");-1!==o&&(n=t.substr(o),t=t.substr(0,o));var i=t.indexOf("?");return-1!==i&&(a=t.substr(i),t=t.substr(0,i)),{pathname:t,search:"?"===a?"":a,hash:"#"===n?"":n}}(e),i.state=t):(void 0===(i=(0,n.Z)({},e)).pathname&&(i.pathname=""),i.search?"?"!==i.search.charAt(0)&&(i.search="?"+i.search):i.search="",i.hash?"#"!==i.hash.charAt(0)&&(i.hash="#"+i.hash):i.hash="",void 0!==t&&void 0===i.state&&(i.state=t));try{i.pathname=decodeURI(i.pathname)}catch(r){throw r instanceof URIError?new URIError('Pathname "'+i.pathname+'" could not be decoded. This is likely caused by an invalid percent-encoding.'):r}return a&&(i.key=a),o?i.pathname?"/"!==i.pathname.charAt(0)&&(i.pathname=l(i.pathname,o.pathname)):i.pathname=o.pathname:i.pathname||(i.pathname="/"),i}function f(){var e=null;var t=[];return{setPrompt:function(t){return e=t,function(){e===t&&(e=null)}},confirmTransitionTo:function(t,a,n,o){if(null!=e){var i="function"==typeof e?e(t,a):e;"string"==typeof i?"function"==typeof n?n(i,o):o(!0):o(!1!==i)}else o(!0)},appendListener:function(e){var a=!0;function n(){a&&e.apply(void 0,arguments)}return t.push(n),function(){a=!1,t=t.filter((function(e){return e!==n}))}},notifyListeners:function(){for(var e=arguments.length,a=new Array(e),n=0;nt?a.splice(t,a.length-t,o):a.push(o),u({action:n,location:o,index:t,entries:a})}}))},replace:function(e,t){var n="REPLACE",o=p(e,t,b(),_.location);d.confirmTransitionTo(o,n,a,(function(e){e&&(_.entries[_.index]=o,u({action:n,location:o}))}))},go:y,goBack:function(){y(-1)},goForward:function(){y(1)},canGo:function(e){var t=_.index+e;return t>=0&&t<_.entries.length},block:function(e){return void 0===e&&(e=!1),d.setPrompt(e)},listen:function(e){return d.appendListener(e)}};return _}},8679:(e,t,a)=>{"use strict";var n=a(59864),o={childContextTypes:!0,contextType:!0,contextTypes:!0,defaultProps:!0,displayName:!0,getDefaultProps:!0,getDerivedStateFromError:!0,getDerivedStateFromProps:!0,mixins:!0,propTypes:!0,type:!0},i={name:!0,length:!0,prototype:!0,caller:!0,callee:!0,arguments:!0,arity:!0},l={$$typeof:!0,compare:!0,defaultProps:!0,displayName:!0,propTypes:!0,type:!0},r={};function s(e){return n.isMemo(e)?l:r[e.$$typeof]||o}r[n.ForwardRef]={$$typeof:!0,render:!0,defaultProps:!0,displayName:!0,propTypes:!0},r[n.Memo]=l;var c=Object.defineProperty,d=Object.getOwnPropertyNames,u=Object.getOwnPropertySymbols,g=Object.getOwnPropertyDescriptor,p=Object.getPrototypeOf,f=Object.prototype;e.exports=function e(t,a,n){if("string"!=typeof a){if(f){var o=p(a);o&&o!==f&&e(t,o,n)}var l=d(a);u&&(l=l.concat(u(a)));for(var r=s(t),b=s(a),m=0;m{"use strict";e.exports=function(e,t,a,n,o,i,l,r){if(!e){var s;if(void 0===t)s=new Error("Minified exception occurred; use the non-minified dev environment for the full error message and additional helpful warnings.");else{var c=[a,n,o,i,l,r],d=0;(s=new Error(t.replace(/%s/g,(function(){return c[d++]})))).name="Invariant Violation"}throw s.framesToPop=1,s}}},5826:e=>{e.exports=Array.isArray||function(e){return"[object Array]"==Object.prototype.toString.call(e)}},30984:(e,t,a)=>{"use strict";a.r(t)},46930:(e,t,a)=>{"use strict";a.r(t)},74865:function(e,t,a){var n,o;n=function(){var e,t,a={version:"0.2.0"},n=a.settings={minimum:.08,easing:"ease",positionUsing:"",speed:200,trickle:!0,trickleRate:.02,trickleSpeed:800,showSpinner:!0,barSelector:'[role="bar"]',spinnerSelector:'[role="spinner"]',parent:"body",template:'
    '};function o(e,t,a){return ea?a:e}function i(e){return 100*(-1+e)}function l(e,t,a){var o;return(o="translate3d"===n.positionUsing?{transform:"translate3d("+i(e)+"%,0,0)"}:"translate"===n.positionUsing?{transform:"translate("+i(e)+"%,0)"}:{"margin-left":i(e)+"%"}).transition="all "+t+"ms "+a,o}a.configure=function(e){var t,a;for(t in e)void 0!==(a=e[t])&&e.hasOwnProperty(t)&&(n[t]=a);return this},a.status=null,a.set=function(e){var t=a.isStarted();e=o(e,n.minimum,1),a.status=1===e?null:e;var i=a.render(!t),c=i.querySelector(n.barSelector),d=n.speed,u=n.easing;return i.offsetWidth,r((function(t){""===n.positionUsing&&(n.positionUsing=a.getPositioningCSS()),s(c,l(e,d,u)),1===e?(s(i,{transition:"none",opacity:1}),i.offsetWidth,setTimeout((function(){s(i,{transition:"all "+d+"ms linear",opacity:0}),setTimeout((function(){a.remove(),t()}),d)}),d)):setTimeout(t,d)})),this},a.isStarted=function(){return"number"==typeof a.status},a.start=function(){a.status||a.set(0);var e=function(){setTimeout((function(){a.status&&(a.trickle(),e())}),n.trickleSpeed)};return n.trickle&&e(),this},a.done=function(e){return e||a.status?a.inc(.3+.5*Math.random()).set(1):this},a.inc=function(e){var t=a.status;return t?("number"!=typeof e&&(e=(1-t)*o(Math.random()*t,.1,.95)),t=o(t+e,0,.994),a.set(t)):a.start()},a.trickle=function(){return a.inc(Math.random()*n.trickleRate)},e=0,t=0,a.promise=function(n){return n&&"resolved"!==n.state()?(0===t&&a.start(),e++,t++,n.always((function(){0==--t?(e=0,a.done()):a.set((e-t)/e)})),this):this},a.render=function(e){if(a.isRendered())return document.getElementById("nprogress");d(document.documentElement,"nprogress-busy");var t=document.createElement("div");t.id="nprogress",t.innerHTML=n.template;var o,l=t.querySelector(n.barSelector),r=e?"-100":i(a.status||0),c=document.querySelector(n.parent);return s(l,{transition:"all 0 linear",transform:"translate3d("+r+"%,0,0)"}),n.showSpinner||(o=t.querySelector(n.spinnerSelector))&&p(o),c!=document.body&&d(c,"nprogress-custom-parent"),c.appendChild(t),t},a.remove=function(){u(document.documentElement,"nprogress-busy"),u(document.querySelector(n.parent),"nprogress-custom-parent");var e=document.getElementById("nprogress");e&&p(e)},a.isRendered=function(){return!!document.getElementById("nprogress")},a.getPositioningCSS=function(){var e=document.body.style,t="WebkitTransform"in e?"Webkit":"MozTransform"in e?"Moz":"msTransform"in e?"ms":"OTransform"in e?"O":"";return t+"Perspective"in e?"translate3d":t+"Transform"in e?"translate":"margin"};var r=function(){var e=[];function t(){var a=e.shift();a&&a(t)}return function(a){e.push(a),1==e.length&&t()}}(),s=function(){var e=["Webkit","O","Moz","ms"],t={};function a(e){return e.replace(/^-ms-/,"ms-").replace(/-([\da-z])/gi,(function(e,t){return t.toUpperCase()}))}function n(t){var a=document.body.style;if(t in a)return t;for(var n,o=e.length,i=t.charAt(0).toUpperCase()+t.slice(1);o--;)if((n=e[o]+i)in a)return n;return t}function o(e){return e=a(e),t[e]||(t[e]=n(e))}function i(e,t,a){t=o(t),e.style[t]=a}return function(e,t){var a,n,o=arguments;if(2==o.length)for(a in t)void 0!==(n=t[a])&&t.hasOwnProperty(a)&&i(e,a,n);else i(e,o[1],o[2])}}();function c(e,t){return("string"==typeof e?e:g(e)).indexOf(" "+t+" ")>=0}function d(e,t){var a=g(e),n=a+t;c(a,t)||(e.className=n.substring(1))}function u(e,t){var a,n=g(e);c(e,t)&&(a=n.replace(" "+t+" "," "),e.className=a.substring(1,a.length-1))}function g(e){return(" "+(e.className||"")+" ").replace(/\s+/gi," ")}function p(e){e&&e.parentNode&&e.parentNode.removeChild(e)}return a},void 0===(o="function"==typeof n?n.call(t,a,t,e):n)||(e.exports=o)},27418:e=>{"use strict";var t=Object.getOwnPropertySymbols,a=Object.prototype.hasOwnProperty,n=Object.prototype.propertyIsEnumerable;function o(e){if(null==e)throw new TypeError("Object.assign cannot be called with null or undefined");return Object(e)}e.exports=function(){try{if(!Object.assign)return!1;var e=new String("abc");if(e[5]="de","5"===Object.getOwnPropertyNames(e)[0])return!1;for(var t={},a=0;a<10;a++)t["_"+String.fromCharCode(a)]=a;if("0123456789"!==Object.getOwnPropertyNames(t).map((function(e){return t[e]})).join(""))return!1;var n={};return"abcdefghijklmnopqrst".split("").forEach((function(e){n[e]=e})),"abcdefghijklmnopqrst"===Object.keys(Object.assign({},n)).join("")}catch(o){return!1}}()?Object.assign:function(e,i){for(var l,r,s=o(e),c=1;c{var n=a(5826);e.exports=p,e.exports.parse=i,e.exports.compile=function(e,t){return r(i(e,t),t)},e.exports.tokensToFunction=r,e.exports.tokensToRegExp=g;var o=new RegExp(["(\\\\.)","([\\/.])?(?:(?:\\:(\\w+)(?:\\(((?:\\\\.|[^\\\\()])+)\\))?|\\(((?:\\\\.|[^\\\\()])+)\\))([+*?])?|(\\*))"].join("|"),"g");function i(e,t){for(var a,n=[],i=0,l=0,r="",d=t&&t.delimiter||"/";null!=(a=o.exec(e));){var u=a[0],g=a[1],p=a.index;if(r+=e.slice(l,p),l=p+u.length,g)r+=g[1];else{var f=e[l],b=a[2],m=a[3],v=a[4],h=a[5],y=a[6],_=a[7];r&&(n.push(r),r="");var C=null!=b&&null!=f&&f!==b,N="+"===y||"*"===y,x="?"===y||"*"===y,k=a[2]||d,w=v||h;n.push({name:m||i++,prefix:b||"",delimiter:k,optional:x,repeat:N,partial:C,asterisk:!!_,pattern:w?c(w):_?".*":"[^"+s(k)+"]+?"})}}return l{"use strict";a.d(t,{Z:()=>i});var n=function(){var e=/(?:^|\s)lang(?:uage)?-([\w-]+)(?=\s|$)/i,t=0,a={},n={util:{encode:function e(t){return t instanceof o?new o(t.type,e(t.content),t.alias):Array.isArray(t)?t.map(e):t.replace(/&/g,"&").replace(/=u.reach);x+=N.value.length,N=N.next){var k=N.value;if(t.length>e.length)return;if(!(k instanceof o)){var w,A=1;if(h){if(!(w=i(C,x,e,v))||w.index>=e.length)break;var E=w.index,I=w.index+w[0].length,j=x;for(j+=N.value.length;E>=j;)j+=(N=N.next).value.length;if(x=j-=N.value.length,N.value instanceof o)continue;for(var S=N;S!==t.tail&&(ju.reach&&(u.reach=L);var P=N.prev;if(M&&(P=s(t,P,M),x+=M.length),c(t,P,A),N=s(t,P,new o(g,m?n.tokenize(z,m):z,y,z)),T&&s(t,N,T),A>1){var R={cause:g+","+f,reach:L};l(e,t,a,N.prev,x,R),u&&R.reach>u.reach&&(u.reach=R.reach)}}}}}}function r(){var e={value:null,prev:null,next:null},t={value:null,prev:e,next:null};e.next=t,this.head=e,this.tail=t,this.length=0}function s(e,t,a){var n=t.next,o={value:a,prev:t,next:n};return t.next=o,n.prev=o,e.length++,o}function c(e,t,a){for(var n=t.next,o=0;o"+i.content+""},n}(),o=n;n.default=n,o.languages.markup={comment:{pattern://,greedy:!0},prolog:{pattern:/<\?[\s\S]+?\?>/,greedy:!0},doctype:{pattern:/"'[\]]|"[^"]*"|'[^']*')+(?:\[(?:[^<"'\]]|"[^"]*"|'[^']*'|<(?!!--)|)*\]\s*)?>/i,greedy:!0,inside:{"internal-subset":{pattern:/(^[^\[]*\[)[\s\S]+(?=\]>$)/,lookbehind:!0,greedy:!0,inside:null},string:{pattern:/"[^"]*"|'[^']*'/,greedy:!0},punctuation:/^$|[[\]]/,"doctype-tag":/^DOCTYPE/i,name:/[^\s<>'"]+/}},cdata:{pattern://i,greedy:!0},tag:{pattern:/<\/?(?!\d)[^\s>\/=$<%]+(?:\s(?:\s*[^\s>\/=]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))|(?=[\s/>])))+)?\s*\/?>/,greedy:!0,inside:{tag:{pattern:/^<\/?[^\s>\/]+/,inside:{punctuation:/^<\/?/,namespace:/^[^\s>\/:]+:/}},"special-attr":[],"attr-value":{pattern:/=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+)/,inside:{punctuation:[{pattern:/^=/,alias:"attr-equals"},/"|'/]}},punctuation:/\/?>/,"attr-name":{pattern:/[^\s>\/]+/,inside:{namespace:/^[^\s>\/:]+:/}}}},entity:[{pattern:/&[\da-z]{1,8};/i,alias:"named-entity"},/&#x?[\da-f]{1,8};/i]},o.languages.markup.tag.inside["attr-value"].inside.entity=o.languages.markup.entity,o.languages.markup.doctype.inside["internal-subset"].inside=o.languages.markup,o.hooks.add("wrap",(function(e){"entity"===e.type&&(e.attributes.title=e.content.replace(/&/,"&"))})),Object.defineProperty(o.languages.markup.tag,"addInlined",{value:function(e,t){var a={};a["language-"+t]={pattern:/(^$)/i,lookbehind:!0,inside:o.languages[t]},a.cdata=/^$/i;var n={"included-cdata":{pattern://i,inside:a}};n["language-"+t]={pattern:/[\s\S]+/,inside:o.languages[t]};var i={};i[e]={pattern:RegExp(/(<__[^>]*>)(?:))*\]\]>|(?!)/.source.replace(/__/g,(function(){return e})),"i"),lookbehind:!0,greedy:!0,inside:n},o.languages.insertBefore("markup","cdata",i)}}),Object.defineProperty(o.languages.markup.tag,"addAttribute",{value:function(e,t){o.languages.markup.tag.inside["special-attr"].push({pattern:RegExp(/(^|["'\s])/.source+"(?:"+e+")"+/\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))/.source,"i"),lookbehind:!0,inside:{"attr-name":/^[^\s=]+/,"attr-value":{pattern:/=[\s\S]+/,inside:{value:{pattern:/(^=\s*(["']|(?!["'])))\S[\s\S]*(?=\2$)/,lookbehind:!0,alias:[t,"language-"+t],inside:o.languages[t]},punctuation:[{pattern:/^=/,alias:"attr-equals"},/"|'/]}}}})}}),o.languages.html=o.languages.markup,o.languages.mathml=o.languages.markup,o.languages.svg=o.languages.markup,o.languages.xml=o.languages.extend("markup",{}),o.languages.ssml=o.languages.xml,o.languages.atom=o.languages.xml,o.languages.rss=o.languages.xml,function(e){var t="\\b(?:BASH|BASHOPTS|BASH_ALIASES|BASH_ARGC|BASH_ARGV|BASH_CMDS|BASH_COMPLETION_COMPAT_DIR|BASH_LINENO|BASH_REMATCH|BASH_SOURCE|BASH_VERSINFO|BASH_VERSION|COLORTERM|COLUMNS|COMP_WORDBREAKS|DBUS_SESSION_BUS_ADDRESS|DEFAULTS_PATH|DESKTOP_SESSION|DIRSTACK|DISPLAY|EUID|GDMSESSION|GDM_LANG|GNOME_KEYRING_CONTROL|GNOME_KEYRING_PID|GPG_AGENT_INFO|GROUPS|HISTCONTROL|HISTFILE|HISTFILESIZE|HISTSIZE|HOME|HOSTNAME|HOSTTYPE|IFS|INSTANCE|JOB|LANG|LANGUAGE|LC_ADDRESS|LC_ALL|LC_IDENTIFICATION|LC_MEASUREMENT|LC_MONETARY|LC_NAME|LC_NUMERIC|LC_PAPER|LC_TELEPHONE|LC_TIME|LESSCLOSE|LESSOPEN|LINES|LOGNAME|LS_COLORS|MACHTYPE|MAILCHECK|MANDATORY_PATH|NO_AT_BRIDGE|OLDPWD|OPTERR|OPTIND|ORBIT_SOCKETDIR|OSTYPE|PAPERSIZE|PATH|PIPESTATUS|PPID|PS1|PS2|PS3|PS4|PWD|RANDOM|REPLY|SECONDS|SELINUX_INIT|SESSION|SESSIONTYPE|SESSION_MANAGER|SHELL|SHELLOPTS|SHLVL|SSH_AUTH_SOCK|TERM|UID|UPSTART_EVENTS|UPSTART_INSTANCE|UPSTART_JOB|UPSTART_SESSION|USER|WINDOWID|XAUTHORITY|XDG_CONFIG_DIRS|XDG_CURRENT_DESKTOP|XDG_DATA_DIRS|XDG_GREETER_DATA_DIR|XDG_MENU_PREFIX|XDG_RUNTIME_DIR|XDG_SEAT|XDG_SEAT_PATH|XDG_SESSION_DESKTOP|XDG_SESSION_ID|XDG_SESSION_PATH|XDG_SESSION_TYPE|XDG_VTNR|XMODIFIERS)\\b",a={pattern:/(^(["']?)\w+\2)[ \t]+\S.*/,lookbehind:!0,alias:"punctuation",inside:null},n={bash:a,environment:{pattern:RegExp("\\$"+t),alias:"constant"},variable:[{pattern:/\$?\(\([\s\S]+?\)\)/,greedy:!0,inside:{variable:[{pattern:/(^\$\(\([\s\S]+)\)\)/,lookbehind:!0},/^\$\(\(/],number:/\b0x[\dA-Fa-f]+\b|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:[Ee]-?\d+)?/,operator:/--|\+\+|\*\*=?|<<=?|>>=?|&&|\|\||[=!+\-*/%<>^&|]=?|[?~:]/,punctuation:/\(\(?|\)\)?|,|;/}},{pattern:/\$\((?:\([^)]+\)|[^()])+\)|`[^`]+`/,greedy:!0,inside:{variable:/^\$\(|^`|\)$|`$/}},{pattern:/\$\{[^}]+\}/,greedy:!0,inside:{operator:/:[-=?+]?|[!\/]|##?|%%?|\^\^?|,,?/,punctuation:/[\[\]]/,environment:{pattern:RegExp("(\\{)"+t),lookbehind:!0,alias:"constant"}}},/\$(?:\w+|[#?*!@$])/],entity:/\\(?:[abceEfnrtv\\"]|O?[0-7]{1,3}|U[0-9a-fA-F]{8}|u[0-9a-fA-F]{4}|x[0-9a-fA-F]{1,2})/};e.languages.bash={shebang:{pattern:/^#!\s*\/.*/,alias:"important"},comment:{pattern:/(^|[^"{\\$])#.*/,lookbehind:!0},"function-name":[{pattern:/(\bfunction\s+)[\w-]+(?=(?:\s*\(?:\s*\))?\s*\{)/,lookbehind:!0,alias:"function"},{pattern:/\b[\w-]+(?=\s*\(\s*\)\s*\{)/,alias:"function"}],"for-or-select":{pattern:/(\b(?:for|select)\s+)\w+(?=\s+in\s)/,alias:"variable",lookbehind:!0},"assign-left":{pattern:/(^|[\s;|&]|[<>]\()\w+(?=\+?=)/,inside:{environment:{pattern:RegExp("(^|[\\s;|&]|[<>]\\()"+t),lookbehind:!0,alias:"constant"}},alias:"variable",lookbehind:!0},string:[{pattern:/((?:^|[^<])<<-?\s*)(\w+)\s[\s\S]*?(?:\r?\n|\r)\2/,lookbehind:!0,greedy:!0,inside:n},{pattern:/((?:^|[^<])<<-?\s*)(["'])(\w+)\2\s[\s\S]*?(?:\r?\n|\r)\3/,lookbehind:!0,greedy:!0,inside:{bash:a}},{pattern:/(^|[^\\](?:\\\\)*)"(?:\\[\s\S]|\$\([^)]+\)|\$(?!\()|`[^`]+`|[^"\\`$])*"/,lookbehind:!0,greedy:!0,inside:n},{pattern:/(^|[^$\\])'[^']*'/,lookbehind:!0,greedy:!0},{pattern:/\$'(?:[^'\\]|\\[\s\S])*'/,greedy:!0,inside:{entity:n.entity}}],environment:{pattern:RegExp("\\$?"+t),alias:"constant"},variable:n.variable,function:{pattern:/(^|[\s;|&]|[<>]\()(?:add|apropos|apt|apt-cache|apt-get|aptitude|aspell|automysqlbackup|awk|basename|bash|bc|bconsole|bg|bzip2|cal|cat|cfdisk|chgrp|chkconfig|chmod|chown|chroot|cksum|clear|cmp|column|comm|composer|cp|cron|crontab|csplit|curl|cut|date|dc|dd|ddrescue|debootstrap|df|diff|diff3|dig|dir|dircolors|dirname|dirs|dmesg|docker|docker-compose|du|egrep|eject|env|ethtool|expand|expect|expr|fdformat|fdisk|fg|fgrep|file|find|fmt|fold|format|free|fsck|ftp|fuser|gawk|git|gparted|grep|groupadd|groupdel|groupmod|groups|grub-mkconfig|gzip|halt|head|hg|history|host|hostname|htop|iconv|id|ifconfig|ifdown|ifup|import|install|ip|jobs|join|kill|killall|less|link|ln|locate|logname|logrotate|look|lpc|lpr|lprint|lprintd|lprintq|lprm|ls|lsof|lynx|make|man|mc|mdadm|mkconfig|mkdir|mke2fs|mkfifo|mkfs|mkisofs|mknod|mkswap|mmv|more|most|mount|mtools|mtr|mutt|mv|nano|nc|netstat|nice|nl|node|nohup|notify-send|npm|nslookup|op|open|parted|passwd|paste|pathchk|ping|pkill|pnpm|podman|podman-compose|popd|pr|printcap|printenv|ps|pushd|pv|quota|quotacheck|quotactl|ram|rar|rcp|reboot|remsync|rename|renice|rev|rm|rmdir|rpm|rsync|scp|screen|sdiff|sed|sendmail|seq|service|sftp|sh|shellcheck|shuf|shutdown|sleep|slocate|sort|split|ssh|stat|strace|su|sudo|sum|suspend|swapon|sync|tac|tail|tar|tee|time|timeout|top|touch|tr|traceroute|tsort|tty|umount|uname|unexpand|uniq|units|unrar|unshar|unzip|update-grub|uptime|useradd|userdel|usermod|users|uudecode|uuencode|v|vcpkg|vdir|vi|vim|virsh|vmstat|wait|watch|wc|wget|whereis|which|who|whoami|write|xargs|xdg-open|yarn|yes|zenity|zip|zsh|zypper)(?=$|[)\s;|&])/,lookbehind:!0},keyword:{pattern:/(^|[\s;|&]|[<>]\()(?:case|do|done|elif|else|esac|fi|for|function|if|in|select|then|until|while)(?=$|[)\s;|&])/,lookbehind:!0},builtin:{pattern:/(^|[\s;|&]|[<>]\()(?:\.|:|alias|bind|break|builtin|caller|cd|command|continue|declare|echo|enable|eval|exec|exit|export|getopts|hash|help|let|local|logout|mapfile|printf|pwd|read|readarray|readonly|return|set|shift|shopt|source|test|times|trap|type|typeset|ulimit|umask|unalias|unset)(?=$|[)\s;|&])/,lookbehind:!0,alias:"class-name"},boolean:{pattern:/(^|[\s;|&]|[<>]\()(?:false|true)(?=$|[)\s;|&])/,lookbehind:!0},"file-descriptor":{pattern:/\B&\d\b/,alias:"important"},operator:{pattern:/\d?<>|>\||\+=|=[=~]?|!=?|<<[<-]?|[&\d]?>>|\d[<>]&?|[<>][&=]?|&[>&]?|\|[&|]?/,inside:{"file-descriptor":{pattern:/^\d/,alias:"important"}}},punctuation:/\$?\(\(?|\)\)?|\.\.|[{}[\];\\]/,number:{pattern:/(^|\s)(?:[1-9]\d*|0)(?:[.,]\d+)?\b/,lookbehind:!0}},a.inside=e.languages.bash;for(var o=["comment","function-name","for-or-select","assign-left","string","environment","function","keyword","builtin","boolean","file-descriptor","operator","punctuation","number"],i=n.variable[1].inside,l=0;l]=?|[!=]=?=?|--?|\+\+?|&&?|\|\|?|[?*/~^%]/,punctuation:/[{}[\];(),.:]/},o.languages.c=o.languages.extend("clike",{comment:{pattern:/\/\/(?:[^\r\n\\]|\\(?:\r\n?|\n|(?![\r\n])))*|\/\*[\s\S]*?(?:\*\/|$)/,greedy:!0},string:{pattern:/"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"/,greedy:!0},"class-name":{pattern:/(\b(?:enum|struct)\s+(?:__attribute__\s*\(\([\s\S]*?\)\)\s*)?)\w+|\b[a-z]\w*_t\b/,lookbehind:!0},keyword:/\b(?:_Alignas|_Alignof|_Atomic|_Bool|_Complex|_Generic|_Imaginary|_Noreturn|_Static_assert|_Thread_local|__attribute__|asm|auto|break|case|char|const|continue|default|do|double|else|enum|extern|float|for|goto|if|inline|int|long|register|return|short|signed|sizeof|static|struct|switch|typedef|typeof|union|unsigned|void|volatile|while)\b/,function:/\b[a-z_]\w*(?=\s*\()/i,number:/(?:\b0x(?:[\da-f]+(?:\.[\da-f]*)?|\.[\da-f]+)(?:p[+-]?\d+)?|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:e[+-]?\d+)?)[ful]{0,4}/i,operator:/>>=?|<<=?|->|([-+&|:])\1|[?:~]|[-+*/%&|^!=<>]=?/}),o.languages.insertBefore("c","string",{char:{pattern:/'(?:\\(?:\r\n|[\s\S])|[^'\\\r\n]){0,32}'/,greedy:!0}}),o.languages.insertBefore("c","string",{macro:{pattern:/(^[\t ]*)#\s*[a-z](?:[^\r\n\\/]|\/(?!\*)|\/\*(?:[^*]|\*(?!\/))*\*\/|\\(?:\r\n|[\s\S]))*/im,lookbehind:!0,greedy:!0,alias:"property",inside:{string:[{pattern:/^(#\s*include\s*)<[^>]+>/,lookbehind:!0},o.languages.c.string],char:o.languages.c.char,comment:o.languages.c.comment,"macro-name":[{pattern:/(^#\s*define\s+)\w+\b(?!\()/i,lookbehind:!0},{pattern:/(^#\s*define\s+)\w+\b(?=\()/i,lookbehind:!0,alias:"function"}],directive:{pattern:/^(#\s*)[a-z]+/,lookbehind:!0,alias:"keyword"},"directive-hash":/^#/,punctuation:/##|\\(?=[\r\n])/,expression:{pattern:/\S[\s\S]*/,inside:o.languages.c}}}}),o.languages.insertBefore("c","function",{constant:/\b(?:EOF|NULL|SEEK_CUR|SEEK_END|SEEK_SET|__DATE__|__FILE__|__LINE__|__TIMESTAMP__|__TIME__|__func__|stderr|stdin|stdout)\b/}),delete o.languages.c.boolean,function(e){var t=/\b(?:alignas|alignof|asm|auto|bool|break|case|catch|char|char16_t|char32_t|char8_t|class|co_await|co_return|co_yield|compl|concept|const|const_cast|consteval|constexpr|constinit|continue|decltype|default|delete|do|double|dynamic_cast|else|enum|explicit|export|extern|final|float|for|friend|goto|if|import|inline|int|int16_t|int32_t|int64_t|int8_t|long|module|mutable|namespace|new|noexcept|nullptr|operator|override|private|protected|public|register|reinterpret_cast|requires|return|short|signed|sizeof|static|static_assert|static_cast|struct|switch|template|this|thread_local|throw|try|typedef|typeid|typename|uint16_t|uint32_t|uint64_t|uint8_t|union|unsigned|using|virtual|void|volatile|wchar_t|while)\b/,a=/\b(?!)\w+(?:\s*\.\s*\w+)*\b/.source.replace(//g,(function(){return t.source}));e.languages.cpp=e.languages.extend("c",{"class-name":[{pattern:RegExp(/(\b(?:class|concept|enum|struct|typename)\s+)(?!)\w+/.source.replace(//g,(function(){return t.source}))),lookbehind:!0},/\b[A-Z]\w*(?=\s*::\s*\w+\s*\()/,/\b[A-Z_]\w*(?=\s*::\s*~\w+\s*\()/i,/\b\w+(?=\s*<(?:[^<>]|<(?:[^<>]|<[^<>]*>)*>)*>\s*::\s*\w+\s*\()/],keyword:t,number:{pattern:/(?:\b0b[01']+|\b0x(?:[\da-f']+(?:\.[\da-f']*)?|\.[\da-f']+)(?:p[+-]?[\d']+)?|(?:\b[\d']+(?:\.[\d']*)?|\B\.[\d']+)(?:e[+-]?[\d']+)?)[ful]{0,4}/i,greedy:!0},operator:/>>=?|<<=?|->|--|\+\+|&&|\|\||[?:~]|<=>|[-+*/%&|^!=<>]=?|\b(?:and|and_eq|bitand|bitor|not|not_eq|or|or_eq|xor|xor_eq)\b/,boolean:/\b(?:false|true)\b/}),e.languages.insertBefore("cpp","string",{module:{pattern:RegExp(/(\b(?:import|module)\s+)/.source+"(?:"+/"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"|<[^<>\r\n]*>/.source+"|"+/(?:\s*:\s*)?|:\s*/.source.replace(//g,(function(){return a}))+")"),lookbehind:!0,greedy:!0,inside:{string:/^[<"][\s\S]+/,operator:/:/,punctuation:/\./}},"raw-string":{pattern:/R"([^()\\ ]{0,16})\([\s\S]*?\)\1"/,alias:"string",greedy:!0}}),e.languages.insertBefore("cpp","keyword",{"generic-function":{pattern:/\b(?!operator\b)[a-z_]\w*\s*<(?:[^<>]|<[^<>]*>)*>(?=\s*\()/i,inside:{function:/^\w+/,generic:{pattern:/<[\s\S]+/,alias:"class-name",inside:e.languages.cpp}}}}),e.languages.insertBefore("cpp","operator",{"double-colon":{pattern:/::/,alias:"punctuation"}}),e.languages.insertBefore("cpp","class-name",{"base-clause":{pattern:/(\b(?:class|struct)\s+\w+\s*:\s*)[^;{}"'\s]+(?:\s+[^;{}"'\s]+)*(?=\s*[;{])/,lookbehind:!0,greedy:!0,inside:e.languages.extend("cpp",{})}}),e.languages.insertBefore("inside","double-colon",{"class-name":/\b[a-z_]\w*\b(?!\s*::)/i},e.languages.cpp["base-clause"])}(o),function(e){var t=/(?:"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"|'(?:\\(?:\r\n|[\s\S])|[^'\\\r\n])*')/;e.languages.css={comment:/\/\*[\s\S]*?\*\//,atrule:{pattern:/@[\w-](?:[^;{\s]|\s+(?![\s{]))*(?:;|(?=\s*\{))/,inside:{rule:/^@[\w-]+/,"selector-function-argument":{pattern:/(\bselector\s*\(\s*(?![\s)]))(?:[^()\s]|\s+(?![\s)])|\((?:[^()]|\([^()]*\))*\))+(?=\s*\))/,lookbehind:!0,alias:"selector"},keyword:{pattern:/(^|[^\w-])(?:and|not|only|or)(?![\w-])/,lookbehind:!0}}},url:{pattern:RegExp("\\burl\\((?:"+t.source+"|"+/(?:[^\\\r\n()"']|\\[\s\S])*/.source+")\\)","i"),greedy:!0,inside:{function:/^url/i,punctuation:/^\(|\)$/,string:{pattern:RegExp("^"+t.source+"$"),alias:"url"}}},selector:{pattern:RegExp("(^|[{}\\s])[^{}\\s](?:[^{};\"'\\s]|\\s+(?![\\s{])|"+t.source+")*(?=\\s*\\{)"),lookbehind:!0},string:{pattern:t,greedy:!0},property:{pattern:/(^|[^-\w\xA0-\uFFFF])(?!\s)[-_a-z\xA0-\uFFFF](?:(?!\s)[-\w\xA0-\uFFFF])*(?=\s*:)/i,lookbehind:!0},important:/!important\b/i,function:{pattern:/(^|[^-a-z0-9])[-a-z0-9]+(?=\()/i,lookbehind:!0},punctuation:/[(){};:,]/},e.languages.css.atrule.inside.rest=e.languages.css;var a=e.languages.markup;a&&(a.tag.addInlined("style","css"),a.tag.addAttribute("style","css"))}(o),function(e){var t,a=/("|')(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/;e.languages.css.selector={pattern:e.languages.css.selector.pattern,lookbehind:!0,inside:t={"pseudo-element":/:(?:after|before|first-letter|first-line|selection)|::[-\w]+/,"pseudo-class":/:[-\w]+/,class:/\.[-\w]+/,id:/#[-\w]+/,attribute:{pattern:RegExp("\\[(?:[^[\\]\"']|"+a.source+")*\\]"),greedy:!0,inside:{punctuation:/^\[|\]$/,"case-sensitivity":{pattern:/(\s)[si]$/i,lookbehind:!0,alias:"keyword"},namespace:{pattern:/^(\s*)(?:(?!\s)[-*\w\xA0-\uFFFF])*\|(?!=)/,lookbehind:!0,inside:{punctuation:/\|$/}},"attr-name":{pattern:/^(\s*)(?:(?!\s)[-\w\xA0-\uFFFF])+/,lookbehind:!0},"attr-value":[a,{pattern:/(=\s*)(?:(?!\s)[-\w\xA0-\uFFFF])+(?=\s*$)/,lookbehind:!0}],operator:/[|~*^$]?=/}},"n-th":[{pattern:/(\(\s*)[+-]?\d*[\dn](?:\s*[+-]\s*\d+)?(?=\s*\))/,lookbehind:!0,inside:{number:/[\dn]+/,operator:/[+-]/}},{pattern:/(\(\s*)(?:even|odd)(?=\s*\))/i,lookbehind:!0}],combinator:/>|\+|~|\|\|/,punctuation:/[(),]/}},e.languages.css.atrule.inside["selector-function-argument"].inside=t,e.languages.insertBefore("css","property",{variable:{pattern:/(^|[^-\w\xA0-\uFFFF])--(?!\s)[-_a-z\xA0-\uFFFF](?:(?!\s)[-\w\xA0-\uFFFF])*/i,lookbehind:!0}});var n={pattern:/(\b\d+)(?:%|[a-z]+(?![\w-]))/,lookbehind:!0},o={pattern:/(^|[^\w.-])-?(?:\d+(?:\.\d+)?|\.\d+)/,lookbehind:!0};e.languages.insertBefore("css","function",{operator:{pattern:/(\s)[+\-*\/](?=\s)/,lookbehind:!0},hexcode:{pattern:/\B#[\da-f]{3,8}\b/i,alias:"color"},color:[{pattern:/(^|[^\w-])(?:AliceBlue|AntiqueWhite|Aqua|Aquamarine|Azure|Beige|Bisque|Black|BlanchedAlmond|Blue|BlueViolet|Brown|BurlyWood|CadetBlue|Chartreuse|Chocolate|Coral|CornflowerBlue|Cornsilk|Crimson|Cyan|DarkBlue|DarkCyan|DarkGoldenRod|DarkGr[ae]y|DarkGreen|DarkKhaki|DarkMagenta|DarkOliveGreen|DarkOrange|DarkOrchid|DarkRed|DarkSalmon|DarkSeaGreen|DarkSlateBlue|DarkSlateGr[ae]y|DarkTurquoise|DarkViolet|DeepPink|DeepSkyBlue|DimGr[ae]y|DodgerBlue|FireBrick|FloralWhite|ForestGreen|Fuchsia|Gainsboro|GhostWhite|Gold|GoldenRod|Gr[ae]y|Green|GreenYellow|HoneyDew|HotPink|IndianRed|Indigo|Ivory|Khaki|Lavender|LavenderBlush|LawnGreen|LemonChiffon|LightBlue|LightCoral|LightCyan|LightGoldenRodYellow|LightGr[ae]y|LightGreen|LightPink|LightSalmon|LightSeaGreen|LightSkyBlue|LightSlateGr[ae]y|LightSteelBlue|LightYellow|Lime|LimeGreen|Linen|Magenta|Maroon|MediumAquaMarine|MediumBlue|MediumOrchid|MediumPurple|MediumSeaGreen|MediumSlateBlue|MediumSpringGreen|MediumTurquoise|MediumVioletRed|MidnightBlue|MintCream|MistyRose|Moccasin|NavajoWhite|Navy|OldLace|Olive|OliveDrab|Orange|OrangeRed|Orchid|PaleGoldenRod|PaleGreen|PaleTurquoise|PaleVioletRed|PapayaWhip|PeachPuff|Peru|Pink|Plum|PowderBlue|Purple|Red|RosyBrown|RoyalBlue|SaddleBrown|Salmon|SandyBrown|SeaGreen|SeaShell|Sienna|Silver|SkyBlue|SlateBlue|SlateGr[ae]y|Snow|SpringGreen|SteelBlue|Tan|Teal|Thistle|Tomato|Transparent|Turquoise|Violet|Wheat|White|WhiteSmoke|Yellow|YellowGreen)(?![\w-])/i,lookbehind:!0},{pattern:/\b(?:hsl|rgb)\(\s*\d{1,3}\s*,\s*\d{1,3}%?\s*,\s*\d{1,3}%?\s*\)\B|\b(?:hsl|rgb)a\(\s*\d{1,3}\s*,\s*\d{1,3}%?\s*,\s*\d{1,3}%?\s*,\s*(?:0|0?\.\d+|1)\s*\)\B/i,inside:{unit:n,number:o,function:/[\w-]+(?=\()/,punctuation:/[(),]/}}],entity:/\\[\da-f]{1,8}/i,unit:n,number:o})}(o),o.languages.javascript=o.languages.extend("clike",{"class-name":[o.languages.clike["class-name"],{pattern:/(^|[^$\w\xA0-\uFFFF])(?!\s)[_$A-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\.(?:constructor|prototype))/,lookbehind:!0}],keyword:[{pattern:/((?:^|\})\s*)catch\b/,lookbehind:!0},{pattern:/(^|[^.]|\.\.\.\s*)\b(?:as|assert(?=\s*\{)|async(?=\s*(?:function\b|\(|[$\w\xA0-\uFFFF]|$))|await|break|case|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally(?=\s*(?:\{|$))|for|from(?=\s*(?:['"]|$))|function|(?:get|set)(?=\s*(?:[#\[$\w\xA0-\uFFFF]|$))|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)\b/,lookbehind:!0}],function:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*(?:\.\s*(?:apply|bind|call)\s*)?\()/,number:{pattern:RegExp(/(^|[^\w$])/.source+"(?:"+/NaN|Infinity/.source+"|"+/0[bB][01]+(?:_[01]+)*n?/.source+"|"+/0[oO][0-7]+(?:_[0-7]+)*n?/.source+"|"+/0[xX][\dA-Fa-f]+(?:_[\dA-Fa-f]+)*n?/.source+"|"+/\d+(?:_\d+)*n/.source+"|"+/(?:\d+(?:_\d+)*(?:\.(?:\d+(?:_\d+)*)?)?|\.\d+(?:_\d+)*)(?:[Ee][+-]?\d+(?:_\d+)*)?/.source+")"+/(?![\w$])/.source),lookbehind:!0},operator:/--|\+\+|\*\*=?|=>|&&=?|\|\|=?|[!=]==|<<=?|>>>?=?|[-+*/%&|^!=<>]=?|\.{3}|\?\?=?|\?\.?|[~:]/}),o.languages.javascript["class-name"][0].pattern=/(\b(?:class|extends|implements|instanceof|interface|new)\s+)[\w.\\]+/,o.languages.insertBefore("javascript","keyword",{regex:{pattern:/((?:^|[^$\w\xA0-\uFFFF."'\])\s]|\b(?:return|yield))\s*)\/(?:\[(?:[^\]\\\r\n]|\\.)*\]|\\.|[^/\\\[\r\n])+\/[dgimyus]{0,7}(?=(?:\s|\/\*(?:[^*]|\*(?!\/))*\*\/)*(?:$|[\r\n,.;:})\]]|\/\/))/,lookbehind:!0,greedy:!0,inside:{"regex-source":{pattern:/^(\/)[\s\S]+(?=\/[a-z]*$)/,lookbehind:!0,alias:"language-regex",inside:o.languages.regex},"regex-delimiter":/^\/|\/$/,"regex-flags":/^[a-z]+$/}},"function-variable":{pattern:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*[=:]\s*(?:async\s*)?(?:\bfunction\b|(?:\((?:[^()]|\([^()]*\))*\)|(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)\s*=>))/,alias:"function"},parameter:[{pattern:/(function(?:\s+(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)?\s*\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\))/,lookbehind:!0,inside:o.languages.javascript},{pattern:/(^|[^$\w\xA0-\uFFFF])(?!\s)[_$a-z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*=>)/i,lookbehind:!0,inside:o.languages.javascript},{pattern:/(\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\)\s*=>)/,lookbehind:!0,inside:o.languages.javascript},{pattern:/((?:\b|\s|^)(?!(?:as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)(?![$\w\xA0-\uFFFF]))(?:(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*\s*)\(\s*|\]\s*\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\)\s*\{)/,lookbehind:!0,inside:o.languages.javascript}],constant:/\b[A-Z](?:[A-Z_]|\dx?)*\b/}),o.languages.insertBefore("javascript","string",{hashbang:{pattern:/^#!.*/,greedy:!0,alias:"comment"},"template-string":{pattern:/`(?:\\[\s\S]|\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}|(?!\$\{)[^\\`])*`/,greedy:!0,inside:{"template-punctuation":{pattern:/^`|`$/,alias:"string"},interpolation:{pattern:/((?:^|[^\\])(?:\\{2})*)\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}/,lookbehind:!0,inside:{"interpolation-punctuation":{pattern:/^\$\{|\}$/,alias:"punctuation"},rest:o.languages.javascript}},string:/[\s\S]+/}},"string-property":{pattern:/((?:^|[,{])[ \t]*)(["'])(?:\\(?:\r\n|[\s\S])|(?!\2)[^\\\r\n])*\2(?=\s*:)/m,lookbehind:!0,greedy:!0,alias:"property"}}),o.languages.insertBefore("javascript","operator",{"literal-property":{pattern:/((?:^|[,{])[ \t]*)(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*:)/m,lookbehind:!0,alias:"property"}}),o.languages.markup&&(o.languages.markup.tag.addInlined("script","javascript"),o.languages.markup.tag.addAttribute(/on(?:abort|blur|change|click|composition(?:end|start|update)|dblclick|error|focus(?:in|out)?|key(?:down|up)|load|mouse(?:down|enter|leave|move|out|over|up)|reset|resize|scroll|select|slotchange|submit|unload|wheel)/.source,"javascript")),o.languages.js=o.languages.javascript,function(e){var t=/#(?!\{).+/,a={pattern:/#\{[^}]+\}/,alias:"variable"};e.languages.coffeescript=e.languages.extend("javascript",{comment:t,string:[{pattern:/'(?:\\[\s\S]|[^\\'])*'/,greedy:!0},{pattern:/"(?:\\[\s\S]|[^\\"])*"/,greedy:!0,inside:{interpolation:a}}],keyword:/\b(?:and|break|by|catch|class|continue|debugger|delete|do|each|else|extend|extends|false|finally|for|if|in|instanceof|is|isnt|let|loop|namespace|new|no|not|null|of|off|on|or|own|return|super|switch|then|this|throw|true|try|typeof|undefined|unless|until|when|while|window|with|yes|yield)\b/,"class-member":{pattern:/@(?!\d)\w+/,alias:"variable"}}),e.languages.insertBefore("coffeescript","comment",{"multiline-comment":{pattern:/###[\s\S]+?###/,alias:"comment"},"block-regex":{pattern:/\/{3}[\s\S]*?\/{3}/,alias:"regex",inside:{comment:t,interpolation:a}}}),e.languages.insertBefore("coffeescript","string",{"inline-javascript":{pattern:/`(?:\\[\s\S]|[^\\`])*`/,inside:{delimiter:{pattern:/^`|`$/,alias:"punctuation"},script:{pattern:/[\s\S]+/,alias:"language-javascript",inside:e.languages.javascript}}},"multiline-string":[{pattern:/'''[\s\S]*?'''/,greedy:!0,alias:"string"},{pattern:/"""[\s\S]*?"""/,greedy:!0,alias:"string",inside:{interpolation:a}}]}),e.languages.insertBefore("coffeescript","keyword",{property:/(?!\d)\w+(?=\s*:(?!:))/}),delete e.languages.coffeescript["template-string"],e.languages.coffee=e.languages.coffeescript}(o),function(e){var t=/[*&][^\s[\]{},]+/,a=/!(?:<[\w\-%#;/?:@&=+$,.!~*'()[\]]+>|(?:[a-zA-Z\d-]*!)?[\w\-%#;/?:@&=+$.~*'()]+)?/,n="(?:"+a.source+"(?:[ \t]+"+t.source+")?|"+t.source+"(?:[ \t]+"+a.source+")?)",o=/(?:[^\s\x00-\x08\x0e-\x1f!"#%&'*,\-:>?@[\]`{|}\x7f-\x84\x86-\x9f\ud800-\udfff\ufffe\uffff]|[?:-])(?:[ \t]*(?:(?![#:])|:))*/.source.replace(//g,(function(){return/[^\s\x00-\x08\x0e-\x1f,[\]{}\x7f-\x84\x86-\x9f\ud800-\udfff\ufffe\uffff]/.source})),i=/"(?:[^"\\\r\n]|\\.)*"|'(?:[^'\\\r\n]|\\.)*'/.source;function l(e,t){t=(t||"").replace(/m/g,"")+"m";var a=/([:\-,[{]\s*(?:\s<>[ \t]+)?)(?:<>)(?=[ \t]*(?:$|,|\]|\}|(?:[\r\n]\s*)?#))/.source.replace(/<>/g,(function(){return n})).replace(/<>/g,(function(){return e}));return RegExp(a,t)}e.languages.yaml={scalar:{pattern:RegExp(/([\-:]\s*(?:\s<>[ \t]+)?[|>])[ \t]*(?:((?:\r?\n|\r)[ \t]+)\S[^\r\n]*(?:\2[^\r\n]+)*)/.source.replace(/<>/g,(function(){return n}))),lookbehind:!0,alias:"string"},comment:/#.*/,key:{pattern:RegExp(/((?:^|[:\-,[{\r\n?])[ \t]*(?:<>[ \t]+)?)<>(?=\s*:\s)/.source.replace(/<>/g,(function(){return n})).replace(/<>/g,(function(){return"(?:"+o+"|"+i+")"}))),lookbehind:!0,greedy:!0,alias:"atrule"},directive:{pattern:/(^[ \t]*)%.+/m,lookbehind:!0,alias:"important"},datetime:{pattern:l(/\d{4}-\d\d?-\d\d?(?:[tT]|[ \t]+)\d\d?:\d{2}:\d{2}(?:\.\d*)?(?:[ \t]*(?:Z|[-+]\d\d?(?::\d{2})?))?|\d{4}-\d{2}-\d{2}|\d\d?:\d{2}(?::\d{2}(?:\.\d*)?)?/.source),lookbehind:!0,alias:"number"},boolean:{pattern:l(/false|true/.source,"i"),lookbehind:!0,alias:"important"},null:{pattern:l(/null|~/.source,"i"),lookbehind:!0,alias:"important"},string:{pattern:l(i),lookbehind:!0,greedy:!0},number:{pattern:l(/[+-]?(?:0x[\da-f]+|0o[0-7]+|(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?|\.inf|\.nan)/.source,"i"),lookbehind:!0},tag:a,important:t,punctuation:/---|[:[\]{}\-,|>?]|\.\.\./},e.languages.yml=e.languages.yaml}(o),function(e){var t=/(?:\\.|[^\\\n\r]|(?:\n|\r\n?)(?![\r\n]))/.source;function a(e){return e=e.replace(//g,(function(){return t})),RegExp(/((?:^|[^\\])(?:\\{2})*)/.source+"(?:"+e+")")}var n=/(?:\\.|``(?:[^`\r\n]|`(?!`))+``|`[^`\r\n]+`|[^\\|\r\n`])+/.source,o=/\|?__(?:\|__)+\|?(?:(?:\n|\r\n?)|(?![\s\S]))/.source.replace(/__/g,(function(){return n})),i=/\|?[ \t]*:?-{3,}:?[ \t]*(?:\|[ \t]*:?-{3,}:?[ \t]*)+\|?(?:\n|\r\n?)/.source;e.languages.markdown=e.languages.extend("markup",{}),e.languages.insertBefore("markdown","prolog",{"front-matter-block":{pattern:/(^(?:\s*[\r\n])?)---(?!.)[\s\S]*?[\r\n]---(?!.)/,lookbehind:!0,greedy:!0,inside:{punctuation:/^---|---$/,"front-matter":{pattern:/\S+(?:\s+\S+)*/,alias:["yaml","language-yaml"],inside:e.languages.yaml}}},blockquote:{pattern:/^>(?:[\t ]*>)*/m,alias:"punctuation"},table:{pattern:RegExp("^"+o+i+"(?:"+o+")*","m"),inside:{"table-data-rows":{pattern:RegExp("^("+o+i+")(?:"+o+")*$"),lookbehind:!0,inside:{"table-data":{pattern:RegExp(n),inside:e.languages.markdown},punctuation:/\|/}},"table-line":{pattern:RegExp("^("+o+")"+i+"$"),lookbehind:!0,inside:{punctuation:/\||:?-{3,}:?/}},"table-header-row":{pattern:RegExp("^"+o+"$"),inside:{"table-header":{pattern:RegExp(n),alias:"important",inside:e.languages.markdown},punctuation:/\|/}}}},code:[{pattern:/((?:^|\n)[ \t]*\n|(?:^|\r\n?)[ \t]*\r\n?)(?: {4}|\t).+(?:(?:\n|\r\n?)(?: {4}|\t).+)*/,lookbehind:!0,alias:"keyword"},{pattern:/^```[\s\S]*?^```$/m,greedy:!0,inside:{"code-block":{pattern:/^(```.*(?:\n|\r\n?))[\s\S]+?(?=(?:\n|\r\n?)^```$)/m,lookbehind:!0},"code-language":{pattern:/^(```).+/,lookbehind:!0},punctuation:/```/}}],title:[{pattern:/\S.*(?:\n|\r\n?)(?:==+|--+)(?=[ \t]*$)/m,alias:"important",inside:{punctuation:/==+$|--+$/}},{pattern:/(^\s*)#.+/m,lookbehind:!0,alias:"important",inside:{punctuation:/^#+|#+$/}}],hr:{pattern:/(^\s*)([*-])(?:[\t ]*\2){2,}(?=\s*$)/m,lookbehind:!0,alias:"punctuation"},list:{pattern:/(^\s*)(?:[*+-]|\d+\.)(?=[\t ].)/m,lookbehind:!0,alias:"punctuation"},"url-reference":{pattern:/!?\[[^\]]+\]:[\t ]+(?:\S+|<(?:\\.|[^>\\])+>)(?:[\t ]+(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|\((?:\\.|[^)\\])*\)))?/,inside:{variable:{pattern:/^(!?\[)[^\]]+/,lookbehind:!0},string:/(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|\((?:\\.|[^)\\])*\))$/,punctuation:/^[\[\]!:]|[<>]/},alias:"url"},bold:{pattern:a(/\b__(?:(?!_)|_(?:(?!_))+_)+__\b|\*\*(?:(?!\*)|\*(?:(?!\*))+\*)+\*\*/.source),lookbehind:!0,greedy:!0,inside:{content:{pattern:/(^..)[\s\S]+(?=..$)/,lookbehind:!0,inside:{}},punctuation:/\*\*|__/}},italic:{pattern:a(/\b_(?:(?!_)|__(?:(?!_))+__)+_\b|\*(?:(?!\*)|\*\*(?:(?!\*))+\*\*)+\*/.source),lookbehind:!0,greedy:!0,inside:{content:{pattern:/(^.)[\s\S]+(?=.$)/,lookbehind:!0,inside:{}},punctuation:/[*_]/}},strike:{pattern:a(/(~~?)(?:(?!~))+\2/.source),lookbehind:!0,greedy:!0,inside:{content:{pattern:/(^~~?)[\s\S]+(?=\1$)/,lookbehind:!0,inside:{}},punctuation:/~~?/}},"code-snippet":{pattern:/(^|[^\\`])(?:``[^`\r\n]+(?:`[^`\r\n]+)*``(?!`)|`[^`\r\n]+`(?!`))/,lookbehind:!0,greedy:!0,alias:["code","keyword"]},url:{pattern:a(/!?\[(?:(?!\]))+\](?:\([^\s)]+(?:[\t ]+"(?:\\.|[^"\\])*")?\)|[ \t]?\[(?:(?!\]))+\])/.source),lookbehind:!0,greedy:!0,inside:{operator:/^!/,content:{pattern:/(^\[)[^\]]+(?=\])/,lookbehind:!0,inside:{}},variable:{pattern:/(^\][ \t]?\[)[^\]]+(?=\]$)/,lookbehind:!0},url:{pattern:/(^\]\()[^\s)]+/,lookbehind:!0},string:{pattern:/(^[ \t]+)"(?:\\.|[^"\\])*"(?=\)$)/,lookbehind:!0}}}}),["url","bold","italic","strike"].forEach((function(t){["url","bold","italic","strike","code-snippet"].forEach((function(a){t!==a&&(e.languages.markdown[t].inside.content.inside[a]=e.languages.markdown[a])}))})),e.hooks.add("after-tokenize",(function(e){"markdown"!==e.language&&"md"!==e.language||function e(t){if(t&&"string"!=typeof t)for(var a=0,n=t.length;a",quot:'"'},s=String.fromCodePoint||String.fromCharCode;e.languages.md=e.languages.markdown}(o),o.languages.graphql={comment:/#.*/,description:{pattern:/(?:"""(?:[^"]|(?!""")")*"""|"(?:\\.|[^\\"\r\n])*")(?=\s*[a-z_])/i,greedy:!0,alias:"string",inside:{"language-markdown":{pattern:/(^"(?:"")?)(?!\1)[\s\S]+(?=\1$)/,lookbehind:!0,inside:o.languages.markdown}}},string:{pattern:/"""(?:[^"]|(?!""")")*"""|"(?:\\.|[^\\"\r\n])*"/,greedy:!0},number:/(?:\B-|\b)\d+(?:\.\d+)?(?:e[+-]?\d+)?\b/i,boolean:/\b(?:false|true)\b/,variable:/\$[a-z_]\w*/i,directive:{pattern:/@[a-z_]\w*/i,alias:"function"},"attr-name":{pattern:/\b[a-z_]\w*(?=\s*(?:\((?:[^()"]|"(?:\\.|[^\\"\r\n])*")*\))?:)/i,greedy:!0},"atom-input":{pattern:/\b[A-Z]\w*Input\b/,alias:"class-name"},scalar:/\b(?:Boolean|Float|ID|Int|String)\b/,constant:/\b[A-Z][A-Z_\d]*\b/,"class-name":{pattern:/(\b(?:enum|implements|interface|on|scalar|type|union)\s+|&\s*|:\s*|\[)[A-Z_]\w*/,lookbehind:!0},fragment:{pattern:/(\bfragment\s+|\.{3}\s*(?!on\b))[a-zA-Z_]\w*/,lookbehind:!0,alias:"function"},"definition-mutation":{pattern:/(\bmutation\s+)[a-zA-Z_]\w*/,lookbehind:!0,alias:"function"},"definition-query":{pattern:/(\bquery\s+)[a-zA-Z_]\w*/,lookbehind:!0,alias:"function"},keyword:/\b(?:directive|enum|extend|fragment|implements|input|interface|mutation|on|query|repeatable|scalar|schema|subscription|type|union)\b/,operator:/[!=|&]|\.{3}/,"property-query":/\w+(?=\s*\()/,object:/\w+(?=\s*\{)/,punctuation:/[!(){}\[\]:=,]/,property:/\w+/},o.hooks.add("after-tokenize",(function(e){if("graphql"===e.language)for(var t=e.tokens.filter((function(e){return"string"!=typeof e&&"comment"!==e.type&&"scalar"!==e.type})),a=0;a0)){var r=g(/^\{$/,/^\}$/);if(-1===r)continue;for(var s=a;s=0&&p(c,"variable-input")}}}}function d(e){return t[a+e]}function u(e,t){t=t||0;for(var a=0;a?|<|>)?|>[>=]?|\b(?:AND|BETWEEN|DIV|ILIKE|IN|IS|LIKE|NOT|OR|REGEXP|RLIKE|SOUNDS LIKE|XOR)\b/i,punctuation:/[;[\]()`,.]/},function(e){var t=e.languages.javascript["template-string"],a=t.pattern.source,n=t.inside.interpolation,o=n.inside["interpolation-punctuation"],i=n.pattern.source;function l(t,n){if(e.languages[t])return{pattern:RegExp("((?:"+n+")\\s*)"+a),lookbehind:!0,greedy:!0,inside:{"template-punctuation":{pattern:/^`|`$/,alias:"string"},"embedded-code":{pattern:/[\s\S]+/,alias:t}}}}function r(e,t){return"___"+t.toUpperCase()+"_"+e+"___"}function s(t,a,n){var o={code:t,grammar:a,language:n};return e.hooks.run("before-tokenize",o),o.tokens=e.tokenize(o.code,o.grammar),e.hooks.run("after-tokenize",o),o.tokens}function c(t){var a={};a["interpolation-punctuation"]=o;var i=e.tokenize(t,a);if(3===i.length){var l=[1,1];l.push.apply(l,s(i[1],e.languages.javascript,"javascript")),i.splice.apply(i,l)}return new e.Token("interpolation",i,n.alias,t)}function d(t,a,n){var o=e.tokenize(t,{interpolation:{pattern:RegExp(i),lookbehind:!0}}),l=0,d={},u=s(o.map((function(e){if("string"==typeof e)return e;for(var a,o=e.content;-1!==t.indexOf(a=r(l++,n)););return d[a]=o,a})).join(""),a,n),g=Object.keys(d);return l=0,function e(t){for(var a=0;a=g.length)return;var n=t[a];if("string"==typeof n||"string"==typeof n.content){var o=g[l],i="string"==typeof n?n:n.content,r=i.indexOf(o);if(-1!==r){++l;var s=i.substring(0,r),u=c(d[o]),p=i.substring(r+o.length),f=[];if(s&&f.push(s),f.push(u),p){var b=[p];e(b),f.push.apply(f,b)}"string"==typeof n?(t.splice.apply(t,[a,1].concat(f)),a+=f.length-1):n.content=f}}else{var m=n.content;Array.isArray(m)?e(m):e([m])}}}(u),new e.Token(n,u,"language-"+n,t)}e.languages.javascript["template-string"]=[l("css",/\b(?:styled(?:\([^)]*\))?(?:\s*\.\s*\w+(?:\([^)]*\))*)*|css(?:\s*\.\s*(?:global|resolve))?|createGlobalStyle|keyframes)/.source),l("html",/\bhtml|\.\s*(?:inner|outer)HTML\s*\+?=/.source),l("svg",/\bsvg/.source),l("markdown",/\b(?:markdown|md)/.source),l("graphql",/\b(?:gql|graphql(?:\s*\.\s*experimental)?)/.source),l("sql",/\bsql/.source),t].filter(Boolean);var u={javascript:!0,js:!0,typescript:!0,ts:!0,jsx:!0,tsx:!0};function g(e){return"string"==typeof e?e:Array.isArray(e)?e.map(g).join(""):g(e.content)}e.hooks.add("after-tokenize",(function(t){t.language in u&&function t(a){for(var n=0,o=a.length;n]|<(?:[^<>]|<[^<>]*>)*>)*>)?/,lookbehind:!0,greedy:!0,inside:null},builtin:/\b(?:Array|Function|Promise|any|boolean|console|never|number|string|symbol|unknown)\b/}),e.languages.typescript.keyword.push(/\b(?:abstract|declare|is|keyof|readonly|require)\b/,/\b(?:asserts|infer|interface|module|namespace|type)\b(?=\s*(?:[{_$a-zA-Z\xA0-\uFFFF]|$))/,/\btype\b(?=\s*(?:[\{*]|$))/),delete e.languages.typescript.parameter,delete e.languages.typescript["literal-property"];var t=e.languages.extend("typescript",{});delete t["class-name"],e.languages.typescript["class-name"].inside=t,e.languages.insertBefore("typescript","function",{decorator:{pattern:/@[$\w\xA0-\uFFFF]+/,inside:{at:{pattern:/^@/,alias:"operator"},function:/^[\s\S]+/}},"generic-function":{pattern:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*\s*<(?:[^<>]|<(?:[^<>]|<[^<>]*>)*>)*>(?=\s*\()/,greedy:!0,inside:{function:/^#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*/,generic:{pattern:/<[\s\S]+/,alias:"class-name",inside:t}}}}),e.languages.ts=e.languages.typescript}(o),function(e){function t(e,t){return RegExp(e.replace(//g,(function(){return/(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*/.source})),t)}e.languages.insertBefore("javascript","function-variable",{"method-variable":{pattern:RegExp("(\\.\\s*)"+e.languages.javascript["function-variable"].pattern.source),lookbehind:!0,alias:["function-variable","method","function","property-access"]}}),e.languages.insertBefore("javascript","function",{method:{pattern:RegExp("(\\.\\s*)"+e.languages.javascript.function.source),lookbehind:!0,alias:["function","property-access"]}}),e.languages.insertBefore("javascript","constant",{"known-class-name":[{pattern:/\b(?:(?:Float(?:32|64)|(?:Int|Uint)(?:8|16|32)|Uint8Clamped)?Array|ArrayBuffer|BigInt|Boolean|DataView|Date|Error|Function|Intl|JSON|(?:Weak)?(?:Map|Set)|Math|Number|Object|Promise|Proxy|Reflect|RegExp|String|Symbol|WebAssembly)\b/,alias:"class-name"},{pattern:/\b(?:[A-Z]\w*)Error\b/,alias:"class-name"}]}),e.languages.insertBefore("javascript","keyword",{imports:{pattern:t(/(\bimport\b\s*)(?:(?:\s*,\s*(?:\*\s*as\s+|\{[^{}]*\}))?|\*\s*as\s+|\{[^{}]*\})(?=\s*\bfrom\b)/.source),lookbehind:!0,inside:e.languages.javascript},exports:{pattern:t(/(\bexport\b\s*)(?:\*(?:\s*as\s+)?(?=\s*\bfrom\b)|\{[^{}]*\})/.source),lookbehind:!0,inside:e.languages.javascript}}),e.languages.javascript.keyword.unshift({pattern:/\b(?:as|default|export|from|import)\b/,alias:"module"},{pattern:/\b(?:await|break|catch|continue|do|else|finally|for|if|return|switch|throw|try|while|yield)\b/,alias:"control-flow"},{pattern:/\bnull\b/,alias:["null","nil"]},{pattern:/\bundefined\b/,alias:"nil"}),e.languages.insertBefore("javascript","operator",{spread:{pattern:/\.{3}/,alias:"operator"},arrow:{pattern:/=>/,alias:"operator"}}),e.languages.insertBefore("javascript","punctuation",{"property-access":{pattern:t(/(\.\s*)#?/.source),lookbehind:!0},"maybe-class-name":{pattern:/(^|[^$\w\xA0-\uFFFF])[A-Z][$\w\xA0-\uFFFF]+/,lookbehind:!0},dom:{pattern:/\b(?:document|(?:local|session)Storage|location|navigator|performance|window)\b/,alias:"variable"},console:{pattern:/\bconsole(?=\s*\.)/,alias:"class-name"}});for(var a=["function","function-variable","method","method-variable","property-access"],n=0;n*\.{3}(?:[^{}]|)*\})/.source;function i(e,t){return e=e.replace(//g,(function(){return a})).replace(//g,(function(){return n})).replace(//g,(function(){return o})),RegExp(e,t)}o=i(o).source,e.languages.jsx=e.languages.extend("markup",t),e.languages.jsx.tag.pattern=i(/<\/?(?:[\w.:-]+(?:+(?:[\w.:$-]+(?:=(?:"(?:\\[\s\S]|[^\\"])*"|'(?:\\[\s\S]|[^\\'])*'|[^\s{'"/>=]+|))?|))**\/?)?>/.source),e.languages.jsx.tag.inside.tag.pattern=/^<\/?[^\s>\/]*/,e.languages.jsx.tag.inside["attr-value"].pattern=/=(?!\{)(?:"(?:\\[\s\S]|[^\\"])*"|'(?:\\[\s\S]|[^\\'])*'|[^\s'">]+)/,e.languages.jsx.tag.inside.tag.inside["class-name"]=/^[A-Z]\w*(?:\.[A-Z]\w*)*$/,e.languages.jsx.tag.inside.comment=t.comment,e.languages.insertBefore("inside","attr-name",{spread:{pattern:i(//.source),inside:e.languages.jsx}},e.languages.jsx.tag),e.languages.insertBefore("inside","special-attr",{script:{pattern:i(/=/.source),alias:"language-javascript",inside:{"script-punctuation":{pattern:/^=(?=\{)/,alias:"punctuation"},rest:e.languages.jsx}}},e.languages.jsx.tag);var l=function(e){return e?"string"==typeof e?e:"string"==typeof e.content?e.content:e.content.map(l).join(""):""},r=function(t){for(var a=[],n=0;n0&&a[a.length-1].tagName===l(o.content[0].content[1])&&a.pop():"/>"===o.content[o.content.length-1].content||a.push({tagName:l(o.content[0].content[1]),openedBraces:0}):a.length>0&&"punctuation"===o.type&&"{"===o.content?a[a.length-1].openedBraces++:a.length>0&&a[a.length-1].openedBraces>0&&"punctuation"===o.type&&"}"===o.content?a[a.length-1].openedBraces--:i=!0),(i||"string"==typeof o)&&a.length>0&&0===a[a.length-1].openedBraces){var s=l(o);n0&&("string"==typeof t[n-1]||"plain-text"===t[n-1].type)&&(s=l(t[n-1])+s,t.splice(n-1,1),n--),t[n]=new e.Token("plain-text",s,null,s)}o.content&&"string"!=typeof o.content&&r(o.content)}};e.hooks.add("after-tokenize",(function(e){"jsx"!==e.language&&"tsx"!==e.language||r(e.tokens)}))}(o),function(e){e.languages.diff={coord:[/^(?:\*{3}|-{3}|\+{3}).*$/m,/^@@.*@@$/m,/^\d.*$/m]};var t={"deleted-sign":"-","deleted-arrow":"<","inserted-sign":"+","inserted-arrow":">",unchanged:" ",diff:"!"};Object.keys(t).forEach((function(a){var n=t[a],o=[];/^\w+$/.test(a)||o.push(/\w+/.exec(a)[0]),"diff"===a&&o.push("bold"),e.languages.diff[a]={pattern:RegExp("^(?:["+n+"].*(?:\r\n?|\n|(?![\\s\\S])))+","m"),alias:o,inside:{line:{pattern:/(.)(?=[\s\S]).*(?:\r\n?|\n)?/,lookbehind:!0},prefix:{pattern:/[\s\S]/,alias:/\w+/.exec(a)[0]}}}})),Object.defineProperty(e.languages.diff,"PREFIXES",{value:t})}(o),o.languages.git={comment:/^#.*/m,deleted:/^[-\u2013].*/m,inserted:/^\+.*/m,string:/("|')(?:\\.|(?!\1)[^\\\r\n])*\1/,command:{pattern:/^.*\$ git .*$/m,inside:{parameter:/\s--?\w+/}},coord:/^@@.*@@$/m,"commit-sha1":/^commit \w{40}$/m},o.languages.go=o.languages.extend("clike",{string:{pattern:/(^|[^\\])"(?:\\.|[^"\\\r\n])*"|`[^`]*`/,lookbehind:!0,greedy:!0},keyword:/\b(?:break|case|chan|const|continue|default|defer|else|fallthrough|for|func|go(?:to)?|if|import|interface|map|package|range|return|select|struct|switch|type|var)\b/,boolean:/\b(?:_|false|iota|nil|true)\b/,number:[/\b0(?:b[01_]+|o[0-7_]+)i?\b/i,/\b0x(?:[a-f\d_]+(?:\.[a-f\d_]*)?|\.[a-f\d_]+)(?:p[+-]?\d+(?:_\d+)*)?i?(?!\w)/i,/(?:\b\d[\d_]*(?:\.[\d_]*)?|\B\.\d[\d_]*)(?:e[+-]?[\d_]+)?i?(?!\w)/i],operator:/[*\/%^!=]=?|\+[=+]?|-[=-]?|\|[=|]?|&(?:=|&|\^=?)?|>(?:>=?|=)?|<(?:<=?|=|-)?|:=|\.\.\./,builtin:/\b(?:append|bool|byte|cap|close|complex|complex(?:64|128)|copy|delete|error|float(?:32|64)|u?int(?:8|16|32|64)?|imag|len|make|new|panic|print(?:ln)?|real|recover|rune|string|uintptr)\b/}),o.languages.insertBefore("go","string",{char:{pattern:/'(?:\\.|[^'\\\r\n]){0,10}'/,greedy:!0}}),delete o.languages.go["class-name"],function(e){function t(e,t){return"___"+e.toUpperCase()+t+"___"}Object.defineProperties(e.languages["markup-templating"]={},{buildPlaceholders:{value:function(a,n,o,i){if(a.language===n){var l=a.tokenStack=[];a.code=a.code.replace(o,(function(e){if("function"==typeof i&&!i(e))return e;for(var o,r=l.length;-1!==a.code.indexOf(o=t(n,r));)++r;return l[r]=e,o})),a.grammar=e.languages.markup}}},tokenizePlaceholders:{value:function(a,n){if(a.language===n&&a.tokenStack){a.grammar=e.languages[n];var o=0,i=Object.keys(a.tokenStack);!function l(r){for(var s=0;s=i.length);s++){var c=r[s];if("string"==typeof c||c.content&&"string"==typeof c.content){var d=i[o],u=a.tokenStack[d],g="string"==typeof c?c:c.content,p=t(n,d),f=g.indexOf(p);if(f>-1){++o;var b=g.substring(0,f),m=new e.Token(n,e.tokenize(u,a.grammar),"language-"+n,u),v=g.substring(f+p.length),h=[];b&&h.push.apply(h,l([b])),h.push(m),v&&h.push.apply(h,l([v])),"string"==typeof c?r.splice.apply(r,[s,1].concat(h)):c.content=h}}else c.content&&l(c.content)}return r}(a.tokens)}}}})}(o),function(e){e.languages.handlebars={comment:/\{\{![\s\S]*?\}\}/,delimiter:{pattern:/^\{\{\{?|\}\}\}?$/,alias:"punctuation"},string:/(["'])(?:\\.|(?!\1)[^\\\r\n])*\1/,number:/\b0x[\dA-Fa-f]+\b|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:[Ee][+-]?\d+)?/,boolean:/\b(?:false|true)\b/,block:{pattern:/^(\s*(?:~\s*)?)[#\/]\S+?(?=\s*(?:~\s*)?$|\s)/,lookbehind:!0,alias:"keyword"},brackets:{pattern:/\[[^\]]+\]/,inside:{punctuation:/\[|\]/,variable:/[\s\S]+/}},punctuation:/[!"#%&':()*+,.\/;<=>@\[\\\]^`{|}~]/,variable:/[^!"#%&'()*+,\/;<=>@\[\\\]^`{|}~\s]+/},e.hooks.add("before-tokenize",(function(t){e.languages["markup-templating"].buildPlaceholders(t,"handlebars",/\{\{\{[\s\S]+?\}\}\}|\{\{[\s\S]+?\}\}/g)})),e.hooks.add("after-tokenize",(function(t){e.languages["markup-templating"].tokenizePlaceholders(t,"handlebars")})),e.languages.hbs=e.languages.handlebars}(o),o.languages.json={property:{pattern:/(^|[^\\])"(?:\\.|[^\\"\r\n])*"(?=\s*:)/,lookbehind:!0,greedy:!0},string:{pattern:/(^|[^\\])"(?:\\.|[^\\"\r\n])*"(?!\s*:)/,lookbehind:!0,greedy:!0},comment:{pattern:/\/\/.*|\/\*[\s\S]*?(?:\*\/|$)/,greedy:!0},number:/-?\b\d+(?:\.\d+)?(?:e[+-]?\d+)?\b/i,punctuation:/[{}[\],]/,operator:/:/,boolean:/\b(?:false|true)\b/,null:{pattern:/\bnull\b/,alias:"keyword"}},o.languages.webmanifest=o.languages.json,o.languages.less=o.languages.extend("css",{comment:[/\/\*[\s\S]*?\*\//,{pattern:/(^|[^\\])\/\/.*/,lookbehind:!0}],atrule:{pattern:/@[\w-](?:\((?:[^(){}]|\([^(){}]*\))*\)|[^(){};\s]|\s+(?!\s))*?(?=\s*\{)/,inside:{punctuation:/[:()]/}},selector:{pattern:/(?:@\{[\w-]+\}|[^{};\s@])(?:@\{[\w-]+\}|\((?:[^(){}]|\([^(){}]*\))*\)|[^(){};@\s]|\s+(?!\s))*?(?=\s*\{)/,inside:{variable:/@+[\w-]+/}},property:/(?:@\{[\w-]+\}|[\w-])+(?:\+_?)?(?=\s*:)/,operator:/[+\-*\/]/}),o.languages.insertBefore("less","property",{variable:[{pattern:/@[\w-]+\s*:/,inside:{punctuation:/:/}},/@@?[\w-]+/],"mixin-usage":{pattern:/([{;]\s*)[.#](?!\d)[\w-].*?(?=[(;])/,lookbehind:!0,alias:"function"}}),o.languages.makefile={comment:{pattern:/(^|[^\\])#(?:\\(?:\r\n|[\s\S])|[^\\\r\n])*/,lookbehind:!0},string:{pattern:/(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},"builtin-target":{pattern:/\.[A-Z][^:#=\s]+(?=\s*:(?!=))/,alias:"builtin"},target:{pattern:/^(?:[^:=\s]|[ \t]+(?![\s:]))+(?=\s*:(?!=))/m,alias:"symbol",inside:{variable:/\$+(?:(?!\$)[^(){}:#=\s]+|(?=[({]))/}},variable:/\$+(?:(?!\$)[^(){}:#=\s]+|\([@*%<^+?][DF]\)|(?=[({]))/,keyword:/-include\b|\b(?:define|else|endef|endif|export|ifn?def|ifn?eq|include|override|private|sinclude|undefine|unexport|vpath)\b/,function:{pattern:/(\()(?:abspath|addsuffix|and|basename|call|dir|error|eval|file|filter(?:-out)?|findstring|firstword|flavor|foreach|guile|if|info|join|lastword|load|notdir|or|origin|patsubst|realpath|shell|sort|strip|subst|suffix|value|warning|wildcard|word(?:list|s)?)(?=[ \t])/,lookbehind:!0},operator:/(?:::|[?:+!])?=|[|@]/,punctuation:/[:;(){}]/},o.languages.objectivec=o.languages.extend("c",{string:{pattern:/@?"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"/,greedy:!0},keyword:/\b(?:asm|auto|break|case|char|const|continue|default|do|double|else|enum|extern|float|for|goto|if|in|inline|int|long|register|return|self|short|signed|sizeof|static|struct|super|switch|typedef|typeof|union|unsigned|void|volatile|while)\b|(?:@interface|@end|@implementation|@protocol|@class|@public|@protected|@private|@property|@try|@catch|@finally|@throw|@synthesize|@dynamic|@selector)\b/,operator:/-[->]?|\+\+?|!=?|<>?=?|==?|&&?|\|\|?|[~^%?*\/@]/}),delete o.languages.objectivec["class-name"],o.languages.objc=o.languages.objectivec,o.languages.ocaml={comment:{pattern:/\(\*[\s\S]*?\*\)/,greedy:!0},char:{pattern:/'(?:[^\\\r\n']|\\(?:.|[ox]?[0-9a-f]{1,3}))'/i,greedy:!0},string:[{pattern:/"(?:\\(?:[\s\S]|\r\n)|[^\\\r\n"])*"/,greedy:!0},{pattern:/\{([a-z_]*)\|[\s\S]*?\|\1\}/,greedy:!0}],number:[/\b(?:0b[01][01_]*|0o[0-7][0-7_]*)\b/i,/\b0x[a-f0-9][a-f0-9_]*(?:\.[a-f0-9_]*)?(?:p[+-]?\d[\d_]*)?(?!\w)/i,/\b\d[\d_]*(?:\.[\d_]*)?(?:e[+-]?\d[\d_]*)?(?!\w)/i],directive:{pattern:/\B#\w+/,alias:"property"},label:{pattern:/\B~\w+/,alias:"property"},"type-variable":{pattern:/\B'\w+/,alias:"function"},variant:{pattern:/`\w+/,alias:"symbol"},keyword:/\b(?:as|assert|begin|class|constraint|do|done|downto|else|end|exception|external|for|fun|function|functor|if|in|include|inherit|initializer|lazy|let|match|method|module|mutable|new|nonrec|object|of|open|private|rec|sig|struct|then|to|try|type|val|value|virtual|when|where|while|with)\b/,boolean:/\b(?:false|true)\b/,"operator-like-punctuation":{pattern:/\[[<>|]|[>|]\]|\{<|>\}/,alias:"punctuation"},operator:/\.[.~]|:[=>]|[=<>@^|&+\-*\/$%!?~][!$%&*+\-.\/:<=>?@^|~]*|\b(?:and|asr|land|lor|lsl|lsr|lxor|mod|or)\b/,punctuation:/;;|::|[(){}\[\].,:;#]|\b_\b/},o.languages.python={comment:{pattern:/(^|[^\\])#.*/,lookbehind:!0,greedy:!0},"string-interpolation":{pattern:/(?:f|fr|rf)(?:("""|''')[\s\S]*?\1|("|')(?:\\.|(?!\2)[^\\\r\n])*\2)/i,greedy:!0,inside:{interpolation:{pattern:/((?:^|[^{])(?:\{\{)*)\{(?!\{)(?:[^{}]|\{(?!\{)(?:[^{}]|\{(?!\{)(?:[^{}])+\})+\})+\}/,lookbehind:!0,inside:{"format-spec":{pattern:/(:)[^:(){}]+(?=\}$)/,lookbehind:!0},"conversion-option":{pattern:/![sra](?=[:}]$)/,alias:"punctuation"},rest:null}},string:/[\s\S]+/}},"triple-quoted-string":{pattern:/(?:[rub]|br|rb)?("""|''')[\s\S]*?\1/i,greedy:!0,alias:"string"},string:{pattern:/(?:[rub]|br|rb)?("|')(?:\\.|(?!\1)[^\\\r\n])*\1/i,greedy:!0},function:{pattern:/((?:^|\s)def[ \t]+)[a-zA-Z_]\w*(?=\s*\()/g,lookbehind:!0},"class-name":{pattern:/(\bclass\s+)\w+/i,lookbehind:!0},decorator:{pattern:/(^[\t ]*)@\w+(?:\.\w+)*/m,lookbehind:!0,alias:["annotation","punctuation"],inside:{punctuation:/\./}},keyword:/\b(?:_(?=\s*:)|and|as|assert|async|await|break|case|class|continue|def|del|elif|else|except|exec|finally|for|from|global|if|import|in|is|lambda|match|nonlocal|not|or|pass|print|raise|return|try|while|with|yield)\b/,builtin:/\b(?:__import__|abs|all|any|apply|ascii|basestring|bin|bool|buffer|bytearray|bytes|callable|chr|classmethod|cmp|coerce|compile|complex|delattr|dict|dir|divmod|enumerate|eval|execfile|file|filter|float|format|frozenset|getattr|globals|hasattr|hash|help|hex|id|input|int|intern|isinstance|issubclass|iter|len|list|locals|long|map|max|memoryview|min|next|object|oct|open|ord|pow|property|range|raw_input|reduce|reload|repr|reversed|round|set|setattr|slice|sorted|staticmethod|str|sum|super|tuple|type|unichr|unicode|vars|xrange|zip)\b/,boolean:/\b(?:False|None|True)\b/,number:/\b0(?:b(?:_?[01])+|o(?:_?[0-7])+|x(?:_?[a-f0-9])+)\b|(?:\b\d+(?:_\d+)*(?:\.(?:\d+(?:_\d+)*)?)?|\B\.\d+(?:_\d+)*)(?:e[+-]?\d+(?:_\d+)*)?j?(?!\w)/i,operator:/[-+%=]=?|!=|:=|\*\*?=?|\/\/?=?|<[<=>]?|>[=>]?|[&|^~]/,punctuation:/[{}[\];(),.:]/},o.languages.python["string-interpolation"].inside.interpolation.inside.rest=o.languages.python,o.languages.py=o.languages.python,o.languages.reason=o.languages.extend("clike",{string:{pattern:/"(?:\\(?:\r\n|[\s\S])|[^\\\r\n"])*"/,greedy:!0},"class-name":/\b[A-Z]\w*/,keyword:/\b(?:and|as|assert|begin|class|constraint|do|done|downto|else|end|exception|external|for|fun|function|functor|if|in|include|inherit|initializer|lazy|let|method|module|mutable|new|nonrec|object|of|open|or|private|rec|sig|struct|switch|then|to|try|type|val|virtual|when|while|with)\b/,operator:/\.{3}|:[:=]|\|>|->|=(?:==?|>)?|<=?|>=?|[|^?'#!~`]|[+\-*\/]\.?|\b(?:asr|land|lor|lsl|lsr|lxor|mod)\b/}),o.languages.insertBefore("reason","class-name",{char:{pattern:/'(?:\\x[\da-f]{2}|\\o[0-3][0-7][0-7]|\\\d{3}|\\.|[^'\\\r\n])'/,greedy:!0},constructor:/\b[A-Z]\w*\b(?!\s*\.)/,label:{pattern:/\b[a-z]\w*(?=::)/,alias:"symbol"}}),delete o.languages.reason.function,function(e){e.languages.sass=e.languages.extend("css",{comment:{pattern:/^([ \t]*)\/[\/*].*(?:(?:\r?\n|\r)\1[ \t].+)*/m,lookbehind:!0,greedy:!0}}),e.languages.insertBefore("sass","atrule",{"atrule-line":{pattern:/^(?:[ \t]*)[@+=].+/m,greedy:!0,inside:{atrule:/(?:@[\w-]+|[+=])/}}}),delete e.languages.sass.atrule;var t=/\$[-\w]+|#\{\$[-\w]+\}/,a=[/[+*\/%]|[=!]=|<=?|>=?|\b(?:and|not|or)\b/,{pattern:/(\s)-(?=\s)/,lookbehind:!0}];e.languages.insertBefore("sass","property",{"variable-line":{pattern:/^[ \t]*\$.+/m,greedy:!0,inside:{punctuation:/:/,variable:t,operator:a}},"property-line":{pattern:/^[ \t]*(?:[^:\s]+ *:.*|:[^:\s].*)/m,greedy:!0,inside:{property:[/[^:\s]+(?=\s*:)/,{pattern:/(:)[^:\s]+/,lookbehind:!0}],punctuation:/:/,variable:t,operator:a,important:e.languages.sass.important}}}),delete e.languages.sass.property,delete e.languages.sass.important,e.languages.insertBefore("sass","punctuation",{selector:{pattern:/^([ \t]*)\S(?:,[^,\r\n]+|[^,\r\n]*)(?:,[^,\r\n]+)*(?:,(?:\r?\n|\r)\1[ \t]+\S(?:,[^,\r\n]+|[^,\r\n]*)(?:,[^,\r\n]+)*)*/m,lookbehind:!0,greedy:!0}})}(o),o.languages.scss=o.languages.extend("css",{comment:{pattern:/(^|[^\\])(?:\/\*[\s\S]*?\*\/|\/\/.*)/,lookbehind:!0},atrule:{pattern:/@[\w-](?:\([^()]+\)|[^()\s]|\s+(?!\s))*?(?=\s+[{;])/,inside:{rule:/@[\w-]+/}},url:/(?:[-a-z]+-)?url(?=\()/i,selector:{pattern:/(?=\S)[^@;{}()]?(?:[^@;{}()\s]|\s+(?!\s)|#\{\$[-\w]+\})+(?=\s*\{(?:\}|\s|[^}][^:{}]*[:{][^}]))/,inside:{parent:{pattern:/&/,alias:"important"},placeholder:/%[-\w]+/,variable:/\$[-\w]+|#\{\$[-\w]+\}/}},property:{pattern:/(?:[-\w]|\$[-\w]|#\{\$[-\w]+\})+(?=\s*:)/,inside:{variable:/\$[-\w]+|#\{\$[-\w]+\}/}}}),o.languages.insertBefore("scss","atrule",{keyword:[/@(?:content|debug|each|else(?: if)?|extend|for|forward|function|if|import|include|mixin|return|use|warn|while)\b/i,{pattern:/( )(?:from|through)(?= )/,lookbehind:!0}]}),o.languages.insertBefore("scss","important",{variable:/\$[-\w]+|#\{\$[-\w]+\}/}),o.languages.insertBefore("scss","function",{"module-modifier":{pattern:/\b(?:as|hide|show|with)\b/i,alias:"keyword"},placeholder:{pattern:/%[-\w]+/,alias:"selector"},statement:{pattern:/\B!(?:default|optional)\b/i,alias:"keyword"},boolean:/\b(?:false|true)\b/,null:{pattern:/\bnull\b/,alias:"keyword"},operator:{pattern:/(\s)(?:[-+*\/%]|[=!]=|<=?|>=?|and|not|or)(?=\s)/,lookbehind:!0}}),o.languages.scss.atrule.inside.rest=o.languages.scss,function(e){var t={pattern:/(\b\d+)(?:%|[a-z]+)/,lookbehind:!0},a={pattern:/(^|[^\w.-])-?(?:\d+(?:\.\d+)?|\.\d+)/,lookbehind:!0},n={comment:{pattern:/(^|[^\\])(?:\/\*[\s\S]*?\*\/|\/\/.*)/,lookbehind:!0},url:{pattern:/\burl\((["']?).*?\1\)/i,greedy:!0},string:{pattern:/("|')(?:(?!\1)[^\\\r\n]|\\(?:\r\n|[\s\S]))*\1/,greedy:!0},interpolation:null,func:null,important:/\B!(?:important|optional)\b/i,keyword:{pattern:/(^|\s+)(?:(?:else|for|if|return|unless)(?=\s|$)|@[\w-]+)/,lookbehind:!0},hexcode:/#[\da-f]{3,6}/i,color:[/\b(?:AliceBlue|AntiqueWhite|Aqua|Aquamarine|Azure|Beige|Bisque|Black|BlanchedAlmond|Blue|BlueViolet|Brown|BurlyWood|CadetBlue|Chartreuse|Chocolate|Coral|CornflowerBlue|Cornsilk|Crimson|Cyan|DarkBlue|DarkCyan|DarkGoldenRod|DarkGr[ae]y|DarkGreen|DarkKhaki|DarkMagenta|DarkOliveGreen|DarkOrange|DarkOrchid|DarkRed|DarkSalmon|DarkSeaGreen|DarkSlateBlue|DarkSlateGr[ae]y|DarkTurquoise|DarkViolet|DeepPink|DeepSkyBlue|DimGr[ae]y|DodgerBlue|FireBrick|FloralWhite|ForestGreen|Fuchsia|Gainsboro|GhostWhite|Gold|GoldenRod|Gr[ae]y|Green|GreenYellow|HoneyDew|HotPink|IndianRed|Indigo|Ivory|Khaki|Lavender|LavenderBlush|LawnGreen|LemonChiffon|LightBlue|LightCoral|LightCyan|LightGoldenRodYellow|LightGr[ae]y|LightGreen|LightPink|LightSalmon|LightSeaGreen|LightSkyBlue|LightSlateGr[ae]y|LightSteelBlue|LightYellow|Lime|LimeGreen|Linen|Magenta|Maroon|MediumAquaMarine|MediumBlue|MediumOrchid|MediumPurple|MediumSeaGreen|MediumSlateBlue|MediumSpringGreen|MediumTurquoise|MediumVioletRed|MidnightBlue|MintCream|MistyRose|Moccasin|NavajoWhite|Navy|OldLace|Olive|OliveDrab|Orange|OrangeRed|Orchid|PaleGoldenRod|PaleGreen|PaleTurquoise|PaleVioletRed|PapayaWhip|PeachPuff|Peru|Pink|Plum|PowderBlue|Purple|Red|RosyBrown|RoyalBlue|SaddleBrown|Salmon|SandyBrown|SeaGreen|SeaShell|Sienna|Silver|SkyBlue|SlateBlue|SlateGr[ae]y|Snow|SpringGreen|SteelBlue|Tan|Teal|Thistle|Tomato|Transparent|Turquoise|Violet|Wheat|White|WhiteSmoke|Yellow|YellowGreen)\b/i,{pattern:/\b(?:hsl|rgb)\(\s*\d{1,3}\s*,\s*\d{1,3}%?\s*,\s*\d{1,3}%?\s*\)\B|\b(?:hsl|rgb)a\(\s*\d{1,3}\s*,\s*\d{1,3}%?\s*,\s*\d{1,3}%?\s*,\s*(?:0|0?\.\d+|1)\s*\)\B/i,inside:{unit:t,number:a,function:/[\w-]+(?=\()/,punctuation:/[(),]/}}],entity:/\\[\da-f]{1,8}/i,unit:t,boolean:/\b(?:false|true)\b/,operator:[/~|[+!\/%<>?=]=?|[-:]=|\*[*=]?|\.{2,3}|&&|\|\||\B-\B|\b(?:and|in|is(?: a| defined| not|nt)?|not|or)\b/],number:a,punctuation:/[{}()\[\];:,]/};n.interpolation={pattern:/\{[^\r\n}:]+\}/,alias:"variable",inside:{delimiter:{pattern:/^\{|\}$/,alias:"punctuation"},rest:n}},n.func={pattern:/[\w-]+\([^)]*\).*/,inside:{function:/^[^(]+/,rest:n}},e.languages.stylus={"atrule-declaration":{pattern:/(^[ \t]*)@.+/m,lookbehind:!0,inside:{atrule:/^@[\w-]+/,rest:n}},"variable-declaration":{pattern:/(^[ \t]*)[\w$-]+\s*.?=[ \t]*(?:\{[^{}]*\}|\S.*|$)/m,lookbehind:!0,inside:{variable:/^\S+/,rest:n}},statement:{pattern:/(^[ \t]*)(?:else|for|if|return|unless)[ \t].+/m,lookbehind:!0,inside:{keyword:/^\S+/,rest:n}},"property-declaration":{pattern:/((?:^|\{)([ \t]*))(?:[\w-]|\{[^}\r\n]+\})+(?:\s*:\s*|[ \t]+)(?!\s)[^{\r\n]*(?:;|[^{\r\n,]$(?!(?:\r?\n|\r)(?:\{|\2[ \t])))/m,lookbehind:!0,inside:{property:{pattern:/^[^\s:]+/,inside:{interpolation:n.interpolation}},rest:n}},selector:{pattern:/(^[ \t]*)(?:(?=\S)(?:[^{}\r\n:()]|::?[\w-]+(?:\([^)\r\n]*\)|(?![\w-]))|\{[^}\r\n]+\})+)(?:(?:\r?\n|\r)(?:\1(?:(?=\S)(?:[^{}\r\n:()]|::?[\w-]+(?:\([^)\r\n]*\)|(?![\w-]))|\{[^}\r\n]+\})+)))*(?:,$|\{|(?=(?:\r?\n|\r)(?:\{|\1[ \t])))/m,lookbehind:!0,inside:{interpolation:n.interpolation,comment:n.comment,punctuation:/[{},]/}},func:n.func,string:n.string,comment:{pattern:/(^|[^\\])(?:\/\*[\s\S]*?\*\/|\/\/.*)/,lookbehind:!0,greedy:!0},interpolation:n.interpolation,punctuation:/[{}()\[\];:.]/}}(o),function(e){var t=e.util.clone(e.languages.typescript);e.languages.tsx=e.languages.extend("jsx",t),delete e.languages.tsx.parameter,delete e.languages.tsx["literal-property"];var a=e.languages.tsx.tag;a.pattern=RegExp(/(^|[^\w$]|(?=<\/))/.source+"(?:"+a.pattern.source+")",a.pattern.flags),a.lookbehind=!0}(o),o.languages.wasm={comment:[/\(;[\s\S]*?;\)/,{pattern:/;;.*/,greedy:!0}],string:{pattern:/"(?:\\[\s\S]|[^"\\])*"/,greedy:!0},keyword:[{pattern:/\b(?:align|offset)=/,inside:{operator:/=/}},{pattern:/\b(?:(?:f32|f64|i32|i64)(?:\.(?:abs|add|and|ceil|clz|const|convert_[su]\/i(?:32|64)|copysign|ctz|demote\/f64|div(?:_[su])?|eqz?|extend_[su]\/i32|floor|ge(?:_[su])?|gt(?:_[su])?|le(?:_[su])?|load(?:(?:8|16|32)_[su])?|lt(?:_[su])?|max|min|mul|neg?|nearest|or|popcnt|promote\/f32|reinterpret\/[fi](?:32|64)|rem_[su]|rot[lr]|shl|shr_[su]|sqrt|store(?:8|16|32)?|sub|trunc(?:_[su]\/f(?:32|64))?|wrap\/i64|xor))?|memory\.(?:grow|size))\b/,inside:{punctuation:/\./}},/\b(?:anyfunc|block|br(?:_if|_table)?|call(?:_indirect)?|data|drop|elem|else|end|export|func|get_(?:global|local)|global|if|import|local|loop|memory|module|mut|nop|offset|param|result|return|select|set_(?:global|local)|start|table|tee_local|then|type|unreachable)\b/],variable:/\$[\w!#$%&'*+\-./:<=>?@\\^`|~]+/,number:/[+-]?\b(?:\d(?:_?\d)*(?:\.\d(?:_?\d)*)?(?:[eE][+-]?\d(?:_?\d)*)?|0x[\da-fA-F](?:_?[\da-fA-F])*(?:\.[\da-fA-F](?:_?[\da-fA-D])*)?(?:[pP][+-]?\d(?:_?\d)*)?)\b|\binf\b|\bnan(?::0x[\da-fA-F](?:_?[\da-fA-D])*)?\b/,punctuation:/[()]/};const i=o},29901:e=>{e.exports&&(e.exports={core:{meta:{path:"components/prism-core.js",option:"mandatory"},core:"Core"},themes:{meta:{path:"themes/{id}.css",link:"index.html?theme={id}",exclusive:!0},prism:{title:"Default",option:"default"},"prism-dark":"Dark","prism-funky":"Funky","prism-okaidia":{title:"Okaidia",owner:"ocodia"},"prism-twilight":{title:"Twilight",owner:"remybach"},"prism-coy":{title:"Coy",owner:"tshedor"},"prism-solarizedlight":{title:"Solarized Light",owner:"hectormatos2011 "},"prism-tomorrow":{title:"Tomorrow Night",owner:"Rosey"}},languages:{meta:{path:"components/prism-{id}",noCSS:!0,examplesPath:"examples/prism-{id}",addCheckAll:!0},markup:{title:"Markup",alias:["html","xml","svg","mathml","ssml","atom","rss"],aliasTitles:{html:"HTML",xml:"XML",svg:"SVG",mathml:"MathML",ssml:"SSML",atom:"Atom",rss:"RSS"},option:"default"},css:{title:"CSS",option:"default",modify:"markup"},clike:{title:"C-like",option:"default"},javascript:{title:"JavaScript",require:"clike",modify:"markup",optional:"regex",alias:"js",option:"default"},abap:{title:"ABAP",owner:"dellagustin"},abnf:{title:"ABNF",owner:"RunDevelopment"},actionscript:{title:"ActionScript",require:"javascript",modify:"markup",owner:"Golmote"},ada:{title:"Ada",owner:"Lucretia"},agda:{title:"Agda",owner:"xy-ren"},al:{title:"AL",owner:"RunDevelopment"},antlr4:{title:"ANTLR4",alias:"g4",owner:"RunDevelopment"},apacheconf:{title:"Apache Configuration",owner:"GuiTeK"},apex:{title:"Apex",require:["clike","sql"],owner:"RunDevelopment"},apl:{title:"APL",owner:"ngn"},applescript:{title:"AppleScript",owner:"Golmote"},aql:{title:"AQL",owner:"RunDevelopment"},arduino:{title:"Arduino",require:"cpp",alias:"ino",owner:"dkern"},arff:{title:"ARFF",owner:"Golmote"},armasm:{title:"ARM Assembly",alias:"arm-asm",owner:"RunDevelopment"},arturo:{title:"Arturo",alias:"art",optional:["bash","css","javascript","markup","markdown","sql"],owner:"drkameleon"},asciidoc:{alias:"adoc",title:"AsciiDoc",owner:"Golmote"},aspnet:{title:"ASP.NET (C#)",require:["markup","csharp"],owner:"nauzilus"},asm6502:{title:"6502 Assembly",owner:"kzurawel"},asmatmel:{title:"Atmel AVR Assembly",owner:"cerkit"},autohotkey:{title:"AutoHotkey",owner:"aviaryan"},autoit:{title:"AutoIt",owner:"Golmote"},avisynth:{title:"AviSynth",alias:"avs",owner:"Zinfidel"},"avro-idl":{title:"Avro IDL",alias:"avdl",owner:"RunDevelopment"},awk:{title:"AWK",alias:"gawk",aliasTitles:{gawk:"GAWK"},owner:"RunDevelopment"},bash:{title:"Bash",alias:["sh","shell"],aliasTitles:{sh:"Shell",shell:"Shell"},owner:"zeitgeist87"},basic:{title:"BASIC",owner:"Golmote"},batch:{title:"Batch",owner:"Golmote"},bbcode:{title:"BBcode",alias:"shortcode",aliasTitles:{shortcode:"Shortcode"},owner:"RunDevelopment"},bbj:{title:"BBj",owner:"hyyan"},bicep:{title:"Bicep",owner:"johnnyreilly"},birb:{title:"Birb",require:"clike",owner:"Calamity210"},bison:{title:"Bison",require:"c",owner:"Golmote"},bnf:{title:"BNF",alias:"rbnf",aliasTitles:{rbnf:"RBNF"},owner:"RunDevelopment"},bqn:{title:"BQN",owner:"yewscion"},brainfuck:{title:"Brainfuck",owner:"Golmote"},brightscript:{title:"BrightScript",owner:"RunDevelopment"},bro:{title:"Bro",owner:"wayward710"},bsl:{title:"BSL (1C:Enterprise)",alias:"oscript",aliasTitles:{oscript:"OneScript"},owner:"Diversus23"},c:{title:"C",require:"clike",owner:"zeitgeist87"},csharp:{title:"C#",require:"clike",alias:["cs","dotnet"],owner:"mvalipour"},cpp:{title:"C++",require:"c",owner:"zeitgeist87"},cfscript:{title:"CFScript",require:"clike",alias:"cfc",owner:"mjclemente"},chaiscript:{title:"ChaiScript",require:["clike","cpp"],owner:"RunDevelopment"},cil:{title:"CIL",owner:"sbrl"},cilkc:{title:"Cilk/C",require:"c",alias:"cilk-c",owner:"OpenCilk"},cilkcpp:{title:"Cilk/C++",require:"cpp",alias:["cilk-cpp","cilk"],owner:"OpenCilk"},clojure:{title:"Clojure",owner:"troglotit"},cmake:{title:"CMake",owner:"mjrogozinski"},cobol:{title:"COBOL",owner:"RunDevelopment"},coffeescript:{title:"CoffeeScript",require:"javascript",alias:"coffee",owner:"R-osey"},concurnas:{title:"Concurnas",alias:"conc",owner:"jasontatton"},csp:{title:"Content-Security-Policy",owner:"ScottHelme"},cooklang:{title:"Cooklang",owner:"ahue"},coq:{title:"Coq",owner:"RunDevelopment"},crystal:{title:"Crystal",require:"ruby",owner:"MakeNowJust"},"css-extras":{title:"CSS Extras",require:"css",modify:"css",owner:"milesj"},csv:{title:"CSV",owner:"RunDevelopment"},cue:{title:"CUE",owner:"RunDevelopment"},cypher:{title:"Cypher",owner:"RunDevelopment"},d:{title:"D",require:"clike",owner:"Golmote"},dart:{title:"Dart",require:"clike",owner:"Golmote"},dataweave:{title:"DataWeave",owner:"machaval"},dax:{title:"DAX",owner:"peterbud"},dhall:{title:"Dhall",owner:"RunDevelopment"},diff:{title:"Diff",owner:"uranusjr"},django:{title:"Django/Jinja2",require:"markup-templating",alias:"jinja2",owner:"romanvm"},"dns-zone-file":{title:"DNS zone file",owner:"RunDevelopment",alias:"dns-zone"},docker:{title:"Docker",alias:"dockerfile",owner:"JustinBeckwith"},dot:{title:"DOT (Graphviz)",alias:"gv",optional:"markup",owner:"RunDevelopment"},ebnf:{title:"EBNF",owner:"RunDevelopment"},editorconfig:{title:"EditorConfig",owner:"osipxd"},eiffel:{title:"Eiffel",owner:"Conaclos"},ejs:{title:"EJS",require:["javascript","markup-templating"],owner:"RunDevelopment",alias:"eta",aliasTitles:{eta:"Eta"}},elixir:{title:"Elixir",owner:"Golmote"},elm:{title:"Elm",owner:"zwilias"},etlua:{title:"Embedded Lua templating",require:["lua","markup-templating"],owner:"RunDevelopment"},erb:{title:"ERB",require:["ruby","markup-templating"],owner:"Golmote"},erlang:{title:"Erlang",owner:"Golmote"},"excel-formula":{title:"Excel Formula",alias:["xlsx","xls"],owner:"RunDevelopment"},fsharp:{title:"F#",require:"clike",owner:"simonreynolds7"},factor:{title:"Factor",owner:"catb0t"},false:{title:"False",owner:"edukisto"},"firestore-security-rules":{title:"Firestore security rules",require:"clike",owner:"RunDevelopment"},flow:{title:"Flow",require:"javascript",owner:"Golmote"},fortran:{title:"Fortran",owner:"Golmote"},ftl:{title:"FreeMarker Template Language",require:"markup-templating",owner:"RunDevelopment"},gml:{title:"GameMaker Language",alias:"gamemakerlanguage",require:"clike",owner:"LiarOnce"},gap:{title:"GAP (CAS)",owner:"RunDevelopment"},gcode:{title:"G-code",owner:"RunDevelopment"},gdscript:{title:"GDScript",owner:"RunDevelopment"},gedcom:{title:"GEDCOM",owner:"Golmote"},gettext:{title:"gettext",alias:"po",owner:"RunDevelopment"},gherkin:{title:"Gherkin",owner:"hason"},git:{title:"Git",owner:"lgiraudel"},glsl:{title:"GLSL",require:"c",owner:"Golmote"},gn:{title:"GN",alias:"gni",owner:"RunDevelopment"},"linker-script":{title:"GNU Linker Script",alias:"ld",owner:"RunDevelopment"},go:{title:"Go",require:"clike",owner:"arnehormann"},"go-module":{title:"Go module",alias:"go-mod",owner:"RunDevelopment"},gradle:{title:"Gradle",require:"clike",owner:"zeabdelkhalek-badido18"},graphql:{title:"GraphQL",optional:"markdown",owner:"Golmote"},groovy:{title:"Groovy",require:"clike",owner:"robfletcher"},haml:{title:"Haml",require:"ruby",optional:["css","css-extras","coffeescript","erb","javascript","less","markdown","scss","textile"],owner:"Golmote"},handlebars:{title:"Handlebars",require:"markup-templating",alias:["hbs","mustache"],aliasTitles:{mustache:"Mustache"},owner:"Golmote"},haskell:{title:"Haskell",alias:"hs",owner:"bholst"},haxe:{title:"Haxe",require:"clike",optional:"regex",owner:"Golmote"},hcl:{title:"HCL",owner:"outsideris"},hlsl:{title:"HLSL",require:"c",owner:"RunDevelopment"},hoon:{title:"Hoon",owner:"matildepark"},http:{title:"HTTP",optional:["csp","css","hpkp","hsts","javascript","json","markup","uri"],owner:"danielgtaylor"},hpkp:{title:"HTTP Public-Key-Pins",owner:"ScottHelme"},hsts:{title:"HTTP Strict-Transport-Security",owner:"ScottHelme"},ichigojam:{title:"IchigoJam",owner:"BlueCocoa"},icon:{title:"Icon",owner:"Golmote"},"icu-message-format":{title:"ICU Message Format",owner:"RunDevelopment"},idris:{title:"Idris",alias:"idr",owner:"KeenS",require:"haskell"},ignore:{title:".ignore",owner:"osipxd",alias:["gitignore","hgignore","npmignore"],aliasTitles:{gitignore:".gitignore",hgignore:".hgignore",npmignore:".npmignore"}},inform7:{title:"Inform 7",owner:"Golmote"},ini:{title:"Ini",owner:"aviaryan"},io:{title:"Io",owner:"AlesTsurko"},j:{title:"J",owner:"Golmote"},java:{title:"Java",require:"clike",owner:"sherblot"},javadoc:{title:"JavaDoc",require:["markup","java","javadoclike"],modify:"java",optional:"scala",owner:"RunDevelopment"},javadoclike:{title:"JavaDoc-like",modify:["java","javascript","php"],owner:"RunDevelopment"},javastacktrace:{title:"Java stack trace",owner:"RunDevelopment"},jexl:{title:"Jexl",owner:"czosel"},jolie:{title:"Jolie",require:"clike",owner:"thesave"},jq:{title:"JQ",owner:"RunDevelopment"},jsdoc:{title:"JSDoc",require:["javascript","javadoclike","typescript"],modify:"javascript",optional:["actionscript","coffeescript"],owner:"RunDevelopment"},"js-extras":{title:"JS Extras",require:"javascript",modify:"javascript",optional:["actionscript","coffeescript","flow","n4js","typescript"],owner:"RunDevelopment"},json:{title:"JSON",alias:"webmanifest",aliasTitles:{webmanifest:"Web App Manifest"},owner:"CupOfTea696"},json5:{title:"JSON5",require:"json",owner:"RunDevelopment"},jsonp:{title:"JSONP",require:"json",owner:"RunDevelopment"},jsstacktrace:{title:"JS stack trace",owner:"sbrl"},"js-templates":{title:"JS Templates",require:"javascript",modify:"javascript",optional:["css","css-extras","graphql","markdown","markup","sql"],owner:"RunDevelopment"},julia:{title:"Julia",owner:"cdagnino"},keepalived:{title:"Keepalived Configure",owner:"dev-itsheng"},keyman:{title:"Keyman",owner:"mcdurdin"},kotlin:{title:"Kotlin",alias:["kt","kts"],aliasTitles:{kts:"Kotlin Script"},require:"clike",owner:"Golmote"},kumir:{title:"KuMir (\u041a\u0443\u041c\u0438\u0440)",alias:"kum",owner:"edukisto"},kusto:{title:"Kusto",owner:"RunDevelopment"},latex:{title:"LaTeX",alias:["tex","context"],aliasTitles:{tex:"TeX",context:"ConTeXt"},owner:"japborst"},latte:{title:"Latte",require:["clike","markup-templating","php"],owner:"nette"},less:{title:"Less",require:"css",optional:"css-extras",owner:"Golmote"},lilypond:{title:"LilyPond",require:"scheme",alias:"ly",owner:"RunDevelopment"},liquid:{title:"Liquid",require:"markup-templating",owner:"cinhtau"},lisp:{title:"Lisp",alias:["emacs","elisp","emacs-lisp"],owner:"JuanCaicedo"},livescript:{title:"LiveScript",owner:"Golmote"},llvm:{title:"LLVM IR",owner:"porglezomp"},log:{title:"Log file",optional:"javastacktrace",owner:"RunDevelopment"},lolcode:{title:"LOLCODE",owner:"Golmote"},lua:{title:"Lua",owner:"Golmote"},magma:{title:"Magma (CAS)",owner:"RunDevelopment"},makefile:{title:"Makefile",owner:"Golmote"},markdown:{title:"Markdown",require:"markup",optional:"yaml",alias:"md",owner:"Golmote"},"markup-templating":{title:"Markup templating",require:"markup",owner:"Golmote"},mata:{title:"Mata",owner:"RunDevelopment"},matlab:{title:"MATLAB",owner:"Golmote"},maxscript:{title:"MAXScript",owner:"RunDevelopment"},mel:{title:"MEL",owner:"Golmote"},mermaid:{title:"Mermaid",owner:"RunDevelopment"},metafont:{title:"METAFONT",owner:"LaeriExNihilo"},mizar:{title:"Mizar",owner:"Golmote"},mongodb:{title:"MongoDB",owner:"airs0urce",require:"javascript"},monkey:{title:"Monkey",owner:"Golmote"},moonscript:{title:"MoonScript",alias:"moon",owner:"RunDevelopment"},n1ql:{title:"N1QL",owner:"TMWilds"},n4js:{title:"N4JS",require:"javascript",optional:"jsdoc",alias:"n4jsd",owner:"bsmith-n4"},"nand2tetris-hdl":{title:"Nand To Tetris HDL",owner:"stephanmax"},naniscript:{title:"Naninovel Script",owner:"Elringus",alias:"nani"},nasm:{title:"NASM",owner:"rbmj"},neon:{title:"NEON",owner:"nette"},nevod:{title:"Nevod",owner:"nezaboodka"},nginx:{title:"nginx",owner:"volado"},nim:{title:"Nim",owner:"Golmote"},nix:{title:"Nix",owner:"Golmote"},nsis:{title:"NSIS",owner:"idleberg"},objectivec:{title:"Objective-C",require:"c",alias:"objc",owner:"uranusjr"},ocaml:{title:"OCaml",owner:"Golmote"},odin:{title:"Odin",owner:"edukisto"},opencl:{title:"OpenCL",require:"c",modify:["c","cpp"],owner:"Milania1"},openqasm:{title:"OpenQasm",alias:"qasm",owner:"RunDevelopment"},oz:{title:"Oz",owner:"Golmote"},parigp:{title:"PARI/GP",owner:"Golmote"},parser:{title:"Parser",require:"markup",owner:"Golmote"},pascal:{title:"Pascal",alias:"objectpascal",aliasTitles:{objectpascal:"Object Pascal"},owner:"Golmote"},pascaligo:{title:"Pascaligo",owner:"DefinitelyNotAGoat"},psl:{title:"PATROL Scripting Language",owner:"bertysentry"},pcaxis:{title:"PC-Axis",alias:"px",owner:"RunDevelopment"},peoplecode:{title:"PeopleCode",alias:"pcode",owner:"RunDevelopment"},perl:{title:"Perl",owner:"Golmote"},php:{title:"PHP",require:"markup-templating",owner:"milesj"},phpdoc:{title:"PHPDoc",require:["php","javadoclike"],modify:"php",owner:"RunDevelopment"},"php-extras":{title:"PHP Extras",require:"php",modify:"php",owner:"milesj"},"plant-uml":{title:"PlantUML",alias:"plantuml",owner:"RunDevelopment"},plsql:{title:"PL/SQL",require:"sql",owner:"Golmote"},powerquery:{title:"PowerQuery",alias:["pq","mscript"],owner:"peterbud"},powershell:{title:"PowerShell",owner:"nauzilus"},processing:{title:"Processing",require:"clike",owner:"Golmote"},prolog:{title:"Prolog",owner:"Golmote"},promql:{title:"PromQL",owner:"arendjr"},properties:{title:".properties",owner:"Golmote"},protobuf:{title:"Protocol Buffers",require:"clike",owner:"just-boris"},pug:{title:"Pug",require:["markup","javascript"],optional:["coffeescript","ejs","handlebars","less","livescript","markdown","scss","stylus","twig"],owner:"Golmote"},puppet:{title:"Puppet",owner:"Golmote"},pure:{title:"Pure",optional:["c","cpp","fortran"],owner:"Golmote"},purebasic:{title:"PureBasic",require:"clike",alias:"pbfasm",owner:"HeX0R101"},purescript:{title:"PureScript",require:"haskell",alias:"purs",owner:"sriharshachilakapati"},python:{title:"Python",alias:"py",owner:"multipetros"},qsharp:{title:"Q#",require:"clike",alias:"qs",owner:"fedonman"},q:{title:"Q (kdb+ database)",owner:"Golmote"},qml:{title:"QML",require:"javascript",owner:"RunDevelopment"},qore:{title:"Qore",require:"clike",owner:"temnroegg"},r:{title:"R",owner:"Golmote"},racket:{title:"Racket",require:"scheme",alias:"rkt",owner:"RunDevelopment"},cshtml:{title:"Razor C#",alias:"razor",require:["markup","csharp"],optional:["css","css-extras","javascript","js-extras"],owner:"RunDevelopment"},jsx:{title:"React JSX",require:["markup","javascript"],optional:["jsdoc","js-extras","js-templates"],owner:"vkbansal"},tsx:{title:"React TSX",require:["jsx","typescript"]},reason:{title:"Reason",require:"clike",owner:"Golmote"},regex:{title:"Regex",owner:"RunDevelopment"},rego:{title:"Rego",owner:"JordanSh"},renpy:{title:"Ren'py",alias:"rpy",owner:"HyuchiaDiego"},rescript:{title:"ReScript",alias:"res",owner:"vmarcosp"},rest:{title:"reST (reStructuredText)",owner:"Golmote"},rip:{title:"Rip",owner:"ravinggenius"},roboconf:{title:"Roboconf",owner:"Golmote"},robotframework:{title:"Robot Framework",alias:"robot",owner:"RunDevelopment"},ruby:{title:"Ruby",require:"clike",alias:"rb",owner:"samflores"},rust:{title:"Rust",owner:"Golmote"},sas:{title:"SAS",optional:["groovy","lua","sql"],owner:"Golmote"},sass:{title:"Sass (Sass)",require:"css",optional:"css-extras",owner:"Golmote"},scss:{title:"Sass (SCSS)",require:"css",optional:"css-extras",owner:"MoOx"},scala:{title:"Scala",require:"java",owner:"jozic"},scheme:{title:"Scheme",owner:"bacchus123"},"shell-session":{title:"Shell session",require:"bash",alias:["sh-session","shellsession"],owner:"RunDevelopment"},smali:{title:"Smali",owner:"RunDevelopment"},smalltalk:{title:"Smalltalk",owner:"Golmote"},smarty:{title:"Smarty",require:"markup-templating",optional:"php",owner:"Golmote"},sml:{title:"SML",alias:"smlnj",aliasTitles:{smlnj:"SML/NJ"},owner:"RunDevelopment"},solidity:{title:"Solidity (Ethereum)",alias:"sol",require:"clike",owner:"glachaud"},"solution-file":{title:"Solution file",alias:"sln",owner:"RunDevelopment"},soy:{title:"Soy (Closure Template)",require:"markup-templating",owner:"Golmote"},sparql:{title:"SPARQL",require:"turtle",owner:"Triply-Dev",alias:"rq"},"splunk-spl":{title:"Splunk SPL",owner:"RunDevelopment"},sqf:{title:"SQF: Status Quo Function (Arma 3)",require:"clike",owner:"RunDevelopment"},sql:{title:"SQL",owner:"multipetros"},squirrel:{title:"Squirrel",require:"clike",owner:"RunDevelopment"},stan:{title:"Stan",owner:"RunDevelopment"},stata:{title:"Stata Ado",require:["mata","java","python"],owner:"RunDevelopment"},iecst:{title:"Structured Text (IEC 61131-3)",owner:"serhioromano"},stylus:{title:"Stylus",owner:"vkbansal"},supercollider:{title:"SuperCollider",alias:"sclang",owner:"RunDevelopment"},swift:{title:"Swift",owner:"chrischares"},systemd:{title:"Systemd configuration file",owner:"RunDevelopment"},"t4-templating":{title:"T4 templating",owner:"RunDevelopment"},"t4-cs":{title:"T4 Text Templates (C#)",require:["t4-templating","csharp"],alias:"t4",owner:"RunDevelopment"},"t4-vb":{title:"T4 Text Templates (VB)",require:["t4-templating","vbnet"],owner:"RunDevelopment"},tap:{title:"TAP",owner:"isaacs",require:"yaml"},tcl:{title:"Tcl",owner:"PeterChaplin"},tt2:{title:"Template Toolkit 2",require:["clike","markup-templating"],owner:"gflohr"},textile:{title:"Textile",require:"markup",optional:"css",owner:"Golmote"},toml:{title:"TOML",owner:"RunDevelopment"},tremor:{title:"Tremor",alias:["trickle","troy"],owner:"darach",aliasTitles:{trickle:"trickle",troy:"troy"}},turtle:{title:"Turtle",alias:"trig",aliasTitles:{trig:"TriG"},owner:"jakubklimek"},twig:{title:"Twig",require:"markup-templating",owner:"brandonkelly"},typescript:{title:"TypeScript",require:"javascript",optional:"js-templates",alias:"ts",owner:"vkbansal"},typoscript:{title:"TypoScript",alias:"tsconfig",aliasTitles:{tsconfig:"TSConfig"},owner:"dkern"},unrealscript:{title:"UnrealScript",alias:["uscript","uc"],owner:"RunDevelopment"},uorazor:{title:"UO Razor Script",owner:"jaseowns"},uri:{title:"URI",alias:"url",aliasTitles:{url:"URL"},owner:"RunDevelopment"},v:{title:"V",require:"clike",owner:"taggon"},vala:{title:"Vala",require:"clike",optional:"regex",owner:"TemplarVolk"},vbnet:{title:"VB.Net",require:"basic",owner:"Bigsby"},velocity:{title:"Velocity",require:"markup",owner:"Golmote"},verilog:{title:"Verilog",owner:"a-rey"},vhdl:{title:"VHDL",owner:"a-rey"},vim:{title:"vim",owner:"westonganger"},"visual-basic":{title:"Visual Basic",alias:["vb","vba"],aliasTitles:{vba:"VBA"},owner:"Golmote"},warpscript:{title:"WarpScript",owner:"RunDevelopment"},wasm:{title:"WebAssembly",owner:"Golmote"},"web-idl":{title:"Web IDL",alias:"webidl",owner:"RunDevelopment"},wgsl:{title:"WGSL",owner:"Dr4gonthree"},wiki:{title:"Wiki markup",require:"markup",owner:"Golmote"},wolfram:{title:"Wolfram language",alias:["mathematica","nb","wl"],aliasTitles:{mathematica:"Mathematica",nb:"Mathematica Notebook"},owner:"msollami"},wren:{title:"Wren",owner:"clsource"},xeora:{title:"Xeora",require:"markup",alias:"xeoracube",aliasTitles:{xeoracube:"XeoraCube"},owner:"freakmaxi"},"xml-doc":{title:"XML doc (.net)",require:"markup",modify:["csharp","fsharp","vbnet"],owner:"RunDevelopment"},xojo:{title:"Xojo (REALbasic)",owner:"Golmote"},xquery:{title:"XQuery",require:"markup",owner:"Golmote"},yaml:{title:"YAML",alias:"yml",owner:"hason"},yang:{title:"YANG",owner:"RunDevelopment"},zig:{title:"Zig",owner:"RunDevelopment"}},plugins:{meta:{path:"plugins/{id}/prism-{id}",link:"plugins/{id}/"},"line-highlight":{title:"Line Highlight",description:"Highlights specific lines and/or line ranges."},"line-numbers":{title:"Line Numbers",description:"Line number at the beginning of code lines.",owner:"kuba-kubula"},"show-invisibles":{title:"Show Invisibles",description:"Show hidden characters such as tabs and line breaks.",optional:["autolinker","data-uri-highlight"]},autolinker:{title:"Autolinker",description:"Converts URLs and emails in code to clickable links. Parses Markdown links in comments."},wpd:{title:"WebPlatform Docs",description:'Makes tokens link to WebPlatform.org documentation. The links open in a new tab.'},"custom-class":{title:"Custom Class",description:"This plugin allows you to prefix Prism's default classes (.comment can become .namespace--comment) or replace them with your defined ones (like .editor__comment). You can even add new classes.",owner:"dvkndn",noCSS:!0},"file-highlight":{title:"File Highlight",description:"Fetch external files and highlight them with Prism. Used on the Prism website itself.",noCSS:!0},"show-language":{title:"Show Language",description:"Display the highlighted language in code blocks (inline code does not show the label).",owner:"nauzilus",noCSS:!0,require:"toolbar"},"jsonp-highlight":{title:"JSONP Highlight",description:"Fetch content with JSONP and highlight some interesting content (e.g. GitHub/Gists or Bitbucket API).",noCSS:!0,owner:"nauzilus"},"highlight-keywords":{title:"Highlight Keywords",description:"Adds special CSS classes for each keyword for fine-grained highlighting.",owner:"vkbansal",noCSS:!0},"remove-initial-line-feed":{title:"Remove initial line feed",description:"Removes the initial line feed in code blocks.",owner:"Golmote",noCSS:!0},"inline-color":{title:"Inline color",description:"Adds a small inline preview for colors in style sheets.",require:"css-extras",owner:"RunDevelopment"},previewers:{title:"Previewers",description:"Previewers for angles, colors, gradients, easing and time.",require:"css-extras",owner:"Golmote"},autoloader:{title:"Autoloader",description:"Automatically loads the needed languages to highlight the code blocks.",owner:"Golmote",noCSS:!0},"keep-markup":{title:"Keep Markup",description:"Prevents custom markup from being dropped out during highlighting.",owner:"Golmote",optional:"normalize-whitespace",noCSS:!0},"command-line":{title:"Command Line",description:"Display a command line with a prompt and, optionally, the output/response from the commands.",owner:"chriswells0"},"unescaped-markup":{title:"Unescaped Markup",description:"Write markup without having to escape anything."},"normalize-whitespace":{title:"Normalize Whitespace",description:"Supports multiple operations to normalize whitespace in code blocks.",owner:"zeitgeist87",optional:"unescaped-markup",noCSS:!0},"data-uri-highlight":{title:"Data-URI Highlight",description:"Highlights data-URI contents.",owner:"Golmote",noCSS:!0},toolbar:{title:"Toolbar",description:"Attach a toolbar for plugins to easily register buttons on the top of a code block.",owner:"mAAdhaTTah"},"copy-to-clipboard":{title:"Copy to Clipboard Button",description:"Add a button that copies the code block to the clipboard when clicked.",owner:"mAAdhaTTah",require:"toolbar",noCSS:!0},"download-button":{title:"Download Button",description:"A button in the toolbar of a code block adding a convenient way to download a code file.",owner:"Golmote",require:"toolbar",noCSS:!0},"match-braces":{title:"Match braces",description:"Highlights matching braces.",owner:"RunDevelopment"},"diff-highlight":{title:"Diff Highlight",description:"Highlights the code inside diff blocks.",owner:"RunDevelopment",require:"diff"},"filter-highlight-all":{title:"Filter highlightAll",description:"Filters the elements the highlightAll and highlightAllUnder methods actually highlight.",owner:"RunDevelopment",noCSS:!0},treeview:{title:"Treeview",description:"A language with special styles to highlight file system tree structures.",owner:"Golmote"}}})},2885:(e,t,a)=>{const n=a(29901),o=a(39642),i=new Set;function l(e){void 0===e?e=Object.keys(n.languages).filter((e=>"meta"!=e)):Array.isArray(e)||(e=[e]);const t=[...i,...Object.keys(Prism.languages)];o(n,e,t).load((e=>{if(!(e in n.languages))return void(l.silent||console.warn("Language does not exist: "+e));const t="./prism-"+e;delete a.c[a(16500).resolve(t)],delete Prism.languages[e],a(16500)(t),i.add(e)}))}l.silent=!1,e.exports=l},6726:(e,t,a)=>{var n={"./":2885};function o(e){var t=i(e);return a(t)}function i(e){if(!a.o(n,e)){var t=new Error("Cannot find module '"+e+"'");throw t.code="MODULE_NOT_FOUND",t}return n[e]}o.keys=function(){return Object.keys(n)},o.resolve=i,e.exports=o,o.id=6726},16500:(e,t,a)=>{var n={"./":2885};function o(e){var t=i(e);return a(t)}function i(e){if(!a.o(n,e)){var t=new Error("Cannot find module '"+e+"'");throw t.code="MODULE_NOT_FOUND",t}return n[e]}o.keys=function(){return Object.keys(n)},o.resolve=i,e.exports=o,o.id=16500},39642:e=>{"use strict";var t=function(){var e=function(){};function t(e,t){Array.isArray(e)?e.forEach(t):null!=e&&t(e,0)}function a(e){for(var t={},a=0,n=e.length;a "));var r={},s=e[n];if(s){function c(t){if(!(t in e))throw new Error(n+" depends on an unknown component "+t);if(!(t in r))for(var l in o(t,i),r[t]=!0,a[t])r[l]=!0}t(s.require,c),t(s.optional,c),t(s.modify,c)}a[n]=r,i.pop()}}return function(e){var t=a[e];return t||(o(e,n),t=a[e]),t}}function o(e){for(var t in e)return!0;return!1}return function(i,l,r){var s=function(e){var t={};for(var a in e){var n=e[a];for(var o in n)if("meta"!=o){var i=n[o];t[o]="string"==typeof i?{title:i}:i}}return t}(i),c=function(e){var a;return function(n){if(n in e)return n;if(!a)for(var o in a={},e){var i=e[o];t(i&&i.alias,(function(t){if(t in a)throw new Error(t+" cannot be alias for both "+o+" and "+a[t]);if(t in e)throw new Error(t+" cannot be alias of "+o+" because it is a component.");a[t]=o}))}return a[n]||n}}(s);l=l.map(c),r=(r||[]).map(c);var d=a(l),u=a(r);l.forEach((function e(a){var n=s[a];t(n&&n.require,(function(t){t in u||(d[t]=!0,e(t))}))}));for(var g,p=n(s),f=d;o(f);){for(var b in g={},f){var m=s[b];t(m&&m.modify,(function(e){e in u&&(g[e]=!0)}))}for(var v in u)if(!(v in d))for(var h in p(v))if(h in d){g[v]=!0;break}for(var y in f=g)d[y]=!0}var _={getIds:function(){var e=[];return _.load((function(t){e.push(t)})),e},load:function(t,a){return function(t,a,n,o){var i=o?o.series:void 0,l=o?o.parallel:e,r={},s={};function c(e){if(e in r)return r[e];s[e]=!0;var o,d=[];for(var u in t(e))u in a&&d.push(u);if(0===d.length)o=n(e);else{var g=l(d.map((function(e){var t=c(e);return delete s[e],t})));i?o=i(g,(function(){return n(e)})):n(e)}return r[e]=o}for(var d in a)c(d);var u=[];for(var g in s)u.push(r[g]);return l(u)}(p,d,t,a)}};return _}}();e.exports=t},92703:(e,t,a)=>{"use strict";var n=a(50414);function o(){}function i(){}i.resetWarningCache=o,e.exports=function(){function e(e,t,a,o,i,l){if(l!==n){var r=new Error("Calling PropTypes validators directly is not supported by the `prop-types` package. Use PropTypes.checkPropTypes() to call them. Read more at http://fb.me/use-check-prop-types");throw r.name="Invariant Violation",r}}function t(){return e}e.isRequired=e;var a={array:e,bigint:e,bool:e,func:e,number:e,object:e,string:e,symbol:e,any:e,arrayOf:t,element:e,elementType:e,instanceOf:t,node:e,objectOf:t,oneOf:t,oneOfType:t,shape:t,exact:t,checkPropTypes:i,resetWarningCache:o};return a.PropTypes=a,a}},45697:(e,t,a)=>{e.exports=a(92703)()},50414:e=>{"use strict";e.exports="SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED"},64448:(e,t,a)=>{"use strict";var n=a(67294),o=a(27418),i=a(63840);function l(e){for(var t="https://reactjs.org/docs/error-decoder.html?invariant="+e,a=1;a

    =26xXL=llz`nH~9Wnbppa#=ha)`=Y9o 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 literal 0 HcmV?d00001 diff --git a/assets/images/blog-image-1-3-5b57e295568b12f4b213cb9fc3d39e81.png b/assets/images/blog-image-1-3-5b57e295568b12f4b213cb9fc3d39e81.png new file mode 100644 index 0000000000000000000000000000000000000000..2fbc0b38cacfbb2db1b530922c8029eb61d26eae GIT binary patch literal 53757 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 rCAeD39yhgrpD!|@=6MJ7wlHj!J#~U^E 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 QdEef;VgfU+G=e|;Cm0)qZ~y=R literal 0 HcmV?d00001 diff --git a/assets/images/blog-image-1-4-77065a4476c7a64b6b66d818a4c94a6c.png b/assets/images/blog-image-1-4-77065a4476c7a64b6b66d818a4c94a6c.png new file mode 100644 index 0000000000000000000000000000000000000000..b447e97788aaf0a733a1fe42ceac18e4a43c9761 GIT binary patch literal 68277 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 B7D503 literal 0 HcmV?d00001 diff --git a/assets/images/blog-image-1-5-efe95dbf3756c59403a30a3a9a2b0c7a.png b/assets/images/blog-image-1-5-efe95dbf3756c59403a30a3a9a2b0c7a.png new file mode 100644 index 0000000000000000000000000000000000000000..c170935a3bb3aff8f3621c9253d93a47b5ce74ab GIT binary patch literal 211808 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�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