The Event Hub client library will provide a new API surface for track two, which differs from that of the current track one version. To help illustrate usage of the new API for the target scenarios, a set of small, approachable, and self-contained sample projects is needed.
Implement a sample demonstrating a basic approach for creating an EventHubClient using an Azure identity as the credential and read basic metadata from the Event Hub to illustrate the client working.
The sample should offer discussion and illustration of any needed Azure resources or configuration, such as creating a service principal and configuring RBAC on the Event Hub instance.
The sample has been created for the scenario, with a clear and simple approach and generous comments to support the goal of helping those unfamiliar with Event Hubs to get started in using it.
The sample adheres to the coding conventions and standards defined for track two libraries.
All projects in the solution compile; any warnings that exist have either been fixed or additional work items filed to address them if they were deemed non-critical.
Hello @jsquire :) I wonder if I could help with this?
Hello @jsquire :) I wonder if I could help with this?
Hi @albertodenatale:
Thank you for your willingness to contribute. We'd love for you to help out! I'd be happy to be your point of contact for any questions that come up around the Event Hubs and Identity code or contributing process. Feel free to comment here, or reach out via email (my GitHub alias @ microsoft.com) or Twitter DM (@Jesse_Squire).
The Azure.Identity library is undergoing one final API review this week, but should be at a stable point by the end of next week. It probably makes sense to take that timing into account to avoid any churn.
A good place to get started would be to read through our Contributing Guide, especially towards the end where we detail how to set up your development environment and run the suite of tests. Once you've got the test passing, you may want to look over our existing samples as they use a common structure.
As you work through things, feel free to touch base as you'd like to discuss things or if you have questions. When the time comes, @kinelski and I will be the folks that you'll want to include on any PRs.
Thank you, again, for you willingness to contribute. We look forward to collaborating with you.
_(cc: @AlexGhiondea)_
@jsquire thank you very much for your kind introduction, it is my utter pleasure having the opportunity of contributing and collaborating with you.
Hello @jsquire @kinelski
Hope you are well and wish you a good start of the week.
I forked the repo and got the tests up and running.
Some questions:
UPDATE - I am attaching here the link to what I would propose. Essentially I would modify the interface to take a factory as input.
UPDATE - I managed to get a token back. I attached this name/value in the posted form as suggested here:
response_type=token&grant_type=client_credentials&client_id=<myclientid>&client_secret=<myclientsecret>&scope=<myclientscope>/.default
UPDATE - The right scope would have had to be "https://eventhubs.azure.net/.default", created a pull request about this issue: https://github.com/Azure/azure-sdk-for-net/pull/8321/files
On the same page they say:
The /.default scope can be used in any OAuth 2.0 flow, but is necessary in the On-Behalf-Of flow and client credentials flow.
Think is everything from me now, thank you again and I look forward to your answer.
Alberto
Apologies for the delayed reply; All good questions, and not an easy answer. With respect to the credential type, I don't have a strong preference. I think we're best off with using the approach that requires the least amount of setup in terms of Azure resources. The idea, I think, would be using the implicit identity for the local user - but I'm not sure how far along that story is overall.
The IEventHubsSample allows us to hook the sample in the console runner and within the nightly test runs for the package. Since the tests run in a dynamically created scope, they currently need to be able to accept a connection string to run in that context. This is, by necessity, the first time that we've got to think outside of that bubble and we'll have to think through how to make that more robust. It would be a simple tweak to expose the composite parts of the Event Hubs scope, its the identity part that concerns me. The challenge will be somehow working the identity in while also keeping things simple and straightforward so that the sample doesn't become unapproachable for folks nor require a ton of hidden infrastructure.
The factory is a good first cut, but I think that is an important detail for the sample to show for folks trying to understand how to work with this. Moving creation into the factory would be making it less clear, which it probably not something we want to do. I wonder if we should think about using different interfaces for EventHubsSample and EventHubsIdentitySample where the parameters vary. That would allow us to keep injecting the information needed and showing how to construct the client. The thing that I don't have a good idea on, at the moment, is how to represent the identity in a way that is as self-contained as possible. I'm going to loop back and talk to the identity folks for some inspiration. Any thoughts that you have would be welcome also!
Thinking about this a bit more, talking to some folks, and looking at the Azure Identity ReadMe, I think that it probably makes the most sense to demonstrate a service principal with the ClientSecretCredential.
I think a good first step there would be to try and figure out how we could automate creation of the principal and assignment of the Azure Event Hubs Data Owner role for it. We currently have a simple ARM template to bootstrap the examples and samples experience in our README. When last I attempted to do so, creating service principals and assigning roles wasn't something that an ARM template could automate. We ended up going with PowerShell to setup the principal for our test suite. I believe that we'll want to figure out if we can incorporate this principal in the ARM template or if we should have another script to setup resources for the more advanced samples that would be using Azure.Identity.
Once we have an idea there, I think that my thoughts around extending our scope and creating another classification of sample will work out. I still need to let some of those thoughts firm up, though.
Thank you for your answer, no worries for the time 👍
ClientSecretCredential: got it, thank you
I see your point with IEventHubsSample, we want to move code complexity away from the samples.
I love the name EventHubsIdentitySample. It might not scale optimally if one day we decided to add another type of TokenCredential. CertificateCredential would ask for a certificate thumbprint instead of the client secret. Of course, this event might never occur.
The Program will need a rewrite too as it might ask different information depending on the interface implemented from the sample of choice. Wonder if, following on your idea, creating an entirely new project like Azure.Messaging.EventHubs.Samples.Identity might be another viable option?
For the ARM template/PowerShell script, I will start looking into it and I will come back to you with what I will find. In the meantime, thank you very much for your insights.
I managed to find a way to set instruct the creation of an identity. That would happen by adding the "identity" element:
"identity": {
"type": "SystemAssigned"
}
It requires the apiVersion element to be set to "2017-04-01".
Doing that, will cause a principal to be created and may be retrieved using Get-AzADServicePrincipal. For instance in my scenario:
ServicePrincipalNames : {83e4a673-e673-4a82-9b4b-bbfa9326e2be, https://identity.azure.net/7R3Z2ceefDrasWK0T5GnaIFM5D3VMji0S5SyOmMslEk=}
ApplicationId : 83e4a673-e673-4a82-9b4b-bbfa9326e2be
ObjectType : ServicePrincipal
DisplayName : azuresdk
Id : b0a0c84e-62d9-444e-a90b-ecb3521161cd
Type :
Still looking for a way to output/set the principal's secret and also to set its role.
An idea we could look into could be using an ARM template to deploy an Azure Function (with managed system provided identity set to on) and "Azure Event Hubs Data Owner" role to access an eventhub. The code could be pulled from a sample GitHub repo, similarly to what done here and might be editable on the fly from an App Service Editor?
We would show ManagedIdentityCredential in this scenario.
I love the name
EventHubsIdentitySample. It might not scale optimally if one day we decided to add another type ofTokenCredential.CertificateCredentialwould ask for a certificate thumbprint instead of the client secret. Of course, this event might never occur.
That is certainly a fair point. I definitely haven't thought through all of the scenarios, so just rough brainstorming at the moment, but once we get to that level of complexity, I don't know that our attempts at a unified format really would hold up. Having a prompt in the runner application that asks the user to provide a bunch of different things that are only used on a sample or two starts to become a burden and makes things confusing.
At that point, we may need to consider an approach that offers more flexibility around allowing samples that don't conform to a small known set of parameters. What are your thoughts on having something something like EventHubsAdvancedSample (instead of EventHubsIdentitySample) that would allow it to define prompts that it needs and, potentially, parsing. Rough idea:
public interface IEventHubsAdvancedSample
{
public string Name { get; }
public string Description { get; }
public IEnumerable<string> ArgumentPrompts { get; }
public Task RunAsync(IReadOnlyDictionary<string, string> arguments);
}
The obvious limitations with this is that developers interested in the sample now have a much less clear idea of is actually needed to get it runningdoing any kind of custom validation isn't really possible without adding a bunch of complexity and the sample would have to be able to parse the arguments into something meaningful. We could, of course, hide all that in the RunAsync method and have a concrete method signature where the actual sample is demonstrated which is called by RunAsync.
I'm on the fence, honestly; it feels like we're starting down the doomed path of trying to write an ultra-flexible attribute system for unknown inputs, and that always ends up as a complex mess of disaster. Maybe it would be better to just identify the advanced sample with a name/description interface and then call RunAsync without arguments and let the sample be responsible for prompting/collecting it's own needs.
Thoughts?
_(side note: That would also require an explicit test with knowledge of each advanced sample for nightly smoke tests, but I don't see that as a negative.)_
I managed to find a way to set instruct the creation of an identity. That would happen by adding the "identity" element:
Still looking for a way to output/set the principal's secret and also to set its role.
If it makes you feel any better, you're about two steps ahead of where I got the last time I probed it. I wouldn't spin your wheels on that too much if it doesn't look like it's possible. I'm open to having our advanced samples deviate from the "one-click README setup" and bootstrap via a script. The only concern that I'd have there is that our bootstrap script for live tests and the one for advanced samples should have the same basis - both PowerShell or both Azure CLI. I don't have a preference there; I went with PowerShell, but I've no objections if you wanted to convert the existing one.
An idea we could look into could be using an ARM template to deploy an Azure Function (with managed system provided identity set to on) and "Azure Event Hubs Data Owner" role to access an eventhub. The code could be pulled from a sample GitHub repo, similarly to what done here and might be editable on the fly from an App Service Editor?
We would show
ManagedIdentityCredentialin this scenario.
That's a solid idea for some of the more advanced scenarios and does offer tighter control over the execution environment. That may be an avenue for things like MSI which we really can't do locally. My concern there is that it makes it more difficult for a developer interested in exploring the sample to set breakpoints and step through to explore.
I also wonder if we're crossing over into the responsibility of the Identity client library samples at that point; Event Hubs usage is the same for any TokenCredential. At the same time, there is a benefit to having locality for people interested in Event Hubs who just want to run it in a managed environment. I should probably chat with the Identity SDK folks about what their approach for some of these would be.
I'm on the fence, honestly; it feels like we're starting down the doomed path of trying to write an ultra-flexible attribute system for unknown inputs, and that always ends up as a complex mess of disaster. Maybe it would be better to just identify the advanced sample with a name/description interface and then call
RunAsyncwithout arguments and let the sample be responsible for prompting/collecting it's own needs.Thoughts?
Thank you very much for your answers.
I think you have got a point. Keeping things simple will probably make it for us here.
Shall I set up some draft PRs for this scenario and we will take it from there?
Shall I set up some draft PRs for this scenario and we will take it from there?
Sounds like a great idea to me. I just want to be careful about potentially running you around in circles. If you hit the point where you don't believe that you have a clear path forward or are missing context about some of the surrounding infrastructure, please let me know. I'm happy to try and help before you invest a lot of time on something.
I think you have a great point and definitely I have been moving a bit from one idea to another and I beg your pardon for the many messages/notifications you must have received.
I think we may have something that could work and I really look forward to having your opinion on it.
In a nutshell:
// Infrastructure
public interface INamedSample
{
public string Name { get; }
public string Description { get; }
}
// Infrastructure
public interface IEventHubsSample: INamedSample
{
public Task RunAsync(string connectionString,
string eventHubName);
}
// Infrastructure/Advanced
public interface ISample13_AuthenticateWithClientSecretCredential: INamedSample
{
public Task RunAsync(string fullyQualifiedNamespace,
string eventHubName,
string tenantId,
string clientId,
string secret);
}
// Program.cs
if (sample is IEventHubsSample eventHubsSample)
{
PromptConnectionString(parsedArgs);
PromptEventHubName(parsedArgs);
await eventHubsSample.RunAsync(parsedArgs.ConnectionString, parsedArgs.EventHub);
}
else if (sample is ISample13_AuthenticateWithClientSecretCredential sample13)
{
PromptEventHubName(parsedArgs);
PromptClientId(parsedArgs);
PromptTenantId(parsedArgs);
PromptFullyQualifiedNamespace(parsedArgs);
PromptSecret(parsedArgs);
await sample13.RunAsync(parsedArgs.FullyQualifiedNamespace,
parsedArgs.EventHub,
parsedArgs.TenantId,
parsedArgs.ClientId,
parsedArgs.Secret);
}
Thanks, @albertodenatale! I'll take a look at the PR first thing tomorrow, but wanted to leave a couple high-level thoughts here to keep them centralized.
I've been stewing on some of my suggestions over the weekend and came to the conclusion that I'm undervaluing simplicity and overvaluing trying to keep a single automated execution. Given the goal of having these samples be clear, straightforward for developers to understand, and with minimal hidden infrastructure, I think that we should pull back a bit. I'd rather see us have some duplication and split between simple samples and scenarios with advanced needs. In this case, let's call it the Identity samples for now.
I'm thinking something like:
// This is the current contract, for illustration. No changes are being
// proposed here.
//
public interface IEventHubsSample
{
public string Name { get; }
public string Description { get; }
public Task RunAsync(string connectionString, string eventHubName);
}
// These may be extended over time should we have a certificate path or some other needs specific to
// general-purpose identity.
///
public interface IEventHubsIdentitySample
{
public string Name { get; }
public string Description { get; }
public Task RunAsync(string fullyQualifiedNamespace, string eventHubName, string tenantId, string applicationId, string clientSecret);
}
IEventHubsSample, as it does today, the main focus is the simple samples.IEventHubsIdentitySample.It's probably the same amount of plumbing, albeit with some duplication, but I think that would help to keep things straightforward and avoid hiding complexity. It will also give us some flexibility around splitting out the identity samples into a different project if we decide that is a better twist to take.
I'd be very interested in your thoughts.
Thank you @jsquire. I think your design is awesome.
Adding a different shell experience together with using a common interface IEventHubsIdentitySample for identity samples is very nice.
- We remove the command-line options from the sample runner; since it's interactive, its probably better to simply prompt for them.
I wonder if that may become a burden to the developer who wanted to try them all?
Thank you very much
@jsquire I closed the pull request for the minute
@jsquire I closed the pull request for the minute
Apologies for the churn, and I appreciate your willingness to iterate. Truth be told, we're thinking through the details together on this; I didn't have a fully-baked plan going in _(... and probably still don't.)_
- We remove the command-line options from the sample runner; since it's interactive, its probably better to simply prompt for them.
I wonder if that may become a burden to the developer who wanted to try them all?
An excellent point that I'm not entirely sure how to reconcile, honestly. That said, I'll lay out my thoughts and ask you to poke holes in things or call out what you see differently, if you're willing.
What happens today is that you can either pass the "standard" information (connection string, Event Hub name) via command line or the interactive shell will ask for them as the first thing it does when you start running. Once it has them - whether as command line args or interactive values - it will display the list of available samples and you may then run any without being prompted.
What I'm thinking about for the identity enhancement is having the shell start by prompting you for the "standard" information as it does today if you don't pass via command line. Again, it'll be cached and can be used to execute one or more of the basic samples. Should you choose to run the identity samples, you'll be prompted for the identity information (tenant, application, secret) ... and things will follow the same pattern.
The downside is that if you are only interested in the identity sample, you're going to be prompted for the basic connection information too, even though you won't use it, and then you'll have the menu/list and then another set of prompts for what you actually care about. It's not ideal, but it will probably serve the 80% case for folks interested in running the samples. I see the identity samples being something people are interested in as they are more comfortable with the client library and looking to build production-level solutions. At that point, there is probably less interest in seeing the sample execute and quite a bit more in being able to examine the working code to see how it was done.
_However, I could be looking at that wrong. Your feedback would be most welcome._
A couple of alternate thoughts:
Lazy Parameter Checks: The current approach is to eagerly collect information upfront with the assumption that you'll need it, sooner or later, to run a sample. With the new category, maybe that no longer holds. Perhaps we should consider only prompting for what is needed by a sample if it isn't already known when you ask to run it.
One wrinkle is that allowing any/all to be specified via the command line could potentially introduce a bunch of arguments that don't give you a really good idea of what goes together or which is needed by a specific category of examples. Of course, we could start doing full-on CLI parsing and make subgroups, but that would seem to ramp up complexity for something we intend to be simple.
Separate Executables: Maybe it would be better to create a second project that is stand-alone for the identity samples and keep things organized by "simple, getting started" and "more advanced, identity samples" - and continue to add things as we potentially introduce more advanced categories.
On the upside here, the interfaces and other components are then clearly organized by what uses them. Likewise, the entry point has one clear set of arguments that apply to it again, so we could potentially keep the existing pattern. What I'm not clear on is whether that would potentially confuse people to see samples split that way.
_Any thoughts on where you think things would be most approachable and offer the simplest experience? Other ideas that haven't occurred to me?_
Apologies for the churn, and I appreciate your willingness to iterate. Truth be told, we're thinking through the details together on this; I didn't have a fully-baked plan going in _(... and probably still don't.)_
With this task, I am as much committed as your are in providing Microsoft with something that fits the high standards that it sets for itself and I love iterations :)
Any thoughts on where you think things would be most approachable and offer the simplest experience? Other ideas that haven't occurred to me?
I believe what really makes it or breaks is the quantity and the quality of the samples. The more use case scenarios are covered the better will be.
Here are some ideas:
Debugging Exceptions: One important scenario is when a developer is trying to figuring out something, like for instance how to fill the clientId. They might get some exceptions back from the program and they might not want to pass through the interactive experience on every attempt.
Project.json: That said, another idea could be the project.json/secret.json?
{
"EventHub":{
"ConnectionString":""
},
"Identity":{
}
}
One program per project: Another option could be, in a solution folder, having one project/Program.cs per sample?
Lazily vs Eagerly: I think the idea of prompting parameters lazily is better compared to its eager counterpart
Pull Request: I re-opened the PR with the latest. How do you prefer us to organise? Would you rather have a final PR at the end or is it ok if I am sending things as we go along?
Thank you very much, look forward to having your feedback.
PowerShell script
// identity-tests-azure-setup.ps1
//
[CmdletBinding(DefaultParameterSetName="Help")]
[OutputType([String])]
param
(
[Parameter(Mandatory=$true, ParameterSetName="Help", Position=0)]
[Switch] $Help,
[Parameter(Mandatory=$true, ParameterSetName="Execute", Position=0)]
[ValidateNotNullOrEmpty()]
[string] $SubscriptionName,
[Parameter(Mandatory=$true, ParameterSetName="Execute")]
[ValidateNotNullOrEmpty()]
[string] $ResourceGroupName,
[Parameter(Mandatory=$true, ParameterSetName="Execute")]
[ValidateNotNullOrEmpty()]
[ValidateScript({ $_.Length -ge 6})]
[string] $ServicePrincipalName
)
The script would assign the role of Azure Event Hubs Data Owner to the resource group that is created by the live-tests-azure-setup.ps1 script (and that would be passed in to the script using the variable $ResourceGroupName).
I wonder how you would see reading it from environment variables instead?
- One program per project: Another option could be, in a solution folder, having one project/Program.cs per sample?
Funny you mention that; there has been a good deal of discussion on potentially normalizing on that pattern across samples for the repository. At present, Event Hubs is the outlier in that we're not relying on tests + test infrastructure and environment variables for samples. Not sure where that may go.
Debugging Exceptions: One important scenario is when a developer is trying to figuring out something, like for instance how to fill the clientId. They might get some exceptions back from the program and they might not want to pass through the interactive experience on every attempt.
Good call. That definitely justifies the command line scenario at minimum, or potentially a config file as you're suggesting which may be even a better option. "Give us a config file with these values filled in or we'll prompt for them" - which would allow a single command line parameter and simplify things. I really like that. That is something that I'm likely to pick up at some point.
The script would assign the role of
Azure Event Hubs Data Ownerto the resource group that is created by thelive-tests-azure-setup.ps1script (and that would be passed in to the script using the variable$ResourceGroupName).I wonder how you would see reading it from environment variables instead?
I would see this being combined with the existing live test script to make the resource group owner that creates the hub be assigned the Azure Event Hubs Data Owner on the Event Hubs namespace that it creates. That still leaves us with the question of how we allow developers to quickly and easily build out the environment for their samples. Based on your confirmation around ARM, I'm guessing that our best bet will probably be to say "use the ARM template for the basic samples and run this PowerShell script for the advanced ones, like identity."
Not sure that I'm following the environment variable thought, though.
Thank you very much for your great PR points, I will work on them soon.
I will send you the PowerShell script in a separate PR, I wonder if you reckon that for the live test suite we will need a principal with the Azure Event Hubs Data Owner or such principal is only accessory to the ARM template?
The tests might just work using the existing Contributor principal?
- One program per project: Another option could be, in a solution folder, having one project/Program.cs per sample?
I enjoy throwing ideas :D
In that scenario the Console App that we are currently work on might sit in another project and reference all the project/Program.cs?
I will send you the PowerShell script in a separate PR, I wonder if you reckon that for the live test suite we will need a principal with the
Azure Event Hubs Data Owneror such principal is only accessory to the ARM template?The tests might just work using the existing
Contributorprincipal?
As I understand it, we'll need to pull in the Data Owner role assignment; there was a concerted effort to split the responsibilities between "those who can use the resource" and "those who can manage the resource" as part of the recent RBAC overhaul for the Azure services, including Event Hubs.
That said, I haven't actually tried it since the changes were released, so.... ¯\_(ツ)_/¯
In that scenario the Console App that we are currently work on might sit in another project and reference all the project/Program.cs?
While it's mostly a focus on the client libraries (to keep from bloating dependencies), we tend to use shared source (added via link into a project) over moving common things into a library.
Though I don't think there would be a strong resistance to a shared library where samples are concerned, we would probably do something like creating a folder for containing shared source at the root of the samples directory and then linking it into each sample project - which would let each be built independently and contained within a single executable without additional dependencies.
Something like:
root/sdk/eventhub/Azure.Messaging.EventHubs
|-- samples
| -- SharedSource
| -- Program
| -- IEventHubsSample
| -- IEventHubsIdentitySample
| -- (...)
| -- Sample01_HelloWorld
| -- Sample01_HelloWorld.csproj
| -- SharedSource (folder+contents, included by link)
| -- Program.cs (contains the sample in the RunAsync method as well as inherits from the shared entry point)
I enjoy throwing ideas :D
As do I. This collaboration has been fun. :)
I enjoy throwing ideas :D
As do I. This collaboration has been fun. :)
Yes! It's been amazing, I actually can't believe this is real.
I found a tiny bug when trying to send data to EventHubs using Azure.Identity. I will try fixing it and I will send you the PR.
I found a tiny bug when trying to send data to EventHubs using
Azure.Identity. I will try fixing it and I will send you the PR.
I can't tell you how much this is appreciated; these aren't paths that have been well tested due to the parallel development with Identity and prioritization. Your help in just making sure that things work end-to-end past the very basics is fantastic. Thank you.
I can't tell you how much this is appreciated; these aren't paths that have been well tested due to the parallel development with Identity and prioritization. Your help in just making sure that things work end-to-end past the very basics is fantastic. Thank you.
Thank you, it is a great opportunity for growth and to give my humble contribution to a cause I as well believe in:
Our mission is to empower every person and organization on the planet to achieve more.
I noticed the launchSettings.json is not in the .gitignore wonder if you want me to do anything with this?

I noticed the
launchSettings.jsonis not in the.gitignorewonder if you want me to do anything with this?
I wouldn't do that here, as it will bubble to the entire repository and is something that our engineering team owns. I'll take a note and propose a change so that we don't bog down with another 5+ people pulled into the review as code owners for that file.
Fantastic, thank you again. Shall we cover other sample scenarios?
Like, using managed identites and user principals?
Fantastic, thank you again. Shall we cover other sample scenarios?
Like, using managed identites and user principals?
We probably should, in time. If you've got some ideas on how to do that within the bounds of the new infrastructure, I'd love to hear them. We could probably at least cover the default identity credential and the environment identity credential without too much of a stretch.
I'm not sure that I want to see us extend a bunch of infrastructure and complicate Azure resource creation to demonstrate the other types if we can't, somehow, drive them off of what we've got. _(and I don't have much inspiration there, off the top of my head.)_ That said, I'm willing to bet that the Microsoft Docs folks would probably love a longer tutorial-type sample if that's something that you're interested in. I'd be happy to reach out on your behalf to my contact if you decide that you'd like to go in that direction.
To be honest, I think the biggest win right now would be to take advantage of all of this lovely infrastructure that you just built and extend the Live tests for each area with an Identity-based test or three to hit the major scenarios. That's been on my wish list forever, we just haven't had the means until now. I've been tracking on the side and don't have an official issue tracking it just yet.
extend the Live tests for each area with an Identity-based test or three to hit the major scenarios
Definitely something I could help with and would actually be useful for me. Your vision is like, having:
AuthenticationMethods = { ClientSecretCredential, DefaultAzureCredential, EnvironmentCredential }
SampleCategory = { HelloWorld, ClientCustomOptions, PublishAnEvent, ... }
Ultimately having 33 samples each being a combination { AuthenticationMethod, SampleCategory }
That said, I'm willing to bet that the Microsoft Docs folks would probably love a longer tutorial-type sample if that's something that you're interested in. I'd be happy to reach out on your behalf to my contact if you decide that you'd like to go in that direction.
Would absolutely love to help with that.
Most helpful comment
With this task, I am as much committed as your are in providing Microsoft with something that fits the high standards that it sets for itself and I love iterations :)