How to use WebDeploy with ASP.Net core - iis

We have an Update-Program that publishes automatically WebDeploy-Packages to IIS-Servers. This Program works fine with MVC 5 - Applications but not with .Net Core Applications. We Use the Microsoft.Web.Deployment - API and my publish method looks like this:
private void UpdateIIS(DTOs.ApplicationConfiguration.ApplicationServer applicationServer, DTOs.Update.Update update) {
// SSL-Zertifikatsfehler ignorieren
System.Net.ServicePointManager.ServerCertificateValidationCallback += new System.Net.Security.RemoteCertificateValidationCallback(
(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) => { return true; });
var sourceBaseOptions = new DeploymentBaseOptions();
var destBaseOptions = new DeploymentBaseOptions() {
ComputerName = string.Format("https://{0}:{1}/msdeploy.axd?site={2}", applicationServer.Adresse, applicationServer.MSDeployPort, applicationServer.SiteName),
UserName = applicationServer.User,
Password = applicationServer.Passwort,
AuthenticationType = "Basic"
};
string updatePackage = Directory.GetFiles(update.UpdateDirectory, "*.zip").FirstOrDefault();
if (!File.Exists(updatePackage)) {
throw new Exception("Update-Paketdatei nicht gefunden");
}
using (var deploymentObject = DeploymentManager.CreateObject(
DeploymentWellKnownProvider.Package,
updatePackage,
sourceBaseOptions)
) {
if (deploymentObject.SyncParameters.Count > 0) {
deploymentObject
.SyncParameters
.Single(p => p.Name == "IIS Web Application Name")
.Value = applicationServer.SiteName;
}
var syncOptions = new DeploymentSyncOptions();
var changes = deploymentObject.SyncTo(
DeploymentWellKnownProvider.Auto,
"",
destBaseOptions,
syncOptions
);
}
}
I get no errors, but the Website doesn't change.

Yes you can use WebDeploy with ASP.NET Core. The key is to use the PackagePublish target and a package publish profile:
D:\GitHub\AcmeApp.NET\src\AcmeApp.NET.Web>"c:\Program Files (x86)\MSBuild\14.0\Bin\MSBuild" /t:PackagePublish /p:PublishProfile=Package
http://www.dotnetcatch.com/2016/09/20/webdeploy-packaging-with-asp-net-core/

You can also use dotnet publish to do this directly from your project without explicitly invoking MSDeploy.
$uid='username'
$pswd='password'
dotnet publish /p:PublishProfile=Properties/PublishProfiles/Live.pubxml /p:Username=$uid /p:Password=$pswd -c Release

Related

Azure Functions Dependency Tracking for SQL Server and Service Bus Into Application Insights

Previously I have Azure Web App (.net core) and It successfully track the SQL Server and Service Bus dependency into Application Insights. It is not working some how with Azure Functions.
Environment
dotnet 6
dotnet-isolated mode
log level default set to "Information".
Azure Environment using Consumption plan for Azure Functions.
Application Insights key is configured.
I have Azure API management at front and backend is Azure Function and that call SQL Server and Service Bus.
Api Management Service to Azure function dependency successfully resolved but Azure Function to other component is not working.
I know I am posting my own answer. Also there are chance that in future there may be some good solution or it get integrated the way it is in in-process mode.
By then follow steps.
Add Package
Microsoft.ApplicationInsights.WorkerService
In program.cs in configuring host.
services.AddApplicationInsightsTelemetryWorkerService();
More info at
https://learn.microsoft.com/en-us/azure/azure-monitor/app/worker-service
The only way I've managed to solve this issue so far was by setting up custom Middleware
.ConfigureFunctionsWorkerDefaults(config =>
{
config.UseMiddleware<AiContextMiddleware>();
})
In the IServiceCollection you need to setup simply
.AddApplicationInsightsTelemetryWorkerService()
public class AiContextMiddleware : IFunctionsWorkerMiddleware
{
private readonly TelemetryClient _client;
private readonly string _hostname;
public AiContextMiddleware(TelemetryClient client)
{
_client = client;
_hostname = Environment.GetEnvironmentVariable("AI_CLOUD_ROLE_NAME");
}
public async Task Invoke(FunctionContext context, FunctionExecutionDelegate next)
{
var operationId = ExtractOperationId(context.TraceContext.TraceParent);
// Let's create and start RequestTelemetry.
var requestTelemetry = new RequestTelemetry
{
Name = context.FunctionDefinition.Name,
Id = context.InvocationId,
Properties =
{
{ "ai.cloud.role", _hostname},
{ "AzureFunctions_FunctionName", context.FunctionDefinition.Name },
{ "AzureFunctions_InvocationId", context.InvocationId },
{ "AzureFunctions_OperationId", operationId }
},
Context =
{
Operation =
{
Id = operationId,
ParentId = context.InvocationId,
Name = context.FunctionDefinition.Name
},
GlobalProperties =
{
{ "ai.cloud.role", _hostname},
{ "AzureFunctions_FunctionName", context.FunctionDefinition.Name },
{ "AzureFunctions_InvocationId", context.InvocationId },
{ "AzureFunctions_OperationId", operationId }
}
}
};
var operation = _client.StartOperation(requestTelemetry);
try
{
await next(context);
}
catch (Exception e)
{
requestTelemetry.Success = false;
_client.TrackException(e);
throw;
}
finally
{
_client.StopOperation(operation);
}
}
private static string ExtractOperationId(string traceParent)
=> string.IsNullOrEmpty(traceParent) ? string.Empty : traceParent.Split("-")[1];
}
It's definitely not a perfect solution as you then get two starting logs, but as end result, you get all logs traces + dependencies correlated to an operation.
I've solved this issue in the first place like that, now I'm revisiting whether there are any better ways to solve this.
Let me know too whether you managed to solve this issue on your side.

Extracting application insight instrumentationkey based on name

I'm trying to configure some automation for a large cloud service that requires many different instances of application insight. Is there a way to extract instrumentation key based on application insight name? Through some management library perhaps?
Yes, there is .net library for that.
First, install the following nuget packages in your project:
Install-Package Microsoft.Azure.Management.ApplicationInsights -IncludePrerelease
Install-Package Microsoft.Azure.Services.AppAuthentication -IncludePrerelease
Then, write a method which return the instrumentation key based on app insights name.
static string GetIKey(string app_insights_name)
{
string IKey = "";
var auth = new AzureServiceTokenProvider();
const string url = "https://management.azure.com/";
string token = auth.GetAccessTokenAsync(url).Result;
var cred = new TokenCredentials(token);
var client = new ApplicationInsightsManagementClient(cred)
{
//replace with your subscription id
SubscriptionId = "your-subscription-id",
};
var list = new List<ApplicationInsightsComponent>();
var all = client.Components.List();
list.AddRange(all);
foreach (var item in list)
{
if (item.Name.ToLower() == app_insights_name.ToLower())
{
return item.InstrumentationKey;
}
}
//if no app insights name matches, return ""
return "";
}
Test result:

QueueTrigger is not picking messages- Azure WebJobs SDK 3.0

I'm trying to develop WebJob using SDK 3.0.x, and testing it locally. I've followed the sample in github without any success.
When running it locally everything is going ok, it also see the ProcessQueueMessage function but it doesn't pick the messages from the queue.
Program.cs
static void Main(string[] args)
{
var builder = new HostBuilder();
//builder.UseEnvironment(EnvironmentName.Development);
builder.ConfigureWebJobs(b =>
{
b.AddAzureStorageCoreServices();
b.AddAzureStorage();
});
builder.ConfigureAppConfiguration((context, config) =>
{
config.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
});
builder.ConfigureLogging((context, b) =>
{
b.AddConsole();
// If the key exists in settings, use it to enable Application Insights.
string instrumentationKey = context.Configuration["APPINSIGHTS_INSTRUMENTATIONKEY"];
if (!string.IsNullOrEmpty(instrumentationKey))
{
b.AddApplicationInsights(o => o.InstrumentationKey = instrumentationKey);
}
});
builder.ConfigureServices((context, services) =>
{
//services.AddSingleton<IJobActivator, MyJobActivator>();
services.AddScoped<Functions, Functions>();
services.AddSingleton<IHostService, HostService>();
})
.UseConsoleLifetime();
var host = builder.Build();
using (host)
{
host.Run();
}
}
Functions.cs
public class Functions
{
private readonly IHostService _hostService;
public Functions(IHostService hostService)
{
_hostService = hostService;
}
// This function will get triggered/executed when a new message is written
// on an Azure Queue called queue.
public void ProcessQueueMessage([QueueTrigger("newrequests")] string dd,
//DateTimeOffset expirationTime,
//DateTimeOffset insertionTime,
//DateTimeOffset nextVisibleTime,
//string queueTrigger,
//string id,
//string popReceipt,
//int dequeueCount,
ILogger logger)
{
var newRequestItem = new RequestQueueItem();
logger.LogTrace($"New queue item received...");
//logger.LogInformation($" QueueRef = {id} - DequeueCount = {dequeueCount} - Message Content [Id = {newRequestItem.Id}, RequestDate = {newRequestItem.RequestDate}, Mobile = {newRequestItem.Mobile}, ProviderCode = {newRequestItem.ProviderCode}, ItemIDClass = {newRequestItem.MappingIDClass}]");
// TODO: Read the DatabaseConnectionString from App.config
logger.LogTrace($" Getting DB ConnectionString...");
var connectionString = ConfigurationManager.ConnectionStrings["DatabaseConnection"].ConnectionString;
// TODO: Initiation of provider service instance
logger.LogTrace($" Init IalbayanmtnclientserviceClient service instance...");
var bayanService = new AlbayanMtnWCFService.IalbayanmtnclientserviceClient();
// TODO: sending request to provider service endpoint and wait for response
logger.LogTrace($" Sending request to Service Endpoint...");
var response= bayanService.requestpaymenttransactionAsync("agentcode", "agentpassword", "accountno", int.Parse(newRequestItem.TransactionType), newRequestItem.MappingIDClass, newRequestItem.Mobile, (int)newRequestItem.Id).Result;
logger.LogTrace($"Done processing queue item");
}
}
Here is the screen shot for the output
Appreciate your help
Screen shot for queue messages 'newrequests'
enter image description here
From your snapshot, your webjob runs well on local. It didn't pick message because you don't add message in the newrequests queue.
The function only be triggered after you add the message. Or I will get the same result just like yours.
About the tutorial , your could refer to the official doc:Get started with the Azure WebJobs SDK. And make sure you set the right storage account. The below is my appsettings.json. Make sure the "Copy to output directory" property of the appSettings.json file is set to either Copy if newer or Copy always. Or it will run into exception:Storage account 'Storage' is not configured.
{
"ConnectionStrings": {
"AzureWebJobsStorage": "DefaultEndpointsProtocol=https;AccountName=mystorage;AccountKey=key;..."
}
}
Hope this could help you, if you still have other questions, please let me know.

How to download the documents from Version History from a document in SharePoint

I want to download all the documents from "Version History" of a file in SharePoint.
I need the documents either via an interface or a C# code.
I have also tried below API URL, but I am getting an error.
https://XXXX.sharepoint.com/_api/Web/GetFileByServerRelativeUrl('fileURL')/Versions
Ensure that you are using the latest version of SharePoint Online CSOM or atleast a version after September 2017.
You can get the latest CSOM version from Nuget and add it to your project.
Once done, you can use the code mentioned below to download the versions of a file. Modify it as per your environment, creds as well as file name:
var siteUrl = "https://XXXX.sharepoint.com";
var userName = "user.name#tenantname.onmicrosoft.com";
var password = "password";
using (ClientContext context = new ClientContext(siteUrl))
{
SecureString securePassword = new SecureString();
foreach (char c in password.ToCharArray())
{
securePassword.AppendChar(c);
}
context.AuthenticationMode = ClientAuthenticationMode.Default;
context.Credentials = new SharePointOnlineCredentials(userName, securePassword);
var file = context.Web.GetFileByServerRelativeUrl(siteUrl + "/Documents/test.docx");
var fileVersions = file.Versions;
context.Load(file);
context.Load(fileVersions);
context.ExecuteQuery();
int index = 0;
foreach (var version in fileVersions)
{
var str = version.OpenBinaryStream();
context.ExecuteQuery();
string filename = string.Format("c:\\Downloads\\test-{0}.docx", index);
using (var fileStream = new FileStream(filename, FileMode.OpenOrCreate))
{
str.Value.CopyTo(fileStream);
}
index++;
}
}
Reference - CSOM September 2017 update
Per MSDN, I guess you cannot download all the versions at one shot. You have to send versionid appended within brackets like versions(<version id>) to get particular item.
http://<site url>/_api/web/getfilebyserverrelativeurl('/<folder name>/<file name>')/versions(<version id>)
Make sure the expected url is set properly if you want minor version.
The code below for your reference.
string _SharePointSiteURL = #"https://lz.sharepoint.com/sites/lz";
var _SharePointSiteUser = "lz#lz.onmicrosoft.com";
var password = "Password";
var localPath = #"c:\test\";
var filePath="Shared Documents/ABC/Test.pdf";
var securePassword = new SecureString();
foreach (char c in password)
{
securePassword.AppendChar(c);
}
using (var clientContext = new ClientContext(_SharePointSiteURL))
{
var onlineCredentials = new SharePointOnlineCredentials(_SharePointSiteUser, securePassword);
clientContext.RequestTimeout = 10000000;
clientContext.Credentials = onlineCredentials;
Web web = clientContext.Web;
clientContext.Load(web, website => website.ServerRelativeUrl, website => website.Url);
clientContext.ExecuteQuery();
var spFile = clientContext.Web.GetFileByServerRelativeUrl((web.ServerRelativeUrl.EndsWith("/") ? web.ServerRelativeUrl : web.ServerRelativeUrl + "/") + filePath);
clientContext.Load(spFile);
FileVersionCollection versions = spFile.Versions;
clientContext.Load(versions);
var oldVersions = clientContext.LoadQuery(versions.Where(v => v != null));
clientContext.ExecuteQuery();
if (oldVersions != null)
{
foreach (Microsoft.SharePoint.Client.FileVersion _version in oldVersions)
{
if (!Directory.Exists(localPath))
{
Directory.CreateDirectory(localPath);
}
using (var wc = new System.Net.WebClient())
{
wc.Credentials = onlineCredentials;
wc.Headers.Add("X-FORMS_BASED_AUTH_ACCEPTED", "f");
wc.Headers["User-Agent"] = "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; MDDC)";
wc.DownloadFile(web.Url + "/" + _version.Url, localPath+"Test.pdf");
}
}
}
}
This answer is for someone else who might be looking for a Powershell CSOM script which will download current documents plus versions history.
Admittedly a Powershell newbie, I struggled with finding a script which would download all current files and their versions from SharePoint. Accessing the versions proved to be a challenge as various scripts I tried either came up with (404) Not Found or (403) Forbidden. I was able to piece together a CSOM function which I call from another script I found which downloads all current versions from a specified library/list. I can’t post my entire solution as the originating script isn’t mine. You can find it here:
http://www.sharepointdiary.com/2017/03/sharepoint-online-download-all-files-from-document-library-using-powershell.html#ixzz5YpC0YB00
In the Download-AllFilesFromLibrary function from the above site, I added GetFileVersions as the last statement of the Foreach($File in $FilesColl) loop.
As I said, I’m new to Powershell, so I’m sure some improvements could be made to the function, but it works.
Function GetFileVersions
{
#Get versions of specific document from library
$fileVersions = $file.Versions;
$ctx.Load($fileVersions);
$ctx.ExecuteQuery();
$vcount = $fileversions.count;
If ($vcount -ne “0” ) {
# Download all versions of specific file as individual docs
$fname,$fext = $file.name.split('.'); # Separate filename and extension to different variables
foreach ($fversion in $fileVersions) {
$str = $fversion.OpenBinaryStream();
$ctx.ExecuteQuery();
$dtime = $fversion.created.ToString(“u”) -replace “:”,””; # Convert date/time created to formatted string and replace semicolons in the time with blank
$filename =$localfolder + “\” + $fname + “~” + $dtime + “.” + $fext ; # Compose the filename as the target folder, file name + ~+ datetime created.fileextension (i.e. book~2019-02-12 153659Z.xlsx)
$fs = New-Object IO.FileStream $filename ,'Create','Write','Read';
$str.Value.CopyTo($fs);
#To use the Write-Log function below, see the script at https://gallery.technet.microsoft.com/scriptcenter/Write-Log-PowerShell-999c32d0
#$vmessage = $filename + “*” + $fversion.versionlabel; # Message to write to the log file
#Write-Log $vmessage;
$fs.Close();
}
if ($fs.isdisposable) {$fs.Dispose();}
}
}
I have a working solution for a migrating tool that I developed. The requisite is migrate file and older versions from a SharePoint environment to another one.
To download an older version do this:
public byte[] DownloadFile(string url, bool isHistory = false)
{
bool isSharepointOnline = (SPCurrentContext.Credentials is SharePointOnlineCredentials);
HttpResponseMessage response = null;
byte[] buffer = null;
if (!isHistory)
{
using (FileInformation fileInfo = File.OpenBinaryDirect(SPCurrentContext, url))
using (IO.MemoryStream memory = new IO.MemoryStream())
{
fileInfo.Stream.CopyTo(memory);
buffer = memory.ToArray();
}
}
else if (isSharepointOnline)
{
using (WebClient httpClient = new WebClient())
{
httpClient.Credentials = SPCurrentContext.Credentials;
httpClient.Headers.Add("X-FORMS_BASED_AUTH_ACCEPTED", "f");
httpClient.Headers["User-Agent"] = this.NONISV; // This is the TRICK!!!
buffer = httpClient.DownloadData(string.Concat(SPCurrentContext.Url, url));
}
}
else
{
using (HttpClientHandler httpHandler = new HttpClientHandler() { Credentials = SPCurrentContext.Credentials })
using (HttpClient client = new HttpClient(httpHandler))
{
response = client.GetAsync(string.Concat(SPCurrentContext.Url, url)).Result;
response.EnsureSuccessStatusCode();
response.Content.LoadIntoBufferAsync().Wait();
buffer = response.Content.ReadAsByteArrayAsync().Result;
}
}
return buffer;
}
Explanation:
This method works fine to download current version or older version of a file since you have file URL (File.ServerRelativeUrl and FileVersion.Url should help you with this step). Then, when you try to download file version from "SharePoint Online" as you do in "SP On-Premises", you will get 403 HTTP ERROR.
When you put some user-agent and X-FORMS_BASED_AUTH_ACCEPTED headers, the solution starts to work. But... Certainly you will receive a timeout error due to 429 HTTP ERROR.
To bypass SharePoint Throttling, you have to register your program as a SharePoint Add-In.
To do this, go to:
{your sharepoint online root url}/_layouts/15/AppRegNew.aspx
And generate your Add-In registration (see this link and this link for instructions and more info)
Once you got it, you can use NONISV|{Your Company Name}|{Your Add-In Name}/1.0 as User-Agent HTTP Header. Good luck!

windows azure or IIS slow in inital load

I have a simple personal MVC4 web app that is hosted in Windows Azure.
This web app is very minimal in use, the initial call is very slow specially when I tried to click in the morning.
I’m suspecting that the IIS is sleeping and need to wake up. I found this article and mention that this is a bug in IIS http://social.msdn.microsoft.com/Forums/en-US/wcf/thread/8b3258e7-261c-49a0-888c-0b3e68b2af13 which required setting up in IIS but my web app is hosted in Azure, is there any way to do some sort of setting in Web.config file?
All succeeding calls are fast.
Here is my personal page. javierdelacruz.com
Thanks.
Two options:
Startup Tasks
OnStart Code
For startup tasks, see this link.
For OnStart code, try a function like this (this function does a few more things, too):
private const string _web_app_project_name = "Web";
public static void SetupDefaultEgConfiguration(int idleTimeoutInMinutes = 1440, int recycleTimeoutInMinutes = 1440, string appPoolName = "My Azure App Pool", bool enableCompression = true)
{
if (!RoleEnvironment.IsEmulated)
{
Trace.TraceWarning("Changing IIS settings upon role's OnStart. Inputs: ({0}, {1}, {2}, {3}", idleTimeoutInMinutes, recycleTimeoutInMinutes, appPoolName, enableCompression);
// Tweak IIS Settings
using (var iisManager = new ServerManager())
{
try
{
var roleSite = iisManager.Sites[RoleEnvironment.CurrentRoleInstance.Id + "_" + _web_app_project_name];
if (enableCompression)
{
//================ Enable or disable static/Dynamic compression ===================//
var config = roleSite.GetWebConfiguration();
var urlCompressionSection = config.GetSection("system.webServer/urlCompression");
urlCompressionSection["doStaticCompression"] = true;
urlCompressionSection["doDynamicCompression"] = true;
Trace.TraceWarning("Changing IIS settings to enable static and dynamic compression");
}
//================ To change ApplicationPool name ================================//
var app = roleSite.Applications.First();
app.ApplicationPoolName = appPoolName;
//================ To change ApplicationPool Recycle Timeout ================================//
var appPool = iisManager.ApplicationPools[app.ApplicationPoolName];
appPool.Recycling.PeriodicRestart.Time = new TimeSpan(0, recycleTimeoutInMinutes, 0);
//================ idletimeout ====================================================//
var defaultIdleTimeout = iisManager.ApplicationPoolDefaults.ProcessModel.IdleTimeout;
var newIdleTimeout = new TimeSpan(0, idleTimeoutInMinutes, 0);
if ((int)newIdleTimeout.TotalMinutes != (int)defaultIdleTimeout.TotalMinutes)
{
appPool.ProcessModel.IdleTimeout = newIdleTimeout;
}
// Commit the changes done to server manager.
iisManager.CommitChanges();
}
catch (Exception e)
{
Trace.TraceError("Failure when configuring IIS in Azure: " + e.ToString().Take(63000));
}
}
}
}
Source and some more details for the function I included here - there are some dependencies you'll likely need to accomplish this.

Resources