TeamCity - Encrypt Connection Strings? - asp.net

I've been fighting with TeamCity and cannot get this command to succeed. (It works fine manually.) I've tried many things and surprisingly Google wasn't much help. All I'm trying to do is encrypt the connection strings in my web.config file after a deploy.
cd C:\Windows\Microsoft.NET\Framework\v4.0.30319
ASPNET_REGIIS -pef "connectionStrings" "{mypath}"
Error: The configuration for physical path '{mypath}' cannot be opened.
I have it as the 2nd build step after my publish as a Command Line runner type.
I should also mention:
Yes, I'm selecting the folder the web.config is in and not the file itself.
Yes, the path is correct assuming it's supposed to be the one on the server. (C:\inetpub\mysite)
Most Importantly: I'm looking for a solution within TeamCity, not something manual or in my site's code.
Thanks!

Related

Is it possible to publish/deploy a web package, using a non-admin account, from the command line only?

The title needs some expansion. In summary, I am finding it impossible to:
Deploy a web site to an IIS 8 host
Using a Web Deploy Package
Using the out-of-the-box publish functionality in VS 2013
Using a non-admin IIS Manager User, which is delegated permission to deploy to the given site
It all seems to come down to one small detail that messes it all up, but I should describe my process up to the point where it all falls apart.
I create a publish profile Publish in VS 2013, configured to publish to a web package. I then fire the following command at a developer command prompt:
msbuild Solution.sln /t:Build /p:DeployOnBuild=true;PublishProfile=Publish;IsDesktopBuild=false
This goes through a build process and I now see the expected package and deployment files in the _PublishedWebsites\Web_Package folder. My next step is run this from the Web_Package folder:
Web.deploy.cmd /Y /M:https://example.blah:8172/MsDeploy.axd /U:user /P:p#44w0rd /A:Basic
This is where the problem comes in. This results in the following expanded command (formatted for ease of reading):
msdeploy.exe
-source:package='.\Web.zip'
-dest:auto,computerName="https://example.blah:8172/MsDeploy.axd",userName="user",password="p#44w0rd",authtype="Basic",includeAcls="False"
-verb:sync
-disableLink:AppPoolExtension
-disableLink:ContentExtension
-disableLink:CertificateExtension
-setParamFile:".\Web.SetParameters.xml"
whose execution results in:
Error Code: ERROR_USER_UNAUTHORIZED
More Information: Connected to the remote computer ("example.blah") using the Web Management Service, but could not authorize. Make sure that you are using the correct user name and password, that the site you are connecting to exists, and that the credentials represent a user who has permissions to access the site. Learn more at: http://go.microsoft.com/fwlink/?LinkId=221672#ERROR_USER_UNAUTHORIZED.
I can fix this problem by manually re-running just the expanded command and tagging on the site parameter to the MsDeploy.axd URL, like so:
msdeploy.exe
-source:package='.\Web.zip'
-dest:auto,computerName="https://example.blah:8172/MsDeploy.axd?site=example.blah",userName="user",password="p#44w0rd",authtype="Basic",includeAcls="False"
-verb:sync
-disableLink:AppPoolExtension
-disableLink:ContentExtension
-disableLink:CertificateExtension
-setParamFile:".\Web.SetParameters.xml"
However, I cannot see any way to have this set through Web.deploy.cmd which was auto-generated by MSBuild. If I try this:
Web.deploy.cmd /Y /M:https://example.blah:8172/MsDeploy.axd?site=example.blah /U:user /P:p#44w0rd /A:Basic
It results in this (again, formatted for ease of reading):
msdeploy.exe
-source:package='D:\DEV\Solution\Web\bin\_PublishedWebsites\Web_Package\Web.zip'
-dest:auto,computerName="https://example.blah:8172/MsDeploy.axd?site",userName="user",password="p#44w0rd",authtype="Basic",includeAcls="False"
-verb:sync
-disableLink:AppPoolExtension
-disableLink:ContentExtension
-disableLink:CertificateExtension
-setParamFile:"D:\DEV\Solution\Web\bin\_PublishedWebsites\Web_Package\Web.SetParameters.xml" example.blah
Error: Unrecognized argument 'example.blah'. All arguments must begin with "-".
Error count: 1.
I can perform this process fine using an admin account. But it seems that the non-admin requires this site= querystring value and the auto-generated Web.deploy.cmd just isn't having any of that.
Am I missing something obvious here? Is there a permission I'm missing on the IIS Management side? I've made sure I have the Management Service Delegation rule set up, as directed in this blog post.
I can't see a way around this using the process I've laid out. My solution here is to simply take the expanded MSDeploy.exe command line and use it directly instead of the generated *.deploy.cmd file.
Of course, if anyone comes along with an actual solution to my original problem, I'll happily mark that one as the answer. Until then, this is my solution.
You can put the /M: parameter in quotes like so:
Web.deploy.cmd /Y "/M:https://example.blah:8172/MsDeploy.axd?site=example.blah" /U:user /P:p#44w0rd /A:Basic

Using psexec to enable asp.net remote access with aspnet_regiis -config+ : File not found?

I need to programatically enable remote config access to various servers that do not have remote access enabled.
I need to enable remote access, read the machine.config, and disable it again.
I'm trying to use psexec as outlined in this question's answers: How to execute a command in a remote computer?
However the aspreg_iis -config+ command is returning a file not found error:
psexec \\server "c:\...Net 4 path...\aspnet_regiis -config+"
The system cannot find the file specified.
Can anyone point out what I am doing wrong here?
If this is not supported, is there another way to accomplish this?
Well that was embarrassingly simple: just omit the quotation marks:
psexec \server c:...Net 4 path...\aspnet_regiis -config+
and it works fine.

(MSDeploy) Deploying Contents of a Folder to a Remote IIS Server

Is it possible to deploy the contents of a folder (plain Html files) to a specific web application on a remote IIS6/7 server?
The following command does not error, but neither does it publish any files to the remote server:
msdeploy.exe -verb:sync -source:dirPath="c:\myHtmlFiles" -dest:auto,ComputerName="http://deploy.mycompany.co.uk/msdeploy?site=TestSite",includeAcls="false",username="administrator",password="myPassword" -enableRule:DoNotDeleteRule -disableLink:AppPoolExtension -disableLink:ContentExtension -allowUntrusted
NOTE:
WebDeploy is correctly installed on the destination server and works happily with packages created from msbuild scripts for .NET projects.
'http://deploy.mycompany.co.uk/msdeploy' is correct for the listening end-point.
The '?site=TestSite' query string was suggested elsewhere, but does not work.
The 'TestSite' web application exists on the target server.
Parameter files and -setParam do not work, and renders errors relating to the source not supporting the parameter 'IIS Web Application Name' if you attempt to set, declare, or provide it.
I just wrote a blog post to answer this at http://sedodream.com/2012/08/20/WebDeployMSDeployHowToSyncAFolder.aspx. From your question it looks like you are pretty familiar with MSDeploy so the answer might be a bit verbose but I wanted people with less knowledge of MSDeploy to be able to understand. I've pasted the answer below.
Web Deploy (aka MSDeploy) uses a provider model and there are a good number of providers available out of the box. To give you an example of some of the providers; when syncing an IIS web application you will use iisApp, for an MSDeploy package you will use package, for a web server webServer, etc. If you want to sync a local folder to a remote IIS path then you can use the contentPath provider. You can also use this provider to sync a folder from one website to another website.
The general idea of what we want to do in this case is to sync a folder from your PC to your IIS website. Calls to msdeploy.exe can be a bit verbose so let’s construct the command one step at at time. We will use the template below.
msdeploy.exe -verb:sync -source:contentPath="" -dest:contentPath=""
We use the sync verb to describe what we are trying to do, and then use the contentPath provider for both the source and the dest. Now let’s fill in what those values should be. For the source value you will need to pass in the full path to the folder that you want to sync. In my case the files are at C:\temp\files-to-pub. For the dest value you will give the path to the folder as an IIS path. In my case the website that I’m syncing to is named sayedupdemo so the IIS path that I want to sync is ‘sayedupdemo/files-to-pub’. Now that give us.
msdeploy.exe –verb:sync -source:contentPath="C:\temp\files-to-pub" -dest:contentPath='sayedupdemo/files-to-pub'
For the dest value we have not given any parameters indicating what server those command are supposed to be sent to. We will need to add those parameters. The parameters which typically need to be passed in are.
ComputerName – this is the URL or computer name which will handle the publish operation
Username – the username
Password – the password
AuthType – the authType to be used. Either NTLM or Basic. For WMSvc this is typically Basic, for Remote Agent Service this is NTLM
In my case I’m publishing to a Windows Azure Web Site. So the values that I will use are:
ComputerName: https://waws-prod-blu-001.publish.azurewebsites.windows.net/msdeploy.axd?site=sayedupdemo
Username: $sayedupdemo
Password: thisIsNotMyRealPassword
AuthType: Basic
All of these values can be found in the .publishSettings file (can be downloaded from Web Site dashboard from WindowsAzure.com). For the ComputerName value you will need to append the name of your site to get the full URL. In the example above I manually added ?site=sayedupdemo, this is the same name as shown in the Azure portal. So now the command which we have is.
msdeploy.exe
–verb:sync
-source:contentPath="C:\temp\files-to-pub"
-dest:contentPath='sayedupdemo/files-to-pub'
,ComputerName="https://waws-prod-blu-001.publish.azurewebsites.windows.net/msdeploy.axd?site=sayedupdemo"
,UserName='$sayedupdemo'
,Password='thisIsNotMyRealPassword'
,AuthType='Basic'
OK we are almost there! In my case I want to make sure that I do not delete any files from the server during this process. So I will also add –enableRule:DoNotDeleteRule. So our command is now.
msdeploy.exe
–verb:sync
-source:contentPath="C:\temp\files-to-pub"
-dest:contentPath='sayedupdemo/files-to-pub'
,ComputerName="https://waws-prod-blu-001.publish.azurewebsites.windows.net/msdeploy.axd?site=sayedupdemo"
,UserName='$sayedupdemo'
,Password='thisIsNotMyRealPassword'
,AuthType='Basic'
-enableRule:DoNotDeleteRule
At this point before I execute this command I’ll first execute it passing –whatif. This will give me a summary of what operations will be without actually causing any changes. When I do this the result is shown in the image below.
After I verified that the changes are all intentional, I removed the –whatif and executed the command. After that the local files were published to the remote server. Now that I have synced the files each publish after this will be result in only changed files being published.
If you want to learn how to snyc an individual file you can see my previous blog post How to take your web app offline during publishing.
dest:auto
In your case I noted that you were using dest:auto, you can use that but you will have to pass in the IIS app name as a parameter and it will replace the path to the folder. Below is the command.
msdeploy.exe
-verb:sync
-source:contentPath="C:\temp\files-to-pub"
-dest:auto
,ComputerName="https://waws-prod-blu-001.publish.azurewebsites.windows.net/msdeploy.axd?site=sayedupdemo"
,UserName='$sayedupdemo'
,Password='thisIsNotMyRealPassword'
,AuthType='Basic'
-enableRule:DoNotDeleteRule
-setParam:value='sayedupdemo',kind=ProviderPath,scope=contentPath,match='^C:\\temp\\files-to-pub$'

How can I shell out of an ASP.NET page and execute a git command?

I want my ASP.NET page to shell out and execute git commands. I put the commands in a bat file which works:
REM cd to the git repo folder
cd c:\temp\mygitrepo
"c:\Program Files\Git\Bin\git.exe" show c090dc4b8b1b3512c1b5363c371e21d810d02f54 -- myfile.txt
If I run my .bat file from a cmd prompt, no problem.
If I run it using System.Diagnostics.Process.Start, I get this error:
RUNTIME_PREFIX requested, but prefix computation failed. Using static fallback
The error is coming from this git file: http://github.com/git/git/blob/master/exec_cmd.c
I use exactly the same technique to run svn.exe commands, no problem.
EDIT 1: From the thread here I've learned that msysgit installs some files in a location associated with the current user, me, instead of all users. The IIS web server is running under another user account. I tried copying some of the git files that caught my eye, like .gitconfig, to other users (in Documents and Settings). No luck. So, I have switched my focus to getting IIS to impersonate me when it launches the git command.
Is it actually an error, or is it just notifying you of a fallback? Based on the code linked, it appears that the command should still execute successfully, only using the static PREFIX rather than the RUNTIME_PREFIX.
If the command is indeed failing, you might want to make sure that any required environment variables are available when starting the process from .NET. You might also need to impersonate a different windows identity to run the command with proper permissions (I am assuming that the ASP.NET identity is restricted as much as possible.) The ProcessStartInfo object provides a few ways to configure the process, including the windows identity, the process verb, and environment variables. Make sure you are providing the proper context that your batch file requires before running it.

How do you deal with connection strings when deploying an ASP.NET site?

Right now our test and production databases are on the same server, but with different names. Deploying has meant editing Web.config to change all the connection strings for the correct database. A step which I forget all too frequently...
We've finally created a new database server for testing, and I'm moving the databases over... but now the server will be different and we'll still need to deal with connection string issues.
I was thinking of managing it via a hosts file, but the thought of switching that on my desktop machine whenever I need to test against production data seems cumbersome at best.
So I'm just wondering if there's a better way out there. Something that would build with a "production" web config for deployment would be ideal...
Use a Web Deployment Project and update the wdproj file (it's just an MSBuild file) with some post build tasks to output the correct .config file. I keep a web.config and web.release.config then use this in the wdproj file:
<Target Name="AfterBuild">
<Copy Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' " SourceFiles="$(SourceWebPhysicalPath)\web.release.config" DestinationFiles="$(OutputPath)\web.config" />
<Delete Files="$(OutputPath)\web.release.config" />
</Target>
More information
A simpler solution some like is using configSource property of appSettings and connectionStrings and then never overwriting that file on the production server.
I usually have three separate web configs: one for my development machine, one for QA, and one for production. The development one connects to my local SQL database (which is firewalled from outside) and it is the default web.config. The others are named web-prod.config and web-qa.config. After publishing I delete the two that I don't need and rename the correct one to web.config. If I forget, the app breaks the first time it attempts to access the database, since the default config references one it can't get to.
Since IIS refuses to serve up a file named .config, I make sure they all end in .config instead of say web.config-prod or web.config-qa.
Here's another thing you can try:
Using SQL Server Configuration Manager, make a db Alias for your development database so that the web.config file can be the same on both your development box and the production server.
I create a database alias on each server to point to the database. I then use this alias in my web.config files. If I need to change which database the application points to, then I change the alias and not the web.config.
For SQL Server, go to SQL Server Configuration Manager > SQL Native Client Configuration > Aliases > Create New Alias.
You can do the same thing with Oracle with the tnsnames file.
have environment folders with separate configs for each environment
deploy out the correct one for the environment
I did this so often, I made the web.config on the production server read-only.
I've been in a few places now that store them in the registry.
There's probably more elaborate ways to do it now but a lot of code I've worked on with a 1.0/1.1 heritage store the strings in the registry.
The registry has a few advantages
It keeps people from deploying the code to the wrong places since machines not configured properly will lack the keys
It eliminates the problem wherein a developer will accidentally package a web.config file with the development connection strings in it (followed by a frantic phone call in the middle of the night wherein it is revealed that the late night sysadmin did not back up the previous web.config and the developer does not know or recall the production strings)
It limits the possibility of a hacker being able to get the connection string by fetching the web.config off of the machine. Plus the registry has more levels of security than the filesystem.
We drive our a deployments from our CI server. We usualy have a seperate file for each location and have the CI server switch to the appropriate config depending on the arguments passed ot it. All the file editing is done in NAnt scripts, so develops can run the sam build on their machine to get their own settings.
I'll put my connection strings in the machine.config on our QA and Production boxes. I'll keep them in the web.config on my dev box for flexibility, though. Then, I'll use a web deployment project to overwrite my dev connection strings with nothing (no connection strings) when deploying to QA. Therefore the QA site relies on the connection strings in machine.config. I still deploy to Production manually to make sure everything succeeds. I do this by manually copying everything from QA (except for web.config) to production.
This kind of task is exactly what build events are designed to address. Think of building as building for a specific target, any target specific configuration should be done there. (with the normal caveat that there are always some exceptions to the rule)
I've recently been leaning towards config manipulation on the continuous integration server. That's because we've had problems with multiple web.config, web.qa.config, web.production.config keeping the 95% of the file that should be the same in sync.
In a nutshell: there's only the one web.config in source control and it's the development configuration (debug friendly, local db, etc.). The build server does the compile, then a deploy to the canary site, then the package for release candidate.
We're using nant, so it's the .build file that has xmlpoke to set debug="false", alter connection strings, and whatever else needs to change in the canary copy and the packaging copy of the web.config.
The build machine's deploy is called "canary" because it's the first thing to die if there's a problem.

Resources