#Install-Module -Name 'WebAdministration'
Import-Module -Name WebAdministration
function AddSelfSignedCertificateToSSL([String]$dnsname, [String]$siteName='Default Web Site'){
$newCert = New-SelfSignedCertificate -DnsName $dnsname -CertStoreLocation Cert:\LocalMachine\My
$binding = Get-WebBinding -Name $siteName -Protocol "https"
$binding.AddSslCertificate($newCert.GetCertHashString(), "My")
$newCertThumbprint = $newCert.Thumbprint
$sourceCertificate = $('cert:\localmachine\my\' + $newCertThumbprint)
$store = new-object system.security.cryptography.X509Certificates.X509Store -argumentlist "Root", LocalMachine
$store.Open([System.Security.Cryptography.X509Certificates.OpenFlags]"ReadWrite")
$store.Add($newCert)
return $newCertThumbprint
}
Write-Host Installing self-signed certificate Cert:\LocalMachine\My and Cert:\LocalMachine\Root ..
$certinstalledThumbprint = AddSelfSignedCertificateToSSL 'someacmeapp.somedomain.net'
Write-Host Added certificate $certinstalledThumbprint to Cert:\LocalMachine\My and Cert:\LocalMachine\Root and set this up as the SSL certificate on Default Web Site.
Wednesday, 10 June 2020
Creating a self signed certificate with Powershell and preparing it for IIS
I just wrote an automated routine in Powershell to create a self signed certificate.
Thursday, 28 May 2020
Creating an AngularJs directive for a horizontal scroller at top and bottom of HTML container element
I made an AngularJs directive today that adds a horizontal scroller at top and bottom of an HTML container element, such as text area, table or div.
The AngularJs directive uses the link function of AngularJs to prepend and wrap the necessary scrolling mechanism and add some Javascript scroll event handlers using
jQuery.
import angular from 'angular';
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope, $compile) {
$scope.name = 'Dual wielded horizontal scroller';
});
app.directive('doubleHscroll', function($compile) {
return {
restrict: 'C',
link: function(scope, elem, attr){
var elemWidth = parseInt(elem[0].clientWidth);
elem.wrap(`<div id='wrapscroll' style='width:${elemWidth}px;overflow:scroll'></div>`);
//note the top scroll contains an empty space as a 'trick'
$('#wrapscroll').before(`<div id='topscroll' style='height:20px; overflow:scroll;width:${elemWidth}px'><div style='min-width:${elemWidth}px'> </div></div>`);
$(function(){
$('#topscroll').scroll(function(){
$("#wrapscroll").scrollLeft($("#topscroll").scrollLeft());
});
$('#wrapscroll').scroll(function() {
$("#topscroll").scrollLeft($("#wrapscroll").scrollLeft());
});
});
}
};
});
The HTML that uses this directive, restricted to 'C' (class) is then simply using the class 'double-Hscroll' following AngularJs 'snake escaping' naming convention of capitalization and dashes.
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="lib/style.css" />
<script src="lib/script.js"></script>
<script
src="https://code.jquery.com/jquery-3.5.1.js"
integrity="sha256-QWo7LDvxbWT2tbbQ97B53yJnYU3WhH/C8ycbRAkjPDc="
crossorigin="anonymous"></script>
</head>
<body ng-app="plunker" ng-cloak>
<div ng-controller="MainCtrl">
<h1>Hello {{name}}</h1>
<p>Dual horizontal scroll top and below a text area.</p>
<textarea noresize class="double-hscroll" rows="10" cols="30">
lorem ipsum dolores lorem ipsum dolores
lorem ipsum dolores
lorem ipsum dolores sit amen
lorem ipsum dolores
lorem ipsum dolores sit amen
lorem ipsum dolores
lorem ipsum dolores amen sit
</textarea>
</div>
</body>
</html>
Etiketter:
AngularJs,
Directive,
Javascript,
Js,
Webdev
Tuesday, 7 April 2020
Writing to Event Log in .Net Core (Tested with .Net Core 3.1)
Writing to the Event Log in .Net Core requires first a Nuget package installation
Install-Package Microsoft.Extensions.Logging.EventLog -Version 3.1.2Note that the correct version to install depends on the version of .Net Core you are running.The package above was tested OK with .Net Core. Then we need to add EventLog. In the Program class we can do this like so:
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.EventLog;
namespace SomeAcme.SomeApi
{
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureLogging((hostingContext, logging) =>
{
logging.ClearProviders();
logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
logging.AddEventLog(new EventLogSettings()
{
**SourceName = "SomeApi",
LogName = "SomeApi",**
Filter = (x, y) => y >= LogLevel.Warning
});
logging.AddConsole();
})
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup();
});
}
}
And our appsettings.json file includes setup:
{
"ConnectionStrings": {
"DefaultConnection": "Server=.\\SQLEXPRESS;Database=SomeApi;Trusted_Connection=True;MultipleActiveResultSets=true"
},
**"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},**
"AllowedHosts": "*"
}
We can inject the ILogger instance
using SomeAcme.SomeApi.SomeModels;
using SomeAcme.SomeApi.Services;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using System.Collections.Generic;
namespace SomeAcme.SomeApi.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class SomeController : ControllerBase
{
private readonly ISomeService _healthUnitService;
private readonly ILogger _logger;
public SomeController(ISomeService someService, ILogger logger)
{
_someService= someService;
_logger = logger;
}
// GET: api/Some
[HttpGet]
public IEnumerable GetAll()
{
return _someService.GetAll();
}
}
}
More advanced use, add a global exception handler inside Configure method of Startup class in .Net Core:
//Set up a global error handler for handling Unhandled exceptions in the API by logging it and giving a HTTP 500 Error with diagnostic information in Development and Staging
app.UseExceptionHandler(errorApp =>
{
errorApp.Run(async context =>
{
context.Response.StatusCode = 500; // or another Status accordingly to Exception Type
context.Response.ContentType = "application/json";
var status = context.Features.Get();
var error = context.Features.Get();
if (error != null)
{
var ex = error.Error;
string exTitle = "Http 500 Internal Server Error in SomeAcme.SomeApi occured. The unhandled error is: ";
string exceptionString = !env.IsProduction() ? (new ExceptionModel
{
Message = exTitle + ex.Message,
InnerException = ex?.InnerException?.Message,
StackTrace = ex?.StackTrace,
OccuredAt = DateTime.Now,
QueryStringOfException = status?.OriginalQueryString,
RouteOfException = status?.OriginalPath
}).ToString() : new ExceptionModel()
{
Message = exTitle + ex.Message,
OccuredAt = DateTime.Now
}.ToString();
try
{
_logger.LogError(exceptionString);
}
catch (Exception err)
{
Console.WriteLine(err);
}
await context.Response.WriteAsync(exceptionString, Encoding.UTF8);
}
});
});
And finally a helper model to pack our exception information into.
using System;
using Newtonsoft.Json;
namespace SomeAcme.SomeApi.Models
{
///
/// Exception model for generic useful information to be returned to client caller
///
public class ExceptionModel
{
public string Message { get; set; }
public string InnerException { get; set; }
public DateTime OccuredAt { get; set; }
public string StackTrace { get; set; }
public string RouteOfException { get; set; }
public string QueryStringOfException { get; set; }
public override string ToString()
{
return JsonConvert.SerializeObject(this);
}
}
}
The tricky bit here is to get hold of a logger inside the Startup class. You can inject ILoggerFactory for this and just do :
_logger = loggerFactory.CreateLoggerWhere _logger is used in the global error handler above. Now back again to the question of how to write to the event log, look at the source code for SomeController above. We inject ILogger here. Just use that instance and it offers different methods for writing to your configured logs. Since we added in the Program class event log, this happens automatically. Before you test out the code above, run the following Powershell script as administrator to get your event log source:();
New-EventLog -LogName SomeApi -SourceName SomeApiWhat I like with this approach is that if we do everything correct, the exceptions pops up inside the SomeApi source nicely and not inside the application event log (clutter IMHO).
Sunday, 5 April 2020
Deploying an SQL Express database in Azure Devops pipeline with YAML and generating and updating the database with migrate scripts using EF Core Code First tools
Here a full example of how I achieved running Integration tests using Sql Express in Azure Devops. I had to use the YAML based pipelines so I could use simonauner's approach using Chocolatey to install Sql Express. Make note that I had to install EF Core tools since I use .Net Core 3.1 in this pipeline. Also note that I generate an EF Code First migration SQL file on the fly so that the rigged SQL Express instance is filled with contents.
Deploy SQL Express instance in Azure Devops, install and generate and run EF Code first migration sql script to update database with schema and seed data using EF Code First tools.
# ASP.NET Core (.NET Framework)
# Build and test ASP.NET Core projects targeting the full .NET Framework.
# Add steps that publish symbols, save build artifacts, and more:
# https://docs.microsoft.com/azure/devops/pipelines/languages/dotnet-core
trigger:
- feature/testability
pool:
vmImage: 'windows-latest'
variables:
solution: '**/*.sln'
buildPlatform: 'Any CPU'
buildConfiguration: 'Release'
steps:
- script: choco install sql-server-express
- task: NuGetToolInstaller@1
- task: VisualStudioTestPlatformInstaller@1
displayName: 'Visual Studio Test Platform Installer'
inputs:
versionSelector: latestStable
- task: NuGetCommand@2
inputs:
restoreSolution: '$(solution)'
- task: DotNetCoreCLI@2
displayName: Build
inputs:
command: build
projects: '**/*.csproj'
arguments: '--configuration Debug' # Update this to match your need
- script: 'dotnet tool install --global dotnet-ef'
displayName: 'Generate EF Code First Migrations SQL Script Update script'
- script: 'dotnet ef migrations script -i -o %BUILD_ARTIFACTSTAGINGDIRECTORY%\migrate.sql --project .\SomeAcme\SomeAcme.csproj'
displayName: 'Generate EF Code First Migrations migrate.sql'
- script: 'sqlcmd -S .\SQLEXPRESS -Q "CREATE DATABASE [SomeAcmeDb]"'
displayName: 'Create database SomeAcmeDb in Azure Devops SQL EXPRESS'
- script: 'sqlcmd -i %BUILD_ARTIFACTSTAGINGDIRECTORY%\migrate.sql -S .\SQLEXPRESS -d SomeAcmeDb'
displayName: ' Run migrate.sql on SQL EXPRESS in Azure Devops'
# PowerShell
# Run a PowerShell script on Linux, macOS, or Windows
- task: PowerShell@2
inputs:
targetType: 'inline' # Optional. Options: filePath, inline
#filePath: # Required when targetType == FilePath
#arguments: # Optional
script: 'gci -recurse -filter *.dll' # Required when targetType == Inline
#errorActionPreference: 'stop' # Optional. Options: stop, continue, silentlyContinue
#failOnStderr: false # Optional
#ignoreLASTEXITCODE: false # Optional
#pwsh: false # Optional
#workingDirectory: # Optional
- task: VSTest@2
displayName: 'VsTest - testAssemblies'
inputs:
testAssemblyVer2: |
**\*SomeAcme.Tests.dll
!**\*TestAdapter.dll
!**\obj\**
vsTestVersion: toolsInstaller
testFiltercriteria: 'Category=IntegrationTest'
runInParallel: false
codeCoverageEnabled: false
testRunTitle: 'XUnit tests SomeAcme solution integration test starting'
failOnMinTestsNotRun: true
rerunFailedTests: false
Etiketter:
Azure Devops,
Chocolatey,
Code First,
DevOps,
EF,
EF Core,
EF Core Code First,
entityframework,
Pipeline,
SQL Express,
YAML
Subscribe to:
Posts (Atom)