Golo Roden: Einführung in React, Folge 1: Erste Schritte

Die von Facebook entwickelte Open-Source-Bibliothek React dient dem effizienten Rendern der Anzeige in Webanwendungen. Sie verwendet zahlreiche Konzepte der funktionalen Programmierung und basiert auf Komponenten. Wie gelingt der Einstieg?

Norbert Eder: ASPNET Core: Prerendering failed – Cannot find module main-server

Nach dem Ziehen eines .NET Core/Angular-Projektes, basierend auf das .NET Core Angular Starter Template, konnte dieses nicht korrekt ausgeführt werden. Die Datei main-server.js wurde nicht erstellt und somit natürlich nicht geladen werden:

fail: Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware[0]
      An unhandled exception has occurred: 
      Call to Node module failed with error: 
      Prerendering failed because of error: 
      Error: Cannot find module '\..\main-server'

Die Ursache dieses Fehlers liegt darin, dass .NET Core für dieses Projekt nicht im Development-Modus betrieben wird und für diesen Modus entsprechende Tasks konfiguriert sind. Damit alle notwendigen Dateien vollständig generiert werden, ist für dieses Projekt in dessen Eigenschaften die Umgebungsvariable ASPNETCORE_ENVIRONMENT auf Development umzustellen:

.NET Core ASPNETCORE_ENVIRONMENT

Insgesamt stehen die Modi Development, Staging und Production zur Verfügung.

The post ASPNET Core: Prerendering failed – Cannot find module main-server appeared first on Norbert Eder.

AIT: Neu in TFS 2018: Keine XAML Builds mehr unterstützt

In diesem kurzen Beitrag möchten wir darauf aufmerksam machen, dass mit dem TFS 2018 eine große Änderung bzgl. der Abwärtskompatibilität des Build Systems verbunden ist. Nachdem mit TFS 2015 ein komplett neues Buildsystem eingeführt wurde (siehe auch: Neu in TFS 2015 – Das Build-System), haben beide Systeme – alt und neu – eine Zeit lang parallel existiert. Dies hatte zum Ziel, dass zunächst das neue Buildsystem weiter reifen und sich die Benutzer daran gewöhnen konnten.

Mit dem TFS 2018 ist es nicht mehr möglich einen XAML Build Controller anzuschließen und auf dem XAML Workflow basierte Builds zu verwalten. Das heißt ganz konkret: Solange Sie noch (teilweise) XAML Builds produktiv in Verwendung haben, sollten Sie kein Upgrade auf TFS 2018 durchführen. Sie sehen im TFS 2018 jedoch nach wie vor Ihre XAML Builddefinitionen und können dort die gespeicherten Buildergebnisse abrufen, jedoch eben keine Builds mehr starten.

Wenn Sie also noch XAML Builds verwenden, heißt es jetzt Abschied nehmen und den Weg in die neue Welt antreten. Wir wurden in unseren Projekten oft nach Migrationswerkzeugen gefragt. Die schlechte Nachricht zuerst: Es gibt keine Werkzeuge, die per Knopfdruck XAML Builds auf die neue webbasierte Buildengine migrieren. Aber auch diese Me­dail­le hat zwei Seiten. Denn dieser harte Einschnitt ist nicht nur eine gute Gelegenheit alte Zöpfe abzuschneiden sondern er hat auch einiges zu bieten. MVP Jeff Bramwell hat in seinem Blogpost Why Should I Leave XAML Builds? bereits 2016 die wesentlichen Motivationspunkte für den Umstieg aufgezeigt. Das neue Konzept, die Offenheit und die Erweiterbarkeit einschließlich des Marketplaces ermöglichen es viele selbsterstellte Lösungsbausteine zu hinterfragen und abzulösen.

Eine detailliertere Darstellung und konkrete Hilfestellung zum Umstieg finden Sie in unserem kostenfreien Artikel Von TFS XAML Build nach TFS Build, der auch in der Windows Developer 3.2017 erschienen ist. Dort finden Sie auch Beispiele an typischen Anpassungen im alten XAML-Buildsystem zusammen mit unseren Vorschlägen wie diese im neuen Buildsystem umgesetzt werden können. Die nachfolgende Tabelle, die aus dem Artikel entnommen ist, zeigt zusammengefasst Beispiele von XAML-Anpassungen und ihre Alternativen.

Bitte klicken Sie zum Vergrößern der Tabelle darauf.

Gerne unterstützen wir Sie bei Ihrem konkreten Umstieg auf das neue Buildsystem oder generell bei der Einführung von TFS Builds. Sprechen Sie uns einfach an.

Jürgen Gutsch: Current Activities

Wow! Some weeks without a new blog post. My plan was to write at least one post per week. But this doesn't always work. Usually I write in the train, when I go to work to Basel (CH). Sometimes I write two or three posts in advance, to publish it later on. But I had some vacation, worked two weeks completely at home, and I had some other things to prepare and to manage. This is a short overview of the current activities:

INETA Germany

The last year was kinda busy, even from the developer community perspective. The leadership of the INETA Germany was one, but a pretty small part. We are currently planning some huge changes with the INETA Germany. One of the changes is to open the INETA Germany for Austrian and Swiss user groups and speakers. We already have some Austrian and Swiss speakers registered in the Speakers Bureau. Contact us via hallo@ineta-deutschland.de, if you are a Austrian or Swiss user group and if you wanna be a INETA member.

Authoring articles for a magazine

Also the last twelve months was busy, because almost every month I wrote an article for one of the most popular German speaking developer Magazine. All this articles were about ASP.NET Core and .NET Core. Here is a list of them:

And there will be some more in the next months.

Technical Review of a book

For the first time I do a technical review for a book about "ASP.NET Core 2.0 and Angular 4". I'm a little bit proud of getting asked doing it. The author is one of the famous European authors and this book is awesome and great for Angular and ASP.NET Core beginners. Unfortunately I cannot yet mention the Authors name and link to the book title, but I will definitely if it is finished.

Talks

What else? Yes I was talking and I will talk about various topics.

In August I did a talk at the Zurich Azure meetup about how we (the yooapps.com) created the digital presentation (web site, mobile apps) for one of the most successful soccer clubs in Switzerland on Microsoft Azure. It is also about how we maintain it and how we deploy it to Azure. I'l do the talk at the Basel .NET User Group today and in October at the Azure meetup Karlsruhe. I'd like to do this talk at a lot more user groups, because we are kinda proud of that project. Contact me, if you like to have this talk in your user group. (BTW: I'm in the Seattle (WA) area at the beginning of March 2018. So I could do this talk in Seattle or Portland too.)

Title: Soccer in the cloud – FC Basel on Azure

Abstract: Three years ago we released a completely new version of the digital presentation (website, mobile apps, live ticker, image database, videos, shop and extranet) of one of the most successful soccer clubs In Switzerland. All of the services of that digital presentation are designed to run on Microsoft Azure. Since then we are continuously working on it, adding new features, integrating new services and improving performance, security and so on. This talk is about, how the digital presentation was built, how it works and how we continuously deploy new feature and improvements to Azure.

At the ADC 2017 in Cologne I talked about customizing ASP.NET Core (sources on GitHub) and I did a one day workshop about developing a web application using ASP.NET Core (sources on GitHub)

Also in October I do an introduction talk about .NET Core, .NET Standard and ASP.NET Core at the "Azure & .NET meetup Freiburg" (Germany)

Open Source:

One Project that should be released for a while is LightCore. Unfortunately I didn't find some time the last weeks to work on that. Also Peter Bucher (who initially created that project and is currently working on that) was busy too. We Currently have a critical bug, which needs to be solved first, before we are able to release. Even the ASP.NET Core integrations needs to be finished first.

Doing a lot of sports

Since almost 18 Months, I do a lot of running and biking. This also takes some time, but is a lot of addictive fun. I cannot live two days without being outside running and biking. I wouldn't believe that, if you told me about it two years before. That changed my life a lot. I attend official runs and bike races and last but not least: I lost around 20 kilos the last one and a half years.

Stefan Henneken: IEC 61131-3: Extend UNION via inheritance

In the post IEC 61131-3: Additional language extensions, I briefly described the UNION. A reader comment has pointed out to me the possibility, that a UNION can also be expanded by EXTENDS. Since this simplifies the handling of a UNION and the norm does not indicate that, I would like to introduce this possibility in a (very) short post.

As already described in a post, a UNION makes it possible to define your own data types, all elements of which share the same storage space. Here is once more the example, where a 16-bit variable can be accessed individually to the low byte and the high byte. First, a structure consisting of two bytes is created.

TYPE U_Test :
UNION
  nVar1   : WORD;
  stVar2  : ST_Bytes;
END_UNION
END_TYPE
TYPE ST_Bytes :
STRUCT
  nLSB   : BYTE;
  nMSB   : BYTE;
END_STRUCT
END_TYPE

This example shows how the lower byte (LSB) and the higher byte (MSB) are determined from a variable of type WORD without bit operations.

PROGRAM MAIN
VAR
  uVar    : U_Test;
  nA, nB  : BYTE;
END_VAR

uVar.nVar1 := 16#1234;
nA := uVar.stVar2.nLSB; // Value: 16#34 (LSB)
nB := uVar.stVar2.nMSB; // Value: 16#12 (MSB)

EXTENDS for UNIONS

The same task can also be solved with inheritance, which is a bit more elegant. In this case, the union U_Test inherits from ST_Bytes.

TYPE U_Test EXTENDS ST_Bytes :
UNION
  nVar1 : WORD;
  // stVar2 : ST_Bytes; // Not necessary, is inherited from ST_Bytes.
END_UNION
END_TYPE

Access to nLSB and nMSB no longer needs to be done via a variable explicitly declared in the UNION.

uVar.nVar1 := 16#1234;
nA := uVar.nLSB; // Value: 16#34 (LSB)
nB := uVar.nMSB; // Value: 16#12 (MSB)

Holger Schwichtenberg: Änderungen in ASP.NET Core 2.0 und veraltete Dokumentation

Microsoft ändert - auch nach einer mehr als zweijährigen Betaphase - zwischen Hauptversionen immer noch Konzepte in ASP.NET Core. Selbst einen Monat nach Erscheinen von ASP.NET Core 2.0 ist die Dokumentation an vielen Stellen auf dem Stand von Version 1.x.

Robert Meyer: SelfHosted ASP.NET Core 2.0 Application

Auf Grund einer Projektanforderung musste ich mich die Tage damit auseinandersetzen, wie man eine ASP.NET Core 2.0 Website und Web API in einem eigenen Prozess als Windows Service bereitstellt.
Hierbei gibt es einige Besonderheiten zu beachten, auf welche ich in diesem Post gern näher eingehen möchte.

Folgende Technologien kommen zum Einsatz:

  • Alle Projekte basieren auf .NET Core 2.0 oder .NET Standard 2.0
  • Autofac als IoC Container
  • NLog für das Logging
  • Peter Kottas WindowsService als ServerFabric (GitHub)

Grundlegende Architektur der Anwendung

Architecture

Zu finden ist dieses Beispiel auf GitHub.

Grundlegendes Vorgehen

Die Konsolenapplikation kann als WindowsService oder direkt ausgeführt werden und hostet die Website und die API. Die Website und die API sind beides .NETStandard 2.0 Projekte, während die Console eine .NET Core 2.0 Anwendung darstellt.

Aufbau der SelfHosted.Console

IApplication definiert ein Interface zum Starten und Stoppen der Applikationen. Eine Applikation kann eine Website oder eine WebApi sein.

IApplication

Diese Applikationen werden in Autofac registriert und einem gekapselten MicroService geladen und gestartet.

	public class SelfHostedWindowService : MicroService, IMicroService
    {
        private IServiceProvider _provider;

        public void Start()
        {
            this.StartBase();

            var builder = new ContainerBuilder();
            builder.RegisterType<WebApplication>().As<IApplication>();
            builder.RegisterType<WebApiApplication>().As<IApplication>();
            var applicationContainer = builder.Build();

            _provider = new AutofacServiceProvider(applicationContainer);

            foreach (var app in _provider.GetServices<IApplication>())
            {
                app.Start();
            }

            System.Console.WriteLine("Windows services started.");
        }

        public void Stop()
        {
            this.StopBase();

            foreach (var app in _provider.GetServices<IApplication>())
            {
                app.Stop();
            }

            System.Console.WriteLine("Windows services stopped.");
        }
    }

Beim Starten der Konsole oder des Services, wird der MicroService registriert und in einer ServiceFactory geladen. Dadurch starten alle Applikationen, welche in den jeweiligen MicroService definiert sind.

	ServiceRunner<SelfHostedWindowService>.Run(config =>
	{
		var serviceName = "SelfHosted.WindowsService";

		config.SetName(serviceName);
		config.Service(serviceConfig =>
		{
			serviceConfig.ServiceFactory((service, extraArguments) =>
			{
				return new SelfHostedWindowService();
			});

			serviceConfig.OnStart((service, extraArguments) =>
			{
				System.Console.WriteLine("Service {0} started", serviceName);
				service.Start();
			});

			serviceConfig.OnStop((service) =>
			{
				System.Console.WriteLine("Service {0} stopped", serviceName);
				service.Stop();
			});

			serviceConfig.OnError(e =>
			{
				System.Console.WriteLine($"Service '{serviceName}' errored with exception : {e.Message}");
			});
		});
	});

Besonderheiten in der ASP.NET Core 2.0 Website

Es gibt jedoch beim hosten seiner ASP.NET Core 2.0 Website in einer Console noch drei wichtige Dinge zu beachten.

      1. Alle Views müssen Embedded werden. Dafür habe ich folgende Extension Methode geschrieben, welche im Startup bei AddRazorOptions aufgerufen wird.
        	public static RazorViewEngineOptions AddViews(this RazorViewEngineOptions options)
        	{
        		options.FileProviders.Add(new EmbeddedFileProvider(typeof(ServiceCollectionExtensions).GetTypeInfo().Assembly, "SelfHosted.Website"));
        		return options;
        	}
        
      2.  Danach stellte sich heraus, dass noch einige Assemblies fehlten. Diese werden ebenfalls per Extension Methode im Startup geladen.
        	public static RazorViewEngineOptions AddCompilationAssemblies(this RazorViewEngineOptions options)
        	{
        		var myAssemblies = AppDomain
        		   .CurrentDomain
        		   .GetAssemblies()
        		   .Where(x => !x.IsDynamic)
        		   .Concat(new[] { // additional assemblies used in Razor pages:
        					typeof(HtmlString).Assembly, // Microsoft.AspNetCore.Html.Abstractions
        					typeof(IViewLocalizer).Assembly, // Microsoft.AspNetCore.Mvc.Localization
        					typeof(IRequestCultureFeature).Assembly // Microsoft.AspNetCore.Localization
        		   })
        		   .Select(x => MetadataReference.CreateFromFile(x.Location))
        		   .ToArray();
        
        		var previous = options.CompilationCallback;
        
        		options.CompilationCallback = context =>
        		{
        			previous?.Invoke(context);
        			context.Compilation = context.Compilation.AddReferences(myAssemblies);
        		};
        
        		return options;
        	}
        
      3. Jetzt müssen noch alle statischen Files, welche z.B. im wwwroot liegen embedded werden. Auch hier gibt es wieder eine passende Extension Methode.
        	public static IServiceCollection AddStaticFiles(this IServiceCollection collection)
        	{
        		// static files are embedded resources in the "wwwroot" folder
        		collection.Configure<StaticFileOptions>(options =>
        		{
        			options.FileProvider = new EmbeddedFileProvider(typeof(Startup).Assembly, typeof(Startup).Namespace + ".wwwroot");
        		});
        
        		return collection;
        	}
        

Aufgerufen werden die Extension Methods im Startup der Website, in der Funktion ConfigureServices wie folgt:

	public IServiceProvider ConfigureServices(IServiceCollection services)
	{
		services.AddSingleton<IConfiguration>(Configuration);
		services.AddMvc()
			.AddRazorOptions(options =>
			{
				options.AddViews();
				options.AddCompilationAssemblies();
			});
		services.AddStaticFiles();

		var builder = new ContainerBuilder();
		builder.Populate(services);
		this.ApplicationContainer = builder.Build();

		return new AutofacServiceProvider(this.ApplicationContainer);
	}

 

Zusammenfassung

Auf diesem Weg erreicht man eine sehr leichtgewichtige Anwendung, welche komplett in einem eigenen Prozess unabhängig von dem Betriebssystem und Webserver installiert werden kann. Somit erreicht man bei seiner Produktentwicklung, welchen bei Kunden vor Ort installiert werden muss, eine sehr hohe Flexibilität und ich unabhängig der Umgebung.


MSDN Team Blog AT [MS]: Teil 2/2: Xamarin Case Study: Smart Energy Management System by Levion

Wir wollen euch in den nächsten Monaten von ISVs und Startups berichten, mit denen wir an verschiedenen Technologien arbeiten und auf interessante technische Fragen aufstoßen. Solche Lösungen wollen wir mit euch teilen. Vielleicht hilft es euch bei euren Projekten oder inspiriert für neue Ansätze.

Levion Technologies GmbH ist ein Grazer Startup, das eine innovative Lösung im Bereich von Energie Management hat. Die hier zum Teil vorgestellte Lösung besitzt auch eine öffentliche API, sodass Entwickler weitere Lösungen auf ihr aufbauen können.

Im Teil 1 haben wir bereits mit den Highlights begonnen. Nun folgen weitere Highlights.

 

Online Communication with Azure Web Apps and Redis

The online communication between the SEMS control unit and the mobile application works via a NodeJS application that is hosted on Azure Web Apps and connects to the backend control unit and the frontend app via Websockets. The messages are relayed via Redis Cache service in Microsoft Azure.

Here is how the NodeJS application is built.

var ws           = require('ws'),
    nconf        = require('nconf'),
    express      = require('express'),
    url          = require('url'),
    redispubsub  = require('node-redis-pubsub');

/// endpoints defines (for websocket path matching and authentification)
const regex_websocket_path = 
  '^\/api\/v1\/relay\/(frontend|backend)\/([a-zA-Z0-9]+)$'

// Setup nconf to use (in-order): 
//   1. Command-line arguments 
//   2. Environment variables 
//   3. config file (read-only)
nconf.argv().env().file({ file: __dirname+'/config/config.json' });

// check for needed VARS
if(!nconf.get('PORT')) {
  console.log("FATAL: PORT not defined.");
  process.exit()
}

// log only when enabled
const logging = nconf.get('DEBUG_CONSOLE');
function logMessage(message) {
  if(logging)
    console.log(message);
}

/// INIT main parts
var app = express();
var http = require('http').createServer(app);

// redis publish/subscribe 
var tls;
if(nconf.get('REDIS_SSL'))
  tls={servername: nconf.get('REDIS_HOST') }

var relay = new redispubsub({
                host: nconf.get('REDIS_HOST'),
                port: nconf.get('REDIS_PORT'),
                scope: nconf.get('REDIS_SCOPE'),
                tls: tls,
                auth_pass: nconf.get('REDIS_KEY')
                });

relay.on('error', function(err) {
  logMessage('some redis related error: ' + err);
});

/// authentification within websockets
///
function checkAuthorization(info, cb) {

  //* THIS WAS REMOVED *//
  cb(true);
};

/// WEBSSOCKETS
///
///
var server = new ws.Server({
  perMessageDeflate: false,
  server: http,
  verifyClient: checkAuthorization,
  });


/// on each websocket we do ...
server.on('connection', function(socket) {

    // get the url
    const location = url.parse(socket.upgradeReq.url, true);
    const req_url = location.path;

    // callback for unsubscribing
    var unsubscribe;

    // get type of client and serial of target/source system
    const match = location.path.match(regex_websocket_path);
    if(!match) // should never happen
      return;

    // extract values from path    
    const endpoint = match[1];
    const serial   = match[2];

    // BACKEND (a SEM)    
    if(endpoint === 'backend')
    {
       logMessage('device ' + serial + ' available');

       // message from the relay
         unsubscribe = relay.on('to:'  +serial, function(data) {
            var msg = new Buffer(data.msg, 'base64').toString('utf8')
            //logMessage('message for '+serial+': '+data.message);
            socket.send(msg);         
         });

    }

    // FRONTEND (an app)
    else if(endpoint === 'frontend')
    {
       logMessage('client looking for ' + serial +' arrived');     

       // message from the relay
         unsubscribe = relay.on('from:'  +serial, function(data) {
            var msg = new Buffer(data.msg, 'base64').toString('utf8')
            //logMessage('message from '+serial+': '+data.message);
            socket.send(msg);         
         }); 
    }

    // unknown (auth should not let this happen)
    else
    {
      logMessage("rejecting unknown path");
      socket.close();
      return;
    }

    // message from the web sockets
    socket.on('message', function(message,flags) {
      var msg64 = new Buffer(message).toString('base64');

      if(endpoint === 'backend') {
        //logMessage('from '+serial+': '+message);
        relay.emit('from:'+serial, { msg: msg64 } );      
      } else { // frontend
        //logMessage('to '+serial+': '+message);
        relay.emit('to:'  +serial, { msg: msg64 });
      }

      });

      // handle closes properly
      socket.on('close', function(message,flags) {
        if(unsubscribe)
          unsubscribe();

        if(endpoint === 'backend') {
          logMessage('device ' + serial + ' left');
        } else { // frontend
          logMessage('client looking for ' + serial +' gone');
        }
      });

});

/// handle default location if someone calls that
app.get('/', function (req, res) {
  res.send(
  'This is the relay service for SEMS. This is an API and no website.<br>'+
  'Please visit <a href="http://sems.energy" '+
  'target="_blank">http://sems.energy</a> for more information');
});


/// start http server
http.listen( nconf.get('PORT') , function () {
  logMessage('staring sems relay service on port '+ nconf.get('PORT') );
});

/// shutdown detection for graceful shutdown
process.on( 'SIGINT', function() {
  logMessage( "\nGracefully shutting down from SIGINT (Ctrl-C)" );
  relay.quit();
  process.exit();
})

DevOps with Build and Release Management

Since LEVION wanted to automate build and release management of the app, they used Visual Studio Team Services for this task.

The build definition for the UWP App consists of following steps.

builddef

This build definition is based on the UWP template from the suggested build definitions with a couple of changes.

In the Nuget Restore Step it was important to select Nuget Version 3.5.0.

The Apply Version Step consists of a powershell script that makes sure the UWP Package Version corresponds to the running build. This is how the script looks like:

#Based on https://www.visualstudio.com/docs/build/scripts/index
# Enable -Verbose option
[CmdletBinding()] 

$VersionRegex = "\d+\.\d+\.\d+\.\d+"

$ManifestVersionRegex = " Version=""\d+\.\d+\.\d+\.\d+"""

if (-not $Env:BUILD_BUILDNUMBER)
{
    Write-Error ("BUILD_BUILDNUMBER environment variable is missing.")
    exit 1
}
Write-Verbose "BUILD_BUILDNUMBER: $Env:BUILD_BUILDNUMBER"

$ScriptPath = $null
try
{
    $ScriptPath = (Get-Variable MyInvocation).Value.MyCommand.Path
    $ScriptDir = Split-Path -Parent $ScriptPath
}
catch {}

if (!$ScriptPath)
{
    Write-Error "Current path not found!"
    exit 1
}

# Get and validate the version data
$VersionData = [regex]::matches($Env:BUILD_BUILDNUMBER,$VersionRegex)
switch($VersionData.Count)
{
   0        
      { 
         Write-Error "Could not find version number data in BUILD_BUILDNUMBER."
         exit 1
      }
   1 {}
   default 
      { 
         Write-Warning "Found more than instance of version data in BUILD_BUILDNUMBER." 
         Write-Warning "Will assume first instance is version."
      }
}
$NewVersion = $VersionData[0]
Write-Verbose "Version: $NewVersion"


$AssemblyVersion = $NewVersion
$ManifestVersion = " Version=""$NewVersion"""

Write-Host "Version: $AssemblyVersion"
Write-Host "Manifest: $ManifestVersion"
Write-Host "ScriptDir: " $ScriptDir

# Apply the version to the assembly property files
$assemblyInfoFiles = gci $ScriptDir -recurse -include "*Properties*","My Project" | 
    ?{ $_.PSIsContainer } | 
    foreach { gci -Path $_.FullName -Recurse -include AssemblyInfo.* }

if($assemblyInfoFiles)
{
    Write-Host "Will apply $AssemblyVersion to $($assemblyInfoFiles.count) Assembly Info Files."

    foreach ($file in $assemblyInfoFiles) {
        $filecontent = Get-Content($file)
        attrib $file -r
        $filecontent -replace $VersionRegex, $AssemblyVersion | Out-File $file utf8

        Write-Host "$file.FullName - version applied"
    }
}
else
{
    Write-Warning "No Assembly Info Files found."
}

# Try Manifests
$manifestFiles = gci .\ -recurse -include "Package.appxmanifest" 

if($manifestFiles)
{
    Write-Host "Will apply $ManifestVersion to $($manifestFiles.count) Manifests."

    foreach ($file in $manifestFiles) {
        $filecontent = Get-Content($file)
        attrib $file -r
        $filecontent -replace $ManifestVersionRegex, $ManifestVersion | Out-File $file utf8

        Write-Host "$file.FullName - version applied to Manifest"
    }
}
else
{
    Write-Warning "No Manifest files found."
}

Write-Host ("##vso[task.setvariable variable=AppxVersion;]$NewVersion")

In order for this script to run without problems, it is necessary to change the Build number format which can be found in the General tab of the Build definition edit screen:

BuildVersion

After the build package has been created. It is taken from the drop location and copied to Azure blog in a release definiation:

ReleaseBlog

The following release definition shows how the package is submitted to the store in a specific package flight.

ReleaseStore

Conclusion

Intially the team was not convinced Xamarin would be the right solution for their mobile application, and they wanted to begin with the UWP version only and develop the Android and iOS version outsourced natively. However, working with the framework they were convinced of Xamarin's benefit of code sharing that they will now go ahead and also build the Android and iOS version with Xamarin. Effectively this means that they only have to work on the respective UIs for Android and iOS since the app logic is completely capsulated in the shared codebase of several Portable Class Libraries. The description above highlights some of their solutions for this app. As discussed, their shared logic worked for all platforms in the PCL without problems.

Additional resources

AIT: Neu in TFS 2018: Neues für Work Item Tracking

In diesem Beitrag unserer Blogserie „Neu in TFS 2018“ nehmen wir die Neuerungen, die TFS 2018 für das Work Item Tracking mit sich bringt, unter die Lupe. Zwar enthält das neueste Release keine grundlegenden Änderungen in diesem Bereich, jedoch sind es die kleinen Dinge, die einem den Projektalltag erleichtern. Lesen Sie also in den folgenden Abschnitten über die kleinen Helfer im Bereich Work Item Tracking.

Backlogs, Boards und Abfragen besser filtern

Die teils verwirrende und unterschiedliche Verwendung von Filtermechanismen wird mit TFS 2018 endlich vereinheitlicht. Damit ist das Filtern über Backlogs, Kanban-Boards, Sprints-Backlogs und Abfragen sowie die Testfallverwaltung konsistent (Bild 1). Des Weiteren kann das Backlog nun zusätzlich zu den Tag- und Schlüsselwort-Filtern auch nach Work Item Typen, Zuständen und zugewiesenen Personen weiter eingeschränkt werden.

Bild 1: Backlogs, Boards und Abfragen besser filtern

Bild 1: Backlogs, Boards und Abfragen besser filtern

 

Kanban-Board-Element erweitern, um leere Felder auf Karten anzuzeigen

Im Kanban-Board war es bisher nur möglich, leere Felder auf den Karten auszublenden. Wenn man diese dann aber füllen wollte, musste man umständlich das Element öffnen und die Daten dort eingeben.

Ab TFS 2018 ändert sich dieser Umstand durch die Einführung des Chevrons im Kanban-Board (Bild 2). Dieses klappt automatisch die leeren Felder ein, zeigt aber durch einen Klick, diese auch wieder an. Nun kann man also auch in der Board-Ansicht fehlende Werte leicht und schnell nachtragen (Bild 3).

Bild 2: Kanban-Board-Element erweitern, um leere Felder auf einem anzuzeigen

Bild 2: Kanban-Board-Element erweitern, um leere Felder auf einem anzuzeigen

Bild 3: Kanban-Board-Element erweitert

Bild 3: Kanban-Board-Element erweitert

Sicherstellung der Eingabe von wichtigen Informationen

In bestimmten Kundenumfeldern, zum Beispiel stark regulierten Bereichen, ist es nötig, dass ein TFS-Anwender, bei der Bearbeitung von Work Items, Felder nicht leer lässt. Um dies sicherzustellen können nun per Work Item Form Service Methode IPromise<void> setError(errorMessage) der REST API benutzerdefinierte Steuerelemente, Gruppen und Seiten von Formularen das Speichern explizit verhindern, bis entsprechende Werte eigegeben wurden.

Erweiterte Projekterstellung im Web Access

Das Erstellen eines Teamprojekts im TFS Web Access enthält nun den Großteil der Features, die auch beim Erstellen eines Teamprojekts in Visual Studio verfügbar sind. Der Unterschied zu Visual Studio ist, dass in der Webvariante keine SSRS-Berichte automatisch mit angelegt werden. Jedoch können diese im Nachgang über das Kommandozeilen-Tool tfsconfig bereitgestellt und auch aktualisiert werden. Weitere Informationen finden Sie unter Hinzufügen von Projektberichten.

Prozess Template-Manager im Web

Um Ihre Prozess Templates hochzuladen, können Sie ab TFS 2018 auch den Webzugriff verwenden. Dadurch entfällt der Zwang Visual Studio für diese administrative Tätigkeit nutzen zu müssen. Bis Visual Studio 2017 Update 4 ist die Möglichkeit aber weiterhin enthalten. Ab Visual Studio 2017 Update 5 und höher jedoch erfolgt eine automatische Weiterleitung aus dem Visual Studio-Prozess Template Manager in den Web Access.

Mobiles Arbeitselementformular

Mit der neuen mobilen Integration und Navigation können Sie nun alle Teile des TFS auch von unterwegs erreichen. Hinzu kommt, dass alle Felder und Elemente zur mobilen Verwendung überarbeitet wurden.

Bild 4: Mobiles Arbeitselementformular

Bild 4: Mobiles Arbeitselementformular

Fazit

Wie Sie in den vorhergehenden Abschnitten lesen konnten, gibt es einige kleine aber durchaus feine Verbesserungen im TFS 2018 für den Bereich Work Item Tracking. Insbesondere die einheitliche Filterung in den verschiedenen Ansichten erfüllt einen lang gehegten Wunsch vieler User.

Im nächsten Beitrag dieser Reihe geht es um die Änderungen im Build- & Release- Management. Seien Sie also gespannt, welche neuen Features TFS 2018 noch mit sich bringt.

MSDN Team Blog AT [MS]: September Update der österreichischen PowerShell Community

Was war im Summer los in der PowerShell Community in Österreich ? Was wird uns der Herbst bringen?

Kommende Events:

5. Oktober: Experts Live Café Linz – mit PowerShell - Schwerpunkt Security

7. November: Experts Live Conference Austria

Vergangene Events:

Newsletter - die "Schnipseljagd"

Unsere wöchentlichen Newsletter kamen regelmäßig raus und beinhalteten folgende Themen:

http://www.powershell.co.at/powershell-schnipseljagd-3317/

  • PowerShell lernen
  • Erstellen von HTML Reports
  • Excel Reports
  • Visualisierungen ohne GUI
  • PowerShell Swagger
  • PowerShell Packaging
  • Converting GPO to DSC
  • Test Driven Development

http://www.powershell.co.at/powershell-schnipseljagd-3417/

  • Desired State Configuration DSC
  • PowerShell und Security
  • PowerShell 2.0 nicht mehr im Fall Creators Update
  • Azure managen mit PowerShell
  • PowerShell und REST API
  • Installierte Updates finden und fehlende finden

http://www.powershell.co.at/powershell-schnipseljagd-3517/

  • Group Policy Link Report mit PowerShell (Links zu OU´s, Vererbungsblocks, …)
  • GPO Backups anlegen
  • Mit HashTables schneller vergleichen
  • Speicherauslastung von Remote-Servern abrufen
  • Mehr Spass mit WSUS dank PowerShell

http://www.powershell.co.at/powershell-schnipseljagd-3617/

  • PowerShell spielend lernen
  • Deepl – Übersetzen
  • ARM Templates für Service Maps
  • Setzen von $PSDefaultParameterValues
  • PowerShell Security
  • PowerShell und Tesla
  • ConsolenPower: Graph in der PowerShell Console
  • System Center Service Manager

http://www.powershell.co.at/powershell-schnipseljagd-3717/

  • PowerShell Pester Buch gratis!
  • Patches runterladen, installieren und rebooten
  • WiFi Passwort auslesen
  • Azure Ressourcennutzung mit PowerShell auswerten
  • WSUS besser verwenden
  • Mit HTTP eine Datei herunterladen

So, nun hoffe ich es war für jeden etwas dabei!

Die PowerShell UserGroup Austria: www.powershell.co.at

AIT: Neu in TFS 2018: Nur noch das neue Work Item Modell

Update – 22.09.2017

Bezüglich der XML-Definition des alten Formulartyps (<LAYOUT>-Knoten in der Work Item Type Definition) haben wir den Blogpost noch einmal angepasst.

 

In diesem Beitrag unserer Blogserie „Neu in TFS 2018“ beleuchten wir eine wesentliche Änderung bei den Work Item Formularen. Da es sich um eine einschneidende Änderung handelt, sollte sich jeder, der von einem Bestandssystem auf einen TFS 2018 migriert, damit beschäftigen. Erfahren Sie hier, welche Auswirkungen die Änderung hat und unsere Empfehlung für Sie. Es geht dabei lediglich um die administrative Sicht. Eine fachliche Betrachtung der neuen Formulare wird in einem späteren Blogpost durchgeführt.

Ausgangslage ist, dass Microsoft bereits mit dem TFS 2017 ein neues Work Item Formular eingeführt hat. Bislang wurden beide Formulartypen (alt und neu) parallel unterstützt und der TFS Administrator konnte entscheiden, welches Modell verwendet wird bzw. ob die TFS Benutzer selbst wählen sollten.

Mit dem TFS 2018 wird das alte Formular im Web Access gar nicht mehr unterstützt. D.h. es gibt keine Möglichkeit mehr, den alten Formulartyp dort anzuzeigen. Wenn man bereits in TFS 2017 auf das neue Formularlayout gewechselt ist, hat der TFS selbst den neuen XML-Tag „<WebLayout“>” ergänzt. Diese Funktionalität ist immer noch enthalten, d.h. spätestens mit dem Wechsel auf TFS 2018 wird dieser Bereich in den Work Item Type Definitionen automatisch ergänzt. Jedoch werden bestehende Team Project Collections zeitgleich umgestellt, so dass die alte Formulardefinition, also der XML-Tag “<Layout>”, im Web Access nicht mehr verwendet wird. Technisch betrachtet bleibt die alte Formulardefinition („<Layout“-Knoten im XML) aus Abwärtskompatibilitätsgründen erhalten, z.B. für den Zugriff mittels Microsoft Test Manager (MTM). Wenn Ihr TFS davon betroffen ist, erhalten Sie beim Upgrade folgende Warnung:

[VS403364]: This release introduces major updates to the work item form layout and functionality and deprecates legacy custom controls. Consequently, the upgrade process will update all work item type definitions to use the new work item form WebLayout element and remove all custom controls. For additional information and recommended upgrade steps, see the Deployment Guide.

Haben Sie keinerlei Anpassungen an Ihren Work Item Typen in der Vergangenheit vorgenommen, kann man gut mit dem Automatismus leben. Wurden die Work Item Typen jedoch früher modifiziert (z.B. neue Felder hinzugefügt oder neue Work Item Typen eingeführt), so ist manuelle Nacharbeit sinnvoll, wie nachfolgend beschrieben.

Wenn Sie bereits unserer Best Practice Empfehlung folgen und Ihre Process Templates in der Versionskontrolle vorhalten, dann können Sie dort die automatisch generierten Formulare nach Ihren Wünschen anpassen. Wenn Sie die Templates noch nicht in der Versionskontrolle haben, dann ist jetzt ein guter Zeitpunkt, damit zu beginnen. Laden Sie dazu einfach die Process Templates aus Ihrem bestehenden TFS mittels Visual Studio, wie im nachfolgenden Screenshot dargestellt, herunter.

image

Nach dem automatischen Anpassen der Work Item Type Definitionen beim Upgrade sollten Sie die neuen Dateien herunterladen und auch in die Versionskontrolle aufnehmen. Entscheidend ist jetzt, dass die einzelnen Elemente auf der Oberfläche, insbesondere die vorher eigens hinzugefügten, automatisch angeordnet sind. Deshalb ist es sinnvoll, diese automatischen Anpassungen zu reviewen und diese manuell so zu verändern, dass sie Ihren Ansprüchen an das Formularlayout (Anordnung der Felder) gerecht wird.

Um dies ohne Einfluss auf die Produktivumgebung zu machen, empfehlen wir die vorher beschriebenen Aktivitäten komplett in einer Parallelumgebung durchzuführen. Speziell für solche Fälle wird ein s.g. Pre-Production-Upgrade angeboten: https://docs.microsoft.com/en-us/vsts/tfs-server/upgrade/pre-production

Wenn Sie Hilfe beim Upgrade benötigen, sprechen Sie uns gerne an.

AIT: „Neu in TFS 2018“ – Startschuss zur Blogserie

 

Es ist soweit! Nicht mal ein Jahr nach Veröffentlichung von TFS 2017 RTM hat Microsoft Ende August den ersten Release-Kandidaten für die nächste Major Version des Team Foundation Servers unter dem Namen TFS 2018 RC 1 bereitgestellt. Wie auch vorhergehende Release-Kandidaten ist auch die aktuelle Version ein „Go-Live“ Release und damit für Verwendung in Produktivumgebungen freigegeben.

Die neue Major Version des TFS wartet mit einer Vielzahl von neuen Features auf sowie auch mit einigen Breaking Changes, wie z.B. der Abkündigung des XAML-Buildsystems und der SharePoint-Integration. Einen ersten Einblick über die Neuheiten gewinnt man in den TFS 2018 Release Notes, die in Visual Studio Docs dokumentierten Anforderungsänderungen für TFS 2018 sowie auch in der Visual Studio Feature Timeline. In der letzteren ist dabei aktuell die neue Version noch unter dem Namen TFS vNext zu finden.

SNAGHTML5e529b

Beim Überfliegen der Neuerungen ist bereits an der Länge der Release Notes der einzelnen Teilsysteme (Build, Code,…) zu erkennen, dass Microsoft für TFS 2018 klare Schwerpunkte gesetzt hat: Zum einen liegt ein besonderer Fokus auf dem Thema Git. Auf der anderen Seite dominieren die Neuheiten im Bereich Build- und Release Management. Damit macht Microsoft einen weiteren großen Schritt zur Verbesserung der Continouos Integration und Deployment-Prozesse.

Wie auch bei vorhergehenden Major Versionen des TFS möchten wir es uns auch bei TFS 2018 nicht nehmen lassen, Sie bei dieser spannenden Entwicklung zu begleiten und Ihnen einen Auszug der Neuheiten in einer Blogserie vorzustellen. Diese soll keine vollumfassende Feature-Dokumentation darstellen. Vielmehr möchten wir den Fokus auf diejenigen Neuheiten legen, welche aus unserer Erfahrung heraus dem Projektalltag besonders zu Gute kommen.

Freuen Sie sich also in den nächsten Wochen auf spannende Beiträge und die Highlights der neuen Major Version TFS 2018.

Holger Schwichtenberg: .NET Conf 2017 heute kostenfrei im Internet

Heute um 17:00 Uhr startet die vierte Ausgabe von Microsofts virtueller .NET-Entwicklerkonferenz.

Marco Scheel: SharePoint Online Device Access Option fehlt - Lösung: First Release for everyone

In meinen Projekten (Office 365 / 100% Cloud) ist Conditional Access (CA) nicht mehr wegzudenken. Für uns “SharePoint“ Leute hat CA eine spezielle Geschmacksrichtung. Eine der ersten Session-Based CA Policies arbeitet mit SharePoint und ermöglicht so zusätzliche Kontrollen beim Zugriff auf SharePoint. Microsoft hat das ganze in einem Blogpost beschrieben. Jetzt wurde angekündigt, dass es in die nächste Runde geht und die Konfiguration nicht mehr auf den ganzen Tenant zieht, sondern einzelne Site Collections adressiert. In diesem Zuge wollte ich mal wieder meine AAD + SPO Konfiguration gerade ziehen, aber siehe da… es fehlt was! So sieht es im SharePoint Admin Center unter dem Punkt “Device Access” aus:

image

Eigentlich sollte der Dialog so aussehen:

image

Es fehlt der entscheidende Teil:
“Control access from devices that aren’t compliant or joined to a domain”
oder auf deutsch:
“Zugriff von Geräten steuern, die nicht konform oder einer Domäne beigetreten sind”
Jetzt stellt sich die Frage warum? Mein Tenant ist auf “First Release”, also sollte es doch funktionieren. Eine kurze Recherche hat mich zum überraschenden Ergebnis geführt:

image

Quelle: Microsoft Support - Control access from unmanaged devices

Mein Tenant war tatsächlich für einen Test auf die Option “First release for selected users” eingestellt:

image

Lösung

Wie im Support Dokument beschrieben habe ich das Setting geändert auf die Option “First release for everyone”:

image

Kurze Zeit später (es zieht nicht sofort), konnte ich über das SharePoint Admin Center die gewünschte “Device Access” Konfiguration abschließen.

Ich habe in der Vergangenheit immer nach den Unterschieden der beiden “First Release” Einstellungen gesucht. Dieser Fall ist der erste mir bekannte.

Holger Schwichtenberg: Übersichtliche Konfiguration in Entity Framework Core mit IEntityTypeConfiguration<T>

Entity Framework Core 2.0 bietet nun die Schnittstelle IEntityTypeConfiguration, mit der man eine getrennte Konfigurationsklasse für einen einzelnen Entitätstyp implementieren kann.

MSDN Team Blog AT [MS]: Teil 1/2: Xamarin Case Study: Smart Energy Management System by Levion

 

Wir wollen euch in den nächsten Monaten von ISVs und Startups berichten, mit denen wir an verschiedenen Technologien arbeiten und auf interessante technische Fragen aufstoßen. Solche Lösungen wollen wir mit euch teilen. Vielleicht hilft es euch bei euren Projekten oder inspiriert für neue Ansätze.

Levion Technologies GmbH ist ein Grazer Startup, das eine innovative Lösung im Bereich von Energie Management hat. Die hier zum Teil vorgestellte Lösung besitzt auch eine öffentliche API, sodass Entwickler weitere Lösungen auf ihr aufbauen können.

 

Intro

LEVION offers a Smart Energy Management System where users can montior and manage the energy profile and consumption of their electrical appliances at home.

In this development effort Microsoft supported LEVION to build a mobile application that connects with their SEMS control unit. The app was built with Xamarin in order to accelerate development for iOS, Android and Windows Phone and the app uses backend services in the form of Azure App Service and Redis Cache on Azure to store and visualize relevant data for their solution. In the first step the build for UWP platform is prioritized so the ISV could leverage their extensive .Net and UWP Skills. We further looked at the DevOps process together to create automated builds for their apps.

  • Key technologies used:
    • Xamarin
    • Template 10 (UWP)
    • MVVMlight by GalaSoft
    • Azure Web Apps & Redis Cache on Azure
    • VSTS for Build and Release Management and Source Control

Customer profile

Kernteam

LEVION Technologies offers complete software and hardware solutions as well as consulting in projects. The foremost goal of this innovative company located in Austria is to provide cutting-edge technology in a simple and meaningful way, always adopting the users' point of view and the benefits of the product itself. Therefore, quality, usability and service play an important role in all decision-making processes.

SEMSLogo

Their main focus lies on the smart energy management system “SEMS”. It manages energy intelligently and opens a path for the meaningful use of renewable energies. Thanks to the smart connection of large electricity consumers, household appliances and photovoltaic systems the energy can always be consumed when it comes cheap and therefore is produced in excess. The central control unit SEM (short for: Smart Energy Manager) can be operated intuitively from the living room – completely without annoying cabling or major installation efforts. This way, the energy household can be optimized simple, smart and swift.

sems-schema-systemübersicht-2

Problem statement

Currently, LEVION offers the Smart Energy Management Systems SEMS with their Smart Energy Manager SEM as a control unit. SEMS allows customers to monitor and control the energy consumption of their connected electrical appliances and devices. Since the solution is rolled out in different parts of Austria, including rural areas where Internet connectivity among the households is not always given, it was important that the solution would work both with and without an internet connection.

LEVION wanted to enable their customers to monitor and control their energy consumption not only through their own custom control unit but also through a mobile application that is available on Android, iOS and Windows. Since LEVION focuses their technology skills on .NET and not Java or Swift/Objective C, they were considering of the developing the UWP version inhouse and outsourcing the Android and iOS development to agencies. We supported LEVION by introducing Xamarin as a cross-plattform native technology using .NET so that they opted for Xamarin as a way to develop a native application with a common codebase. This way they could not only leverage their .NET Skills to the fullest but also keep the iOS and Android developments inhouse. For the scope of this case study we have focused on the UWP version first.

Solution, steps, and delivery

Architecture

A technical overview of the Smart Energy Management Solution can be seen in the following architectural diagram.

ArchitekturFin

The solution revolves around the custom control unit of the SEMS System. This control unit connects directly to an Azure Web App to store persistent data in Azure Blob Storage and Azure SQL Database.

The mobile application should speak to the control unit and allow the user to not only monitor the energy consumption through the control unit, which is mounted at a fixed place in the house, but also anywhere with a mobile device. Since LEVION did not want to assume that their customer has an internet connection it was important that the mobile app connects to the control unit directly over sockets via TCP with UDP discovery. If there is internet connection, then the mobile app connects via a broker in Azure to the control unit. The broker is essentially an Azure Web App with a Redis Cache for performance improvements.

The connection to the control unit allows the mobile application to display current and recent energy data. For a future version the mobile app will also display historic energy data for which the mobile application will directly connect to the SEMS Service in Azure that returns the historic data from the Azure SQL DB and Blob Storage.

Xamarin Solution

Xamarin allows you to natively develop cross-platform apps where you can share code of your app logic in Portable Class Libraries or Shared Projects, which can be referenced in the native platform projects (iOS, Android, Windows UWP). The UI can be developed either natively in the platform projects or with Xamarin.Forms you can also share most of the UI Code.

Due to the fact, that LEVION uses a lot of custom controls in their UI, displaying diagrams of the energy consumption, we decided to share only the app logic and implement the Views for the App UI natively on each platform.

Below you can see the solution structure of the Xamarin app.

XamarinSolutionOverviewHighlighted

The portable class libraries are highlighted and these are the projects that are used in the following platform projects:

  • Levion.Sems.Mobile.UWP (Windows)
  • Levion.Sems.Mobile.Android
  • Levion.Sems.Mobile.iOs

The shared logic of the app is divided into several portable class libraries:

  • Levion.SEMS.Mobile.Core contains all the models for the entities of the system.
  • Levion.SEMS.Mobile.DAL contains the logic for connecting to the broker and connecting directly to the control unit.
  • Levion.SEMS.Mobile.BI contains most of the app logic.
  • Levion.SEMS.Mobile.ViewLogic contains all ViewModels for the native platform Views.

For UWP we also have a helper project Levion.Lib.Mobile.UWP that only contains some custom UI control (hamburger menu and corresponding MenuNavigationService) that we will explain in the Challenges section.

Highlights and Challenges

MVVMlight, Template 10 and Xamarin

Since LEVION is already experienced with UWP Development they wanted to leverage Template10 as it already contains a good structure with services like Navigation Service and also ready-made UI controls. Especially the hamburger control works very well in Template 10 and they wanted to reuse this from Template 10.

LEVION also wanted to incorporate the MVVM pattern for their Xamarin solution. We discussed different frameworks and finally settled for MVVMlight by GalaSoft as it is a light framework and works well with Xamarin.

The challenge that we faced here was that Template 10 already comes with its own navigation service, as does MVVMlight. The hamburger menu control from Template 10 is tightly linked to its own navigation service. Since we did not want to implement our own interactive hamburger menu, what we did is take out the source code of the hamburger menu into Levion.SEMS.Lib.Mobile.UWP and hook it up with our custom navigation service that inherits from MVVMlight navigation service, so that it all plays nicely together.

This is the interface of our custom navigation service (PCL):

using GalaSoft.MvvmLight.Views;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.UI.Xaml.Controls;

namespace Levion.Lib.Mobile.Uwp.Services
{
    /// <summary>
    /// Navigation service for menus.
    /// </summary>
    public interface IMenuNavigationService : INavigationService
    {
        Frame Content { get; }
    }
}

Here is the implementation (PCL):

using GalaSoft.MvvmLight.Views;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;

namespace Levion.Lib.Mobile.Uwp.Services
{
    /// <summary>
    /// Navigation service for UWP applications with custom menu.
    /// </summary>
    public class MenuNavigationService : IMenuNavigationService
    {
        /// <summary>
        /// The key that is returned by the <see cref="CurrentPageKey"/> property
        /// when the current Page is the root page.
        /// </summary>
        public const string RootPageKey = "-- ROOT --";

        /// <summary>
        /// The key that is returned by the <see cref="CurrentPageKey"/> property
        /// when the current Page is not found.
        /// This can be the case when the navigation wasn't managed by this NavigationService,
        /// for example when it is directly triggered in the code behind, and the
        /// NavigationService was not configured for this page type.
        /// </summary>
        public const string UnknownPageKey = "-- UNKNOWN --";

        /// <summary>
        /// The pages by key.
        /// </summary>
        private readonly Dictionary<string, Type> _pagesByKey = new Dictionary<string, Type>();

        /// <summary>
        /// Initializes a new instance of the <see cref="MenuNavigationService"/> class.
        /// </summary>
        public MenuNavigationService()
        {
        }

        /// <summary>
        /// The key corresponding to the currently displayed page.
        /// </summary>
        public string CurrentPageKey
        {
            get
            {
                lock (_pagesByKey)
                {
                    var frame = this.GetFrame();

                    if (frame == null)
                    {
                        return null;
                    }

                    if (frame.BackStackDepth == 0)
                    {
                        return RootPageKey;
                    }

                    if (frame.Content == null)
                    {
                        return UnknownPageKey;
                    }

                    var currentType = frame.Content.GetType();

                    if (_pagesByKey.All(p => p.Value != currentType))
                    {
                        return UnknownPageKey;
                    }

                    var item = _pagesByKey.FirstOrDefault(
                        i => i.Value == currentType);

                    return item.Key;
                }
            }
        }

        /// <summary>
        /// Gets the content.
        /// </summary>
        /// <value>
        /// The content.
        /// </value>
        public Frame Content
        {
            get
            {
                return this.GetFrame();
            }
        }

        /// <summary>
        /// If possible, instructs the navigation service
        /// to discard the current page and display the previous page
        /// on the navigation stack.
        /// </summary>
        public void GoBack()
        {
            var frame = this.GetFrame();

            if (frame.CanGoBack)
            {
                frame.GoBack();
            }
        }

        /// <summary>
        /// Instructs the navigation service to display a new page
        /// corresponding to the given key. Depending on the platforms,
        /// the navigation service might have to be configured with a
        /// key/page list.
        /// </summary>
        /// <param name="pageKey">The key corresponding to the page
        /// that should be displayed.</param>
        public void NavigateTo(string pageKey)
        {
            this.NavigateTo(pageKey, null);
        }

        /// <summary>
        /// Instructs the navigation service to display a new page
        /// corresponding to the given key, and passes a parameter
        /// to the new page.
        /// Depending on the platforms, the navigation service might
        /// have to be Configure with a key/page list.
        /// </summary>
        /// <param name="pageKey">The key corresponding to the page
        /// that should be displayed.</param>
        /// <param name="parameter">The parameter that should be passed
        /// to the new page.</param>
        /// <exception cref="System.ArgumentException">pageKey</exception>
        public void NavigateTo(string pageKey, object parameter)
        {
            lock (_pagesByKey)
            {
                if (!_pagesByKey.ContainsKey(pageKey))
                {
                    throw new ArgumentException(
                        string.Format(
                            "No such page: {0}. Did you forget to call NavigationService.Configure?",
                            pageKey),
                        "pageKey");
                }


                ////var frame = this.rootFrame ?? ((Frame)Window.Current.Content);
                var frame = this.GetFrame();

                if (frame != null)
                {
                    frame.Navigate(_pagesByKey[pageKey], parameter);
                }
            }
        }

        /// <summary>
        /// Adds a key/page pair to the navigation service.
        /// </summary>
        /// <param name="key">The key that will be used later
        /// in the <see cref="NavigateTo(string)"/> or <see cref="NavigateTo(string, object)"/> methods.</param>
        /// <param name="pageType">The type of the page corresponding to the key.</param>
        public void Configure(string key, Type pageType)
        {
            lock (_pagesByKey)
            {
                if (_pagesByKey.ContainsKey(key))
                {
                    throw new ArgumentException("This key is already used: " + key);
                }

                if (_pagesByKey.Any(p => p.Value == pageType))
                {
                    throw new ArgumentException(
                        "This type is already configured with key " + _pagesByKey.First(p => p.Value == pageType).Key);
                }

                _pagesByKey.Add(
                    key,
                    pageType);
            }
        }

        /// <summary>
        /// Gets the frame.
        /// </summary>
        /// <returns>The current frame.</returns>
        protected virtual Frame GetFrame()
        {
            var current = Window.Current.Content;

            if (current == null)
            {
                return new Frame();
            }            
        }
    }
}

In the platform project for UWP we then get the actual Frame.

using Levion.Lib.Mobile.Uwp.Services;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;

namespace Levion.Sems.Mobile.Uwp.Services
{
    /// <summary>
    /// Navigation service to navigate the views.
    /// </summary>
    public class NavigationService : MenuNavigationService
    {
        /// <summary>
        /// Gets the frame.
        /// </summary>
        /// <returns>
        /// The current frame.
        /// </returns>
        protected override Frame GetFrame()
        {
            var current = Window.Current.Content;

            if (current == null)
            {
                return new Frame();
            }

            // Get frame from hamburger menu.
            if (current is Views.Shell)
            {
                Views.Shell converted = current as Views.Shell;
                var menu = converted.Content as Lib.Mobile.Uwp.Controls.HamburgerMenu;
                return (Frame)menu.ContentFrame;
            }
            else
            {
                return current as Frame;
            }
        }
    }
}

In the OnLaunched() Method of the UWP Project we have the following instantiation for the Navigation Service:

            if (rootFrame == null)
            {
                // Create a Frame to act as the navigation context and navigate to the first page
                rootFrame = new Frame();

                rootFrame.NavigationFailed += OnNavigationFailed;

                if (e.PreviousExecutionState == ApplicationExecutionState.Terminated)
                {
                    //TODO: Load state from previously suspended application
                }

                // Place the frame in the current Window
                Window.Current.Content = new Views.Shell(ServiceLocator.Current.GetInstance<IMenuNavigationService>());
            }

A further adaption was necessary to fix the use of a Pivot control in combination with the hamburger menu. Since on Windows it uses a Pivot that was not responsive, we created our own style version of the Pivot that is responsive regarding the element titles. Here is the corresponding XAML code that is found in a LevionStyles.xaml file for the AdaptivePivot control. We are only posting the relevant portion, because mostly our AdaptivePivot corresponds to the normal Pivot style on UWP. The interesting part here is the NarrowState Visual State.

<VisualStateGroup x:Name="AdaptiveHeader">
                                <VisualState x:Name="NarrowState">
                                    <VisualState.StateTriggers>
                                        <AdaptiveTrigger MinWindowWidth="0"/>
                                    </VisualState.StateTriggers>
                                    <VisualState.Setters>
                                        <Setter Target="HeaderClipper.Margin" Value="48,0,0,0"/>
                                    </VisualState.Setters>
                                </VisualState>
                                <VisualState x:Name="NormalState">
                                    <VisualState.StateTriggers>
                                        <AdaptiveTrigger MinWindowWidth="512"/>
                                    </VisualState.StateTriggers>
                                    <VisualState.Setters>
                                        <Setter Target="HeaderClipper.Margin" Value="0,0,0,0"/>
                                    </VisualState.Setters>
                                </VisualState>
                                <VisualState x:Name="WideState">
                                    <VisualState.StateTriggers>
                                        <AdaptiveTrigger MinWindowWidth="1200"/>
                                    </VisualState.StateTriggers>
                                    <VisualState.Setters>
                                        <Setter Target="HeaderClipper.Margin" Value="0,0,0,0"/>
                                    </VisualState.Setters>
                                </VisualState>
                            </VisualStateGroup>
                        </VisualStateManager.VisualStateGroups>

To be continued …

Holger Schwichtenberg: Infotag: Softwareentwickler-Update 2017/2018 für .NET- und Webentwickler

Eine ganztägige Infoveranstaltung bietet ein Potpourri aktueller Themen für Softwareentwickler im Umfeld von .NET- und Webanwendungen: .NET, .NET Core, C#, Visual Studio, TypeScript, Angular, TFS/VSTS, DevOps, Docker und Cloud.

Stefan Henneken: IEC 61131-3: Parameter transfer via parameter list

Parameter lists are an interesting alternative for transferring parameters to PLC libraries. Strictly speaking, these are global constants (VAR_GLOBAL CONSTANT) whose initialization values can be edited in the Library Manager.

When declaring arrays, their boundaries must be defined as constants. At the time of compilation, it must be known how large the array should be. The attempt to define the array boundaries by a variable is rejected with an error message.

PROGRAM MAIN
VAR
  nUpperBound      : INT := 5;  
  aTest            : ARRAY [1..nUpperBound] OF INT;
END_VAR
 
// Compiler error: Border 'nUpperBound' of array is no constant value

Only the use of constants solves the problem.

PROGRAM MAIN
VAR
  aTest            : ARRAY [1..nUpperBound] OF INT;
END_VAR
VAR CONSTANT
  nUpperBound      : INT := 5;
END_VAR

This can lead to problems, especially in PLC libraries. For example, if you want to map a data buffer to an array, it is not necessarily known at the time of library development how large this data buffer has to be in the respective applications.

The solution here is a parameter list, which is inserted when creating a PLC library.

Pic01

A parameter list can be added at any point in the PLC project (the PLC project from which the PLC library originates). Parameter lists are usually created for the global variable lists (GVLs).

Pic02

The parameters are declared as constant global variables.

{attribute 'qualified_only'}
VAR_GLOBAL CONSTANT
  cUpperBound    : INT := 3;
  cString        : STRING := 'Hello';
  cIntArray      : ARRAY [1..cUpperBound] OF INT := [1, 2, 3];
  cStructArray   : ARRAY [1..3] OF ST_Test := [3( (nValue := 1,
                                                   fValue := 3.1,
                                                   wsValue := "abc") )];
  cEnum          : E_Test := E_Test.eValue02;
  cStruct        : ST_Test := (nValue := 1, fValue := 3.1, wsValue := "abc");
END_VAR

The variables can be pre-initialized by initialization values. Even more complex variable structures can be initialized with values.

These parameters can be edited in the Library Manager in the application in which the library was integrated. The Library Manager is opened by double-clicking on the respective library reference.

Pic03

The fields in the column Value (editable) can now be changed as required. These changeable constants can be used within the respective PLC library or in the application that references them.

FUNCTION F_AddArrayValues : INT
VAR_INPUT
  aTest      : REFERENCE TO ARRAY[1..Param.cUpperBound] OF INT; 
END_VAR
VAR
  nIndex     : INT;
END_VAR
 
FOR nIndex := 1 TO Param.cUpperBound DO
   F_AddArrayValues := F_AddArrayValues + aTest[nIndex];
END_FOR

The changed initialization values are stored in the respective PLC project file (*. plcproj). Thus, each application that references a library with a parameter list has its own values. The library itself remains unchanged.

The values set in this way also withstand a PLC reset, both a cold reset and resetting to the original values. The values are not reset until the library is removed and reinserted. This means that parameter lists can also be used to transfer general parameters.

All parameters in a parameter list are usually accessible via ADS. The fact that parameters always have to be declared with CONSTANT does not prevent them from being written via ADS.

If a parameter is used as an array boundary, changing these variables will have serious consequences. Especially if the value of the parameter is increased.

aTest   : ARRAY [1..MyLibrary.Param.cUpperBound] OF INT;

Even if the parameter is increased at runtime of the PLC program, the array retains its original size. If this parameter is also used in the loops, accesses outside the allowed array boundaries can occur.

FOR nIndex := 1 TO MyLibrary.Param.cUpperBound DO
  aTest[nIndex] := nValue;
END_FOR

To prevent this, the attribute tc_no_symbol can be used to prevent ADS symbol generation. Access via ADS using the symbol name is then no longer possible for this parameter.

VAR_GLOBAL CONSTANT
  {attribute 'tc_no_symbol'}
  cUpperBound             : INT := 5;
END_VAR

Manfred Steyer: angular-oauth2-oidc 2.1 released

Over the last weeks, I've worked on version 2.1 of my OpenId Connect (OIDC) certified library angular-oauth2-oidc which allows for implementing Authentication in Angular using external Identity Providers that support OAuth 2 or OIDC.

Here are the added features:

  • New Config API (the original one is still supported). This allows putting the whole configuration in an own file:

     // auth.config.ts
     
     import { AuthConfig } from 'angular-oauth2-oidc';
     
     export const authConfig: AuthConfig = {
     
       // Url of the Identity Provider
       issuer: 'https://steyer-identity-server.azurewebsites.net/identity',
     
       // URL of the SPA to redirect the user to after login
       redirectUri: window.location.origin + '/index.html',
     
       // The SPA's id. The SPA is registerd with this id at the auth-server
       clientId: 'spa-demo',
     
       // set the scope for the permissions the client should request
       // The first three are defined by OIDC. The 4th is a usecase-specific one
       scope: 'openid profile email voucher',
     }

    After defining the configuration object, you can pass it to the libraries configure method:

     import { OAuthService } from 'angular-oauth2-oidc';
     import { JwksValidationHandler } from 'angular-oauth2-oidc';
     import { authConfig } from './auth.config';
     import { Component } from '@angular/core';
     
     @Component({
         selector: 'flight-app',
         templateUrl: './app.component.html'
     })
     export class AppComponent {
     
         constructor(private oauthService: OAuthService) {
           this.configureWithNewConfigApi();
         }
     
         private configureWithNewConfigApi() {
           this.oauthService.configure(authConfig);
           this.oauthService.tokenValidationHandler = new JwksValidationHandler();
           this.oauthService.loadDiscoveryDocumentAndTryLogin();
         }
     }
  • New convenience methods in OAuthService to streamline default tasks:

    • setupAutomaticSilentRefresh()
    • loadDiscoveryDocumentAndTryLogin()
  • Single Sign out through Session Status Change Notification according to the OpenID Connect Session Management specs. This means, you can be notified when the user logs out using at the login provider:

    Single Sign out via Session Checks

    To use this feature, your Identity Provider needs to support it. You also have to activate it in the configuration:

     import { AuthConfig } from 'angular-oauth2-oidc';
     
     export const authConfig: AuthConfig = {
       [...]  
       sessionChecksEnabled: true,
       sessionCheckIntervall: 3 * 1000
     }

    The optional configuration option sessionCheckIntervall which defaults to 3000 msec defines the interval that is used to check whether the user has logged out at the identity provider.

  • Possibility to define the ValidationHandler, the Config as well as the OAuthStorage via DI

     [...],
     providers: [
         {provide: AuthConfig, useValue: authConfig },
         { provide: OAuthStorage, useClass: DemoStorage },
         { provide: ValidationHandler, useClass: JwksValidationHandler },
     ],
     [...]
  • Better structured documentation:

    Documentation

Uli Armbruster: Programmieraufgaben für Bewerber

Ich habe in diesem Post beispielhaft eine unserer Programmieraufgaben herausgezogen, die kürzlich unser neuer Mitarbeiter Hr. Jörg Weißbecker gelöst hat. In der Regel ist das Ziel ein möglichst hochwertiges Ergebnis zu programmieren, das z.B. die Prinzipien OCP und SoC solide umsetzt.

Zeitdruck ist an der Stelle völlig sekundär, sodass die Bewerber die Aufgaben immer von zuhause in aller Ruhe entwickeln können. Sie erhalten dann von mir nach jeder Iteration die nächste Anforderung übermittelt.

Am Ende besprechen wir das Ergebnis, sprich sie stellen ihren Gedankengang vor, reflektieren Probleme und stellen sich meinen Fragen.

 

Allgemeines vorab:

  • Es ist keine Oberfläche notwendig, es genügt eine Konsolenanwendung
  • Welche Bibliotheken genutzt werden, ist freigestellt
  • Das Schreiben von Tests ist optional
  • Aber: Setzte das objektorientierte Paradigma so gkonsequent als möglich um (!)

 

Iteration 1: Entwickeln Sie ein Programm, welches prüft, ob 2 Dateien identisch sind. Beispiel für den Aufruf:

Dublettenfinder.exe file1.txt file2.txt

 

Iteration 2: Statt 2 Dateien sollen jetzt 2 Ordnerpfade übergeben werden, für welche das Programm prüft, ob das Ordnerpaar Dubletten enthält. Wenn ja, soll es pro Dublette die Dateipfade gruppiert ausgeben. Beispiel:

Hash: xyxzasdfadsf

  • Datei1: c:\abc.pdf
  • Datei2: d:\efg.pdf

 

Iteration 3: Es soll möglich sein eine Liste von Verzeichnissen zu übergeben, sodass beliebig viele Verzeichnisse verglichen werden können. Außerdem sollen, sofern vorhanden, auch die Unterverzeichnisse geprüft werden.

 

Iteration 4: Fügen Sie bitte eine optionale Konfigurationsmöglichkeit hinzu, um nur bestimmte Dateitypen vergleichen zu können, sodass entweder alle Dateitypen, genau einen Dateityp oder n-Dateitypen verglichen werden können.

 

Iteration 5: Machen Sie den Algorithmus austauschbar, sodass der Identitätscheck z.B. anhand von Sha256 Hashwerten oder Bildanalyse durchgeführt werden kann.

 

Iteration 6: Nutzen Sie einen IoC Container, um dynamisch beim Start der Anwendung alle vorhandenen Algorithmen zu laden und dann vom User in der Konsole auswählen zu lassen. Jeder Algorithmus soll in einer eigenen Assembly liegen. Machen Sie dann eine Assembly für MD5 und Sha256.

 

Die  Lösung zu diesem Zeitpunkt müsste sich Fragen bzgl. der Erweiterung der folgenden 2 Änderungen stellen:

  • Lösche Dubletten anhand eines bestimmten Patterns, z.B. alle in Verzeichnis ‚xy‘ oder mit Dateinamen, die ‚yz‘ enthalten.
  • Ermögliche die Erkennung von doppelten Bildern anhand der Bilderkennung und nehme immer die beste Auflösung.

 

Kürzlich gab es in der dotnetpro einen Artikel von Stefan Lieser, welcher eine recht ähnliche Kata gelöst hat. Die Lösung von Herrn Weißbecker befindet sich hier.


Einsortiert unter:Development, German Tagged: Lernen, Patterns

Holger Schwichtenberg: Erste Schritte mit der PowerShell Core unter Ubuntu-Linux

Die plattformunabhängige PowerShell Core hat mittlerweile den Stand "Beta 6" erreicht und basiert auf der fertigen Version 2.0 von .NET Core. Dieser Beitrag zeigt die Installation auf Ubuntu und erste Schritte in der Anwendung.

MSDN Team Blog AT [MS]: Homie – Ein Bot der die menschliche Sprache versteht

Heute freue ich mich besonders Euch einen Gast Beitrag von Stephan Bisser, präsentieren zu können:

Die Begriffe „Smart Homes“, „Smart Vehicles“ oder „Smart Everything“ sind heutzutage allgegenwärtig. Jedes Gerät, welches wir im Alltag einsetzen, muss „smart“ sein, damit wir es überhaupt benutzen. Es gibt heutzutage eine Vielzahl an Möglichkeiten ein Gerät mit Intelligenz zu versehen, ob das Spracherkennung ist oder das Gerät autonom Aktionen durchführen kann anhand von antrainiertem Wissen um uns das Leben zu erleichtern. Die wichtigste Komponente, um dieses Ziel zu erreichen, ist in jeder Variante immer dieselbe, egal ob das Produkt von Microsoft, Google oder Amazon ist. Hinter jedem dieser Produkte steckt stets künstliche Intelligenz (engl.: „Artificial Intelligence“) oder kurz AI.

Da ich mich sehr intensiv mit dem AI Ökosystem von Microsoft beschäftige (insbesondere mit den Themen Cognitive Services, Bot Framework und Azure Machine Learning) wollte ich einen intelligenten Service entwickeln, der mir das Leben erleichtert. Mein Hauptziel war es eine Lösung zu entwerfen, die gewisse Aktionen durchführen, sobald ich diesem Service eine Anweisung gebe. Und so entstand die Idee zu „Homie“ – meinem persönlichen Assistenten der mir gewisse alltägliche Dinge abnimmt. Nun wollt Ihr bestimmt wissen wer oder was Homie ist und vor allem was er kann. Die nächsten Absätze sollen hierzu ein bisschen die Funktionsweisen und den Aufbau der Lösung erläutern.

Wie sieht das Service-Design aus?

Stellt Euch vor Ihr sitzt im Büro und seit wirklich vertieft in die Arbeit. Dabei sollte man ja stets konzentriert und fokussiert sein auf das was man macht um die Aufgabe bestmöglich zu meistern. Da kann es schon mal vorkommen, dass man die Zeit übersieht und es draußen plötzlich dunkel wird. Anfangs ist das ja noch kein Problem, jedoch wenn es draußen wirklich stockfinster ist findet man sich selbst in einem dunklen Raum wieder wobei das einzige Licht durch den Monitor erzeugt wird. Als gemütliche Person ist man jedoch zu faul um aufzustehen und das Licht im Raum anzuschalten. Wie wäre es, wenn ich Euch sage, dass man ab sofort nicht mehr aufstehen muss, um das Licht anzuschalten? Wie wäre es, wenn Homie das für Euch erledigen würde? Um Homie dies beizubringen müsst Ihr lediglich die folgenden Schritte bewerkstelligen und die Services in der nachfolgenden Grafik miteinander verbinden und schon kann er für euch Aktionen durchführen.

clip_image002

Abbildung 1: Diese Lösung verbindet einige Azure Services miteinander um den Bot intelligent zu machen

Wie mache ich den Bot intelligent?

Zuerst muss der Bot einmal erstellt werden, indem man im Azure Portal einen neuen Azure Bot Service anlegt. Nachdem der Service fertig provisioniert wurde, ist es an der Zeit den Bot zu konfigurieren und so zum Leben zu erwecken. Ich wählte einen NodeJS Bot vom Typ „Language understanding“, da hierbei bereits einige Text- und Spracherkennungsfeatures vorkonfiguriert sind. Dieser Typ nutzt die Language Understanding Intelligent Service (LUIS) API aus dem Azure Cognitive Services Portfolio. Nun ist es an der Zeit die sogenannten „Intents“ zu der automatisch erstellten LUIS App hinzuzufügen, damit der Service weiß, auf welche Textmuster er getriggerd werden soll. Um dies durchzuführen muss man auf https://www.luis.ai navigieren, sich einloggen (oder registrieren) und danach einige der vordefinierten Intents hinzufügen. In diesem Szenario habe ich die Intents, welche im folgenden Bild zu sehen sind, gewählt, da diese für die Steuerung von Heimgeräten hervorragend geeignet sind.

clip_image004

Abbildung 2: Dies sind die gewählten vordefinierten LUIS Intents

Nachdem die Intents hinzugefügt wurden ist es an der Zeit die LUIS App zu testen und zu trainieren, damit die Sprach- und Texterkennung funktioniert. Sobald dies erfolgreich durchgeführt wurde, ist es an der Zeit wieder zum Azure Portal zurück zu kehren, um dem Bot einiges an „Magie“ einzuflößen. Die folgenden Codezeilen sorgen dafür, dass der Bot tatsächlich intelligent wird und dass der Bot Aktionen durchführt an Hand der erkannten Intention, welche aus der Nachricht des Users hervorgeht.

intents.matches('HomeAutomation.TurnOn',function(session, args){ 
    session.sendTyping();
    if(args.entities[0]) {
        var deviceEntity = builder.EntityRecognizer.findEntity(args.entities, 'HomeAutomation.Device');
        var roomEntity = builder.EntityRecognizer.findEntity(args.entities, 'HomeAutomation.Room');
    if (roomEntity){
        session.send("Ok, I will turn the %s on in the %s", deviceEntity['entity'], roomEntity['entity']);
        session.endDialog();
    var commandJSON= {
        "device": deviceEntity['entity'],
        "room": roomEntity['entity'],
        "action": "Turn on"
        };
        request({
            url: "<URL of your Azure Function>",
            method: "POST",
            json: true, // <-- This is very important!
            body: commandJSON
            }, function (error, response, body){
            console.log(response);
            });
        }
    } 
});

Das Internet der Dinge

Nachdem der Bot nun bereit ist um Nachrichten zu erhalten und zu verarbeiten, muss der IoT Part noch an die Service Landschaft angebunden werden, um die smarten Heimgeräte zu steuern. Dazu wird ein Azure IoT Hub verwendet, der mit den Geräten kommuniziert und Kommandos an die Geräte senden kann beziehungsweise auch Telemetrydaten von den Geräten empfangen und weiteleiten kann. Also erstellen wir im Azure Portal einen neuen IoT Hub und fügen Geräte hinzu. In diesem Szenario habe ich ein Gerät für mein Büro hinzugefügt, welches ein Raspberry PI ist. Somit habe ich das Device „office_controller” genannt, wie in der nächsten Grafik zu sehen ist.

clip_image005

Abbildung 3: Bei der Anlage eines neuen Gerätes am IoT Hub ist es wichtig, dass der Authentication Type "Symmetric Key" ausgewählt ist

Azure Functions um die Server zu schonen

Nachdem der IoT Hub konfiguriert ist, wird der letzte Azure Service konfiguriert. Ich habe mich dazu entschieden den Großteil meiner Logik in eine Azure Function (JavaScript HTTP Trigger) zu verpacken, da diese perfekt dazu geeignet sind. Nachdem die Azure Function im Portal angelegt wurde müssen zu aller erst die Dependencies via NPM mittels folgendem Befehl in der Command Line der Function installiert werden:

npm install azure-iothub –save

Nun können wir im Code auf dieses Package referenzieren und es nutzen. Der Code der auf die IoT Hub Funktionalitäten zugreift sieht wie folgt aus:

var Client = require('azure-iothub').Client;
var Message = require('azure-iot-common').Message;
var connectionString = '<yourIoTHubConnectionString>';
var targetDevice = 'office_controller';
var serviceClient = Client.fromConnectionString(connectionString);

Um eine Nachricht an den Azure IoT Hub zu übermitteln werden folgende Codezeilen verwendet:

var message = new Message('lightsOnOffice');
message.ack = 'full';
message.messageId = "ID_001";
serviceClient.send(targetDevice, message, printResultFor('send'));

Und wo bleiben die Devices?

Um die Cloud Services jetzt auch wirklich nutzen zu können, muss das Gerät welches gesteuert werden soll noch konfiguriert werden. Ich habe mich in diesem Fall für ein Raspberry PI 3 entschieden, da es sich für diesen Showcase gut eignet. Um zu zeigen, dass Homie wirklich das Licht ein/ausschalten kann habe ich in diesem Fall ein Breadboard mit dem PI verbunden auf dem ich ein LED Licht angebunden habe, da ich keine „kluge Glühbirne“ bei der Hand hatte. Es geht ja schließlich auch darum zu zeigen, dass Homie in der Lage ist Geschriebenes zu verstehen und eine Aktion anhand der Usernachrichten zu setzen, oder? Die Bauanleitung für den PI samt Breadboard und LED sieht wie folgt aus:

clip_image007

Abbildung 4: Pinbelegung des Raspberry PI wie auf GitHub zu finden

Nachdem die Pinbelegung so aussieht wie auf der obenstehenden Grafik ist es an der Zeit am PI NPM und NodeJS zu installieren, da wir eine kleine NodeJS Applikation in Betrieb nehmen werden, welche die Befehle die vom Azure IoT Hub kommen auch empfangen und ausführen können. Der folgende Code ist dafür zuständig das Licht im Büro ein- beziehungsweise auszuschalten, wenn der Befehl dazu vom IoT Hub übermittelt wird.

var wpi = require('wiring-pi');

// GPIO pin of the led
var configPin = 7;
wpi.setup('wpi');
wpi.pinMode(configPin, wpi.OUTPUT);

// Turn LED on
var isLedOn = 0;
isLedOn = +!isLedOn;
wpi.digitalWrite(configPin, isLedOn );

Und das ist es im Prinzip. Unser Homie ist nun bereit Nachrichten zu empfangen und die richtigen Aktionen daraus abzuleiten. So sollte der Bot bei der Nachricht “Turn the lights on in the office” das Licht beziehungsweise die LED Lampe im Büro einschalten.

Einbinden des Bots in Microsoft Teams

Nun da der Bot soweit fertig ist, müssen wir ihn unseren Usern zur Verfügung stellen. Und welches Tool eignet sich besser um mit einem Bot zu chatten als Microsoft Teams? Die verfügbaren Kanäle die in der nächsten Illustration zu sehen sind können unter https://dev.botframework.com/bots hinzugefügt werden. Ihr könnt natürlich für euren Bot eure Lieblingskanäle hinzufügen.

clip_image009

Abbildung 5: Dies sind die momentan verfügbaren Kanäle in denen der Bot eingebunden werden kann

Nachdem der Teams Kanal hinzugefügt wurde, ist der Bot bereit Nachrichten in Teams zu empfangen ohne weitere Konfiguration:

clip_image011

Abbildung 6: Die Kommunikation mit dem Bot in Teams ist gleich wie die Kommunikation mit Arbeitskollegen

Das Fazit

  • Azure, Cognitive Services und das Bot Framework passen hervorragend zusammen, da die Interaktion zwischen den Services out-of-the-box funktioniert
  • Azure Functions eignen sich stets gut um Applikationslogik zu hosten und diese anderen Services bereitzustellen
  • Bots die die menschliche Sprache verstehen werden in Zukunft sowohl im Consumer als auch im Business Bereich immer mehr eingesetzt, da diese einige alltäglichen Dinge erleichtern können
  • Azure Cognitive Services können einen Service etwas menschlicher gestalten, da sie es den Menschen erlauben mit Services zu interagieren als wären diese ebenfalls Menschen

clip_image012

Eine kurzer Showcase von Homie ist unter https://www.youtube.com/watch?v=kZPuI3JwVww zu finden.

Christian Dennig [MS]: Secure an Aurelia Single Page App with Azure Active Directory B2C / MSAL

If you create a modern web application with an API / REST backend and a Single Page Application (SPA) as your frontend, that you want to run in the internet, you definitely don’t want to handle security / user management on your own. You will want to use a service like Auth0 or Azure Active Directory to handle authentication and/or authorization.

In this example, we will develop a basic Aurelia frontend application, that will be secured via Microsoft Authentication Library (MSAL) for JavaScript backed by a custom Azure Active Directory (B2C). The B2C directory is the identity store where users of our application will be stored. The Aurelia app will login to the B2C directory via MSAL and use an ID token provided after a successful login to call the web API (authorization token in the request header).

23

Implicit Flow

Let’s start by creating the directory…

Create a B2C Directory

The creation of an Azure Active Directory is quite simple. Log in to your Azure account and select “New”. Search for “Azure Active Directory B2C” and select “Create” –> “Create a new Azure AD B2C Tenant”. Enter your desired organization and domain name and select the country/region you want the directory to be located. In my example, the configuration looks like that:

2

After the tenant is created, we have to add an application that can access the directory. Therefore, go to the newly created B2C directory and select “Applications” in the settings blade. Click “Add” and enter a name for the application. Also, be sure to select “Include web app / web API”, because we want to use the “implicit flow” as shown in the intro section above. In the reply URL enter “http://localhost:9000“, because this is the URL our Aurelia app will be running on. After a successful login, Azure AD will redirect the browser to that given URL.

4

B2C Application Settings

Note down the Application ID and the domain name (https://{yourtenantname}.onmicrosoft.com – in my case https://aureliab2c.onmicrosoft.com), as we will need the values when we configure the authentication of our Web API.

Next, to give users the ability to register for the application, you have to create a policy within the directory that allows users to create an account. You also need a policy to sign in and – if you want to – a policy to reset the password for a user.

In this example, we will only use “local accounts” (login with username and password). You can also add further identity providers like Google or Facebook to add social login capabilities to your application. To keep it simple, we only support local accounts at the moment.

Fortunately, we can create a policy that allows users to add an account and configure the login process in one single configuration. It’s called “Sign-up or sign-in policy”.

Click on the corresponding entry in the configuration blade and add a new one.

Next, you have to enter information about the identity provider to use (in our case “Local Account”, the signup attributes (fields a user can enter during signup like “Given Name” and “Surname”) and application claims (information that will be sent with the identity token after login). In our application, the configuration is as follows:

5

Identity Provider

6

Signup Attributes

7

Application Claims

Of course, you can adjust a lot more settings, but for our case, this is enough in the first place.

After you have created the policy, you can test it by clicking on the “Run Now” button and create a user for yourself.

19

Login Page / B2C

20

Signup Page

As a last step, note down the name of the policy you have just created – in our case: B2C_1_signupin. We need it later on…

Our directory is now ready to go, so let’s head over to the API…

Create REST API

The REST API we want to develop is based on ASP.NET Core 2.0 and will be secured by the Azure AD we just created. To achieve this in a very convenient way, you can enter the connection to the Azure AD during project setup.

In Visual Studio, open “File –> New — Project” and select “ASP.NET Core Web Application” and in the following wizard, select “Web API”.

Create Web API

Create Web API

To enter the B2C data you noted down in the last steps, click on the “Change Authentication” button, select “Individual User Accounts” and enter the following details:

  • the domain name of your B2C tenant
  • the application ID that corresponds to the API
  • the policy to use
11

Auth Configuration

The wizard adds settings to you appsettings.json file and configures you startup class to use the B2C information you just entered.

While we want to focus on the authentication process and securing the backend, the API will only provide one controller that can be called on the path “people“. The GET request on that path will return a list of characters (from StarWars 😉 – well, just sample data). As the controller is annotated with the Authorize attribute, requests will only be successful, if the caller adds the Authorization header with a valid ID / Bearer token. The token will be provided by the AAD B2C directory after a successful login. More one that later when we implement the Aurelia app.

So, as I said…if you want to call the people controller without a valid token, you will receive a 401 status code (Unauthorized) – the way we want it. To test that behavior, we can use Postman.

13

401 Unauthorized / Call API without a Bearer Token

The backend is now secured by the Azure B2C directory…so let’s add the frontend.

Create the Aurelia SPA

Before we can start developing the frontend application with Aurelia, we have to install the command line interface (CLI). To do this, open a command prompt an enter the following command:

npm install -g aurelia-cli@latest

This command will install the Aurelia CLI globally on your machine. After the command returns, we can create a new Aurelia application via

au new frontend

The command (btw. frontend is the name of our application) will start a wizard where you can select different options, e.g. if you want to use plain JavaScript or TypeScript.

15

Start the wizard

16

Select Options

We will use TypeScript for our application.

After you have entered all the necessary information, you can finish the wizard and install the dependencies (you will be ask by the wizard) afterwards.

The result will be an Aurelia application with minimal dependencies, built with TypeScript. You can test the application by running…

au run --watch

and opening the browser, pointing to http://localhost:9000.

To be able to call the API, request an ID token at the B2C directory and display the results from the people controller, we need a few more dependencies.

First, we need the current version of the MSAL JS library, which is – at the time of writing – still in preview for JavaScript. You can install it via NPM, but the current version available (0.11) has a few bugs. Therefore, we will take the current dev version directly from the GitHub repository 🙂

You can download the version from https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/out/msal.js and save it in a “lib” folder in the root directory of the Aurelia app.

Next, we need the Aurelia fetch library and jwt_decode (to decode the ID token we receive after login from the B2C directory). Install both dependencies via

npm install aurelia-fetch-client@latest jwt_decode@latest --save

Because we are “compiling” the application via the Aurelia CLI, we also need to add these additional libraries to the aurelia.json file located in the “aurelia_project” folder. Insert entries for the MSAL lib (line 4), aurelia-fetch-client (line 12) and jwt_decode (starting line 47) in the dependencies section of the JSON file.

Now we are ready to implement the application. As we want to focus on the authentication process, I will concentrate on the pieces that are important for our case.

  1. Create a settings file, where we can access the B2C settings during runtime.
  2. Add a class (in auth.ts file) that is responsible for handling the authentication process
    1. Check, if user is already authenticated
    2. Get the current ID / JWT token
    3. Login via B2C directory
    4. Logout
  3. Add a class (HttpConfig) that will configure the Aurelia HttpClient class to intercept each request and insert the Authentication header (Bearer token)

The Auth class is the “main actor” in our frontend example. It uses the MSAL class UserAgentApplication to login & logout…as well as getting the ID token after a successful login attempt.

As we want to secure the complete frontend application and not only a few path/routes, we use the Auth class in the bootstrapping process of the Aurelia app – in the main.ts file.

As you can see, after loading the Aurelia framework, we check if the user is already logged in (by calling isAuthenticated on the auth object). If the promise is rejected (user is not logged in), we redirect to the AAD login page by invoking auth.login. After entering the username and password and submitting the form, we will be redirected with an ID token as URL parameter (id_token) to our app. The MSAL library picks up the id_token and handles expiration checking and storing all the necessary data for us. If everything is ok, the next call to isAuthenticated will successfully resolve and the app loads the main class App (in app.ts). While loading the view and view-model, a call to the web API is invoked (in method activate, line 4 – one of the lifecycle methods of an Aurelia view. activate is called before the view is visible/attached to the DOM).

In the .NET Core application, the request headers are checked for a valid Bearer token. If the token provided is valid, the call is routed to the people controller and the results are sent to the Aurelia app, which in turn displays the list of…StarWars characters 🙂

To see the contents of the ID token in the frontend, we decode the token in the App class (see above) and display the results (given name, surname, email and token expiration date) in a message box.

22

Final Application

If the Logout button in the top menu is clicked, auth.logout is called, the MSAL library clears stored data about the ID token and endpoints and redirects to http://localhost:9000/#.

Wrap Up

As you have seen, it is possible to secure a REST API and a corresponding Single Page Application – in our case an Aurelia app – with Azure Active Direct B2C. Of course, the app is far away from “production-ready”, but it is a good example to get an impression of how you can integrate an OpenID Connect flow in your own application and secure a SPA in a very simple way.

You can find the complete sample on GitHub: https://github.com/cdennig/azure-b2c-aurelia-example

Have fun with it…

Cheers!


Uli Armbruster: Angular Starthilfe – 2-tägiger Workshop in Leipzig

Zur Unterstützung des Developer Open Space 2017 bieten wir allen Teilnehmern, die uns eine Kopie ihres Tickets an schulung@co-IT.eu zukommen lassen, die Teilnahme an unserem 2-tägigen Angular 4 Workshop zum reduzierten Preis von 1250€ an.

Das Training ist vom 11-12. Oktober in Leipzig und damit direkt vorm #devspace selbst, sodass eine separate Anfahrt nicht notwendig ist. In unserem Firmenprofil könnt ihr euch vorab über uns informieren.

Angular_full_color_logo.svg

Für alle diejenigen, die meinen Blog lesen und nicht zur Veranstaltung gehen können, habe ich ebenfalls eine spezielle Aktion: Wer mir als erstes an die oben genannte E-Mail-Adresse eine Nachricht mit dem Titel „ich blogge“ schreibt und darin seinen Blog verlinkt, der erhält ebenfalls einen Rabattcode.

Die Anmeldung ist ab sofort auf Eventbrite freigeschaltet.


Einsortiert unter:Development, Events, German, Web Tagged: Workshop

Uli Armbruster: Einfachere Reisekostenabrechnungen für alle

Optimierung unserer Prozesse ist bei uns schon fast eine Leidenschaft, daher evaluieren wir regelmäßig neue Tools. Eines davon ist Einfach Reisekosten.

Unsere Anforderungen waren klar umrissen:

  • Skalierung auch mit 30 Mitarbeitern möglich
  • Reduzierung der benötigten Zeit pro Consultant auf 30 Minuten pro Monat
  • Reduzierung der Durchlaufzeit im Rechnungswesen
  • Auswertungen zur Kalkulation unserer Preise möglich
  • Einheiltung der immer aktuellen gesetzlichen Regelungen
  • Nice-to-Have: Integration in Debitoor (*)

*) Zu unserer Buchhaltungssoftware Debitoor kann ich gerne einen Artikel verfassen, wenn Interesse besteht. Schreibt mir einfach einen Kommentar.

Nach mehreren Tests, unter anderem dank des schnellen und freundlichen Supports, welcher uns einen Demo Team Account zur Verfügung gestellt hat, haben wir jetzt das Abo gebucht. Besonders gefällt uns, dass Entfernungen anhand von Google Maps automatisch berechnet werden und längere Projekte, in denen immer die gleichen Fahrten anfallen, mit wenigen Schritten erfasst werden können. Das Synchronisieren in Debitoor und die korrekte Zuordnung gemäß DATEV-Kostenrahmen funktioniert problemlos. Selbst so seltene Vorgänge wie das Erfassen von W-LAN Kosten, SIM-Karten, Vignetten, Fähren u.v.m. lassen sich damit abbilden. Durch die genaue Zeiterfassung werden die jährlich aktualisierte Pauschalen automatisch erfasst. Eine Reduktion durch Eingabe der gestellten Verpflegung z.B. von Frühstück im Hotel rundet das Ganze ab. Die Administration der Mitarbeiter scheint auf den ersten Blick auch keine Wünsche offen zu lassen.

Schwächen sehen wir aktuell noch im Zusammenspiel mit Debitoor. So findet kein Abruf des Kundenstamms statt, um den Kunden bzw. das Projekt in der Abrechnung per Auswahl erfassen zu können. Die PDF mit der Abrechnung würden wir darüber hinaus gerne customizen. Preislich wäre ein Rabatt mit steigender Mitarbeiterzahl wünschenswert.

Gerne ziehe ich Vorschläge vom Leser in Betracht. Postet diese in die Kommentare.


Einsortiert unter:CIO Topics, German, Misc Tagged: Produktbewertungen

Kazim Bahar: Tutorial: Twitter Sentiment Analyse per WPF-Client

In diesem sehr interessanten Tutorial von Microsoft wird gezeigt, wie man mit Hilfe der Twitter...

Uli Armbruster: ebuero leider nicht für alle geeignet

Kürzlich haben wir für uns den Dienst von ebuero evaluiert. Danke an dieser Stelle an Hans-Peter Schelian und Thomas Bandt für die Empfehlung.

Das Ziel war es, dass unsere Kunden 24/7 365 Tage im Jahr immer jemanden erreichen können – wohlgemerkt einen Menschen! Das muss nicht zwingend jemand sein, der sofort weiterhelfen kann, aber es sollte damit gewährleistet sein, dass der Anrufer darauf vertrauen konnte, dass sich um sein Anliegen asap gekümmert wird.

Mir hat besonders gefallen, dass unser Kunden immer Telefonistinnen aus der gleichen „Sekretärinnen-Gruppe“ bekommen hätten, sprich 3-4 Mitarbeiter von ebuero wären immer für unsere Anrufer zuständig gewesen. Dass diese bereits alle fließend Englisch sprechen, war ein weiterer  Pluspunkt. Gegen einen Aufpreis wären Muttersprachler möglich. Die Preise waren für mein Empfinden durchaus akzeptabel.

Leider konnten einige unsere must-have Anforderungen nicht erfüllt werden:

  • Freigabe von Nummernblöcken in der VIP-Liste: Leider müssen Nummern einzeln eingetragen werden. Bereiche der Form +49-721-1234-XXX, wie sie für größere Kunden notwendig sind, werden nicht unterstützt. Speziell hierfür fehlt mir das Verständnis, wieso das nicht möglich ist. Während des Schreibens fällt mir auf, dass es interessant zu wissen wäre, ob CSV-Dateien hochgeladen werden können. So ließe sich diese Einschränkung vielleicht umgehen.
  • Benutzerbezogene Benachrichtigungen sind nicht möglich, d.h. die Notifikation über einen Anruf geht an genau eine anzugebende E-Mail-Adresse. Eine Zuordnung zu der Person, die vom Kunden versucht wurde anzurufen, gibt es nicht. Eine Anpassung der E-Mail, um per Regeln in unserem Exchange die automatische Weiterleitung zu gewährleisten, ist ebenfalls nicht möglich.
  • Das direkte, automatisierte Weiterleiten der VIP Anrufer ist nicht möglich, sprich in jedem Fall landet der Kunde erst im virtuellen Sekreteriat.
  • Das Kalender-Sharing Feature, um der Sekretärin Zugriff auf die Termine von uns zu geben, um dem Kunden zu erwartende Rückrufzeiten zu nennen, unterliegt Einschränkungen, die ich hier nicht weiter aufzählen will.

 

Wenn ein Leser uns einen alternativen Dienst empfehlen kann, wäre ich dankbar. Gerne dürfen auch die Nutzer von ebuero hier Verbesserungsvorschläge in die Kommentare posten, da ich den Artikel meinem Ansprechpartner in der Hoffnung zusenden werde, dass diese Punkte noch verbessert werden.


Einsortiert unter:CIO Topics, German, Misc Tagged: Produktbewertungen

Manfred Steyer: Clever white space handling for better performance in Angular 5+ and 4.4

This post is part of my Angular Performance Tuning article series. If you looking for ways to make your Angular application faster, you might find the other articles in this series useful too.

Update: This optimization technique has now been merged into Version 4.4.0-rc.0 too.

Although tuning an application's performance can be difficult, sometimes all we need to do is laying back and waiting for the next version of the used framework. This especially holds true for Angular as the Core Team is working constantly on improving things under the hoods. A good example for this is the conciser code the Angular Compiler emits beginning with Version 4 or the Angular Bundle Optimizer that transforms code to make it more treeshakable.

Another of these optimization techniques landed with 5.0.0-beta.4 about two weeks ago. It allows the compiler to remove unneeded (consecutive) white spaces from text nodes and to remove even whole text nodes that only contain white space characters. This leads to less code emitted by the AOT compiler and therefore to smaller bundle sizes as well as faster start up times.

In this post I'm describing how to use it, which performance gains I measured when applying to an example application as well as how this approach works under the covers. The example application I've used for this can be found in my GitHub repository.

Removing white spaces

While removing white spaces from HTML is most of the time a safe operation (at least when respecting some rules outlined below), it can also destroy your layout. Because of this, you have to opt-in for it. For instance, you could to this on component level by setting the new property preserveWhitespaces to false:

@Component({
  selector: 'app-passenger-search',
  templateUrl: './passenger-search.component.html',
  styleUrls: ['./passenger-search.component.css'],
  preserveWhitespaces: false
})
export class PassengerSearchComponent  {
}

Instead of this, you can also set this property on application level by passing the new preserveWhitespaces command line option when calling the Angular Compiler ngc. At the time of writing, the Angular CLI did not support this option but this seems to be just a matter of time.

In addition to this, you can protect a section within a template by adding the attribute ngPreserveWhitespaces to a tag. If you want to spot a space that isn't allowed to be removed, you can use the pseudo-entity &ngsp;, which is converted to a space in the emitted code. Don't confuse this with the well known entity.

Results for example application

When I applied this optimization technique to my example application, I got the following results:

  • ~ 8% reduction in size for the javascript bundle
  • ~ 4% reduction in size for the gzipped javascript bundle
  • ~ 9% faster application startup time

As this shows, this approach allows for an appreciable performance gain. This gain we get after "just" removing white spaces is higher than you might have expected. The next section shows how this is possible.

Under the hoods

To understand why such a high performance gain is possible by "just" removing white spaces, let's look at a simple template:

<h1>
  Search for Passengers 
</h1>
<p>
  Lorem ipsum dolor sit amet.
</p>

When we compile this template, the AOT compiler transforms it to JavaScript code:

function View_PassengerSearchComponent_0(_l) {
  return __WEBPACK_IMPORTED_MODULE_1__angular_core__["_37" /* ɵvid */](
      0,
      [
        (_l()(), __WEBPACK_IMPORTED_MODULE_1__angular_core__["_15" /* ɵeld */](
                     0, null, null, 1, 'h1', [], null, null, null, null, null)),
        (_l()(), __WEBPACK_IMPORTED_MODULE_1__angular_core__["_35" /* ɵted */](
                     null, [ '\n  Search for Passenger \n' ])),
        (_l()(), __WEBPACK_IMPORTED_MODULE_1__angular_core__["_35" /* ɵted */](
                     null, [ '\n' ])),
        (_l()(), __WEBPACK_IMPORTED_MODULE_1__angular_core__["_15" /* ɵeld */](
                     0, null, null, 1, 'p', [], null, null, null, null, null)),
        (_l()(), __WEBPACK_IMPORTED_MODULE_1__angular_core__["_35" /* ɵted */](
                     null, [ '\n  Lorem ipsum dolor sit amet.\n' ]))
      ],
      null, null);
}

As you can see, this code contains a function call for each dom node we need. For instance, there is a function call for the h1 tag as well one for the p tag. In addition to that, there are function calls for text-nodes. When analyzing this, we find characters as well as even a text node that aren't displayed in the browser due to the way HTML works. Examples are consecutive white spaces as well as empty text nodes like the one between the closing h1 tag and the opening p tag.

When using the discussed technique, the compiler removes these whites spaces or these text nodes. The following image shows a diff between the emitted code above and the code that is created after activating white-space-removal:

diff between version with and w/o whitespaces

This shows that a whole function call as well as some consecutive white space characters are removed. When we just count the removed lines for the sake of simplicity we see they make up about 10 % of all lines. This explains the reduction in bundle size we've faced. Of course, a reduction in function calls means that the Browser can download the bundles faster as well as Angular has less to do when starting the application. Both leads to the improvement of start up performance mentioned above.

Holger Schwichtenberg: Roadmap für Entity Framework Core 2.1

Zwei Wochen nach dem Erscheinen von Entity Framework Core 2.0 mit doch eher enttäuschenden Neuerungen hat Microsoft nun eine Roadmap für die Version 2.1 veröffentlicht und dabei versprochen, zumindest einige der zuvor als "kritisch" und "High Priority" eingestuften Funktionen endlich zu realisieren.

Code-Inside Blog: IdentityServer3 with WindowsAuthentication with ASP.NET WebApi & ASP.NET & WPF App

Please note: In my sample and in this blogpost I cover IdentityServer 3, because last year when I was working on the sample and our real implementation IdentityServer4 (a rewrite of IdentityServer 3) was in beta. My guess is, that most stuff should still apply even if you are using IdentityServer4, but I didn’t test it.

Also: I’m not a security expert - this might be all wrong, but currently this more or less works for us. If you find something strange, please let me know!

Overview

The sample consists of the following projects:

IdentityTest.IdServerHost: That’s the central IdentityServer in our solution. It contains all “clients” & “identityprovider” settings. IdentityTest.WinAuth: This is our Windows-Authentication provider. Because of the nature of WindowsAuth it needs to be an extra project. This needs to be hosted via IIS (or IIS Express) with Windows authentication enabled. The ASP.NET app acts as a bridge and will convert the Windows-Auth ticket into a SAML token, which can be integrated into the IdentityServer. It is more or less like a mini-ADFS. IdentityTest.WebApp: The WebApp itself can be used via browser and also hosts a WebApi. The WebApi is secured by the IdentityServer and secured pages will trigger the authentication against the IdServerHost. IdentityTest.WpfClient: With the WPFApp we want to get a AccessToken via a WebBrowser-Control from the IdServerHost and call the WebApi that is hosted and secured by the very same IdServerHost.

The IdentityServer team did a great job and have a large sample repository on GitHub.

x

I will talk about each part in my sample. Now lets beginn with…

The ‘IdServerHost’ Project

The IdentityServerHost is a plain ASP.NET application. To include the IdentityServer3 you need to add IdentityServer3 NuGet-Package.

The code is more or less identical with the Minimal-Sample from the IdentityServer3 team, but I disabled the SSL requirements for my demo.

Be aware: The IdentityServer use a certificate to sign the tokens, but this has nothing to do with the SSL certificate. This was a hard learning curve for me and IISExpress or something messed things up. In the end I disabled the SSL requirements for my development enviroment and could start to understand how each part is communicating with each other. The signing certificate in the sample is the sample .pfx file from the offical samples.

Remember: DO USE SSL IN PRODUCTION. Oh - and use the Cert-Store for the signing certificate as well!

Cert-Handling in IdentityServer in a nutshell: Do use SSL in production with a valid SSL certificate and setup another certificate that the IdentityServer will use to sign the tokens.

Besides the SSL stuff the most important stuff might be the client-registration and the identity-provider-registration.

The IdentityServer - as the auth-central - knows each ‘client’ and each ‘identity-provider’. Make sure all URLs are correct otherwise you will end up with errors. Even a slightly difference like ‘http://test.com/’ and ‘http://test.com’ (without the trailing slash at the end) will result in strange errors.

The ‘WinAuth’ Project

As already written this is our Windows-Authentication provider. Of course, it is only needed if you need WinAuth. If you want to use any other provider, like a Google/Microsoft/Facebook/Twitter-Login, then this is not needed. It is a bridge to the enterprise world and works quite well.

In the project I just reference the IdentityServer.WindowsAuthentication NuGet-Package and I’m nearly done. In the config I need to insert the URL of my IdentityServer host - those two parts needs to know each other and they will exchange public keys so they can trust each other.

For this trust-relationship the WinAuth provider has its own certificate. Actually you can reuse the same cert from the IdentityServerHost but I’m not sure if this is super secure, but it works.

The code and sample can also be found on the offical GitHub repo

The ‘WebApp’ Project

This project is a regular ASP.NET MVC project with WebApi 2 included. Nothing ASP.NET Core related, but the actual doing would be pretty similar.

On this page there are two ways to interact:

  • Via Browser
  • Via the WebApi

Browser Auth via OpenIdConnect Auth:

The NuGet Package Microsoft.Owin.Security.OpenIdConnect does the heavy lifting for us. In combination with the Microsoft.Owin.Security.Cookies NuGet package the authentication will kick in when someone access a [Authorize] marked Controller. The Cookie-Auth will preserve the identity information.

WebApi Auth:

To use the protected WebApi with any HTTP client the request must have a JWT bearer token. The implementation is super simple with this NuGet package IdentityServer3.AccessTokenValidation.

Setup of both auth options:

The setup is quite easy with the NuGet packages:

public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        app.UseIdentityServerBearerTokenAuthentication(new IdentityServerBearerTokenAuthenticationOptions
        {
            Authority = ConfigurationManager.AppSettings["Security.Authority"],
            RequiredScopes = new[] { "openid" }
        });

        app.UseCookieAuthentication(new CookieAuthenticationOptions()
        {
            AuthenticationType = "cookies",
        });

        app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions()
        {
            AuthenticationType = "oidc",
            SignInAsAuthenticationType = "cookies",
            Authority = ConfigurationManager.AppSettings["Security.Authority"],
            ClientId = "webapp",
            RedirectUri = ConfigurationManager.AppSettings["Security.RedirectUri"],
            ResponseType = "id_token",
            Scope = "openid all_claims"
        });
    }
}

It is important to use the correct “clientIds” and URLs as configured in the IdentityServer, otherwise you will receive errors from the IdentityServer.

The ‘WpfClient’ Project

This project is a small version of the original WpfOidcClientPop sample. The idea behind this sample is that a user can sign in with his regular account.

Auth via browser:

Instead of a Loginname/Password form rendered from the WPF app itself the authentication is delegated to a embedded browser control. Another option is to delegate it to the “real” installed browser, but this is another topic. The Microsoft Account login in Visual Studio is made that way or think of any popular “Facebook-Login” mobile app on your phone: The auth-process is basically a typical Web signin.

This scenario is also convered as a offical OpenID Connect specification. In WPF your best and easiest choice would be the IdentityModel.OidcClient2 package.

Auth “Steps”

The first step in the sample project is to aquire a access token from the IdentityServer. The actual implementation is thanks to the OidcClient quite simple as you can see here.

The OidcClient will try to get the needed accesstoken in the silent mode first (this can be configured) and if this fails a embedded browser will be rendered and the user needs to sign in there. After a successful signin you will get a accesstoken and refreshtoken.

Sample note: If you try this on your local machine the auth-window should not appear, because it will just do a “silent” windows auth login.

Multiple IdentityProvider: If you configure multiple identity provider, a simple designed identity selection will appear in the embedded browser window.

After the intial sign in you can regenerate new accesstokens via the refreshtoken.

With the accesstoken we craft a HTTP request to our beloved WebApi and write use the token in the authorization header and finally we are done.

Things to consider:

It is important to setup the OIDCClient the correct way with the values that you specified in your IdentityServer configuration. Also you should read about OpenID scopes because they are linked to the actual result. Without the correct scope you might not get a accesstoken or refreshtoken.

x

Summary

With these 4 projects we have a quite mighty solution created. We can still use Windows Auth for enterprise needs, we can protect WebApis and WebPages via a central Identity solution and also use “native” apps. The IdentityServer itself has a wide range of configuration possibilities.

If you start doing something in this direction I would point you to the IdentityServer4, because new is always better, right?

GitHub Link

The full sample can be found on GitHub.

Hope this helps.

AIT: New releases of TFS ASAP for TFS 2015 Update 4 and TFS 2017 Update 2

We are proud to present new releases of our TFS Automated Servicing and Administration Platform (TFS ASAP). The releases are now supporting the following versions of Microsoft Team Foundation Server:

  • TFS 2015 Update 4
  • TFS 2017 Update 2

The installer for TFS 2015 Update 4 (Build 14.4.17170.1) can be downloaded here and the installer for TFS 2017 Update 2 (Build 15.2.17227.3) can be found here.  For further information please contact us at support@tfsasap.com.

Christian Dennig [MS]: Deploy an Aurelia SPA to Azure Web App with VSTS

I’ve been working with Aurelia for quite a while now…well, since it has been released as an alpha version. I think it’s one of the best SPA frameworks out there and I’m heavily relying on the work of Rob Eisenberg and his team when it comes to production apps as well as example apps during my work at Microsoft.

Today, I want to show you how to deploy an Aurelia app via Visual Studio Team Services (VSTS), how to setup a Continuous Integration pipeline and automatically deploy the build artifacts to an Azure App Service.

As an example app, I’m taking the “Contacts Manager App” from the Aurelia team on GitHub. You can find it here.

contacts

Aurelia Contacts Manager

To be able to work with the repository in Visual Studio Team Service, I have forked the repository into my own GitHub account.

Visual Studio Team Services

If you don’t have a VSTS account yet, you can create one for free at the VSTS website. What you will get:

  • a complete DevOps platform for your development team (up to five members)
  • Code Hosting (TFVC, Git)
  • Agile Tools (issue/ticket tracking, dashboards, Scrum / Kanban templates…)
  • Continuous Integration
  • Release Management
  • Web-/Load-Test environment

The first step, before we can create the continuous integration pipeline, is to add a new project in VSTS.

create_project

You give it a name and select the desired version control system, project templates and security settings. As we are connecting our build to the source code repository from GitHub and won’t be using the issue tracking system in VSTS, these settings are not important at the moment (except security, of course).

Build The Project

To create the continuous integration pipeline, switch to the “Build Definitions” view via the main menu “Build & Release” and click “New“. The creation-wizard appears where you can select a template for the build you want to execute. We will start with an empty definition, so click on “Empty process“.

wizard_build

Build Definition Wizard

On the next screen, give your build definition a name and select a default agent queue. Basically, you can pick any of the agents in the list. As we will be using the command line task to execute the Aurelia Command Line Interface (to build the Aurelia app), you have to select a Windows based agent (needless to say, that you can also run the Aurelia CLI on a Linux based system).

wizard_1

Build Definition

On the “Get Sources” task, you select you project you wish to build in the current build definition. As you select GitHub, VSTS will automatically add a connection to the platform for you which you can reuse in other build definitions. Afterwards, just select the appropriate project and branch in GitHub and you are done with that task.

wizard_2

Connect to GitHub

To build the Aurelia sources, add the following tasks:

  • NPM to install the Aurelia CLI
  • NPM to install all the dependencies of Aurelia project
  • Command Line to execute the Aurelia CLI build task
  • Copy Files to copy artifacts into a build folder where all files of the application will be put in
  • Archive Files to create a ZIP file from content of the build folder
  • Publish Build Artifacts – well 🙂

Your build process should look now similar to that:

wizard_3

Initial Build Process

NPM Task #1

The first NPM task in our build process will be installing the Aurelia Command Line Interface onto our build agent. Click on the task in the list on the left and give the build step a display name and switch the “Command” drop-down from “Install” to “Custom“. In the “Command and arguments” field insert “install -g aurelia-cli“. The NPM command will be globally installing the CLI.

wizard_4

NPM Task #1

NPM Task #2

The second NPM task will be installing all the project dependencies of our Aurelia application. As done before, give the task a name and leave all the other settings as provided by the VSTS template.

wizard_5

NPM Task #2

Command Line Task

The Command Line task is responsible for executing the Aurelia CLI to build the application for us (uglify, bundling, minification, building source maps etc.). Basically, the task will execute the following script:

au build --env prod

To run the CLI from our task, enter “au” in the “Tool” and “build –env prod” in the “Arguments” field.

wizard_6

Command Line Task

Copy Files Task

To run our application on Azure, we need a few additional files from the project. First, we need the build results from the Command Line Task that was executed before. Second, the index.html and favicon.ico files are also needed. To be able to package all the files into one archive (next step in the build process), we copy all the necessary artifacts into one build  folder. To achieve this, enter the following line in the “Contents” textbox:

index.html
favicon.ico
scripts/**

In the “Target Folder” enter “build” .

The whole build task should now look like that:

wizard_7

Copy Files Task

Archive Files Task

The archive task will package our application into one ZIP file which we can use in the release pipeline to deploy the application to an Azure Web App. To bundle all the files, enter “build” in the “Root folder (or file) to archive” and “$(Build.ArtifactStagingDirectory)/spa.zip” in the “Archive file to create” field. The ZIP file will then be placed in the artifact staging directory from where we can publish the file afterwards.

Also please make sure to uncheck “Prefix root folder name to archive paths“, otherwise the archive contents will remain in the same folder structure as they are currently in the build directory.

wizard_8

Archive Files Task

Publish Build Artifacts

To publish the artifacts of our build process, simply enter “$(Build.ArtifactStagingDirectory)/spa.zip” in the “Path to Publish” field an give the artifact a proper name (in my case “spabuild“).

wizard_9

Publish Artifacts Task

Result

After going through all these configuration steps, the build process should look like that in your own build pipeline.

build_result

To execute the build, press “Save & Queue” from the menu. If you want to kick-off a new build every time the source code is updated, switch to the “Triggers” tab and enable “Continuous Integration“.

While the build is running, let’s create the release pipeline.

Release Pipeline

To create a release definition in VSTS, head over to the “Releases” view and click on the plus-button and “Create release definition“. A wizard will appear where you select the “Azure App Service Deployment” template.

release_1

Release Definition Template

On the next screen, you have to define the connection to the build definition (and the corresponding artifacts) which you have created before. Select the project and the correct build definition. If you want to deploy a new version every time the build is successful, check “Continuous deployment (create release and deploy whenever a build completes)“.

release_2

Release Definition Artifacts / Connection

To configure the deployment task, you have to select the Azure subscription where your target App Service is running (you should have created the App Service before, of course). After selecting (or adding via “Manage“) the subscription, choose the correct App Service name from the drop-down field and leave the rest of the settings as is. It should now look similar to this:

release_3

Configure Deployment Task

Click “Save” and “Release –> Create Release“. A new deployment is started and you can follow the progress by checking the logs of the running agent:

release_result

After a certain amount of time, the new version of the Aurelia app is deployed to your Web App and you can check the results by opening the corresponding website:

deployed

Aurelia App Running on an Azure Web App

Wrap Up

As you could see, it is really easy to automatically deploy an Aurelia App to an Azure App Service via Visual Studio Team Services. Of course, in a production environment, we would deploy the new version of the website to a deployment slot and after double-checking that everything works as desired, switch the staging slot with the production slot. Nevertheless, I hope you got an impression of how elegant and straight-forward the whole CI-/CD-process for Aurelia is with Visual Studio Team Services!

Have fun with it 🙂


AIT: Hilfe, meine TFS Collection Datenbank ist riesig!

Eine TFS Collection Datenbank wächst stetig an. Das ist völlig normal. Dabei kann es immer wieder Phasen geben, in denen das Wachstum stärker oder schwächer ausgeprägt ist. Auch hier schellen meistens noch keine Alarmglocken. Erst wenn ein vorher eingestellter Schwellwert (z.B. DB-Größe, Festplattenauslastung, Backup-Zeiten) erreicht wird, sucht man nach Ursachen und Möglichkeiten.

Die häufig einfachste Möglichkeit, in einer virtualisierten Umgebung, ist auf dem Data Tier die Festplattenkapazität zu vergrößern. Das kann in vielen Fällen ein ausreichendes Mittel sein. Es gibt aber auch Fälle, in denen Ursachenforschung betrieben werden muss, um festzustellen, wo die riesigen Datenmengen eigentlich herkommen und wie sich diese effektiv verringern lassen.

Gründe für die gezielte Reduktion können folgende sein:

  • Eine weitere Vergrößerung der Festplattenkapazität ist nicht mehr gewünscht. Die Ursache dafür kann wiederum auf verschiedene Motivationen begründet sein:
    • Kostenfaktor
    • Technischer Faktor (Infrastrukturgrenze erreicht)
  • Die Migrationszeit soll verkürzt werden:
    Sowohl bei On-Premises-Migrationen (Backup, Restore und Migration), als auch bei Migrationen nach VSTS (DACPAC-Export und Upload).

Letztendlich ist der Grund für die beabsichtigte Reduktion nicht ausschlaggebend. Anbei geben wir Ihnen einige Ideen mit, wie Sie zielgerichtet nach Einsparpotenzialen in Ihrer Collection suchen können.

  • Nicht mehr benötigte Buildergebnisse löschen:
    Je nach Branch-Struktur gibt es verschiedene Arten von Builds (z.B. Continuous Integration Build-Ergebnisse kann man meist ohne Folgeschäden löschen). Hier kann man schwer pauschalisieren. Das kommt sehr auf das verwendete Branch- und Buildkonzept an.
    Build and release retention policies
  • Nicht mehr benötigte Test Attachments löschen:
    Die Testausführung, insbesondere automatisierte Tests, erzeugen erhebliche Datenmengen. Die Test Attachments lassen sich mit dem Test Attachment Cleaner (bis TFS 2015) aufräumen. Mit einer Test Retention Policy (ab TFS 2015 Update 1) können alle Daten der Testergebnisse gelöscht werden, die ein definiertes Alter erreicht haben.
    Test result data retention with Team Foundation Server 2015
  • Nicht mehr benötigte Workspaces löschen:
    Alte Workspaces, die seit längerem nicht mehr genutzt wurden, können gelöscht werden.

  • Nicht mehr benötigte Shelvesets löschen:
    Alte Shelvesets, die seit längerem nicht mehr genutzt wurden, können ebenfalls gelöscht werden. Es ist zu beachten, dass bei der Verwendung von Gated-Checkins, jedes Mal wenn ein Gated-Checkin nicht erfolgreich war, das Shelveset nicht automatisch gelöscht wird! Daher ist es ratsam, in regelmäßigen Abständen, nicht mehr benötigte Shelvesets zu recyceln.
    Lifetime of ShelveSet used for Gated Checkin

Unsere Erfahrungen haben gezeigt, dass das zu erwartenden Einsparpotenzial sehr stark variiert. Wir haben in unseren Projekten eine Spanne von einem niedrigen einstelligen Prozentwert bis zu 80 Prozent gesehen. Steht auch bei Ihnen die Aktualisierung ihrer TFS Umgebung an oder denken Sie bereits über eine Migration nach VSTS nach, dann zögern Sie nicht uns anzusprechen.

Golo Roden: DDD, Teil 8: Eventual Consistency

Die vergangene Folge hat das Entwurfsmuster CQRS vorgestellt, das die Trennung von Schreib- und Leseseite einer Anwendung beschreibt. Das verbessert nicht nur die Struktur, sondern führt auch zu einer unkomplizierten Skalierbarkeit. Der Preis dafür ist die erforderliche Synchronisation – einschließlich ihrer Nebenwirkungen.

Uli Armbruster: Train the Trainer

Kürzlich haben wir für unsere Trainer in der co-IT.eu nach Workshops gesucht, um diesen neue Werkzeuge zur Optimierung ihrer Seminare an die Hand zu geben. Die ein oder andere Stellschraube lässt sich bekanntlich immer noch drehen, um das Wissen noch effizienter zu vermitteln. Dabei stellt das Fachliche nicht die Herausforderung dar, vielmehr ist es eine Kunst komplexes Wissen möglichst einfach zu vermitteln, sodass der Teilnehmer das Neue im Alltag auch reproduzieren kann.

Daher waren wir v.a. an Erkenntnissen aus Lernpsychologie und der Pädagogik interessiert. Wie funktioniert das Gedächtnis, wie erzeuge ich Bilder oder erzähle ich meinen Themenstoff als Story (Storytelling). Darüber hinaus waren uns Themen wie Auftreten, Charisma, Schlagfertigkeit oder aber Kritikfähigkeit wichtig, da diese den fruchtbaren Boden für Wissensvermittlung darstellen.

In einer Workshopprofil Präsentationstechniken haben wir brainstormartig alles notiert, was wir in irgendeiner Form gerne in dem Workshop sehen und hören wollten. Zu dem Zeitpunkt war bereits klar: Wir werden jährlich ein solches Training absolvieren. Demzufolge war nicht das Ziel möglichst viel in kurzer Zeit abzudecken, sondern dedizierte zusammenhängende Themen in einer notwendigen Tiefe. Mit der Wunschliste und der Bitte um eine Konzeptausarbeitung schrieb ich dann mehrere Unternehmen an. Natürlich nicht ohne über Facebook und Twitter nach Empfehlungen zu fragen. Danke Daniel an dieser Stelle für deine Rückmeldung.

Folgende Unternehmen haben wir kontaktiert:

Mit jedem der Unternehmen habe ich telefoniert, um die Konstellation und den Rahmen sauber abzustecken. Nachdem aufgrund terminlicher Konflikte zwei Anbieter weggefallen waren, haben wir demokratisch in der Gruppe der Teilnehmer die persönlichen Rankings erfasst. Weil ohnehin alle die Rhetorikhelden auf Platz 1 gelistet hatten, war die Sache schnell geklärt.

Der Beitrag soll dem Leser ein paar Anlaufstellen vermitteln und mit der oben verlinkten Wunschliste einen schnelleren Einstieg ermöglichen.


Einsortiert unter:CIO Topics, Events, German Tagged: Beratung, Firmenkontakt, Retrospektive

AIT: HoloLens: Reparieren eines beschädigten Kopfbandes

Microsoft HoloLens ist ein faszinierendes Gerät. Diese Faszination konnten wir sowohl in den unzähligen Demonstrationen als auch in unseren Kundenprojekten vielfach erfahren. Leuchtende Augen und kindliche Begeisterung sind häufig das Ergebnis der Nutzung von Microsoft HoloLens. Aber wo Licht ist, da ist auch Schatten. 

Eine dieser Schattenseiten ist der Schließmechanismus des Kopfbandes, mit dem die HoloLens auf dem Kopf befestigt wird. Bereits nach kurzem Einsatz war uns klar, dass dieser nicht für die Ewigkeit konstruiert ist. Nach ca. einem Jahr im Einsatz bemerkten wir, dass der Schließmechanismus immer öfter festhängt. Durch das regelmäßige Aufsetzten und Absetzten beim Entwickeln von HoloLens Anwendungen oder beim Vorstellen der HoloLens auf Messen und Konferenzen ist das Plastik ermüdet und schließlich gebrochen. Daraufhin war das Rädchen, welches die Größe des Kopfbandes reguliert ohne Funktion.

Da die HoloLens eine Entwicklerversion ist, bietet Microsoft keine Reparatur oder Erstattung an. Unsere Lösung gewinnt zwar keinen Designpreis, ist dafür aber preiswert und einfach umzusetzen. Dabei wird der Schließmechanismus der HoloLens durch ein Ersatzteil eines Fahrradhelms ersetzt.

IMG_5617 IMG_5618

Benötigt werden:

  • Fahrradhelm Ersatzteil, welches den Schließmechanismus der HoloLens ersetzt. Wir haben einen alten Fahrradhelm recycelt. Die Ersatzteile gibt es aber auch einzeln zu kaufen, zum Beispiel hier.
  • Seitenschneider oder eine stabile Schere zum Kappen der Bänder.
  • Nietenzange und Nieten zum Verbinden des HoloLens Kopfbandes mit dem neuen Schließmechanismus.

Nachteilig an diesem Ansatz ist die geringere Spanne des neuen Kopfbandes. Das Ersatzteil sollte deshalb so montiert werden, dass es komplett ausgefahren gerade so über den Kopf passt. So hat man noch genug Spiel die HoloLens festzuziehen.

Golo Roden: Rechtliche Sicherheit bei Open-Source-Beiträgen

Wer Software unter einer Open-Source-Lizenz veröffentlicht, erhält gelegentlich Beiträge von der Community. Was zunächst nach einem Gewinn für beide Seiten klingt, kann später unter Umständen zu rechtlichen Problemen führen. Wie lässt sich das vermeiden?

Christian Dennig [MS]: Deploy a Web App infrastructure using ARM templates

In my last post I discussed the basics of ARM templates and the Azure Resource Manager. In the following article, I would like to discuss a few other ways to deploy larger infrastructures, including dependencies, via an ARM template.

The example infrastructure to be built here consists of the following components:

All in all, a typical landscape for the backend of a modern web / mobile application. What is not covered in this example, is the Continuous Integration / Deployment area for the API or the database. The focus is entirely on the infrastructure and the corresponding “wiring” of the components. To illustrate the dependencies, here is a quick chart:

infra

I will not go into all the details of the template, but point out the important sections that e.g. connect the WebApp to Azure Search, create the Content Delivery Network, create the staging slot etc.

Let’s get to the individual components…

WebAPI

The creation of the WebAPI differs marginally from the standard template, which can be generated via Visual Studio. Since there is only one API running in the WebApp and .NET code will be executed, some settings are adapted to it. Among other things, PHP (line 65) and ARR (line 18) are disabled, the AlwaysOn feature enabled (line 64), the 64bit environment activated (line 63), the default page is removed (line 66). In addition, all connection strings (starting with Line 20) and properties , which are necessary for the communication with other services in the template (starting line 38), are filled out during the deployment.

Deployment Slot / Staging

A deployment slot provides an additional endpoint / additional WebApp within the app service plan, into which the actual app can be deployed. Deployment slots are a popular feature to launch a new version of an application with almost zero-downtime. You deploy the new version into a slot, test the functionality, generate load to “warm up” the app and then swap the production slot with the staging slot (“swapping” – corresponds to an IP switch on the contained loadbalancer). This can be done either via the portal, Powershell or e.g. Visual Studio Team Services.

The template for the “staging slot” to be created is as follows:

Storage Account and Content Delivery Network

Nearly every web application / API must be able to deal with files and the associated “delivery”. A storage account (Blob Storage) is created for the storage of files. A content delivery network is then created, which has the appropriate storage account as the source. The important thing is that the container, which is responsible for storing the files in the storage account, is created at least with the “Blobs (anonymous read access for blobs only)” permission. Unfortunately, you can not create containers by ARM template at the time of writing (see: Let me define preconfigured Blob Containers, Tables, Queue in ARM template).

Important in this section of the template is the “linking” of the CDN with the storage account (starting from Line 31).

Azure Redis Cache and Azure Search

In order to provide users with the best possible performance of an application, caches (for general requirements / caching) or search engines (especially for the search in the respective data) are often used. Azure has the Azure Redis Cache and Azure Search offers in the service portfolio. The creation of the services is relatively simple, as you can see in the following template section.

The interesting thing about the two created resources is the selection of the admin keys, so that the web app gets access to the respective service. Similar to the connection string of the SQL server, connections strings / properties must be created with which the connection from the Web app can be established. The access to the keys of the Redis Cache and Azure Search are located in the web app resource (lines 33 and 49), for completeness here again:

The two functions listKeys and listAdminKeys are so-called resource functions, which are made available by the underlying resource provider. Theoretically, each resource provider can provide such functions. To find out which functions a provider supports, there is the possibility to “ask” via Powershell (or CLI, REST api) at the desired provider. For Azure Storage, Redis or Azure Search, the calls would look like this:

Get-AzureRmProviderOperation -OperationSearchString "Microsoft.Storage/*" | where {$_.Operation -like "*list*"} | FT Operation

Get-AzureRmProviderOperation -OperationSearchString "Microsoft.Cache/*" | where {$_.Operation -like "*list*"} | FT Operation

Get-AzureRmProviderOperation -OperationSearchString "Microsoft.Search/*" | where {$_.Operation -like "*list*"} | FT Operation

The official documentation for the template functions, in particular resource functions, can be found here: Resource functions for Azure Resource Manager templates.

Other Resources

The template used here contains other resources, such as application insights, scale settings and alerts for the web application, SQL DB etc., which are created “straightforward” and for which no further explanations are necessary.

In the end, settings / information, such as the instrumentation key of AppInsights or the connection string of the database, are entered automatically during deployment time in the corresponding resource (mainly the WebApp).

Deployment

To deploy the template, there are several options available, which I have already briefly discussed in the previous article. Therefore, I only provide the Powershell script here:

# Login to Azure
Login-AzureRmAccount

# Resource Group
$rg = "myappinfra-rg"
New-AzureRmResourceGroup -Name $rg -Location westeurope

# Deploy via Resource Group Deployment
New-AzureRmResourceGroupDeployment -ResourceGroupName $rg -Mode Complete -TemplateFile ".\azuredeploy.json" -TemplateParameterFile ".\azuredeploy.parameters.json"

 

Wrap Up

The template including parameter file and deployment script are available on GitHub.

Have fun with 🙂

 


Holger Schwichtenberg: .NET Core 2.0 mit Visual Studio 2017 nutzen

Wer das am 14. August 2017 erschienene .NET Core 2.0 nutzen will, braucht Visual Studio 2017 Update 3 (interne Versionsnummer: 15.3) und zusätzlich das .NET Core 2.0 SDK.

Golo Roden: DDD & Co., Teil 7: CQRS

Das Verarbeiten von Kommandos und Erzeugen der fachlichen Ereignisse ist seit der vergangenen Folge in semantisch sinnvollem Code abgebildet. Auf dem Weg lassen sich die Konsistenz und Integrität effizient sicherstellen, auch das Speichern der Ereignisse passt dank Event Sourcing konzeptionell dazu. Wie steht es um das Lesen der Daten?

Stefan Henneken: IEC 61131-3: UNION erweitern per Vererbung

In dem Post IEC 61131-3: Weitere Spracherweiterungen bin ich kurz auf die UNION eingegangen. Ein Leserkommentar hat mich auf die Möglichkeit hingewiesen, dass auch eine UNION per EXTENDS erweitert werden kann. Da dieses die Handhabung einer UNION vereinfacht und die Norm auch nicht darauf hinweist, will ich diese Möglichkeit in einem (sehr) kurzen Post vorstellen.

Wie schon zuvor in einem Post beschrieben, ist es mit einer UNION möglich, eigene Datentypen zu definieren, deren Elemente alle denselben Speicherplatz belegen. Hier nochmal das Beispiel, bei dem auf eine 16-Bit Variable auch jeweils einzeln auf das Low-Byte und auf das High-Byte zugegriffen werden kann.

TYPE U_Test :
UNION
  nVar1  : WORD;
  stVar2 : ST_Bytes;
END_UNION
END_TYPE
TYPE ST_Bytes :
STRUCT
  nLSB  : BYTE;
  nMSB  : BYTE;
END_STRUCT
END_TYPE

Dieses Beispiel zeigt, wie ohne Bit-Operationen aus einer Variablen vom Typ WORD das niedrigere Byte (LSB) und das höherwertige Byte (MSB) ermittelt werden.

PROGRAM MAIN
VAR
  uVar     : U_Test;
  nA, nB   : BYTE;
END_VAR

uVar.nVar1 := 16#1234;
nA := uVar.stVar2.nLSB;  // Wert: 16#34 (LSB)
nB := uVar.stVar2.nMSB;  // Wert: 16#12 (MSB)

EXTENDS bei UNION

Etwas eleganter kann die gleiche Aufgabe auch mit Vererbung gelöst werden. Hierbei erbt der Verbund U_Test von ST_Bytes.

TYPE U_Test EXTENDS ST_Bytes :
UNION
	nVar1   : WORD;
	// stVar2  : ST_Bytes;	// kann entfallen, wird von ST_Bytes geerbt.
END_UNION
END_TYPE

Der Zugriff auf nLSB und nMSB muss jetzt nicht mehr über eine explizit deklarierte Variable in der UNION erfolgen.

uVar.nVar1 := 16#1234;
nA := uVar.nLSB;    // Wert: 16#34 (LSB)
nB := uVar.nMSB;    // Wert: 16#12 (MSB)

Manfred Steyer: Announcing angular-oauth2-oidc, Version 2

Today, I've released a new version of the angular library angular-oauth2-oidc, which allows for implementing token-based Security using OAuth2/ OpenId Connect and JWTs with Angular.

This new major version comes with some breaking changes. You find a list at the beginning of the updated readme. I think they won't affect everyone and even when you are affected you should be able to deal with them quite quickly.

Silent Token Refresh

Silent Refresh was the most requested feature for this library. It is a standard compliant way to refresh your tokens when/ before they expire using implicit flow.

If the application is prepared for it, performing a silent refresh is as easy as this:

this
    .oauthService
    .silentRefresh()
    .then(info => console.debug('refresh ok', info))
    .catch(err => console.error('refresh error', err));

By leveraging the new events observable, an application can automatically perform such a refresh when/ sometime before the current tokens expire:

this
    .oauthService
    .events
    .filter(e => e.type == 'token_expires')
    .subscribe(e => {
        this.oauthService.silentRefresh();
    });

More information about this can be found within the updated readme.

Validating the signature of id_tokens

The library can now directly validate the signature of received id_tokens. For this, just assign a ValidationHandler:

import { JwksValidationHandler } from 'angular-oauth2-oidc';

[...]

this.oauthService.tokenValidationHandler = new JwksValidationHandler();

The JwksValidationHandler shown here uses the JavaScript library jsrasign to validate the signature directly in the browser without the need to call the server.

You can also hook in an own ValidationHandler by implementing an interface.

More Security Checks

Some additional security checks have been added. The library insists on using https now and only makes an exception for localhost. It also validates the received discovery document.

Feedback

If you are using it or if you are trying it out, don't hesitate to send me some feedback -- either directly via my blog or via GitHub.

Alexander Schmidt: WPF und MVVM richtig einsetzen – Teil 5

Bindings gegen Mengen

Golo Roden: Einführung in Node.js, Folge 23: Child-Prozesse

In zahlreichen Anwendungen besteht die Anforderung, andere Anwendungen als Child-Prozesse zu starten. Zu dem Zweck enthält Node.js das Modul child_process. Allerdings ist Vorsicht geboten, denn das Modul führt rasch zu plattformabhängigem Code. Wie lässt sich das Problem anders lösen?

MSDN Team Blog AT [MS]: Sommer Update der österreichischen PowerShell Community

Auch im Sommer sind Roman und Patrick aktiv und haben für Euch die folgenden Infos gesammelt. Was also war im Juli los in der PowerShell Community in Österreich ?

Kommende Events:

Vergangene Events:

Blogeinträge:

Newsletter - die “Schnipseljagd”

Die wöchentlichen Newsletter beinhalteten die folgende Themen:

Powershell Schnipseljagd Woche 27

Netzwerkkarten Hardwarehersteller auslesen anhand der MAC Adresse
E-Mails mit G-Mail SMTP settings und Powershell versenden
Fehlende Patches auf Remote-Servern finden
Ein PS-Script zu einer DSC Konfiguration konvertieren
Mit PowerShell ein Zertifikat anfordern

Powershell Schnipseljagd Woche 28

Den Helpdesk mit PowerShell verbessern
Mit PowerShell den Ärger mit WSUS verringern
Vererbung in DSC Ressourcen verwenden
PowerShell mit VMWare vCenter
DBA Tools mit VSCode
PowerShell Einsteigerbuch
Sichere Passwörter in PowerShell

Powershell Schnipseljagd Woche 29

Get-Location, [Environment] und [System.IO.Path], was man darüber wissen sollte
Mit lokalen Accounts arbeiten
Software mit PowerShell installieren und verteilen
Wie lange ist es bis Weihnachten
Speicherplatz freimachen indem man temporäre Dateien löscht.
Passwörter auf der Befehlszeile erzeugen.

Powershell Schnipseljagd 30

Security - Einige Security Überlegungen zum Thema PowerShell.
Bösartige Makros generieren mit LuckyStrike:
PowerShell Community. Die gibt es auch in Deutschland. Hier ein aktueller Überblick.
File / Folder Owner
Daten verschlüsselt speichern & lesen
Graph Explorer (nur wenig PowerShell)
Visual Studio Code – weil man sich das ansehen sollte

PowerShell Schnipseljagd 31

Wie werde ich zum Powershell Profi.
Übersicht von interessanten Modulen für PowerShell Profis
PowerShell Module PlatyPS - Ein Module zum Erzeugen von Hilfeseiten in Markdown für PowerShell.
Forschung: Automatisierte Fehlersuche Wie weit ist die Forschung bei der automatisierten Fehlersuche?
ARM Templates - Hier gibt es ein paar Serverless ARM Templates.
Style Sheets and PowerShell Reports - Verwendung von Style Sheets in PowerShell Reports
PowerShell Style Guide
PowerShell Security - Using PowerShell without PowerShell.exe

Die PowerShell UserGroup Austria

www.powershell.co.at

Golo Roden: DDD & Co., Teil 6: Vom Modell zum Code

Die Domäne ist modelliert, gespeichert werden Events. Wie lässt sich das Ganze nun von der Theorie in die Praxis überführen? Wie könnte Code aussehen, der die fachliche Modellierung widerspiegelt? Ein Entwurf.

Stefan Henneken: IEC 61131-3: Parameterübergabe per Parameterliste

Parameterlisten sind eine interessante Variante, Parameter an SPS-Bibliotheken zu übergeben. Genaugenommen handelt es sich um globale Konstanten (VAR_GLOBAL CONSTANT), deren Initialisierungswerte im Library Manager editierbar sind.

Bei der Deklaration von Arrays müssen dessen Grenzen Konstanten sein. Zum Zeitpunkt der Compilierung muss bekannt sein, wie groß das Array anzulegen ist. Der Versuch die Arraygrenzen durch eine Variable zu definieren, wird mit einer entsprechenden Fehlermeldung abgelehnt.

PROGRAM MAIN
VAR
  nUpperBound      : INT := 5;	
  aTest            : ARRAY [1..nUpperBound] OF INT;
END_VAR

// Compiler error: Border 'nUpperBound' of array is no constant value

Erst die Verwendung von Konstanten löst das Problem.

PROGRAM MAIN
VAR
  aTest            : ARRAY [1..nUpperBound] OF INT;
END_VAR
VAR CONSTANT
  nUpperBound      : INT := 5;
END_VAR

Gerade in SPS-Bibliotheken kann dieses zu Problemen führen. Soll durch ein Array z.B. ein Datenpuffer abgebildet werden, so ist nicht unbedingt zum Zeitpunkt der Bibliotheksentwicklung bekannt, wie groß dieser Datenpuffer in den jeweiligen Anwendungen zu sein hat.

Abhilfe schafft hier eine Parameterliste, die bei der Erstellung einer SPS-Bibliothek eingefügt wird.

Pic01

Eine Parameterliste kann an beliebiger Stelle des SPS-Projektes (das SPS-Projekt, aus der die SPS-Bibliothek entsteht) hinzugefügt werden. Üblicherweise werden Parameterlisten bei den globalen Variablenlisten (GVLs) angelegt.

Pic02

Deklariert werden die Parameter wie konstante, globale Variablen.

{attribute 'qualified_only'}
VAR_GLOBAL CONSTANT
  cUpperBound    : INT := 3;
  cString        : STRING := 'Hello';
  cIntArray      : ARRAY [1..cUpperBound] OF INT := [1, 2, 3];
  cStructArray   : ARRAY [1..3] OF ST_Test := [3( (nValue := 1,
                                                   fValue := 3.1,
                                                   wsValue := "abc") )];
  cEnum          : E_Test := E_Test.eValue02;
  cStruct        : ST_Test := (nValue := 1, fValue := 3.1, wsValue := "abc");
END_VAR

Durch Initialisierungswerte können die Variablen vorinitialisiert werden. Auch komplexere Variablenstrukturen können so mit Werten initialisiert werden.

In der Anwendung, in der die Bibliothek eingebunden wurde, können diese Parameter im Library Manager editiert werden. Der Library Manager wird durch einen Doppelklick auf der jeweiligen Bibliotheks-Referenz geöffnet.

Pic03

Die Felder in der Spalte Value (editable) können jetzt beliebig verändert werden. Die so veränderbaren Konstanten können innerhalb der jeweiligen SPS-Bibliothek oder auch in der Anwendung, die diese referenziert, verwendet werden.

FUNCTION F_AddArrayValues : INT
VAR_INPUT
  aTest      : REFERENCE TO ARRAY[1..Param.cUpperBound] OF INT;	
END_VAR
VAR
  nIndex     : INT;
END_VAR

FOR nIndex := 1 TO Param.cUpperBound DO
   F_AddArrayValues := F_AddArrayValues + aTest[nIndex];
END_FOR

Abgespeichert werden die geänderten Initialisierungswerte in der jeweiligen SPS-Projektdatei (*.plcproj). Jede Anwendung, die eine Bibliothek mit einer Parameterliste referenziert, besitzt somit seine eigenen Werte. Die Bibliothek an sich bleibt unverändert.

Die so eingestellten Werte überleben auch einen SPS-Reset, sowohl einen Kaltreset, als auch das Zurücksetzen auf die Ursprungswerte. Zurückgesetzt werden die Werte erst dann, wenn die Bibliothek entfernt und wieder eingefügt wird. Somit können Parameterlisten auch zur Übergabe von allgemeinen Parametern genutzt werden.

Alle Parameter einer Parameterliste sind i.d.R. per ADS erreichbar. Die Tatsache, dass Parameter immer mit CONSTANT deklariert werden müssen, verhindert nicht, dass diese per ADS beschreibbar sind.

Dient ein Parameter als Arraygrenze, so wird eine Änderung dieser Variablen schwerwiegende Folgen haben. Insbesondere dann, wenn der Wert des Parameters vergrößert wird.

aTest   : ARRAY [1..MyLibrary.Param.cUpperBound] OF INT;

Auch wenn der Parameter zur Laufzeit des SPS-Programms vergrößert wird, so behält das Array seine ursprüngliche Größe bei. Wird dieser Parameter aber auch in Schleifen eingesetzt, so können Zugriffe außerhalb der erlaubten Arraygrenzen entstehen.

FOR nIndex := 1 TO MyLibrary.Param.cUpperBound DO
  aTest[nIndex] := nValue;
END_FOR

Um dieses zu verhindern, kann mit dem Attribut tc_no_symbol die ADS-Symbolerzeugung verhindert werden. Ein Zugriff per ADS über den Symbolnamen ist dann für diesen Parameter nicht mehr möglich.

VAR_GLOBAL CONSTANT
  {attribute 'tc_no_symbol'}
  cUpperBound             : INT := 5;
END_VAR

MSDN Team Blog AT [MS]: Visual Studio 2017 – Architektur & Code Analyse

Visual Studio bietet umfangreiche Funktionen im Bereich Architektur und Code Analyse über die nicht häufig berichtet wird. Diese Funktionen sind aber ungerechtfertigter Weise "hinter dem Vorhang" und haben auch mit Visual Studio 2017 einige Neuerungen und Weiterentwicklung bekommen.

 

Echtzeitüberprüfung von Architekturabhängigkeiten: Der Editor überprüft während Änderungen im Code vorgenommen werden ob Architekturvorgaben verletzt werden. Unerwünschte Abhängigkeiten werden vermieden und in der Fehlerliste angezeigt bzw. mit Wellenlinien im Text-Editor angezeigt.

Live Unit Tests: Live Unit Tests geben direktes Feedback zur Testabdeckung und Ergebnissen der Testausführung. Dazu werden bei Änderungen im Code permanent Unit Tests (MSTest, xUnit, NUnit) ausgeführt und in der Codezeile das Feedback angezeigt (erfolgreich, nicht erfolgreich, keine Abdeckung). Live Unit Testing ist für C# und VB (.NET Framework) verfügbar sowie auch für das .NET Core Framework (ab 2.0 und Visual Studio 2017 Update 3).

Ergänzung (9. Aug.): Live Unit Tests und die Echtzeitüberprüfung von Architekturabhängigkeiten sind Funktionen von Visual Studio Enterprise.

Code Maps: Die Visualisierung von Abhängigkeiten im Code hilft bei der Einarbeitung in ein Projekt und beim Erkennen von Abhängigkeiten ohne den gesamten Code lesen zu müssen. Die grafische Darstellung kann sowohl mit Visual Studio Professional sowie auch Visual Studio Enterprise gelesen werden aber nur mit Visual Studio Enterprise erstellt werden. Der Schwerpunkt bei der Sprachunterstützung liegt im managed .NET Bereich:

Codeabhängigkeiten können in folgenden Sprachen durch Code Maps dargestellt werden:

  • Visual C# .NET oder Visual Basic .NET in einer Projektmappe oder in Assemblys (.dll oder .exe)
  • Den systemeigenen oder verwalteten C oder C++-Code in Visual C++-Projekten, Headerdateien (.h oder #include) oder Binärdateien
  • Aus .NET-Modulen für Microsoft Dynamics AX erstellte X++-Projekte und X++-Assemblydateien
Hinweis: Für andere als C#- oder Visual Basic .NET-Projekte sind weniger Optionen zum Starten einer Code Map oder zum Hinzufügen von Elementen zu einer vorhanden Code Map verfügbar. Beispielweise können Sie nicht mit der rechten Maustaste auf ein Objekt im Text-Editor eines C++-Projekts klicken, es einer Code Map hinzuzufügen. Sie können jedoch einzelne Codeelemente oder Dateien per Drag & Drop aus dem Projektmappen-Explorer, der Klassenansicht und dem Objektbrowser verschieben.

Speziell im Bereich der grafischen Darstellung von Codeabhängigkeiten gibt es auch außerhalb von .NET Erweiterungen und cross-Plattform Partnerlösungen. Ein Anbieter aus Österreich (Salzburg) ist neu in den Visual Studio Marktplatz gekommen: Sourcetrail
Sourcetrail ist ein corss-Plattform Werkzeug für C/C++/Java und bietet eine intelligente Suche, grafische Darstellung und Anzeige des dazugehörigen Codes an. Visual Studio wird dabei als Editor nicht ersetzt und eine JSON-basierte Compilation DB rundet den cross-Plattform Gedanken ab.

Visual Studio sowie auch Microsoft Azure richtet sich an Entwickler aller Plattformen und Sprachen: Ein spezielle Webseite hat Informationen zu Visual Studio für Java Entwickler zusammengefasst. Mit Azure können Sie Java-Apps ganz leicht bereitstellen und skalieren - mit den Tools Ihrer Wahl.

MSDN Team Blog AT [MS]: Wie weiß man wann der Jausenwagen da ist?

Gastartikel von unserem MVP Thomas Gölles, Fa. Solvion

Hallo MoCaDeSyMo!

Wie vermutlich in jedem Unternehmen in Österreich gibt es auch bei Solvion unterschiedliche Möglichkeiten zur Gestaltung der Mittagspause. Vom schnellen Weg zur Cafeteria bis zum bestellen bei unterschiedlichen Lieferservices spannt sich ein breiter Bogen verschiedener lukulischer Genüsse. Wir sind dann noch zusätzlich in der glücklichen Lage das täglich ein Jausenwagen vom Imbiss Rainer bei uns in der Einfahrt seine Waren anbietet. In den letzten Jahren war die letzte Option fast ein täglicher Weg. Ich hab direkt von meinem Arbeitsplatz direkt auf die Einfahrt gesehen und wurde quasi täglich vom Jausenwagen an die Mittagspause erinnert. Letzten Oktober aber haben wir intern Büro gewechselt und seit dem sitze ich am anderen Ende des Gebäudes ohne Sicht auf die Einfahrt. In der modernen Arbeitswelt kein Problem, einfach gegen 11:00 den Kollegen in Skype for Business pingen und fragen „Is da Rainer schon da?“ und die Sache ist erledigt. Da die Kollegen mit freier Sicht rar sind und ich auch nicht täglich zwecks der Jause anklopfen will musste eine andere Lösung her.

Der erste Gedanke drehte sich um eine mobile App, die der Fahrer des Vertrauens des geliebten Jausenwagens einfach auf seinem Phone installiert und welche dann mittels GPS und Azure einen Push sendet wenn er in unsere Nähe kommt. Eigentlich eine coole Lösung, aber jemanden fremden eine App installieren damit man selber weiß wann die Jause da ist, dass hat sich einfach nicht richtig angefühlt. Gut weiter im Brainstorming, vor Jahren auf der Uni gab es mal eine Übung in Computer Vision. Ein Foto von der Einfahrt mit dem Jausenwagen oben, ein klein wenig Kantendetektion vielleicht noch eine Histogrammverschiebung dazu und dieser Wagen müsste sich doch erkennen lassen. Die Idee galt es zu verfolgen.

Spätestens mit der Build 2017 und allen Announcments rund um Cognitive Services und die Custom Vision API war klar, ein Raspberry Pi muss her und der Jausenwagen muss erkannt werden. Ohne viel weiter Einleitung, ich darf vorstellen, MoCaDeSyMo:

clip_image002

MoCaDeSyMo (mobile carbohydrate delivery system monitor) ist der rote Kollege am linken Bild Rand und das jüngste Mitglied der Solvion Familie.

An Wochentagen versucht er zwischen 10:45 und 11:30 minütlich anhand eines Fotos der Firmeneinfahrt zu erkennen ob nun der bereits viel zitierte Jausenwagen zum Plündern bereit steht.

clip_image004

Um auch wirklich sicher zu gehen nutzt MoCaDeSyMo Azure Cognitive Services um den Jausenwagen zu erkennen und informiert danach mittels Microsoft Teams unterschiedliche Gruppen innerhalb der Firma. Aktuell bekommen wir die Info über die Wahrscheinlichkeiten und einen Link des Fotos in einen speziellen Teams Channel.

clip_image006

Der Output hier ist von der Marke „von Entwickler für Entwickler“ und muss für Business User sicher angepasst werden, aber für uns passt es genau so.

In weiterer Folge des Posts wird in einem kurzen Überblick das Zusammenspiel der verschiedenen Teile der Gesamtarchitektur vorgestellt.

clip_image008

Was läuft am Rasbperry Pi?

Basis unseres MoCaDeSyMo ist ein Raspberry Pi 3 mit dem Pi Kamera Modul. Als Betriebssystem läuft ein standard Raspbian Jessie image, in der Server Variante. Wenn schon Linux, dann richtig also ohne GUI. (Außerdem ein guter Grund die durchaus eingerosteten Linux Kenntnisse wieder auf zu frischen und wieder lieben zu lernen, Stichwort cron job) Man hätte dafür natürlich auch Windows 10 IoT verwenden können, eine schnelle Recherche hat aber ergeben, dass das default Kamera Modul in Linux weit besser anzusprechen ist, daher viel die Entscheidung pro Raspbian. Und hier nun der gesamte Code der auf dem Pi läuft:

PATH=$PATH:/home/pi/bin
export AZURE_STORAGE_ACCOUNT='%YOUR_STORAGE_ACCOUNT%'
export AZURE_STORAGE_ACCESS_KEY='%YOUR_ACCESS_KEY'
 
container_name='%YOUR_CONTAINER%'
 
today=`date '+%Y-%m-%d-%H-%M-%S'`;
filename="$today.png"
 
echo "taking the picture..."
raspistill -o $filename
 
echo "croping the picture ..."
mogrify -crop 1487x619+907+904 $filename
 
echo "logging into azure..."
az login --service-principal -u %USER_GUID% --password "%YOUR_STRONG_PWD%" --tenant %TENANT_GUID%
 
echo "uploading image"
az storage blob upload --container-name $container_name --file $filename --name $filename
 
echo "deleting the image"
rm $filename
 
echo "logging out from azure"
az logout
 
echo "triggering azure function ..."
image_param='image=https://%YOUR_STORAGE_ACCOUNT%.blob.core.windows.net/%YOUR_CONTAINER%/'
function_url='%YOUR_FUNCTION-URL'
 
curl -G $function_url -d $image_param$filename
 
echo ""

Diese 35 Zeilen sind die Basis, direkt am Pi. Dieses Skript wird mittels cron job gestartet und bedient sich einiger Linux Bibliotheken.

Um das Foto zu schießen reicht ein Aufruf von raspistill. Dieses Tool hat eine Menge an Features an Board, unteranderem eine Methode von selbst in einer Art Timer Job konstant Fotos zu machen:

raspistill -t 30000 -tl 2000 -o image%04d.jpg

Hier wird im Zeitraum von 30 Sekunden alle zwei Sekunden ein Foto gemacht und im aktuellen Verzeichnis abgelegt. Diese Methode ist sehr nützlich wenn es darum geht Trainingsbilder für die image recognition zu erzeugen, dazu aber später mehr.

Nach den ersten Testaufnahmen war klar, dass die 8 Megapixel Auflösung des Pi sehr viel „statischen“ Content des Bereichs der Firmeneinfahrt einfängt. Daher wird im zweiten Schritt das Bild auf eine gewisse Größe zusammen geschnitten um auch wirklich nur den Bereich weiter zu verarbeiten der die gewünschte Information hält.

Das „fertige“ Bild wird anschließend in einen Azure Blob Storage hochgeladen. Dazu wurde am Pi die Azure CLI 2.0 (https://azure.github.io/projects/clis/) installiert und verwendet. Um auch gleich Richtung Security vorbildlich zu agieren wird statt der Authentifizierung mittels User/Passwort auf einen Service Principal gesetzt. Diesen kann man auch gleich direkt am Pi mit der CLI erstellen und anschließend verwenden. Kleiner Hinweis, wer die Skripte gerne am Windows Rechner erstellt und nur auf den Pi überträgt, sollte zwei Befehle im Hinterkopf haben. Erstens chmod +x um das Skript auch wirklich ausführbar zu machen und ganz wichtig dos2unix um etwaige Probleme in den Textformaten zwischen Windows und Linux glatt zu bügeln.

Zum Abschluss wird am Pi noch mittels curl eine Azure Function aufgerufen und die URL des Fotos im Blob Storage übergeben.

Die Power aus der Cloud

In der Azure Function wird nun der Custom Vision API Endpoint aufgerufen. Nach der Übergabe des predicition keys des eigenen Custom Vision Projekts kann der Endpoint mit der URL zum Bild aufgerufen werden. Den Schlüssel und die Definition des Endpoints erhält man unter https://www.customvision.ai Hier legt man ein neues Projekt an und kann auch gleich Fotos hochladen und kategorisieren. Wir haben dazu schon ohne fix fertige Lösung Fotos vom Pi hochgeladen und die image recognition getestet. Um es kurz zu machen, schon die ersten Eindrücke waren der pure Wahnsinn. Rund 80 Fotos von der Einfahrt mit dem Jausenwagen und rund 160 ohne diesen reichten aus um bereits Wahrscheinlichkeiten über 90% zu bekommen. Diesen Test kann man auch direkt im Portal durchführen.

clip_image009

Hier kann man direkt eine URL zu einem Bild eintragen oder eben auch ein Foto von der lokalen Maschine hochladen und dann testen lassen. Man sieht hier auch schön einen Denkfehler meinerseits. Um die Kategoriesierung möglichst einfach zu machen, habe ich einfach False und True als Werte für die Tags verwendet. Das ist prinzipiell nicht falsch, führt aber dazu, dass man wie im Foto unten sieht bei der Konstellation von Wahrscheinlichkeiten die in Summe über 100% gehen verwirrt ist. Der Trugschluss ist, dass True und False eindeutig und völlig unabhängig von einander sind. Die image recognition hier kategorisiert aber nur, sprich es sieht den Fall False als losgelöstes Einzelereignis und gibt dafür die Wahrscheinlichkeit an. Daher ist es auch klar, dass es in Summe durchaus über 100% gehen kann.

clip_image010

Als Antwort schickt die Custom Vision API ein Json Objekt das wie folgt aussieht:

{\"Id\":\"%ID%\",\"Project\":\"%PROJECT_ID%\",\"Iteration\":\"%ITERATION_ID%\",\"Created\":\"2017-06-26T16:07:48.1746322Z\",\"Predictions\":[{\"TagId\":\"7c24f243-ed2c-4f20-b35f-88b7b96b1711\",\"Tag\":\"False\",\"Probability\":1.0},{\"TagId\":\"9721bc40-c00c-460d-97e4-67a269ce543a\",\"Tag\":\"True\",\"Probability\":3.00917918E-07}]}

Um diesen Json String wieder in ein C# Objekt zu bekommen reicht eine kleine Suche im Internet um auf fertige Klasse wie diese zu stossen:

public class CustomVisionResponse
    {
        public string Id { get; set; }
        public string Project { get; set; }
        public string Iteration { get; set; }
        public string Created { get; set; }
        public List<Prediction> Predictions { get; set; }
    }
public class Prediction
    {
        public string TagId { get; set; }
        public string Tag { get; set; }
        public string Probability { get; set; }
    }

Zu finden hier: http://aihelpwebsite.com/Blog/EntryId/1025/Microsoft-Cognitive-Custom-Vision-Service-ndash-A-Net-Core-Angular-4-Application-Part-One)

Mit Hilfe dieses Objektes ist es ein leichtes für die einzelnen Tags die Wahrscheinlichkeit auszulesen und damit fest zu stellen mit eben welcher Wahrscheinlichkeit es richtig wäre auf zu stehen und sich sein Mittagessen zu holen.

Fehlt noch ein Schritt, wie informieren wir die Benutzer? Dazu nutzen wir im konkreten Fall den Connector eines Microsoft Teams Channels. Diesen kann man sich direkt in Teams erstellen lassen und ist praktisch eine URL an die man seine Message schickt welche dann in Teams angezeigt wird.

using (var teams_client = new HttpClient())
{
   var json_ = "{\"text\":\""+output+ link_to_img+"\"}";
   log.Info(json_);
   var response_ = await client.PostAsync("%YOUR_TEAMS_CONNECTOR_URL%", new StringContent(json_, System.Text.Encoding.UTF8, "application/json"));
   var result_ = await response_.Content.ReadAsStringAsync();
}

Zusammenfassung

Das hier gezeigte Projekt ist binnen weniger Stunden vom eingepackten Raspberry Pi bis zur fertigen Meldung im Teams Client entstanden. Der Raspberry Pi schießt periodisch Fotos, lädt die in einen Azure Blob Storage und triggert eine Azure Function. Diese schickt das Bild zur Custom Vision API und informiert mittels Microsoft Teams die Benutzer. Das alles in ca 8-9 Stunden. Natürlich ist der Code nicht production ready sondern nur ein Prototyp und natürlich ist auch der Use Case ein sehr einfacher und simpler Fall der konkret im Business Umfeld sich nicht in Millionen an Gewinn niederschlägt. Es zeigt aber sehr schön auf was mit ein wenig Kreativität und den Mitteln der Azure Cloud möglich ist und das ganze mit nicht mal 200 Zeilen Code.

Golo Roden: Einführung in Node.js, Folge 22: CLI-Anwendungen

Node.js wird in der Regel als Laufzeitumgebung für Webanwendungen verwendet. Doch es eignet sich auch für andere Arten von Anwendungen wie Konsolenprogramme. Das wirft jedoch eine Reihe von Problemen auf, von der Analyse der Parameter bis zur Ausgabe der Hilfe. Wie lässt sich das bewerkstelligen?

Don't contact us via this (fleischfalle@alphasierrapapa.com) email address.