Thursday, 29 September 2016

Create a file when publishing using a .pubxml file

Customise the pubxml file
I have searched the interwebs far and wide and the documentation on customising the pubxml file is ridiculously limited. Even in this day and age of automation.

What is the pubxml file? 
The pubxml file is what Visual Studio/MSBuild uses to publish a project. This may include as a basic description, the location of where the files will go, what build configuration (Debug, Release etc.) to use, what method to use (FileSystem, MSDeploy etc) etc.

Example of a basic template
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <WebPublishMethod>FileSystem</WebPublishMethod>
    <LastUsedBuildConfiguration>Release</LastUsedBuildConfiguration>
    <LastUsedPlatform>Any CPU</LastUsedPlatform>
    <SiteUrlToLaunchAfterPublish />
    <LaunchSiteAfterPublish>True</LaunchSiteAfterPublish>
    <ExcludeApp_Data>False</ExcludeApp_Data>
    <publishUrl>C:\Deployments\SomeName\</publishUrl>
    <DeleteExistingFiles>false</DeleteExistingFiles>
  </PropertyGroup>
</Project>
The UI that is provided in Visual Studio does not expose what can be created in the PubXml file. In this post I just want to focus on creating a file during the publish process.

Scenario
For an example I want to create two files one will be a nuspec file and another a powershell script. Contents are not important for now.

To create the file(s) you need to use a custom Target node. Example below,

<Target Name="CreateFiles" AfterTargets="GatherAllFilesToPublish">
    <Message Text="Creating Powershell script" Importance="high"></Message>
    <WriteLinesToFile File="$(PublishUrl)CustomCreatedNuspecFile.nuspec" Overwrite="true" Lines="[Contents to go into this file]">
    </WriteLinesToFile>
    <WriteLinesToFile File="$(PublishUrl)AGoodPowerShellScript.ps1" Overwrite="true" Lines="[Content to go into this file]">
    </WriteLinesToFile>
  </Target>

That's it. As you can see its pretty straightforward. The AfterTargets is the crucial part as this tells the publish process when to execute the Target node. I have tried to find a definitive list of what can go in here, except for AfterBuild, BeforeBuld, CopyAllFilesToSingleFolderForMsdeploy, GatherAllFilesToPublish and custom Target names (like CreateFiles) I cannot find one.

However I have found out that if you look in your own system there is a file which contains some information. For the case if you have Visual Studio 2015 installed go to
C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\v14.0\Web\Microsoft.Web.Publishing.targets 
Which supposedly contains it all but you have to work out what each one does, its over 4500 lines long, so good luck with that.

Ok, more clarity of what Target can do.

So say you want to get the version of the assembly your publishing from and spit that out in a text file. You can put the below xml into the pubxml file.

<Target Name="AfterCompile">
    <GetAssemblyIdentity AssemblyFiles="$(ProjectDir)obj\$(ConfigurationName)\$(TargetFileName)">
      <Output TaskParameter="Assemblies" ItemName="AssemblyInfo" />
    </GetAssemblyIdentity>
    <PropertyGroup>
      <ApplicationVersion>%(AssemblyInfo.Version)</ApplicationVersion>
    </PropertyGroup>
</Target>
<Target Name="CreateVersionFile" DependsOnTargets="AfterCompile" AfterTargets="GatherAllFilesToPublish">
    <Message Text="Creating text file" Importance="high"></Message>
    <WriteLinesToFile File="$(PublishUrl)ProjectVersion.txt" Overwrite="true" Lines="Version : $(ApplicationVersion)">
    </WriteLinesToFile>
</Target>

Again I have tried to find a list of all the $(..) parameters that there are and again I can not find one. Its one of those things that if you search for specifically what you want and hopefully someone out there has written/answered a question about it.

Finally you can take this a little further and create a file using the below example.

<Target Name="CreateFile" AfterTargets="GatherAllFilesToPublish ">
    <ItemGroup>
      <Line Include="line01">
        <Text>
          First Line
        </Text>
      </Line>
      <Line Include="line02">
        <Text>
          Second Line
        </Text>
      </Line>
      <Line Include="line03">
        <Text>
          Third Line
        </Text>
      </Line>
      <LineText Include="%(Line.Text)" />
    </ItemGroup>
    <WriteLinesToFile File="$(PublishUrl)ProjectVersion.txt" Lines="@(LineText)" Overwrite="true"/>
</Target>

With this method you can control each line, if you want to, or just have one Line node that has all your text in there like below

<Target Name="CreateFile" AfterTargets="GatherAllFilesToPublish ">
    <ItemGroup>
      <Line Include="line01">
        <Text>
          First Line
          Second Line
          Third Line
        </Text>
      </Line>
      <LineText Include="%(Line.Text)" />
    </ItemGroup>
    <WriteLinesToFile File="$(PublishUrl)ProjectVersion.txt" Lines="@(LineText)" Overwrite="true"/>
</Target>
Either method of creating a file is fine just pick one that suits you. Just make sure that if someone else picks it up its clear and makes sense.

Also, I have seen a lot of Stackoverflow answers say that AfterPublish is a target that is available, which will fire after the publish has completed. I don't think that is true and has resulted in a lot of copy and paste of a supposedly correct answer. What you can do is create a target called AfterPublish and then you can set that in another Target's AfterTargets or DependsOnTargets attribute.

Tuesday, 30 August 2016

Create ECDSA Certificate using OpenSSL

Create a Public and Private key PFX and Certificate file in simple steps.

Assuming that you have OpenSSL installed in a windows environment. Commonly its C:\Program Files (x86)\GnuWin32\bin>. For simplicity move the openssl.cnf to the local C:\ drive so it can be referenced from there.

Start a Command window as Administrator and navigate to the OpenSSL exe. (C:\Program Files (x86)\GnuWin32\bin>)

--Start the process, pick your curve you want (this one is prime256v1)
openssl ecparam -genkey -name prime256v1 -out privatekey.pem

--Next step is to create the Public certificate which will ask you a few questions.
openssl req -new -x509 -key privatekey.pem -out publickey.cer -days 365 -config c:\openssl.cnf

--Final step creates the Private pfx file
openssl pkcs12 -export -out public_privatekey.pfx -inkey privatekey.pem -in publickey.cer

You will end up with a few files which will be the Public certificate and Private PFX file.

Thursday, 11 August 2016

Asp Net Core MVC Change Folder Name And Now The Site Wont Run Anymore

So, you created a nice new project and realise that you spelt something wrong in the folder structure, something like the example below.

C:\Git\XmlXsltValidator

When is should have been...


C:\Git\XmlXsdValidator

Now this should be a simple thing of renaming the folder, updating whatever source control you use and its back to work again.

Unfortunately it's not, when I first did this by creating new project in Visual Studio 2015, corrected the folder name and then tried to run the project again I got a error dialog appear directing me to where the error was logged. 

A snippet of the error logged is below.
HTTP Error 500.19 - Internal Server Error
Config Error   Cannot read configuration file
Config File   \\?\C:\Git\XmlXsltValidator\src\XmlXsltValidator\web.config

To fix this I opened up the applicationhost.config, following the path described above, and then scrolled down to the system.applicationHost\sites node and noticed that were three sites which one was pointing to the old folder location. I removed this element, closed and reopened the solution and it was working again.

Incorrect XML
<site name="WebSite1" id="1" serverAutoStart="true">
                <application path="/">
                    <virtualDirectory path="/" physicalPath="%IIS_SITES_HOME%\WebSite1" />
                </application>
                <bindings>
                    <binding protocol="http" bindingInformation=":8080:localhost" />
                </bindings>
            </site>
            <site name="XmlXsltValidator" id="2">
                <application path="/" applicationPool="Clr4IntegratedAppPool">
                    <virtualDirectory path="/" physicalPath="C:\Git\XmlXsltValidator\src\XmlXsltValidator" />
                </application>
                <bindings>
                    <binding protocol="http" bindingInformation="*:61097:localhost" />
                </bindings>
            </site>
            <site name="XmlXsdValidator" id="3">
                <application path="/" applicationPool="Clr4IntegratedAppPool">
                    <virtualDirectory path="/" physicalPath="C:\Git\XmlXsdValidator\src\XmlXsdValidator" />
                </application>
                <bindings>
                    <binding protocol="http" bindingInformation="*:61097:localhost" />
                </bindings>
            </site>



Correct XML

<site name="WebSite1" id="1" serverAutoStart="true">
                <application path="/">
                    <virtualDirectory path="/" physicalPath="%IIS_SITES_HOME%\WebSite1" />
                </application>
                <bindings>
                    <binding protocol="http" bindingInformation=":8080:localhost" />
                </bindings>
            </site>
            <site name="XmlXsdValidator" id="2">
                <application path="/" applicationPool="Clr4IntegratedAppPool">
                    <virtualDirectory path="/" physicalPath="C:\Git\XmlXsdValidator\src\XmlXsdValidator" />
                </application>
                <bindings>
                    <binding protocol="http" bindingInformation="*:61097:localhost" />
                </bindings>
            </site>

Wednesday, 3 August 2016

Javascript getYear() is not returning correct year

getYear() has a Y2K bug so if you need the actual year then call the getFullYear() method.

Tuesday, 2 August 2016

[DBNETLIB][ConnectionOpen (Connect()).]SQL Server does not exist or access denied.

Getting this issue?

Goto Protocols for MSSQLSERVER and enable TCP/IP and check the IP configuration is enabled