Let’s imagine this typical scenario: You are an indie developer or a company developing apps in Visual Studio. You have already developed and use several own useful libraries across various projects. Right now you are maintaining each of these libraries in separate Git/TFS repository and adding these libraries as submodules to your projects. Because these libraries are hierarchically dependent one on another, it’s quite hard to maintain the dependencies and changes properly. You’ve thought about packaging these libraries as separate NuGet packages, but adding them to public nuget.org gallery is not an option since these libraries contain private knowhow.
In this article I’ll demonstrate, how to deploy your own NuGet Server on free instance of Azure Website, secure it with Basic HTTP Authentication, setup this new server so it can be used from Visual Studio, easily create automatic PowerShell scripts for deploying these libraries as NuGet packages to your server, use these NuGet packages in your projects and also how to achieve all this in just couple of minutes!
Step 1. New Azure Website
This guide uses for hosting private NuGet server free instance of Azure Website. Installing private Nuget server on virtual machine and IIS is really not much different.
Step 1.1 Azure account
In case you don’t have Azure account already, you can create one here for free.
For this article you don’t need to use any payed Azure service, private NuGet server can run just fine in free Azure Website instance, but if you need better availability, custom domain or SSL, then you need to switch to one of payed tiers.
With a valid Azure account the first step is creating new Azure Website, where the NuGet server will be hosted. Click New in the Website section, pick any free name in the URL field and then pick any hosting plan and location. You can choose the Free Website plan and change it later to payed one.
The new website is created in just couple of minutes.
Step 2. New ASP.NET Web project in Visual Studio
The private NuGet server will be deployed as a simple ASP.NET Web site, so just open Visual Studio 2013 and create empty ASP.NET Web project in the Cloud section:
In the next dialog just select vanilla empty ASP.NET project. Note the “Host in the cloud” option should be unchecked, you will be using website already created in the previous step.
Step 3. Adding and configuring the NuGet.Server package
Once the empty ASP.NET project is created in Visual Studio, the next step is adding the NuGet.Server package to it… from NuGet. It sounds a bit meta, but it’s actually really simple. Folks from the nuget.org created single NuGet package for deploying your own NuGet server. You can also check the original guide as a reference.
So far it was just a simple Wizard like guide without really any configuration, but this changes now. Once the NuGet.Server package is added including all dependencies, you need to configure it a bit.
First you need to configure the web.config file with your unique API key to secure the deployment of your new NuGet packages. New Guid can be generated simply using nguid ReSharper snippet.
The other web.config switches are optional, more information about those switches is in the original NuGet.Server article.
Once the API key is configured, you can test the build and deployment of the project to Azure. Right click the Project and choose Publish.
Here just log in with your Azure credentials and you should get this dialog with list of your Azure Websites.
Note if you don’t see the Publish option in Visual Studio, you probably need to install the latest Azure Tools for Visual Studio 2013
Once you are logged in, you can just press Publish and after few seconds your own NuGet server is deployed and running in the cloud, yay!
Step 4. Adding Basic HTTP Authentication
So far it was just a quite simple guide, but it’s time for some actual customization. Publishing private NuGet server to the cloud is great, but the problem with Azure website is that anyone can access the repository once the Uri is revealed. One solution, how to make the NuGet server available only for selected developers, is adding Basic HTTP Authentication.
How Basic HTTP Authentication works in a nutshell:
The server sends back a header stating it requires authentication for a given realm. The user provides the username and password, which the browser or client concatenates (username + “:” + password), and base64 encodes. This encoded string is then sent using a “Authorization”-header on each request from the browser. Because the credentials are only encoded, not encrypted, this is highly insecure unless it is sent over https.
Securing the NuGet site with basic authentication can be done by following this guide. Start with adding BasicAuthenticationModule to the project + few supplemental configuration files that can be found here, and then finally by adding configuration for the authentication in web.config file:
<configSections> <section name="basicAuth" type="Devbridge.BasicAuthentication.Configuration.BasicAuthenticationConfigurationSection" /> ... </configSections> ... <basicAuth> <credentials> <add username="nugettest42" password="42nugettest"/> </credentials> </basicAuth> <system.webServer> <validation validateIntegratedModeConfiguration="false" /> <modules runAllManagedModulesForAllRequests="true"> <add name="MyBasicAuthenticationModule" type="Devbridge.BasicAuthentication.BasicAuthenticationModule" /> ... </modules> ... </system.webServer>
After rebuilding and republishing the project to Azure this is what you get if no error occurs.
No one can access you packages now without knowing the username and password.
Step 5. Publishing NuGet packages to the private NuGet server
The NuGet server that was just created is up and running. The next step is creating NuGet package for libraries you actually use.
In this sample let’s create .NET 4.5 library for generating numbers based on seed 42.
This library is then consumed by simple .NET 4.5 WPF project.
The library is now used as a project reference, so let’s change it to a proper NuGet package.
Step 5.1 Creating NuGet package for the library
How to create NuGet packages is described thoroughly on nuget.org website, in short it’s necessary to create .nuspec metadata file describing the package name, version, all included dlls and other required data. This file can be edited either as a XML document in any text editor, or using NuGet Package Explorer tool, if you prefer editors with UI. Running in command line this command produces the actual .nupkg file:
nuget pack MyRandomGenerator.nuspec
Note you can run this command either in NuGet Package Manager Console or in cmd.exe or PowerShell. In case of the latter you need to download the nuget.exe executable and place into folder, that is included in PATH Environment variable.
Pushing the nupkg file to either official nuget.org gallery or to own NuGet server is done by running:
nuget push MyRandomGenerator.nupkg
Step 5.2 Adding your NuGet server into Visual Studio
In order to use your new NuGet server as a package source in Visual Studio, you need to add it first into the list of sources, so open Visual Studio and go to Tools -> Options:
If you look back at the last screenshot in Step 3, these are the Uris you need to use.
Note, the package sources can be added either using Visual Studio Options dialog, or using command line. In our example where NuGet Server requires Basic HTTP Authentication you have to use the command line option, because the dialog in Options does not support adding password protected remote sources. So let’s run this in NuGet Package Manager Console:
nuget sources add -Name "NugetTest 42" -Source "https://nugettest42.azurewebsites.net/nuget" -UserName "nugettest42" -Password "42nugettest" nuget sources add -Name "NugetTest 42 Publish" -Source "https://nugettest42.azurewebsites.net/" -UserName "nugettest42" -Password "42nugettest"
Step 5.3 Uploading NuGet package to your NuGet server
There are several ways, how to upload your MyRandomGenerator.nupkg to your NuGet server:
– the file can be either included directly in the Web app project in the Packages folder
– or it can be uploaded directly to the server using filetransfer via FTP
– or using the nuget.exe:
nuget push MyRandomGenerator.nupkg -Source https://nugettest42.azurewebsites.net/ -ApiKey fd9907c1-22c1-42c4-b9c6-b485684ea25c
Step 6. Automatic scripts for publishing NuGet packages
The previous Step shows, how to pack your .NET library as a NuGet package, upload it to your NuGet server and add your server into Visual Studio as a package source. So far it was quite lot of a manual work. To make the process a single click user experience, the following guide shows, how to pack all these steps in two Powershell scripts.
First script called 0_InitSources.ps1 contains methods for adding both NuGet server sources to Visual Studio. This is useful for initial configuration of your environment when joining the developer team.
# initialize Write-Host "`nInitializing NuGet sources" -foregroundcolor Green nuget sources add -Name "NugetTest 42" -Source "https://nugettest42.azurewebsites.net/nuget" -UserName "nugettest42" -Password "42nugettest" nuget sources add -Name "NugetTest 42 Publish" -Source "https://nugettest42.azurewebsites.net/" -UserName "nugettest42" -Password "42nugettest" # wait for user input Write-Host "`nDone!" -foregroundcolor Green Pause
Second script called 1_PushNewVersion.ps1 contains logic for single click rebuilding of your MyRandomGenerator library, packing it into NuGet package and publishing it to your NuGet server. This second script also contains interactive dialog where the user can enter version number of the new NuGet package and also release notes for this new package.
Note that the second script is based on New-NuGetPackage PowerShell Script which contains all important and useful methods that you might need when building automated NuGet deployment scrips:
$nuspec = "MyRandomGenerator.nuspec" $solution = "..\NuGetDemo.sln" $config = "Release" $msbuild = "C:\Program Files (x86)\MSBuild\12.0\bin\MSBuild.exe" $pushoptions = "-Source ""https://nugettest42.azurewebsites.net/"" -ApiKey ""fd9907c1-22c1-42c4-b9c6-b485684ea25c""" set-alias msbuild $msbuild # clean Write-Host "`nCleaning solution" -foregroundcolor Green msbuild $solution /t:Clean /p:Configuration=$config /verbosity:m /nologo # build Write-Host "Building solution`n" -foregroundcolor Green msbuild $solution /t:Build /p:Configuration=$config /verbosity:m /nologo # create NuGet package Write-Host "`nPublishing NuGet package" -foregroundcolor Green .\New-NuGetPackage.ps1 -NuSpecFilePath $nuspec -PushPackageToNuGetGallery -PushOptions $pushoptions # wait for user input Write-Host "`nDone!" -foregroundcolor Green Pause
In short this script builds your project and calls the New-NuGetPackage.ps1 with the path of your nuspec file and path to your server, it’s really that simple.
For configuring the PowerShell scripts with other projects you just need to change the path of your NuGet server, API key, name, password and path to your project and nuspec file. The rest is done automatically.
Summary
It’s really easy to deploy your own NuGet server from Visual Studio in cloud on Azure Website, add it as package source to Visual Studio and build new package of your library with just a single click, so if you have been wondering, how hard it could be to deploy something similar on your own, just grab the source code and try it yourself 🙂
Why not just use a grown-up server like ProGet?
because they are expensive?
Because this solution is free, open source, easy to deploy and does what I want 🙂
How are the packages persited on the server? As far as I know (I don’t know much 🙂 ) azure websites is not a file storage and my NuGet server should store them in a blobstorage.
I have the same question, have you found the solution for this?
Assuming you’re using the default NuGet.Server configuration to store and server packages from ~/packages vdir then the files will be uploaded in wwwrootpackages of your Azure Website which is persisted with your website. For free websites you get 1GB worth of storage in wwwroot. To understand this better, connect to your website using FTP (you can get credentials from the Azure Management portal) and you’ll be able to browse the directory structure.
FYI…the NuGet command-line utility is probably not automatically be installed in everyones Visual Studio solution. As such, running commands will fail in the Package Manager Console.
To run the commands mentioned in this article install the NuGet command-line in the Package Manager Console:
Install-Package NuGet.CommandLine
– Enjoy
FYI…use the following command-line to push your packages:
nuget push {packagefile} -s {NuGetServerUrl} {apikey}
Pingback: Handling platforms | Being Lost...
Great post! When I try it i get promted for the username when push with the apikey. Any ideas?
If you get prompted for username & password when you push with the apikey it’s because you haven’t registered the credentials with your local nuget.exe client (see 0_InitSources.ps1 above)
I just tried this, but whenever I try to download a package it always asks me for credentials and the ones I set on the web.config file do not work. What can I do in that case?
Great write-up! One thing I would add in step 4 (basic authentication) is an auto-redirect to HTTPS. As long as you access an Azure Website using its *.azurewebsites.net hostname you can use HTTPS (even the free tier) because Microsoft provisioned a wildcard certificate. So throughout your example you should be using https://nugettest42.azurewebsites.net/nuget. To enable auto-redirect to the HTTPS endpoint add this in web.config system.webServer section:
I’m considering doing this and as I was reading this, I was thinking to myself: I sure hope basic auth w/o TLS is not my only option!!
Pingback: How To Deploy A Website On A Server | Information
BasicAuth is having an issue. The event log shows “Exception message: Server cannot set status after HTTP headers have been sent.” This happens each time a Nuget Push or Nuget Delete is executed. Push command returns InternalServerError (3x) and finally “response status code does not indicate success: 500 (Package Vusion.Exporter 1.1.8.28398 already exists. The server is configured to not allow overwriting packages that already exist.)”. The package is indeed pushed even with the error message. Before anyone asks, yes I’m sure the package does not exist on server first. Nuget Install seems to work fine. Also, I tested with BasicAuth module disabled and it works fine. And yes, I have sources set with credentials in nuget.config
Can anyone help?
The full exception is …
Exception information:
Exception type: HttpException
Exception message: Server cannot set status after HTTP headers have been sent.
at System.Web.HttpResponse.set_StatusCode(Int32 value)
at Devbridge.BasicAuthentication.BasicAuthenticationModule.IssueAuthenticationChallenge(Object source, EventArgs e) in C:UserschammonsGoogle DriveGitReposvusionpackagesNugetServerBasicAuthenticationModule.cs:line 149
at System.Web.HttpApplication.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)
Request information:
Request URL: http://localhost:52812/
Request path: /
User host address: ::1
User:
Is authenticated: False
Authentication Type:
Thread account name: PEOPLENETONLINEchammons
Thread information:
Thread ID: 12
Thread account name: PEOPLENETONLINEchammons
Is impersonating: False
Stack trace: at System.Web.HttpResponse.set_StatusCode(Int32 value)
at Devbridge.BasicAuthentication.BasicAuthenticationModule.IssueAuthenticationChallenge(Object source, EventArgs e) in C:UserschammonsGoogle DriveGitReposvusionpackagesNugetServerBasicAuthenticationModule.cs:line 149
at System.Web.HttpApplication.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)
I did not solve this but did find a work around. Since nuget.server already authenticates push and delete by requireing an apikey we do not need basicauth for that. Just install, list and other that don’t require an apikey. I added the below to config to exclude some urls …
Step 4 works perfectly on localhost but I don’t know why it doesn’t with Azure web app, no login form popup.