<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://mattward.net/feed.xml" rel="self" type="application/atom+xml" /><link href="https://mattward.net/" rel="alternate" type="text/html" /><updated>2024-08-07T17:31:26+08:00</updated><id>https://mattward.net/feed.xml</id><title type="html">mattward.net</title><author><name>Matt Ward</name></author><entry><title type="html">Enabling CitizenOps for Integration Workloads with Azure AI Services</title><link href="https://mattward.net/2024/08/07/citizenops.html" rel="alternate" type="text/html" title="Enabling CitizenOps for Integration Workloads with Azure AI Services" /><published>2024-08-07T00:00:00+08:00</published><updated>2024-08-07T00:00:00+08:00</updated><id>https://mattward.net/2024/08/07/citizenops</id><content type="html" xml:base="https://mattward.net/2024/08/07/citizenops.html"><![CDATA[<p>We build a lot of integration solutions. These solutions focus on moving data from one system to another. Often this is transitory in nature, an integration is about the movement of data, rather than about a user making a change to one particular system. However, what happens when an integration fails?</p>

<p><img src="/assets/images/citizen-ops/integration.png" alt="System integration" /></p>

<p>A failed integration usually does not visibly disrupt a system, but it likely does leave your overall organisation in more subtle state of error. How do we become aware that a failed integration has happened? How do we investigate and remedy the situation?</p>

<p>Today we would usually build a very technical integration dashboard. An operations engineer would use the dashboard to dig into the issue, understand what had caused the failure, and then pass that information on to a business user where necessary.</p>

<p><img src="/assets/images/citizen-ops/integration-dashboard.png" alt="Integration dashboard" /></p>

<h2 id="can-we-do-better">Can we do better?</h2>

<p>Very often when an integration fails, it is a data problem. The receiving system may have different validation requirements on a field compared to the sending system, and what was acceptable to one system is not acceptable to the other. In my experience data isses are for more common than more technical issues, such as networking or authenitcation errors.</p>

<p>Given that these errors are often better resolved by a business user than a technical person, I wondered if there is there a way to surface them in a more suitable fashion.</p>

<p>I envisioned a solution where a business user could ask a question such as “Why didn’t employee Bob Smith make it to System X?” and receive an answer that was actionable for them.</p>

<p><img src="/assets/images/citizen-ops/the-goal.png" alt="The goal" /></p>

<p>Using Azure AI Services, Azure Integration Services, and a (very) simple web application, we can achieve this vision!</p>

<h2 id="so-how-does-it-work">So how does it work?</h2>

<p>The implementation uses the popular Retrieval Augmented Generation (RAG) pattern, utilising the Azure AI Search and Azure OpenAI services. This pattern is widely used in many “chat with your own data” style applications.</p>

<p>In this pattern, a company’s data is initially loaded into a vector database, which is a kind of database that excels at searching for things that are ‘similar’. Then, when a user’s query is received, a database lookup is performed to look for similar content, and then the user’s query and the database response is fed into a ChatGPT-style Generative AI to create a user-friendly response.</p>

<p><img src="/assets/images/citizen-ops/rag.png" alt="RAG pattern" /></p>

<p>We generally build integrations using Azure Logic Apps. The Logic App team recently announced new connectors for Azure AI Search and Azure OpenAI. This opened the door to easily extending current integration implementations for our customers, without needing to build entirely new AI solutions.</p>

<p>I created a new, reusable ingestion workflow. The workflow receives a small payload containing the request and response information from an attempted system integration, as well as some accompanying metadata that provides context to help improve the quality of the results. This new workflow leveraged Azure OpenAI to ‘vectorise’ the payload, and Azure AI Search to store the payload and its vector.</p>

<p><img src="/assets/images/citizen-ops/ingestion-flow.png" alt="Ingestion flow" /></p>

<p>This workflow can then easily be called from any existing Logic App integrations.</p>

<p>Below is a common integration pattern where a workflow is picking up a pub/sub style message (in this instance from an Azure Service Bus Topic) and attempting to send the message to an operational system. The only addition needed to this workflow to enable this solution is the final action to call the new ingestion workflow.</p>

<p><img src="/assets/images/citizen-ops/calling-flow.png" alt="Calling flow" /></p>

<p>The other half of the solution is the retrieval workload. This flow takes the user’s question, vectorises it, retrieves data from Azure AI Search, then passes it all in to Azure OpenAI to generate that user-friendly content. This flow did not need to be built using a Logic App, but here I wanted to test out the new connectors.</p>

<p><img src="/assets/images/citizen-ops/retrieval-flow.png" alt="Retrieval flow" /></p>

<p>The retrieval flow can then be called from the simple web app shown above, enable citizen users to investigate integration issues!</p>

<hr />

<p>I find it interesting to explore the new approaches to building solutions that have arrived with AI services, especially being able to leverage so much out-of-the-box with Azure AI Services.</p>]]></content><author><name>Matt Ward</name></author><category term="genai" /><category term="logicapps" /><summary type="html"><![CDATA[We build a lot of integration solutions. These solutions focus on moving data from one system to another. Often this is transitory in nature, an integration is about the movement of data, rather than about a user making a change to one particular system. However, what happens when an integration fails?]]></summary></entry><entry><title type="html">Exploring the new Generative AI capabilities in Azure API Management</title><link href="https://mattward.net/2024/07/24/apim-genai.html" rel="alternate" type="text/html" title="Exploring the new Generative AI capabilities in Azure API Management" /><published>2024-07-24T00:00:00+08:00</published><updated>2024-07-24T00:00:00+08:00</updated><id>https://mattward.net/2024/07/24/apim-genai</id><content type="html" xml:base="https://mattward.net/2024/07/24/apim-genai.html"><![CDATA[<p>As companies progress their Generative AI workloads towards a Production environment, they need to start considering how they can apply aspects of modern cloud governance to these workloads, such as security, cost optimisation, and observability.</p>

<p>Placing Azure API Management (APIM) in front of an Open AI service was already a good idea - It provides a centralised point to manage your Azure OpenAI credentials without having to distriute them across your organisation, and it provides load-balancing capabilities to manage traffic across different models, regions, or pricing models.</p>

<p><img src="/assets/images/apim-genai/arch.png" alt="Architecture" /></p>

<p>Recently, Microsoft <a href="https://techcommunity.microsoft.com/t5/azure-integration-services-blog/introducing-genai-gateway-capabilities-in-azure-api-management/ba-p/4146525">announced some new Generative AI capabilities</a> in Azure API Management that make the service even more useful for Generative AI workloads.</p>

<p>Pricing in Azure OpenAI is fundamentally constructed around ‘tokens’. Every request in and response out of an Azure Open AI service has a token count, and the more tokens you consume, the more you have to pay.</p>

<p>Out of the box Azure OpenAI lets you set a token-per-minute limit for a model deployment and provides metrics on token consumption - however, you cannot drill down on these metrics to see either per-user, or per-application details. This lack of ability to drill down can make it hard to understand how your workloads are consuming Generative AI, and hard to enable sensible controls.</p>

<p>The chart below shows some sample traffic to an Azure OpenAI service over a 30 minute period. We can see overall token consumption, but not much else.</p>

<p><img src="/assets/images/apim-genai/azure-monitor.png" alt="Out of the box" /></p>

<p>The first new Generative AI capability in Azure APIM is simply enhanced metrics. You can now easily view token usage at either the user or the application level.</p>

<p>Looking at the chart below, we can now drill down and see service usage per user. In this example, we can now see that over half of our OpenAI usage can be attributed to one user.</p>

<p><img src="/assets/images/apim-genai/custom-metrics.png" alt="Custom metrics" /></p>

<p>Taking this to the next level, there is now the ability to apply rate-limiting, also at either the user or the application level. This can help ensure that your total tokens assigned to your model are being shared around, and not being entirely consumed by one hungry user.</p>

<p>In this chart, the Azure APIM service is enacting a per-user rate limit of 1000 tokens/minute. We can see that the user with the largest usage has had their consumption limited, while the other users consumption levels are unaffected.</p>

<p><img src="/assets/images/apim-genai/rate-limiting.png" alt="Rate limiting" /></p>

<p>The next capability is a little more advanced, but fundamentally involves the ability to cache common requests in Azure API Management to avoid needing to use the Open AI service as much.</p>

<p>This may initially sound counter-intuitive for GenAI, as a lot of our individual experiences to date have been blank-page open-world style discussions across wide ranging topics with ChatGPT, but I think it is more relevant when you consider the types of Generative AI use cases that organisations generally implement.</p>

<p>Consider, for example, a public facing chatbot on a company’s website. The chatbot allows a customer to search over the company’s data, using the popular Retrieval Augmented Generation (RAG) pattern. This pattern takes a user’s request, uses the request to look up relevant data in a database, and then passes the user’s request and the database result into OpenAI to generate a user-friendly response.</p>

<p>If you have a lot of customers making very similar requests, such as store locations and opening times, or event information such as start times, then this style of caching is perfect. The capability even allows a ‘similarity threshold’, enabling you to fine-tune what is and isn’t considered a similar request.</p>

<p>The chart below shows the overall traffic with caching enabled, and we can see that the total token usage within the OpenAI service is now only half of what it originally was.</p>

<p><img src="/assets/images/apim-genai/caching.png" alt="Caching" /></p>

<p>This caching comes with some warnings tho - firstly it requires the use of an Azure Redis Enterprise cache, which is not a cheap resource, and secondly it uses your OpenAI service as part of the caching process, so could possibly increase your overall cost.</p>

<hr />

<p>Overall these new Generative AI capabilities in Azure API Management make it much easier for organisations to govern their usage of Azure OpenAI. The new metrics and rate limiting capabilities allow you to control access per user or per application, and with the right use case the caching capability could allow you to heavily reduce your token consumption.</p>]]></content><author><name>Matt Ward</name></author><category term="genai" /><category term="apim" /><summary type="html"><![CDATA[As companies progress their Generative AI workloads towards a Production environment, they need to start considering how they can apply aspects of modern cloud governance to these workloads, such as security, cost optimisation, and observability.]]></summary></entry><entry><title type="html">High-density hosting on Azure App Service</title><link href="https://mattward.net/2023/02/01/high-density-app-service.html" rel="alternate" type="text/html" title="High-density hosting on Azure App Service" /><published>2023-02-01T00:00:00+08:00</published><updated>2023-02-01T00:00:00+08:00</updated><id>https://mattward.net/2023/02/01/high-density-app-service</id><content type="html" xml:base="https://mattward.net/2023/02/01/high-density-app-service.html"><![CDATA[<p>I have long been a fan of Azure App Service as a compute option for server-side generated web sites and APIs. You get that Heroku-style ease of use, not having to  worry about containerisation, not having to worry about cold starts, on top of the usual serverless benefits like not having to worry about servers.</p>

<p>But, as with most things in architecture, there are trade-offs. App Service has somewhat of a trade-off around cost optimisation. For established workloads that are worthy of their own (or multiple) compute units, the trade-off disappears, but for new, small things, App Service can be an expensive option.</p>

<p>Microsoft somewhat alleviates this by letting you group multiple services together. The scale unit for an App Service is called an App Service Plan. An App Service Plan is the thing you pay for, and once you have created it you can deploy many different App Services to it. You can even deploy your Function Apps there side-by-side with App Services, to stop you needing to pay for both App Service Plans and Function App Plans.</p>

<p>So what might a typical lifecycle look like? Initially we have our App Service Plan hosting an App Service or two, but what happens as we grow?</p>

<blockquote>
  <p>When you create an app in App Service, it is put into an App Service plan. When the app runs, it runs on all the VM instances configured in the App Service plan. If multiple apps are in the same App Service plan, they all share the same VM instances.</p>
</blockquote>

<p>We aren’t going to be able to keep adding App Services to our App Service Plan for ever, sooner or later it is going to start getting full - i.e. run out of memory or CPU. So what are our options?</p>

<p><img src="/assets/images/high-density/scale_options.png" alt="Scale Options" /></p>

<p>We could scale out if we are hitting CPU limits and just need to balance the traffic across some extra nodes, but this is unlikely to help us if we are hitting memory limits.</p>

<p>We could scale up, but this is just kicking the problem down the road. App Service Plans come in a few sizes (i.e. Small, Medium, Large), but there is a limit and we can’t scale up forever.</p>

<p>We could create a second App Service Plan, and start putting new services on there, but this creates a whole bunch of other problems that we’d prefer to avoid. How to decide which App Services go on which App Service Plan? We could just put the new ones on the new App Service Plan, but wouldn’t we prefer to scale certain services together, etc. Then there are other concerns such as virtual network integration… wouldn’t it be much nicer if we could stay within the one App Service Plan?</p>

<p>Then there is one final option, something Microsoft call <a href="https://learn.microsoft.com/en-us/azure/app-service/manage-scale-per-app">“High-density hosting on Azure App Service using per-app scaling”</a>!</p>

<p>It is somewhat buried away in the docs (red flag!), not mentioned at all in the Microsoft Learn AZ-204 training (red flag!), and not actually configurable via the portal, only Powershell/ARM (red flag!), but I decided to try it out anyways.</p>

<p>The general idea is that when enabled, an App Service no longer runs on all the instances configured in the App Service Plan, instead running on the number of instances that you configure that particular App Service to run on.</p>

<p><img src="/assets/images/high-density/general_idea.png" alt="The General Idea" /></p>

<p>Sounds straightforward, right? Well, I decided to try it out and see if it does what it says on the box.</p>

<h2 id="experiment-1---creation">Experiment #1 - Creation</h2>

<p>The docs provide an example powershell statement on how to create an App Service Plan with Per Site Scaling.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>New-AzAppServicePlan -ResourceGroupName $ResourceGroup 
                            -Name $AppServicePlan
                            -Location $Location
                            -Tier Premium 
                            -WorkerSize Small
                            -NumberofWorkers 2 
                            -PerSiteScaling $true
</code></pre></div></div>

<p>The number of instances for an App Service is again controlled by a NumberOfWorkers setting. The default appears to be 1, which suits me just fine for this experiment.</p>

<p>I started created App Services, observing the instance they were being deployed to, expecting them to be evenly spread across the two instances.</p>

<p>The first App Service went to a certain instance. Then the second App Service went to the same instance (hmm). Then the third App Service went to the same instance (hmmmmm). Then the fourth, then the fifth, and so on.</p>

<p><img src="/assets/images/high-density/experiment1-creation.png" alt="Experiment 1 - Expects vs Actual" /></p>

<p>Looking at the App Service Plan in Azure Monitor showed that Azure had only created one instance in the App Service Plan, despite the number of workers being set to two.</p>

<p>Something was clearly wrong.</p>

<p>Digging back into the docs, there is a section at the bottom labelled <a href="https://learn.microsoft.com/en-us/azure/app-service/manage-scale-per-app#recommended-configuration-for-high-density-hosting">“Recommended configuration for high-density hosting”</a> that states</p>

<blockquote>
  <p>Follow these steps to configure high-density hosting for your apps:</p>

  <ol>
    <li>Designate an App Service plan as the high-density plan and scale it out to the desired capacity.</li>
    <li>Set the PerSiteScaling flag to true on the App Service plan.</li>
  </ol>
</blockquote>

<p>Following these steps did lead to having all instances activated, and App Services were then spread evenly across instances. However, needing to know the desired capacity up front is a pretty big turn-off, I don’t think that that is how this cloud thing is meant to work at all! How would it handle scaling out my App Service Plan? Time to find out!</p>

<h2 id="experiment-2---scaling-out">Experiment #2 - Scaling Out</h2>

<p>Next I wanted to see if scaling-out an App Service Plan would result in the existing App Services being rebalanced across all instances.</p>

<p>Confidence was low after Experiment #1, but the following line in the <a href="https://learn.microsoft.com/en-us/azure/app-service/manage-scale-per-app">docs</a> gave me hope -</p>

<blockquote>
  <p>Applications are rebalanced only when instances are added or removed from the App Service plan.</p>
</blockquote>

<p>I started with an App Service Plan with two instances, each hosting three App Services. I then scaled out the App Service Plan to three instances, hoping that an App Service from each of the previously existing instances would be moved over to the new instance.</p>

<p><img src="/assets/images/high-density/experiment2-scale_out.png" alt="Experiment 2 - Expects vs Actual" /></p>

<p>What happened? Absolutely nothing. The services stayed exactly where they were.</p>

<hr />

<p>I did find ways to get my services to rebalance, but these were beyond what I feel I can reasonably ask of a team.</p>

<p>Conceptually, I think this capability could further increase the surface area where App Service is suitable as a compute option. However due to the manner in which it has been implemented, at least for my use cases, that potential value is not able to be utilised.</p>]]></content><author><name>Matt Ward</name></author><category term="AppService" /><category term="serverless" /><category term="compute" /><summary type="html"><![CDATA[I have long been a fan of Azure App Service as a compute option for server-side generated web sites and APIs. You get that Heroku-style ease of use, not having to worry about containerisation, not having to worry about cold starts, on top of the usual serverless benefits like not having to worry about servers.]]></summary></entry><entry><title type="html">Taking Azure AD B2C ‘Seamless Migration’ for a spin</title><link href="https://mattward.net/2022/02/14/b2c-seamless.html" rel="alternate" type="text/html" title="Taking Azure AD B2C ‘Seamless Migration’ for a spin" /><published>2022-02-14T00:00:00+08:00</published><updated>2022-02-14T00:00:00+08:00</updated><id>https://mattward.net/2022/02/14/b2c-seamless</id><content type="html" xml:base="https://mattward.net/2022/02/14/b2c-seamless.html"><![CDATA[<p>For many organisations, cloud is not simply heading off to fresh pastures, but instead entails complex migrations out of ‘on-prem’ data centres.</p>

<p>A core part of this migration, especially for customer-facing organisations, is moving users. Good customer experience demands a frictionless approach, a-la ‘Seamless Migration’, but migrating user passwords that you (hopefully!) do not have in plain text, or do not know the hashing algorithm for, presents a challenge.</p>

<h2 id="azure-ad-b2c">Azure AD B2C</h2>

<p><a href="https://docs.microsoft.com/en-us/azure/active-directory-b2c/overview">Azure AD B2C</a> is Azure’s Identity as a Service (IDaaS) customer identity access management service.</p>

<p>AD B2C uses standards-based authentication protocols including OpenID Connect and OAuth 2.0. By serving as the central authentication authority for your web applications, mobile apps, and APIs, Azure AD B2C enables you to build a single sign-on (SSO) solution for them all.</p>

<p><img src="/assets/images/seamless-migration/azureadb2c-overview.png" alt="AD B2C Overview" title="AD B2C Overview" /></p>

<p>Its competitors in the IDaaS space are Okta, probably the leader in the space (at least according to Gartner), but more expensive, and Ping. <a href="https://www.okta.com/resources/whitepaper/not-all-identity-clouds-are-created-equal/">This Okta blog post</a>, comparing themselves to Azure AD B2C is insightful, but should of course be read with a grain of salt.</p>

<p>There are a variety of open-source options in the self-hosted space, including Duende IdentityServer which is popular in the .NET ecosystem.</p>

<p>Importantly however, a lot of cloud migrations have core principles of utilising managed services, and of leveraging as much of their chosen cloud vendor as possible, so for anyone leaning towards Azure it makes sense to start by evaluating AD B2C.</p>

<h2 id="seamless-migration">Seamless Migration</h2>

<p>The AD B2C docs does have <a href="https://docs.microsoft.com/en-us/azure/active-directory-b2c/user-migration">a page</a> on migrating users to AD B2C which discusses Seamless Migration.</p>

<p>There are two main components - first, an initial ‘pre-migration’ of all user accounts into AD B2C is performed. This is followed by creating a callback to your existing identity solution to validate a user’s password.</p>

<p>Then, using an AD B2C ‘custom policy’, each time a user logs in a call is triggered to the legacy system. If it returns that the password is correct then AD B2C stores the password, marks the user as migrated, and no longer needs to callback for that user.</p>

<p>If your legacy solution is not accessible via an API call then you are out of luck.</p>

<p><img src="/assets/images/seamless-migration/seamless-migration.png" alt="Diagram Illustrating Seamless Migration" title="AD B2C Seamless Migration" /></p>

<p>For me, the existence of this page in the offical documentation initially implied that Seamless Migration was ‘built-in’ functionality. However the docs eventually link you to <a href="https://github.com/azure-ad-b2c/user-migration/tree/master/seamless-account-migration">a GitHub repo</a> which has the rather ominous disclaimer.</p>

<p>“The migration application is developed and managed by the open-source community in GitHub. The application is not part of Azure AD B2C product and it’s not supported under any Microsoft standard support program or service. This migration app is provided AS IS without warranty of any kind.”</p>

<h2 id="lets-try-it-out">Let’s try it out</h2>

<p>With the exception of being slightly out of date, the repository readme does an excellent job of guiding you through what you need to do to perform Seamless Migration.</p>

<p>In order to track whether a user has been migrated, a <a href="https://docs.microsoft.com/en-us/azure/active-directory-b2c/user-flow-custom-attributes">custom attribute</a> is created in Azure AD B2C. It is great that there exists the ability to create custom attributes, however it would be really helpful to be able to see the value of a custom attribute for a user from within the portal, and to be able to sort/filter all users on a custom attribute. I ended up creating my own management web app to display this value.</p>

<p><img src="/assets/images/seamless-migration/custom-attribute.png" alt="Custom Attribue" title="AD B2C Custom Attribute" /></p>

<p>The pre-migration step was relatively straightforward, utilising the user-friendly Microsoft Graph APIs to create a bunch of users. The name of the custom attribute used when accessing it programatically is a bit odd, but not a big deal.</p>

<p>The biggest challenge came with Azure AD B2C <a href="https://docs.microsoft.com/en-us/azure/active-directory-b2c/custom-policy-overview_">Custom Policies</a>. I understand that adding extensibility points within products is hard, but this felt like customising a service that was not designed for customisation.</p>

<p>B2C is explicit that you are entering ‘identity pro’ land, but I am not convinced that slapping a warning on something gets you out of delivering a good product experience! This admission is tellingly called out in the above-mentioned Okta blog post.</p>

<p><img src="/assets/images/seamless-migration/custom-policies.png" alt="Custom Policies Warning" title="AD B2C Custom Policies" /></p>

<p>There is a large amount of boilerplate point-and-click to enable custom policies in B2C, outside of authoring the policies themselves. There is a <a href="https://b2ciefsetupapp.azurewebsites.net/">community app</a> to automate this process, but it would be great to see this process automated within B2C itself.</p>

<p>Likewise, there is a (somewhat out of date) <a href="https://github.com/Azure-Samples/active-directory-b2c-custom-policy-starterpack">community repo</a> full of custom policy samples, but no canonical ‘starting point’, which I believe would be useful. Taking the sample policies and updating them according to the readme results in policies that fail validation, which is not great.</p>

<p>Authoring the policies was challenging. The policies are written in xml, and while there are <a href="https://docs.microsoft.com/en-us/azure/active-directory-b2c/trustframeworkpolicy_">fairly strong docs</a>, a lot of the terms used are not particularly user-friendly. Being honest, I would say I more cut-and-pasted sections from blogs/tutorials/samples than actually wrote much policy.</p>

<p>Lastly, uploading policies triggers a validation process, that in the case of failure (i.e. quite often) resulted in strangely formatted error messages.</p>

<p><img src="/assets/images/seamless-migration/custom-policies-validation-error.png" alt="Custom Policies Validatiom Error" title="AD B2C Custom Policies Validation Error" /></p>

<p>Combining determination and my knowledge of the domain eventually resulted in policies that both passed validation and worked as desired! I wouldn’t say I deeply understand how they are working - I may return and figure it out, I may not.</p>

<hr />

<p>In summary, I achieved what I was looking to achieve, but spent a little longer to do so than I would have liked. I don’t particularly value time spent learning how to configure SaaS tools, and feel Azure AD B2C would benefit from a little more investment in the customer experience for some of these more advanced scenarios.</p>]]></content><author><name>Matt Ward</name></author><category term="IDaaS" /><category term="b2c" /><category term="Identity" /><category term="Auth" /><summary type="html"><![CDATA[For many organisations, cloud is not simply heading off to fresh pastures, but instead entails complex migrations out of ‘on-prem’ data centres.]]></summary></entry></feed>