Merge pull request #446 from Azure/serkar.AddCustomRbacEhSample
Adding RBAC custom role sample
This commit is contained in:
Коммит
d47a55f3fe
|
@ -15,7 +15,7 @@ namespace SampleEphReceiver
|
|||
private const string StorageContainerName = "Storage account container name";
|
||||
private const string StorageAccountName = "Storage account name";
|
||||
private const string StorageAccountKey = "Storage account key";
|
||||
|
||||
|
||||
private static readonly string StorageConnectionString = string.Format("DefaultEndpointsProtocol=https;AccountName={0};AccountKey={1}", StorageAccountName, StorageAccountKey);
|
||||
|
||||
public static void Main(string[] args)
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
<AssemblyName>CustomRole</AssemblyName>
|
||||
<OutputType>Exe</OutputType>
|
||||
<PackageId>CustomRole</PackageId>
|
||||
<RuntimeFrameworkVersion>2.1.0</RuntimeFrameworkVersion>
|
||||
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
|
||||
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
|
||||
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
|
||||
<LangVersion>latest</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Azure.EventHubs" Version="4.0.0" />
|
||||
<PackageReference Include="Microsoft.Azure.EventHubs.Processor" Version="4.0.0" />
|
||||
<PackageReference Include="Microsoft.Azure.Storage.Blob" Version="9.4.2" />
|
||||
<PackageReference Include="Microsoft.Identity.Client" Version="4.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -0,0 +1,25 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.26730.10
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CustomRole", "CustomRole.csproj", "{679DFCC5-76BD-4725-A51E-AFBB01565401}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{679DFCC5-76BD-4725-A51E-AFBB01565401}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{679DFCC5-76BD-4725-A51E-AFBB01565401}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{679DFCC5-76BD-4725-A51E-AFBB01565401}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{679DFCC5-76BD-4725-A51E-AFBB01565401}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {4190FE18-2881-43ED-9A45-6AECB8D3314A}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
|
@ -0,0 +1,95 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
namespace CustomRole
|
||||
{
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Azure.EventHubs;
|
||||
using Microsoft.Azure.EventHubs.Processor;
|
||||
using Microsoft.Identity.Client;
|
||||
using Microsoft.WindowsAzure.Storage;
|
||||
using Microsoft.WindowsAzure.Storage.Auth;
|
||||
|
||||
public class Program
|
||||
{
|
||||
private const string Authority = "Authority of tenant";
|
||||
private const string ClientId = "AAD app id";
|
||||
private const string ClientSecret = "AAD app secret";
|
||||
private const string EventHubNamespace = "FQDN of Event Hubs namespace";
|
||||
private const string EventHubName = "Event hub name";
|
||||
private const string StorageAccountName = "Storage account name";
|
||||
private const string StorageContainerName = "Storage account container name";
|
||||
|
||||
static IConfidentialClientApplication tokenClient;
|
||||
|
||||
public static async Task Main(string[] args)
|
||||
{
|
||||
// Use the same identity client for both Event Hubs and Storage
|
||||
tokenClient = ConfidentialClientApplicationBuilder.Create(ClientId)
|
||||
.WithAuthority(Authority)
|
||||
.WithClientSecret(ClientSecret)
|
||||
.Build();
|
||||
|
||||
// Create Storage client with access token provider
|
||||
var tokenAndFrequency = await TokenRenewerAsync(tokenClient, CancellationToken.None);
|
||||
var tokenCredential = new TokenCredential(tokenAndFrequency.Token,
|
||||
TokenRenewerAsync,
|
||||
tokenClient,
|
||||
tokenAndFrequency.Frequency.Value);
|
||||
var storageCredentials = new StorageCredentials(tokenCredential);
|
||||
CloudStorageAccount cloudStorageAccount = new CloudStorageAccount(storageCredentials, StorageAccountName, string.Empty, true);
|
||||
|
||||
// Create Event Hubs access token provider and processor host
|
||||
TokenProvider tp = TokenProvider.CreateAzureActiveDirectoryTokenProvider(
|
||||
new AzureActiveDirectoryTokenProvider.AuthenticationCallback(GetAccessTokenAsync), Authority, tokenClient);
|
||||
var eventProcessorHost = new EventProcessorHost(
|
||||
new Uri(EventHubNamespace),
|
||||
EventHubName,
|
||||
PartitionReceiver.DefaultConsumerGroupName,
|
||||
tp,
|
||||
cloudStorageAccount,
|
||||
StorageContainerName);
|
||||
|
||||
// Registers the Event Processor Host and starts receiving messages
|
||||
Console.WriteLine("Registering EventProcessor...");
|
||||
await eventProcessorHost.RegisterEventProcessorAsync<SimpleEventProcessor>();
|
||||
|
||||
Console.WriteLine("Receiving. Press enter key to stop worker.");
|
||||
Console.ReadLine();
|
||||
|
||||
// Disposes of the Event Processor Host
|
||||
await eventProcessorHost.UnregisterEventProcessorAsync();
|
||||
}
|
||||
|
||||
private static async Task<NewTokenAndFrequency> TokenRenewerAsync(Object state, CancellationToken cancellationToken)
|
||||
{
|
||||
var authResult = await AcquireTokenAsync("https://storage.azure.com/");
|
||||
|
||||
// Renew the token 5 minutes before it expires.
|
||||
var next = (authResult.ExpiresOn - DateTimeOffset.UtcNow) - TimeSpan.FromMinutes(5);
|
||||
if (next.Ticks < 0)
|
||||
{
|
||||
next = default(TimeSpan);
|
||||
Console.WriteLine("Renewing token...");
|
||||
}
|
||||
|
||||
// Return the new token and the next refresh time.
|
||||
return new NewTokenAndFrequency(authResult.AccessToken, next);
|
||||
}
|
||||
|
||||
static async Task<string> GetAccessTokenAsync(string audience, string authority, object state)
|
||||
{
|
||||
var authResult = await AcquireTokenAsync(audience);
|
||||
|
||||
return authResult.AccessToken;
|
||||
}
|
||||
|
||||
static async Task<AuthenticationResult> AcquireTokenAsync(string audience)
|
||||
{
|
||||
return await tokenClient
|
||||
.AcquireTokenForClient(new string[] { $"{audience}/.default" }).ExecuteAsync();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("CustomRole")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("679dfcc5-76bd-4725-a51e-afbb01565401")]
|
|
@ -0,0 +1,44 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
namespace CustomRole
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Azure.EventHubs;
|
||||
using Microsoft.Azure.EventHubs.Processor;
|
||||
|
||||
public class SimpleEventProcessor : IEventProcessor
|
||||
{
|
||||
public Task CloseAsync(PartitionContext context, CloseReason reason)
|
||||
{
|
||||
Console.WriteLine($"Processor Shutting Down. Partition '{context.PartitionId}', Reason: '{reason}'.");
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task OpenAsync(PartitionContext context)
|
||||
{
|
||||
Console.WriteLine($"SimpleEventProcessor initialized. Partition: '{context.PartitionId}'");
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task ProcessErrorAsync(PartitionContext context, Exception error)
|
||||
{
|
||||
Console.WriteLine($"Error on Partition: {context.PartitionId}, Error: {error.Message}");
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task ProcessEventsAsync(PartitionContext context, IEnumerable<EventData> messages)
|
||||
{
|
||||
foreach (var eventData in messages)
|
||||
{
|
||||
var data = Encoding.UTF8.GetString(eventData.Body.Array, eventData.Body.Offset, eventData.Body.Count);
|
||||
Console.WriteLine($"Message received. Partition: '{context.PartitionId}', Data: '{data}', Partition Key: '{eventData.SystemProperties.PartitionKey}'");
|
||||
}
|
||||
|
||||
return context.CheckpointAsync();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
# Receive events with the Event Processor Host in .NET Standard with a custom role which grants Listen claim for Event Hubs and blob claims for Storage accounts.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
* [Microsoft Visual Studio 2015 or 2017](http://www.visualstudio.com).
|
||||
* [.NET Core Visual Studio 2015 or 2017 tools](http://www.microsoft.com/net/core).
|
||||
* An Azure subscription.
|
||||
* [An event hub namespace and an event hub](event-hubs-quickstart-namespace-portal.md).
|
||||
* An Azure Storage account.
|
||||
|
||||
## Run the sample
|
||||
|
||||
To run the sample, follow these steps:
|
||||
|
||||
1. Clone or download this GitHub repo.
|
||||
2. [Create an Event Hubs namespace and an event hub](https://docs.microsoft.com/azure/event-hubs/event-hubs-create).
|
||||
3. Create a Storage account to host a blob container, needed for lease management by the Event Processor Host.
|
||||
3. [Create a new custom role](https://docs.microsoft.com/en-us/azure/role-based-access-control/custom-roles) with the definition below.
|
||||
4. [Create a new AAD (Azure Active Directory) application](https://docs.microsoft.com/en-us/azure/active-directory/develop/howto-create-service-principal-portal).
|
||||
5. Assign AAD application to both Event Hubs namespace and Storage account with the custom role you just created.
|
||||
|
||||
```
|
||||
{
|
||||
"Name": "Custom role for RBAC sample",
|
||||
"Id": "8ddab47f-cf99-4b04-8fc3-1d2d857fb931",
|
||||
"Description": "Test role",
|
||||
"IsCustom": true,
|
||||
"Actions": [
|
||||
"Microsoft.Storage/*"
|
||||
],
|
||||
"NotActions": [],
|
||||
"DataActions": [
|
||||
"Microsoft.EventHub/namespaces/messages/receive/action",
|
||||
"Microsoft.Storage/*"
|
||||
],
|
||||
"NotDataActions": [],
|
||||
"AssignableScopes": [
|
||||
"/subscriptions/your-subscription-id"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
6. Update the sample with Event Hubs namespace and Storage account name.
|
||||
7. Run [Sender application](https://github.com/Azure/azure-event-hubs/tree/serkar.AddCustomRbacEhSample/samples/DotNet/Microsoft.Azure.EventHubs/SampleSender) to send some number of messages to your event hub.
|
||||
8. Run the CustomRole sample to receive those events back.
|
Загрузка…
Ссылка в новой задаче