I would like to write a web app that allows users to write C# scripts and execute them using Azure Functions.
I checked Azure SDK documentation and didn't find any nuget packages for managing Function Apps.
Is there a way I can:
retrieve list of available azure functions
deploy azure function
update azure function
delete azure function
using Azure SDK? If not, what would be another way to do it?
Update on Jul 8, 2019
I found List and Delete functions in Azure SDK (Microsoft.Azure.Management.WebSites):
List:
Msdn Documentation
Delete
Msdn Documentation
I tested them and they work.
The problem is with Create method _CreateFunctionWithHttpMessagesAsync (Msdn Documentation)
It is not clear which parameters should be passed for it to work.
Currently I call it like this:
var response = await webClient.WebApps.CreateFunctionWithHttpMessagesAsync(ResourceGroupName, FunctionAppName, functionName, new FunctionEnvelope());
In about 10-20s it returns error:
"Microsoft.Azure.Management.WebSites.Models.DefaultErrorResponseException : Operation returned an invalid status code 'InternalServerError'"
I think it is related to empty FunctionEnvelope. I tried passing various values, but none of them worked.
AFAIK there is no SDK's available. But there are REST API's which let you perform all the above operations.
List Functions
Delete Function
For updating and Deployment you can make use of the zip deployment for Azure Functions.
Generate the FunctionApp.zip using the msbuild command pointing to your csproj->
/p:DeployOnBuild=true /p:DeployTarget=Package;CreatePackageOnPublish=true
The above will generate a zip file which can be used in the later part.
Now 2nd step is to obtain the Publish credentials using this api, if you get the response it will in the below class format
public class GetPublishCredentials
{
public string Id { get; set; }
public string Name { get; set; }
public string Type { get; set; }
public string Location { get; set; }
public Properties Properties { get; set; }
}
public class Properties
{
public string Name { get; set; }
public string PublishingUserName { get; set; }
public string PublishingPassword { get; set; }
public object PublishingPasswordHash { get; set; }
public object PublishingPasswordHashSalt { get; set; }
public object Metadata { get; set; }
public bool IsDeleted { get; set; }
public string ScmUri { get; set; }
}
After obtaining the credentials, follow the below piece of code to deploy or update your Azure Functions
var base64Auth = Convert.ToBase64String(Encoding.Default.GetBytes
($"{functionCredentials.Properties.PublishingUserName}:{functionCredentials.Properties.PublishingPassword}"));
var stream = new MemoryStream(File.ReadAllBytes("zip file of azure function"));
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Add("Authorization", "Basic " + base64Auth);
var apiUrl = "https://" + parameters.FunctionAppName + ".scm.azurewebsites.net/api/zip/site/wwwroot";
var httpContent = new StreamContent(stream);
client.PutAsync(apiUrl, httpContent).Result;
}
Now your functionapp should be deployed.
Related
We have a SharePoint list that contains a large PDF attachment and have set up an Azure Webhook to notify an Azure Function App of a change to the SharePoint list and would like to have the ability to check for and parse a PDF attachment.
I am fairly still new to this type of development, but have found an example that I followed, and our test currently seems to be triggering an event in our Azure Function and can see that the trigger is successful:
Monitor Invocation:
The code that I've implemented is pretty much verbatim from the above link example and it appears that most of the functionality is currently simply writing out log information, however I've not been able to find many examples of detailed implementation scenarios using SharePoint Lists to get an attachment from a SharePoint List.
Should we be getting our attachment data after reading in the request into a StreamReader object here?
var content = await new StreamReader(req.Body).ReadToEndAsync();
Full Code Context:
#r "Newtonsoft.Json"
using System.Net;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Primitives;
using Newtonsoft.Json;
public static async Task<IActionResult> Run(HttpRequest req,
ICollector<string> outputQueueItem, ILogger log)
{
log.LogInformation($"Webhook was triggered!");
// Grab the validationToken URL parameter
string validationToken = req.Query["validationtoken"];
// If a validation token is present, we need to respond within 5 seconds by
// returning the given validation token. This only happens when a new
// webhook is being added
if (validationToken != null)
{
log.LogInformation($"Validation token {validationToken} received");
return (ActionResult)new OkObjectResult(validationToken);
}
log.LogInformation($"SharePoint triggered our webhook...great :-)");
//Is the attachment available via the content variable?
var content = await new StreamReader(req.Body).ReadToEndAsync();
log.LogInformation($"Received following payload: {content}");
var notifications = JsonConvert.DeserializeObject<ResponseModel<NotificationModel>>(content).Value;
log.LogInformation($"Found {notifications.Count} notifications");
if (notifications.Count > 0)
{
log.LogInformation($"Processing notifications...");
foreach(var notification in notifications)
{
// add message to the queue
string message = JsonConvert.SerializeObject(notification);
log.LogInformation($"Before adding a message to the queue. Message content: {message}");
outputQueueItem.Add(message);
log.LogInformation($"Message added :-)");
}
}
// if we get here we assume the request was well received
return (ActionResult)new OkObjectResult($"Added to queue");
}
// supporting classes
public class ResponseModel<T>
{
[JsonProperty(PropertyName = "value")]
public List<T> Value { get; set; }
}
public class NotificationModel
{
[JsonProperty(PropertyName = "subscriptionId")]
public string SubscriptionId { get; set; }
[JsonProperty(PropertyName = "clientState")]
public string ClientState { get; set; }
[JsonProperty(PropertyName = "expirationDateTime")]
public DateTime ExpirationDateTime { get; set; }
[JsonProperty(PropertyName = "resource")]
public string Resource { get; set; }
[JsonProperty(PropertyName = "tenantId")]
public string TenantId { get; set; }
[JsonProperty(PropertyName = "siteUrl")]
public string SiteUrl { get; set; }
[JsonProperty(PropertyName = "webId")]
public string WebId { get; set; }
}
public class SubscriptionModel
{
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public string Id { get; set; }
[JsonProperty(PropertyName = "clientState", NullValueHandling = NullValueHandling.Ignore)]
public string ClientState { get; set; }
[JsonProperty(PropertyName = "expirationDateTime")]
public DateTime ExpirationDateTime { get; set; }
[JsonProperty(PropertyName = "notificationUrl")]
public string NotificationUrl {get;set;}
[JsonProperty(PropertyName = "resource", NullValueHandling = NullValueHandling.Ignore)]
public string Resource { get; set; }
}
I thought that I would try to debug this integration remotely since the code is running in the Azure Portal in order to set up watches and look into the HTTPRequest and StreamReader objects, however that's presented a whole new set of challenges and we've been unsuccessful there as well.
Thanks in advance.
What you've done here is fine, assuming you end up with an item placed on a queue. The real heavy lifting though will happen with the function that picks up the item off the queue. The json you send to the queue only notes that a change occurred; your queue receiver is going to have to authenticate and call back into SharePoint to fetch the data and do what it needs to do.
Read the reference implementation section of the documentation you linked for a better explanation of the webhooks architecture.
I will add that developing Azure functions directly in the portal is going to be a nightmare for anything but trivial applications. And your application is not trivial. The ngrok based approach in the Get Started section is good advice and really the only way to debug webhooks. Good luck!
I have a problem in Azure search, I get data from a csv file in blob storage. To simplify let assume my object as:
public class Instrument
{
public Identifier Identifier { get; set; }
[SearchableField(IsSortable = true, IsKey = true)]
public string Id { get; set; }
[SearchableField(IsSortable = true)]
public string RefIs { get; set; }
}
public class Identifier
{
[SearchableField(IsSortable = true)]
public string Code { get; set; }
}
when I receive my data it's flat in the csv such as : _id,_ref,_code
When I create my index through SDK I setup Mapping for simple fields:
indexer.FieldMappings.Add(new FieldMapping("_ref")
{
TargetFieldName = "RefIs"
});
but I cannot figure out the way I declare it to complex type in my indexer ? given code does not work for it :
indexer.FieldMappings.Add(new FieldMapping("_code")
{
TargetFieldName = "Code"
});
indexer.FieldMappings.Add(new FieldMapping("_code")
{
TargetFieldName = "Identifier.Code"
});
error is the same TargetField is not present in index.
Could someone help ?
The best way to overcome is to parse your .csv file your own way and then post documents to ACS using exposed API. This is the best effective way (do not rely on builtin serialiser)
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?}");
});
I'm trying to make a small upload file api by using ASP.NET Core 1.0.
I have a model which I took from an MVC5 application:
public class RoomModel
{
public int Id { get; set; }
public string Name { get; set; }
public HttpPostedFileBase Image { get; set; }
}
I want to make a function like :
[HttpPost("upload")]
public IActionResult Upload(List<RoomModel> rooms)
{
// Check and upload file here.
// Save parameter to database.
}
In my MVC5, it is OK with HttpPostedFileBase , but in ASP.NET Core, I don't know how to archive with the same result.
Can anyone help me please ?
Thank you.
P/S: I've searched for tutorials like this but found nothing. Every tutorial I've read is only about get key-value parameters , no tutorial is about placing information in a model like this .
Finally, I've found this post :
http://www.mikesdotnetting.com/article/288/uploading-files-with-asp-net-core-1-0-mvc
From this post, my RoomModel will be like this :
public class RoomModel
{
public int Id { get; set; }
public string Name { get; set; }
public IFormFile Image { get; set; }
}
So, as my current knowledge, IFromFile replaces HttpPostedFileBase, and the step handling files is the same as HttpPostedFileBase.
I'm looking to create some custom API apps for the sole purpose of creating a/some Logic Apps. With these custom API Apps, I want to pass around files. These files will usually be CSV, ZIP, Excel, TXT, and some other formats - unknown to the consumer until the file is returned (i.e. the client does not dictate the file format).
How does one do something like this in a way that's compatible with Swagger/Swashbuckle, Web API, and Logic Apps? I'll ultimately be tying this into an FTP connector, Dropbox, Onebox, or other file-storage connector.
Does following something like this work or do I need to take a different approach? For example, should I simply work with JSON objects and let my binary be base64-encoded by using a model like this?
public class BinaryFile
{
public string FileName { get; set; }
public string FileExtension { get; set; }
public string DeducedMimeType { get; set; }
public int FileSize { get; set; }
public string FileEncoding { get; set; }
public byte[] FileBinary { get; set; }
}
(this question is cross-posted to MSDN Forums)
The approach I've taken is what I posed in the question with the BinaryFile class. I've broken it out into a few classes. I'm not done - I have some improvements to make still but this is functional right now.
Main class with some common fields:
public class FileResult<T>
{
public string Source { get; set; }
public T File { get; set; }
public IList<string> ErrorMessages { get; set; } = new List<string>();
public bool IsSuccessful { get; set; } = false;
}
This is the <T> in my FileResult<T> class:
public class CsvFile
{
public string Filename { get; set; }
public string Contents { get; set; }
public int Size { get; set; }
}
public class BinaryFile
{
public string FileName { get; set; }
public string FileExtension { get; set; }
public string DeducedMimeType { get; set; }
public int Size { get; set; }
public byte[] Contents { get; set; }
}
Note that in my case, there are some times when I am working with multiple files and not just one, so what could appear to be some common fields are still within the type passed in as the <T> so I can have something like FileResult<IEnumerable<CsvFile>>.
This is all playing nicely with Swagger, Swashbuckle, Web API, Azure API Apps, and Azure Logic Apps. For the cases where I am returning multiple files to my Azure Logic App, I use the fairly hidden splitsOn feature (that also has some designer bugs, so be careful!) to easily iterate over all of the files. It works very nicely.