RazorEngine Could not resolve template - razorengine

I have a console application that uses RazorEngine to send email based on the run of the application. I am successfully able to send emails both in the development IDE and from running the console application via the executable. However when I run the console application from a scheduled task I receive an error "Could not resolve template" on line,
var emailHtmlBody = Engine.Razor.RunCompile("ResponseErrors.cshtml", null, model);
has anyone run into this?
var config = new TemplateServiceConfiguration
{
TemplateManager = new ResolvePathTemplateManager(new[] {"EmailTemplates"}),
DisableTempFileLocking = true
};
Engine.Razor = RazorEngineService.Create(config);
var emailHtmlBody = Engine.Razor.RunCompile("ResponseErrors.cshtml", null, model);
UPDATE 1:
The server that is executing the scheduled task is the same server where the console app resides.
The account that is executing the console app in the Scheduled Task is a domain account with local admin rights to the server.
UPDATE 2:
There is no RazorEngine... folder that was created in the user's temp folder.
UPDATE 3:
I just created a sample console app and it's now doing the same thing in the IDE. I put it up on GitHub as RazorEngine_Test
UPDATE 4:
It seems that the TemplateServiceConfiguration is not working as designed or the examples I worked off of are incorrect. I updated the test project with the 2 lines below and that solved my issue. I don't think this was the intended implementation method but it works. Posting because I know someone will run into the same issue.
string path = AppDomain.CurrentDomain.BaseDirectory;
string filePath = $"{path}EmailTemplates\\ExceptionEmail.cshtml";

So I was able to confirm the fix in Production today. Adding those 2 lines did correct the problem. So the complete code block for this is
var config = new TemplateServiceConfiguration
{
TemplateManager = new ResolvePathTemplateManager(new[] {"EmailTemplates"}),
DisableTempFileLocking = true
};
string path = AppDomain.CurrentDomain.BaseDirectory;
string filePath = $"{path}EmailTemplates\\ExceptionEmail.cshtml";
Engine.Razor = RazorEngineService.Create(config);
var emailHtmlBody = Engine.Razor.RunCompile(filePath, null, model);

Had the same Problem my solution was changing the Project file ProjectName.csproj external and adding the View as content and not as None
from
<None Include="Views/Folder/Name.cshtml"/>
to
<Content Include="Views\Emails\Name.cshtml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>

Related

Unable to use SelectPDF after deployment to IIS

I have a function that generates a PDF from a HTML page like this:
HtmlToPdf converter = new HtmlToPdf();
PdfDocument doc = converter.ConvertUrl(url);
var PdfArray = doc.Save();
doc.Close();
This works perfectly when I run it in VS 2017, However when I deploy to IIS it throws the following exception: "Conversion failure error 5."
According to my Googling this is related to the IIS not having the correct access to write. However I have as an attempt given that application access to every operation.
All suggestions would be greatly appreciated.
From the troubleshooting page on SelectPdf website:
https://selectpdf.com/docs/Troubleshooting.htm#item3
The error code is this:
ERROR_ACCESS_DENIED
5 (0x5)
Access is denied.
Enable execute permissions on Select.Html.dep.
You need to go to the bin folder of your deployment to IIS and set execute permissions for the Select.Html.dep file. If you do not know the app pool user, in the first place, just set permissions for Everyone to see if it works.

Deploying to a private IIS server from a build in Visual Studio Team Services

Having done what is suggested here: Deploy from Visual Studio Online build to private IIS server
... how do I setup automatic deploys as part of my build when I build a whole branch **/*.sln?
What I have tried ...
In VS I can get the latest version of the code, open a solution and then ...
right click > publish > pick publish profile > deploy
I have named my publish profiles things like "dev", "qa", "production", these refer to the environments into which the project will be deployed and the profiles contain all of the configuration information needed for VS to deploy (via webdeploy / msdeploy) using "one click deploy" that application.
I want to have Team Services on the build server do the exact same thing for projects that have publish profiles defined after it's built the code.
My understanding was that I could just add the msbuild args like this ...
this results in the deployment part of the build throwing the following exception in to the build log ...
C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\v14.0\Web\Microsoft.Web.Publishing.targets(4288,5):
Error ERROR_USER_NOT_ADMIN: Web deployment task failed.
(Connected to 'server' using the Web Deployment Agent Service, but could not authorize. Make sure you are an administrator on 'server'.
Learn more at: http://go.microsoft.com/fwlink/?LinkId=221672#ERROR_USER_NOT_ADMIN.)
What user is this using if not the user defined in the publish profile?
Related issues:
Publishing via TFS Build Service fails with "User not Admin"
TFS Builds: Running the builds as administrator
I added an account to the server in question (since the build and server to be deployed to are the same server it made things easier), I also added a group to the server called "MSDepSvcUsers" and added the new account in question to it and the admins group on the box.
I then told both the Web Deployment Agent service and the Team Services Agent service to run under this account (and restarted them).
Unfortunately the result is the same ... I now really want to know how I go about ensuring the account that is used for the msdeploy command is something I expect without relying on loads of scripting ... or maybe that's why Microsoft haven't set this up as a default deploy step option in Team Services already!
Ok so I had some long conversations with the VSTS team over at Microsoft about this and the long and short of it is ...
Microsoft:
We understand your frustration with this area and a big project is
about to spin up to resolve this issue
...
Me being me, came up with some "trick to make it happen".
I managed to figure out that the build box for some odd reason can't be the same server that you are deploying too (no idea why) but having figured that out I wrote a simple console app that with some additional feedback from Microsoft came out pretty good.
It even reports progress back to the process and can log exceptions in the deployment as exceptions in order to fail the build by calling up "internal commands" (neat how this works by the way kudos to the team for that).
There are some hacks in here and it's not perfect but hopefully it'll help someone else, I call this because it's part of the code that gets built in my repo so I am able to add a step in to the build process to call this from within the build output passing the environment name I want to deploy to.
This in tern grabs all the packages (as per the settings above) and uses their publish profiles to figure out where the packages need to go and sends them to the right servers to be deployed ...
using System;
using System.Diagnostics;
using System.IO;
using System.Reflection;
namespace Deploy
{
class Program
{
static string msDeployExe = #"C:\Program Files\IIS\Microsoft Web Deploy V3\msdeploy.exe";
static void Main(string[] args)
{
var env = args[0];
var buildRoot = Path.Combine(Assembly.GetExecutingAssembly().Location.Replace("Deploy.exe", ""), env);
//var commands = GetCommands(buildRoot);
var packages = new DirectoryInfo(buildRoot).GetFiles("*.zip", SearchOption.AllDirectories);
bool success = true;
for (int i = 0; i < packages.Length; i++)
{
if (!Deploy(packages[i], env)) success = false;
Console.WriteLine("##vso[task.setprogress]" + (int)(((decimal)i / (decimal)packages.Length) * 100m));
}
Console.WriteLine("##vso[task.setprogress]100");
if(success) Console.WriteLine("##vso[task.complete result=Succeeded]");
else Console.WriteLine("##vso[task.complete result=SucceededWithIssues]");
}
static bool Deploy(FileInfo package, string environment)
{
bool succeeded = true;
Console.WriteLine("Deploying " + package.FullName);
var procArgs = new ProcessStartInfo
{
FileName = msDeployExe,
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
Arguments =
"-source:package='" + package.FullName + "' " +
"-dest:auto,ComputerName='" + environment + ".YourDomain.com',UserName='deployment user',Password='password',AuthType='ntlm',IncludeAcls='False' " +
"-verb:sync " +
"-disableLink:AppPoolExtension " +
"-disableLink:ContentExtension " +
"-disableLink:CertificateExtension " +
"-setParamFile:\"" + package.FullName.Replace("zip", "SetParameters.xml") + "\""
};
try
{
Console.WriteLine(msDeployExe + " " + procArgs.Arguments);
using (var process = Process.Start(procArgs))
{
var result = process.StandardOutput.ReadToEnd().Split('\n');
var error = process.StandardError.ReadToEnd();
process.WaitForExit();
if (!string.IsNullOrEmpty(error))
{
Console.WriteLine("##vso[task.logissue type=error]" + error);
succeeded = false;
}
foreach (var l in result)
if (l.ToLowerInvariant().StartsWith("error"))
{
Console.WriteLine("##vso[task.logissue type=error]" + l);
succeeded = false;
}
else
Console.WriteLine(l);
}
}
catch (Exception ex) {
succeeded = false;
Console.WriteLine("##vso[task.logissue type=error]" + ex.Message);
Console.WriteLine("##vso[task.logissue type=error]" + ex.StackTrace);
}
return succeeded;
}
}
}
No you don't need a ton of PS scripts to achieve this. MSDeploy.exe is an incredibly useful tool that can probably cover your needs. Add the /t:Package build argument to your VS build task to create a package. Then use a Commandline task to deploy the MSDeploy package to your IIS site. Here are more details about out WebDeploy/MSDeploy works:
http://www.dotnetcatch.com/2016/02/25/the-anatomy-of-a-webdeploy-package/
I do this all of the time. What I did was setup a Release in the Release tab and signed up to enable Deployment Groups. Once you have a the Deployment Group enabled on your account (needed contact MS to get this enabled). I could download PS script that I run on each of the machines that I want to deploy to. Then in the Release screen I can setup the steps to run in a Deployment Group and then the various publish tasks run on the local server allowing them to work.
Using the Deployment Groups is an excellent solution because if you have it load balanced it will deploy to only a portion of the load balanced servers at a time. Allowing the app to stay up the whole time.

Get website's ID in IIS 6 using the command line

I need to get the website's ID by its name in IIS 6 in Windows server 2003. I did it using C# but later discovered that the server doesn't have the .Net framework installed. I just know the name of the website.
string directoryEntry = string.Format("IIS://localhost/w3svc");
var w3Svc = new DirectoryEntry(directoryEntry);
foreach (DirectoryEntry de in w3Svc.Children) {
if (de.SchemaClassName == "IIsWebServer" && de.Properties["ServerComment"][0].ToString() == "MyWebsiteName") {
Environment.ExitCode = Convert.ToInt32(de.Name);
return;
}
}
int id = Process.Start("MyApp.exe");
I have to run a batch file to start and stop the website on a remote machine. I am able to run the batch to start and stop the website by using PSexec but I don't know how to get the website's ID dynamically. Can anybody please help me with this?
Please let me know if I am not clear enough or if I need to provide more info on this.
It looks like IisApp.vbs is the recomended option. Check out the documentation for it. Sorry if this isn't what you needed.

Windows Azure Exception: "Access to the path XYZ.exe is denied."

I use local storage on Windows Azure to store temporary files. In there I call an .exe file to make a conversion of several other files in same local storage folder. Problem is I always get the exception "Access to the path XYZ.exe is denied.".
I should mention the following:
- I am using a worker role
- set in the service definition file
and tried to add permission to the folder I am accessing:
public static void AddPermission(string absoluteFolderPath)
{
DirectoryInfo myDirectoryInfo = new DirectoryInfo(absoluteFolderPath);
DirectorySecurity myDirectorySecurity = myDirectoryInfo.GetAccessControl();
myDirectorySecurity.AddAccessRule(new FileSystemAccessRule(
"NETWORK SERVICE",
FileSystemRights.FullControl,
AccessControlType.Allow));
myDirectoryInfo.SetAccessControl(myDirectorySecurity);
}
UPDATE:
I tried with this code now:
public static void FixPermissions()
{
var tempDirectory = RoleEnvironment.GetLocalResource("localStorage").RootPath;
Helper.addPermission(tempDirectory);
var dir = new DirectoryInfo(tempDirectory);
foreach (var d in dir.GetDirectories())
Helper.addPermission(d.FullName);
}
private static void addPermission(string path)
{
FileSystemAccessRule everyoneFileSystemAccessRule = new FileSystemAccessRule("Everyone",
FileSystemRights.FullControl,
InheritanceFlags.ContainerInherit | InheritanceFlags.ObjectInherit,
PropagationFlags.None, AccessControlType.Allow);
DirectoryInfo directoryInfo = new DirectoryInfo(path);
DirectorySecurity directorySecurity = directoryInfo.GetAccessControl();
directorySecurity.AddAccessRule(everyoneFileSystemAccessRule);
directoryInfo.SetAccessControl(directorySecurity);
}
I get a really strange behaviour of the page. I still get the errors but sometimes some files gets converted by the ffmpeg.exe file.
Can someone help me out here??
Thanks a lot.
SOLUTION:
So seems the problem was that I ran the .exe file within local storage and therefore had the given security issues. Putting the .exe into the application and referring directly solved my issue.
Thx for your help.
By default your worker role will most likely not be running with sufficient privilege to allow changes to the access control lists on Azure folders.
There's two possible options:
Best: run a script at startup to set the permissions. Details are on MSDN here: http://msdn.microsoft.com/en-us/library/gg456327.aspx. You'll want to set executionContext="elevated".
The best way to write the script itself is through Powershell. An example is here: http://weblogs.thinktecture.com/cweyer/2011/01/fixing-windows-azure-sdk-13-full-iis-diagnostics-and-tracing-bug-with-a-startup-task-a-grain-of-salt.html. Alternatively, write a console application to do the same thing.
Easiest, but much less secure: set the security in your OnStart method, and run your whole worker role elevated: in your service definition file include
<WebRole name="WebApplication2">
<Runtime executionContext="elevated" />
<Sites>
However, I'd really not recommend that as it's a terrible security hole for something that's running in the public cloud.

Retrieving IIS Logs from Azure

I have been trying to get IIS Logs from Azure, and I was able to get to get it going once - now, no matter what I try, I can't get it to transfer logs to my Storage account.
I was trying to do this without re-deploying my code, which after reading around seemed possible. And, as I mentioned, I was successful. But this is driving me insane, it just won't do it anymore. Although, it does create the Queue in my storage account when I start a transfer, but that's all it seems to do.
The basic steps I am doing are:
Adding the storage name and key to my config as "DiagnosticsConnectionString"*.
Setting a DiagnosticMonitorConfiguration for one minute, with a DirectoriesBufferConfiguration.
Starting an OnDemand Transfer with a new queue name.
I've done all of the above both programmatically, and through the cmdaplets for PowerShell. As soon as I start a transfer, it just stays with a status of "Not Yet Published (Do not end/cancel)".
I have tried Logs, Directories and even deleted and recreated my storage account. Nothing seems to be working. It appeared to work when I directly added my storage account info to my role config via the azure portal; after it Updated the deployment I saw the logs. But this is not working anymore. Does anyone have some good advice/material? I just want to transfer my IIS logs to my storage account - I've been at it for days.
Update:*This is my config: . My WebRole.cs contained the following, when it worked:
DiagnosticMonitor.Start("DiagnosticsConnectionString");
I've updated it to start transfers:
var diag = new DiagnosticMonitorConfiguration()
{
ConfigurationChangePollInterval = TimeSpan.FromMinutes(1),
Directories = new DirectoriesBufferConfiguration()
{
ScheduledTransferPeriod = TimeSpan.FromMinutes(1)
},
Logs = new BasicLogsBufferConfiguration()
{
ScheduledTransferLogLevelFilter = LogLevel.Verbose,
ScheduledTransferPeriod = TimeSpan.FromMinutes(1)
}
};
DiagnosticMonitor.Start("DiagnosticsConnectionString", diag);
Change one line:
From
var diag = new DiagnosticMonitorConfiguration()
to
var diag = DiagnosticMonitor.GetDefaultInitialConfiguration()
Afterwords, use the existing objects within the diag and not add your own.
This is my OnStart:
var config = DiagnosticMonitor.GetDefaultInitialConfiguration();
config.DiagnosticInfrastructureLogs.ScheduledTransferLogLevelFilter = LogLevel.Information;
config.DiagnosticInfrastructureLogs.ScheduledTransferPeriod = TimeSpan.FromMinutes(1);
config.Directories.ScheduledTransferPeriod = TimeSpan.FromMinutes(1);
DiagnosticMonitor.Start("DiagnosticsConnectionString", config);
It could be that the logs are not being generated, rather than a problem at the download time.
There is a progam called AzureLogFetcher that may help, tips on getting logging to work can be found here

Resources