Is it possible to connect to a Service Bus Topic using App Service Function App's MSI? Any documentations or examples to perform this via ARM Template or .NET SDK (C#) would be helpful.
See example below:
namespaces:
using Microsoft.Azure.ServiceBus;
using Microsoft.Azure.ServiceBus.Primitives;
Adding services to the container:
services.Configure<ServiceBusConfiguration>(Configuration.GetSection("ServiceBus"));
Main implementation:
public class ServiceBusConfiguration
{
public string Namespace { get; set; }
public string Topic { get; set; }
}
public class HomeController : Controller
{
public ServiceBusConfiguration Config { get; }
public HomeController(IOptions<ServiceBusConfiguration> serviceBusConfig)
{
Config = serviceBusConfig.Value;
}
[HttpPost]
public async Task<ActionResult> Send(ServiceBusMessageData messageInfo)
{
if (string.IsNullOrEmpty(messageInfo.MessageToSend))
{
return RedirectToAction("Index");
}
var tokenProvider = TokenProvider.CreateManagedServiceIdentityTokenProvider();
var sendClient = new TopicClient($"sb://{Config.Namespace}.servicebus.windows.net/", Config.Topic, tokenProvider);
await sendClient.SendAsync(new Message(Encoding.UTF8.GetBytes(messageInfo.MessageToSend)));
await sendClient.CloseAsync();
return RedirectToAction("Index");
}
}
To get more details please see Use Service Bus from App Service with Managed Service Identity
Related
I am using .net core for expose API. When I call api from postman, some method not hitting, get 404 not found error message.
[HttpPost]
public async Task<bool> AddLogs([FromBody]List<LogModel> model)
{
var result = false;
foreach (var item in model)
{
result = await _logService.Insert("Logs", item);
}
return result;
}
public class LogModel: TableEntity
{
public int Status { get; set; }
public bool IsBreak { get; set; }
public string Location { get; set; }
public DateTime StartDateAndTime { get; set; }
public DateTime EndDateAndTime { get; set; }
public string Remarks { get; set; }
public int Id { get; set; }
}
When I call the api 'AddLogs' , get not found error message.
But when try ,
[HttpPost]
public async Task<bool> Post()
{
return true;
}
It will return the true value.
But I noted that when I call in localhost 'AddLogs' api working fine. It will hit the api. But When I publish in azure, it shows me not found.
I test in my site and it works well.
The reason for this is that the deployment or default ASP.NET Core Web API template does not include a default document in the root directory of the web site. For example, index.htm, defualt.aspx, default.htm are default documents and IIS will deliver them if there is no specific file provided when accessing the URL.
You could set [HttpPost("AddLogs/")] to specify the AddLogs action if you have several httppost method. Remember also add the following code in Configure method.
app.UseMvc(routes =>
{
routes.MapRoute(name: "default", template: "api/{controller}/{action}/{id?}");
});
Is there a way to configure my Azure Cloud storage in ConfigureServices of the Startup class, the same way I configure my DatabaseContext?
var connection = Configuration.GetConnectionString("myDatabaseConnectionString");
services.AddDbContext<DatabaseContext>
(options => options.UseSqlServer(connection));
I know I could access the connection string from within a controller by adding the IConfiguration instance to the service collection as a singleton object in ConfigureServices and then inject IConfiguration in the controller.
But how do I access this in the following class or is this a bad practice and there is a better way (e.g. in the services.Add ...) ?
public class ClassifiedsToProcess
{
public static void AddMessage(string message)
{
var storageConnectionString = Configuration.Get("AppSettings:ConnectionString");
storageAccount = CloudStorageAccount.Parse(storageConnectionString);
...
}
}
Thanks !
Here is how I solved my problem using IOptions as suggested by Martin Brandl
Controller code
private string makeJson;
private MyAzureQueue MyAzureQueue;
public AdminController(IOptions<AzureOptions> optionsAccessor)
{
MyAzureQueue = new MyAzureQueue(optionsAccessor);
}
public IActionResult Index()
{
MyAzureQueue.AddMessage("my message");
return View();
}
MyAzureQueue class
public interface IMessageRepository
{
void AddMessage(string message);
}
public class MyAzureQueue: IMessageRepository
{
private readonly CloudQueue _queue;
public ClassifiedsToProcess(IOptions<AzureOptions> optionsAccessor)
{
CloudStorageAccount storageAccount = AzureStorageHelper.getStorageAccount(optionsAccessor.Value.StorageConnectionString);
_queue = AzureStorageHelper.GetQueue(storageAccount, "mystorageaccount");
}
public void AddMessage(string message)
{
CloudQueueMessage cloudQueueMessage = new CloudQueueMessage(message);
_queue.AddMessageAsync(cloudQueueMessage);
}
}
Azure Options class
public class AzureOptions
{
public AzureOptions()
{
}
public string StorageConnectionString { get; set; }
}
In the Startup.cs ConfigureServices method I added the following two lines
services.AddOptions();
services.Configure<AzureOptions>(Configuration.GetSection("Azure"));
And this is what my appsettings.json looks like
{
"Azure": {
"StorageConnectionString": "DefaultEndpointsProtocol=https;AccountName=mystorageaccount;AccountKey=XXXXXXXXXXXXXXXXXXXXX;EndpointSuffix=core.windows.net"
}
}
You should try to avoid using IConfiguration directly and use the Option pattern in
ASP.NET Core instead.
In your example, I would e. g. implement an IMessageRepository which uses the storage account to persist the messages.
Your ClassifiedsToProcess class would have a constructor dependency to it and your AddMessage method would used it (it shouldn't be static since you want to access the repository).
Is there a way to specifically create an Application Insights using Azure Fluent API?
I see there is a Monitor code sample, but this is not specific to Application Insights.
EDIT:
After trying the Azure SDK API from here, I got an error not identified in their documentation.
Currently Fluent API does not support for Application Insights.
It appears the .NET Fluent Azure libraries do not support Application
Insight provisioning while the Java SDK does
Same with C# too.
According to Azure SDK API, there is no Application Insight management API currently.
But we could create the Application Insights with following API. For more information, please refer to this.
You could test it directly with [try it].
PUT https://management.azure.com/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Insights/components/{resourceName}?api-version=2015-05-01
This is an old topic, but apparently Application Insights still isn't part of the Azure Management API yet. I created a class for this, which works a bit like the Fluent API would.
public class AppInsights
{
public IAzureRestClient azureRestClient { get; private set; }
public AppInsights(IAzureRestClient client)
{
this.azureRestClient = client;
}
public Dictionary<string, string> Tags { get; private set; }
public AppInsights WithTags(Dictionary<string, string> tags)
{
this.Tags = tags;
return this;
}
public string Name { get; private set; }
public AppInsights WithName(string name)
{
this.Name = name;
return this;
}
public string SubscriptionId { get; private set; }
public AppInsights WithSubscriptionId(string id)
{
this.SubscriptionId = id;
return this;
}
public IResourceGroup ResourceGroup { get; private set; }
public AppInsights WithResourceGroup(IResourceGroup group)
{
this.ResourceGroup = group;
return this;
}
public string AssociatedApp { get; private set; }
public AppInsights WithAssociatedApp(string appName)
{
this.AssociatedApp = appName;
return this;
}
public AppInsightsComponent Create()
{
// initialize tags
var tags = new Dictionary<string, string>(Tags);
if (!string.IsNullOrWhiteSpace(AssociatedApp))
{
// Add a tag to associate the App Insights instance with the app service
// ARM syntax for this: "[concat('hidden-link:', resourceGroup().id, '/providers/Microsoft.Web/sites/', parameters('webSiteName'))]": "Resource",
tags.Add($"hidden-link:{ResourceGroup.Id}/providers/Microsoft.Web/sites/{Name}-portal", "Resource");
}
// Create the resource
var url = $"https://management.azure.com/subscriptions/{SubscriptionId}/resourceGroups/{ResourceGroup.Name}/providers/Microsoft.Insights/components/{Name}?api-version=2015-05-01";
dynamic reqBody = new JObject();
reqBody.Location = ResourceGroup.RegionName;
reqBody.Kind = "web";
reqBody.tags = JObject.FromObject(tags);
dynamic properties = new JObject();
properties.ApplicationType = "web";
properties.FlowType = "Bluefield";
properties.RequestSource = "rest";
reqBody.Properties = properties;
var response = azureRestClient.Put(url, reqBody);
if (!response.IsSuccessStatusCode)
{
throw new HttpRequestException($"management.azure.com returned {response.StatusCode.ToString()}");
}
var responseBody = response.Content.ReadAsStringAsync().Result;
return Newtonsoft.Json.JsonConvert.DeserializeObject<AppInsightsComponent>(responseBody);
}
There's more context on this github page.
PFB my code.
namespace ManualCSharpe
{
public class MyServices : Service
{
[Route("/L/hello/")] //RequestDTO one
public class HelloL
{
public string Name { get; set; }
}
[Route("/H/hello/")] //RequestDTO two
public class HelloH
{
public string Name1 { get; set; }
}
public class HelloResponse //ResponseDTO
{
public string Result { get; set; }
}
public class HelloServiceL : Service //Service One
{
public object Get(HelloL request)
{
return new HelloResponse { Result = "Low" };
}
}
public class HelloServiceH : Service //Service
{
public object Get(HelloH request)
{
return new HelloResponse { Result = "High" };
}
}
//Define the Web Services AppHost
public class AppHost : AppSelfHostBase
{
public AppHost()
: base("HttpListener Self-Host",new Assembly[] {typeof(HelloServiceL).Assembly, typeof(HelloServiceH).Assembly}) { }
public override void Configure(Funq.Container container) { }
}
//Run it!
static void Main(string[] args)
{
var listeningOn = args.Length == 0 ? "http://*:133/" : args[0];
var appHost = new AppHost()
.Init()
.Start(listeningOn);
Console.WriteLine("AppHost Created at {0}, listening on {1}",
DateTime.Now, listeningOn);
Console.ReadKey();
}
}
}
When I am tring to added two service then it is show below exception.
An unhandled exception of type 'System.Reflection.AmbiguousMatchException' occurred in ServiceStack.dll
Additional information: Could not register Request 'ManualCSharpe.MyServices+HelloL' with service 'ManualCSharpe.MyServices+HelloServiceL' as it has already been assigned to another service.
Each Request DTO can only be handled by 1 service.
I have below douts.
Here I have created two different DTO for Two Service. Why it is showing error like Each Request DTO can only be handled by 1 service. In simple word, Two route mapped with two DTO with two Service.
Can I create one route for multiple RequestDTO with multiple service? In Simple word, One Route/L/hello/ can be mapped with two DTO HelloL and HelloH.
You can't have Service class implementations nested inside another outer MyServices class:
public class MyServices : Service
{
[Route("/L/hello/")] //RequestDTO one
public class HelloL
{
public string Name { get; set; }
}
[Route("/H/hello/")] //RequestDTO two
public class HelloH
{
public string Name1 { get; set; }
}
public class HelloResponse //ResponseDTO
{
public string Result { get; set; }
}
public class HelloServiceL : Service //Service One
{
public object Get(HelloL request)
{
return new HelloResponse { Result = "Low" };
}
}
public class HelloServiceH : Service //Service
{
public object Get(HelloH request)
{
return new HelloResponse { Result = "High" };
}
}
}
Remove the outer MyServices class completely and just have the DTO's and Service classes directly under a C# namespace.
Also routes shouldn't end with a / suffix, so I'd change:
[Route("/L/hello/")]
to:
[Route("/L/hello")]
#mythz answer is correct for OP but I came here looking for an answer for a different situation which the cause was not particularly obvious - you will get this exception if you attempt to register the same assembly twice, for example, if you move a service implementation into the same assembly and were pulling it in like so:
public AppHost() : base("App", typeof(AdminService).GetAssembly(), typeof(InboundService).GetAssembly(),typeof(ProductService).GetAssembly())
For those of you who come here from a google search, a AmbiguousMatchException exception in ServiceStack can sometimes be triggered within ServiceStack but handled internally.
You can change your exception setting so it doesn't break on this exception.
I had changed my exception setting to break on all exceptions and this had me stuck for a while.
I was following the instructions on http://leastprivilege.com/2013/11/11/client-certificate-authentication-middleware-for-katana/ but also followed Diminic's Pluralishight video on Web API security as I was trying to apply a client certificate authentication on my self hosted Web API v2 project.
I call the service from Advanced REST Client Chrome extension app, meaning it does not contain a client certificate in the request, and I see that cert == null but after that I still get a valid response from the server.
Is there something missing from this tutorial code?
public class ClientCertificateAuthenticationOptions : AuthenticationOptions
{
public X509CertificateValidator Validator { get; set; }
public bool CreateExtendedClaimSet { get; set; }
public ClientCertificateAuthenticationOptions() : base(“X.509″)
{
Validator = X509CertificateValidator.ChainTrust;
CreateExtendedClaimSet = false;
}
}
public class ClientCertificateAuthenticationHandler :
AuthenticationHandler<ClientCertificateAuthenticationOptions>
{
protected override Task<AuthenticationTicket> AuthenticateCoreAsync()
{
var cert = Context.Get<X509Certificate2>(“ssl.ClientCertificate”);
if (cert == null)
{
return Task.FromResult<AuthenticationTicket>(null);
}
try
{
Options.Validator.Validate(cert);
}
catch
{
return Task.FromResult<AuthenticationTicket>(null);
}
var claims = GetClaimsFromCertificate(
cert, cert.Issuer, Options.CreateExtendedClaimSet);
var identity = new ClaimsIdentity(Options.AuthenticationType);
identity.AddClaims(claims);
var ticket = new AuthenticationTicket(
identity, new AuthenticationProperties());
return Task.FromResult<AuthenticationTicket>(ticket);
}
}
public class ClientCertificateAuthenticationMiddleware :
AuthenticationMiddleware<ClientCertificateAuthenticationOptions>
{
public ClientCertificateAuthenticationMiddleware(
OwinMiddleware next,
ClientCertificateAuthenticationOptions options)
: base(next, options)
{ }
protected override AuthenticationHandler<ClientCertificateAuthenticationOptions> CreateHandler()
{
return new ClientCertificateAuthenticationHandler();
}
}
app.UseClientCertificateAuthentication();
app.UseWebApi(WebApiConfig.Register());
Did you decorate your ApiController with the Authorize attribute?
[Authorize]
public class MyWebApiController : ApiController
{
}
Otherwise, the status code of your request will be 200 and not 401 even if the username/password do not match.